From 371b988dac10622f298916f077576b5b593709d7 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Wed, 1 Jan 2014 13:34:25 -0500 Subject: [PATCH] library: Various updates - Combine Artist and Genre struct - Add in exceptions, rather than boolean returns - Update the library through idle tasks I have not implemented importing Ocarina 5.11 libraries yet. Signed-off-by: Anna Schumaker --- design.txt | 2 +- design/library.txt | 2 +- include/library.h | 34 +++------ lib/Sconscript | 2 +- lib/library.cpp | 145 +++++++++++++++++++------------------ tests/library/library.cpp | 51 +++++++++++-- tests/library/library.good | 8 +- 7 files changed, 137 insertions(+), 107 deletions(-) diff --git a/design.txt b/design.txt index c1d82b59..8ce3b951 100644 --- a/design.txt +++ b/design.txt @@ -558,7 +558,7 @@ Library: (lib/library.cpp) - Album: class library :: Album : public DatabaseEntry { public: - /* primary_key = "$year$name" */ + /* primary_key = "$name.$year.$artist_id" */ string name; string name_lower; unsigned int year; diff --git a/design/library.txt b/design/library.txt index 87e31f23..26974593 100644 --- a/design/library.txt +++ b/design/library.txt @@ -31,7 +31,7 @@ Library: (lib/library.cpp) - Album: class library :: Album : public DatabaseEntry { public: - /* primary_key = "$year$name" */ + /* primary_key = "$name.$year.$artist_id" */ string name; string name_lower; unsigned int year; diff --git a/include/library.h b/include/library.h index cbb2e059..562a9ad5 100644 --- a/include/library.h +++ b/include/library.h @@ -22,12 +22,13 @@ namespace library }; - class Artist : public DatabaseEntry { + class AGInfo : public DatabaseEntry { public: - std :: string name; + DB_Type db_type; + std :: string key_lower; - Artist(); - Artist(TagLib :: Tag *); + AGInfo(); + AGInfo(DB_Type, TagLib :: Tag *); void read(File &); void write(File &); #ifdef CONFIG_DEBUG @@ -39,6 +40,7 @@ namespace library class Album : public DatabaseEntry { public: std :: string name; + std :: string name_lower; unsigned int year; unsigned int artist_id; @@ -52,20 +54,6 @@ namespace library }; - class Genre : public DatabaseEntry { - public: - std:: string name; - - Genre(); - Genre(TagLib :: Tag *); - void read(File &); - void write(File &); -#ifdef CONFIG_DEBUG - void print(); -#endif /* CONFIG_DEBUG */ - }; - - class Library : public DatabaseEntry { public: std::string root_path; @@ -97,6 +85,7 @@ namespace library bool banned; std :: string title; + std :: string title_lower; std :: string length_str; std :: string filepath; @@ -114,18 +103,19 @@ namespace library struct Song { library :: Album *album; - library :: Artist *artist; - library :: Genre *genre; + library :: AGInfo *artist; + library :: AGInfo *genre; library :: Library *library; library :: Track *track; }; void init(); - bool add_path(const std::string &); + void add_path(const std::string &); void del_path(unsigned int); void update_path(unsigned int); - bool lookup(unsigned int, library :: Song *); + void update_all(); + void lookup(unsigned int, library :: Song *); #ifdef CONFIG_DEBUG void print_db(DB_Type); void reset(); diff --git a/lib/Sconscript b/lib/Sconscript index 6215d222..38328264 100644 --- a/lib/Sconscript +++ b/lib/Sconscript @@ -21,7 +21,7 @@ modules = { "FILE" : Module("file.cpp", package = "glib-2.0"), "FILTER" : Module("filter.cpp", depends = [ "DATABASE" ]), "IDLE" : Module("idle.cpp"), - "LIBRARY" : Module("library.cpp", package = "taglib", depends = [ "DATABASE", "IDLE" ]), + "LIBRARY" : Module("library.cpp", package = "taglib", depends = [ "DATABASE", "FILTER", "IDLE" ]), "PLAYLIST" : Module("playlist.cpp", depends = [ "DATABASE" ]), "PLAYQUEUE" : Module("playlist.cpp", depends = [ "FILE" ]), diff --git a/lib/library.cpp b/lib/library.cpp index f2e2dc76..088d51ea 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -1,17 +1,20 @@ /* * Copyright 2013 (c) Anna Schumaker. */ +#include +#include #include #include #include #include -static Database artist_db("artist.db"); -static Database album_db("album.db"); -static Database genre_db("genre.db"); +static Database album_db("album.db"); +static Database artist_db("artist.db"); +static Database genre_db("genre.db"); +static Database track_db("track.db"); + static Database library_db("library.db"); -static Database track_db("track.db"); @@ -19,31 +22,40 @@ static Database track_db("track.db"); * library :: Artist: Artist tag information */ -library :: Artist :: Artist() - : name("") +library :: AGInfo :: AGInfo() { } -library :: Artist :: Artist(TagLib :: Tag *tag) - : name(tag->artist().stripWhiteSpace().to8Bit(true)) +library :: AGInfo :: AGInfo(DB_Type type, TagLib :: Tag *tag) + : db_type(type) { - primary_key = name; + if (db_type == DB_ARTIST) + primary_key = tag->artist().stripWhiteSpace().to8Bit(true); + else if (db_type == DB_GENRE) + primary_key = tag->genre().stripWhiteSpace().to8Bit(true); + else + throw -EINVAL; + + key_lower = filter :: to_lowercase(primary_key); } -void library :: Artist :: read(File &f) +void library :: AGInfo :: read(File &f) { - name = f.getline(); + primary_key = f.getline(); } -void library :: Artist :: write(File &f) +void library :: AGInfo :: write(File &f) { - f << name; + f << primary_key; } #ifdef CONFIG_DEBUG -void library :: Artist :: print() +void library :: AGInfo :: print() { - :: print("Artist: %s", name.c_str()); + if (db_type == DB_ARTIST) + :: print("Artist: %s", primary_key.c_str()); + else + :: print("Genre: %s", primary_key.c_str()); } #endif /* CONFIG_DEBUG */ @@ -63,8 +75,9 @@ library :: Album :: Album(TagLib :: Tag *tag, unsigned int artist) year(tag->year()), artist_id(artist) { std::stringstream ss; - ss << name << "." << year << "." << artist_id; + ss << artist_id << "." << name << "." << year; primary_key = ss.str(); + name_lower = filter :: to_lowercase(name); } void library :: Album :: read(File &f) @@ -81,41 +94,7 @@ void library :: Album :: write(File &f) #ifdef CONFIG_DEBUG void library :: Album :: print() { - :: print("Album: %s (%u) by %s", name.c_str(), year, artist_db[artist_id].name.c_str()); -} -#endif /* CONFIG_DEBUG */ - - - -/* - * library :: Genre: Genre tag information - */ - -library :: Genre :: Genre() - : name("") -{ -} - -library :: Genre :: Genre(TagLib :: Tag *tag) - : name(tag->genre().stripWhiteSpace().to8Bit(true)) -{ - primary_key = name; -} - -void library :: Genre :: read(File &f) -{ - name = f.getline(); -} - -void library :: Genre :: write(File &f) -{ - f << name; -} - -#ifdef CONFIG_DEBUG -void library :: Genre :: print() -{ - :: print("Genre: %s", name.c_str()); + :: print("Album: %s (%u) by %s", name.c_str(), year, artist_db[artist_id].primary_key.c_str()); } #endif /* CONFIG_DEBUG */ @@ -177,8 +156,20 @@ library :: Track :: Track(TagLib :: Tag *tag, TagLib :: AudioProperties *audio, play_count(0), length(audio->length()), banned(false), title(tag->title().stripWhiteSpace().to8Bit(true)) { + std::stringstream ss; + unsigned int minutes, seconds; + primary_key = path; filepath = path.substr(library_db[library_id].root_path.size() + 1); + title_lower = filter :: to_lowercase(title); + + minutes = length / 60; + seconds = length % 60; + ss << minutes << ":"; + if (seconds < 10) + ss << "0"; + ss << seconds; + length_str = ss.str(); } void library :: Track :: read(File &f) @@ -194,7 +185,7 @@ void library :: Track :: write(File &f) { f << library_id << " " << artist_id << " " << album_id << " " << genre_id; f << " " << track << " " << last_year << " " << last_month << " " << last_day; - f << " " << play_count << " " << length << " " << banned << std :: endl; + f << " " << play_count << " " << length << " " << length_str << std :: endl; f << title << std :: endl; f << filepath; } @@ -203,10 +194,10 @@ void library :: Track :: write(File &f) void library :: Track :: print() { :: print("%u. %s by %s from %s (%u)\n", track, title.c_str(), - artist_db[artist_id].name.c_str(), + artist_db[artist_id].primary_key.c_str(), album_db[album_id].name.c_str(), album_db[album_id].year); :: print(" Genre: %s, Length: %u (seconds)\n", - genre_db[genre_id].name.c_str(), length); + genre_db[genre_id].primary_key.c_str(), length); :: print(" Play count: %u, last played %u/%u/%u\n", play_count, last_day, last_month, last_year); :: print(" %s", filepath.c_str()); @@ -218,7 +209,11 @@ void library :: Track :: print() /* * Internal library functions */ -static void do_scan_path(unsigned int, const std :: string &); +struct scan_info { + unsigned int lib_id; + std :: string path; +}; +static void do_scan_path(struct scan_info &); static void read_tags(unsigned int lib_id, const std :: string &path) { @@ -236,9 +231,9 @@ static void read_tags(unsigned int lib_id, const std :: string &path) audio = ref.audioProperties(); - artist_id = artist_db.insert(library :: Artist(tag)); + artist_id = artist_db.insert(library :: AGInfo(library :: DB_ARTIST, tag)); album_id = album_db.insert(library :: Album(tag, artist_id)); - genre_id = genre_db.insert(library :: Genre(tag)); + genre_id = genre_db.insert(library :: AGInfo(library :: DB_GENRE, tag)); track_db.insert(library :: Track(tag, audio, lib_id, artist_id, album_id, genre_id, path)); } @@ -246,11 +241,14 @@ static void read_tags(unsigned int lib_id, const std :: string &path) static void process_path(unsigned int lib_id, const std :: string &dir, const std :: string &name) { + struct scan_info scan; std :: string path = dir + "/" + name; - if (g_file_test(path.c_str(), G_FILE_TEST_IS_DIR) == true) - do_scan_path(lib_id, path); - else + if (g_file_test(path.c_str(), G_FILE_TEST_IS_DIR) == true) { + scan.lib_id = lib_id; + scan.path = path; + idle :: schedule (do_scan_path, scan); + } else read_tags(lib_id, path); } @@ -262,24 +260,24 @@ static void save_all_dbs() track_db.save(); } -static void do_scan_path(unsigned int lib_id, const std :: string &path) +static void do_scan_path(struct scan_info &scan) { GDir *dir; const char *name; - dir = g_dir_open(path.c_str(), 0, NULL); + dir = g_dir_open(scan.path.c_str(), 0, NULL); if (dir == NULL) return; name = g_dir_read_name(dir); while (name != NULL) { - process_path(lib_id, path, name); + process_path(scan.lib_id, scan.path, name); save_all_dbs(); name = g_dir_read_name(dir); } } -static void do_validate_library(unsigned int lib_id) +static void do_validate_library(unsigned int &lib_id) { std :: string path; @@ -300,8 +298,9 @@ static void do_validate_library(unsigned int lib_id) static void do_update_library(unsigned int lib_id) { - do_validate_library(lib_id); - do_scan_path(lib_id, library_db[lib_id].root_path); + struct scan_info scan = { lib_id, library_db[lib_id].root_path }; + idle :: schedule(do_validate_library, lib_id); + idle :: schedule(do_scan_path, scan); } @@ -315,16 +314,15 @@ void library :: init() library_db.load(); } -bool library :: add_path(const std::string &dir) +void library :: add_path(const std::string &dir) { unsigned int id; if (g_file_test(dir.c_str(), G_FILE_TEST_IS_DIR) == false) - return false; + throw -EINVAL; id = library_db.insert(library :: Library(dir, true)); library_db.save(); update_path(id); - return true; } void library :: del_path(unsigned int id) @@ -342,16 +340,19 @@ void library :: update_path(unsigned int id) do_update_library(id); } -bool library :: lookup(unsigned int id, library :: Song *song) +void library :: lookup(unsigned int id, library :: Song *song) { if (id >= track_db.num_rows()) - return false; + throw -EEXIST; + song->track = &track_db[id]; + if (song->track->valid == false) + throw -EEXIST; + song->artist = &artist_db[song->track->artist_id]; song->album = &album_db[song->track->album_id]; song->genre = &genre_db[song->track->genre_id]; song->library = &library_db[song->track->library_id]; - return true; } #ifdef CONFIG_DEBUG diff --git a/tests/library/library.cpp b/tests/library/library.cpp index 889385db..c704c377 100644 --- a/tests/library/library.cpp +++ b/tests/library/library.cpp @@ -1,6 +1,7 @@ /* * Copyright 2013 (c) Anna Schumaker. */ +#include #include #include @@ -12,13 +13,30 @@ void gen_library() print("\n"); } +void run_idle_tasks() +{ + while (idle :: run_task()); +} + +bool check_add_dir(const std::string &dir) +{ + try { + library :: add_path(dir.c_str()); + return true; + } catch (int error) { + return false; + } +} + void test_add_dir(const std::string &test, const std::string &dir, bool expected) { print("Test %s: ", test.c_str()); - if (library :: add_path(dir.c_str()) == expected) + if (check_add_dir(dir.c_str()) == expected) print("PASSED\n"); else print("FAILED\n"); + + run_idle_tasks(); library :: print_db(library :: DB_LIBRARY); } @@ -29,6 +47,16 @@ void test_del_dir(const std::string &test, const unsigned int path_id) library :: print_db(library :: DB_LIBRARY); } +bool check_lookup(const unsigned int track_id, library :: Song *song) +{ + try { + library :: lookup(track_id, song); + return true; + } catch (int error) { + return false; + } +} + void test_lookup(const std::string &test, const unsigned int track_id, bool expected) { library :: Song song; @@ -36,17 +64,25 @@ void test_lookup(const std::string &test, const unsigned int track_id, bool expe print("Test %s (track_id == %u): ", test.c_str(), track_id); - res = library :: lookup(track_id, &song); + res = check_lookup(track_id, &song); if (res == expected) print("PASSED\n"); else print("FAILED\n"); if (res == true) { - print(" %s by %s from %s\n", song.track->title.c_str(), - song.artist->name.c_str(), song.album->name.c_str()); - print(" Genre: %s, Library: %s\n", song.genre->name.c_str(), - song.library->root_path.c_str()); + print(" %s (%s) by %s (%s) from %s (%s) Length: %s\n", + song.track->title.c_str(), + song.track->title_lower.c_str(), + song.artist->primary_key.c_str(), + song.artist->key_lower.c_str(), + song.album->name.c_str(), + song.album->name_lower.c_str(), + song.track->length_str.c_str()); + print(" Genre: %s (%s), Library: %s\n", + song.genre->primary_key.c_str(), + song.genre->key_lower.c_str(), + song.library->root_path.c_str()); } } @@ -150,12 +186,14 @@ void test_6() print("6b: Updating library 0 (nothing should change)\n"); library :: update_path(0); + run_idle_tasks(); library :: print_db(library :: DB_TRACK); print("\n"); print("6c: Delete /tmp/library/0/Artist 2\n"); system("rm -rf /tmp/library/0/Artist\\ 2/"); library :: update_path(0); + run_idle_tasks(); library :: print_db(library :: DB_TRACK); print("\n"); @@ -164,6 +202,7 @@ void test_6() gen_library(); fflush(stdout); library :: update_path(0); + run_idle_tasks(); library :: print_db(library :: DB_TRACK); } diff --git a/tests/library/library.good b/tests/library/library.good index 6afea029..604ebe2d 100644 --- a/tests/library/library.good +++ b/tests/library/library.good @@ -717,11 +717,11 @@ Allocated rows: 1 Valid rows: 1 db[0] = /tmp/library/0 (enabled) Test 5c (track_id == 0): PASSED - Track 10 by Artist 2 from Album 2 - Genre: Tryout, Library: /tmp/library/0 + Track 10 (track 10) by Artist 2 (artist 2) from Album 2 (album 2) Length: 1:00 + Genre: Tryout (tryout), Library: /tmp/library/0 Test 5d (track_id == 42): PASSED - Track 8 by Artist 4 from Album 1 - Genre: Trial, Library: /tmp/library/0 + Track 8 (track 8) by Artist 4 (artist 4) from Album 1 (album 1) Length: 0:01 + Genre: Trial (trial), Library: /tmp/library/0 Test 5e (track_id == 100000): PASSED Test 6a: PASSED