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 <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2017-04-21 10:02:21 -04:00
parent f9a573b6a3
commit cc464ed198
3 changed files with 111 additions and 36 deletions

View File

@ -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++;

View File

@ -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 *);

View File

@ -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();