tagdb: Remove tagdb

This shifts the taglib code into library.cpp.  I also remove the tagdb
section of the DESIGN document.

Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
Anna Schumaker 2014-12-02 08:24:36 -05:00
parent c7fe5b18d5
commit e7e36caa3a
10 changed files with 37 additions and 460 deletions

56
DESIGN
View File

@ -33,7 +33,7 @@ Core Layers:
* Library (include/core/library.h)
* Queue (include/core/queue.h)
* RNG (include/core/random.h)
* Tag database (include/core/tags.h)
* Tags (include/core/tags/*.h)
* Idle queue (include/core/idle.h)
* Filter (include/core/filter.h)
* Index (include/core/index.h)
@ -66,60 +66,6 @@ Callbacks:
Tag Database:
The tag database is actually several databases that describe every
track added by the user. I do not expect the artist_db, album_db,
genre_db and library_db to change often, so they can be created as
autosaving databases that will write to disk whenever they are changed.
The track_db can have several additions and removals in a row, so the
commit() function is used to control when this db is written to disk,
avoiding the huge performance hit that would come with saving on EVERY
change.
Tags are defined in the sections below.
- Databases:
Database<Artist> artist_db;
Database<Album> album_db;
Database<Genre> genre_db;
Database<Library> library_db;
Database<Track> track_db;
- API:
void tagdb :: init();
Load all databases from disk.
void tagdb :: commit();
Write track_db to disk.
Track *tagdb :: add_track(const std::string &filepath, Library *library);
Add a new track to the track_db and return a pointer to it.
Return NULL if this track is already in the database or if
there is an error when tagging.
Library *tagdb :: add_library(const std::string &filepath);
Add a new path to library_db. Return a pointer to the new path
or return NULL if the path is already in the database.
void tagdb :: remove_track(unsigned int track_id);
Remove the track with id equal to track_id from the track_db.
void tagdb :: remove_library(unsigned int library_id);
Remove all tracks associated with this library from the
track_db, then remove this library from the library_db.
Track *tagdb :: lookup(unsigned int track_id);
Look up the track_id in the track database. Return NULL if
there is no matching track.
Database<Track> &tagdb :: get_track_db();
Return a reference to the track_db.
Database<Library> &tagdb :: get_library_db();
Return a reference to the library_db.
Track Tag:
The track tag is used to store information about a single track in the
user's music collection.

View File

@ -4,8 +4,11 @@
*/
#include <core/idle.h>
#include <core/library.h>
#include <core/print.h>
#include <glib.h>
#include <taglib/tag.h>
#include <taglib/fileref.h>
class LibraryQueue : public Queue {
@ -76,6 +79,33 @@ static void scan_path(struct scan_info &);
/*
* Scanning functions are here
*/
static void tag_track(Library *library, const std::string &filepath)
{
Track *track;
TagLib :: Tag *tag;
TagLib :: AudioProperties *audio;
TagLib :: FileRef ref(filepath.c_str(), true, TagLib::AudioProperties::Fast);
if (ref.isNull()) {
print("WARNING: Could not read tags for file %s\n", filepath.c_str());
return;
}
tag = ref.tag();
audio = ref.audioProperties();
track = tags :: add_track(
tags :: get_album(tag->album().stripWhiteSpace().to8Bit(true), tag->year()),
tags :: get_artist(tag->artist().stripWhiteSpace().to8Bit(true)),
tags :: get_genre(tag->genre().stripWhiteSpace().to8Bit(true)),
library, filepath,
tag->title().stripWhiteSpace().to8Bit(true),
audio->length(), tag->track()
);
if (track)
library_q.add(track);
}
static void process_path(Library *library, const std :: string &dir,
const std :: string &name)
@ -87,11 +117,8 @@ static void process_path(Library *library, const std :: string &dir,
if (g_file_test(scan.path.c_str(), G_FILE_TEST_IS_DIR) == true)
idle :: schedule (scan_path, scan);
else {
Track *track = tagdb :: add_track(scan.path, library);
if (track)
library_q.add(track);
}
else
tag_track(library, scan.path);
}
static void scan_path(struct scan_info &scan)

View File

@ -1,44 +0,0 @@
/**
* @file
* Copyright 2014 (c) Anna Schumaker.
*/
#include <core/tags.h>
#include <core/print.h>
#include <taglib/tag.h>
#include <taglib/fileref.h>
/**
*
* Tagdb functions
*
*/
Track *tagdb :: add_track(const std::string &filepath, Library *library)
{
Track *track;
TagLib :: Tag *tag;
TagLib :: AudioProperties *audio;
TagLib :: FileRef ref(filepath.c_str(), true, TagLib::AudioProperties::Fast);
if (ref.isNull()) {
print("WARNING: Could not read tags for file %s\n", filepath.c_str());
return NULL;
}
tag = ref.tag();
audio = ref.audioProperties();
track = tags :: add_track(
tags :: get_album(tag->album().stripWhiteSpace().to8Bit(true), tag->year()),
tags :: get_artist(tag->artist().stripWhiteSpace().to8Bit(true)),
tags :: get_genre(tag->genre().stripWhiteSpace().to8Bit(true)),
library, filepath,
tag->title().stripWhiteSpace().to8Bit(true),
audio->length(), tag->track()
);
return track;
}

View File

@ -6,7 +6,6 @@
#define OCARINA_CORE_LIBRARY_H
#include <core/queue.h>
#include <core/tags.h>
#include <string>

View File

@ -6,7 +6,7 @@
#define OCARINA_CORE_QUEUE_H
#include <core/file.h>
#include <core/tags.h>
#include <core/tags/track.h>
#include <vector>
#include <list>

View File

@ -1,26 +0,0 @@
/**
* @file
* Copyright 2014 (c) Anna Schumaker.
*/
#ifndef OCARINA_CORE_TAGS_H
#define OCARINA_CORE_TAGS_H
#include <core/tags/library.h>
#include <core/tags/track.h>
/** Functions for accessing the tag database */
namespace tagdb
{
/**
* Add a track to the database
* @param filepath Filepath to be added
* @param library Library containing the new track
* @return A pointer to the newly created track
*/
Track *add_track(const std::string &, Library *);
}
#endif /* OCARINA_CORE_TAGS_H */

View File

@ -3,7 +3,7 @@
*/
#include <core/library.h>
#include <core/playlist.h>
#include <core/tags.h>
#include <core/tags/track.h>
#include <lib/colmgr.h>
#include <lib/lib.h>
#include <gtkmm.h>

View File

@ -19,17 +19,14 @@ test( "tags/artist" )
test( "tags/album" )
test( "tags/genre" )
test( "tags/library" )
test_env.UsePackage("taglib")
objs += [ get_test_obj("tags", "core") ]
objs += [ get_test_obj("tags/tags", "core") ]
test( "tags/track" )
#test( "tags" )
test( "random" )
objs += [ get_test_obj("callback", "core") ]
objs += [ get_test_obj("tags/tags", "core") ]
test( "queue" )
test_env.UsePackage("taglib")
test( "library" )
test( "playlist" )
test( "deck" )

View File

@ -1,321 +0,0 @@
/*
* Copyright 2014 (c) Anna Schumaker.
* Test a DatabaseEntry
*/
#include <core/tags.h>
#include <tests/test.h>
static Library *LIB_NULL = NULL;
static Track *TRACK_NULL = NULL;
struct TagArgs {
struct Library *library;
std::string full_path;
std::string artist;
std::string artist_lower;
std::string album;
std::string album_lower;
unsigned int year;
std::string genre;
std::string genre_lower;
unsigned int track;
unsigned int length;
unsigned int play_count;
unsigned int last_year;
unsigned int last_month;
unsigned int last_day;
std::string title;
std::string title_lower;
std::string filepath;
std::string length_str;
};
static void test_library()
{
Library *lib = tagdb :: add_library("tests/Music/");
test_not_equal(lib, LIB_NULL);
test_equal(lib->root_path, (std::string)"tests/Music/");
test_equal(lib->count, (unsigned)0);
test_equal(lib->enabled, true);
test_equal(tagdb :: add_library("tests/Music/"), LIB_NULL);
test_equal(lib, tagdb :: lookup_library(0));
tagdb :: remove_library(0);
test_equal(tagdb :: get_library_db().size(), (unsigned)0);
}
static void test_track(struct TagArgs *args)
{
Track *track = tagdb :: add_track(args->full_path, args->library);
test_not_equal(track, TRACK_NULL);
test_equal(track->path(), args->full_path);
/*
* Check tags
*/
test_equal(track->artist->name, args->artist);
test_equal(track->artist->lower, args->artist_lower);
test_equal(track->album->name, args->album);
test_equal(track->album->lower, args->album_lower);
test_equal(track->album->year, args->year);
test_equal(track->genre->name, args->genre);
test_equal(track->genre->lower, args->genre_lower);
test_equal(track->track, args->track);
test_equal(track->length, args->length);
test_equal(track->play_count, args->play_count);
test_equal(track->last_year, args->last_year);
test_equal(track->last_month, args->last_month);
test_equal(track->last_day, args->last_day);
test_equal(track->title, args->title);
test_equal(track->title_lower, args->title_lower);
test_equal(track->filepath, args->filepath);
test_equal(track->length_str, args->length_str);
test_equal(tagdb :: lookup(args->track - 1), track);
test_equal(args->library->count, (unsigned)1);
tagdb :: remove_track(args->track - 1);
test_equal(tagdb :: lookup(args->track - 1), TRACK_NULL);
test_equal(tagdb :: get_track_db().size(), (unsigned)0);
test_equal(args->library->count, (unsigned)0);
/*
* Mark track played, double check new values
*/
track->played();
test_equal(track->play_count, args->play_count + 1);
test_not_equal(track->last_year, args->last_year);
test_not_equal(track->last_month, args->last_month);
test_not_equal(track->last_day, args->last_day);
}
static void test_invalid_track(Library *lib)
{
unsigned int library_size = lib->count;
Track *track = tagdb :: add_track("tests/Music/invalid_track", lib);
test_equal(track, TRACK_NULL);
test_equal(lib->count, library_size);
}
static void test_all_tracks()
{
struct TagArgs expected;
Library *library = tagdb :: add_library("tests/Music");
expected.library = library;
expected.full_path = "tests/Music/1.ogg";
expected.artist = "Artist";
expected.artist_lower = "artist";
expected.album = "Album";
expected.album_lower = "album";
expected.year = 2014;
expected.genre = "Silence";
expected.genre_lower = "silence";
expected.track = 1;
expected.length = 1;
expected.play_count = 0;
expected.last_year = 0;
expected.last_month = 0;
expected.last_day = 0;
expected.title = "One";
expected.title_lower = "one";
expected.filepath = "1.ogg";
expected.length_str = "0:01";
run_test("Tags Track Test (1.ogg)", test_track, &expected);
expected.full_path = "tests/Music/10.ogg";
expected.track = 2;
expected.length = 10;
expected.title = "Ten";
expected.title_lower = "ten";
expected.filepath = "10.ogg";
expected.length_str = "0:10";
run_test("Tags Track Test (10.ogg)", test_track, &expected);
expected.full_path = "tests/Music/15.ogg";
expected.track = 3;
expected.length = 15;
expected.title = "Fifteen";
expected.title_lower = "fifteen";
expected.filepath = "15.ogg";
expected.length_str = "0:15";
run_test("Tags Track Test (15.ogg)", test_track, &expected);
expected.full_path = "tests/Music/60.ogg";
expected.track = 4;
expected.length = 60;
expected.title = "Sixty";
expected.title_lower = "sixty";
expected.filepath = "60.ogg";
expected.length_str = "1:00";
run_test("Tags Track Test (60.ogg)", test_track, &expected);
expected.full_path = "tests/Music/90.ogg";
expected.album = "Album Two";
expected.album_lower = "album two";
expected.track = 5;
expected.length = 90;
expected.title = "Ninety";
expected.title_lower = "ninety";
expected.filepath = "90.ogg";
expected.length_str = "1:30";
run_test("Tags Track Test (90.ogg)", test_track, &expected);
expected.full_path = "tests/Music/600.ogg";
expected.track = 6;
expected.length = 600;
expected.title = "Six Hundred";
expected.title_lower = "six hundred";
expected.filepath = "600.ogg";
expected.length_str = "10:00";
run_test("Tags Track Test (600.ogg)", test_track, &expected);
expected.full_path = "tests/Music/666.ogg";
expected.track = 7;
expected.length = 666;
expected.title = "Six-Six-Six";
expected.title_lower = "six six six";
expected.filepath = "666.ogg";
expected.length_str = "11:06";
run_test("Tags Track Test (666.ogg)", test_track, &expected);
run_test("Tags Track Test (Invalid)", test_invalid_track, library);
}
static void test_comparison()
{
Library *lib = tagdb :: lookup_library(1);
Track *track1 = tagdb :: add_track("tests/Music/1.ogg", lib);
Track *track10 = tagdb :: add_track("tests/Music/10.ogg", lib);
Track *track15 = tagdb :: add_track("tests/Music/15.ogg", lib);
Track *track60 = tagdb :: add_track("tests/Music/60.ogg", lib);
Track *track90 = tagdb :: add_track("tests/Music/90.ogg", lib);
Track *track600 = tagdb :: add_track("tests/Music/600.ogg", lib);
Track *track666 = tagdb :: add_track("tests/Music/666.ogg", lib);
Artist art2("");
track10->artist = &art2;
test_equal(track1->less_than(track10, SORT_ARTIST), -1);
test_equal(track10->less_than(track1, SORT_ARTIST), 1);
Artist art3("Artist Three");
track10->artist = &art3;
test_equal(track1->less_than(track1, SORT_ARTIST), 0);
test_equal(track1->less_than(track10, SORT_ARTIST) < 0, true);
test_equal(track10->less_than(track1, SORT_ARTIST) > 0, true);
test_equal(track1->less_than(track1, SORT_ALBUM), 0);
test_equal(track1->less_than(track90, SORT_ALBUM) < 0, true);
test_equal(track90->less_than(track1, SORT_ALBUM) > 0, true);
track15->play_count++;
test_equal(track1->less_than(track1, SORT_COUNT), 0);
test_equal(track1->less_than(track15, SORT_COUNT) < 0, true);
test_equal(track15->less_than(track1, SORT_COUNT) > 0, true);
Genre gen2("X-Treme Silence!!!");
track60->genre = &gen2;
test_equal(track1->less_than(track1, SORT_GENRE), 0);
test_equal(track1->less_than(track60, SORT_GENRE) < 0, true);
test_equal(track60->less_than(track1, SORT_GENRE) > 0, true);
test_equal(track1->less_than(track1, SORT_LENGTH), 0);
test_equal(track1->less_than(track600, SORT_LENGTH) < 0, true);
test_equal(track600->less_than(track1, SORT_LENGTH) > 0, true);
track15->last_year = 2014;
test_equal(track1->less_than(track1, SORT_PLAYED), 0);
test_equal(track1->less_than(track15, SORT_PLAYED) < 0, true);
test_equal(track15->less_than(track1, SORT_PLAYED) > 0, true);
track1->last_year = 2014;
track15->last_month = 5;
test_equal(track1->less_than(track15, SORT_PLAYED) < 0, true);
test_equal(track15->less_than(track1, SORT_PLAYED) > 0, true);
track1->last_month = 5;
track15->last_day = 6;
test_equal(track1->less_than(track15, SORT_PLAYED) < 0, true);
test_equal(track15->less_than(track1, SORT_PLAYED) > 0, true);
test_equal(track1->less_than(track1, SORT_TITLE), 0);
test_equal(track1->less_than(track666, SORT_TITLE) < 0, true);
test_equal(track666->less_than(track1, SORT_TITLE) > 0, true);
test_equal(track1->less_than(track1, SORT_TRACK), 0);
test_equal(track1->less_than(track60, SORT_TRACK) < 0, true);
test_equal(track60->less_than(track1, SORT_TRACK) > 0, true);
track666->album->year = 2048;
test_equal(track1->less_than(track1, SORT_YEAR), 0);
test_equal(track1->less_than(track666, SORT_YEAR) < 0, true);
test_equal(track666->less_than(track1, SORT_YEAR) > 0, true);
}
static void test_lib_removal()
{
test_equal(tagdb :: get_track_db().size(), (unsigned)7);
test_equal(tagdb :: lookup_library(1)->count, (unsigned)7);
tagdb :: remove_library(1);
test_equal(tagdb :: get_track_db().size(), (unsigned)0);
}
static void test_save_load()
{
Database<Artist> artist("artist.db", false);
Database<Album> album("album.db", false);
Database<Genre> genre("genre.db", false);
artist.load();
album.load();
genre.load();
test_equal(artist.size(), (unsigned)1);
test_equal(album.size(), (unsigned)2);
test_equal(genre.size(), (unsigned)1);
Database<Library> library("library.db", false);
library.load();
test_equal(library.size(), (unsigned)0);
Library *lib = tagdb :: add_library("tests/Music");
library.load();
test_equal(library.size(), (unsigned)1);
Database<Library> library2("library.db", false);
lib->enabled = false;
tagdb :: commit_library();
library2.load();
test_equal(library2.at(2)->enabled, false);
tagdb :: add_track("tests/Music/1.ogg", lib);
tagdb :: add_track("tests/Music/15.ogg", lib);
tagdb :: add_track("tests/Music/60.ogg", lib);
Database<Track> track("track.db", false);
track.load();
test_equal(track.size(), (unsigned)0);
tagdb :: commit();
track.load();
test_equal(track.size(), (unsigned)3);
}
int main(int argc, char **argv)
{
run_test("Tags Library Test", test_library);
test_all_tracks();
run_test("Tags Comparison Test", test_comparison);
run_test("Tags Library Removal Test", test_lib_removal);
run_test("Tags Save and Load Test", test_save_load);
return 0;
}

View File

@ -3,7 +3,6 @@
*/
#include <core/idle.h>
#include <core/library.h>
#include <core/tags.h>
#include <lib/colmgr.h>
#include <lib/lib.h>
#include <tests/test.h>