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 <schumaker.anna@gmail.com>
This commit is contained in:
Anna Schumaker 2014-01-01 13:34:25 -05:00 committed by Anna Schumaker
parent 8d8e9262d6
commit 371b988dac
7 changed files with 137 additions and 107 deletions

View File

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

View File

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

View File

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

View File

@ -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" ]),

View File

@ -1,17 +1,20 @@
/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <filter.h>
#include <idle.h>
#include <library.h>
#include <print.h>
#include <glib.h>
#include <sstream>
static Database<library :: Artist> artist_db("artist.db");
static Database<library :: Album> album_db("album.db");
static Database<library :: Genre> genre_db("genre.db");
static Database<library :: Album> album_db("album.db");
static Database<library :: AGInfo> artist_db("artist.db");
static Database<library :: AGInfo> genre_db("genre.db");
static Database<library :: Track> track_db("track.db");
static Database<library :: Library> library_db("library.db");
static Database<library :: Track> track_db("track.db");
@ -19,31 +22,40 @@ static Database<library :: Track> 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

View File

@ -1,6 +1,7 @@
/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <idle.h>
#include <library.h>
#include <print.h>
@ -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);
}

View File

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