From cc464ed198250b3b342d27be9dff2223043d6946 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Fri, 21 Apr 2017 10:02:21 -0400 Subject: [PATCH] core/tags/track: Add support for allocating external tracks There might be rare occasions where users want to play a track that exists outside their music library. This patch adds support for allocating and freeing these external tracks without adding them to the track database. Signed-off-by: Anna Schumaker --- core/tags/track.c | 85 +++++++++++++++++++++++++++------------ include/core/tags/track.h | 7 ++++ tests/core/tags/track.c | 55 ++++++++++++++++++++----- 3 files changed, 111 insertions(+), 36 deletions(-) diff --git a/core/tags/track.c b/core/tags/track.c index 949f7d73..ca535d89 100644 --- a/core/tags/track.c +++ b/core/tags/track.c @@ -41,26 +41,18 @@ static struct track *__track_alloc() return track; } - -struct db_entry *track_alloc(const gchar *key, unsigned int index) +static struct track *__track_alloc_filepath(const gchar *filepath) { + TagLib_File *file = taglib_file_new(filepath); const TagLib_AudioProperties *audio; - struct library *library; + struct track *track = NULL; struct artist *artist; struct genre *genre; - struct track *track = NULL; - unsigned int lib_id; - TagLib_File *file; - TagLib_Tag *tag; - char *fullpath, *path; + TagLib_Tag *tag; - sscanf(key, "%u/%m[^\n]", &lib_id, &path); - library = library_get(lib_id); - fullpath = library_file(library, path); - file = taglib_file_new(fullpath); if (!file || !taglib_file_is_valid(file)) { - g_printerr("WARNING: Could not read tags for: %s\n", fullpath); - goto out; + g_printerr("WARNING: Could not read tags for: %s\n", filepath); + return NULL; } track = __track_alloc(); @@ -69,24 +61,50 @@ struct db_entry *track_alloc(const gchar *key, unsigned int index) artist = artist_find(taglib_tag_artist(tag)); genre = genre_find(taglib_tag_genre(tag)); - track->tr_album = album_find(artist, genre, taglib_tag_album(tag), - taglib_tag_year(tag)); - track->tr_library = library; + track->tr_album = album_find(artist, genre, taglib_tag_album(tag), + taglib_tag_year(tag)); - unplayed_count++; track->tr_count = 0; track->tr_length = taglib_audioproperties_length(audio); track->tr_track = taglib_tag_track(tag); date_set(&track->tr_date, 0, 0, 0); - track->tr_path = g_strdup(key); track->tr_title = g_strdup(taglib_tag_title(tag)); track->tr_tokens = g_str_tokenize_and_fold(track->tr_title, NULL, &track->tr_alts); taglib_tag_free_strings(); taglib_file_free(file); -out: + return track; +} + +static void __track_free(struct track *track) +{ + g_strfreev(track->tr_tokens); + g_strfreev(track->tr_alts); + g_free(track->tr_title); + g_free(track); +} + + +struct db_entry *track_alloc(const gchar *key, unsigned int index) +{ + struct library *library; + char *fullpath, *path; + struct track *track; + unsigned int lib_id; + + sscanf(key, "%u/%m[^\n]", &lib_id, &path); + library = library_get(lib_id); + fullpath = library_file(library, path); + track = __track_alloc_filepath(fullpath); + + if (track) { + track->tr_library = library; + track->tr_path = g_strdup(key); + unplayed_count++; + } + g_free(path); g_free(fullpath); return track ? &track->tr_dbe : NULL; @@ -100,10 +118,7 @@ static void track_free(struct db_entry *dbe) if (track->tr_count == 0) unplayed_count--; - g_strfreev(track->tr_tokens); - g_strfreev(track->tr_alts); - g_free(track->tr_title); - g_free(track); + __track_free(track); } static gchar *track_key(struct db_entry *dbe) @@ -251,6 +266,24 @@ unsigned int track_db_average_plays() return play_count / (track_db.db_size - unplayed_count); } +struct track *track_alloc_external(const gchar *filepath) +{ + struct track *track = __track_alloc_filepath(filepath); + if (track) { + track->tr_library = NULL; + track->tr_path = g_strdup(filepath); + } + return track; +} + +void track_free_external(struct track *track) +{ + if (TRACK_IS_EXTERNAL(track)) { + g_free(track->tr_path); + __track_free(track); + } +} + struct track *track_add(struct library *library, const gchar *filepath) { unsigned int offset = strlen(library->li_path) + 1; @@ -346,11 +379,13 @@ gchar *track_path(struct track *track) g_free(path); return res; } - return g_strdup(""); + return g_strdup(track->tr_path); } void track_played(struct track *track) { + if (TRACK_IS_EXTERNAL(track)) + return; if (track->tr_count == 0) unplayed_count--; track->tr_count++; diff --git a/include/core/tags/track.h b/include/core/tags/track.h index 51116861..85d81b3e 100644 --- a/include/core/tags/track.h +++ b/include/core/tags/track.h @@ -58,6 +58,7 @@ struct track { }; #define TRACK(dbe) ((struct track *)DBE_DATA(dbe)) +#define TRACK_IS_EXTERNAL(track) (track->tr_library == NULL) /* Called to initialize the track database. */ @@ -87,6 +88,12 @@ unsigned int track_db_count_plays(); /* Called to find the average play count of tracks in the database. */ unsigned int track_db_average_plays(); +/* Called to allocate a track without adding it to the database. */ +struct track *track_alloc_external(const gchar *); + +/* Called to free an external track. */ +void track_free_external(struct track *); + /* Called to add a track tag to the database. */ struct track *track_add(struct library *, const gchar *); diff --git a/tests/core/tags/track.c b/tests/core/tags/track.c index 82f2bed4..56773085 100644 --- a/tests/core/tags/track.c +++ b/tests/core/tags/track.c @@ -17,27 +17,38 @@ static struct track *test_alloc(const gchar *key) return TRACK(dbe); } -static void test_verify_tags(struct track *track) +static void test_verify_tags(struct track *track, bool external) { g_assert_cmpstr(track->tr_album->al_name, ==, "Hyrule Symphony"); g_assert_cmpuint(track->tr_album->al_year, ==, 1998); g_assert_cmpstr(track->tr_album->al_artist->ar_name, ==, "Koji Kondo"); g_assert_cmpstr(track->tr_album->al_genre->ge_name, ==, "Game"); - g_assert_cmpstr(track->tr_library->li_path, ==, "tests/Music"); + + if (external) { + g_assert_true(TRACK_IS_EXTERNAL(track)); + g_assert_null(track->tr_library); + } else { + g_assert_false(TRACK_IS_EXTERNAL(track)); + g_assert_cmpstr(track->tr_library->li_path, ==, "tests/Music"); + } } -static void test_verify_track(struct track *track) +static void test_verify_track(struct track *track, bool external) { const struct db_ops *track_ops = test_track_ops(); - test_verify_tags(track); + + if (!external) + track_free_external(track); + test_verify_tags(track, external); g_assert_cmpstr(track->tr_title, ==, "Title Theme"); g_assert_cmpstr(track->tr_tokens[0], ==, "title"); g_assert_cmpstr(track->tr_tokens[1], ==, "theme"); g_assert_null(track->tr_tokens[2]); g_assert_null(track->tr_alts[0]); - g_assert_cmpstr(track_ops->dbe_key(&track->tr_dbe), ==, - "0/Hyrule Symphony/01 - Title Theme.ogg"); + if (!external) + g_assert_cmpstr(track_ops->dbe_key(&track->tr_dbe), ==, + "0/Hyrule Symphony/01 - Title Theme.ogg"); g_assert_cmpstr_free(track_path(track), ==, "tests/Music/Hyrule Symphony/01 - Title Theme.ogg"); g_assert_cmpstr_free(track_last_play(track), ==, "Never"); @@ -50,7 +61,7 @@ static void test_verify_track(struct track *track) static void test_verify_notrack(struct track *track) { const struct db_ops *track_ops = test_track_ops(); - test_verify_tags(track); + test_verify_tags(track, false); g_assert_cmpstr(track->tr_title, ==, ""); g_assert_null(track->tr_tokens[0]); @@ -82,7 +93,7 @@ static void test_track() track = test_alloc("0/Hyrule Symphony/01 - Title Theme.ogg"); g_assert_nonnull(track); track->tr_dbe.dbe_index = 0; - test_verify_track(track); + test_verify_track(track, false); g_assert_true( track_match_token(track, "title")); g_assert_true( track_match_token(track, "theme")); @@ -107,7 +118,7 @@ static void test_track() track = TRACK(track_ops->dbe_read(&f, 0)); track->tr_dbe.dbe_index = 0; - test_verify_track(track); + test_verify_track(track, false); file_close(&f); track_played(track); @@ -119,6 +130,27 @@ static void test_track() g_free(date); } +static void test_track_external() +{ + struct track *track = track_alloc_external( + "tests/Music/Hyrule Symphony/01 - Title Theme.ogg"); + + g_assert_nonnull(track); + test_verify_track(track, true); + g_assert_cmpuint(track_db_get()->db_size, ==, 0); + g_assert_cmpuint(track_db_count_unplayed(), ==, 0); + + track_played(track); + g_assert_cmpuint(track_db_count_unplayed(), ==, 0); + g_assert_cmpuint(track_db_count_plays(), ==, 0); + + track_free_external(track); + g_assert_cmpuint(track_db_count_unplayed(), ==, 0); + g_assert_cmpuint(track_db_count_plays(), ==, 0); + + g_assert_null(track_alloc_external("/home/Zelda/Music/ocarina.ogg")); +} + static void test_track_compare() { const struct db_ops *track_ops = test_track_ops(); @@ -215,7 +247,7 @@ static void __test_track_db_subprocess() g_assert_null(track_lookup(path)); track = track_add(library, path); - test_verify_track(track); + test_verify_track(track, false); g_assert(track_lookup(path) == track); g_assert_cmpuint(track_db_count_unplayed(), ==, 1); @@ -232,7 +264,7 @@ static void __test_track_db_subprocess() track_db_commit(); db_load(&track_db); g_assert_cmpuint(track_db.db_size, ==, 1); - test_verify_track(TRACK(db_first(&track_db))); + test_verify_track(TRACK(db_first(&track_db)), false); /* Make sure our unplayed count isn't skewed */ db_deinit(&track_db); @@ -314,6 +346,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_test_add_func("/Core/Tags/Track", test_track); + g_test_add_func("/Core/Tags/Track/External", test_track_external); g_test_add_func("/Core/Tags/Track/Comparison", test_track_compare); g_test_add_func("/Core/Tags/Track/Database", test_track_db); ret = g_test_run();