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:
parent
8d8e9262d6
commit
371b988dac
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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" ]),
|
||||
|
||||
|
|
145
lib/library.cpp
145
lib/library.cpp
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue