Implement Album and Track metadata for duration and genres.
For tracks, we will fetch the duration from the ID3 tag information, alongside genres. These genres will be used for multiple purposes, e.g. search. At the moment however, it is used in koto_album_add_track to collate a list of genres that apply to the album based on the contents of it. We can have multiple genres, we separate them by semi-column as is the case for TagLib. We will attempt to filter and rename some select genres to enforce consistency. Changed position and playback position from guint to guint64. Implemented multiple koto utility functions: - koto_utils_join_string_list - koto_utils_string_contains_substring - koto_utils_string_to_string_list Drastically simplified koto_utils_replace_string_all. Implement koto_track_helpers_init to initialize our hashtable. Fixed a segfault during first load of Koto where we were attempting to get the playback engine last used volume in koto_config_save, when the KotoPlaybackEngine was not yet initialized. Default it to 1.0.
This commit is contained in:
parent
812cdc6677
commit
381cc9ce4c
15 changed files with 387 additions and 44 deletions
|
@ -506,7 +506,12 @@ void koto_config_save(KotoConfig * self) {
|
||||||
gchar * playback_hash = g_strdup("playback");
|
gchar * playback_hash = g_strdup("playback");
|
||||||
gchar * ui_hash = g_strdup("ui");
|
gchar * ui_hash = g_strdup("ui");
|
||||||
|
|
||||||
gdouble current_playback_volume = koto_playback_engine_get_volume(playback_engine); // Get the last used volume in the playback engine
|
gdouble current_playback_volume = 1.0;
|
||||||
|
|
||||||
|
if (KOTO_IS_PLAYBACK_ENGINE(playback_engine)) { // Have a playback engine (useful since it may not be initialized before the config performs saving on first application load)
|
||||||
|
current_playback_volume = koto_playback_engine_get_volume(playback_engine); // Get the last used volume in the playback engine
|
||||||
|
}
|
||||||
|
|
||||||
self->playback_last_used_volume = current_playback_volume; // Update our value so we have it during save
|
self->playback_last_used_volume = current_playback_volume; // Update our value so we have it during save
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
|
@ -39,8 +39,8 @@ void close_db() {
|
||||||
|
|
||||||
int create_db_tables() {
|
int create_db_tables() {
|
||||||
gchar * tables_creation_queries = "CREATE TABLE IF NOT EXISTS artists(id string UNIQUE PRIMARY KEY, name string, art_path string);"
|
gchar * tables_creation_queries = "CREATE TABLE IF NOT EXISTS artists(id string UNIQUE PRIMARY KEY, name string, art_path string);"
|
||||||
"CREATE TABLE IF NOT EXISTS albums(id string UNIQUE PRIMARY KEY, artist_id string, name string, art_path string, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);"
|
"CREATE TABLE IF NOT EXISTS albums(id string UNIQUE PRIMARY KEY, artist_id string, name string, art_path string, genres string, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);"
|
||||||
"CREATE TABLE IF NOT EXISTS tracks(id string UNIQUE PRIMARY KEY, artist_id string, album_id string, name string, disc int, position int, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);"
|
"CREATE TABLE IF NOT EXISTS tracks(id string UNIQUE PRIMARY KEY, artist_id string, album_id string, name string, disc int, position int, duration int, genres string, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);"
|
||||||
"CREATE TABLE IF NOT EXISTS libraries_albums(id string, album_id string, path string, PRIMARY KEY (id, album_id) FOREIGN KEY(album_id) REFERENCES albums(id) ON DELETE CASCADE);"
|
"CREATE TABLE IF NOT EXISTS libraries_albums(id string, album_id string, path string, PRIMARY KEY (id, album_id) FOREIGN KEY(album_id) REFERENCES albums(id) ON DELETE CASCADE);"
|
||||||
"CREATE TABLE IF NOT EXISTS libraries_artists(id string, artist_id string, path string, PRIMARY KEY(id, artist_id) FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);"
|
"CREATE TABLE IF NOT EXISTS libraries_artists(id string, artist_id string, path string, PRIMARY KEY(id, artist_id) FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);"
|
||||||
"CREATE TABLE IF NOT EXISTS libraries_tracks(id string, track_id string, path string, PRIMARY KEY(id, track_id) FOREIGN KEY(track_id) REFERENCES tracks(id) ON DELETE CASCADE);"
|
"CREATE TABLE IF NOT EXISTS libraries_tracks(id string, track_id string, path string, PRIMARY KEY(id, track_id) FOREIGN KEY(track_id) REFERENCES tracks(id) ON DELETE CASCADE);"
|
||||||
|
|
|
@ -116,6 +116,7 @@ int process_albums(
|
||||||
gchar * artist_uuid = g_strdup(koto_utils_unquote_string(fields[1]));
|
gchar * artist_uuid = g_strdup(koto_utils_unquote_string(fields[1]));
|
||||||
gchar * album_name = g_strdup(koto_utils_unquote_string(fields[2]));
|
gchar * album_name = g_strdup(koto_utils_unquote_string(fields[2]));
|
||||||
gchar * album_art = (fields[3] != NULL) ? g_strdup(koto_utils_unquote_string(fields[3])) : NULL;
|
gchar * album_art = (fields[3] != NULL) ? g_strdup(koto_utils_unquote_string(fields[3])) : NULL;
|
||||||
|
gchar * album_genres = (fields[4] != NULL) ? g_strdup(koto_utils_unquote_string(fields[4])) : NULL;
|
||||||
|
|
||||||
KotoAlbum * album = koto_album_new_with_uuid(artist, album_uuid); // Create our album
|
KotoAlbum * album = koto_album_new_with_uuid(artist, album_uuid); // Create our album
|
||||||
|
|
||||||
|
@ -125,6 +126,8 @@ int process_albums(
|
||||||
album_name, // Set name
|
album_name, // Set name
|
||||||
"art-path",
|
"art-path",
|
||||||
album_art, // Set art path if any
|
album_art, // Set art path if any
|
||||||
|
"preparsed-genres",
|
||||||
|
album_genres,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -134,6 +137,7 @@ int process_albums(
|
||||||
g_free(album_uuid);
|
g_free(album_uuid);
|
||||||
g_free(artist_uuid);
|
g_free(artist_uuid);
|
||||||
g_free(album_name);
|
g_free(album_name);
|
||||||
|
g_free(album_genres);
|
||||||
|
|
||||||
if (album_art != NULL) {
|
if (album_art != NULL) {
|
||||||
g_free(album_art);
|
g_free(album_art);
|
||||||
|
@ -224,7 +228,9 @@ int process_tracks(
|
||||||
gchar * album_uuid = g_strdup(koto_utils_unquote_string(fields[2]));
|
gchar * album_uuid = g_strdup(koto_utils_unquote_string(fields[2]));
|
||||||
gchar * name = g_strdup(koto_utils_unquote_string(fields[3]));
|
gchar * name = g_strdup(koto_utils_unquote_string(fields[3]));
|
||||||
guint * disc_num = (guint*) g_ascii_strtoull(fields[4], NULL, 10);
|
guint * disc_num = (guint*) g_ascii_strtoull(fields[4], NULL, 10);
|
||||||
guint * position = (guint*) g_ascii_strtoull(fields[5], NULL, 10);
|
guint64 * position = (guint64*) g_ascii_strtoull(fields[5], NULL, 10);
|
||||||
|
guint64 * duration = (guint64*) g_ascii_strtoull(fields[6], NULL, 10);
|
||||||
|
gchar * genres = g_strdup(koto_utils_unquote_string(fields[7]));
|
||||||
|
|
||||||
KotoTrack * track = koto_track_new_with_uuid(track_uuid); // Create our file
|
KotoTrack * track = koto_track_new_with_uuid(track_uuid); // Create our file
|
||||||
|
|
||||||
|
@ -240,13 +246,22 @@ int process_tracks(
|
||||||
disc_num,
|
disc_num,
|
||||||
"position",
|
"position",
|
||||||
position,
|
position,
|
||||||
|
"duration",
|
||||||
|
duration,
|
||||||
|
"preparsed-genres",
|
||||||
|
genres,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
g_free(name);
|
||||||
|
|
||||||
int track_paths = sqlite3_exec(koto_db, g_strdup_printf("SELECT id, path FROM libraries_tracks WHERE track_id=\"%s\"", track_uuid), process_track_paths, track, NULL); // Process all pathes associated with the track
|
int track_paths = sqlite3_exec(koto_db, g_strdup_printf("SELECT id, path FROM libraries_tracks WHERE track_id=\"%s\"", track_uuid), process_track_paths, track, NULL); // Process all pathes associated with the track
|
||||||
|
|
||||||
if (track_paths != SQLITE_OK) { // Failed to read the paths
|
if (track_paths != SQLITE_OK) { // Failed to read the paths
|
||||||
g_warning("Failed to read paths associated with track %s: %s", track_uuid, sqlite3_errmsg(koto_db));
|
g_warning("Failed to read paths associated with track %s: %s", track_uuid, sqlite3_errmsg(koto_db));
|
||||||
|
g_free(track_uuid);
|
||||||
|
g_free(artist_uuid);
|
||||||
|
g_free(album_uuid);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +279,6 @@ int process_tracks(
|
||||||
g_free(track_uuid);
|
g_free(track_uuid);
|
||||||
g_free(artist_uuid);
|
g_free(artist_uuid);
|
||||||
g_free(album_uuid);
|
g_free(album_uuid);
|
||||||
g_free(name);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ enum {
|
||||||
PROP_ALBUM_NAME,
|
PROP_ALBUM_NAME,
|
||||||
PROP_ART_PATH,
|
PROP_ART_PATH,
|
||||||
PROP_ARTIST_UUID,
|
PROP_ARTIST_UUID,
|
||||||
|
PROP_ALBUM_PREPARED_GENRES,
|
||||||
N_PROPERTIES
|
N_PROPERTIES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -64,6 +65,8 @@ struct _KotoAlbum {
|
||||||
gchar * art_path;
|
gchar * art_path;
|
||||||
gchar * artist_uuid;
|
gchar * artist_uuid;
|
||||||
|
|
||||||
|
GList * genres;
|
||||||
|
|
||||||
GList * tracks;
|
GList * tracks;
|
||||||
GHashTable * paths;
|
GHashTable * paths;
|
||||||
|
|
||||||
|
@ -147,6 +150,14 @@ static void koto_album_class_init(KotoAlbumClass * c) {
|
||||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
props[PROP_ALBUM_PREPARED_GENRES] = g_param_spec_string(
|
||||||
|
"preparsed-genres",
|
||||||
|
"Preparsed Genres",
|
||||||
|
"Preparsed Genres",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_WRITABLE
|
||||||
|
);
|
||||||
|
|
||||||
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
||||||
|
|
||||||
album_signals[SIGNAL_TRACK_ADDED] = g_signal_new(
|
album_signals[SIGNAL_TRACK_ADDED] = g_signal_new(
|
||||||
|
@ -178,6 +189,7 @@ static void koto_album_class_init(KotoAlbumClass * c) {
|
||||||
|
|
||||||
static void koto_album_init(KotoAlbum * self) {
|
static void koto_album_init(KotoAlbum * self) {
|
||||||
self->has_album_art = FALSE;
|
self->has_album_art = FALSE;
|
||||||
|
self->genres = NULL;
|
||||||
self->tracks = NULL;
|
self->tracks = NULL;
|
||||||
self->paths = g_hash_table_new(g_str_hash, g_str_equal);
|
self->paths = g_hash_table_new(g_str_hash, g_str_equal);
|
||||||
}
|
}
|
||||||
|
@ -196,17 +208,34 @@ void koto_album_add_track(
|
||||||
|
|
||||||
gchar * track_uuid = koto_track_get_uuid(track);
|
gchar * track_uuid = koto_track_get_uuid(track);
|
||||||
|
|
||||||
if (g_list_index(self->tracks, track_uuid) == -1) { // Haven't already added the track
|
if (g_list_index(self->tracks, track_uuid) != -1) { // Have added it already
|
||||||
koto_cartographer_add_track(koto_maps, track); // Add the track to cartographer if necessary
|
return;
|
||||||
self->tracks = g_list_insert_sorted_with_data(self->tracks, track_uuid, koto_track_helpers_sort_tracks_by_uuid, NULL);
|
|
||||||
|
|
||||||
g_signal_emit(
|
|
||||||
self,
|
|
||||||
album_signals[SIGNAL_TRACK_ADDED],
|
|
||||||
0,
|
|
||||||
track
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
koto_cartographer_add_track(koto_maps, track); // Add the track to cartographer if necessary
|
||||||
|
self->tracks = g_list_insert_sorted_with_data(self->tracks, track_uuid, koto_track_helpers_sort_tracks_by_uuid, NULL);
|
||||||
|
|
||||||
|
GList * track_genres = koto_track_get_genres(track); // Get the genres for the track
|
||||||
|
GList * current_genre_list;
|
||||||
|
|
||||||
|
gchar * existing_genres_as_string = koto_utils_join_string_list(self->genres, ";");
|
||||||
|
|
||||||
|
for (current_genre_list = track_genres; current_genre_list != NULL; current_genre_list = current_genre_list->next) { // Iterate over each item in the track genres
|
||||||
|
gchar * track_genre = current_genre_list->data; // Get this genre
|
||||||
|
|
||||||
|
if (koto_utils_string_contains_substring(existing_genres_as_string, track_genre)) { // Genres list contains this genre
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->genres = g_list_append(self->genres, g_strdup(track_genre)); // Duplicate the genre and add it to our list
|
||||||
|
}
|
||||||
|
|
||||||
|
g_signal_emit(
|
||||||
|
self,
|
||||||
|
album_signals[SIGNAL_TRACK_ADDED],
|
||||||
|
0,
|
||||||
|
track
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void koto_album_commit(KotoAlbum * self) {
|
void koto_album_commit(KotoAlbum * self) {
|
||||||
|
@ -214,18 +243,23 @@ void koto_album_commit(KotoAlbum * self) {
|
||||||
koto_album_set_album_art(self, ""); // Set to an empty string
|
koto_album_set_album_art(self, ""); // Set to an empty string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gchar * genres_string = koto_utils_join_string_list(self->genres, ";");
|
||||||
|
|
||||||
gchar * commit_op = g_strdup_printf(
|
gchar * commit_op = g_strdup_printf(
|
||||||
"INSERT INTO albums(id, artist_id, name, art_path)"
|
"INSERT INTO albums(id, artist_id, name, art_path, genres)"
|
||||||
"VALUES('%s', '%s', quote(\"%s\"), quote(\"%s\"))"
|
"VALUES('%s', '%s', quote(\"%s\"), quote(\"%s\"), '%s')"
|
||||||
"ON CONFLICT(id) DO UPDATE SET artist_id=excluded.artist_id, name=excluded.name, art_path=excluded.art_path;",
|
"ON CONFLICT(id) DO UPDATE SET artist_id=excluded.artist_id, name=excluded.name, art_path=excluded.art_path, genres=excluded.genres;",
|
||||||
self->uuid,
|
self->uuid,
|
||||||
self->artist_uuid,
|
self->artist_uuid,
|
||||||
self->name,
|
self->name,
|
||||||
self->art_path
|
self->art_path,
|
||||||
|
genres_string
|
||||||
);
|
);
|
||||||
|
|
||||||
new_transaction(commit_op, "Failed to write our album to the database", FALSE);
|
new_transaction(commit_op, "Failed to write our album to the database", FALSE);
|
||||||
|
|
||||||
|
g_free(genres_string);
|
||||||
|
|
||||||
GHashTableIter paths_iter;
|
GHashTableIter paths_iter;
|
||||||
g_hash_table_iter_init(&paths_iter, self->paths); // Create an iterator for our paths
|
g_hash_table_iter_init(&paths_iter, self->paths); // Create an iterator for our paths
|
||||||
gpointer lib_uuid_ptr, album_rel_path_ptr;
|
gpointer lib_uuid_ptr, album_rel_path_ptr;
|
||||||
|
@ -302,6 +336,14 @@ void koto_album_find_album_art(KotoAlbum * self) {
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GList * koto_album_get_genres(KotoAlbum * self) {
|
||||||
|
if (!KOTO_IS_ALBUM(self)) { // Not an album
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self->genres;
|
||||||
|
}
|
||||||
|
|
||||||
static void koto_album_get_property(
|
static void koto_album_get_property(
|
||||||
GObject * obj,
|
GObject * obj,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
|
@ -357,6 +399,9 @@ static void koto_album_set_property(
|
||||||
case PROP_ARTIST_UUID:
|
case PROP_ARTIST_UUID:
|
||||||
koto_album_set_artist_uuid(self, g_value_get_string(val));
|
koto_album_set_artist_uuid(self, g_value_get_string(val));
|
||||||
break;
|
break;
|
||||||
|
case PROP_ALBUM_PREPARED_GENRES:
|
||||||
|
koto_album_set_preparsed_genres(self, g_strdup(g_value_get_string(val)));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
||||||
break;
|
break;
|
||||||
|
@ -573,6 +618,30 @@ void koto_album_set_as_current_playlist(KotoAlbum * self) {
|
||||||
koto_current_playlist_set_playlist(current_playlist, new_album_playlist); // Set our new current playlist
|
koto_current_playlist_set_playlist(current_playlist, new_album_playlist); // Set our new current playlist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void koto_album_set_preparsed_genres(
|
||||||
|
KotoAlbum * self,
|
||||||
|
gchar * genrelist
|
||||||
|
) {
|
||||||
|
if (!KOTO_IS_ALBUM(self)) { // Not an album
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!koto_utils_is_string_valid(genrelist)) { // If it is an empty string
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GList * preparsed_genres_list = koto_utils_string_to_string_list(genrelist, ";");
|
||||||
|
|
||||||
|
if (g_list_length(preparsed_genres_list) == 0) { // No genres
|
||||||
|
g_list_free(preparsed_genres_list);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Do a pass on in first memory optimization phase to ensure string elements are freed.
|
||||||
|
g_list_free_full(self->genres, NULL); // Free the existing genres list
|
||||||
|
self->genres = preparsed_genres_list;
|
||||||
|
};
|
||||||
|
|
||||||
KotoAlbum * koto_album_new(gchar * artist_uuid) {
|
KotoAlbum * koto_album_new(gchar * artist_uuid) {
|
||||||
if (!koto_utils_is_string_valid(artist_uuid)) { // Invalid artist UUID provided
|
if (!koto_utils_is_string_valid(artist_uuid)) { // Invalid artist UUID provided
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -72,12 +72,12 @@ void index_folder(
|
||||||
KotoAlbum * album = koto_album_new(artist_uuid);
|
KotoAlbum * album = koto_album_new(artist_uuid);
|
||||||
|
|
||||||
koto_album_set_path(album, self, full_path);
|
koto_album_set_path(album, self, full_path);
|
||||||
koto_album_commit(album); // Save to database immediately
|
|
||||||
|
|
||||||
koto_cartographer_add_album(koto_maps, album); // Add our album to the cartographer
|
koto_cartographer_add_album(koto_maps, album); // Add our album to the cartographer
|
||||||
koto_artist_add_album(artist, album); // Add the album
|
koto_artist_add_album(artist, album); // Add the album
|
||||||
|
|
||||||
index_folder(self, full_path, depth); // Index inside the album
|
index_folder(self, full_path, depth); // Index inside the album
|
||||||
|
koto_album_commit(album); // Save to database immediately
|
||||||
g_free(artist_name);
|
g_free(artist_name);
|
||||||
} else if (depth == 3) { // Possibly CD within album
|
} else if (depth == 3) { // Possibly CD within album
|
||||||
gchar ** split = g_strsplit(full_path, G_DIR_SEPARATOR_S, -1);
|
gchar ** split = g_strsplit(full_path, G_DIR_SEPARATOR_S, -1);
|
||||||
|
|
|
@ -250,6 +250,11 @@ void koto_album_set_path(
|
||||||
const gchar * fixed_path
|
const gchar * fixed_path
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void koto_album_set_preparsed_genres(
|
||||||
|
KotoAlbum * self,
|
||||||
|
gchar * genrelist
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File / Track Functions
|
* File / Track Functions
|
||||||
**/
|
**/
|
||||||
|
@ -267,13 +272,17 @@ void koto_track_commit(KotoTrack * self);
|
||||||
|
|
||||||
guint koto_track_get_disc_number(KotoTrack * self);
|
guint koto_track_get_disc_number(KotoTrack * self);
|
||||||
|
|
||||||
|
guint64 koto_track_get_duration(KotoTrack * self);
|
||||||
|
|
||||||
|
GList * koto_track_get_genres(KotoTrack * self);
|
||||||
|
|
||||||
GVariant * koto_track_get_metadata_vardict(KotoTrack * self);
|
GVariant * koto_track_get_metadata_vardict(KotoTrack * self);
|
||||||
|
|
||||||
gchar * koto_track_get_path(KotoTrack * self);
|
gchar * koto_track_get_path(KotoTrack * self);
|
||||||
|
|
||||||
gchar * koto_track_get_name(KotoTrack * self);
|
gchar * koto_track_get_name(KotoTrack * self);
|
||||||
|
|
||||||
guint koto_track_get_position(KotoTrack * self);
|
guint64 koto_track_get_position(KotoTrack * self);
|
||||||
|
|
||||||
gchar * koto_track_get_uniqueish_key(KotoTrack * self);
|
gchar * koto_track_get_uniqueish_key(KotoTrack * self);
|
||||||
|
|
||||||
|
@ -305,6 +314,16 @@ void koto_track_set_cd(
|
||||||
guint cd
|
guint cd
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void koto_track_set_duration(
|
||||||
|
KotoTrack * self,
|
||||||
|
guint64 duration
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_set_genres(
|
||||||
|
KotoTrack * self,
|
||||||
|
char * genrelist
|
||||||
|
);
|
||||||
|
|
||||||
void koto_track_set_parsed_name(
|
void koto_track_set_parsed_name(
|
||||||
KotoTrack * self,
|
KotoTrack * self,
|
||||||
gchar * new_parsed_name
|
gchar * new_parsed_name
|
||||||
|
@ -318,7 +337,12 @@ void koto_track_set_path(
|
||||||
|
|
||||||
void koto_track_set_position(
|
void koto_track_set_position(
|
||||||
KotoTrack * self,
|
KotoTrack * self,
|
||||||
guint pos
|
guint64 pos
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_set_preparsed_genres(
|
||||||
|
KotoTrack * self,
|
||||||
|
gchar * genrelist
|
||||||
);
|
);
|
||||||
|
|
||||||
void koto_track_update_metadata(KotoTrack * self);
|
void koto_track_update_metadata(KotoTrack * self);
|
||||||
|
|
|
@ -24,6 +24,25 @@
|
||||||
|
|
||||||
extern KotoCartographer * koto_maps;
|
extern KotoCartographer * koto_maps;
|
||||||
|
|
||||||
|
GHashTable * genre_replacements;
|
||||||
|
|
||||||
|
void koto_track_helpers_init() {
|
||||||
|
genre_replacements = g_hash_table_new(g_str_hash, g_str_equal);
|
||||||
|
|
||||||
|
gchar * corrected_genre_hiphop = g_strdup("hip-hop");
|
||||||
|
gchar * correct_genre_indie = g_strdup("indie");
|
||||||
|
gchar * correct_genre_scifi = g_strdup("sci-fi");
|
||||||
|
|
||||||
|
g_hash_table_insert(genre_replacements, g_strdup("alternative/indie"), correct_genre_indie); // Change Alternative/Indie (lowercased) to indie
|
||||||
|
g_hash_table_insert(genre_replacements, g_strdup("rap-&-hip-hop"), corrected_genre_hiphop); // Change rap-&-hip-hop to just hip-hop
|
||||||
|
g_hash_table_insert(genre_replacements, g_strdup("science-fiction"), correct_genre_scifi); // Change science-fiction to sci-fi
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar * koto_track_helpers_get_corrected_genre(gchar * original_genre) {
|
||||||
|
gchar * lookedup_genre = g_hash_table_lookup(genre_replacements, original_genre); // Look up the genre
|
||||||
|
return koto_utils_is_string_valid(lookedup_genre) ? lookedup_genre : original_genre;
|
||||||
|
}
|
||||||
|
|
||||||
gchar * koto_track_helpers_get_name_for_file(
|
gchar * koto_track_helpers_get_name_for_file(
|
||||||
const gchar * path,
|
const gchar * path,
|
||||||
gchar * optional_artist_name
|
gchar * optional_artist_name
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
|
|
||||||
#include <glib-2.0/glib.h>
|
#include <glib-2.0/glib.h>
|
||||||
|
|
||||||
|
void koto_track_helpers_init();
|
||||||
|
|
||||||
|
gchar * koto_track_helpers_get_corrected_genre(gchar * original_genre);
|
||||||
|
|
||||||
gchar * koto_track_helpers_get_name_for_file(
|
gchar * koto_track_helpers_get_name_for_file(
|
||||||
const gchar * path,
|
const gchar * path,
|
||||||
gchar * optional_artist_name
|
gchar * optional_artist_name
|
||||||
|
|
|
@ -37,8 +37,10 @@ struct _KotoTrack {
|
||||||
|
|
||||||
gchar * parsed_name;
|
gchar * parsed_name;
|
||||||
guint cd;
|
guint cd;
|
||||||
guint position;
|
guint64 position;
|
||||||
guint * playback_position;
|
guint64 duration;
|
||||||
|
guint64 * playback_position;
|
||||||
|
GList * genres;
|
||||||
|
|
||||||
gboolean do_initial_index;
|
gboolean do_initial_index;
|
||||||
};
|
};
|
||||||
|
@ -54,7 +56,9 @@ enum {
|
||||||
PROP_PARSED_NAME,
|
PROP_PARSED_NAME,
|
||||||
PROP_CD,
|
PROP_CD,
|
||||||
PROP_POSITION,
|
PROP_POSITION,
|
||||||
|
PROP_DURATION,
|
||||||
PROP_PLAYBACK_POSITION,
|
PROP_PLAYBACK_POSITION,
|
||||||
|
PROP_PREPARSED_GENRES,
|
||||||
N_PROPERTIES
|
N_PROPERTIES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -133,31 +137,52 @@ static void koto_track_class_init(KotoTrackClass * c) {
|
||||||
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
||||||
);
|
);
|
||||||
|
|
||||||
props[PROP_POSITION] = g_param_spec_uint(
|
props[PROP_POSITION] = g_param_spec_uint64(
|
||||||
"position",
|
"position",
|
||||||
"Position in Audiobook, Album, etc.",
|
"Position in Audiobook, Album, etc.",
|
||||||
"Position in Audiobook, Album, etc.",
|
"Position in Audiobook, Album, etc.",
|
||||||
0,
|
0,
|
||||||
G_MAXUINT16,
|
G_MAXUINT64,
|
||||||
0,
|
0,
|
||||||
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
||||||
);
|
);
|
||||||
|
|
||||||
props[PROP_PLAYBACK_POSITION] = g_param_spec_uint(
|
props[PROP_DURATION] = g_param_spec_uint64(
|
||||||
|
"duration",
|
||||||
|
"Duration of Track",
|
||||||
|
"Duration of Track",
|
||||||
|
0,
|
||||||
|
G_MAXUINT64,
|
||||||
|
0,
|
||||||
|
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
||||||
|
);
|
||||||
|
|
||||||
|
props[PROP_PLAYBACK_POSITION] = g_param_spec_uint64(
|
||||||
"playback-position",
|
"playback-position",
|
||||||
"Current playback position",
|
"Current playback position",
|
||||||
"Current playback position",
|
"Current playback position",
|
||||||
0,
|
0,
|
||||||
G_MAXUINT16,
|
G_MAXUINT64,
|
||||||
0,
|
0,
|
||||||
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
props[PROP_PREPARSED_GENRES] = g_param_spec_string(
|
||||||
|
"preparsed-genres",
|
||||||
|
"Preparsed Genres",
|
||||||
|
"Preparsed Genres",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_WRITABLE
|
||||||
|
);
|
||||||
|
|
||||||
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void koto_track_init(KotoTrack * self) {
|
static void koto_track_init(KotoTrack * self) {
|
||||||
|
self->duration = 0; // Initialize our duration
|
||||||
|
self->genres = NULL; // Initialize our genres list
|
||||||
self->paths = g_hash_table_new(g_str_hash, g_str_equal); // Create our hash table of paths
|
self->paths = g_hash_table_new(g_str_hash, g_str_equal); // Create our hash table of paths
|
||||||
|
self->position = 0; // Initialize our duration
|
||||||
}
|
}
|
||||||
|
|
||||||
static void koto_track_get_property(
|
static void koto_track_get_property(
|
||||||
|
@ -226,10 +251,16 @@ static void koto_track_set_property(
|
||||||
koto_track_set_cd(self, g_value_get_uint(val));
|
koto_track_set_cd(self, g_value_get_uint(val));
|
||||||
break;
|
break;
|
||||||
case PROP_POSITION:
|
case PROP_POSITION:
|
||||||
koto_track_set_position(self, g_value_get_uint(val));
|
koto_track_set_position(self, g_value_get_uint64(val));
|
||||||
break;
|
break;
|
||||||
case PROP_PLAYBACK_POSITION:
|
case PROP_PLAYBACK_POSITION:
|
||||||
self->playback_position = GUINT_TO_POINTER(g_value_get_uint(val));
|
self->playback_position = GUINT_TO_POINTER(g_value_get_uint64(val));
|
||||||
|
break;
|
||||||
|
case PROP_DURATION:
|
||||||
|
koto_track_set_duration(self, g_value_get_uint64(val));
|
||||||
|
break;
|
||||||
|
case PROP_PREPARSED_GENRES:
|
||||||
|
koto_track_set_preparsed_genres(self, g_strdup(g_value_get_string(val)));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
||||||
|
@ -250,9 +281,12 @@ void koto_track_commit(KotoTrack * self) {
|
||||||
self->album_uuid = g_strdup("");
|
self->album_uuid = g_strdup("");
|
||||||
}
|
}
|
||||||
|
|
||||||
gchar * commit_msg = "INSERT INTO tracks(id, artist_id, album_id, name, disc, position)" \
|
gchar * commit_msg = "INSERT INTO tracks(id, artist_id, album_id, name, disc, position, duration, genres)" \
|
||||||
"VALUES('%s', '%s', '%s', quote(\"%s\"), %d, %d)" \
|
"VALUES('%s', '%s', '%s', quote(\"%s\"), %d, %d, %d, '%s')" \
|
||||||
"ON CONFLICT(id) DO UPDATE SET album_id=excluded.album_id, artist_id=excluded.artist_id, name=excluded.name, disc=excluded.disc, position=excluded.position;";
|
"ON CONFLICT(id) DO UPDATE SET album_id=excluded.album_id, artist_id=excluded.artist_id, name=excluded.name, disc=excluded.disc, position=excluded.position, duration=excluded.duration, genres=excluded.genres;";
|
||||||
|
|
||||||
|
// Combine our list items into a semi-colon separated string
|
||||||
|
gchar * genres = koto_utils_join_string_list(self->genres, ";"); // Join our GList of strings into a single
|
||||||
|
|
||||||
gchar * commit_op = g_strdup_printf(
|
gchar * commit_op = g_strdup_printf(
|
||||||
commit_msg,
|
commit_msg,
|
||||||
|
@ -261,13 +295,17 @@ void koto_track_commit(KotoTrack * self) {
|
||||||
self->album_uuid,
|
self->album_uuid,
|
||||||
g_strescape(self->parsed_name, NULL),
|
g_strescape(self->parsed_name, NULL),
|
||||||
(int) self->cd,
|
(int) self->cd,
|
||||||
(int) self->position
|
(int) self->position,
|
||||||
|
(int) self->duration,
|
||||||
|
genres
|
||||||
);
|
);
|
||||||
|
|
||||||
if (new_transaction(commit_op, "Failed to write our file to the database", FALSE) != SQLITE_OK) {
|
if (new_transaction(commit_op, "Failed to write our file to the database", FALSE) != SQLITE_OK) {
|
||||||
g_message("Failed with song: %s", self->parsed_name);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_free(genres); // Free the genres string
|
||||||
|
|
||||||
GHashTableIter paths_iter;
|
GHashTableIter paths_iter;
|
||||||
g_hash_table_iter_init(&paths_iter, self->paths); // Create an iterator for our paths
|
g_hash_table_iter_init(&paths_iter, self->paths); // Create an iterator for our paths
|
||||||
gpointer lib_uuid_ptr, track_rel_path_ptr;
|
gpointer lib_uuid_ptr, track_rel_path_ptr;
|
||||||
|
@ -292,6 +330,14 @@ guint koto_track_get_disc_number(KotoTrack * self) {
|
||||||
return KOTO_IS_TRACK(self) ? self->cd : 1;
|
return KOTO_IS_TRACK(self) ? self->cd : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guint64 koto_track_get_duration(KotoTrack * self) {
|
||||||
|
return KOTO_IS_TRACK(self) ? self->duration : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GList * koto_track_get_genres(KotoTrack * self) {
|
||||||
|
return KOTO_IS_TRACK(self) ? self->genres : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
GVariant * koto_track_get_metadata_vardict(KotoTrack * self) {
|
GVariant * koto_track_get_metadata_vardict(KotoTrack * self) {
|
||||||
if (!KOTO_IS_TRACK(self)) {
|
if (!KOTO_IS_TRACK(self)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -374,7 +420,7 @@ gchar * koto_track_get_path(KotoTrack * self) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
guint koto_track_get_position(KotoTrack * self) {
|
guint64 koto_track_get_position(KotoTrack * self) {
|
||||||
return KOTO_IS_TRACK(self) ? self->position : 0;
|
return KOTO_IS_TRACK(self) ? self->position : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,6 +519,10 @@ void koto_track_set_cd(
|
||||||
KotoTrack * self,
|
KotoTrack * self,
|
||||||
guint cd
|
guint cd
|
||||||
) {
|
) {
|
||||||
|
if (!KOTO_IS_TRACK(self)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (cd == 0) { // No change really
|
if (cd == 0) { // No change really
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -481,10 +531,68 @@ void koto_track_set_cd(
|
||||||
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_CD]);
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_CD]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void koto_track_set_duration(
|
||||||
|
KotoTrack * self,
|
||||||
|
guint64 duration
|
||||||
|
) {
|
||||||
|
if (!KOTO_IS_TRACK(self)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duration == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->duration = duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_set_genres(
|
||||||
|
KotoTrack * self,
|
||||||
|
char * genrelist
|
||||||
|
) {
|
||||||
|
if (!KOTO_IS_TRACK(self)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!koto_utils_is_string_valid((gchar*) genrelist)) { // If it is an empty string
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar ** genres = g_strsplit(genrelist, ";", -1); // Split on semicolons for each genre, e.g. Electronic;Rock
|
||||||
|
guint len = g_strv_length(genres);
|
||||||
|
|
||||||
|
if (len == 0) { // No genres
|
||||||
|
g_strfreev(genres); // Free the list
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (guint i = 0; i < len; i++) { // Iterate over each item
|
||||||
|
gchar * genre = genres[i]; // Get the genre
|
||||||
|
gchar * lowercased_genre = g_utf8_strdown(g_strstrip(genre), -1); // Lowercase the genre
|
||||||
|
|
||||||
|
gchar * lowercased_hyphenated_genre = koto_utils_replace_string_all(lowercased_genre, " ", "-");
|
||||||
|
g_free(lowercased_genre); // Free the lowercase genre string since we no longer need it
|
||||||
|
|
||||||
|
gchar * corrected_genre = koto_track_helpers_get_corrected_genre(lowercased_hyphenated_genre); // Get any corrected genre
|
||||||
|
|
||||||
|
if (g_list_index(self->genres, corrected_genre) == -1) { // Don't have this genre added
|
||||||
|
self->genres = g_list_append(self->genres, g_strdup(corrected_genre)); // Add the genre to our list
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(lowercased_hyphenated_genre); // Free our remaining string
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev(genres); // Free the list of genres locally
|
||||||
|
}
|
||||||
|
|
||||||
void koto_track_set_parsed_name(
|
void koto_track_set_parsed_name(
|
||||||
KotoTrack * self,
|
KotoTrack * self,
|
||||||
gchar * new_parsed_name
|
gchar * new_parsed_name
|
||||||
) {
|
) {
|
||||||
|
if (!KOTO_IS_TRACK(self)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!koto_utils_is_string_valid(new_parsed_name)) {
|
if (!koto_utils_is_string_valid(new_parsed_name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -529,7 +637,7 @@ void koto_track_set_path(
|
||||||
|
|
||||||
void koto_track_set_position(
|
void koto_track_set_position(
|
||||||
KotoTrack * self,
|
KotoTrack * self,
|
||||||
guint pos
|
guint64 pos
|
||||||
) {
|
) {
|
||||||
if (pos == 0) { // No position change really
|
if (pos == 0) { // No position change really
|
||||||
return;
|
return;
|
||||||
|
@ -550,7 +658,11 @@ void koto_track_update_metadata(KotoTrack * self) {
|
||||||
|
|
||||||
if ((t_file != NULL) && taglib_file_is_valid(t_file)) { // If we got the taglib file and it is valid
|
if ((t_file != NULL) && taglib_file_is_valid(t_file)) { // If we got the taglib file and it is valid
|
||||||
TagLib_Tag * tag = taglib_file_tag(t_file); // Get our tag
|
TagLib_Tag * tag = taglib_file_tag(t_file); // Get our tag
|
||||||
|
koto_track_set_genres(self, taglib_tag_genre(tag)); // Set our genres to any genres listed for the track
|
||||||
koto_track_set_position(self, (uint) taglib_tag_track(tag)); // Get the track, convert to uint and cast as a pointer
|
koto_track_set_position(self, (uint) taglib_tag_track(tag)); // Get the track, convert to uint and cast as a pointer
|
||||||
|
|
||||||
|
const TagLib_AudioProperties * tag_props = taglib_file_audioproperties(t_file); // Get the audio properties of the file
|
||||||
|
koto_track_set_duration(self, taglib_audioproperties_length(tag_props)); // Get the length of the track and set it as our duration
|
||||||
} else { // Failed to get tag info
|
} else { // Failed to get tag info
|
||||||
guint64 position = koto_track_helpers_get_position_based_on_file_name(g_path_get_basename(optimal_track_path)); // Get the likely position
|
guint64 position = koto_track_helpers_get_position_based_on_file_name(g_path_get_basename(optimal_track_path)); // Get the likely position
|
||||||
koto_track_set_position(self, position); // Set our position
|
koto_track_set_position(self, position); // Set our position
|
||||||
|
@ -560,6 +672,30 @@ void koto_track_update_metadata(KotoTrack * self) {
|
||||||
taglib_file_free(t_file); // Free the file
|
taglib_file_free(t_file); // Free the file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void koto_track_set_preparsed_genres(
|
||||||
|
KotoTrack * self,
|
||||||
|
gchar * genrelist
|
||||||
|
) {
|
||||||
|
if (!KOTO_IS_TRACK(self)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!koto_utils_is_string_valid(genrelist)) { // If it is an empty string
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GList * preparsed_genres_list = koto_utils_string_to_string_list(genrelist, ";");
|
||||||
|
|
||||||
|
if (g_list_length(preparsed_genres_list) == 0) { // No genres
|
||||||
|
g_list_free(preparsed_genres_list);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Do a pass on in first memory optimization phase to ensure string elements are freed.
|
||||||
|
g_list_free_full(self->genres, NULL); // Free the existing genres list
|
||||||
|
self->genres = preparsed_genres_list;
|
||||||
|
}
|
||||||
|
|
||||||
KotoTrack * koto_track_new(
|
KotoTrack * koto_track_new(
|
||||||
const gchar * artist_uuid,
|
const gchar * artist_uuid,
|
||||||
const gchar * album_uuid,
|
const gchar * album_uuid,
|
||||||
|
|
|
@ -133,6 +133,7 @@ void koto_button_show_image(
|
||||||
);
|
);
|
||||||
|
|
||||||
void koto_button_unflatten(KotoButton * self);
|
void koto_button_unflatten(KotoButton * self);
|
||||||
|
|
||||||
void koto_button_unset_pseudoactive_styling(KotoButton * self);
|
void koto_button_unset_pseudoactive_styling(KotoButton * self);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
|
@ -205,7 +205,7 @@ static void koto_expander_init(KotoExpander * self) {
|
||||||
|
|
||||||
// koto_expander_set_icon_name will set the icon for our inner KotoButton for this Expander
|
// koto_expander_set_icon_name will set the icon for our inner KotoButton for this Expander
|
||||||
void koto_expander_set_icon_name(
|
void koto_expander_set_icon_name(
|
||||||
KotoExpander *self,
|
KotoExpander * self,
|
||||||
gchar * icon
|
gchar * icon
|
||||||
) {
|
) {
|
||||||
if (!KOTO_IS_EXPANDER(self)) { // Not a KotoExpander
|
if (!KOTO_IS_EXPANDER(self)) { // Not a KotoExpander
|
||||||
|
|
|
@ -36,7 +36,7 @@ KotoExpander * koto_expander_new_with_button(
|
||||||
GtkWidget * koto_expander_get_content(KotoExpander * self);
|
GtkWidget * koto_expander_get_content(KotoExpander * self);
|
||||||
|
|
||||||
void koto_expander_set_icon_name(
|
void koto_expander_set_icon_name(
|
||||||
KotoExpander *self,
|
KotoExpander * self,
|
||||||
gchar * icon
|
gchar * icon
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include "koto-utils.h"
|
||||||
|
|
||||||
extern GtkWindow * main_window;
|
extern GtkWindow * main_window;
|
||||||
|
|
||||||
|
@ -102,6 +103,32 @@ gchar * koto_utils_get_filename_without_extension(gchar * filename) {
|
||||||
return stripped_file_name;
|
return stripped_file_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gchar * koto_utils_join_string_list (
|
||||||
|
GList * list,
|
||||||
|
gchar * sep
|
||||||
|
) {
|
||||||
|
gchar * liststring = NULL;
|
||||||
|
GList * cur_list;
|
||||||
|
for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) { // For each item in the list
|
||||||
|
gchar * current_item = cur_list->data;
|
||||||
|
if (!koto_utils_is_string_valid(current_item)) { // Not a valid string
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar * item_plus_sep = g_strdup_printf("%s%s", current_item, sep);
|
||||||
|
|
||||||
|
if (koto_utils_is_string_valid(liststring)) { // Is a valid string
|
||||||
|
gchar * new_string = g_strconcat(liststring, item_plus_sep, NULL);
|
||||||
|
g_free(liststring);
|
||||||
|
liststring = new_string;
|
||||||
|
} else { // Don't have any content yet
|
||||||
|
liststring = item_plus_sep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (liststring == NULL) ? g_strdup("") : liststring;
|
||||||
|
}
|
||||||
|
|
||||||
gboolean koto_utils_is_string_valid(gchar * str) {
|
gboolean koto_utils_is_string_valid(gchar * str) {
|
||||||
return ((str != NULL) && (g_strcmp0(str, "") != 0));
|
return ((str != NULL) && (g_strcmp0(str, "") != 0));
|
||||||
}
|
}
|
||||||
|
@ -123,15 +150,42 @@ gchar * koto_utils_replace_string_all(
|
||||||
gchar * find,
|
gchar * find,
|
||||||
gchar * repl
|
gchar * repl
|
||||||
) {
|
) {
|
||||||
gchar * cleaned_string = "";
|
|
||||||
gchar ** split = g_strsplit(str, find, -1); // Split on find
|
gchar ** split = g_strsplit(str, find, -1); // Split on find
|
||||||
|
|
||||||
for (guint i = 0; i < g_strv_length(split); i++) { // For each split
|
guint split_len = g_strv_length(split);
|
||||||
cleaned_string = g_strjoin(repl, cleaned_string, split[i], NULL); // Join the strings with our replace string
|
|
||||||
|
if (split_len == 1) { // Only one item
|
||||||
|
g_strfreev(split);
|
||||||
|
return g_strdup(str); // Just set to the string we were provided
|
||||||
}
|
}
|
||||||
|
|
||||||
g_strfreev(split);
|
return g_strdup(g_strjoinv(repl, split));
|
||||||
return cleaned_string;
|
}
|
||||||
|
|
||||||
|
gboolean koto_utils_string_contains_substring(
|
||||||
|
gchar * s,
|
||||||
|
gchar * sub
|
||||||
|
) {
|
||||||
|
gchar ** separated_string = g_strsplit(s, sub, -1); // Split on our substring
|
||||||
|
gboolean contains = (g_strv_length(separated_string) > 1);
|
||||||
|
g_strfreev(separated_string);
|
||||||
|
return contains;
|
||||||
|
}
|
||||||
|
|
||||||
|
GList * koto_utils_string_to_string_list(
|
||||||
|
gchar * s,
|
||||||
|
gchar * sep
|
||||||
|
) {
|
||||||
|
GList * list = NULL;
|
||||||
|
gchar ** separated_strings = g_strsplit(s, sep, -1); // Split on separator for the string
|
||||||
|
|
||||||
|
for (guint i = 0; i < g_strv_length(separated_strings); i++) { // Iterate over each item
|
||||||
|
gchar * item = separated_strings[i];
|
||||||
|
list = g_list_append(list, g_strdup(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev(separated_strings); // Free our strings
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
gchar * koto_utils_unquote_string(gchar * s) {
|
gchar * koto_utils_unquote_string(gchar * s) {
|
||||||
|
|
|
@ -34,6 +34,11 @@ gchar * koto_utils_gboolean_to_string(gboolean b);
|
||||||
|
|
||||||
gchar * koto_utils_get_filename_without_extension(gchar * filename);
|
gchar * koto_utils_get_filename_without_extension(gchar * filename);
|
||||||
|
|
||||||
|
gchar * koto_utils_join_string_list(
|
||||||
|
GList * list,
|
||||||
|
gchar * sep
|
||||||
|
);
|
||||||
|
|
||||||
gboolean koto_utils_is_string_valid(gchar * str);
|
gboolean koto_utils_is_string_valid(gchar * str);
|
||||||
|
|
||||||
void koto_utils_mkdir(gchar * path);
|
void koto_utils_mkdir(gchar * path);
|
||||||
|
@ -49,6 +54,16 @@ gchar * koto_utils_replace_string_all(
|
||||||
gchar * repl
|
gchar * repl
|
||||||
);
|
);
|
||||||
|
|
||||||
|
gboolean koto_utils_string_contains_substring(
|
||||||
|
gchar * s,
|
||||||
|
gchar * sub
|
||||||
|
);
|
||||||
|
|
||||||
|
GList * koto_utils_string_to_string_list(
|
||||||
|
gchar * s,
|
||||||
|
gchar * sep
|
||||||
|
);
|
||||||
|
|
||||||
gchar * koto_utils_unquote_string(gchar * s);
|
gchar * koto_utils_unquote_string(gchar * s);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "db/cartographer.h"
|
#include "db/cartographer.h"
|
||||||
#include "db/db.h"
|
#include "db/db.h"
|
||||||
#include "db/loaders.h"
|
#include "db/loaders.h"
|
||||||
|
#include "indexer/track-helpers.h"
|
||||||
#include "playback/engine.h"
|
#include "playback/engine.h"
|
||||||
#include "playback/media-keys.h"
|
#include "playback/media-keys.h"
|
||||||
#include "playback/mimes.h"
|
#include "playback/mimes.h"
|
||||||
|
@ -88,6 +89,7 @@ int main (
|
||||||
gtk_init();
|
gtk_init();
|
||||||
gst_init(&argc, &argv);
|
gst_init(&argc, &argv);
|
||||||
|
|
||||||
|
koto_track_helpers_init(); // Init our track helpers (primarily our genre replacement hashtable)
|
||||||
koto_paths_setup(); // Set up our required paths
|
koto_paths_setup(); // Set up our required paths
|
||||||
|
|
||||||
supported_mimes_hash = g_hash_table_new(g_str_hash, g_str_equal);
|
supported_mimes_hash = g_hash_table_new(g_str_hash, g_str_equal);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue