diff --git a/.gitignore b/.gitignore index 8956b56d..6698338a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,14 @@ *.o *.sw* -*.run -*.test +*.out *.glade~ share/ocarina/#* bin/ .sconsign.dblite *.patch *.tar.gz +tests/*-core +tests/*-lib +*.gcov +*.gcda +*.gcno diff --git a/DESIGN b/DESIGN index 79d3121f..b503a520 100644 --- a/DESIGN +++ b/DESIGN @@ -1,16 +1,16 @@ =============================================================================== = = -= Ocarina 6.0 = += Ocarina 6.1 = = = =============================================================================== -Ocarina 6.0 is the 6th implementation of the Ocarina music player - a +Ocarina 6.1 is the 6th implementation of the Ocarina music player - a lightweight, GTK+ based music player with all the features that I want. Improvements over the 5.x series will include the existence of both a design document (this file) and unit tests. This should make maintenance easier and help me stay focused. -Ocarina 6.0 will use Gstreamer 1.0 for audio playback, GTK-MM 3 for user +Ocarina 6.1 will use Gstreamer 1.0 for audio playback, GTK-MM 3 for user interface development, and Taglib for extracting tags. @@ -33,19 +33,6 @@ API: -Errors: - This file contains an enum defining error codes used throughout the - codebase. Errors will be reported using "throw" and "catch". - -Error Codes: - enum error_t { - E_AUDIO = 1, /* Audio error */ - E_EXIST, /* Requested object does not exist */ - E_INVAL, /* Invalid operation requested */ - }; - - - Printing: Sometimes text needs to be printed to the screen so users (or debuggers) can trace what is going on. @@ -87,9 +74,7 @@ On-disk files: in a subdirectory of $XDG_DATA_HOME. The filse class will support reading and writing files in the users - $XDG_CONFIG_HOME/ocarina{-debug|test}. In addition, Ocarina 6.0 will - support reading library files from the Ocarina 5.10 directory: - $HOME/.ocarina{-debug}. + $XDG_CONFIG_HOME/ocarina{-debug|test}. Items should be written to a file with either a space or new line separating multiple values. @@ -97,16 +82,6 @@ On-disk files: - Notation: "File << aaaaa << bbbbb << endl" is translated into "aaaaa bbbbb\n" -- File version: - #define FILE_VERSION 0 - -- Hint where the file is located: - enum FileLocHint { - FILE_TYPE_DATA, - FILE_TYPE_LEGACY, - FILE_TYPE_INVALID, - } - - Open mode: enum OpenMode { OPEN_READ, @@ -118,14 +93,14 @@ On-disk files: class File : public std::fstream { private: unsigned int version; + unsigned int prev_version; OpenMode mode; - FileLocHint hint; - string filepath; + string filename; public: - File(string, FileLocHint); + File(const std::string &, unsigned int); ~File(); - const char *get_filepath(); + const std::string get_filepath(); const unsigned int get_version(); bool exists(); void open(OpenMode); @@ -138,26 +113,27 @@ On-disk files: File << ; - API: - File :: File(string filepath, FileLocHint hint); - Resolve filepath to one of: - XDG_DATA_HOME/ocarina/filepath - XDG_DATA_HOME/ocarina-debug/filepath - XDG_DATA_HOME/ocarina-test/filepath - $HOME/.ocarina/ - $HOME/.ocarina-debug/ - $HOME/.ocarina-test/ - - If filepath is an empty string, set the file hint to - FILE_TYPE_INVALID and do not set the filepath field. + File :: File(const std::string &filename, unsigned int version); + Store the name of the file and the file version. File :: ~File(); Close the file stream if it is open. - const char *File :: get_filepath(); - Return the full filepath to the file. + const std::string File :: get_filepath(); + If filename == "": + return "" + + Otherwise, resolve filepath to one of: + XDG_DATA_HOME/ocarina/filepath + XDG_DATA_HOME/ocarina-debug/filepath + XDG_DATA_HOME/ocarina-test/filepath + + and return the result. const unsigned int File :: get_version(); - Return the file version number. + if mode == OPEN_READ: + return prev_version + return version bool File :: exists(); Return true if the file exists in the filesystem. @@ -165,17 +141,16 @@ On-disk files: bool File :: open(OpenMode mode); Return false if: - - hint == FILE_TYPE_INVALID + - filename == "" - mode == NOT_OPEN - The file is already open When opening a file for reading (mode == OPEN_READ), - Return false if the file does not exist - Open the file - - Read in version from the start of the file + - Read in prev_version from the start of the file When opening a file for writing (mode == OPEN_WRITE), - - Return false if the file has FILE_TYPE_LEGACY set - Create missing directories as needed - Write version information to the start of the file @@ -199,12 +174,11 @@ Database Entry: - DatabaseEntry: class DatabaseEntry { public: - bool valid; unsigned int id; DatabaseEntry(); virtual void ~DatabaseEntry() = 0; - virtual const std::string primary_key() = 0; + virtual const std::string primary_key() const = 0; virtual void write(File &) = 0; virtual void read(File &) = 0; @@ -212,9 +186,9 @@ Database Entry: - API: DatabaseEntry :: DatabaseEntry(): - Set valid = false and id = 0. + Set id = 0. - const std::string DatabaseEntry :: primary_key(); + const std::string DatabaseEntry :: primary_key() const; This function should return a unique string representing this DatabaseEntry instance, which will be used to prevent duplicates in a database. This string is not expected to @@ -251,12 +225,6 @@ Database: uniqueness. This key is used when inserting a new value into the Database, and will not be updated after. -- Valid bit: - The "valid" bit of a DatabaseEntry is completely managed by the entry's - Database container. It will be set to true when an entry is inserted - and false when deleted. The Database is also in charge of writing the - valid bit to file. - - Id: The "id" field of a DatabaseEntry is also managed by the entry's Database container It will be set to the entry's ID when the entry is @@ -266,21 +234,22 @@ Database: template class Database { private: - std::vector _db; + std::vector _db; std::map _keys; unsigned int _size; /* Number of valid rows */ bool _autosave; File _file; public: - typedef std::vector::iterator iterator; - typedef std::vector::const_iterator const_iterator; + typedef std::vector::iterator iterator; + typedef std::vector::const_iterator const_iterator; Database(std::string, bool); + ~Database(); void save(); void load(); - unsigned int insert(T); + T *insert(const T &); void remove(unsigned int); unsigned int size(); unsigned int actual_size(); @@ -289,8 +258,8 @@ Database: iterator end(); iterator next(iterator &); - iterator at(unsigned int); - iterator find(const std::string &); + T *at(unsigned int); + T *find(const std::string &); }; - File format: @@ -305,28 +274,32 @@ Database: data on disk. If the file already exists, read the data into the backing vector. + Database :: ~Database(); + Delete all entries remaining in the database. + void Database :: save(); Save the database to disk. void Database :: load(); Load the database from disk. - unsigned int Database :: insert(T &item); + T *Database :: insert(const T &item); Look up the item in the _keys map. If we find an item with the same key: - - Return the index of the item to the caller. + - Return NULL. Otherwise: + - Use new to allocate memory for a new item. - Add the new item to the end of the _db. - Add the new item to _keys. - - Set item.valid = true. - - Set item.id to the index of the new item. + - Set new item.id to the index of the new item. _ Increment _size. - If autosave == true: save(). - - Return item.id. + - Return a pointer to the new item. - unsigned int Database :: remove(); + unsigned int Database :: remove(unsigned int id); - Remove the item from the _keys map. - - Set item.valid = false. + - Delete _db[id] + - Set _db[id] = NULL - If autosave == true: save(). - Decrement _size. @@ -350,17 +323,17 @@ Database: Return the next DatabaseEntry with valid == true or _db.end() if there are no valid entries left. - iterator Database :: at(unsigned int i); - If _db[i].valid == true: - Return an iterator pointing to _db[i]; + T *Database :: at(unsigned int i); + If i is beyond the end of the container: + Return NULL Otherwise: - Return _db.end(); + Return _db[i]; - iterator Database :: find(const std::string &key); + T *Database :: find(const std::string &key); If key is in the _keys map: - Return an iterator pointing to the corresponding entry. + Return a pointer to the corresponding entry. Otherwise: - Return _db.end(); + Return NULL; @@ -375,7 +348,7 @@ Index: set values; IndexEntry(const std::string &); - const std::string primary_key(); + const std::string primary_key() const; void insert(unsigned int); void remove(unsigned int); @@ -390,7 +363,7 @@ Index: IndexEntry :: IndexEntry(); Creat an empty IndexEntry. - std::string IndexEntry :: primary_key(); + std::string IndexEntry :: primary_key() const; return key; void IndexEntry :: insert(unsigned int value); @@ -528,15 +501,603 @@ Idle queue: -Playlists: (lib/playlist.cpp) +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_db; + Database album_db; + Database genre_db; + Database library_db; + Database 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. + + 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 &tagdb :: get_track_db(); + Return a reference to the track_db. + + Database &tagdb :: get_library_db(); + Return a reference to the library_db. + + + +Artist Tag: + The arist tag is used to collect basic information about the various + artists that have been added to the library. + +- Artist: + class Artist : public DatabaseEntry { + public: + std::string name; + std::string lower; + + Artist(); + Artist(const std::string &); + const std::string primary_key() const; + void read(File &); + void write(File &); + }; + +- File Format: + File << name; + +- API: + Artist(); + Initialize an invalid Artist instance. + + Artist(const std::string &artist_name); + Set artist_name and find the lowercase form. + + const std::string Artist :: primary_key() const; + Use artist name as primary key. + + void Artist :: read(File &f); + Read artist name from file and find the lowercase form. + + void Artist :: write(File &f); + Write artist name to file. + + + +Album Tag: + The album tag is used to collect information about each artist's albums. + +- Album: + class Album : public DatabaseEntry { + public: + std::string name; + std::string lower; + unsigned int year; + + Album(); + Album(const std::string &, unsigned int); + const std::string primary_key() const; + void read(File &); + void write(File &); + }; + +- File Format: + File << year << name; + +- API: + Album(); + Initialize an invalid Album instance. + + Album(const std::string &album_name, unsigned int album_year); + Set name and year from album name and album_year. Find the + lowercase form of the album name. + + const std::string Album :: primary_key() const; + Return the string: "$year.$name" + + void Album :: read(File &f); + Read year, and name from file. Then set the lowercase form + of the album name. + + void Artist :: write(File &f); + Write album information to file. + + + +Genre Tag: + The genre tag is used to collect basic information about the various + genres of songs in the library. + +- Genre: + class Genre : public DatabaseEntry { + public: + std::string genre; + std::string lower; + + Genre(); + Genre(const std::string &); + const std::string primary_key() const; + void read(File &); + void write(File &); + }; + +- File Format: + File << name; + +- API: + Genre(); + Initialize an invalid Genre instance. + + Genre(const std::string &genre_name); + Set genre from genre name and find the lowercase form. + + const std::string Genre :: primary_key() const; + Use genre as primary key. + + void Genre :: read(File &f); + Read genre from file and find the lowercase form. + + void Genre :: write(File &f); + Write genre to file. + + + +Library Tag: + The library tag is used to store a single directory added to Ocarina + by the user. It is not an ID3 tag, and is instead something I use + internally to keep track of paths added by the user. The count field + will be managed by the Track tag class. + +- Library: + class Library : public DatabaseEntry { + public: + std::string root_path; + unsigned int count; + bool enabled; + + Library(); + Library(const std::string &); + const std::string primary_key() const; + void read(File &); + void write(File &); + }; + +- File Format: + File << enabled << root_path + +- API: + Library(); + Initialize an invalid Library instance. + Set count = 0. + Set enabled = false. + + Library(const std::string &path); + Set root_path from the provided path. + Set count = 0. + Set enabled = true. + + const std::string Library :: primary_key() const; + Use root_path as the primary key, + + void read(File &f); + Read a library path from file. + + void write(File &f); + Write a library path to file. + + + +Track Tag: + The track tag is used to store information about a single track in the + user's music collection. + +- Sorting: + enum sort_t { + SORT_ARTIST, + SORT_ALBUM, + SORT_COUNT, + SORT_GENRE, + SORT_LENGTH, + SORT_PLAYED, + SORT_TITLE, + SORT_TRACK, + SORT_YEAR, + }; + +- Track: + class Track : public DatabaseEntry { + public: + Library *library; + Artist *artist; + Album *album; + Genre *genre; + + 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; + + Track(); + Track(const std::string &, Library *); + const std::string primary_key() const; + void read(File &); + void write(File &); + + void tag(); + const std::string path(); + void played(); + bool less_than(Track *, sort_t); + }; + +- File Format: + File << library->id << artist->id << album->id << genre->id << track; + File << last_year << last_month << last_day << play_count << length; + File << title << endl; + File << filepath << endl; + +- API: + Track(); + Initialize an invalid Track instance. + + Track(const std::string &full_path, Library *lib); + This function will only set up the primary key, and the tag() + function must be called to find audio tags. + + - Strip library path from the beginning of full_path and use + the result to set filepath. + - Set library = lib. + + const std::string Track :: primary_key() const; + return path(); + + void read(File &f); + Read track information from file. Look up the corresponding + Library, Artist, Album and Genre pointers. Add title to the + filter and find the string version of length. + + void write(File &f); + Write track information to file. + + void Track :: tag(); + Use TagLib to find tags and audio properties for this file. + + - Insert Artist, Album, and Genre into their databases and + set the corresponding pointers. + - Find title, track number, and length. + - Set play_count, last_year, last_month and last_day = 0. + - Set lowercase title and find the string form of length. + + const std::string Track :: path(); + Combine library->path and filepath to find the full path to + the audio file. + + void Track :: played(); + Called when a track has been played. Increment the play count + and set last_day, last_month, and last_year to today's date. + + Call tagdb :: commit() to save the track database. + + int Track :: less_than(Track *rhs, sort_t field); + Compare the requested field for this Track instance to the same + field in the provided track. + + Return -1 if this < rhs. + Return 0 if this == rhs. + Return 1 if this > rhs. + + + +Random: + The random number generator is used by the Queue to pick a song as + randomly as possible. I use two different RNG implementations, + depending on if Ocarina is compiled with CONFIG_TEST or not. + + When CONFIG_TEST is enabled, the RNG is a simple counter. Every time + _pick_random() is called, the counter is incremented and returned. + When compiled without CONFIG_TEST we will return values using the + rand() function from stdlib.h. + +- API: + void random_seed(unsigned int n); + Seed the random number generator based on n. + + void random(unsigned int min, unsigned int max); + Return a random number between min and max, inclusive. + If min >= max: return min. + + + +Queue: + Queues are lists of songs that the user has requested to play. They + are the main interface for all music played by Ocarina. + +- Flags: + enum queue_flag { + Q_ENABLED (1 << 0), + Q_RANDOM (1 << 1), + Q_REPEAT (1 << 2), + Q_NO_SORT (1 << 3), + }; + +- Sort info: + struct sort_info { + sort_t field; + bool ascending; + }; + +- Sorting: + Sorting is done using std::stable_sort() to make sure that orders won't + change unexpectedly. + +- Queue: + class Queue { + protected: + vector _tracks; + list _sort_order; + unsigned int _cur; + unsigned int _flags; + unsigned int _length; + + public: + Queue(); + Queue(unsigned int); + void read(File &); + void write(File &); + + void set_flag(queue_flag); + void unset_flag(queue_flag); + bool has_flag(queue_flag); + + unsigned int add(Track *); + void del(Track *); + void del(unsigned int); + void updated(Track *); + Track *next(); + + unsigned int size(); + const std::string size_str(); + const std::string length_str(); + + void sort(sort_t, bool); + Track *operator[](unsigned int); + void track_selected(unsigned int); + }; + +File Format: + File << flags << tracks.size() << tracks[0] << tracks[1] << ... << tracks[N]; + +- API + Queue :: Queue(); + Initialize _flags = 0, _cur = -1, _length = 0, and empty sort + order. + + Queue :: Queue(unsigned int flags); + Initialize _flags = flags, _cur = -1, _length = 0, and empty + sort order. + + void Queue :: read(File &f); + Read queue from file. + + void Queue :: write(File &f); + Write queue to file. + + void Queue :: set_flag(queue_flag f); + Set the appropriate flag. + + void Queue :: unset_flag(queue_flag f); + Unset the appropriate flag. + + bool Queue :: has_flag(queue_flag f); + Return true if the queue has the flag enabled and false + otherwise. + + unsigned int Queue :: add(Track *track); + Add a new track to the tracks vector and return the index. + Increase length by the length of the track. + + void Queue :: del(Track *track); + Remove all instances of the requested track from the queue. + + void Queue :: del(unsigned int queue_id); + Remove the track at the given index from the queue. + + void Queue :: updated(Track *track); + Find all indexes of the updated track and notify the UI that + it has changed. + + Track *Queue :: next(); + Return the next track to play. + + if (tracks.size() == 0) + return NULL; + + if (flags & PL_RANDOM): + _cur += rand() % tracks.size(); + else: + _cur += 1; + + if (_cur >= tracks.size()) + _cur -= tracks.size(); + track = tracks[_cur]; + + if (!(flags & PL_REPEAT)): + del(_cur); + return track; + + unsigned int Queue :: size(); + Return the number of tracks currently on the queue. + + const std::string Queue :: size_str(); + Return the number of tracks currently on the queue, in string + form. + + const std::string Queue :: length_str(); + Return the remaining length of the queue in a human-readable + format. + + void Queue :: sort(sort_t field, bool reset); + If the field is already in the sort order, toggle its + ascending value. Otherwise, add a new sort field to the end + of the sort order with ascending set to true. If reset is set + to true, clear the sorting list before appending. + + Track *Queue :: operator[](unsigned int i); + Return the track and index i. + + void Queue :: track_selected(unsigned int queue_id); + Set _cur to queue_id. If PQ_REPEAT is not set, remove the + track from the queue. + + + +Library: + The library is in charge of scanning and updating library paths added + to the tag database. In addition, the library layer is also in charge + of managing a library queue used by the UI. This queue has a special + file format, and will be saved to the file "library.q". + +- Queue: + class LibraryQueue : public Queue { + public: + LibraryQueue(); + save(); + load(); + set_flag(queue_flag); + unset_flag(queue_flag); + sort(sort_t, bool); + }; + + File << flags << _sort_order.size() + File << _sort_order[N].field << _sort_order[N].ascending << ... + +- Validation: + Use a single idle function to loop over each track in the track + database. Check if the track still exists in the filesystem and remove + it from the tagdb if not. + +- Updating: + Scan over all files in the current directory directory. + For each encountered directory: + Use the idle queue to call the update function with the new + directory as the "current" directory. + For each encountered file: + Attempt to add the file to the track_db. + Commit the database if at least one new file has been added. + +- Testing: + The script tests/library/gen_library.sh will create a sample library + in the /tmp/ directory for testing purposes. All the track files are + complete silence, but the script will fake up tags for each file. + + To test importing, create several mock library files and copy them to + ~/.ocarina-test/library/ and attempt to read them in. + +- LibraryQueue API: + LibraryQueue :: LibraryQueue(); + Initialize a Queue with the flags Q_ENABLED and Q_REPEAT. The + default sorting order should be artist, year, track. + + LibraryQueue :: save(); + Write a library queue to disk. + + LibraryQueue :: load(); + Read a library queue from disk. + + LibraryQueue :: set_flag(queue_flag f); + LibraryQueue :: unset_flag(queue_flag f); + LibraryQueue :: sort(sort_t field, bool reset); + These functions are wrappers around the default Queue + implementation. First call the original function, then use + save() to store the changes. + +- API + void library :: init(); + Scan the tagdb track list, and add each track to the library + queue. + + Library *library :: add(string dir); + If dir is not a directory: + return NULL + + Add a new path to the tag database, trigger an update, and + then return the corresponding Library tag to the caller. + + void library :: remove(Library *library); + Invalidate a library_db row and all tracks owned by that path. + Do not use the library pointer after calling this function. + + void library :: update(Library *library); + First, validate all tracks in the given library. + Next, trigger an update on the given library. + + void library :: update_all(); + Update all valid library paths. + + void library :: set_enabled(Library *library, bool enabled); + Toggle if a library path is enabled or not. A disabled + library path will have its tracks removed from the + LibraryQueue. + + Queue *library :: get_queue(); + Return the LibraryQueue to the caller. + + + +Playlist: Playlists are a new feature in Ocarina 6 and are modeled after Gmail - labels. Ocarina 6.0 will support two different playlists that the + labels. Ocarina 6.1 will support two different playlists that the user can add tracks to: banned and favorites. + The playlist layer will maintain a queue that is used by the UI to + display tracks in a given playlist. This queue is inherited from + the base Queue class to provide extra features. + Future releases will add support for more playlists. -- Database: - Database playlist_db +- Index: + Index playlist_db("playlist.db", true); + +- Queue: + class PlaylistQueue : public Queue { + public: + PlaylistQueue(); + fill(IndexEntry *); + }; - Default playlists: Favorites: @@ -548,535 +1109,331 @@ Playlists: (lib/playlist.cpp) are banned and added back to the playqueue when they are un-banned. +- PlaylistQueue API: + PlaylistQueue :: PlaylistQueue(); + Initialize a Queue with the flags Q_ENABLED, Q_REPEAT, and + Q_NO_SORT set. Default sorting order should be artist, year, + track. + + PlaylistQueue :: fill(IndexEntry *ent); + Remove all tracks in the queue and repopulate using ent. + - API void playlist :: init(): - Load the playlist database. + Load the playlist index from file. Remove every banned song + from the LibraryQueue in the library layer. - void playlist :: add(name, track_id); - Add the track_id to the playlist named "name". Save the - database to disk. - Throw -EEXIST if "name" does not exist. + void playlist :: add(Track *track, const std::string &name); + Add track->id to the playlist named "name" and return true. + Return false if the playlist does not exist. - void playlist :: del(name, track_id); - Remove the track_id from the playlist named "name". Save the - database to disk. Attempting to remove a track_id that does - not exist is not an error. - Throw -EEXIST if "name" does not exist. + If "name" is the currently selected playlist, add the track + to the PlaylistQueue. - const std::set &playlist :: get_tracks(name); - Return the set of tracks representing the requested group. - Throw -EEXIST if "name" does not exist. + If "name" is "Banned", remove the track from the LibraryQueue + in the library layer. - void playlist :: clear(); - This function only exists if CONFIG_TEST is enabled. Clear - (reset) the playlist database. + void playlist :: del(Track *track, const std::string &name); + Remove track->id from the playlist named "name" and return true. + Return false if the playlist does not exist or if the track + is not in the playlist. + + If "name" is the currently selected playlist, remove the track + from the PlaylistQueue. + + If "name" is "Banned", add the track to the LibraryQueue in the + library layer. + + bool playlist :: has(Track *track, const std::string &name); + Return true if the chosen playlist has the given track. + Return false otherwise. + + void playlist :: select(const std::string &name); + Change the currently displayed playlist to "name". + + const IndexEntry *playlist :: get_tracks(const std::string &name); + Return the IndexEntry represeting the requested playlist. + Return NULL if the requested playlist does not exist. + + Queue *playlist :: get_queue(); + Return the PlaylistQueue to the caller. -Library: (lib/library.cpp) - The library manages databases containing track information added by the - user. Ocarina 6 splits the library into multiple database tables for - storing content. The library will exist in a library namespace to - to make functions and classes more unique. +Deck: + The deck is used to hold temporary queues created by the user. This + layer is also in charge of maintaining a "recently played" queue of + tracks. -- Databases: - enum DB_Type { - DB_ALBUM, - DB_ARTIST, - DB_GENRE, - DB_LIBRARY, - DB_TRACK, - }; + The deck will be saved to the file "deck". When upgrading from file + version V0 to V1, use the saved random flag and sort order to set up + the library_q. -- Album: - class library :: Album : public DatabaseEntry { +- TempQueue: + class TempQueue : public Queue { public: - /* primary_key = "$name.$year.$artist_id" */ - string name; - string name_lower; - unsigned int year; - unsigned int artist_id; + TempQueue(bool); + + void set_flag(queue_flag); + void unset_flag(queue_flag); + + unsigned int add(Track *); + void del(Track *); + void del(unsigned int); + + void sort(sort_t, bool); }; - File << artist_id << year << name - -- Artist and Genre: - class library :: AGInfo : public DatabaseEntry { - public: - string name; /* primary key */ - string name_lower; - }; - - File << name - -- Library: - class library :: Library : public DatabaseEntry { - public: - string root_path; /* primary_key */ - unsigned int count; - bool enabled; - }; - - File << enabled << root_path - -- Track: - The primary key for a track is the full filepath (library.root_path + - track.filepath) - - class library :: Track : public DatabaseEntry { - public: - /* primary_key = library.root_path + "/" + filepath */ - unsigned int library_id; - unsigned int artist_id; - unsigned int album_id; - unsigned int genre_id; - - unsigned int track; - unsigned int last_year; - unsigned int last_month; - unsigned int last_day; - unsigned int play_count; - unsigned int length; - - string length_str; - string title; - string title_lower; - string filepath; - }; - - File << library_id << artist_id << album_id << genre_id; - File << track << last_year << last_month << last_day << play_count; - File << length << length_str << endl - File << title << endl; - File << filepath << endl; - -- Song: - struct Song { - library :: Album *album; - library :: AGInfo *artist; - library :: AGInfo *genre; - library :: Library *library; - library :: Track *track; - }; - -- Databases: - Database album_db(album.db); - Database artist_db(artist.db); - Database genre_db(genre.db); - Database library_db(library.db); - Database track_db(track.db); - -- Updating algorithm: - 1) Use a single IdleTask to loop over each track in the library, check - if the track still exists in the filesystem and remove it from - library_db if not. - 2) For each directory in the scan directory, create an IdleTask to - scan the next level of directories. - 3) For each file in the scan directory, check if the file already - exists in the track_db and add it to the database if not. Save - each database after adding files. - - The taglib library should be used for finding artist, album, etc. tags - for each track. - -- Importing - Ocarina 5.11 stores library files in ~/.ocarina/library/. Importing - involves reading each file and adding them to the database. If the file - describes a path already in the database then DO NOT overwrite the - current path and instead move on to the next file. If version != 2 then - move on to the next file. - - File format: - File << version << endl; /* version == 2 */ - File << path << endl; - File << id << enabled << next_track_id << size << endl; - File << - - Track format: - File << filepath << endl; - File << title << endl; - File << artist << endl; - File << album << endl; - File << comment << endl; - File << genre << endl; - File << lenstr << endl; - File << id << year << track << count; - File << last_day << last_month << last_year; - File << length << bitrate << sample << channels << banned << endl; - -- Testing: - The script tests/library/gen_library.sh will create a sample library - in the /tmp/ directory for testing purposes. All the track files are - complete silence, but the script will fake up tags for each file. - - To test importing, create several mock library files and copy them to - ~/.ocarina-test/library/ and attempt to read them in. - -- API - void library :: init(); - Initialize databases and read files from disk. While reading - the library: - - Update the count of tracks in each library path - - Find the lowercase text of artist, album, genre, track - - void library :: add_path(string dir); - If dir is not a directory: - throw -EINVAL - - Trigger the on_library_add() callback on success. - - void library :: del_path(unsigned int lib_id); - Invalidate a library_db row and all tracks owned by that path - if lib_id is not valid, throw -EEXIST. - - void library :: update_path(lib_id); - Update the given library_db row. - If lib_id is not valid, throw -EEXIST. - - Trigger the on_library_update() callback. - - void library :: update_all(); - Update all library paths. - Trigger the on_library_update() callback for each path. - - struct Song library :: lookup(track_id); - Fill out a Song structure for the provided track_id. - Throw -EEXIST if there is no track mapping to track_id. - - struct library :: Library *library :: lookup_path(unsigned int id); - Return the library path with index id. - Throw -EEXIST if there is no such path. - - void library :: import(); - Call this function to import an Ocarina 5.11 style library, - following the "Importing" section above. - -#ifdef CONFIG_TEST - void library :: print_db(DB_Type); - Print the database corresponding to DB_Type - - void library :: reset(); - Clear all databases, returning the library to an empty state. -endif /* CONFIG_TEST */ - - - - -Playqueue: (lib/playqueue.cpp) - Playqueues are a list of songs that the user has requested to play. - -- Flags: - enum playqueue_flags { - PQ_ENABLED (1 << 0), - PQ_RANDOM (1 << 1), - PQ_REPEAT (1 << 2), - }; - -- Sort order: - enum sort_t { - SORT_ARTIST_ASC = 1, - SORT_ARTIST_DESC = 2, - SORT_ALBUM_ASC = 3, - SORT_ALBUM_DESC = 4, - SORT_COUNT_ASC = 5, - SORT_COUNT_DESC = 6, - SORT_GENRE_ASC = 7, - SORT_GENRE_DESC = 8, - SORT_LENGTH_ASC = 9, - SORT_LENGTH_DESC = 10, - SORT_PLAYED_ASC = 11, - SORT_PLAYED_DESC = 12, - SORT_TITLE_ASC = 13, - SORT_TITLE_DESC = 14, - SORT_TRACK_ASC = 15, - SORT_TRACK_DESC = 16, - SORT_YEAR_ASC = 17, - SORT_YEAR_DESC = 18, - }; - -- Playqueue: - class Playqueue { - private: - vector tracks; - list sort_order; /* default = { SORT_ARTIST_ASC, - SORT_YEAR_ASC, - SORT_TRACK_ASC }; - unsigned int cur; - unsigned int flags; - unsigned int length; - public: - Playqueue(flags); - void write(File &); - void read(File &); - - void set_flag(playqueue_flags); - void unset_flag(playqueue_flags); - const unsigned int get_flags(); - string get_length(); - - unsigned int add(track_id); - unsigned int add_front(track_id); - void del(playqueue_id); - unsigned int size(); - - void reset_sort(); - void add_sort(sort_t, bool); - void sort(); - - unsigned int next(); - void reset_cur(); - } - - File << flags << tracks.size() << tracks[0] << tracks[1] << ... << tracks[N]; - -- API - Playqueue :: Playlist(unsigned int flags); - Create a new playqueue with the appropriate flags set. - sort_order = { (SORT_ARTIST, true), (SORT_YEAR, true), - (SORT_TRACK, true) }; - - unsigned int Playqueue :: add(unsigned int track_id); - unsigned int Playqueue :: add_front(unsigned int track_id); - Add a new track to the tracks vector and return the index. If - add_front is called, the track will be added to the front of - the playqueue (index = 0). - length += track.length. - - void Playqueue :: del(unsigned int playqueue_id); - Erase tracks[playqueue_id] from the tracks vector. - length -= track.length. - - void Playqueue :: del_track(unsigned int track_id); - Erase all tracks with track id track_id. - - void Playqueue :: set_flag(playqueue_flags flag); - void Playqueue :: unset_flag(playqueue_flags flag); - Set or unset the given flag. - - const unsigned int Playqueue :: get_flags(); - Return the currently enabled flags. - - song Playqueue :: get_length(); - Convert the length variable into a string and return the result - to the caller. - - unsigned int Playqueue :: size(); - Return tracks.size(); - - void Playqueue :: write(File &); - void Playqueue :: read(File &); - Read or write the playqueue to the file. - - void Playqueue :: reset_sort(); - Reset the sort_order list to empty. - - void Playqueue :: add_sort(sort_t type, bool ascending); - Add a new term to the sort order. - - void Playqueue :: sort(); - Perform a stable sort on the entire playqueue. Compare tracks - based on the sort_order list. - - unsigned int Playqueue :: next(); - Return the next track_id to play. - - if (tracks.size() == 0) - throw -EEXIST; - - if (flags & PL_RANDOM): - cur += rand() % tracks.size(); - else: - cur += 1; - - if (cur > = tracks.size()) - cur -= tracks.size(); - track = tracks[cur]; - - if (!(flags & PL_REPEAT)): - length -= track.length; - tracks.erase(cur); - return track; - - void Playqueue :: reset_cur(); - This function is intended to be used by the audio layer when - managing the recently played playqueue. - - cur = 0; - - - -Deck: (lib/deck.cpp) - The playqueue deck is used to hold the temporary playqueues created by - the user. - - This module also controls the library playqueue, which should be updated - using the on_library_track_add() and on_library_track_del() callback - functions. The library playqueue will always have PQ_ENABLED and - PQ_REPEAT set. This playlist will default to PQ_RANDOM unset. - - Deck: - list deck; - Playqueue library_pq; + list deck; - File << library_pq.random << deck.size() << endl; - File << deck[0] << endl; - File << deck[N] << endl; + V0: + File << library_q.random << library_q.sort_order().size(); + File << lib12order()[N].field << lib12order()[N].ascending; + File << deck.size() << endl; + File << deck[0] << endl; + File << deck[N] << endl; + + V1: + File << deck.size() << endl; + File << deck[0] << endl; + File << deck[1] << endl; + +- RecentQueue: + class RecentQueue : public Queue { + public: + RecentQueue(); + unsigned int add(Track *); + }; + +- TempQueue API: + TempQueue :: TempQueue(bool random); + Initialize a new TempQueue with the flag Q_ENABLED set. + If random is True then also set the Q_RANDOM flag. + + void TempQueue :: set_flag(queue_flag flag); + void TempQueue :: unset_flag(queue_flag flag); + unsigned int TempQueue :: add(Track *track); + void TempQueue :: del(Track *track); + void TempQueue :: del(unsigned int index); + void TempQueue :: sort(sort_t field, bool ascending); + These functions are all wrappers around the basic Queue + functions of the same name. First, call the corresponding + Queue :: () function to make the correct Queue + modification. Then, call deck :: write() to save changes to + disk. + +- RecentQueue API: + RecentQueue :: RecentQueue(); + Initialize a Queue with the flags Q_ENABLED, Q_REPEAT, and + Q_NO_SORT set. + + unsigned int RecentQueue :: add(Track *track); + The RecentQueue is designed to be a uniqueue queue that displays + the most recent tracks first. + + del(track); + _cur = 0; + return _add_at(track, 0); - API void deck :: init(); - Set up callbacks used by the library. + Read the deck file from disk and restore the queues. - void deck :: read(File &); void deck :: write(File &); Read or write the playqueue file. This will be called from the audio layer to store state. - Playqueue *deck :: create(); - Adds a new playqueue to the end of the deck and returns a - pointer to it. + Queue *deck :: create(bool random); + Adds a new queue to the end of the deck and return a pointer + to it. Save the deck to disk. - void deck :: remove(N); - Remove playqueue N from the deck. + void deck :: destroy(Queue *queue); + Remove the requested queue from the deck and trigger the + on_pq_removed() callback. Save the deck to disk. - Playqueue *deck :: get(N); - Return playqueue N from the deck. + void deck :: move(Queue *queue, unsigned int pos); + Move the queue to the new location in the deck. Save the deck + to disk. - void deck :: move(M, N); - Move playqueue at index M to index N. + unsigned int deck :: index(Queue *queue); + Return the index of the queue in the deck or deck.size() if + the queue is not currently in the deck. - unsigned int deck :: next(); - Iterate through the deck until you find a playqueue with the - flag PQ_ENABLED set. Call next() on this playqueue and return - the result. + Queue *deck :: get(unsigned int index); + Return the queue at the requested index, or NULL if no queue + is found. - If the playqueue is empty after calling next(), remove it from - the deck. + Track *deck :: next(); + Find the first enabled queue on the deck and return the track + given by queue->next(). - If there are no playqueues on the deck, play a song from the - library playqueue. + If the queue is empty after calling next(), call destroy() to + remove it from the list. - If there are no playable IDs, throw -EEXIST. + If there are no enabled queues, return a track from the library + queue. If the library queue is empty, return NULL. - void deck :: reset(); - This function only exists if CONFIG_TEST is enabled. Erase - all the playqueue information and reset the deck list. + If the result is non-NULL, add the found track to the recent + queue. - void deck :: print_info(); - This function only exists if CONFIG_TEST is enabled. Print - out helpful stats about the current state of the playqueue deck. + Save the deck before returning. - Playqueue *deck :: get_library_pq(); - Return the library playqueue. + Track *deck :: prev(); + Return the track given by recent_queue->next(). If the recent + queue is empty, return NULL. + + list &deck :: get_queues(); + Return the list of queues to the caller. + + Queue *deck :: get_queue(); + Return the RecentQueue to the caller. + + +Audio Driver: + The audio driver creates a way to fake audio playback for testing. This + will allow for more accurate tests, since I will know in advance what + values are returned to the Audio layer. This layer will derive from + the Driver class to implement either the GSTDriver or the TestDriver. + +- Seconds -> Nanoseconds conversion: + static const unsigned long O_SECOND = 1000000000; + +- Driver: + class Driver { + protected: + void (*on_eos)(); + + public: + Driver(); + ~Driver(); + virtual void init(int *, char ***, void (*)(), void (*)()) = 0; + + virtual void load(const std::string &) = 0; + virtual void play() = 0; + virtual void pause() = 0; + virtual void is_playing() = 0; + + virtual void seek_to(long) = 0; + virtual long position() = 0; + virtual long duration() = 0 + }; + +- Driver API: + void Driver :: Driver(); + Initialize the audio driver. This involves setting up a GST + Bus in the GSTDriver case. + + void Driver :: ~Driver(); + In the GSTDriver case, call gst_deinit() to avoid memory leak + false positives. + + void Driver :: init(int argc, char **argv, void (*eos_cb)(), + void (*error_cb)()); + The GSTDriver will use this function to set up the playbin2. + When an end-of-stream message is received, call eos_cb(). + If there is an error, call error_cb(); + + void Driver :: load(const std::string &file); + Load file for playback, but do not begin playback yet. + + void Driver :: play(); + Start playback. Return true if the state change operation + succeeds, false otherwise. + + void Driver :: pause(); + Pause playback. Return true if the state change operation + succeeds, false otherwise. + + bool Driver :: is_playing(); + Return true if the player is currently playing, false otherwise. + + void Driver :: seek_to(long pos); + Change playback position in the current track in nanoseconds. + + long Driver :: position(); + Return the current position in the track in nanoseconds. + + long Driver :: duration(); + Return the duration of the track in nanoseconds. + +- API: + Driver *driver :: get_driver(); + Return the current driver to be used for audio playback. This + could be either the GSTDriver or the TestDriver depending on + if CONFIG_TEST is set when compiling. -Audio: (lib/audio.cpp) - This file will introduce an "audio" namespace containing all of the - functions interacting with gstreamer. This will create a wrapper - namespace that will be easier to work with than using raw gstreamer - functions. - - The audio layer is meant to be an interface used by the front end to - control most features of the backend library. This means implementing - next track, previous track, and so on here. +Audio: + The audio layer uses the configured driver to control audio playback. Gstreamer options passed to audio :: init() can be found by running `gst-inspect-1.0 --help-gst` on the command line. -- Internal: - Set up a message bus to look for end-of-stream and error messages so - the next song can be played. This function should call the play - function after loading a track and after checking the "pause after N" - count. +- File: + File cur_track("cur_track"); - The audio layer will also create an internal playqueue for tracking - recently played songs. - -- State: - The audio layer will store the current trackid to disk, and then save - the playqueue deck. - - File << current_track << endl - deck.write(File); + File << current_track->id << endl; - API: - void audio :: init(argc, argv); - Initialize the gstreamer layer and reload the track that was - last loaded before shutdown. Gstreamer is supposed to remove - options from the argv array as they are processed, so pass - pointers to argc and argv to this function. - - Read in the state file. - - void audio :: quit(); - Clean up memory allocated by gstreamer. - Write out the state file. + void audio :: init(int *argc, char ***argv); + Initialize the audio driver through argc and argv. Read in + the current track file and load the track. void audio :: play(); void audio :: pause(); - Change the gstreamer state to either GST_STATE_PLAYING or - GST_STATE_PAUSED. Do nothing if there is not a track loaded - in the pipeline. Throw -EAUDIO if there is an error changing - state. - - void audio :: seek_to(long); - Seek to a position X nanoseconds into the track. Throw -EAUDIO - if there is an error seeking to the requested position. Do - nothing if there is not a track loaded in the pipeline. - - Seconds can be converted to nanoseconds by multiplying with - GST_SECOND. + void audio :: seek_to(long pos); + Call the corresponding function from the audio driver, but only + if a track is loaded. void audio :: stop(); pause() seek_to(0) + long audio :: position(); + long audio :: duration(); + Call the corresponding function from the audio driver. Return + 0 if no track is currently loaded. + + std::string audio :: position_str(); + Return the current audio position in string form. + Return an empty string if there is no current track. + void audio :: next(); - Call the deck :: next() function to get the next trackid, - and load that file into the gstreamer pipeline. Do not change - the state of the pipeline (if nothing is playing yet, don't - call play()). Throw -EEXIST if there is no track to load - into the pipeline. + Call the deck :: next() function to get the next track that + should be played and use the audio driver to load the track. - When a track is loaded: - Is it already in the recently played playqueue? - If yes, remove it from the playqueue. - Add to the front of the recently played playqueue. - Reset the current pointer in the playqueue to 0. + Save that track's ID to the cur_track file. - Write out the state file. + void audio :: prev(); + Call the deck :: previous() function to find a new track to + play and use the audio driver to load the track. - void audio :: previous(); - Call the playlist :: previous() function to iterate backwards - through the recently played playqueue. Load the returned trackid - without changing the pipeline state. + Save that track's ID to the cur_track file. - trackid audio :: current_trackid(); - Return the trackid of the currently playing song. If no track - is loaded throw -EEXIST; + void audio :: load_track(Track *track); + Load the requested track. - unsigned int audio :: position(); - Return the number of seconds that the song has played. + Save that track's ID to the cur_track file. - unsigned int audio :: duration(); - Return the duration of the current song in seconds. + Track *audio :: current_track(); + Return the currently playing Track. + Return NULL if there is no current track. void audio :: pause_after(bool enabled, unsigned int N); - Pause after N tracks. The first parameter is a bool - representing if this feature is enabled or not (true == enabled, - false == disabled). + If enabled == true: + Configure Ocarina to pause playback after N tracks + have been played. + If enabled == false: + Do not automatically pause. - The count will only be decremented when an end-of-stream message - is received by the gstreamer pipeline, and not when calling - next(). + If N is greater than the current pause count then enabled should + be set to true. bool audio :: pause_enabled(); - Return true if pausing is enabled, and false if pausing is - disabled. - unsigned int audio :: pause_count(); - Return the number of tracks that will be played before - playback pauses. + Use these functions to access the current "pause after N" state. diff --git a/Sconstruct b/Sconstruct index fa617c40..4dd9d1b7 100644 --- a/Sconstruct +++ b/Sconstruct @@ -2,8 +2,11 @@ import os # Configuration variables -CONFIG_VERSION = 6.0 -CONFIG_DEBUG = False +CONFIG_VERSION = 6.1 +CONFIG_DEBUG = True +CONFIG_TEST_VALGRIND = False +CONFIG_TEST_COVERAGE = False +CONFIG_TEST_CPPCHECK = False # Set up default environment @@ -11,28 +14,43 @@ CONFIG_CCFLAGS = [ "-O2" ] if CONFIG_DEBUG == True: CONFIG_CCFLAGS = [ "-Wall", "-Werror", "-g", "-DCONFIG_DEBUG" ] -env = Environment( CCFLAGS = CONFIG_CCFLAGS ) -env.Append(CPPPATH = os.path.abspath("include")) -env.Append(CXXCOMSTR = "C++ $TARGET") -env.Append(LINKCOMSTR = "Linking $TARGET") +class OEnvironment(Environment): + Debug = False + Version = 0 + Valgrind = False + Coverage = False + CppCheck = False + def __init__(self, CCFLAGS = CONFIG_CCFLAGS): + Environment.__init__(self, CCFLAGS = CCFLAGS) + self.Append(CPPPATH = os.path.abspath("include")) + self.Append(CXXCOMSTR = "C++ $TARGET") + self.Append(LINKCOMSTR = "Linking $TARGET") + self.Debug = CONFIG_DEBUG + self.Version = CONFIG_VERSION + self.Valgrind = CONFIG_TEST_VALGRIND + self.Coverage = CONFIG_TEST_COVERAGE + self.CppCheck = CONFIG_TEST_CPPCHECK -def use_package(name): - env.ParseConfig("pkg-config --cflags --libs %s" % name) + def UsePackage(self, name): + self.ParseConfig("pkg-config --cflags --libs %s" % name) -Export("env", "use_package", "CONFIG_DEBUG", "CONFIG_VERSION") +env = OEnvironment() +test_env = OEnvironment( CONFIG_CCFLAGS + [ "-DCONFIG_TEST" ] ) +Export("env", "test_env") include = SConscript("include/Sconscript") - -lib = SConscript("lib/Sconscript") -Export("lib") - -tests = SConscript("tests/Sconscript") - +core = SConscript("core/Sconscript") gui = SConscript("gui/Sconscript") -ocarina = env.Program("bin/ocarina", lib + gui) +tests = SConscript("tests/Sconscript") +Clean(tests, Glob("*.gcov")) +Clean(tests, Glob("tests/*.gcda")) +Clean(tests, Glob("tests/*.gcno")) + + +ocarina = env.Program("bin/ocarina", core + gui) Default(ocarina) Clean(ocarina, "bin/") diff --git a/TODO b/TODO index 073916f7..a2943e1f 100644 --- a/TODO +++ b/TODO @@ -61,14 +61,8 @@ Future work: the library? Perhaps create a mirror group? - Extra testing ideas: - - Run tests through valgrind to help find memory leaks - - Some way to enable / disable tests during development - - Run tests based on dependencies - - Fix tests that will only work on my computer - - Double check that all inputs and outputs are tested - - Simple testing library? - - Stop tests on failure - Test callbacks + - Create a default main() that deletes data directory - Preferences: - Set default sort @@ -86,13 +80,12 @@ Future work: Calculate value after first playback? Store in library :: Track structure - - Autosave databases - Artist, album, genere and library path don't change often so - saving on every add / remove won't be a huge performance hit - and may actually be more efficient in the long run. - - Playqueue and database inherit from common class - "About" dialog - Investigate "Bulk insert" callbacks for performance - - Initialize PQs with multiple flags - - Prefill Song structures in each library :: Track + - Use "friend" class for valid and id flags of a DatabaseEntry + - Doxygen support for documentation? + - Jenkins + - Run tests + - Show gcov graph + - Run cppcheck diff --git a/core/Sconscript b/core/Sconscript new file mode 100644 index 00000000..17003556 --- /dev/null +++ b/core/Sconscript @@ -0,0 +1,8 @@ +#!/usr/bin/python +Import("env") + +env.UsePackage("gstreamer-1.0") +env.UsePackage("taglib") + +res = Glob("*.cpp") +Return("res") diff --git a/core/audio.cpp b/core/audio.cpp new file mode 100644 index 00000000..e73dd2f6 --- /dev/null +++ b/core/audio.cpp @@ -0,0 +1,185 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include +#include +#include + +#include +#include + + +static bool _pause_enabled = false; +static unsigned int _pause_count = 0; + +static Track *cur_track = NULL; +static File f_cur_track("cur_track", 0); + + +static void save_state() +{ + f_cur_track.open(OPEN_WRITE); + f_cur_track << cur_track->id << std::endl; + f_cur_track.close(); +} + +static void _load_track(Track *track, bool start_playback) +{ + cur_track = track; + if (!track) + return; + + driver :: get_driver()->load(track->path()); + get_callbacks()->on_track_loaded(track); + if (start_playback) + audio :: play(); + else + audio :: pause(); + save_state(); +} + +static inline void _load_track_default(Track *track) +{ + _load_track(track, driver :: get_driver()->is_playing()); +} + +static bool continue_playback() +{ + bool ret = true; + + if (_pause_enabled) { + if (_pause_count == 0) { + ret = false; + _pause_enabled = false; + } else + _pause_count--; + get_callbacks()->on_pause_count_changed(_pause_enabled, _pause_count); + } + + return ret; +} + +static void on_eos() +{ + if (cur_track) { + cur_track->played(); + library :: get_queue()->updated(cur_track); + } + + _load_track(deck :: next(), continue_playback()); +} + +void audio :: init(int *argc, char ***argv) +{ + unsigned int id; + + driver :: get_driver()->init(argc, argv, on_eos, audio :: next); + if (f_cur_track.exists()) { + f_cur_track.open(OPEN_READ); + f_cur_track >> id; + f_cur_track.close(); + audio :: load_track(tagdb :: lookup(id)); + } +} + +void audio :: play() +{ + if (!cur_track) + return; + if (driver :: get_driver()->play()) + get_callbacks()->on_play(); +} + +void audio :: pause() +{ + if (!cur_track) + return; + if (driver :: get_driver()->pause()) + get_callbacks()->on_pause(); +} + +void audio :: seek_to(long pos) +{ + if (!cur_track) + return; + driver :: get_driver()->seek_to(pos); +} + +void audio :: stop() +{ + pause(); + seek_to(0); +} + +long audio :: position() +{ + if (!cur_track) + return 0; + return driver :: get_driver()->position(); +} + +long audio :: duration() +{ + if (!cur_track) + return 0; + return driver :: get_driver()->duration(); +} + +std::string audio :: position_str() +{ + std::stringstream ss; + long cur = position() / O_SECOND; + unsigned int minutes = cur / 60; + unsigned int seconds = cur % 60; + + ss << minutes << ":"; + if (seconds < 10) + ss << "0"; + ss << seconds; + return ss.str(); +} + +void audio :: next() +{ + _load_track_default(deck :: next()); +} + +void audio :: prev() +{ + _load_track_default(deck :: prev()); +} + +void audio :: load_track(Track *track) +{ + if (!track || track == cur_track) + return; + + _load_track(track, driver :: get_driver()->is_playing()); + deck :: get_queue()->add(cur_track); +} + +Track *audio :: current_track() +{ + return cur_track; +} + +void audio :: pause_after(bool enabled, unsigned int n) +{ + if (n > _pause_count) + enabled = true; + + _pause_enabled = enabled; + _pause_count = n; + get_callbacks()->on_pause_count_changed(enabled, n); +} + +bool audio :: pause_enabled() +{ + return _pause_enabled; +} + +unsigned int audio :: pause_count() +{ + return _pause_count; +} diff --git a/core/callback.cpp b/core/callback.cpp new file mode 100644 index 00000000..dca3094e --- /dev/null +++ b/core/callback.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + */ +#include + + +static void no_op() {} +static void no_op(bool, unsigned int) {} +static void no_op(Queue *, unsigned int) {} +static void no_op(Queue *) {} +static void no_op(Track *) {} + + +static struct Callbacks callbacks = { + .on_play = no_op, + .on_pause = no_op, + .on_track_loaded = no_op, + .on_pause_count_changed = no_op, + + .on_pq_removed = no_op, + + .on_queue_track_add = no_op, + .on_queue_track_del = no_op, + .on_queue_track_changed = no_op, +}; + + +struct Callbacks *get_callbacks() +{ + return &callbacks; +} diff --git a/lib/database.cpp b/core/database.cpp similarity index 50% rename from lib/database.cpp rename to core/database.cpp index a68af4ae..0e7c2d17 100644 --- a/lib/database.cpp +++ b/core/database.cpp @@ -1,10 +1,12 @@ /* * Copyright 2013 (c) Anna Schumaker. */ -#include +#include DatabaseEntry :: DatabaseEntry() - : valid(false), id(0) + : id(0) { } + +DatabaseEntry :: ~DatabaseEntry() {} diff --git a/core/deck.cpp b/core/deck.cpp new file mode 100644 index 00000000..013845f5 --- /dev/null +++ b/core/deck.cpp @@ -0,0 +1,237 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include +#include +#include + + +class RecentQueue : public Queue +{ +public: + RecentQueue() : Queue(Q_ENABLED | Q_REPEAT | Q_NO_SORT) {} + + unsigned int add(Track *track) + { + del(track); + _cur = 0; + return _add_at(track, 0); + } +}; + + +static std::list queue_deck; +static RecentQueue recent_queue; +static File deck_file("deck", 1); + + +TempQueue :: TempQueue() {} +TempQueue :: TempQueue(bool random) + : Queue(Q_ENABLED | (random ? Q_RANDOM : 0)) {} + +void TempQueue :: set_flag(queue_flags flag) +{ + Queue :: set_flag(flag); + deck :: write(); +} + +void TempQueue :: unset_flag(queue_flags flag) +{ + Queue :: unset_flag(flag); + deck :: write(); +} + +unsigned int TempQueue :: add(Track *track) +{ + unsigned int res = Queue :: add(track); + deck :: write(); + return res; +} + +void TempQueue :: del(Track *track) +{ + Queue :: del(track); + deck :: write(); +} + +void TempQueue :: del(unsigned int id) +{ + Queue :: del(id); + deck :: write(); +} + +void TempQueue :: sort(sort_t field, bool ascending) +{ + Queue :: sort(field, ascending); + deck :: write(); +} + + +static void upgrade_v0() +{ + int random, ascending; + unsigned int num, field; + Queue *library = library :: get_queue(); + + deck_file >> random >> num; + if (random) + library->set_flag(Q_RANDOM); + + for (unsigned int i = 0; i < num; i++) { + deck_file >> field >> ascending; + library->sort((sort_t)field, (i == 0) ? true : false); + if (!ascending) + library->sort((sort_t)field, false); + } +} + +void deck :: init() +{ + unsigned int num; + bool upgraded = false; + std::list::iterator it; + + if (!deck_file.open(OPEN_READ)) + return; + + if (deck_file.get_version() == 0) { + upgrade_v0(); + upgraded = true; + } + + deck_file >> num; + queue_deck.resize(num); + + for (it = queue_deck.begin(); it != queue_deck.end(); it++) + it->read(deck_file); + deck_file.close(); + + if (upgraded) + deck :: write(); +} + +void deck :: write() +{ + std::list::iterator it; + + if (!deck_file.open(OPEN_WRITE)) + return; + + deck_file << queue_deck.size() << std :: endl; + for (it = queue_deck.begin(); it != queue_deck.end(); it++) { + it->write(deck_file); + deck_file << std::endl; + } + + deck_file.close(); +} + +Queue *deck :: create(bool random) +{ + queue_deck.push_back(TempQueue(random)); + return &queue_deck.back(); +} + +static void _destroy(std::list::iterator &it) +{ + get_callbacks()->on_pq_removed(&(*it)); + queue_deck.erase(it); + deck :: write(); +} + +void deck :: destroy(Queue *queue) +{ + std::list::iterator it; + + for (it = queue_deck.begin(); it != queue_deck.end(); it++) { + if (&(*it) == queue) { + _destroy(it); + return; + } + } +} + +void deck :: move(Queue *queue, unsigned int new_pos) +{ + unsigned int old_pos = deck :: index(queue); + std::list::iterator it_old = queue_deck.begin(); + std::list::iterator it_new = queue_deck.begin(); + + for (unsigned int i = 0; i < queue_deck.size(); i++) { + if (i < old_pos) + it_old++; + + if (i < new_pos) + it_new++; + } + + if (new_pos > old_pos) + it_new++; + + queue_deck.splice(it_new, queue_deck, it_old); + write(); +} + +unsigned int deck :: index(Queue *queue) +{ + unsigned int i = 0; + std::list::iterator it; + + for (it = queue_deck.begin(); it != queue_deck.end(); it++) { + if (&(*it) == queue) + return i; + i++; + } + + return queue_deck.size(); +} + +Queue *deck :: get(unsigned int index) +{ + std::list::iterator it; + + for (it = queue_deck.begin(); it != queue_deck.end(); it++) { + if (index == 0) + return &(*it); + index--; + } + return NULL; +} + +Track *deck :: next() +{ + Track *track = NULL; + std::list::iterator it; + + for (it = queue_deck.begin(); it != queue_deck.end(); it++) { + if (it->has_flag(Q_ENABLED) == false) + continue; + + track = it->next(); + if (it->size() == 0) + _destroy(it); + break; + } + + if (!track) + track = library :: get_queue()->next(); + if (track) + recent_queue.add(track); + return track; +} + +Track *deck :: prev() +{ + return recent_queue.next(); +} + +std::list &deck :: get_queues() +{ + return queue_deck; +} + +Queue *deck :: get_queue() +{ + return &recent_queue; +} diff --git a/core/driver.cpp b/core/driver.cpp new file mode 100644 index 00000000..258ac6d1 --- /dev/null +++ b/core/driver.cpp @@ -0,0 +1,163 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + */ +#include + + +Driver :: Driver() {} +Driver :: ~Driver() {} + + +#ifdef CONFIG_TEST + +TestDriver :: TestDriver() : playing(false), cur_pos(0), cur_duration(0) {} +TestDriver :: ~TestDriver() {} + +void TestDriver :: init(int *argc, char ***argv, void (*eos_cb)(), void (*error_cb)()) + { on_eos = eos_cb; on_error = error_cb; } +void TestDriver :: load(const std::string &file) + { cur_file = file; playing = false; cur_pos = 0; } +bool TestDriver :: play() { playing = true; return true; } +bool TestDriver :: pause() { playing = false; return true; } +bool TestDriver :: is_playing() { return playing; } + +void TestDriver :: seek_to(long pos) { cur_pos = pos; } +long TestDriver :: position() { return cur_pos; } +long TestDriver :: duration() { return cur_duration; } + +void TestDriver :: eos() { on_eos(); } +void TestDriver :: error() { on_error(); } + +#else /* CONFIG_TEST */ + +static gboolean on_gst_message(GstBus *bus, GstMessage *message, gpointer data) +{ + GSTDriver *driver = (GSTDriver *)data; + driver->on_message(message); + return TRUE; +} + +static void parse_gst_error(GstMessage *error, const std::string filepath) +{ + GError *err; + gchar *debug; + + gst_message_parse_error(error, &err, &debug); + g_print("Error playing file: %s\n", filepath.c_str()); + g_print("Error: %s\n", err->message); + g_error_free(err); + g_free(debug); +} + +GSTDriver :: GSTDriver() {} + +GSTDriver :: ~GSTDriver() +{ + change_state(GST_STATE_NULL); + gst_deinit(); +} + +bool GSTDriver :: change_state(GstState state) +{ + GstStateChangeReturn ret = gst_element_set_state(player, state); + switch (ret) { + case GST_STATE_CHANGE_SUCCESS: + case GST_STATE_CHANGE_ASYNC: + return true; + default: + return false; + } +} + +void GSTDriver :: init(int *argc, char ***argv, void (*eos_cb)(), void (*error_cb)()) +{ + GstBus *bus; + + on_eos = eos_cb; + on_error = error_cb; + + gst_init(argc, argv); + player = gst_element_factory_make("playbin", "ocarina_player"); + bus = gst_pipeline_get_bus(GST_PIPELINE(player)); + gst_bus_add_watch(bus, on_gst_message, this); +} + +void GSTDriver :: load(const std::string &filepath) +{ + gchar *uri; + + cur_file = filepath; + change_state(GST_STATE_NULL); + uri = gst_filename_to_uri(filepath.c_str(), NULL); + g_object_set(G_OBJECT(player), "uri", uri, NULL); + g_free(uri); +} + +bool GSTDriver :: play() +{ + return change_state(GST_STATE_PLAYING); +} + +bool GSTDriver :: pause() +{ + return change_state(GST_STATE_PAUSED); +} + +bool GSTDriver :: is_playing() +{ + GstState state; + gst_element_get_state(player, &state, NULL, GST_CLOCK_TIME_NONE); + return state == GST_STATE_PLAYING; +} + +void GSTDriver :: seek_to(long pos) +{ + gst_element_seek_simple(player, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, pos); +} + +long GSTDriver :: position() +{ + long position; + if (gst_element_query_position(player, GST_FORMAT_TIME, &position)) + return position; + return 0; +} + +long GSTDriver :: duration() +{ + long duration; + + if (gst_element_query_duration(player, GST_FORMAT_TIME, &duration)) + return duration; + return 0; +} + +void GSTDriver :: on_message(GstMessage *message) +{ + switch (GST_MESSAGE_TYPE(message)) { + case GST_MESSAGE_ERROR: + parse_gst_error(message, cur_file); + on_error(); + break; + case GST_MESSAGE_EOS: + on_eos(); + break; + default: + break; + } +} + +#endif /* CONFIG_TEST */ + + +#ifdef CONFIG_TEST +static TestDriver cur_driver; +#else /* CONFIG_TEST */ +static GSTDriver cur_driver; +#endif /* CONFIG_TEST */ + + +Driver *driver :: get_driver() +{ + return &cur_driver; +} diff --git a/core/file.cpp b/core/file.cpp new file mode 100644 index 00000000..53bd4ab3 --- /dev/null +++ b/core/file.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include + +#ifdef CONFIG_TEST +const std::string OCARINA_DIR = "ocarina-test"; +#elif CONFIG_DEBUG +const std::string OCARINA_DIR = "ocarina-debug"; +#else +const std::string OCARINA_DIR = "ocarina"; +#endif + +File :: File(const std::string &name, unsigned int vers) + : mode(NOT_OPEN), filename(name), version(vers), prev_version(0) +{ +} + +File :: ~File() +{ + close(); +} + +const std::string File :: find_dir() +{ + std::string res(g_get_user_data_dir()); + res += "/" + OCARINA_DIR; + return res; +} + +const std::string File :: get_filepath() +{ + std::string res = ""; + + if (filename != "") { + res = find_dir(); + res += "/" + filename; + } + return res; +} + +const unsigned int File :: get_version() +{ + if (mode == OPEN_READ) + return prev_version; + return version; +} + +bool File :: exists() +{ + return g_file_test(get_filepath().c_str(), G_FILE_TEST_EXISTS); +} + +bool File :: open_read() +{ + if (!exists()) + return false; + + std::fstream::open(get_filepath().c_str(), std::fstream::in); + if (std::fstream::fail()) + return false; + + mode = OPEN_READ; + std::fstream::operator>>(prev_version); + getline(); + return true; +} + +bool File :: open_write() +{ + if (g_mkdir_with_parents(find_dir().c_str(), 0755) != 0) + return false; + + std::fstream::open(get_filepath().c_str(), std::fstream::out); + if (std::fstream::fail()) + return false; + + mode = OPEN_WRITE; + std::fstream::operator<<(version) << std::endl; + return true; +} + +bool File :: open(OpenMode m) +{ + if ((filename == "") || (m == NOT_OPEN) || (mode != NOT_OPEN)) + return false; + + else if (m == OPEN_READ) + return open_read(); + else /* m == OPEN_WRITE */ + return open_write(); +} + +void File :: close() +{ + if (mode != NOT_OPEN) + std::fstream::close(); + mode = NOT_OPEN; +} + +std::string File :: getline() +{ + char c; + std::string res; + + /* Ignore leading whitespace */ + while (peek() == ' ') + read(&c, 1); + + std::getline(*static_cast(this), res); + return res; +} diff --git a/lib/filter.cpp b/core/filter.cpp similarity index 92% rename from lib/filter.cpp rename to core/filter.cpp index b1aa2b43..941c797f 100644 --- a/lib/filter.cpp +++ b/core/filter.cpp @@ -2,9 +2,8 @@ * Copyright 2013 (c) Anna Schumaker. */ -#include -#include -#include +#include +#include #include #include @@ -93,7 +92,7 @@ std::string filter :: add(const std::string &text, unsigned int track_id) static void find_intersection(std::string &text, std::set &res) { - Index::iterator it = filter_index.find(text); + IndexEntry *it = filter_index.find(text); std::set tmp; set_intersection(it->values.begin(), it->values.end(), @@ -106,17 +105,17 @@ void filter :: search(const std::string &text, std::set &res) { std::list parsed; std::list::iterator it; + IndexEntry *found; parse_text(text, parsed); if (parsed.size() == 0) return; it = parsed.begin(); - try { - res = filter_index.find(*it)->values; - } catch (...) { + found = filter_index.find(*it); + if (!found) return; - } + res = found->values; for (it++; it != parsed.end(); it++) find_intersection(*it, res); diff --git a/lib/idle.cpp b/core/idle.cpp similarity index 96% rename from lib/idle.cpp rename to core/idle.cpp index 08e389a4..899a291e 100644 --- a/lib/idle.cpp +++ b/core/idle.cpp @@ -1,7 +1,7 @@ /* * Copyright 2013 (c) Anna Schumaker. */ -#include +#include #include diff --git a/lib/index.cpp b/core/index.cpp similarity index 81% rename from lib/index.cpp rename to core/index.cpp index d1842b0d..d1495391 100644 --- a/lib/index.cpp +++ b/core/index.cpp @@ -1,7 +1,7 @@ /* * Copyright 2014 (c) Anna Schumaker. */ -#include +#include IndexEntry :: IndexEntry() {} @@ -9,7 +9,7 @@ IndexEntry :: IndexEntry(const std::string &k) : key(k) {} -const std::string IndexEntry :: primary_key() +const std::string IndexEntry :: primary_key() const { return key; } @@ -51,9 +51,9 @@ Index :: Index(const std::string &filepath, bool autosave) void Index :: insert(const std::string &key, unsigned int val) { - iterator it = find(key); - if (it == end()) - it = at(Database :: insert(IndexEntry(key))); + IndexEntry *it = find(key); + if (it == NULL) + it = Database :: insert(IndexEntry(key)); it->insert(val); autosave(); @@ -61,9 +61,9 @@ void Index :: insert(const std::string &key, unsigned int val) void Index :: remove(const std::string &key, unsigned int val) { - iterator it = find(key); + IndexEntry *it = find(key); - if (it == end()) + if (it == NULL) return; it->remove(val); diff --git a/core/library.cpp b/core/library.cpp new file mode 100644 index 00000000..1609e65e --- /dev/null +++ b/core/library.cpp @@ -0,0 +1,218 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include + +#include + + +class LibraryQueue : public Queue { +private: + File f; + +public: + + LibraryQueue() : Queue(Q_ENABLED | Q_REPEAT), f("library.q", 0) + { + Queue :: sort(SORT_ARTIST, true); + Queue :: sort(SORT_YEAR, false); + Queue :: sort(SORT_TRACK, false); + } + + void save() + { + std::vector::iterator it; + + f.open(OPEN_WRITE); + f << _flags << " " << _sort_order.size(); + for (it = _sort_order.begin(); it != _sort_order.end(); it++) + f << " " << it->field << " " << it->ascending; + f << std::endl; + f.close(); + } + + void load() + { + unsigned int field; + bool ascending; + unsigned int n; + + if (!f.open(OPEN_READ)) + return; + + f >> _flags >> n; + for (unsigned int i = 0; i < n; i++) { + f >> field >> ascending; + Queue :: sort((sort_t)field, (i == 0) ? true : false); + if (ascending == false) + Queue :: sort((sort_t)field, false); + } + } + + void set_flag(queue_flags f) { Queue :: set_flag(f); save(); } + void unset_flag(queue_flags f) { Queue :: unset_flag(f); save(); } + + void sort(sort_t field, bool ascending) + { + Queue :: sort(field, ascending); + save(); + }; + +}; + +static LibraryQueue library_q; + +struct scan_info { + Library *library; + std :: string path; +}; + +static void scan_path(struct scan_info &); + + + +/* + * Scanning functions are here + */ + +static void process_path(Library *library, const std :: string &dir, + const std :: string &name) +{ + struct scan_info scan = { + .library = library, + .path = dir + "/" + name, + }; + + 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); + } +} + +static void scan_path(struct scan_info &scan) +{ + GDir *dir; + const char *name; + + 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(scan.library, scan.path, name); + name = g_dir_read_name(dir); + } + + tagdb :: commit(); +} + +static void validate_library(Library *&library) +{ + Track *track; + Database::iterator it; + Database *db = &tagdb :: get_track_db(); + + for (it = db->begin(); it != db->end(); it = db->next(it)) { + track = *it; + if (track->library != library) + continue; + + if (g_file_test(track->path().c_str(), G_FILE_TEST_EXISTS) == false) { + library_q.del(track); + tagdb :: remove_track(track->id); + } + } +} + + + +/* + * External API begins here + */ + +void library :: init() +{ + Database::iterator it; + Database *db = &tagdb :: get_track_db(); + + library_q.load(); + + for (it = db->begin(); it != db->end(); it = db->next(it)) { + if ((*it)->library->enabled) + library_q.add(*it); + } +} + +Library *library :: add(const std::string &dir) +{ + Library *library = NULL; + + if (g_file_test(dir.c_str(), G_FILE_TEST_IS_DIR) == false) + return library; + + library = tagdb :: add_library(dir); + if (library) + update(library); + return library; +} + +void library :: remove(Library *library) +{ + if (library) { + set_enabled(library, false); + tagdb :: remove_library(library->id); + } +} + +void library :: update(Library *library) +{ + struct scan_info scan = { + .library = library, + }; + + if (library) { + scan.path = library->root_path; + idle :: schedule(validate_library, library); + idle :: schedule(scan_path, scan); + } +} + +void library :: update_all() +{ + Database::iterator it; + Database *db = &tagdb :: get_library_db(); + + for (it = db->begin(); it != db->end(); it = db->next(it)) + update(*it); +} + +void library :: set_enabled(Library *library, bool enabled) +{ + Database::iterator it; + Database *db = &(tagdb :: get_track_db()); + + if (!library || (library->enabled == enabled)) + return; + + library->enabled = enabled; + tagdb :: commit_library(); + + for (it = db->begin(); it != db->end(); it = db->next(it)) { + if ((*it)->library == library) { + if (enabled) + library_q.add(*it); + else + library_q.del(*it); + } + } +} + +Queue *library :: get_queue() +{ + return &library_q; +} diff --git a/core/playlist.cpp b/core/playlist.cpp new file mode 100644 index 00000000..06629d74 --- /dev/null +++ b/core/playlist.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include + + +class PlaylistQueue : public Queue { +public: + + PlaylistQueue() : Queue(Q_ENABLED | Q_REPEAT) + { + sort(SORT_ARTIST, true); + sort(SORT_YEAR, false); + sort(SORT_TRACK, false); + set_flag(Q_NO_SORT); + } + + void fill(IndexEntry *ent) + { + std::set::iterator it; + + while (size() > 0) + del((unsigned)0); + + for (it = ent->values.begin(); it != ent->values.end(); it++) + add(tagdb :: lookup(*it)); + } + +}; + + +static Index playlist_db("playlist.db", true); +static PlaylistQueue playlist_q; +static std::string cur_plist; + + +void playlist :: init() +{ + std::set::iterator it; + + playlist_db.load(); + + IndexEntry *ent = get_tracks("Banned"); + if (!ent) + return; + + for (it = ent->values.begin(); it != ent->values.end(); it++) + library :: get_queue()->del(tagdb :: lookup(*it)); +} + +bool playlist :: has(Track *track, const std::string &name) +{ + std::set::iterator it; + IndexEntry *ent = playlist_db.find(name); + + if (ent == NULL) + return false; + + it = ent->values.find(track->id); + return it != ent->values.end(); +} + +void playlist :: add(Track *track, const std::string &name) +{ + if (!( (name == "Banned") || (name == "Favorites") )) + return; + + if (!has(track, name)) { + playlist_db.insert(name, track->id); + if (cur_plist == name) + playlist_q.add(track); + if (name == "Banned") + library :: get_queue()->del(track); + } +} + +void playlist :: del(Track *track, const std::string &name) +{ + playlist_db.remove(name, track->id); + if (cur_plist == name) + playlist_q.del(track); + if (name == "Banned") + library :: get_queue()->add(track); + +} + +void playlist :: select(const std::string &name) +{ + IndexEntry *ent; + + if (cur_plist == name) + return; + + ent = playlist_db.find(name); + if (ent == NULL) + return; + + playlist_q.fill(ent); + cur_plist = name; +} + +IndexEntry *playlist :: get_tracks(const std::string &name) +{ + return playlist_db.find(name); +} + +Queue *playlist :: get_queue() +{ + return &playlist_q; +} diff --git a/core/queue.cpp b/core/queue.cpp new file mode 100644 index 00000000..cd3bfe68 --- /dev/null +++ b/core/queue.cpp @@ -0,0 +1,257 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include +#include + +#include +#include + +#define O_MINUTES (60) +#define O_HOURS (60 * O_MINUTES) +#define O_DAYS (24 * O_HOURS) + +Queue :: Queue() + : _cur(-1), _flags(0), _length(0) +{ +} + +Queue :: Queue(unsigned int f) + : _cur(-1), _flags(f & Q_FLAG_MASK), _length(0) +{ +} + +Queue :: ~Queue() +{ +} + +void Queue :: write(File &f) +{ + f << _flags << " " << _tracks.size(); + for (unsigned int i = 0; i < _tracks.size(); i++) + f << " " << _tracks[i]->id; +} + +void Queue :: read(File &f) +{ + unsigned int n, id; + f >> _flags >> n; + _tracks.resize(n); + for (unsigned int i = 0; i < n; i++) { + f >> id; + _tracks[i] = tagdb :: lookup(id); + _length += _tracks[i]->length; + } +} + +void Queue :: set_flag(queue_flags f) +{ + _flags |= f; +} + +void Queue :: unset_flag(queue_flags f) +{ + _flags &= ~f; +} + +bool Queue :: has_flag(queue_flags f) +{ + return (_flags & f) == (unsigned int)f; +} + +static bool track_less_than(Track *lhs, Track *rhs, + std::vector &order) +{ + int res; + + for (unsigned int i = 0; i < order.size(); i++) { + if (order[i].ascending == true) + res = lhs->less_than(rhs, order[i].field); + else + res = rhs->less_than(lhs, order[i].field); + if (res == 0) + continue; + break; + } + return res < 0; +} + +unsigned int Queue :: find_sorted_id(Track *rhs) +{ + Track *lhs; + unsigned int begin = 0, end = (_tracks.size() - 1), mid; + + if (_tracks.size() == 0) + return 0; + + while (end > begin) { + mid = begin + ((end - begin) / 2); + lhs = _tracks[mid]; + if (track_less_than(lhs, rhs, _sort_order)) + begin = mid + 1; + else { + if (mid == begin) + return begin; + else + end = mid - 1; + } + } + + lhs = _tracks[begin]; + if (track_less_than(lhs, rhs, _sort_order)) + return begin + 1; + return begin; +} + +unsigned int Queue :: _add_at(Track *track, unsigned int pos) +{ + _tracks.insert(_tracks.begin() + pos, track); + _length += track->length; + get_callbacks()->on_queue_track_add(this, pos); + return pos; +} + +void Queue :: _del_at(Track *track, unsigned int pos) +{ + _tracks.erase(_tracks.begin() + pos); + _length -= track->length; + get_callbacks()->on_queue_track_del(this, pos); +} + +unsigned int Queue :: add(Track *track) +{ + unsigned int id = _tracks.size(); + if (_sort_order.size() > 0) + id = find_sorted_id(track); + return _add_at(track, id); +} + +void Queue :: del(Track *track) +{ + for (unsigned int i = 0; i < _tracks.size(); i++) { + if (_tracks[i] == track) + _del_at(track, i); + } +} + +void Queue :: del(unsigned int id) +{ + _del_at(_tracks[id], id); +} + +void Queue :: updated(Track *track) +{ + for (unsigned int i = 0; i < _tracks.size(); i++) { + if (_tracks[i] == track) + get_callbacks()->on_queue_track_changed(this, i); + } +} + +Track *Queue :: next() +{ + Track *res; + + if (_tracks.size() == 0) + return NULL; + else if (_tracks.size() == 1) + _cur = 0; + else if (_flags & Q_RANDOM) + _cur += random(1, _tracks.size() / 2); + else + _cur++; + + _cur %= _tracks.size(); + res = _tracks[_cur]; + if (!(_flags & Q_REPEAT)) { + del(_cur); + _cur--; + } + return res; +} + +unsigned int Queue :: size() +{ + return _tracks.size(); +} + +const std::string Queue :: size_str() +{ + std::stringstream ss; + ss << size(); + return ss.str(); +} + +const std::string Queue :: length_str() +{ + std::stringstream ss; + unsigned int factor[4] = { O_DAYS, O_HOURS, O_MINUTES, 1 }; + std::string fields[4] = { "day", "hour", "minute", "second" }; + unsigned int len = _length; + + for (unsigned int i = 0; i < 4; i++) { + unsigned int dur = len / factor[i]; + len -= dur * factor[i]; + + if (dur > 0) { + ss << dur << " " << fields[i]; + if (dur > 1) + ss << "s"; + if (len > 0) + ss << ", "; + } + } + + return ss.str(); +} + +class SortTracks { +public: + std::vector fields; + SortTracks(std::vector f) : fields(f) {} + bool operator()(Track *lhs, Track *rhs) + { + return track_less_than(lhs, rhs, fields); + } +}; + +void Queue :: sort(sort_t field, bool reset) +{ + bool found = false; + struct sort_info info = { field, true }; + + if (_flags & Q_NO_SORT) + return; + if (reset) + _sort_order.clear(); + + for (unsigned int i = 0; i < _sort_order.size(); i++) { + if (_sort_order[i].field == info.field) { + _sort_order[i].ascending = !_sort_order[i].ascending; + found = true; + break; + } + } + + if (!found) + _sort_order.push_back(info); + + std::stable_sort(_tracks.begin(), _tracks.end(), SortTracks(_sort_order)); + + for (unsigned int i = 0; i < _tracks.size(); i++) + get_callbacks()->on_queue_track_changed(this, i); +} + +Track *Queue :: operator[](unsigned int i) +{ + return _tracks[i]; +} + +void Queue :: track_selected(unsigned int id) +{ + _cur = id; + if (has_flag(Q_REPEAT) == false) { + del(_cur); + _cur--; + } +} diff --git a/core/random.cpp b/core/random.cpp new file mode 100644 index 00000000..84de4890 --- /dev/null +++ b/core/random.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2014 (c) Anna Schumaker + */ +#ifdef CONFIG_TEST + +#include + +static unsigned int _random_value = 0; + + +void _seed_random(unsigned int n) +{ + _random_value = n; +} + +unsigned int _pick_random() +{ + return ++_random_value; +} + +#endif /* CONFIG_TEST */ diff --git a/core/tags.cpp b/core/tags.cpp new file mode 100644 index 00000000..b6a0357a --- /dev/null +++ b/core/tags.cpp @@ -0,0 +1,413 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + */ + +#include +#include +#include + +#include +#include +#include + +Database artist_db("artist.db", true); +Database album_db("album.db", true); +Database genre_db("genre.db", true); +Database library_db("library.db", true); +Database track_db("track.db", false); + + +/** + * + * Artist tag + * + */ + +Artist :: Artist() {} + +Artist :: Artist(const std::string &s) + : name(s), lower(filter :: lowercase(name)) +{ +} + +const std::string Artist :: primary_key() const +{ + return name; +} + +void Artist :: read(File &f) +{ + name = f.getline(); + lower = filter :: lowercase(name); +} + +void Artist :: write(File &f) +{ + f << name; +} + + + +/** + * + * Album tag + * + */ + +Album :: Album() {} + +Album :: Album(const std::string &s, unsigned int y) + : name(s), lower(filter :: lowercase(name)), year(y) +{ +} + +const std::string Album :: primary_key() const +{ + std::stringstream ss; + ss << year << "." << name; + return ss.str(); +} + +void Album :: read(File &f) +{ + f >> year; + name = f.getline(); + lower = filter :: lowercase(name); +} + +void Album :: write(File &f) +{ + f << year << " " << name; +} + + + +/** + * + * Genre tag + * + */ + +Genre :: Genre() {} + +Genre :: Genre(const std::string &s) + : name(s), lower(filter :: lowercase(name)) +{ +} + +const std::string Genre :: primary_key() const +{ + return name; +} + +void Genre :: read(File &f) +{ + name = f.getline(); + lower = filter :: lowercase(name); +} + +void Genre :: write(File &f) +{ + f << name; +} + + + +/** + * + * Library tag + * + */ + +Library :: Library() + : count(0), enabled(false) +{ +} + +Library :: Library(const std::string &s) + : root_path(s), count(0), enabled(true) +{ +} + +const std::string Library :: primary_key() const +{ + return root_path; +} + +void Library :: read(File &f) +{ + f >> enabled; + root_path = f.getline(); +} + +void Library :: write(File &f) +{ + f << enabled << " " << root_path; +} + + + +/** + * + * Track tag + * + */ + +Track :: Track() : library(NULL), artist(NULL), album(NULL), genre(NULL){} + +Track :: Track(const std::string &f, Library *l) + : library(l), artist(NULL), album(NULL), genre(NULL), + play_count(0), last_year(0), last_month(0), last_day(0), + filepath(f.substr(l->root_path.size() + 1)) +{ + library->count++; +} + +Track :: ~Track() +{ + library->count--; +} + +const std::string Track :: primary_key() const +{ + return path(); +} + +void Track :: read(File &f) +{ + unsigned int library_id, artist_id, album_id, genre_id; + + f >> library_id >> artist_id >> album_id >> genre_id; + f >> track >> last_year >> last_month >> last_day; + f >> play_count >> length; + + title = f.getline(); + filepath = f.getline(); + + library = library_db.at(library_id); + artist = artist_db.at(artist_id); + album = album_db.at(album_id); + genre = genre_db.at(genre_id); + + title_lower = filter :: add(title, id); + filter :: add(artist->name, id); + filter :: add(album->name, id); + library->count++; + set_length_str(); +} + +void Track :: write(File &f) +{ + f << library->id << " " << artist->id << " " << album->id << " "; + f << genre->id << " " << track << " "; + f << last_year << " " << last_month << " " << last_day << " "; + f << play_count << " " << length << " " << title << std::endl; + f << filepath << std::endl; +} + +void Track :: set_length_str() +{ + std::stringstream ss; + unsigned int minutes = length / 60; + unsigned int seconds = length % 60; + + ss << minutes << ":"; + if (seconds < 10) + ss << "0"; + ss << seconds; + length_str = ss.str(); +} + +static inline const std::string format_tag(const TagLib::String &str) +{ + return str.stripWhiteSpace().to8Bit(true); +} + +template +static T *find_or_insert(const T &tag, Database &db) +{ + T *ret = db.find(tag.primary_key()); + if (!ret) + ret = db.insert(tag); + return ret; +} + +void Track :: tag() +{ + TagLib :: Tag *tag; + TagLib :: AudioProperties *audio; + TagLib :: FileRef ref(path().c_str(), true, TagLib::AudioProperties::Fast); + + if (ref.isNull()) { + print("ERROR: Could not read tags for file %s\n", path().c_str()); + return; + } + + tag = ref.tag(); + audio = ref.audioProperties(); + + artist = find_or_insert(Artist(format_tag(tag->artist())), artist_db); + album = find_or_insert(Album(format_tag(tag->album()), tag->year()), album_db); + genre = find_or_insert(Genre(format_tag(tag->genre())), genre_db); + track = tag->track(); + length = audio->length(); + title = format_tag(tag->title()); + + title_lower = filter :: add(title, id); + set_length_str(); + + filter :: add(artist->name, id); + filter :: add(album->name, id); + + library->count++; +} + +const std::string Track :: path() const +{ + return library->root_path + "/" + filepath; +} + +void Track :: played() +{ + time_t the_time = time(NULL); + struct tm *now = localtime(&the_time); + + play_count++; + last_day = now->tm_mday; + last_month = now->tm_mon + 1; + last_year = now->tm_year + 1900; + + tagdb :: commit(); +} + +/* + * Returns: + * 0: lhs == rhs + * < 0: lhs < rhs, or rhs is empty + * > 0: lhs > rhs, or lhs is empty + */ +static inline int compare_string(const std::string &a, const std::string &b) +{ + if (a.size() == 0) + return 1; + else if (b.size() == 0) + return -1; + return a.compare(b); +} + +static inline int compare_uint(unsigned int a, unsigned int b) +{ + if (a == b) + return 0; + if (a < b) + return -1; + return 1; +} + +int Track :: less_than(Track *rhs, sort_t field) +{ + int ret; + switch (field) { + case SORT_ARTIST: + return compare_string(artist->lower, rhs->artist->lower); + case SORT_ALBUM: + return compare_string(album->lower, rhs->album->lower); + case SORT_COUNT: + return compare_uint(play_count, rhs->play_count); + case SORT_GENRE: + return compare_string(genre->lower, rhs->genre->lower); + case SORT_LENGTH: + return compare_uint(length, rhs->length); + case SORT_PLAYED: + ret = compare_uint(last_year, rhs->last_year); + if (ret == 0) { + ret = compare_uint(last_month, rhs->last_month); + if (ret == 0) + ret = compare_uint(last_day, rhs->last_day); + } + return ret; + case SORT_TITLE: + return compare_string(title_lower, rhs->title_lower); + case SORT_TRACK: + return compare_uint(track, rhs->track); + case SORT_YEAR: + return compare_uint(album->year, rhs->album->year); + } + return 0; +} + + + +/** + * + * Tagdb functions + * + */ + +void tagdb :: init() +{ + artist_db.load(); + album_db.load(); + genre_db.load(); + library_db.load(); + track_db.load(); +} + +void tagdb :: commit() +{ + track_db.save(); +} + +void tagdb :: commit_library() +{ + library_db.save(); +} + +Track *tagdb :: add_track(const std::string &filepath, Library *library) +{ + Track *track = track_db.insert(Track(filepath, library)); + if (track) + track->tag(); + return track; +} + +Library *tagdb :: add_library(const std::string &filepath) +{ + return library_db.insert(Library(filepath)); +} + +void tagdb :: remove_track(unsigned int track_id) +{ + track_db.remove(track_id); +} + +void tagdb :: remove_library(unsigned int library_id) +{ + Database::iterator it; + for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) { + if ((*it)->library->id == library_id) + track_db.remove((*it)->id); + } + tagdb :: commit(); + library_db.remove(library_id); +} + +Track *tagdb :: lookup(unsigned int track_id) +{ + return track_db.at(track_id); +} + +Library *tagdb :: lookup_library(unsigned int library_id) +{ + return library_db.at(library_id); +} + +Database &tagdb :: get_track_db() +{ + return track_db; +} + +Database &tagdb :: get_library_db() +{ + return library_db; +} diff --git a/gui/Sconscript b/gui/Sconscript index 268611ad..abc00633 100644 --- a/gui/Sconscript +++ b/gui/Sconscript @@ -1,7 +1,7 @@ #!/usr/bin/python -Import("use_package") +Import("env") -use_package("gtkmm-3.0") +env.UsePackage("gtkmm-3.0") res = Glob("*.cpp") Return("res") diff --git a/gui/collection.cpp b/gui/collection.cpp index a69fe4e8..801406d3 100644 --- a/gui/collection.cpp +++ b/gui/collection.cpp @@ -1,9 +1,10 @@ /* * Copyright 2014 (c) Anna Schumaker. */ -#include -#include -#include +#include +#include +#include +#include class CollectionTab : public Tab { @@ -16,7 +17,7 @@ public: CollectionTab :: CollectionTab() - : Tab(deck::get_library_pq()) + : Tab(library::get_queue()) { tab_random = get_widget("o_collection_random"); tab_search = get_widget("o_collection_entry"); @@ -46,7 +47,7 @@ bool CollectionTab :: on_key_press_event(const std::string &key) tab_selected_ids(ids); for (unsigned int i = 0; i < ids.size(); i++) - playlist :: add("Banned", ids[i]); + playlist :: add(tagdb :: lookup(ids[i]), "Banned"); return true; } diff --git a/gui/collection_mgr.cpp b/gui/collection_mgr.cpp index 557b6426..6cafc087 100644 --- a/gui/collection_mgr.cpp +++ b/gui/collection_mgr.cpp @@ -1,10 +1,11 @@ /* * Copyright 2014 (c) Anna Schumaker. */ -#include -#include -#include +#include +#include +#include +static void on_library_add(unsigned int id, Library *library); static class CollectionColumns : public Gtk::TreeModelColumnRecord { public: @@ -29,8 +30,9 @@ static void on_collection_ok() chooser = get_widget("o_collection_chooser"); path = chooser->get_filename(); - library::add_path(path); + Library *library = library::add(path); enable_idle(); + on_library_add(library->id, library); } static void on_collection_update() @@ -39,12 +41,6 @@ static void on_collection_update() enable_idle(); } -static void on_collection_import() -{ - library :: import(); - enable_idle(); -} - static void on_collection_row_activated(const Gtk::TreePath &path, Gtk::TreeViewColumn *col) { @@ -69,7 +65,7 @@ void do_collection_delete() if (path) { Glib::RefPtr list = get_collection_list(); Gtk::TreeModel::Row row = *(list->get_iter(path)); - library :: del_path(row[collection_cols.c_col_id]); + library :: remove(tagdb :: lookup_library(row[collection_cols.c_col_id])); list->erase(row); } } @@ -92,23 +88,23 @@ void on_collection_toggled(const Glib::ustring &path) Glib::RefPtr list = get_collection_list(); Gtk::TreeModel::Row row = *(list->get_iter(path)); row[collection_cols.c_col_enabled] = !row[collection_cols.c_col_enabled]; - library :: set_enabled(row[collection_cols.c_col_id], + library :: set_enabled(tagdb :: lookup_library(row[collection_cols.c_col_id]), row[collection_cols.c_col_enabled]); } -static void on_library_add(unsigned int id, library :: Library *path) +static void on_library_add(unsigned int id, Library *library) { Gtk::TreeModel::Row row; Glib::RefPtr list = get_collection_list(); row = *(list->append()); row[collection_cols.c_col_id] = id; - row[collection_cols.c_col_enabled] = path->enabled; - row[collection_cols.c_col_size] = path->size; - row[collection_cols.c_col_path] = path->root_path; + row[collection_cols.c_col_enabled] = library->enabled; + row[collection_cols.c_col_size] = library->count; + row[collection_cols.c_col_path] = library->root_path; } -static void on_library_update(unsigned int id, library :: Library *path) +static void on_library_update(unsigned int id, Library *library) { Gtk::TreeModel::Row row; Glib::RefPtr list = get_collection_list(); @@ -118,29 +114,43 @@ static void on_library_update(unsigned int id, library :: Library *path) it != children.end(); it++) { row = *it; if (row[collection_cols.c_col_id] == id) - row[collection_cols.c_col_size] = path->size; + row[collection_cols.c_col_size] = library->count; } } void collection_mgr_init() { - struct Callbacks *cb = get_callbacks(); - Gtk::TreeView *treeview = get_widget("o_collection_treeview"); Glib::RefPtr list = get_collection_list(); Glib::RefPtr toggle; toggle = get_object("o_collection_toggle"); - cb->on_library_add = on_library_add; - cb->on_library_update = on_library_update; - connect_button("o_collection_ok", on_collection_ok); connect_button("o_collection_update", on_collection_update); - connect_button("o_collection_import", on_collection_import); list->set_sort_column(collection_cols.c_col_path, Gtk::SORT_ASCENDING); treeview->signal_row_activated().connect(sigc::ptr_fun(on_collection_row_activated)); treeview->signal_key_press_event().connect(sigc::ptr_fun(on_collection_key_pressed)); toggle->signal_toggled().connect(sigc::ptr_fun(on_collection_toggled)); } + +void collection_mgr_init2() +{ + Database *db; + Database::iterator it; + + db = &tagdb :: get_library_db(); + for (it = db->begin(); it != db->end(); it = db->next(it)) + on_library_add((*it)->id, *it); +} + +void collection_mgr_update() +{ + Database *db; + Database::iterator it; + + db = &tagdb :: get_library_db(); + for (it = db->begin(); it != db->end(); it = db->next(it)) + on_library_update((*it)->id, *it); +} diff --git a/gui/gui.cpp b/gui/gui.cpp index 9af95c46..a9c54d99 100644 --- a/gui/gui.cpp +++ b/gui/gui.cpp @@ -1,15 +1,16 @@ /* * Copyright 2014 (c) Anna Schumaker. */ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include -#include -#include -#include +#include +#include static bool audio_playing = false; static Glib::RefPtr builder; @@ -64,7 +65,7 @@ static void set_label_text(Gtk::Label *label, const std::string &size, Glib::Markup::escape_text(text) + ""); } -static void on_track_loaded(library :: Song &song) +static void on_track_loaded(Track *track) { Gtk::ToggleButton *ban = get_widget("o_ban"); Gtk::ToggleButton *fav = get_widget("o_favorite"); @@ -73,16 +74,13 @@ static void on_track_loaded(library :: Song &song) Gtk::Label *album = get_widget("o_album"); Gtk::Label *duration = get_widget("o_total_time"); - set_label_text(title, "xx-large", song.track->title); - set_label_text(artist, "x-large", "By: " + song.artist->name); - set_label_text(album, "x-large", "From: " + song.album->name); - duration->set_text(song.track->length_str); + set_label_text(title, "xx-large", track->title); + set_label_text(artist, "x-large", "By: " + track->artist->name); + set_label_text(album, "x-large", "From: " + track->album->name); + duration->set_text(track->length_str); - std::set ids = playlist :: get_tracks("Banned"); - bool banned = ids.find(song.track_id) != ids.end(); - - ids = playlist :: get_tracks("Favorites"); - bool favorite = ids.find(song.track_id) != ids.end(); + bool banned = playlist :: has(track, "Banned"); + bool favorite = playlist :: has(track, "Favorites"); ban_connection.block(); fav_connection.block(); @@ -107,12 +105,12 @@ static void on_pause_count_changed(bool enabled, unsigned int count) static void on_ban_toggled() { - Gtk::ToggleButton *ban = get_widget("o_pan"); + Gtk::ToggleButton *ban = get_widget("o_ban"); if (ban->get_active() == true) - playlist :: add("Banned", audio::current_trackid()); + playlist :: add(audio :: current_track(), "Banned"); else - playlist :: del("Banned", audio::current_trackid()); + playlist :: del(audio::current_track(), "Banned"); } static void on_fav_toggled() @@ -120,9 +118,9 @@ static void on_fav_toggled() Gtk::ToggleButton *fav = get_widget("o_favorite"); if (fav->get_active() == true) - playlist :: add("Favorites", audio::current_trackid()); + playlist :: add(audio::current_track(), "Favorites"); else - playlist :: del("Favorites", audio::current_trackid()); + playlist :: del(audio::current_track(), "Favorites"); } @@ -147,20 +145,20 @@ static bool on_window_key_pressed(GdkEventKey *event) tab_focus_search(); else if (key >= "0" && key <= "9") { unsigned int n = atoi(key.c_str()); - if (n < deck::size()) + if (n < deck::get_queues().size()) notebook->set_current_page(n); } else if (key == "c") - notebook->set_current_page(deck::size()); + notebook->set_current_page(deck::get_queues().size()); else if (key == "h") - notebook->set_current_page(deck::size() + 1); + notebook->set_current_page(deck::get_queues().size() + 1); else if (key == "m") - notebook->set_current_page(deck::size() + 3); + notebook->set_current_page(deck::get_queues().size() + 3); else if (key == "n") on_next(); else if (key == "N") - audio :: previous(); + audio :: prev(); else if (key == "p") - notebook->set_current_page(deck::size() + 2); + notebook->set_current_page(deck::get_queues().size() + 2); else return false; return true; @@ -170,9 +168,12 @@ static bool on_window_key_released(GdkEventKey *event) { std::string key = gdk_keyval_name(event->keyval); - if (key == "space") - audio :: toggle_play(); - else + if (key == "space") { + if (driver :: get_driver()->is_playing()) + audio :: pause(); + else + audio :: play(); + } else return false; return true; } @@ -193,6 +194,8 @@ bool on_idle() prog->show(); prog->set_fraction(idle::get_progress()); } + + collection_mgr_update(); return ret; } @@ -258,7 +261,7 @@ Gtk::Window *setup_gui() connect_button("o_play", audio::play); connect_button("o_pause", audio::pause); connect_button("o_stop", audio::stop); - connect_button("o_prev", audio::previous); + connect_button("o_prev", audio::prev); connect_button("o_next", on_next); count->signal_changed().connect(sigc::ptr_fun(on_config_pause)); diff --git a/gui/history.cpp b/gui/history.cpp index 9b076a22..6290034f 100644 --- a/gui/history.cpp +++ b/gui/history.cpp @@ -1,8 +1,8 @@ /* * Copyright 2014 (c) Anna Schumaker. */ -#include -#include +#include +#include class HistoryTab : public Tab { @@ -13,7 +13,7 @@ public: HistoryTab :: HistoryTab() - : Tab(audio::get_recent_pq()) + : Tab(deck :: get_queue()) { tab_search = get_widget("o_history_entry"); tab_size = get_widget("o_history_size"); diff --git a/gui/main.cpp b/gui/main.cpp index d3b47d13..70d9b3fd 100644 --- a/gui/main.cpp +++ b/gui/main.cpp @@ -1,11 +1,12 @@ /* * Copyright 2014 (c) Anna Schumaker. */ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include const std::string share_file(const std::string &file) { @@ -27,15 +28,16 @@ const std::string share_file(const std::string &file) Gtk::Window *ocarina_init(int *argc, char ***argv) { Gtk::Window *window = setup_gui(); - audio::init(argc, argv); - deck::init(); + tagdb :: init(); library::init(); playlist::init(); - deck::init2(); + deck::init(); + audio::init(argc, argv); + playlist :: select("Favorites"); share_file("ocarina6.glade"); post_init_tabs(); - audio::load_state(); + collection_mgr_init2(); return window; } diff --git a/gui/model.cpp b/gui/model.cpp index 1d61bf10..c9cda64d 100644 --- a/gui/model.cpp +++ b/gui/model.cpp @@ -4,14 +4,14 @@ * See the example at: * https://git.gnome.org/browse/gtkmm-documentation/tree/examples/others/treemodelcustom */ -#include -#include +#include +#include #include #include -PlayqueueModel::PlayqueueModel(Playqueue *q) - : Glib::ObjectBase( typeid(PlayqueueModel) ), +QueueModel::QueueModel(Queue *q) + : Glib::ObjectBase( typeid(QueueModel) ), Glib::Object(), queue(q) { do { @@ -19,14 +19,14 @@ PlayqueueModel::PlayqueueModel(Playqueue *q) } while (stamp == 0); } -void PlayqueueModel::increment_stamp() +void QueueModel::increment_stamp() { do { stamp++; } while (stamp == 0); } -void PlayqueueModel::on_row_inserted(unsigned int row) +void QueueModel::on_row_inserted(unsigned int row) { Gtk::TreePath path; Gtk::TreeIter iter; @@ -36,7 +36,7 @@ void PlayqueueModel::on_row_inserted(unsigned int row) row_inserted(path, iter); } -void PlayqueueModel::on_row_deleted(unsigned int row) +void QueueModel::on_row_deleted(unsigned int row) { Gtk::TreePath path; @@ -45,7 +45,7 @@ void PlayqueueModel::on_row_deleted(unsigned int row) row_deleted(path); } -void PlayqueueModel::on_row_changed(unsigned int row) +void QueueModel::on_row_changed(unsigned int row) { Gtk::TreePath path; Gtk::TreeIter iter; @@ -55,21 +55,21 @@ void PlayqueueModel::on_row_changed(unsigned int row) row_changed(path, iter); } -void PlayqueueModel::on_path_selected(const Gtk::TreePath &path) +void QueueModel::on_path_selected(const Gtk::TreePath &path) { - audio :: load_trackid(path_to_id(path)); - queue->path_selected(path[0]); + audio :: load_track(tagdb :: lookup(path_to_id(path))); + queue->track_selected(path[0]); audio :: play(); } -unsigned int PlayqueueModel :: iter_to_id(const Gtk::TreeIter &iter) +unsigned int QueueModel :: iter_to_id(const Gtk::TreeIter &iter) { return GPOINTER_TO_UINT(iter.gobj()->user_data); } -unsigned int PlayqueueModel::path_to_id(const Gtk::TreePath &path) +unsigned int QueueModel::path_to_id(const Gtk::TreePath &path) { - return queue->operator[](path[0]); + return queue->operator[](path[0])->id; } @@ -80,17 +80,17 @@ unsigned int PlayqueueModel::path_to_id(const Gtk::TreePath &path) * */ -Gtk::TreeModelFlags PlayqueueModel::get_flags_vfunc() const +Gtk::TreeModelFlags QueueModel::get_flags_vfunc() const { return Gtk::TREE_MODEL_LIST_ONLY; } -int PlayqueueModel::get_n_columns_vfunc() const +int QueueModel::get_n_columns_vfunc() const { return 10; } -GType PlayqueueModel::get_column_type_vfunc(int index) const +GType QueueModel::get_column_type_vfunc(int index) const { switch (index) { case 0: @@ -110,7 +110,7 @@ GType PlayqueueModel::get_column_type_vfunc(int index) const } } -void PlayqueueModel::get_value_uint(struct library::Song &song, int column, +void QueueModel::get_value_uint(Track *track, int column, Glib::ValueBase &value) const { Glib::Value specific; @@ -118,20 +118,20 @@ void PlayqueueModel::get_value_uint(struct library::Song &song, int column, switch (column) { case 0: - specific.set(song.track->track); + specific.set(track->track); break; case 5: - specific.set(song.album->year); + specific.set(track->album->year); break; case 7: - specific.set(song.track->play_count); + specific.set(track->play_count); } value.init(Glib::Value::value_type()); value = specific; } -void PlayqueueModel::get_value_str(struct library::Song &song, int column, +void QueueModel::get_value_str(Track *track, int column, Glib::ValueBase &value) const { std::stringstream ss; @@ -140,32 +140,32 @@ void PlayqueueModel::get_value_str(struct library::Song &song, int column, switch (column) { case 1: - specific.set(song.track->title); + specific.set(track->title); break; case 2: - specific.set(song.track->length_str); + specific.set(track->length_str); break; case 3: - specific.set(song.artist->name); + specific.set(track->artist->name); break; case 4: - specific.set(song.album->name); + specific.set(track->album->name); break; case 6: - specific.set(song.genre->name); + specific.set(track->genre->name); break; case 8: - if (song.track->play_count == 0) + if (track->play_count == 0) specific.set("Never"); else { - ss << song.track->last_month << " / "; - ss << song.track->last_day << " / "; - ss << song.track->last_year; + ss << track->last_month << " / "; + ss << track->last_day << " / "; + ss << track->last_year; specific.set(ss.str()); } break; case 9: - specific.set(Glib::Markup::escape_text(song.track->full_path)); + specific.set(Glib::Markup::escape_text(track->path())); } value.init(Glib::Value::value_type()); @@ -173,11 +173,11 @@ void PlayqueueModel::get_value_str(struct library::Song &song, int column, } -void PlayqueueModel::get_value_vfunc(const Gtk::TreeIter &iter, int column, +void QueueModel::get_value_vfunc(const Gtk::TreeIter &iter, int column, Glib::ValueBase &value) const { unsigned int row; - struct library::Song song; + Track *track; if (!check_iter_validity(iter)) return; @@ -186,20 +186,20 @@ void PlayqueueModel::get_value_vfunc(const Gtk::TreeIter &iter, int column, return; row = GPOINTER_TO_UINT(iter.gobj()->user_data); - library :: lookup((*queue)[row], &song); + track = queue->operator[](row); switch (column) { case 0: case 5: case 7: - get_value_uint(song, column, value); + get_value_uint(track, column, value); break; default: - get_value_str(song, column, value); + get_value_str(track, column, value); } } -bool PlayqueueModel::iter_next_vfunc(const Gtk::TreeIter &iter, +bool QueueModel::iter_next_vfunc(const Gtk::TreeIter &iter, Gtk::TreeIter &iter_next) const { unsigned int index; @@ -213,35 +213,35 @@ bool PlayqueueModel::iter_next_vfunc(const Gtk::TreeIter &iter, return iter_nth_root_child_vfunc(++index, iter_next); } -bool PlayqueueModel::iter_children_vfunc(const Gtk::TreeIter &parent, +bool QueueModel::iter_children_vfunc(const Gtk::TreeIter &parent, Gtk::TreeIter &iter) const { return iter_nth_child_vfunc(parent, 0, iter); } -bool PlayqueueModel::iter_has_child_vfunc(const Gtk::TreeIter &iter) const +bool QueueModel::iter_has_child_vfunc(const Gtk::TreeIter &iter) const { return (iter_n_children_vfunc(iter) > 0); } -int PlayqueueModel::iter_n_children_vfunc(const Gtk::TreeIter &iter) const +int QueueModel::iter_n_children_vfunc(const Gtk::TreeIter &iter) const { return 0; } -int PlayqueueModel::iter_n_root_children_vfunc() const +int QueueModel::iter_n_root_children_vfunc() const { return queue->size(); } -bool PlayqueueModel::iter_nth_child_vfunc(const Gtk::TreeIter &parent, +bool QueueModel::iter_nth_child_vfunc(const Gtk::TreeIter &parent, int n, Gtk::TreeIter &iter) const { iter = Gtk::TreeIter(); return false; } -bool PlayqueueModel::iter_nth_root_child_vfunc(int n, Gtk::TreeIter &iter) const +bool QueueModel::iter_nth_root_child_vfunc(int n, Gtk::TreeIter &iter) const { iter = Gtk::TreeIter(); if (n >= (int)queue->size()) @@ -252,14 +252,14 @@ bool PlayqueueModel::iter_nth_root_child_vfunc(int n, Gtk::TreeIter &iter) const return true; } -bool PlayqueueModel::iter_parent_vfunc(const Gtk::TreeIter &child, +bool QueueModel::iter_parent_vfunc(const Gtk::TreeIter &child, Gtk::TreeIter &iter) const { iter = Gtk::TreeIter(); return false; } -Gtk::TreeModel::Path PlayqueueModel::get_path_vfunc(const Gtk::TreeIter &iter) const +Gtk::TreeModel::Path QueueModel::get_path_vfunc(const Gtk::TreeIter &iter) const { Gtk::TreeModel::Path path; @@ -268,7 +268,7 @@ Gtk::TreeModel::Path PlayqueueModel::get_path_vfunc(const Gtk::TreeIter &iter) c return path; } -bool PlayqueueModel::get_iter_vfunc(const Gtk::TreePath &path, +bool QueueModel::get_iter_vfunc(const Gtk::TreePath &path, Gtk::TreeIter &iter) const { if (path.size() != 1) { @@ -278,7 +278,7 @@ bool PlayqueueModel::get_iter_vfunc(const Gtk::TreePath &path, return iter_nth_root_child_vfunc(path[0], iter); } -bool PlayqueueModel::check_iter_validity(const Gtk::TreeIter &iter) const +bool QueueModel::check_iter_validity(const Gtk::TreeIter &iter) const { return stamp == iter.get_stamp(); } diff --git a/gui/playlist.cpp b/gui/playlist.cpp index 9f9d4500..d9982f9f 100644 --- a/gui/playlist.cpp +++ b/gui/playlist.cpp @@ -1,8 +1,8 @@ /* * Copyright 2014 (c) Anna Schumaker. */ -#include -#include +#include +#include static const std::string current_playlist(); @@ -20,7 +20,7 @@ public: PlaylistTab :: PlaylistTab() - : Tab(playlist :: get_pq()) + : Tab(playlist :: get_queue()) { tab_search = get_widget("o_playlist_entry"); tab_treeview = get_widget("o_playlist_pq_treeview"); @@ -43,7 +43,7 @@ bool PlaylistTab :: on_key_press_event(const std::string &key) tab_selected_ids(ids); for (unsigned int i = 0; i < ids.size(); i++) - playlist :: del(current_playlist(), ids[i]); + playlist :: del(tagdb :: lookup(ids[i]), current_playlist()); return true; } diff --git a/gui/queue.cpp b/gui/queue.cpp index 1d0686ed..29449288 100644 --- a/gui/queue.cpp +++ b/gui/queue.cpp @@ -1,9 +1,9 @@ /* * Copyright 2014 (c) Anna Schumaker. */ -#include -#include -#include +#include +#include +#include #include @@ -63,7 +63,7 @@ private: public: - QueueTab(Playqueue *, unsigned int num); + QueueTab(Queue *, unsigned int num); ~QueueTab(); /** @@ -88,7 +88,7 @@ public: static std::map queue_mapping; -QueueTab :: QueueTab(Playqueue *pq, unsigned int num) +QueueTab :: QueueTab(Queue *pq, unsigned int num) : Tab(pq) { /* @@ -218,7 +218,7 @@ void QueueTab :: on_post_init() tab_init_random(); tab_init_repeat(); - bool active = (tab_pq->get_flags() & PQ_ENABLED) == PQ_ENABLED; + bool active = (tab_pq->has_flag(Q_ENABLED)); q_switch.set_active(active); q_switch.property_active().signal_changed().connect( sigc::mem_fun(*this, &QueueTab :: on_switch_changed)); @@ -291,16 +291,16 @@ void QueueTab :: queue_set_sensitive(bool sensitive) void QueueTab :: on_close_clicked() { - deck :: remove(tab_page_num()); + deck :: destroy(tab_pq); } void QueueTab :: on_switch_changed() { if (q_switch.get_active()) { - tab_pq->set_flag(PQ_ENABLED); + tab_pq->set_flag(Q_ENABLED); queue_set_sensitive(true); } else { - tab_pq->unset_flag(PQ_ENABLED); + tab_pq->unset_flag(Q_ENABLED); queue_set_sensitive(false); } } @@ -319,13 +319,13 @@ static void renumber_queues() it->second->on_tab_reordered(); } -static void on_pq_created(Playqueue *pq, unsigned int num) +void on_pq_created(Queue *pq, unsigned int num) { QueueTab *tab = new QueueTab(pq, num); tab->on_post_init(); } -static void on_pq_removed(Playqueue *pq) +static void on_pq_removed(Queue *pq) { Tab *tab = find_tab(pq); if (tab) { @@ -338,8 +338,8 @@ static void on_page_reordered(Gtk::Widget *page, int num) { Gtk::Notebook *notebook = get_widget("o_notebook"); - if ((unsigned int)num >= deck :: size()) { - notebook->reorder_child(*page, deck::size() - 1); + if ((unsigned int)num >= deck :: get_queues().size()) { + notebook->reorder_child(*page, deck::get_queues().size() - 1); return; } @@ -354,9 +354,17 @@ static void on_page_reordered(Gtk::Widget *page, int num) void init_queue_tabs() { struct Callbacks *cb = get_callbacks(); - cb->on_pq_created = on_pq_created; cb->on_pq_removed = on_pq_removed; Gtk::Notebook *notebook = get_widget("o_notebook"); notebook->signal_page_reordered().connect(sigc::ptr_fun(on_page_reordered)); } + +void post_init_queue_tabs() +{ + std::list::iterator it; + unsigned int i = 0; + + for (it = deck :: get_queues().begin(); it != deck :: get_queues().end(); it++) + on_pq_created(&(*it), i++); +} diff --git a/gui/tabs.cpp b/gui/tabs.cpp index d9776de6..e47b503e 100644 --- a/gui/tabs.cpp +++ b/gui/tabs.cpp @@ -1,17 +1,17 @@ /* * Copyright 2014 (c) Anna Schumaker. */ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include -static std::map queue_mapping; +static std::map queue_mapping; static sort_t sort_fields[] = { SORT_TRACK, SORT_TITLE, SORT_LENGTH, @@ -27,10 +27,10 @@ static sort_t sort_fields[] = { * */ -Tab :: Tab(Playqueue *pq) +Tab :: Tab(Queue *pq) : tab_sorting_count(0), tab_pq(pq), tab_size(NULL) { - tab_model = Glib::RefPtr(new PlayqueueModel(tab_pq)); + tab_model = Glib::RefPtr(new QueueModel(tab_pq)); queue_mapping[tab_pq] = this; } @@ -71,19 +71,19 @@ void Tab :: tab_finish_init() void Tab :: tab_init_random() { - tab_random->set_active(tab_pq->get_flags() & PQ_RANDOM); + tab_random->set_active(tab_pq->has_flag(Q_RANDOM)); tab_random->signal_toggled().connect(sigc::mem_fun(*this, &Tab::on_random_toggled)); } void Tab :: tab_init_repeat() { - tab_repeat->set_active(tab_pq->get_flags() & PQ_REPEAT); + tab_repeat->set_active(tab_pq->has_flag(Q_REPEAT)); tab_repeat->signal_toggled().connect(sigc::mem_fun(*this, &Tab::on_repeat_toggled)); } -void Tab :: tab_toggle_button(Gtk::ToggleButton *button, playqueue_flags flag) +void Tab :: tab_toggle_button(Gtk::ToggleButton *button, queue_flags flag) { if (button->get_active()) tab_pq->set_flag(flag); @@ -106,7 +106,7 @@ bool Tab :: tab_is_cur() void Tab :: tab_runtime_changed() { if (tab_is_cur()) - get_widget("o_queue_time")->set_text(tab_pq->get_length_str()); + get_widget("o_queue_time")->set_text(tab_pq->length_str()); } void Tab :: tab_display_sorting() @@ -155,31 +155,32 @@ void Tab :: tab_selected_ids(std::vector &ids) sel->unselect_all(); } -void Tab :: tab_queue_add(Playqueue *pq) +void Tab :: tab_queue_add(Queue *pq) { std::vector ids; tab_selected_ids(ids); for (unsigned int i = 0; i < ids.size(); i++) - pq->add(ids[i]); + pq->add(tagdb :: lookup(ids[i])); } bool Tab :: tab_queue_selected(bool random) { - if (deck :: size() >= 10) + if (deck :: get_queues().size() >= 10) return true; - Playqueue *pq = deck :: create(random); + Queue *pq = deck :: create(random); + on_pq_created(pq, deck :: get_queues().size() - 1); tab_queue_add(pq); return true; } bool Tab :: tab_add_to_queue(unsigned int n) { - if (n >= deck :: size()) + if (n >= deck :: get_queues().size()) return true; - Playqueue *pq = deck :: get(n); + Queue *pq = deck :: get(n); tab_queue_add(pq); return true; } @@ -190,7 +191,7 @@ bool Tab :: tab_add_to_playlist(const std::string &playlist) tab_selected_ids(ids); for (unsigned int i = 0; i < ids.size(); i++) - playlist :: add(playlist, ids[i]); + playlist :: add(tagdb :: lookup(ids[i]), playlist); return true; } @@ -251,7 +252,7 @@ bool Tab :: on_key_press_event(const std::string &key) void Tab :: on_show_rc_menu() { std::string item; - unsigned int size = deck :: size(); + unsigned int size = deck :: get_queues().size(); if (size == 0) { get_widget("o_add_to_pq")->hide(); @@ -284,12 +285,12 @@ void Tab :: on_show_rc_menu() void Tab :: on_random_toggled() { - tab_toggle_button(tab_random, PQ_RANDOM); + tab_toggle_button(tab_random, Q_RANDOM); } void Tab :: on_repeat_toggled() { - tab_toggle_button(tab_repeat, PQ_REPEAT); + tab_toggle_button(tab_repeat, Q_REPEAT); } void Tab :: on_row_activated(const Gtk::TreePath &path, Gtk::TreeViewColumn *col) @@ -314,9 +315,9 @@ void Tab :: on_column_clicked(unsigned int col) { if (tab_sorting_count == 0) { tab_sorting_title = tab_treeview->get_column(col)->get_title(); - tab_pq->reset_sort(sort_fields[col]); + tab_pq->sort(sort_fields[col], true); } else - tab_pq->add_sort(sort_fields[col]); + tab_pq->sort(sort_fields[col], false); tab_sorting_count++; tab_display_sorting(); @@ -355,7 +356,7 @@ bool Tab :: on_filter_visible(const Gtk::TreeIter &iter) return true; pq_id = tab_model->iter_to_id(iter); - it = visible_ids.find(tab_pq->operator[](pq_id)); + it = visible_ids.find(tab_pq->operator[](pq_id)->id); return it != visible_ids.end(); } @@ -379,9 +380,9 @@ void Tab :: on_entry_changed() * */ -Tab *find_tab(Playqueue *pq) +Tab *find_tab(Queue *pq) { - std::map::iterator it; + std::map::iterator it; it = queue_mapping.find(pq); if (it != queue_mapping.end()) return it->second; @@ -390,7 +391,7 @@ Tab *find_tab(Playqueue *pq) static Tab *find_tab(int num) { - std::map::iterator it; + std::map::iterator it; for (it = queue_mapping.begin(); it != queue_mapping.end(); it++) { if (it->second->tab_page_num() == num) return it->second; @@ -400,7 +401,7 @@ static Tab *find_tab(int num) static Tab *cur_tab() { - std::map::iterator it; + std::map::iterator it; for (it = queue_mapping.begin(); it != queue_mapping.end(); it++) { if (it->second->tab_is_cur()) return it->second; @@ -408,21 +409,21 @@ static Tab *cur_tab() return NULL; } -static void on_track_added(Playqueue *pq, unsigned int row) +static void on_track_added(Queue *pq, unsigned int row) { Tab *tab = find_tab(pq); if (tab) tab->on_track_added(row); } -static void on_track_deleted(Playqueue *pq, unsigned int row) +static void on_track_deleted(Queue *pq, unsigned int row) { Tab *tab = find_tab(pq); if (tab) tab->on_track_deleted(row); } -static void on_track_changed(Playqueue *pq, unsigned int row) +static void on_track_changed(Queue *pq, unsigned int row) { Tab *tab = find_tab(pq); if (tab) @@ -516,13 +517,15 @@ void init_tabs() void post_init_tabs() { - std::map::iterator it; + std::map::iterator it; for (it = queue_mapping.begin(); it != queue_mapping.end(); it++) it->second->on_post_init(); + post_init_queue_tabs(); + unsigned int tab = 0; - for (tab = 0; tab < deck::size(); tab++) { - if ((deck :: get(tab)->get_flags() & PQ_ENABLED) == PQ_ENABLED) + for (tab = 0; tab < deck::get_queues().size(); tab++) { + if ((deck :: get(tab)->has_flag(Q_ENABLED))) break; } get_widget("o_notebook")->set_current_page(tab); diff --git a/include/Sconscript b/include/Sconscript index 6ea804e1..5a1db8b7 100644 --- a/include/Sconscript +++ b/include/Sconscript @@ -1,8 +1,9 @@ #!/usr/bin/python -Import("env", "CONFIG_DEBUG", "CONFIG_VERSION") +Import("env", "test_env") -version = str(CONFIG_VERSION) -if CONFIG_DEBUG == True: +version = str(env.Version) +if env.Debug == True: version += "-debug" -env.Append( CCFLAGS = [ "-DCONFIG_VERSION='\"%s\"'" % version ] ) +for e in (env, test_env): + e.Append( CCFLAGS = [ "-DCONFIG_VERSION='\"%s\"'" % version ] ) diff --git a/include/callback.h b/include/callback.h deleted file mode 100644 index 2a3a41e4..00000000 --- a/include/callback.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014 (c) Anna Schumaker. - */ -#ifndef OCARINA_CALLBACK_H -#define OCARINA_CALLBACK_H - -#include -#include - - -struct Callbacks { - /* Audio callbacks */ - void (*on_play)(); - void (*on_pause)(); - void (*on_track_loaded)(library :: Song &); - void (*on_pause_count_changed)(bool, unsigned int); - - /* Deck callbacks */ - void (*on_pq_created)(Playqueue *, unsigned int); - void (*on_pq_removed)(Playqueue *); - - /* Library callbacks */ - void (*on_library_add)(unsigned int, library :: Library *); - void (*on_library_update)(unsigned int, library :: Library *); - void (*on_library_track_add)(unsigned int); - void (*on_library_track_del)(unsigned int); - void (*on_library_track_updated)(unsigned int); - void (*on_library_import_ban)(unsigned int); - - /* Playlist callbacks */ - void (*on_playlist_ban)(unsigned int); - void (*on_playlist_unban)(unsigned int); - - /* Playqueue callbacks */ - void (*on_queue_track_add)(Playqueue *, unsigned int); - void (*on_queue_track_del)(Playqueue *, unsigned int); - void (*on_queue_track_changed)(Playqueue *, unsigned int); - void (*on_queue_changed)(); -}; - - -struct Callbacks *get_callbacks(); - - -#endif /* OCARINA_CALLBACK_H */ diff --git a/include/audio.h b/include/core/audio.h similarity index 65% rename from include/audio.h rename to include/core/audio.h index 427d39e7..9ea204e3 100644 --- a/include/audio.h +++ b/include/core/audio.h @@ -4,35 +4,27 @@ #ifndef OCARINA_AUDIO_H #define OCARINA_AUDIO_H -#include - -extern "C" { - #include -} - +#include #include namespace audio { void init(int *, char ***); - void load_state(); - void quit(); void play(); void pause(); - void toggle_play(); - void stop(); - void next(); - void previous(); - void load_trackid(unsigned int); - unsigned int current_trackid(); - Playqueue *get_recent_pq(); - void seek_to(long); + void stop(); + long position(); - std::string position_str(); long duration(); + std::string position_str(); + + void next(); + void prev(); + void load_track(Track *track); + Track *current_track(); void pause_after(bool, unsigned int); bool pause_enabled(); diff --git a/include/core/callback.h b/include/core/callback.h new file mode 100644 index 00000000..55aa03fb --- /dev/null +++ b/include/core/callback.h @@ -0,0 +1,31 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + */ +#ifndef OCARINA_CALLBACK_H +#define OCARINA_CALLBACK_H + +#include +#include + + +struct Callbacks { + /* Audio callbacks */ + void (*on_play)(); + void (*on_pause)(); + void (*on_track_loaded)(Track *); + void (*on_pause_count_changed)(bool, unsigned int); + + /* Deck callbacks */ + void (*on_pq_removed)(Queue *); + + /* Queue callbacks */ + void (*on_queue_track_add)(Queue *, unsigned int); + void (*on_queue_track_del)(Queue *, unsigned int); + void (*on_queue_track_changed)(Queue *, unsigned int); +}; + + +struct Callbacks *get_callbacks(); + + +#endif /* OCARINA_CALLBACK_H */ diff --git a/include/database.h b/include/core/database.h similarity index 68% rename from include/database.h rename to include/core/database.h index a0c5f55d..c0953f49 100644 --- a/include/database.h +++ b/include/core/database.h @@ -4,8 +4,7 @@ #ifndef OCARINA_DATABASE_H #define OCARINA_DATABASE_H -#include -#include +#include #include #include @@ -13,11 +12,11 @@ class DatabaseEntry { public: - bool valid; unsigned int id; DatabaseEntry(); - virtual const std::string primary_key() = 0; + virtual ~DatabaseEntry() = 0; + virtual const std::string primary_key() const = 0; virtual void write(File &) = 0; virtual void read(File &) = 0; }; @@ -27,15 +26,15 @@ public: template class Database { private: - std::vector _db; + std::vector _db; std::map _keys; unsigned int _size; bool _autosave; File _file; public: - typedef typename std::vector::iterator iterator; - typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; Database(std::string, bool); ~Database(); @@ -43,7 +42,7 @@ public: void autosave(); void load(); - unsigned int insert(T); + T *insert(const T &); void remove(unsigned int); unsigned int size(); unsigned int actual_size(); @@ -52,8 +51,8 @@ public: iterator end(); iterator next(iterator &); - iterator at(unsigned int); - iterator find(const std::string &); + T *at(unsigned int); + T *find(const std::string &); }; #include "database.hpp" diff --git a/include/database.hpp b/include/core/database.hpp similarity index 62% rename from include/database.hpp rename to include/core/database.hpp index fad06e44..0ae77a93 100644 --- a/include/database.hpp +++ b/include/core/database.hpp @@ -7,17 +7,18 @@ #ifndef OCARINA_DATABASE_HPP #define OCARINA_DATABASE_HPP -#include - template Database :: Database(std::string filepath, bool autosave) - : _size(0), _autosave(autosave), _file(filepath, FILE_TYPE_DATA) + : _size(0), _autosave(autosave), _file(filepath, 0) { } template Database :: ~Database() { + iterator it; + for (it = begin(); it != end(); it = next(it)) + delete (*it); } template @@ -26,12 +27,13 @@ void Database :: save() if (_file.open(OPEN_WRITE) == false) return; - _file << _db.size() << std::endl; + _file << actual_size() << std::endl; for (unsigned int i = 0; i < _db.size(); i++) { - _file << _db[i].valid; - if (_db[i].valid == true) { - _file << " "; - _db[i].write(_file); + if (_db[i] == NULL) + _file << false; + else { + _file << true << " "; + _db[i]->write(_file); } _file << std::endl; } @@ -50,6 +52,7 @@ template void Database :: load() { unsigned int db_size; + bool valid; if (_file.exists() == false) return; @@ -59,11 +62,14 @@ void Database :: load() _file >> db_size; _db.resize(db_size); for (unsigned int i = 0; i < db_size; i++) { - _file >> _db[i].valid; - if (_db[i].valid == true) { - _db[i].read(_file); - _db[i].id = i; - _keys.insert(std::pair(_db[i].primary_key(), i)); + _file >> valid; + if (valid == false) + _db[i] = NULL; + else { + _db[i] = new T; + _db[i]->id = i; + _db[i]->read(_file); + _keys[_db[i]->primary_key()] = i; _size++; } } @@ -72,25 +78,21 @@ void Database :: load() } template -unsigned int Database :: insert(T val) +T *Database :: insert(const T &val) { - unsigned int id; - iterator it = find(val.primary_key()); + T *t = find(val.primary_key()); - if (it != end()) - return it - _db.begin(); + if (t != NULL) + return NULL; - /* - * Check primary key stuff here - */ - id = _db.size(); - _db.push_back(val); - _keys[val.primary_key()] = id; - _db[id].valid = true; - _db[id].id = id; + t = new T(val); + _db.push_back(t); + t->id = actual_size() - 1; + + _keys[t->primary_key()] = t->id; _size++; autosave(); - return id; + return t; } template @@ -98,10 +100,11 @@ void Database :: remove(unsigned int id) { if (id >= actual_size()) return; - if (_db[id].valid == false) + if (_db[id] == NULL) return; - _keys.erase(_db[id].primary_key()); - _db[id].valid = false; + _keys.erase(_db[id]->primary_key()); + delete _db[id]; + _db[id] = NULL; _size--; autosave(); } @@ -125,7 +128,7 @@ typename Database::iterator Database :: begin() return end(); iterator it = _db.begin(); - if ( (*it).valid == true ) + if ( (*it) != NULL ) return it; return next(it); } @@ -141,28 +144,25 @@ typename Database::iterator Database :: next(iterator &it) { do { it++; - } while ((it != end()) && ((*it).valid == false)); + } while ((it != end()) && (*it) == NULL); return it; } template -typename Database::iterator Database :: at(unsigned int id) +T *Database :: at(unsigned int id) { if (id >= actual_size()) - return end(); - - if (_db[id].valid == false) - return end(); - return _db.begin() + id; + return NULL; + return _db[id]; } template -typename Database::iterator Database :: find(const std::string &key) +T *Database :: find(const std::string &key) { std::map::iterator it; it = _keys.find(key); if (it == _keys.end()) - return end(); - return _db.begin() + it->second; + return NULL; + return _db[it->second]; } #endif /* OCARINA_DATABASE_HPP */ diff --git a/include/core/deck.h b/include/core/deck.h new file mode 100644 index 00000000..a4775d7e --- /dev/null +++ b/include/core/deck.h @@ -0,0 +1,48 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#ifndef OCARINA_DECK_H +#define OCARINA_DECK_H + +#include +#include + + +class TempQueue : public Queue +{ +public: + TempQueue(); + TempQueue(bool random); + + void set_flag(queue_flags); + void unset_flag(queue_flags); + + unsigned int add(Track *); + void del(Track *); + void del(unsigned int); + + void sort(sort_t, bool); +}; + +namespace deck +{ + + void init(); + void write(); + + Queue *create(bool); + void destroy(Queue *); + void move(Queue *, unsigned int); + + unsigned int index(Queue *); + Queue *get(unsigned int); + + Track *next(); + Track *prev(); + + std::list &get_queues(); + Queue *get_queue(); + +}; + +#endif /* OCARINA_DECK_H */ diff --git a/include/core/driver.h b/include/core/driver.h new file mode 100644 index 00000000..11a6994c --- /dev/null +++ b/include/core/driver.h @@ -0,0 +1,91 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + */ + +#include + +static const unsigned long O_SECOND = 1000000000; + + +class Driver { +protected: + void (*on_eos)(); + void (*on_error)(); + +public: + Driver(); + ~Driver(); + virtual void init(int *, char ***, void (*)(), void (*)()) = 0; + + virtual void load(const std::string &) = 0; + virtual bool play() = 0; + virtual bool pause() = 0; + virtual bool is_playing() = 0; + + virtual void seek_to(long) = 0; + virtual long position() = 0; + virtual long duration() = 0; +}; + + +#ifdef CONFIG_TEST +class TestDriver : public Driver +{ +public: + bool playing; + long cur_pos; + long cur_duration; + std::string cur_file; + + TestDriver(); + ~TestDriver(); + void init(int *, char ***, void (*)(), void (*)()); + + void load(const std::string &); + bool play(); + bool pause(); + bool is_playing(); + + void seek_to(long); + long position(); + long duration(); + + void eos(); + void error(); +}; +#else /* !CONFIG_TEST */ + +#include + +class GSTDriver : public Driver +{ +private: + GstElement *player; + std::string cur_file; + bool change_state(GstState state); + +public: + GSTDriver(); + ~GSTDriver(); + void init(int *, char ***, void (*)(), void (*)()); + + void load(const std::string &); + bool play(); + bool pause(); + bool is_playing(); + + void seek_to(long); + long position(); + long duration(); + + void on_message(GstMessage *); +}; +#endif /* CONFIG_TEST */ + + +namespace driver +{ + + Driver *get_driver(); + +} diff --git a/include/file.h b/include/core/file.h similarity index 66% rename from include/file.h rename to include/core/file.h index ecd7a68e..213ff75d 100644 --- a/include/file.h +++ b/include/core/file.h @@ -7,13 +7,6 @@ #include #include -#define FILE_VERSION 0 - -enum FileLocHint { - FILE_TYPE_DATA, - FILE_TYPE_LEGACY, - FILE_TYPE_INVALID, -}; enum OpenMode { OPEN_READ, @@ -21,21 +14,23 @@ enum OpenMode { NOT_OPEN, }; + class File : public std::fstream { private: OpenMode mode; - FileLocHint hint; - std::string filepath; + std::string filename; unsigned int version; + unsigned int prev_version; - std::string find_dir(); + const std::string find_dir(); bool open_read(); bool open_write(); public: - File(const std::string &, FileLocHint); + + File(const std::string &, unsigned int); ~File(); - const char *get_filepath(); + const std::string get_filepath(); const unsigned int get_version(); bool exists(); bool open(OpenMode); diff --git a/include/filter.h b/include/core/filter.h similarity index 100% rename from include/filter.h rename to include/core/filter.h diff --git a/include/idle.h b/include/core/idle.h similarity index 100% rename from include/idle.h rename to include/core/idle.h diff --git a/include/idle.hpp b/include/core/idle.hpp similarity index 100% rename from include/idle.hpp rename to include/core/idle.hpp diff --git a/include/index.h b/include/core/index.h similarity index 90% rename from include/index.h rename to include/core/index.h index 258d4fd1..34cf83fc 100644 --- a/include/index.h +++ b/include/core/index.h @@ -4,7 +4,7 @@ #ifndef OCARINA_INDEX_H #define OCARINA_INDEX_H -#include +#include #include #include @@ -17,7 +17,7 @@ public: IndexEntry(); IndexEntry(const std::string &); - const std::string primary_key(); + const std::string primary_key() const; void insert(unsigned int); void remove(unsigned int); diff --git a/include/core/library.h b/include/core/library.h new file mode 100644 index 00000000..78713525 --- /dev/null +++ b/include/core/library.h @@ -0,0 +1,26 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#ifndef OCARINA_LIBRARY_H +#define OCARINA_LIBRARY_H + +#include +#include +#include + +namespace library +{ + + void init(); + + Library *add(const std::string &); + void remove(Library *); + void update(Library *); + void update_all(); + + void set_enabled(Library *, bool); + Queue *get_queue(); + +}; + +#endif /* OCARINA_LIBRARY_H */ diff --git a/include/core/playlist.h b/include/core/playlist.h new file mode 100644 index 00000000..34e36d28 --- /dev/null +++ b/include/core/playlist.h @@ -0,0 +1,25 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#ifndef OCARINA_PLAYLIST_H +#define OCARINA_PLAYLIST_H + +#include +#include + +#include + +namespace playlist +{ + + void init(); + bool has(Track *, const std::string &); + void add(Track *, const std::string &); + void del(Track *, const std::string &); + void select(const std::string &); + IndexEntry *get_tracks(const std::string &); + Queue *get_queue(); + +}; + +#endif /* OCARINA_PLAYLIST_H */ diff --git a/include/print.h b/include/core/print.h similarity index 100% rename from include/print.h rename to include/core/print.h diff --git a/include/core/queue.h b/include/core/queue.h new file mode 100644 index 00000000..4fda3f9f --- /dev/null +++ b/include/core/queue.h @@ -0,0 +1,65 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#ifndef OCARINA_QUEUE_H +#define OCARINA_QUEUE_H + +#include +#include + +#include +#include + +enum queue_flags { + Q_ENABLED = (1 << 0), + Q_RANDOM = (1 << 1), + Q_REPEAT = (1 << 2), + Q_NO_SORT = (1 << 3), +}; + +static const unsigned int Q_FLAG_MASK = Q_ENABLED | Q_RANDOM | Q_REPEAT | Q_NO_SORT; + +struct sort_info { + sort_t field; + bool ascending; +}; + +class Queue { +protected: + std :: vector _tracks; + std :: vector _sort_order; + unsigned int _cur; + unsigned int _flags; + unsigned int _length; + + unsigned int find_sorted_id(Track *); + unsigned int _add_at(Track *, unsigned int); + void _del_at(Track *, unsigned int); + +public: + Queue(); + Queue(unsigned int); + ~Queue(); + void write(File &); + void read(File &); + + virtual void set_flag(queue_flags); + virtual void unset_flag(queue_flags); + bool has_flag(queue_flags); + + virtual unsigned int add(Track *); + virtual void del(Track *); + virtual void del(unsigned int); + void updated(Track *); + Track *next(); + + unsigned int size(); + const std::string size_str(); + const std::string length_str(); + + virtual void sort(sort_t, bool); + Track *operator[](unsigned int); + void track_selected(unsigned int); +}; + +#endif /* OCARINA_QUEUE_H */ diff --git a/include/core/random.h b/include/core/random.h new file mode 100644 index 00000000..5627ea2f --- /dev/null +++ b/include/core/random.h @@ -0,0 +1,36 @@ +/* + * Copyright 2014 (c) Anna Schumaker + */ + +#ifndef OCARINA_RANDOM_H +#define OCARINA_RANDOM_H + +#ifndef CONFIG_TEST + +#include +static inline void _seed_random(unsigned int n) { srand(time(NULL) + n); } +static inline unsigned int _pick_random() { return rand(); } + +#else /* CONFIG_TEST */ + +void _seed_random(unsigned int); +unsigned int _pick_random(); + +#endif /* CONFIG_TEST */ + + + +static inline void random_seed(unsigned int n) +{ + _seed_random(n); +} + +static inline unsigned int random(unsigned int min, unsigned int max) +{ + if (min >= max) + return min; + return min + (_pick_random() % (max - min)); +} + + +#endif /* OCARINA_RANDOM_H */ diff --git a/include/core/tags.h b/include/core/tags.h new file mode 100644 index 00000000..4f7d5137 --- /dev/null +++ b/include/core/tags.h @@ -0,0 +1,131 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + */ +#ifndef OCARINA_TAGS_H +#define OCARINA_TAGS_H + +#include + + +enum sort_t { + SORT_ARTIST, + SORT_ALBUM, + SORT_COUNT, + SORT_GENRE, + SORT_LENGTH, + SORT_PLAYED, + SORT_TITLE, + SORT_TRACK, + SORT_YEAR, +}; + + +class Artist : public DatabaseEntry { +public: + std::string name; + std::string lower; + + Artist(); + Artist(const std::string &); + const std::string primary_key() const; + void read(File &); + void write(File &); +}; + + +class Album : public DatabaseEntry { +public: + std::string name; + std::string lower; + unsigned int year; + + Album(); + Album(const std::string &, unsigned int); + const std::string primary_key() const; + void read(File &); + void write(File &); +}; + + +class Genre : public DatabaseEntry { +public: + std::string name; + std::string lower; + + Genre(); + Genre(const std::string &); + const std::string primary_key() const; + void read(File &); + void write(File &); +}; + + +class Library : public DatabaseEntry { +public: + std::string root_path; + unsigned int count; + bool enabled; + + Library(); + Library(const std::string &); + const std::string primary_key() const; + void read(File &); + void write(File &); +}; + + +class Track : public DatabaseEntry { +private: + void set_length_str(); + +public: + Library *library; + Artist *artist; + Album *album; + Genre *genre; + + 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; + + Track(); + Track(const std::string &, Library *); + ~Track(); + const std::string primary_key() const; + void read(File &); + void write(File &); + + void tag(); + const std::string path() const; + void played(); + int less_than(Track *, sort_t); +}; + + +namespace tagdb +{ + + void init(); + void commit(); + void commit_library(); + + Track *add_track(const std::string &, Library *); + Library *add_library(const std::string &); + void remove_track(unsigned int); + void remove_library(unsigned int); + Track *lookup(unsigned int); + Library *lookup_library(unsigned int); + Database &get_track_db(); + Database &get_library_db(); + +} + +#endif /* OCARINA_TAGS_H */ diff --git a/include/version.h b/include/core/version.h similarity index 100% rename from include/version.h rename to include/core/version.h diff --git a/include/deck.h b/include/deck.h deleted file mode 100644 index 6882fd51..00000000 --- a/include/deck.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#ifndef OCARINA_DECK_H -#define OCARINA_DECK_H - -#include - -namespace deck -{ - - void init(); - void init2(); - void read(); - void write(); - - Playqueue *create(bool); - void remove(unsigned int); - Playqueue *get(unsigned int); - unsigned int size(); - void move(unsigned int, unsigned int); - void move(Playqueue *, unsigned int); - unsigned int next(); - Playqueue *get_library_pq(); - -#ifdef CONFIG_TEST - void reset(); - void print_info(); -#endif /* CONFIG_TEST */ - -}; - -#endif /* OCARINA_DECK_H */ diff --git a/include/error.h b/include/error.h deleted file mode 100644 index 0190812d..00000000 --- a/include/error.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#ifndef OCARINA_ERROR_H -#define OCARINA_ERROR_H - -enum o_error { - E_AUDIO = 1, /* Audio error */ - E_EXIST, /* Requested object does not exist */ - E_INVAL, /* Invalid operation requested */ -}; - -#endif /* OCARINA_ERROR_H */ diff --git a/include/ocarina.h b/include/gui/ocarina.h similarity index 85% rename from include/ocarina.h rename to include/gui/ocarina.h index c5a35f8d..8458f967 100644 --- a/include/ocarina.h +++ b/include/gui/ocarina.h @@ -4,12 +4,14 @@ #ifndef OCARINA_H #define OCARINA_H -#include +#include #include /* collection_mgr.cpp */ void collection_mgr_init(); +void collection_mgr_init2(); +void collection_mgr_update(); /* main.cpp */ @@ -18,11 +20,11 @@ Gtk::Window *ocarina_init(int *, char ***); /* model.cpp */ -class PlayqueueModel : public Gtk::TreeModel, public Glib::Object { +class QueueModel : public Gtk::TreeModel, public Glib::Object { private: void increment_stamp(); - void get_value_uint(struct library::Song &, int, Glib::ValueBase &) const; - void get_value_str(struct library::Song &, int, Glib::ValueBase &) const; + void get_value_uint(Track *, int, Glib::ValueBase &) const; + void get_value_str(Track *, int, Glib::ValueBase &) const; bool check_iter_validity(const Gtk::TreeIter &) const; protected: @@ -44,8 +46,8 @@ protected: public: int stamp; - Playqueue *queue; - PlayqueueModel(Playqueue *); + Queue *queue; + QueueModel(Queue *); void on_row_inserted(unsigned int); void on_row_deleted(unsigned int); @@ -76,7 +78,8 @@ static Glib::RefPtr get_object(const std::string &name) return Glib::RefPtr::cast_static(get_builder()->get_object(name)); } - +void on_pq_created(Queue *, unsigned int); +void post_init_queue_tabs(); #ifdef CONFIG_TEST void do_collection_delete(); diff --git a/include/tabs.h b/include/gui/tabs.h similarity index 89% rename from include/tabs.h rename to include/gui/tabs.h index aae9a158..6ba20c44 100644 --- a/include/tabs.h +++ b/include/gui/tabs.h @@ -4,8 +4,8 @@ #ifndef OCARINA_TABS_H #define OCARINA_TABS_H -#include -#include +#include +#include #include class Tab { @@ -15,8 +15,8 @@ private: std::string tab_sorting_title; protected: - Playqueue *tab_pq; - Glib::RefPtr tab_model; + Queue *tab_pq; + Glib::RefPtr tab_model; Glib::RefPtr tab_filter; @@ -40,13 +40,13 @@ protected: */ void tab_init_random(); void tab_init_repeat(); - void tab_toggle_button(Gtk::ToggleButton *, playqueue_flags); + void tab_toggle_button(Gtk::ToggleButton *, queue_flags); void tab_dec_sort_count(); virtual void tab_set_size(); void tab_unmap(); public: - Tab(Playqueue *); + Tab(Queue *); virtual ~Tab(); /** @@ -59,7 +59,7 @@ public: void tab_display_sorting(); void tab_focus_search(); void tab_selected_ids(std::vector &); - void tab_queue_add(Playqueue *); + void tab_queue_add(Queue *); bool tab_queue_selected(bool); bool tab_add_to_queue(unsigned int); bool tab_add_to_playlist(const std::string &); @@ -93,7 +93,7 @@ public: }; -Tab *find_tab(Playqueue *); +Tab *find_tab(Queue *); void tab_focus_search(); void init_tabs(); void post_init_tabs(); diff --git a/include/library.h b/include/library.h deleted file mode 100644 index 4a1b9003..00000000 --- a/include/library.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#ifndef OCARINA_LIBRARY_H -#define OCARINA_LIBRARY_H - -#include -#include - -#include -#include - -struct ImportData; - -namespace library -{ - - enum DB_Type { - DB_ALBUM, - DB_ARTIST, - DB_GENRE, - DB_LIBRARY, - DB_TRACK, - }; - - - class AGInfo : public DatabaseEntry { - public: - DB_Type db_type; - std :: string name; - std :: string key_lower; - - AGInfo(); - AGInfo(DB_Type, TagLib :: Tag *); - AGInfo(DB_Type, const std::string &); - const std::string primary_key(); - void read(File &); - void write(File &); - }; - - - class Album : public DatabaseEntry { - public: - std :: string name; - std :: string name_lower; - unsigned int year; - - Album(); - Album(TagLib :: Tag *, unsigned int); - Album(const std::string &, unsigned int, unsigned int); - const std::string primary_key(); - void read(File &); - void write(File &); - }; - - - class Library : public DatabaseEntry { - public: - std::string root_path; - unsigned int size; - bool enabled; - - Library(); - Library(const std::string &, bool); - const std::string primary_key(); - void read(File &); - void write(File &); - }; - - - class Track : public DatabaseEntry { - public: - unsigned int library_id; - unsigned int artist_id; - unsigned int album_id; - unsigned int genre_id; - - unsigned int track; - unsigned int last_year; - unsigned int last_month; - unsigned int last_day; - unsigned int play_count; - unsigned int length; - - std :: string title; - std :: string title_lower; - std :: string length_str; - std :: string filepath; - std :: string full_path; - - Track(); - Track(TagLib :: Tag *, TagLib :: AudioProperties *, - unsigned int, unsigned int, unsigned int, - unsigned int, const std :: string &); - Track(ImportData *, unsigned int, unsigned int, - unsigned int, unsigned int); - const std::string primary_key(); - void read(File &); - void write(File &); - }; - - - struct Song { - unsigned int track_id; - library :: Album *album; - library :: AGInfo *artist; - library :: AGInfo *genre; - library :: Library *library; - library :: Track *track; - }; - - - void init(); - void add_path(const std::string &); - void del_path(unsigned int); - void update_path(unsigned int); - void update_all(); - void set_enabled(unsigned int, bool); - void lookup(unsigned int, library :: Song *); - Library *lookup_path(unsigned int); - void import(); - void track_played(unsigned int); -#ifdef CONFIG_TEST - void print_db(DB_Type); - void reset(); -#endif /* CONFIG_TEST */ -}; - -#endif /* OCARINA_LIBRARY_H */ diff --git a/include/playlist.h b/include/playlist.h deleted file mode 100644 index c9fec9e7..00000000 --- a/include/playlist.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#ifndef OCARINA_PLAYLIST_H -#define OCARINA_PLAYLIST_H - -#include - -#include -#include -#include - -namespace playlist -{ - - void init(); - void add(const std::string &, unsigned int); - void del(const std::string &, unsigned int); - void select(const std::string &); - const std::set &get_tracks(const std::string &); - Playqueue *get_pq(); - -#ifdef CONFIG_TEST - void clear(); -#endif /* CONFIG_TEST */ -}; - -#endif /* OCARINA_PLAYLIST_H */ diff --git a/include/playqueue.h b/include/playqueue.h deleted file mode 100644 index 9302eb44..00000000 --- a/include/playqueue.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#ifndef OCARINA_PLAYQUEUE_H -#define OCARINA_PLAYQUEUE_H - -#include -#include -#include -#include - -enum playqueue_flags { - PQ_ENABLED = (1 << 0), - PQ_RANDOM = (1 << 1), - PQ_REPEAT = (1 << 2), - PQ_NEVER_SORT = (1 << 3), - PQ_DISABLE_CHANGED_SIZE = (1 << 31), -}; - -enum sort_t { - SORT_ARTIST, - SORT_ALBUM, - SORT_COUNT, - SORT_GENRE, - SORT_LENGTH, - SORT_PLAYED, - SORT_TITLE, - SORT_TRACK, - SORT_YEAR, -}; - -struct sort_info { - sort_t field; - bool ascending; -}; - -class Playqueue { -private: - std :: vector tracks; - std :: list sort_order; - unsigned int flags; - unsigned int cur; - unsigned int length; - - unsigned int find_sorted_id(library :: Song &); - void _add_sort(sort_t, bool); - -public: - Playqueue(); - Playqueue(playqueue_flags); - ~Playqueue(); - void write(File &); - void read(File &); - - void set_flag(playqueue_flags); - void unset_flag(playqueue_flags); - const unsigned int get_flags(); - unsigned int get_length(); - std::string get_length_str(); - - unsigned int add(unsigned int); - unsigned int add_front(unsigned int); - void del(unsigned int); - void del_track(unsigned int); - void track_updated(unsigned int); - unsigned int size(); - void recalculate_length(); - - void add_sort(sort_t, bool ascending = true); - void reset_sort(sort_t, bool ascending = true); - void force_clear_sort(); - std::list &get_sort_order(); - - unsigned int operator[](unsigned int); - unsigned int next(); - void set_cur(unsigned int); - void path_selected(unsigned int); -#ifdef CONFIG_TEST - void reset(); -#endif /* CONFIG_TEST */ -}; - -#endif /* OCARINA_PLAYQUEUE_H */ diff --git a/lib/Sconscript b/lib/Sconscript deleted file mode 100644 index 9bc77cf7..00000000 --- a/lib/Sconscript +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/python -Import("use_package") - -use_package("gstreamer-1.0") -use_package("taglib") - -res = Glob("*.cpp") -Return("res") diff --git a/lib/audio.cpp b/lib/audio.cpp deleted file mode 100644 index 2610403a..00000000 --- a/lib/audio.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include -#include -#include - -#include -#include - -static GstElement *ocarina_player; - -static bool player_playing = false; -static bool track_loaded = false; -static unsigned int cur_trackid = 0; - -static bool o_pause_enabled = false; -static unsigned int o_pause_count = 0; -static bool o_should_pause = false; - -static Playqueue o_recently_played(PQ_ENABLED); -static File f_cur_track("cur_track", FILE_TYPE_DATA); - -static void parse_error(GstMessage *error) -{ - GError *err; - gchar *debug; - library :: Song song; - - library :: lookup(cur_trackid, &song); - gst_message_parse_error(error, &err, &debug); - g_print("Error playing file: %s\n", song.track->primary_key().c_str()); - g_print("Error: %s\n", err->message); - g_error_free(err); - g_free(debug); -} - -static void handle_pause_count() -{ - if (o_pause_enabled == false) - return; - else if (o_pause_count == 0) { - o_should_pause = true; - o_pause_enabled = false; - get_callbacks()->on_pause(); - } else - o_pause_count--; - get_callbacks()->on_pause_count_changed(o_pause_enabled, o_pause_count); -} - -static gboolean on_message(GstBus *bus, GstMessage *message, gpointer data) -{ - switch (GST_MESSAGE_TYPE(message)) { - case GST_MESSAGE_ERROR: - parse_error(message); - audio :: next(); - audio :: play(); - break; - case GST_MESSAGE_EOS: - handle_pause_count(); - library :: track_played(cur_trackid); - audio :: next(); - audio :: seek_to(0); - default: - break; - } - return TRUE; -} - -static bool change_state(GstState state) -{ - GstStateChangeReturn ret; - - ret = gst_element_set_state(GST_ELEMENT(ocarina_player), state); - switch (ret) { - case GST_STATE_CHANGE_SUCCESS: - case GST_STATE_CHANGE_ASYNC: - player_playing = (state == GST_STATE_PLAYING); - return true; - default: - player_playing = false; - return false; - } -} - -static void save_state() -{ - f_cur_track.open(OPEN_WRITE); - f_cur_track << cur_trackid << std::endl; - f_cur_track.close(); -} - -static bool load_song(library :: Song &song) -{ - GstState state; - gchar *uri; - std::string filepath = song.library->root_path + "/" + song.track->filepath; - - if (o_should_pause == true) { - state = GST_STATE_PAUSED; - o_should_pause = false; - } else { - gst_element_get_state(GST_ELEMENT(ocarina_player), &state, - NULL, GST_CLOCK_TIME_NONE); - } - - change_state(GST_STATE_NULL); - uri = gst_filename_to_uri(filepath.c_str(), NULL); - g_object_set(G_OBJECT(ocarina_player), "uri", uri, NULL); - g_free(uri); - - get_callbacks()->on_track_loaded(song); - return change_state(state); -} - -void audio :: init(int *argc, char ***argv) -{ - GstBus *bus; - - o_recently_played.set_flag(PQ_REPEAT); - o_recently_played.set_flag(PQ_NEVER_SORT); - o_recently_played.set_flag(PQ_DISABLE_CHANGED_SIZE); - gst_init(argc, argv); - - ocarina_player = gst_element_factory_make("playbin", "ocarina_player"); - bus = gst_pipeline_get_bus(GST_PIPELINE(ocarina_player)); - - gst_bus_add_watch(bus, on_message, NULL); -} - -void audio :: load_state() -{ - unsigned int id; - if (f_cur_track.exists()) { - f_cur_track.open(OPEN_READ); - f_cur_track >> id; - f_cur_track.close(); - audio :: load_trackid(id); - } -} - -void audio :: quit() -{ - change_state(GST_STATE_NULL); - gst_deinit(); -} - -void audio :: play() -{ - if (track_loaded == false) - return; - if (change_state(GST_STATE_PLAYING)) - get_callbacks()->on_play(); -} - -void audio :: pause() -{ - if (track_loaded == false) - return; - if (change_state(GST_STATE_PAUSED)) - get_callbacks()->on_pause(); -} - -void audio :: toggle_play() -{ - if (player_playing == true) - pause(); - else - play(); -} - -void audio :: stop() -{ - pause(); - seek_to(0); -} - -void audio :: seek_to(long pos) -{ - bool ret; - if (track_loaded == false) - return; - ret = gst_element_seek_simple(GST_ELEMENT(ocarina_player), - GST_FORMAT_TIME, - GST_SEEK_FLAG_FLUSH, - pos); - if (!ret) - throw -E_AUDIO; -} - -void audio :: next() -{ - library :: Song song; - unsigned int id; - - track_loaded = false; - id = deck :: next(); - library :: lookup(id, &song); - load_song(song); - track_loaded = true; - - cur_trackid = id; - save_state(); - o_recently_played.del_track(id); - o_recently_played.add_front(id); - o_recently_played.set_cur(0); -} - -void audio :: previous() -{ - library :: Song song; - unsigned int id; - - id = o_recently_played.next(); - if (id == cur_trackid) - return; - - library :: lookup(id, &song); - load_song(song); - cur_trackid = id; - save_state(); -} - -void audio :: load_trackid(unsigned int track_id) -{ - library :: Song song; - - if ((track_id == cur_trackid) && (track_loaded == true)) - return; - - track_loaded = false; - try { - library :: lookup(track_id, &song); - } catch (int err) { - return; - } - load_song(song); - track_loaded = true; - - cur_trackid = track_id; - save_state(); - o_recently_played.del_track(track_id); - o_recently_played.add_front(track_id); - o_recently_played.set_cur(0); -} - -unsigned int audio :: current_trackid() -{ - if (track_loaded == false) - throw -E_EXIST; - return cur_trackid; -} - -Playqueue *audio :: get_recent_pq() -{ - return &o_recently_played; -} - -long audio :: position() -{ - long position; - - if (track_loaded == false) - return 0; - if (!gst_element_query_position(GST_ELEMENT(ocarina_player), GST_FORMAT_TIME, &position)) - return 0; - return position; -} - -std::string audio :: position_str() -{ - std::stringstream ss; - long cur = position() / GST_SECOND; - unsigned int minutes = cur / 60; - unsigned int seconds = cur % 60; - - ss << minutes << ":"; - if (seconds < 10) - ss << "0"; - ss << seconds; - return ss.str(); -} - -long audio :: duration() -{ - long duration; - - if (track_loaded == false) - return 0; - if (!gst_element_query_duration(ocarina_player, GST_FORMAT_TIME, &duration)) - return 0; - return duration; -} - -void audio :: pause_after(bool enabled, unsigned int n) -{ - if (n > o_pause_count) - enabled = true; - - o_pause_enabled = enabled; - o_pause_count = n; - get_callbacks()->on_pause_count_changed(enabled, n); -} - -bool audio :: pause_enabled() -{ - return o_pause_enabled; -} - -unsigned int audio :: pause_count() -{ - return o_pause_count; -} diff --git a/lib/callback.cpp b/lib/callback.cpp deleted file mode 100644 index 7d2394f5..00000000 --- a/lib/callback.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014 (c) Anna Schumaker. - */ -#include - - -static void no_op() {} -static void no_op(unsigned int) {} -static void no_op(bool, unsigned int) {} -static void no_op(unsigned int id, library :: Library *path) {} -static void no_op(Playqueue *, unsigned int) {} -static void no_op(Playqueue *) {} -static void no_op(library :: Song &) {} - - -static struct Callbacks callbacks = { - .on_play = no_op, - .on_pause = no_op, - .on_track_loaded = no_op, - .on_pause_count_changed = no_op, - - .on_pq_created = no_op, - .on_pq_removed = no_op, - - .on_library_add = no_op, - .on_library_update = no_op, - .on_library_track_add = no_op, - .on_library_track_del = no_op, - .on_library_track_updated = no_op, - .on_library_import_ban = no_op, - - .on_playlist_ban = no_op, - .on_playlist_unban = no_op, - - .on_queue_track_add = no_op, - .on_queue_track_del = no_op, - .on_queue_track_changed = no_op, - .on_queue_changed = no_op, -}; - - -struct Callbacks *get_callbacks() -{ - return &callbacks; -} diff --git a/lib/deck.cpp b/lib/deck.cpp deleted file mode 100644 index b093d710..00000000 --- a/lib/deck.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include -#include -#include -#include - -#include - -static std::list playqueue_deck; -static Playqueue library_playqueue(PQ_ENABLED); -static File deck_file("deck", FILE_TYPE_DATA); - -static void add_library_track(unsigned int id) -{ - library_playqueue.add(id); -} - -static void del_library_track(unsigned int id) -{ - library_playqueue.del_track(id); -} - -static void change_library_track(unsigned int id) -{ - library_playqueue.track_updated(id); -} - -void deck :: init() -{ - library_playqueue.set_flag(PQ_REPEAT); - library_playqueue.set_flag(PQ_DISABLE_CHANGED_SIZE); - library_playqueue.add_sort(SORT_ARTIST); - library_playqueue.add_sort(SORT_YEAR); - library_playqueue.add_sort(SORT_TRACK); - read(); - - get_callbacks()->on_playlist_ban = del_library_track; - get_callbacks()->on_playlist_unban = add_library_track; - get_callbacks()->on_library_track_add = add_library_track; - get_callbacks()->on_library_track_del = del_library_track; - get_callbacks()->on_library_track_updated = change_library_track; - get_callbacks()->on_queue_changed = write; -} - -void deck :: init2() -{ - std::list::iterator it; - - library_playqueue.recalculate_length(); - for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) - it->recalculate_length(); -} - -void deck :: read() -{ - unsigned int num; - int random; - unsigned int field; - bool ascending; - std::list::iterator it; - - if (!deck_file.exists()) - return; - - deck_file.open(OPEN_READ); - deck_file >> random >> num; - library_playqueue.force_clear_sort(); - for (unsigned int i = 0; i < num; i++) { - deck_file >> field >> ascending; - if (i == 0) - library_playqueue.reset_sort((sort_t)field, ascending); - else - library_playqueue.add_sort((sort_t)field, ascending); - } - - deck_file >> num; - if (random) - library_playqueue.set_flag(PQ_RANDOM); - - playqueue_deck.resize(num); - - num = 0; - for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) { - it->read(deck_file); - get_callbacks()->on_pq_created(&(*it), num); - num++; - } - deck_file.close(); -} - -void deck :: write() -{ - std::list::iterator it; - std::list::iterator st; - std::list sort_order; - - deck_file.open(OPEN_WRITE); - - /* Save library playqueue */ - sort_order = library_playqueue.get_sort_order(); - deck_file << (library_playqueue.get_flags() & PQ_RANDOM) << " "; - deck_file << sort_order.size() << " "; - for (st = sort_order.begin(); st != sort_order.end(); st++) - deck_file << st->field << " " << st->ascending << " "; - - deck_file << playqueue_deck.size() << std :: endl; - for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) { - it->write(deck_file); - deck_file << std::endl; - } - deck_file.close(); -} - -Playqueue *deck :: create(bool random) -{ - Playqueue *pq; - playqueue_deck.push_back(Playqueue(PQ_ENABLED)); - pq = &playqueue_deck.back(); - if (random == true) - pq->set_flag(PQ_RANDOM); - get_callbacks()->on_pq_created(pq, playqueue_deck.size() - 1); - return pq; -} - -void deck :: remove(unsigned int id) -{ - std::list::iterator it = playqueue_deck.begin(); - for (unsigned int i = 0; i < id; i++) - it++; - get_callbacks()->on_pq_removed(&(*it)); - playqueue_deck.erase(it); - write(); -} - -Playqueue *deck :: get(unsigned int id) -{ - std::list::iterator it = playqueue_deck.begin(); - for (unsigned int i = 0; i < id; i++) - it++; - return &(*it); -} - -unsigned int deck :: size() -{ - return playqueue_deck.size(); -} - -void deck :: move(unsigned int old_pos, unsigned int new_pos) -{ - std::list::iterator it_old = playqueue_deck.begin(); - std::list::iterator it_new = playqueue_deck.begin(); - - for (unsigned int i = 0; i < playqueue_deck.size(); i++) { - if (i < old_pos) - it_old++; - if (i < new_pos) - it_new++; - } - - if (new_pos > old_pos) - it_new++; - - playqueue_deck.splice(it_new, playqueue_deck, it_old); -} - -void deck :: move(Playqueue *pq, unsigned int new_pos) -{ - unsigned int old_pos = 0; - std::list::iterator it_old = playqueue_deck.begin(); - std::list::iterator it_new = playqueue_deck.begin(); - - for (unsigned int i = 0; i < playqueue_deck.size(); i++) { - if (&(*it_old) != pq) { - it_old++; - old_pos++; - } - if (i < new_pos) - it_new++; - } - - if (new_pos > old_pos) - it_new++; - - playqueue_deck.splice(it_new, playqueue_deck, it_old); -} - -unsigned int deck :: next() -{ - unsigned int id = 0; - std::list::iterator it; - - for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) { - if (it->get_flags() & PQ_ENABLED) { - if (it->size() == 0) { - playqueue_deck.erase(it); - get_callbacks()->on_pq_removed(&(*it)); - } else { - id = it->next(); - if (it->size() == 0) { - playqueue_deck.erase(it); - get_callbacks()->on_pq_removed(&(*it)); - } - } - write(); - return id; - } - } - - return library_playqueue.next(); -} - -Playqueue *deck :: get_library_pq() -{ - return &library_playqueue; -} - -#ifdef CONFIG_TEST -static void no_op() {} - -void deck :: reset() -{ - get_callbacks()->on_queue_changed = no_op; - playqueue_deck.clear(); - library_playqueue.reset(); -} - -void deck :: print_info() -{ - unsigned int i = 0; - std::list::iterator it; - - for (it = playqueue_deck.begin(); it != playqueue_deck.end(); it++) { - print("deck[%u] = Playqueue { size = %u, flags = %u }\n", - i, it->size(), it->get_flags()); - i++; - } -} -#endif /* CONFIG_TEST */ diff --git a/lib/file.cpp b/lib/file.cpp deleted file mode 100644 index 02497518..00000000 --- a/lib/file.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include -#include - -#include - -#ifdef CONFIG_TEST -#define OCARINA_DIR "ocarina-test" -#elif CONFIG_DEBUG -#define OCARINA_DIR "ocarina-debug" -#else -#define OCARINA_DIR "ocarina" -#endif - -File :: File(const std::string &path, FileLocHint file_hint) - : mode(NOT_OPEN), hint(file_hint), version(FILE_VERSION) -{ - if (path.size() == 0) - hint = FILE_TYPE_INVALID; - - if (hint == FILE_TYPE_INVALID) - filepath = "INVALID"; - else - filepath = find_dir() + "/" + path; -} - -File :: ~File() -{ - close(); -} - -const char *File :: get_filepath() -{ - return filepath.c_str(); -} - -const unsigned int File :: get_version() -{ - return version; -} - -bool File :: exists() -{ - return g_file_test(filepath.c_str(), G_FILE_TEST_EXISTS); -} - -std::string File :: find_dir() -{ - std::string res; - - if (hint == FILE_TYPE_DATA) { - res = g_get_user_data_dir(); - res += "/"; - res += OCARINA_DIR; - } else /* FILE_TYPE_LEGACY */ { - res = g_get_home_dir(); - res += "/."; - res += OCARINA_DIR; - res += "/library"; - } - - return res; -} - -bool File :: open_read() -{ - if (!exists()) { - dprint("ERROR: File does not exist\n"); - return false; - } - - std::fstream::open(filepath.c_str(), std::fstream::in); - if (std::fstream::fail()) { - dprint("ERROR: File could not be opened for reading\n"); - return false; - } - - mode = OPEN_READ; - std::fstream::operator>>(version); - getline(); - return true; -} - -bool File :: open_write() -{ - if (hint == FILE_TYPE_LEGACY) { - dprint("ERROR: Cannot write to legacy files\n"); - return false; - } - - std::string dir = find_dir(); - if (g_mkdir_with_parents(dir.c_str(), 0755) != 0) { - dprint("ERROR: Could not make directory\n"); - return false; - } - - std::fstream::open(filepath.c_str(), std::fstream::out); - if (std::fstream::fail()) { - dprint("ERROR: Could not open file for writing\n"); - return false; - } - - mode = OPEN_WRITE; - std::fstream::operator<<(FILE_VERSION) << std::endl; - return true; -} - -bool File :: open(OpenMode m) -{ - if (hint == FILE_TYPE_INVALID) { - dprint("ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened\n"); - return false; - } else if (m == NOT_OPEN) { - dprint("ERROR: NOT_OPEN is not a legal OpenMode\n"); - return false; - } else if (mode != NOT_OPEN) { - dprint("ERROR: File is already open\n"); - return false; - } - - else if (m == OPEN_READ) - return open_read(); - else /* m == OPEN_WRITE */ - return open_write(); -} - -void File :: close() -{ - if (mode != NOT_OPEN) - std::fstream::close(); - mode = NOT_OPEN; -} - -std::string File :: getline() -{ - char c; - std::string res; - - /* Ignore leading whitespace */ - while (peek() == ' ') - read(&c, 1); - - std::getline(*static_cast(this), res); - return res; -} diff --git a/lib/library.cpp b/lib/library.cpp deleted file mode 100644 index 21486822..00000000 --- a/lib/library.cpp +++ /dev/null @@ -1,633 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include -#include -#include -#include - -#include -#include -#include - -static Database album_db("album.db", false); -static Database artist_db("artist.db", false); -static Database genre_db("genre.db", false); -static Database track_db("track.db", false); -static Database library_db("library.db", false); - -struct ImportData { - std::string filepath; - std::string title; - unsigned int track; - unsigned int last_day; - unsigned int last_month; - unsigned int last_year; - unsigned int length; - unsigned int count; -}; - - -/* - * library :: Artist: Artist tag information - */ - -library :: AGInfo :: AGInfo() -{ -} - -library :: AGInfo :: AGInfo(DB_Type type, TagLib :: Tag *tag) - : db_type(type) -{ - if (db_type == DB_ARTIST) - name = tag->artist().stripWhiteSpace().to8Bit(true); - else if (db_type == DB_GENRE) - name = tag->genre().stripWhiteSpace().to8Bit(true); - else - throw -E_INVAL; - - key_lower = filter :: lowercase(name); -} - -library :: AGInfo :: AGInfo(DB_Type type, const std::string &str) - : db_type(type) -{ - if ((db_type == DB_ARTIST) || (db_type == DB_GENRE)) { - name = str; - key_lower = filter :: lowercase(name); - } else - throw -E_INVAL; -} - -const std::string library :: AGInfo :: primary_key() -{ - return name; -} - -void library :: AGInfo :: read(File &f) -{ - name = f.getline(); - key_lower = filter :: lowercase(name); -} - -void library :: AGInfo :: write(File &f) -{ - f << name; -} - - - -/* - * library :: Album: Album tag information - */ - -library :: Album :: Album() - : name(""), year(0) -{ -} - -library :: Album :: Album(TagLib :: Tag *tag, unsigned int artist) - : name(tag->album().stripWhiteSpace().to8Bit(true)), - year(tag->year()) -{ - name_lower = filter :: lowercase(name); -} - -library :: Album :: Album(const std::string &str, unsigned int yr, unsigned int artist) - : name(str), year(yr) -{ - name_lower = filter :: lowercase(name); -} - -const std::string library :: Album :: primary_key() -{ - std::stringstream ss; - ss << name << "." << year; - return ss.str(); -} - -void library :: Album :: read(File &f) -{ - f >> year; - name = f.getline(); - name_lower = filter :: lowercase(name); -} - -void library :: Album :: write(File &f) -{ - f << year << " " << name; -} - - - -/* - * library :: Library: Basic information about each directory in the library - */ - -library :: Library :: Library() - : root_path(""), size(0), enabled(false) -{ -} - -library :: Library :: Library(const std::string &path, bool is_enabled) - : root_path(path), size(0), enabled(is_enabled) -{ -} - -const std::string library :: Library :: primary_key() -{ - return root_path; -} - -void library :: Library :: read(File &f) -{ - f >> enabled; - root_path = f.getline(); - size = 0; -} - -void library :: Library :: write(File &f) -{ - f << enabled << " " << root_path; -} - - - -/* - * library :: Track: Track tag information - */ - -library :: Track :: Track() - : library_id(0), artist_id(0), album_id(0), genre_id(0) -{ -} - -library :: Track :: Track(TagLib :: Tag *tag, TagLib :: AudioProperties *audio, - unsigned int lib, unsigned int artist, unsigned int album, - unsigned int genre, const std :: string &path) - : library_id(lib), artist_id(artist), album_id(album), genre_id(genre), - track(tag->track()), last_year(0), last_month(0), last_day(0), - play_count(0), length(audio->length()), - title(tag->title().stripWhiteSpace().to8Bit(true)) -{ - std::stringstream ss; - unsigned int minutes, seconds; - - full_path = path; - filepath = path.substr(library_db.at(library_id)->root_path.size() + 1); - title_lower = filter :: lowercase(title); - - minutes = length / 60; - seconds = length % 60; - ss << minutes << ":"; - if (seconds < 10) - ss << "0"; - ss << seconds; - length_str = ss.str(); -} - -library :: Track :: Track(struct ImportData *data, unsigned int lib, - unsigned int artist, unsigned int album, - unsigned int genre) - : library_id(lib), artist_id(artist), album_id(album), genre_id(genre), - track(data->track), last_year(data->last_year), last_month(data->last_month), - last_day(data->last_day), play_count(data->count), length(data->length), - title(data->title) -{ - std::stringstream ss; - unsigned int minutes, seconds; - - full_path = data->filepath; - filepath = full_path.substr(library_db.at(library_id)->root_path.size() + 1); - title_lower = filter :: lowercase(title); - - minutes = length / 60; - seconds = length % 60; - ss << minutes << ":"; - if (seconds < 10) - ss << "0"; - ss << seconds; - length_str = ss.str(); -} - -const std::string library :: Track :: primary_key() -{ - return full_path; -} - -void library :: Track :: read(File &f) -{ - std::stringstream ss; - unsigned int minutes, seconds; - - f >> library_id >> artist_id >> album_id >> genre_id; - f >> track >> last_year >> last_month >> last_day; - f >> play_count >> length; - title = f.getline(); - filepath = f.getline(); - - title_lower = filter :: lowercase(title); - full_path = library_db.at(library_id)->root_path + "/" + filepath; - library_db.at(library_id)->size++; - - minutes = length / 60; - seconds = length % 60; - ss << minutes << ":"; - if (seconds < 10) - ss << "0"; - ss << seconds; - length_str = ss.str(); -} - -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 << " "; // << std :: endl; - f << title << std :: endl; - f << filepath; -} - - - -/* - * Internal library functions - */ -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) -{ - TagLib :: Tag *tag; - TagLib :: AudioProperties *audio; - TagLib :: FileRef ref(path.c_str(), true, TagLib :: AudioProperties :: Fast); - unsigned int artist_id, album_id, genre_id, track_id; - - if (ref.isNull()) { - print("ERROR: Could not read tags for file %s\n", path.c_str()); - return; - } - - tag = ref.tag(); - - audio = ref.audioProperties(); - - 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 :: AGInfo(library :: DB_GENRE, tag)); - track_id = track_db.insert(library :: Track(tag, audio, lib_id, - artist_id, album_id, genre_id, path)); - - if (track_db.at(track_id)->valid == false) - return; - - library_db.at(lib_id)->size++; - - filter::add(artist_db.at(artist_id)->name, track_id); - filter::add(album_db.at(album_id)->name, track_id); - filter::add(track_db.at(track_id)->title, track_id); - get_callbacks()->on_library_track_add(track_id); -} - -static bool process_path(unsigned int lib_id, const std :: string &dir, - const std :: string &name) -{ - struct scan_info scan; - bool changed = false; - std :: string path = dir + "/" + name; - - 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 { - if (track_db.find(path) == track_db.end()) { - read_tags(lib_id, path); - changed = true; - } - } - - return changed; -} - -static void save_all_dbs() -{ - artist_db.save(); - album_db.save(); - genre_db.save(); - track_db.save(); -} - -static void do_scan_path(struct scan_info &scan) -{ - GDir *dir; - const char *name; - bool changed = false; - - dir = g_dir_open(scan.path.c_str(), 0, NULL); - if (dir == NULL) - return; - - name = g_dir_read_name(dir); - while (name != NULL) { - if (process_path(scan.lib_id, scan.path, name)) - changed = true; - name = g_dir_read_name(dir); - } - - if (changed == true) { - save_all_dbs(); - get_callbacks()->on_library_update(scan.lib_id, - &(*library_db.at(scan.lib_id))); - } -} - -static void do_validate_library(unsigned int &lib_id) -{ - std :: string path; - bool changed = false; - - if (track_db.size() == 0) - return; - - Database::iterator it; - for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) { - if ((*it).library_id != lib_id) - continue; - - path = it->primary_key(); - if (g_file_test(path.c_str(), G_FILE_TEST_EXISTS) == false) { - dprint("Removing file: %s\n", path.c_str()); - track_db.remove(it - track_db.begin()); - library_db.at(lib_id)->size--; - changed = true; - } - } - - if (changed == true) { - get_callbacks()->on_library_update(lib_id, &(*library_db.at(lib_id))); - save_all_dbs(); - } -} - -static void do_update_library(unsigned int lib_id) -{ - struct scan_info scan = { lib_id, library_db.at(lib_id)->root_path }; - idle :: schedule(do_validate_library, lib_id); - idle :: schedule(do_scan_path, scan); -} - -static void do_import_track(File &f, unsigned int lib_id) -{ - struct ImportData data; - std::string artist, album, genre; - unsigned int artist_id, album_id, genre_id, track_id, year, banned, tmp; - - data.filepath = f.getline(); - data.title = f.getline(); - - artist = f.getline(); - album = f.getline(); - f.getline(); /* comment */ - genre = f.getline(); - f.getline(); /* lenstr */ - f >> tmp /* id */ >> year >> data.track >> data.count; - f >> data.last_day >> data.last_month >> data.last_year >> data.length; - f >> tmp >> tmp >>tmp >> banned; /* bitrate, sample, channels, banned */ - f.getline(); /* get rest of line */ - - artist_id = artist_db.insert(library :: AGInfo(library :: DB_ARTIST, artist)); - album_id = album_db.insert(library :: Album(album, year, artist_id)); - genre_id = genre_db.insert(library :: AGInfo(library :: DB_GENRE, genre)); - track_id = track_db.insert(library :: Track(&data, lib_id, artist_id, - album_id, genre_id)); - library_db.at(lib_id)->size++; - - filter::add(artist_db.at(artist_id)->name, track_id); - filter::add(album_db.at(album_id)->name, track_id); - filter::add(track_db.at(track_id)->title, track_id); - get_callbacks()->on_library_track_add(track_id); - - if (banned == true) - get_callbacks()->on_library_import_ban(track_id); -} - -static void do_import_library(std::string &s) -{ - unsigned int id, next_id, size; - std::string path; - bool enabled; - File f(s, FILE_TYPE_LEGACY); - - print("Importing: %s\n", f.get_filepath()); - f.open(OPEN_READ); - - if (f.get_version() != 2) { - print("Version mismatch: %u != 2\n", f.get_version()); - return; - } - - path = f.getline(); - f >> id >> enabled >> next_id >> size; - - /* Assign this path a new id */ - if (library_db.find(path) != library_db.end()) { - print("Library already contains path: %s, skipping\n", path.c_str()); - return; - } - print("Adding path: %s\n", path.c_str()); - id = library_db.insert(library :: Library(path, enabled)); - get_callbacks()->on_library_add(id, &(*library_db.at(id))); - library_db.save(); - - f.getline(); /* Get rest of line */ - for (unsigned int i = 0; i < size; i++) - do_import_track(f, id); - save_all_dbs(); - get_callbacks()->on_library_update(id, &(*library_db.at(id))); - - library :: update_path(id); -} - - - -/* - * API used by the GUI begins here - */ - -void library :: init() -{ - album_db.load(); - artist_db.load(); - genre_db.load(); - library_db.load(); - track_db.load(); - - Database::iterator it; - for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) { - filter::add(artist_db.at((*it).artist_id)->name, it->id); - filter::add(album_db.at((*it).album_id)->name, it->id); - filter::add((*it).title, it->id); - - if (library_db.at((*it).library_id)->enabled) - get_callbacks()->on_library_track_add(it->id); - } - - Database::iterator l_it; - for (l_it = library_db.begin(); l_it != library_db.end(); l_it = library_db.next(l_it)) - get_callbacks()->on_library_add(l_it->id, &(*library_db.at(l_it->id))); -} - -void library :: add_path(const std::string &dir) -{ - unsigned int id; - if (g_file_test(dir.c_str(), G_FILE_TEST_IS_DIR) == false) - throw -E_INVAL; - if (library_db.find(dir) != library_db.end()) - return; - - id = library_db.insert(library :: Library(dir, true)); - library_db.save(); - - get_callbacks()->on_library_add(id, &(*library_db.at(id))); - update_path(id); -} - -void library :: del_path(unsigned int id) -{ - Database::iterator it; - - for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) { - if ((*it).library_id == id) { - get_callbacks()->on_library_track_del(it->id); - track_db.remove(it->id); - } - } - - library_db.remove(id); - - track_db.save(); - library_db.save(); -} - -void library :: update_path(unsigned int id) -{ - if (id > library_db.size()) - return; - if (library_db.at(id)->valid == false) - return; - do_update_library(id); -} - -void library :: update_all() -{ - Database::iterator it; - - for (it = library_db.begin(); it != library_db.end(); it = library_db.next(it)) - update_path(it - library_db.begin()); -} - -void library :: set_enabled(unsigned int id, bool enabled) -{ - unsigned int t; - Database::iterator it; - - library_db.at(id)->enabled = enabled; - library_db.save(); - - for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) { - if ((*it).library_id == id) { - t = it - track_db.begin(); - if (enabled) - get_callbacks()->on_library_track_add(t); - else - get_callbacks()->on_library_track_del(t); - } - } -} - -void library :: lookup(unsigned int id, library :: Song *song) -{ - if (id >= track_db.actual_size()) - throw -E_EXIST; - - song->track = &(*track_db.at(id)); - if (song->track->valid == false) - throw -E_EXIST; - - song->track_id = id; - song->artist = &(*artist_db.at(song->track->artist_id)); - song->album = &(*album_db.at(song->track->album_id)); - song->genre = &(*genre_db.at(song->track->genre_id)); - song->library = &(*library_db.at(song->track->library_id)); -} - -library :: Library *library :: lookup_path(unsigned int id) -{ - if (id >= library_db.actual_size()) - throw -E_EXIST; - if (library_db.at(id)->valid == false) - throw -E_EXIST; - return &(*library_db.at(id)); -} - -void library :: import() -{ - unsigned int i = 0; - std::string name; - - do { - std::stringstream ss; - ss << i; - - name = ss.str(); - File f(name, FILE_TYPE_LEGACY); - - if (f.exists() == false) - break; - - idle :: schedule(do_import_library, name); - ss.clear(); - i++; - } while (true); -} - -void library :: track_played(unsigned int id) -{ - time_t the_time = time(NULL); - struct tm *now = localtime(&the_time); - - track_db.at(id)->play_count++; - track_db.at(id)->last_day = now->tm_mday; - track_db.at(id)->last_month = now->tm_mon + 1; - track_db.at(id)->last_year = now->tm_year + 1900; - - track_db.save(); - get_callbacks()->on_library_track_updated(id); -} - -#ifdef CONFIG_TEST -void library :: print_db(DB_Type type) -{ - switch (type) { - case DB_ALBUM: - break; - case DB_ARTIST: - break; - case DB_GENRE: - break; - case DB_LIBRARY: - break; - case DB_TRACK: - break; - } -} - -void library :: reset() -{ -} -#endif /* CONFIG_TEST */ diff --git a/lib/playlist.cpp b/lib/playlist.cpp deleted file mode 100644 index b6d33487..00000000 --- a/lib/playlist.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include -#include -#include - -static std::set empty_set; -static Index playlist_db("playlist.db", false); -static Playqueue playlist_pq(PQ_ENABLED); -static std::string cur_pq; - -static void import_ban_track(unsigned int track_id) -{ - playlist :: add("Banned", track_id); -} - -void playlist :: init() -{ - std::set ids; - std::set::iterator it; - - playlist_pq.add_sort(SORT_ARTIST); - playlist_pq.add_sort(SORT_YEAR); - playlist_pq.add_sort(SORT_TRACK); - playlist_pq.set_flag(PQ_REPEAT); - playlist_pq.set_flag(PQ_NEVER_SORT); - - get_callbacks()->on_library_import_ban = import_ban_track; - - playlist_db.load(); - - ids = get_tracks("Banned"); - for (it = ids.begin(); it != ids.end(); it++) - get_callbacks()->on_playlist_ban(*it); - - if (cur_pq == "") - return; - - ids = get_tracks(cur_pq); - for (it = ids.begin(); it != ids.end(); it++) - playlist_pq.add(*it); -} - -void playlist :: add(const std::string &name, unsigned int track_id) -{ - if ((name == "Banned") || (name == "Favorites")) { - playlist_db.insert(name, track_id); - playlist_db.save(); - if (name == cur_pq) - playlist_pq.add(track_id); - if (name == "Banned") - get_callbacks()->on_playlist_ban(track_id); - } else - throw -E_EXIST; -} - -void playlist :: del(const std::string &name, unsigned int track_id) -{ - if ((name == "Banned") || (name == "Favorites")) { - playlist_db.remove(name, track_id); - playlist_db.save(); - if (name == cur_pq) - playlist_pq.del_track(track_id); - if (name == "Banned") - get_callbacks()->on_playlist_unban(track_id); - } else - throw -E_EXIST; -} - -void playlist :: select(const std::string &name) -{ - std::set ids = get_tracks(name); - std::set::iterator it; - - while (playlist_pq.size() > 0) - playlist_pq.del(0); - - for (it = ids.begin(); it != ids.end(); it++) - playlist_pq.add(*it); - cur_pq = name; -} - -const std::set &playlist :: get_tracks(const std::string &name) -{ - if ((name == "Banned") || (name == "Favorites")) { - Database::iterator it = playlist_db.find(name); - if (it != playlist_db.end()) - return it->values; - return empty_set; - } - throw -E_EXIST; -} - -Playqueue *playlist :: get_pq() -{ - return &playlist_pq; -} - -#ifdef CONFIG_TEST -void playlist :: clear() -{ -} -#endif /* CONFIG_TEST */ diff --git a/lib/playqueue.cpp b/lib/playqueue.cpp deleted file mode 100644 index 270fb260..00000000 --- a/lib/playqueue.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include -#include - -#include -#include -#include - -#define O_MINUTES (60) -#define O_HOURS (60 * O_MINUTES) -#define O_DAYS (24 * O_HOURS) - -Playqueue :: Playqueue() - : flags(0), cur(-1), length(0) -{ -} - -Playqueue :: Playqueue(playqueue_flags f) - : flags(f), cur(-1), length(0) -{ -} - -Playqueue :: ~Playqueue() -{ -} - -void Playqueue :: write(File &f) -{ - f << flags << " " << tracks.size(); - for (unsigned int i = 0; i < tracks.size(); i++) - f << " " << tracks[i]; -} - -void Playqueue :: read(File &f) -{ - unsigned int n; - f >> flags >> n; - tracks.resize(n); - for (unsigned int i = 0; i < n; i++) - f >> tracks[i]; -} - -void Playqueue :: set_flag(playqueue_flags f) -{ - flags |= f; - get_callbacks()->on_queue_changed(); -} - -void Playqueue :: unset_flag(playqueue_flags f) -{ - flags &= ~f; - get_callbacks()->on_queue_changed(); -} - -const unsigned int Playqueue :: get_flags() -{ - return flags; -} - -unsigned int Playqueue :: get_length() -{ - return length; -} - -static inline void add_duration(std::stringstream &ss, unsigned int dur, - unsigned int remaining, const std::string &field) -{ - if (dur > 0) { - ss << dur << " " << field; - if (dur > 1) - ss << "s"; - if (remaining > 0) - ss << ", "; - } -} - -std::string Playqueue :: get_length_str() -{ - std::stringstream ss; - unsigned int len = length; - - unsigned int days = len / O_DAYS; - len -= days * O_DAYS; - add_duration(ss, days, len, "day"); - - unsigned int hours = len / O_HOURS; - len -= hours *O_HOURS; - add_duration(ss, hours, len, "hour"); - unsigned int mins = len / O_MINUTES; - add_duration(ss, mins, len, "minute"); - unsigned int secs = len - (mins * O_MINUTES); - add_duration(ss, secs, 0, "second"); - - return ss.str(); -} - -/* - * Returns: - * 0: lhs == rhs - * < 0: lhs < rhs - * > 0: lhs > rhs - */ -static inline int compare_uint(unsigned int a, unsigned int b) -{ - if (a == b) - return 0; - if (a < b) - return -1; - return 1; -} - -/* - * Returns: - * 0: lhs == rhs - * < 0: lhs < rhs, or rhs is empty - * > 0: lhs > rhs, or lhs is empty - */ -static inline int compare_string(const std::string &a, const std::string &b) -{ - if (a.size() == 0) - return 1; - else if (b.size() == 0) - return -1; - return a.compare(b); -} - -/* - * std::string.compare() returns - * 0: Strings are equal - * < 0: a < b - * > 0: a > b - */ -static inline int track_compare(library :: Song &lhs, library :: Song &rhs, - sort_t field) -{ - switch (field) { - case SORT_ARTIST: - return compare_string(lhs.artist->key_lower, rhs.artist->key_lower); - case SORT_ALBUM: - return compare_string(lhs.album->name_lower, rhs.album->name_lower); - case SORT_COUNT: - return compare_uint(lhs.track->play_count, rhs.track->play_count); - case SORT_GENRE: - return compare_string(lhs.genre->key_lower, rhs.genre->key_lower); - case SORT_LENGTH: - return compare_uint(lhs.track->length, rhs.track->length); - case SORT_PLAYED: - return compare_uint(lhs.track->play_count, rhs.track->play_count); - case SORT_TITLE: - return compare_string(lhs.track->title_lower, rhs.track->title_lower); - case SORT_TRACK: - return compare_uint(lhs.track->track, rhs.track->track); - default: //case SORT_YEAR - int ret = compare_uint(lhs.album->year, rhs.album->year); - if (ret != 0) - return ret; - return compare_string(lhs.album->name_lower, rhs.album->name_lower); - } -} - -static bool track_less_than(library :: Song &lhs, library :: Song &rhs, - std::list &order) -{ - std::list::iterator it; - int res; - - for (it = order.begin(); it != order.end(); it++) { - if (it->ascending == true) - res = track_compare(lhs, rhs, it->field); - else - res = track_compare(rhs, lhs, it->field); - if (res != 0) - return res < 0; - } - return res; -} - -unsigned int Playqueue :: find_sorted_id(library :: Song &rhs) -{ - library :: Song lhs; - unsigned int begin = 0, end = (tracks.size() - 1), mid; - - if (tracks.size() == 0) - return 0; - - while (end > begin) { - mid = begin + ((end - begin) / 2); - library :: lookup(tracks[mid], &lhs); - if (track_less_than(lhs, rhs, sort_order)) - begin = mid + 1; - else { - if (mid == begin) - return begin; - else - end = mid - 1; - } - } - - library :: lookup(tracks[begin], &lhs); - if (track_less_than(lhs, rhs, sort_order)) - return begin + 1; - return begin; -} - -unsigned int Playqueue :: add(unsigned int track_id) -{ - unsigned int id = tracks.size(); - library :: Song song; - library :: lookup(track_id, &song); - - if (sort_order.size() > 0) - id = find_sorted_id(song); - - tracks.insert(tracks.begin() + id, track_id); - length += song.track->length; - get_callbacks()->on_queue_track_add(this, id); - if (!(flags & PQ_DISABLE_CHANGED_SIZE)) - get_callbacks()->on_queue_changed(); - return id; -} - -unsigned int Playqueue :: add_front(unsigned int track_id) -{ - library :: Song song; - tracks.insert(tracks.begin(), track_id); - - library :: lookup(track_id, &song); - length += song.track->length; - get_callbacks()->on_queue_track_add(this, 0); - if (!(flags & PQ_DISABLE_CHANGED_SIZE)) - get_callbacks()->on_queue_changed(); - return 0; -} - -void Playqueue :: del(unsigned int plist_id) -{ - library :: Song song; - unsigned int track_id = tracks[plist_id]; - - tracks.erase(tracks.begin() + plist_id); - library :: lookup(track_id, &song); - length -= song.track->length; - get_callbacks()->on_queue_track_del(this, plist_id); - if (!(flags & PQ_DISABLE_CHANGED_SIZE)) - get_callbacks()->on_queue_changed(); -} - -void Playqueue :: del_track(unsigned int track_id) -{ - unsigned int i = 0; - while (i < tracks.size()) { - if (tracks[i] == track_id) - del(i); - else - i++; - } -} - -void Playqueue :: track_updated(unsigned int track_id) -{ - for (unsigned int i = 0; i < tracks.size(); i++) { - if (tracks[i] == track_id) - get_callbacks()->on_queue_track_changed(this, i); - } -} - -unsigned int Playqueue :: size() -{ - return tracks.size(); -} - -void Playqueue :: recalculate_length() -{ - library :: Song song; - - length = 0; - for (unsigned int i = 0; i < size(); i++) { - library::lookup(tracks[i], &song); - length += song.track->length; - } -} - - -/* Sorting function */ -class SortTracks { -private: - std::list fields; -public: - SortTracks(std::list f) : fields(f) {} - bool operator()(unsigned int a, unsigned int b) - { - library::Song lhs, rhs; - library :: lookup(a, &lhs); - library :: lookup(b, &rhs); - return track_less_than(lhs, rhs, fields); - } -}; - -void Playqueue :: _add_sort(sort_t field, bool ascending) -{ - struct sort_info info; - std::list::iterator it; - - /* Is field already in the sort_order? */ - for (it = sort_order.begin(); it != sort_order.end(); it++) { - if (it->field == field) { - it->ascending = !it->ascending; - return; - } - } - - info.field = field; - info.ascending = ascending; - sort_order.push_back(info); - if (sort_order.size() >= 4) - sort_order.erase(sort_order.begin()); -} - -void Playqueue :: add_sort(sort_t field, bool ascending) -{ - if (flags & PQ_NEVER_SORT) - return; - - _add_sort(field, ascending); - std::stable_sort(tracks.begin(), tracks.end(), SortTracks(sort_order)); - - for (unsigned int i = 0; i < tracks.size(); i++) - get_callbacks()->on_queue_track_changed(this, i); - get_callbacks()->on_queue_changed(); -} - -void Playqueue :: reset_sort(sort_t field, bool ascending) -{ - if (flags & PQ_NEVER_SORT) - return; - if (sort_order.front().field != field) - sort_order.clear(); - add_sort(field, ascending); -} - -void Playqueue :: force_clear_sort() -{ - sort_order.clear(); -} - -std::list &Playqueue :: get_sort_order() -{ - return sort_order; -} - -unsigned int Playqueue :: operator[](unsigned int i) -{ - return tracks[i]; -} - -unsigned int Playqueue :: next() -{ - unsigned int res; - - if (tracks.size() == 0) - throw -E_EXIST; - else if (tracks.size() == 1) - cur = 0; - else if (flags & PQ_RANDOM) - cur += rand() % (tracks.size() / 2) + 1; - else - cur++; - - if (cur >= tracks.size()) - cur -= tracks.size(); - - res = tracks[cur]; - if (!(flags & PQ_REPEAT)) { - del(cur); - cur--; - } - return res; -} - -void Playqueue :: set_cur(unsigned int c) -{ - cur = c; -} - -void Playqueue :: path_selected(unsigned int id) -{ - cur = id; - if (!(flags &PQ_REPEAT)) { - del(cur); - cur--; - } -} - -#ifdef CONFIG_TEST -void Playqueue :: reset() -{ - tracks.clear(); - set_cur(0); -} -#endif /* CONFIG_TEST */ diff --git a/share/ocarina/ocarina6.glade b/share/ocarina/ocarina6.glade index 84e81fc6..6e30b166 100644 --- a/share/ocarina/ocarina6.glade +++ b/share/ocarina/ocarina6.glade @@ -212,7 +212,7 @@ True False - Ocarina 6.0 + Ocarina 6.1 1024 683 @@ -1242,51 +1242,6 @@ Manager 1 - - - True - True - True - - - True - False - True - - - True - False - gtk-convert - - - False - True - 0 - - - - - True - False - Import - center - - - False - True - 1 - - - - - - - False - True - 5 - 2 - - False diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..b3151414 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,14 @@ +version +file +database +index +filter +idle +tags +random +queue +library +playlist +deck +driver +audio diff --git a/tests/Data/album.db b/tests/Data/album.db new file mode 100644 index 00000000..97cfee0c --- /dev/null +++ b/tests/Data/album.db @@ -0,0 +1,8 @@ +0 +6 +1 1993 Link's Awakening +1 1998 Link's Awakening DX +1 2002 Wind Waker +1 2013 Wind Waker HD +1 2006 Twilight Princess GC +1 2006 Twilight Princess Wii diff --git a/tests/Data/artist.db b/tests/Data/artist.db new file mode 100644 index 00000000..d7450f4a --- /dev/null +++ b/tests/Data/artist.db @@ -0,0 +1,5 @@ +0 +3 +1 Link's Awakening +1 Wind Waker +1 Twilight Princess diff --git a/tests/Data/cur_track b/tests/Data/cur_track new file mode 100644 index 00000000..00750edc --- /dev/null +++ b/tests/Data/cur_track @@ -0,0 +1 @@ +3 diff --git a/tests/Data/deck b/tests/Data/deck new file mode 100644 index 00000000..0449cb69 --- /dev/null +++ b/tests/Data/deck @@ -0,0 +1,4 @@ +0 +2 3 0 1 8 1 7 1 2 +1 4 0 1 2 3 +1 5 4 5 6 7 8 diff --git a/tests/Data/genre.db b/tests/Data/genre.db new file mode 100644 index 00000000..f84d51ee --- /dev/null +++ b/tests/Data/genre.db @@ -0,0 +1,3 @@ +0 +1 +1 Zelda Music diff --git a/tests/Data/library.db b/tests/Data/library.db new file mode 100644 index 00000000..0c0130e9 --- /dev/null +++ b/tests/Data/library.db @@ -0,0 +1,3 @@ +0 +1 +1 1 /home/Zelda/Music diff --git a/tests/Data/playlist.db b/tests/Data/playlist.db new file mode 100644 index 00000000..315bdfb2 --- /dev/null +++ b/tests/Data/playlist.db @@ -0,0 +1,6 @@ +0 +2 +1 Banned +4 0 1 2 3 +1 Favorites +8 16 17 18 19 20 21 22 23 diff --git a/tests/Data/track.db b/tests/Data/track.db new file mode 100644 index 00000000..92ef7a65 --- /dev/null +++ b/tests/Data/track.db @@ -0,0 +1,50 @@ +0 +24 +1 0 0 0 0 1 2001 1 10 0 100 Koholint Island +Links Awakening/1 - Koholint Island.ogg +1 0 0 0 0 2 2002 2 11 1 110 Animal Village +Links Awakening/2 - Animal Village.ogg +1 0 0 0 0 3 2003 3 12 2 120 Dream Shrine +Links Awakening/3 - Dream Shrine.ogg +1 0 0 0 0 4 2004 4 13 3 130 Goponga Swamp +Links Awakening/4 - Goponga Swamp.ogg +1 0 0 1 0 1 2003 5 14 4 140 Kanalet Castle +Links Awakening/1 - Kanalet Castle.ogg +1 0 0 1 0 2 2004 6 15 5 150 Mabe Village +Links Awakening/2 - Mabe Village.ogg +1 0 0 1 0 3 2005 7 16 6 160 Mt. Tamaranch +Links Awakening/3 - Mt Tamaranch.ogg +1 0 0 1 0 4 2006 8 17 7 170 Tal Tal Heights +Links Awakening/4 - Tal Tal Heights.ogg +1 0 1 2 0 1 2005 9 18 8 180 Dragon Roost Island +Wind Waker/1 - Dragon Roost Island.ogg +1 0 1 2 0 2 2006 1 19 9 190 Eastern Fairy Island +Wind Waker/2 - Eastern Fairy Island.ogg +1 0 1 2 0 3 2007 2 20 0 200 Forsaken Fortress +Wind Waker/3 - Forsaken Fortress.ogg +1 0 1 2 0 4 2008 3 21 1 210 Ghost Ship +Wind Waker/4 - Ghost Ship.ogg +1 0 1 3 0 1 2007 4 22 2 220 Outset Isle +Wind Waker/1 - Outset Isle.ogg +1 0 1 3 0 2 2008 5 23 3 230 Tingle Island +Wind Waker/2 - Tingle Island.ogg +1 0 1 3 0 3 2009 6 24 4 240 Tower of the Gods +Wind Waker/3 - Tower of the Gods.ogg +1 0 1 3 0 4 2010 7 25 5 250 Windfall Island +Wind Waker/4 - Windfall Island.ogg +1 0 2 4 0 1 2009 8 26 6 260 City in the Sky +Twilight Princess/1 - City in the Sky.ogg +1 0 2 4 0 2 2010 9 27 7 270 Gerudo Desert +Twilight Princess/2 - Gerudo Desert.ogg +1 0 2 4 0 3 2011 1 28 8 280 Arbiter's Grounds +Twilight Princess/3 - Arbiters Grounds.ogg +1 0 2 4 0 4 2012 2 29 9 290 Bridge of Eldin +Twilight Princess/4 - Bridge of Eldin.ogg +1 0 2 5 0 1 2011 3 30 0 300 Death Mountain +Twilight Princess/1 - Death Mountain.ogg +1 0 2 5 0 2 2012 4 10 1 310 Hidden Village +Twilight Princess/2 - Hidden Village.ogg +1 0 2 5 0 3 2013 5 11 2 320 Malo Mart +Twilight Princess/3 - Malo Mart.ogg +1 0 2 5 0 4 2014 6 12 3 330 Faron Woods +Twilight Princess/4 - Faron Woods.ogg diff --git a/tests/Music/1.ogg b/tests/Music/1.ogg new file mode 100644 index 00000000..5e94d369 Binary files /dev/null and b/tests/Music/1.ogg differ diff --git a/tests/Music/10.ogg b/tests/Music/10.ogg new file mode 100644 index 00000000..6f2e4fcd Binary files /dev/null and b/tests/Music/10.ogg differ diff --git a/tests/Music/15.ogg b/tests/Music/15.ogg new file mode 100644 index 00000000..99bb15b7 Binary files /dev/null and b/tests/Music/15.ogg differ diff --git a/tests/Music/60.ogg b/tests/Music/60.ogg new file mode 100644 index 00000000..16b85beb Binary files /dev/null and b/tests/Music/60.ogg differ diff --git a/tests/Music/600.ogg b/tests/Music/600.ogg new file mode 100644 index 00000000..c7e36082 Binary files /dev/null and b/tests/Music/600.ogg differ diff --git a/tests/Music/666.ogg b/tests/Music/666.ogg new file mode 100644 index 00000000..3586665e Binary files /dev/null and b/tests/Music/666.ogg differ diff --git a/tests/Music/90.ogg b/tests/Music/90.ogg new file mode 100644 index 00000000..abeceb9a Binary files /dev/null and b/tests/Music/90.ogg differ diff --git a/tests/Sconscript b/tests/Sconscript index 25fbea6c..26d5c6a8 100644 --- a/tests/Sconscript +++ b/tests/Sconscript @@ -1,23 +1,100 @@ #!/usr/bin/python -import sys -Import("env") +import sys, os +Import("test_env") +tests = [ -if sys.argv.count("tests") > 0: - env.Append( CCFLAGS = [ "-DCONFIG_TEST" ] ) +### +# +# (source.cpp, use collected lib_files?, [ other files ], [ other packages ]) +# -src = SConscript("src/Sconscript") + ("version.cpp", False, [], [ "glib-2.0" ]), + ("file.cpp", True, [], []), + ("database.cpp", True, [], []), + ("index.cpp", True, [], []), + ("filter.cpp", True, [], []), + ("idle.cpp", False, [ "idle.cpp" ], []), + ("tags.cpp", True, [], [ "taglib" ]), + ("random.cpp", False, [ "random.cpp" ], []), + ("queue.cpp", True, [ "callback.cpp", "random.cpp" ], []), + ("library.cpp", True, [ "idle.cpp" ], []), + ("playlist.cpp", True, [], []), + ("deck.cpp", True, [], []), + ("driver.cpp", False, [ "driver.cpp" ], []), + ("audio.cpp", True, [ "driver.cpp" ], []), -tests = [ "version", "file", "db_entry", "database", "index", "filter", "idle" ] -#scripts = [ "playlist", "library", "playqueue", "deck", "audio", "gui" ] +] -prev = None +env = test_env +env.UsePackage("glib-2.0") +if env.Coverage == True: + env.Append( CCFLAGS = [ "--coverage" ] ) + env.Append( LINKFLAGS = [ "-lgcov", "-coverage" ] ) +check_depends = True -for test in tests: - t = Command("%s.out" % test, [], "./tests/%s" % test) +for arg in sys.argv: + if arg.find("tests") == 0 and len(arg) > 5: + check_depends = False + break - if prev: - Depends(t, prev) - Depends(t, src) - AlwaysBuild(t) - prev = t +def expand_files(extra_files): + res = [] + for f in extra_files: + res += [ env.Object("%s-core" % f, "../core/%s" % f) ] + return res + +def make_program(src, name, extra_files): + return env.Program("%s" % name, [ src ] + expand_files(extra_files)) + +def make_test(src, name): + cmd = "./tests/%s | tee ./tests/%s.out" % (name, name) + if env.Valgrind == True: + cmd = "valgrind -q --leak-check=full --error-exitcode=42 %s" % cmd + + test = Command("%s.out" % name, [], "set -o pipefail; %s" % cmd) + Alias("tests/%s" % name, test) + AlwaysBuild(test) + return test + +def prepare_test(name, src, extra_files): + exe = make_program(src, name, extra_files) + test = make_test(src, name) + Depends(test, exe) + return test + +res = [] +lib_files = [] + +ignore = open(".gitignore", 'w') +for src, lib, extra, pkgs in tests: + name = "%s" % src.rsplit(".")[0] + + if lib == True: + lib_files += [ src ] + extra + extra = lib_files + + for p in pkgs: + env.UsePackage(p) + + test = prepare_test(name, src, extra) + if (check_depends == True) and (len(res) > 0): + Depends(test, res[len(res) - 1]) + + res += [ test ] + ignore.write(name + "\n") +ignore.close(); + +if env.Coverage == True: + cov = Command("ocarina.gcov", [], "gcov -r tests/*.gcda") + Depends(cov, res[len(res) - 1]) + res += [ cov ] + +if env.CppCheck == True: + check = Command("cpp.check", [], "cppcheck -q .") + Depends(check, res[len(res) - 1]) + res += [ check ] + +Return("res") + +##scripts = [ "library", "deck", "audio", "gui" ] diff --git a/tests/_functions b/tests/_functions deleted file mode 100644 index ac9e6d25..00000000 --- a/tests/_functions +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/bash -# Copyright 2014 (c) Anna Schumaker - -function read_config -{ - cat ../Sconstruct | grep ^$1 | awk -F= '{print $2}' | tr -d ' ' -} - -function config_version -{ - read_config CONFIG_VERSION -} - -function config_debug -{ - read_config CONFIG_DEBUG -} - -CUR_TEST=0 -function new_test -{ - echo "$1" - CUR_TEST=0 -} - -function start_test -{ - echo -n " $CUR_TEST: " - let CUR_TEST=($CUR_TEST + 1) -} - -function assert_equal -{ - if [ "$1" == "$2" ]; then - echo "Success!" - return 0 - else - echo "FAILED =(" - echo " Expected: $2" - echo " Actual: $1" - return 1 - fi -} - -function test_equal -{ - start_test - assert_equal "$($1)" "$2" -} - -function on_exit -{ - ret=$? - echo - return $ret -}; trap "on_exit" EXIT - -[ -z $HOME ] && HOME=$(cat /etc/passwd | grep $(whoami) | awk -F: '{print $6}') -[ -z $XDG_DATA_HOME] && XDG_DATA_HOME="$HOME/.local/share" -DATA_DIR="$XDG_DATA_HOME/ocarina-test" -LEGACY_DIR="$HOME/.ocarina-test/library" - -rm -rf $DATA_DIR 2>/dev/null || true - -set -e -cd $(dirname $0) diff --git a/tests/audio.cpp b/tests/audio.cpp new file mode 100644 index 00000000..efda10a5 --- /dev/null +++ b/tests/audio.cpp @@ -0,0 +1,170 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include +#include +#include "test.h" + +Track *TRACK_NULL = NULL; + +void test_pre_init() +{ + TestDriver *driver = (TestDriver *)driver :: get_driver(); + + test_equal(audio :: current_track(), TRACK_NULL); + + audio :: play(); + test_equal(driver->playing, false); + + driver->playing = true; + audio :: pause(); + test_equal(driver->playing, true); + audio :: stop(); + test_equal(driver->playing, true); + driver->playing = false; + + audio :: seek_to(4242); + test_equal(driver->position(), (long)0); + + driver->cur_pos = 4242; + test_equal(audio :: position(), (long)0); + driver->cur_pos = 0; + + driver->cur_duration = 4242; + test_equal(audio :: duration(), (long)0); + driver->cur_duration = 0; + + audio :: next(); + test_equal(audio :: current_track(), TRACK_NULL); + + audio :: prev(); + test_equal(audio :: current_track(), TRACK_NULL); +} + +void test_init(int argc, char **argv) +{ + Track *track; + + test :: cp_data_dir(); + audio :: init(&argc, &argv); + + track = audio :: current_track(); + test_equal(track, TRACK_NULL); + + tagdb :: init(); + library :: init(); + audio :: init(&argc, &argv); + + track = audio :: current_track(); + test_not_equal(track, TRACK_NULL); +} + +void test_playback_controls() +{ + TestDriver *driver = (TestDriver *)driver :: get_driver(); + + audio :: play(); + test_equal(driver->playing, true); + + audio :: pause(); + test_equal(driver->playing, false); + + audio :: seek_to(4242); + test_equal(driver->cur_pos, (long)4242); + test_equal(audio :: position(), (long)4242); + + audio :: play(); + audio :: stop(); + test_equal(driver->playing, false); + test_equal(driver->cur_pos, (long)0); + + audio :: seek_to(4242); + driver->cur_duration = 424242; + test_equal(audio :: position(), (long)4242); + test_equal(audio :: duration(), (long)424242); + + audio :: seek_to(83 * O_SECOND); + test_equal(audio :: position_str(), (std::string)"1:23"); +} + +void test_track_controls() +{ + Track *track = NULL; + + TestDriver *driver = (TestDriver *)driver :: get_driver(); + library :: get_queue()->unset_flag(Q_RANDOM); + + audio :: pause(); + audio :: next(); + test_not_equal(audio :: current_track()->id, (unsigned)2); + test_equal(driver->is_playing(), false); + + audio :: play(); + audio :: next(); + test_equal(driver->is_playing(), true); + + audio :: load_track(track); + test_not_equal(audio :: current_track(), track); + + track = tagdb :: lookup(0); + audio :: seek_to(4242); + audio :: load_track(track); + test_equal(driver->is_playing(), true); + test_equal(audio :: position(), (long)0); + + audio :: seek_to(4242); + audio :: load_track(track); + test_equal(driver->is_playing(), true); + test_equal(audio :: position(), (long)4242); + + driver->error(); + test_not_equal(audio :: current_track(), track); + + track = audio :: current_track(); + driver->eos(); + test_not_equal(audio :: current_track(), track); +} + +void test_autopause() +{ + TestDriver *driver = (TestDriver *)driver :: get_driver(); + + audio :: play(); + test_equal(audio :: pause_enabled(), false); + test_equal(audio :: pause_count(), (unsigned)0); + + audio :: pause_after(true, 3); + test_equal(audio :: pause_enabled(), true); + test_equal(audio :: pause_count(), (unsigned)3); + + audio :: pause_after(false, 3); + test_equal(audio :: pause_enabled(), false); + test_equal(audio :: pause_count(), (unsigned)3); + + audio :: pause_after(false, 5); + test_equal(audio :: pause_enabled(), true); + test_equal(audio :: pause_count(), (unsigned)5); + + for (int i = 4; i >= 0; i--) { + driver->eos(); + test_equal(audio :: pause_enabled(), true); + test_equal(audio :: pause_count(), (unsigned)i); + test_equal(driver->is_playing(), true); + } + + driver->eos(); + test_equal(audio :: pause_enabled(), false); + test_equal(audio :: pause_count(), (unsigned)0); + test_equal(driver->is_playing(), false); +} + +int main(int argc, char **argv) +{ + run_test("Test Audio Pre-Init", test_pre_init); + run_test("Test Audio Init", test_init, argc, argv); + run_test("Test Audio Playback Controls", test_playback_controls); + run_test("Test Audio Track Controls", test_track_controls); + run_test("Test Audio Automatic Pausing", test_autopause); + return 0; +} diff --git a/tests/audio/Sconscript b/tests/audio/Sconscript deleted file mode 100644 index 4cc17946..00000000 --- a/tests/audio/Sconscript +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/python -Import("Test", "CONFIG") - -CONFIG.AUDIO = True - -Test("audio", "audio.cpp") diff --git a/tests/audio/audio.cpp b/tests/audio/audio.cpp deleted file mode 100644 index 7a4b7a32..00000000 --- a/tests/audio/audio.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include -#include -#include -#include - -#include -#include -#include - -void gen_library() -{ - system("tests/library/gen_library.sh"); - print("\n"); -} - -void check_ret(const std :: string &test, bool ret, bool expected) -{ - print("Test %s: ", test.c_str()); - if (ret == expected) - print("Success!\n"); - else - print("Failed.\n"); -} - -template -void check_ret(const std :: string &test, long ret, long expected) -{ - print("Test %s: ", test.c_str()); - if (ret == expected) - print("Success!\n"); - else - print("Failed (Expected %ld but got %ld)\n", expected, ret); -} - -void check_error(int error, int expected) -{ - if (expected == 0) { - if (error == 0) - print("Success!\n"); - else - print("Failed with error: %d\n", error); - } else { - if (error == 0) - print("Failed (expected error: %d)\n", expected); - else - print("Success!\n"); - } -} - -void call_func(const std :: string &test, void (*func)(), int expected) -{ - print("Test %s: ", test.c_str()); - try { - func(); - check_error(0, expected); - } catch (int error) { - check_error(error, expected); - } -} - -void call_func(const std :: string &test, void (*func)(long), long arg, int expected) -{ - print("Test %s: ", test.c_str()); - try { - func(arg); - check_error(0, expected); - } catch (int error) { - check_error(error, expected); - } -} - -/* Call various functions without a track loaded */ -void test_0() -{ - call_func("0a", audio :: play, 0); - call_func("0b", audio :: pause, 0); - call_func("0c", audio :: seek_to, 10, 0); - call_func("0d", audio :: next, -E_EXIST); - call_func("0e", audio :: stop, 0); - check_ret("0f", audio :: position(), 0); - check_ret("0g", audio :: duration(), 0); - try { - print("Test 0h: "); - audio :: current_trackid(); - check_error(0, -E_EXIST); - } catch (int error) { - check_error(error, -E_EXIST); - } - call_func("0i", audio :: previous, -E_EXIST); - print("\n"); -} - -void test_1() -{ - library :: Song song; - library :: lookup(0, &song); - - call_func("1a", audio :: next, 0); - call_func("1b", audio :: play, 0); - call_func("1c", audio :: pause, 0); - call_func("1d", audio :: seek_to, 1 * GST_SECOND, 0); - call_func("1e", audio :: stop, 0); - check_ret("1f", audio :: current_trackid() == 0, true); - check_ret("1g", audio :: position(), 0); - call_func("1h", audio :: previous, 0); - check_ret("1i", audio :: current_trackid() == 0, true); - audio :: next(); - audio :: next(); - call_func("1j", audio :: previous, 0); - check_ret("1k", audio :: current_trackid() == 1, true); - call_func("1l", audio :: previous, 0); - check_ret("1m", audio :: current_trackid() == 0, true); - print("\n"); -} - -/* Test pause_after() */ -unsigned int test_2_count = 0; -int test_2_cb(gpointer data) -{ - long seek_pos, pos, max; - library :: Song song; - GMainLoop *loop = (GMainLoop *)data; - - library :: lookup(audio :: current_trackid(), &song); - pos = audio :: position(); - - switch (test_2_count) { - case 0: - break; - case 1: - check_ret("2g", audio :: duration(), song.track->length * GST_SECOND); - seek_pos = (song.track->length * GST_SECOND) - GST_SECOND; - call_func("2h", audio :: seek_to, seek_pos, 0); - break; - case 2: - max = (song.track->length * GST_SECOND) - GST_SECOND + (501 * GST_MSECOND); - - check_ret("2i", pos <= max, true); - call_func("2j", audio :: stop, 0); - break; - case 3: - check_ret("2k", pos, 0); - call_func("2l", audio :: play, 0); - call_func("2m", audio :: seek_to, audio :: duration() - 1, 0); - break; - case 4: - check_ret("2n", audio :: pause_count(), (long)2); - call_func("2o", audio :: seek_to, audio :: duration() - 1, 0); - break; - case 5: - check_ret("2p", audio :: pause_count(), (long)1); - call_func("2q", audio :: seek_to, audio :: duration() - 1, 0); - break; - case 6: - check_ret("2r", audio :: pause_count(), (long)0); - call_func("2s", audio :: seek_to, audio :: duration() - 1, 0); - break; - case 7: - check_ret("2t", audio :: pause_enabled(), false); - check_ret("2u", audio :: pause_count(), (long)0); - break; - case 8: - pos = audio :: position(); - check_ret("2v", (0 <= pos) && (pos <= GST_MSECOND), true); - break; - case 9: - pos = audio :: position(); - check_ret("2w", (0 <= pos) && (pos <= GST_MSECOND), true); - default: - g_main_quit(loop); - } - - test_2_count++; - return true; -} - -void test_2() -{ - GMainLoop *loop; - - check_ret("2a", audio :: pause_enabled(), false); - check_ret("2b", audio :: pause_count(), (long)0); - audio :: pause_after(true, 3); - check_ret("2c", audio :: pause_enabled(), true); - check_ret("2d", audio :: pause_count(), (long)3); - audio :: next(); - check_ret("2e", audio :: pause_enabled(), true); - check_ret("2f", audio :: pause_count(), (long)3); - - audio :: play(); - loop = g_main_loop_new(NULL, FALSE); - g_timeout_add(500, test_2_cb, loop); - g_main_loop_run(loop); -} - -int main(int argc, char **argv) -{ - Playqueue *pqueue; - - gen_library(); - - /* Initialize before testing */ - audio :: init(&argc, &argv); - test_0(); - - /* Read in library, set up a playlist */ - library::init(); - deck::init(); - library :: reset(); - library :: add_path("/tmp/library/0"); - while (idle :: run_task()); - - pqueue = deck :: create(false); - for (unsigned int i = 0; i < 150; i++) - pqueue->add(i); - - test_1(); - test_2(); - - audio :: quit(); - return 0; -} diff --git a/tests/audio/audio.good b/tests/audio/audio.good deleted file mode 100644 index 9d1fbc72..00000000 --- a/tests/audio/audio.good +++ /dev/null @@ -1,53 +0,0 @@ -Generating library: 0 -Generating library: 1 -Generating library: 2 -Generating library: 3 -Generating library: 4 - -Test 0a: Success! -Test 0b: Success! -Test 0c: Success! -Test 0d: Success! -Test 0e: Success! -Test 0f: Success! -Test 0g: Success! -Test 0h: Success! -Test 0i: Success! - -Test 1a: Success! -Test 1b: Success! -Test 1c: Success! -Test 1d: Success! -Test 1e: Success! -Test 1f: Success! -Test 1g: Success! -Test 1h: Success! -Test 1i: Success! -Test 1j: Success! -Test 1k: Success! -Test 1l: Success! -Test 1m: Success! - -Test 2a: Success! -Test 2b: Success! -Test 2c: Success! -Test 2d: Success! -Test 2e: Success! -Test 2f: Success! -Test 2g: Success! -Test 2h: Success! -Test 2i: Success! -Test 2j: Success! -Test 2k: Success! -Test 2l: Success! -Test 2m: Success! -Test 2n: Success! -Test 2o: Success! -Test 2p: Success! -Test 2q: Success! -Test 2r: Success! -Test 2s: Success! -Test 2t: Success! -Test 2u: Success! -Test 2v: Success! -Test 2w: Success! diff --git a/tests/database b/tests/database deleted file mode 100755 index 2dfac707..00000000 --- a/tests/database +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Copyright 2014 (c) Anna Schumaker - -. $(dirname $0)/_functions - -function test_autosave -{ - new_test "Database Test (n = $1, autosave = true)" - src/database.run -a $1 - if [ ! -f $DATA_DIR/database.db ] && [ $1 != 0 ]; then - echo "ERROR: $DATA_DIR/database.db doesn't exist!" - exit 1 - fi -} - -function test_noautosave -{ - new_test "Database Test (n = $1, autosave = false)" - src/database.run $1 - if [ -f $DATA_DIR/database.db ]; then - echo "ERROR: $DATA_DIR/database.db exists!" - exit 1 - fi -} - -function run_test -{ - rm $DATA_DIR/* 2>/dev/null || true - - if [ $1 -le 1000 ]; then - test_autosave $1 - else - test_noautosave $1 - fi -} - -run_test 0 -echo -run_test 10 -echo -run_test 100 -echo -run_test 1000 -echo -run_test 10000 -echo -run_test 100000 diff --git a/tests/database.cpp b/tests/database.cpp new file mode 100644 index 00000000..b8ced050 --- /dev/null +++ b/tests/database.cpp @@ -0,0 +1,305 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + * Test a Database + */ + +#include +#include "test.h" + +#include + + +/*** + * + * Derive a DatabaseEntry for storing ints + * + */ + +class IntEntry : public DatabaseEntry { +public: + unsigned int val; + + IntEntry(); + IntEntry(unsigned int); + const std::string primary_key() const; + void write(File &); + void read(File &); +}; + +IntEntry :: IntEntry() : val(0) {} +IntEntry :: IntEntry(unsigned int v) : val(v) {} + +const std::string IntEntry :: primary_key() const +{ + std::stringstream ss; + ss << val; + return ss.str(); +} + +void IntEntry :: write(File &f) { f << val; } +void IntEntry :: read(File &f) { f >> val; } + +static IntEntry *INT_NULL = NULL; + + + +/*** + * + * Struct for passing around arguments to tests + * + */ + +struct TestArgs { + const unsigned int n; + unsigned int size; + unsigned int actual; + bool autosave; + + IntEntry *one; + IntEntry *three; + Database *db; + + TestArgs(const unsigned int, bool, Database *); +}; + +TestArgs :: TestArgs(const unsigned int _n, bool _asv, Database *_db) + : n(_n), size(0), actual(0), autosave(_asv), + one(NULL), three(NULL), db(_db) +{} + + +/*** + * + * Run tests with varying database sizes + * + */ + +static void test_insertion(struct TestArgs *args) +{ + /* + * Initial size should be 0 + */ + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); + + test :: begin(); + for (unsigned int i = 0; i < args->n; i++) { + IntEntry *it = args->db->insert(IntEntry(i)); + check_not_equal(it, INT_NULL); + check_equal(it->id, i); + check_equal(it->val, i); + + /* + * Pointers should still be valid after more insertions. + * These will be checked later + */ + if (i == 1) + args->one = it; + if (i == 3) + args->three = it; + + args->size++; + args->actual++; + } + test :: success(); + + /* + * Size should change + */ + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); +} + +static void test_insertion2(struct TestArgs *args) +{ + test :: begin(); + for (unsigned int i = 0; i < args->n; i++) { + IntEntry *it = args->db->insert(IntEntry(i)); + check_equal(it, INT_NULL); + } + test :: success(); + + /* + * Size should not change + */ + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); +} + +static void test_removal(struct TestArgs *args) +{ + test :: begin(); + for (unsigned int i = 0; i < args->n; i+=2) { + args->db->remove(i); + args->size--; + } + test :: success(); + + /* + * Size should change + */ + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); + + /* + * Test out-of-bounds removal + */ + test :: begin(); + for (unsigned int i = 0; i < 10; i++) + args->db->remove(args->n + i); + test :: success(); + + /* + * Size should not change + */ + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); +} + +static void test_removal2(struct TestArgs *args) +{ + test :: begin(); + for (unsigned int i = 0; i < args->n; i+=2) + args->db->remove(i); + test :: success(); + + /* + * Size should not change + */ + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); +} + +static void test_iterator(struct TestArgs *args) +{ + unsigned int index = 1; + Database::iterator it; + + test :: begin(); + for (it = args->db->begin(); it != args->db->end(); it = args->db->next(it)) { + check_not_equal((*it), INT_NULL); + check_equal((*it)->val, index); + check_equal((*it)->id, index); + index += 2; + }; + test :: success(); +} + +static void test_access(struct TestArgs *args) +{ + test :: begin(); + for (unsigned int i = 0; i < args->n; i+=2) { + IntEntry *it = args->db->at(i); + check_equal(it, INT_NULL); + } + test :: success(); + + test :: begin(); + for (unsigned int i = 1; i < args->n; i+=2) { + IntEntry *it = args->db->at(i); + check_not_equal(it, INT_NULL); + } + test :: success(); + + test :: begin(); + for (unsigned int i = 0; i < 10; i++) { + IntEntry *it = args->db->at(args->n + i); + check_equal(it, INT_NULL); + } + test :: success(); +} + +static void test_pointers(struct TestArgs *args) +{ + IntEntry *it = args->db->at(1); + test_equal(it, args->one); + + it = args->db->at(3); + test_equal(it, args->three); +} + +static void test_insertion3(struct TestArgs *args) +{ + test :: begin(); + for (unsigned int i = 0; i < args->n; i+=2) { + IntEntry *it = args->db->insert(IntEntry(i)); + + args->size++; + args->actual++; + check_equal(it->id, args->n + (i / 2)); + } + test :: success(); + + /* + * Size should change + */ + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); +} + +static void test_autosave(struct TestArgs *args) +{ + Database db2("database.db", args->autosave); + db2.load(); + + if (args->autosave == false) { + test_equal(db2.size(), (unsigned)0); + test_equal(db2.actual_size(), (unsigned)0); + + args->db->save(); + db2.load(); + } + + test_equal(db2.size(), args->db->size()); + test_equal(db2.actual_size(), args->db->actual_size()); + + Database::iterator it1; + Database::iterator it2 = db2.begin(); + + test :: begin(); + for (it1 = args->db->begin(); it1 != args->db->end(); it1++) { + if (*it1 == INT_NULL) + check_equal(*it2, INT_NULL); + else { + check_not_equal(*it2, INT_NULL); + check_equal((*it1)->id, (*it2)->id); + check_equal((*it1)->val, (*it2)->val); + } + it2++; + } + test :: success(); +} + +static void db_test(unsigned int n, bool autosave) +{ + test :: rm_data_dir(); + + std::stringstream ss; + ss << " (n = " << n << ")"; + const std::string n_str = ss.str(); + + Database db("database.db", autosave); + struct TestArgs args(n, autosave, &db); + + run_test("Database Insertion Test" + n_str, test_insertion, &args); + run_test("Database Second Insertion Test" + n_str, test_insertion2, &args); + run_test("Database Removal Test" + n_str, test_removal, &args); + run_test("Database Second Removal Test" + n_str, test_removal2, &args); + run_test("Database Iterator Test" + n_str, test_iterator, &args); + run_test("Database Access Test" + n_str, test_access, &args); + run_test("Database Pointer Test" + n_str, test_pointers, &args); + run_test("Database Third Insertion Test" + n_str, test_insertion3, &args); + run_test("Database Save and Load Test" + n_str, test_autosave, &args); +} + +int main(int argc, char **argv) +{ + db_test(0, true); + db_test(10, true); + db_test(100, true); + db_test(1000, true); + db_test(10000, false); + db_test(100000, false); + + return 0; +} diff --git a/tests/db_entry b/tests/db_entry deleted file mode 100755 index 33f97f49..00000000 --- a/tests/db_entry +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash -# Copyright 2014 (c) Anna Schumaker - -. $(dirname $0)/_functions - -function test_entry -{ - test_equal "./src/db_entry.run $1 $2" "$3" -} - -function test_print -{ - test_entry $1 $2 "Value: $1 Key: $2 Valid: 0 Id: 0" -} - -function test_primary_key -{ - test_entry "-p $1" $2 "Primary key: $2" -} - -function test_write -{ - rm $DATA_DIR/db_entry.txt 2>/dev/null || true - ./src/db_entry.run -w $1 $2 - test_equal "tail -1 $DATA_DIR/db_entry.txt" "$1 $2" -} - -function test_read -{ - rm $DATA_DIR/db_entry.txt 2>/dev/null || true - echo 0 > $DATA_DIR/db_entry.txt - echo $1 $2 >> $DATA_DIR/db_entry.txt - test_entry "-r $1" $2 "Value: $1 Key: $2 Valid: 0 Id: 0" -} - - -new_test "Database Entry Print Test" -test_print 1 1 -test_print 1 2 -test_print 2 1 -test_print 2 2 - - -echo -new_test "Database Entry Primary Key Test" -test_primary_key 1 1 -test_primary_key 1 2 -test_primary_key 2 1 -test_primary_key 2 2 - - -echo -new_test "Database Entry Write Test" -test_write 1 1 -test_write 1 2 -test_write 2 1 -test_write 2 2 - - -echo -new_test "Database Entry Read Test" -test_read 1 1 -test_read 1 2 -test_read 2 1 -test_read 2 2 diff --git a/tests/deck.cpp b/tests/deck.cpp new file mode 100644 index 00000000..f1f8939d --- /dev/null +++ b/tests/deck.cpp @@ -0,0 +1,162 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include +#include +#include "test.h" + +static Queue *Q_NULL = NULL; +static Track *TRACK_NULL = NULL; + +static void test_init() +{ + unsigned int val; + File f("deck", 0); + std::list::iterator it; + + test_equal(deck :: next(), TRACK_NULL); + + test :: cp_data_dir(); + tagdb :: init(); + library :: init(); + deck :: init(); + + test_equal(library :: get_queue()->has_flag(Q_RANDOM), true); + test_equal(deck :: get_queues().size(), (size_t)2); + + it = deck :: get_queues().begin(); + test_equal(it->size(), (unsigned)4); + for (unsigned int i = 0; i < 4; i++) + test_equal((*it)[i]->id, i); + + it++; + test_equal(it->size(), (unsigned)5); + for (unsigned int i = 0; i < 5; i++) + test_equal((*it)[i]->id, i + 4); + + /* + * Test that we saved the deck in the new format + */ + f.open(OPEN_READ); + test_equal(f.get_version(), (unsigned)1); + f >> val; /* number of queues */ + test_equal(val, (unsigned)2); + + for (unsigned int i = 0; i < 2; i++) { + f >> val; /* queues[i].flags */ + test_equal(val, (unsigned)1); + + f >> val; /* queues[i].size */ + test_equal(val, 4 + i); + + for (unsigned int j = 0; j < 4 + i; j++) { + f >> val; + test_equal(val, (4 * i) + j); + } + } + + f.close(); +} + +static unsigned int n = 0; +static void on_queue_removed(Queue *queue) +{ + n++; +} + +static void test_create_mv_destroy() +{ + Queue *q1, *q2; + + q1 = deck :: create(true); + test_not_equal(q1, Q_NULL); + test_equal(q1->has_flag(Q_ENABLED), true); + test_equal(q1->has_flag(Q_RANDOM), true); + test_equal(deck :: index(q1), (unsigned)2); + test_equal(deck :: get(2), q1); + + q2 = deck :: create(false); + test_not_equal(q2, Q_NULL); + test_equal(q2->has_flag(Q_ENABLED), true); + test_equal(q2->has_flag(Q_RANDOM), false); + test_equal(deck :: index(q2), (unsigned)3); + test_equal(deck :: get(3), q2); + + deck :: move(q1, 3); + test_equal(deck :: index(q1), (unsigned)3); + deck :: move(q1, 3); + test_equal(deck :: index(q1), (unsigned)3); + deck :: move(q1, 2); + test_equal(deck :: index(q1), (unsigned)2); + + get_callbacks()->on_pq_removed = on_queue_removed; + + deck :: destroy(q1); + test_equal(n, (unsigned)1); + test_equal(deck :: index(q1), (unsigned)3); + test_equal(deck :: index(q2), (unsigned)2); + + deck :: destroy(q2); + test_equal(n, (unsigned)2); + test_equal(deck :: index(q2), (unsigned)2); + + test_equal(deck :: get(3), Q_NULL); +} + +static void test_next_prev() +{ + std::list::iterator it = deck :: get_queues().begin(); + Queue *q = deck :: get_queue(); + Queue *q0 = &(*it++); + Queue *q1 = &(*it++); + + q0->unset_flag(Q_RANDOM); + for (unsigned int i = 0; i < 4; i++) + q0->add(tagdb :: lookup(i)); + + test_not_equal(q, Q_NULL); + test_equal(q->size(), (unsigned)0); + test_equal(deck :: prev(), TRACK_NULL); + + for (unsigned int i = 0; i < 2; i++) { + test_equal(deck :: next()->id, (unsigned)0); + test_equal(deck :: next()->id, (unsigned)1); + test_equal(deck :: next()->id, (unsigned)2); + test_equal(deck :: next()->id, (unsigned)3); + test_equal(q->size(), (unsigned)4); + } + + for (unsigned int i = 0; i < 2; i++) { + if (i == 1) + test_equal(deck :: prev()->id, (unsigned)3); + test_equal(deck :: prev()->id, (unsigned)2); + test_equal(deck :: prev()->id, (unsigned)1); + test_equal(deck :: prev()->id, (unsigned)0); + } + + test_equal(deck :: get_queues().size(), (size_t)1); + test_equal(deck :: index(q1), (unsigned)0); + + q1->unset_flag(Q_ENABLED); + library :: get_queue()->unset_flag(Q_RANDOM); + + test_equal(q1->size(), (unsigned)5); + deck :: next(); + test_equal(q1->size(), (unsigned)5); + + q1->set_flag(Q_ENABLED); + for (unsigned int i = 0; i < 5; i++) + deck :: next(); + test_equal(deck :: get_queues().size(), (size_t)0); +} + +int main(int argc, char **argv) +{ + test :: rm_data_dir(); + + run_test("Deck Init Test", test_init); + run_test("Deck Create, Move and Destroy Test", test_create_mv_destroy); + run_test("Deck Next and Prev Test", test_next_prev); + return 0; +} diff --git a/tests/deck/Sconscript b/tests/deck/Sconscript deleted file mode 100644 index 08935ea3..00000000 --- a/tests/deck/Sconscript +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/python -Import("Test", "CONFIG") - -CONFIG.DECK = True - -Test("deck", "deck.cpp") diff --git a/tests/deck/deck.cpp b/tests/deck/deck.cpp deleted file mode 100644 index da208c8e..00000000 --- a/tests/deck/deck.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include -#include -#include - -void test_add_playlist(unsigned int size) -{ - Playqueue *pqueue = deck :: create(false); - for (unsigned i = 0; i < size; i++) - pqueue->add(i); -} - -/* Test creating a deck of playlists */ -void test_0() -{ - for (unsigned int i = 0; i < 10; i++) - test_add_playlist(10 + i); - print("Test 0:\n"); - deck :: print_info(); - print("\n"); -} - -/* Test removing playlists from the deck */ -void test_1() -{ - print("Test 1:\n"); - deck :: remove(3); - deck :: remove(7); - deck :: remove(1); - deck :: remove(5); - deck :: print_info(); - print("\n"); -} - -/* Get a specific playlist from the deck */ -void test_2() -{ - Playqueue *pqueue; - print("Test 2: "); - - pqueue = deck :: get(3); - print("Playqueue { size = %u, flags = %u }", pqueue->size(), pqueue->get_flags()); - print("\n\n"); -} - -/* Move a playlist to a new position in the deck */ -void test_3() -{ - print("Test 3:\n"); - deck :: move(4, 0); - deck :: print_info(); - print("\n"); - deck :: move(5, 1); - deck :: print_info(); - print("\n"); - deck :: move(2, 5); - deck :: print_info(); - print("\n"); - deck :: move(3, 4); - deck :: print_info(); - print("\n"); - deck :: move(4, 3); - deck :: print_info(); - print("\n"); - deck :: move(4, 4); - deck :: print_info(); - print("\n"); -} - -/* Test the next() function for playlists */ -void test_4() -{ - print("Test 4:\n"); - - deck :: get(0)->unset_flag(PQ_ENABLED); - deck :: get(1)->unset_flag(PQ_ENABLED); - deck :: get(4)->unset_flag(PQ_ENABLED); - - for (unsigned int i = 0; i < 40; i++) { - print("Playing id: %u\n", deck :: next()); - if (i == 11 || i == 25) - deck :: print_info(); - } - - deck :: print_info(); - print("\n"); -} - -/* Test load / save functions */ -void test_5() -{ - print("Test 5:\n"); - - deck :: get(1)->set_flag(PQ_ENABLED); - deck :: get(2)->set_flag(PQ_ENABLED); - deck :: get(2)->set_flag(PQ_RANDOM); - - print("Saving playqueue deck\n"); - deck :: write(); - - print("Clearing deck\n"); - deck :: reset(); - deck :: print_info(); - - print("Reading back playqueue deck\n"); - deck :: init(); - - deck :: print_info(); -} - -int main(int argc, char **argv) -{ - library :: init(); - deck :: init(); - library :: reset(); - library :: add_path("/tmp/library/0"); - while (idle :: run_task()); - print("Library size: %u\n", deck :: get_library_pq()->size()); - - test_0(); - test_1(); - test_2(); - test_3(); - test_4(); - test_5(); - - print("Disabling library path\n"); - library :: set_enabled(0, false); - print("Library size: %u\n", deck :: get_library_pq()->size()); - - print("Enabling library path\n"); - library :: set_enabled(0, true); - print("Library size: %u\n", deck :: get_library_pq()->size()); - - print("Deleting library path\n"); - library :: del_path(0); - while (idle :: run_task()); - print("Library size: %u\n", deck :: get_library_pq()->size()); - - return 0; -} diff --git a/tests/deck/deck.good b/tests/deck/deck.good deleted file mode 100644 index 9a346727..00000000 --- a/tests/deck/deck.good +++ /dev/null @@ -1,133 +0,0 @@ -Library size: 150 -Test 0: -deck[0] = Playqueue { size = 10, flags = 1 } -deck[1] = Playqueue { size = 11, flags = 1 } -deck[2] = Playqueue { size = 12, flags = 1 } -deck[3] = Playqueue { size = 13, flags = 1 } -deck[4] = Playqueue { size = 14, flags = 1 } -deck[5] = Playqueue { size = 15, flags = 1 } -deck[6] = Playqueue { size = 16, flags = 1 } -deck[7] = Playqueue { size = 17, flags = 1 } -deck[8] = Playqueue { size = 18, flags = 1 } -deck[9] = Playqueue { size = 19, flags = 1 } - -Test 1: -deck[0] = Playqueue { size = 10, flags = 1 } -deck[1] = Playqueue { size = 12, flags = 1 } -deck[2] = Playqueue { size = 14, flags = 1 } -deck[3] = Playqueue { size = 15, flags = 1 } -deck[4] = Playqueue { size = 16, flags = 1 } -deck[5] = Playqueue { size = 19, flags = 1 } - -Test 2: Playqueue { size = 15, flags = 1 } - -Test 3: -deck[0] = Playqueue { size = 16, flags = 1 } -deck[1] = Playqueue { size = 10, flags = 1 } -deck[2] = Playqueue { size = 12, flags = 1 } -deck[3] = Playqueue { size = 14, flags = 1 } -deck[4] = Playqueue { size = 15, flags = 1 } -deck[5] = Playqueue { size = 19, flags = 1 } - -deck[0] = Playqueue { size = 16, flags = 1 } -deck[1] = Playqueue { size = 19, flags = 1 } -deck[2] = Playqueue { size = 10, flags = 1 } -deck[3] = Playqueue { size = 12, flags = 1 } -deck[4] = Playqueue { size = 14, flags = 1 } -deck[5] = Playqueue { size = 15, flags = 1 } - -deck[0] = Playqueue { size = 16, flags = 1 } -deck[1] = Playqueue { size = 19, flags = 1 } -deck[2] = Playqueue { size = 12, flags = 1 } -deck[3] = Playqueue { size = 14, flags = 1 } -deck[4] = Playqueue { size = 15, flags = 1 } -deck[5] = Playqueue { size = 10, flags = 1 } - -deck[0] = Playqueue { size = 16, flags = 1 } -deck[1] = Playqueue { size = 19, flags = 1 } -deck[2] = Playqueue { size = 12, flags = 1 } -deck[3] = Playqueue { size = 15, flags = 1 } -deck[4] = Playqueue { size = 14, flags = 1 } -deck[5] = Playqueue { size = 10, flags = 1 } - -deck[0] = Playqueue { size = 16, flags = 1 } -deck[1] = Playqueue { size = 19, flags = 1 } -deck[2] = Playqueue { size = 12, flags = 1 } -deck[3] = Playqueue { size = 14, flags = 1 } -deck[4] = Playqueue { size = 15, flags = 1 } -deck[5] = Playqueue { size = 10, flags = 1 } - -deck[0] = Playqueue { size = 16, flags = 1 } -deck[1] = Playqueue { size = 19, flags = 1 } -deck[2] = Playqueue { size = 12, flags = 1 } -deck[3] = Playqueue { size = 14, flags = 1 } -deck[4] = Playqueue { size = 15, flags = 1 } -deck[5] = Playqueue { size = 10, flags = 1 } - -Test 4: -Playing id: 0 -Playing id: 1 -Playing id: 2 -Playing id: 3 -Playing id: 4 -Playing id: 5 -Playing id: 6 -Playing id: 7 -Playing id: 8 -Playing id: 9 -Playing id: 10 -Playing id: 11 -deck[0] = Playqueue { size = 16, flags = 0 } -deck[1] = Playqueue { size = 19, flags = 0 } -deck[2] = Playqueue { size = 14, flags = 1 } -deck[3] = Playqueue { size = 15, flags = 0 } -deck[4] = Playqueue { size = 10, flags = 1 } -Playing id: 0 -Playing id: 1 -Playing id: 2 -Playing id: 3 -Playing id: 4 -Playing id: 5 -Playing id: 6 -Playing id: 7 -Playing id: 8 -Playing id: 9 -Playing id: 10 -Playing id: 11 -Playing id: 12 -Playing id: 13 -deck[0] = Playqueue { size = 16, flags = 0 } -deck[1] = Playqueue { size = 19, flags = 0 } -deck[2] = Playqueue { size = 15, flags = 0 } -deck[3] = Playqueue { size = 10, flags = 1 } -Playing id: 0 -Playing id: 1 -Playing id: 2 -Playing id: 3 -Playing id: 4 -Playing id: 5 -Playing id: 6 -Playing id: 7 -Playing id: 8 -Playing id: 9 -Playing id: 149 -Playing id: 148 -Playing id: 147 -Playing id: 146 -deck[0] = Playqueue { size = 16, flags = 0 } -deck[1] = Playqueue { size = 19, flags = 0 } -deck[2] = Playqueue { size = 15, flags = 0 } - -Test 5: -Saving playqueue deck -Clearing deck -Reading back playqueue deck -deck[0] = Playqueue { size = 16, flags = 0 } -deck[1] = Playqueue { size = 19, flags = 1 } -deck[2] = Playqueue { size = 15, flags = 3 } -Disabling library path -Library size: 0 -Enabling library path -Library size: 150 -Deleting library path -Library size: 0 diff --git a/tests/driver.cpp b/tests/driver.cpp new file mode 100644 index 00000000..8eba2d89 --- /dev/null +++ b/tests/driver.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + */ +#include +#include "test.h" + +static TestDriver *DRIVER_NULL = NULL; +static unsigned int eos_count = 0; +static unsigned int error_count = 0; + +void on_eos() +{ + eos_count++; +} + +void on_error() +{ + error_count++; +} + +void test_driver() +{ + TestDriver *driver = (TestDriver *)driver :: get_driver(); + const std::string file = "/home/Zelda/Music/Wind Waker/1 - Outset Isle.ogg"; + + test_not_equal(driver, DRIVER_NULL); + + driver->init(0, NULL, on_eos, on_error); + + driver->load(file); + test_equal(driver->cur_file, file); + + test_equal(driver->play(), true); + test_equal(driver->playing, true); + test_equal(driver->is_playing(), true); + + test_equal(driver->pause(), true); + test_equal(driver->playing, false); + test_equal(driver->is_playing(), false); + + driver->seek_to(4242); + test_equal(driver->cur_pos, (long)4242); + test_equal(driver->position(), (long)4242); + + driver->cur_duration = 424242; + test_equal(driver->duration(), (long)424242); + + driver->eos(); + test_equal(eos_count, (unsigned)1); + + driver->error(); + test_equal(error_count, (unsigned)1); + + driver->play(); + driver->seek_to(4242); + driver->load(file); + test_equal(driver->is_playing(), false); + test_equal(driver->position(), (long)0); +} + +int main(int argc, char **argv) +{ + run_test("Test Audio Driver", test_driver); + return 0; +} diff --git a/tests/file b/tests/file deleted file mode 100755 index c23da453..00000000 --- a/tests/file +++ /dev/null @@ -1,117 +0,0 @@ -#!/bin/bash -# Copyright 2014 (c) Anna Schumaker - -. $(dirname $0)/_functions - -function test_file -{ - test_equal "./src/file.run $1" "$2" -} - -function test_chmod -{ - touch $2 - chmod $1 $2 -} - - - -### -# -# Test filepaths -# - -new_test "Filepath Test" - -test_file -D INVALID -test_file -L INVALID -test_file "-D file.txt" "$DATA_DIR/file.txt" -test_file "-L file.txt" "$LEGACY_DIR/file.txt" -test_file "file.txt" INVALID -test_file "-D -v file.txt" "0" -test_file "-L -v file.txt" "0" - -if [ -d $DATA_DIR ]; then - echo "ERROR: $DATA_DIR should not exist!" - exit 1 -fi - - - -### -# -# Test opening files -# - -echo -new_test "File Open Test" - -# Generic open testing -test_file "-o N file.txt" "ERROR: A file with hint = FILE_TYPE_INVALID cannot be opened" -test_file "-D -o N file.txt" "ERROR: NOT_OPEN is not a legal OpenMode" -test_file "-D -o W -O file.txt" "ERROR: File is already open" # This test creates a file -test_file "-D -o R -O file.txt" "ERROR: File is already open" -rm $DATA_DIR/* - -# Test opening for read -test_file "-D -o R file.txt" "ERROR: File does not exist" - -test_chmod -r $DATA_DIR/file.txt -test_file "-D -o R file.txt" "ERROR: File could not be opened for reading" -rm -r $DATA_DIR - -# Test opening for write -test_file "-L -o W file.txt" "ERROR: Cannot write to legacy files" - -touch $DATA_DIR -test_file "-D -o W file.txt" "ERROR: Could not make directory" - -rm $DATA_DIR -mkdir -p $DATA_DIR -test_chmod -w $DATA_DIR/file.txt -test_file "-D -o W file.txt" "ERROR: Could not open file for writing" -rm -rf $DATA_DIR - - - -### -# -# Test closing files -# - -echo -new_test "File Close Test" -test_file "-D -c file.txt" "" - - - -### -# -# Test FILE IO -# - -data="ABCDE FGHIJ KLMNO PQRST UVWXYZ" -echo -new_test "File IO Test" - -# Write to file -./src/file.run -D -w file.txt "$data" -start_test -assert_equal "$(cat $DATA_DIR/file.txt)" "0 -$data" - -# Read data back from file -test_file "-D -r file.txt" "ABCDE -FGHIJ -KLMNO -PQRST -UVWXYZ" - -# Write different data to file -./src/file.run -D -w file.txt " $data" -start_test -assert_equal "$(cat $DATA_DIR/file.txt)" "0 - $data" - -# Read data back in a single line -test_file "-D -r -g file.txt" "$data" diff --git a/tests/file.cpp b/tests/file.cpp new file mode 100644 index 00000000..fb2f0e25 --- /dev/null +++ b/tests/file.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + */ +#include +#include "test.h" + + +static void test_filepath() +{ + File a("", 0); + File b("file.txt", 0); + + test_equal(a.get_version(), (unsigned)0); + test_equal((std::string)a.get_filepath(), (std::string)""); + + test_equal(b.get_version(), (unsigned)0); + test_equal((std::string)b.get_filepath(), test :: data_file("file.txt")); + + test_equal(a.exists(), false); + test_equal(b.exists(), false); + test_equal(test :: data_dir_exists(), false); +} + +static void test_open() +{ + File a("", 0); + + test_equal(a.open(OPEN_READ), false); + test_equal(a.open(OPEN_WRITE), false); + + File b("file.txt", 0); + test_equal(b.open(NOT_OPEN), false); + test_equal(b.open(OPEN_READ), false); + test_equal(b.open(OPEN_WRITE), true); + test_equal(b.open(OPEN_WRITE), false); + b.close(); + + test_equal(test :: data_file_exists("file.txt"), true); +} + +static void test_io() +{ + File a("file.txt", 1); + + test_equal(a.open(OPEN_WRITE), true); + a << "ABCDE FGHIJ KLMNO PQRST UVWXYZ" << std::endl; + a.close(); + test_equal(a.exists(), true); + + File b("file.txt", 0); + std::string res; + + test_equal(b.open(OPEN_READ), true); + test_equal(b.get_version(), (unsigned)1); + b >> res; + test_equal(res, (std::string)"ABCDE"); + b >> res; + test_equal(res, (std::string)"FGHIJ"); + res = b.getline(); + test_equal(res, (std::string)"KLMNO PQRST UVWXYZ"); +} + +int main(int argc, char **argv) +{ + test :: rm_data_dir(); + + run_test("File Constructor Test", test_filepath); + run_test("File Open Test", test_open); + run_test("File I/O Test", test_io); + return 0; +} diff --git a/tests/filter b/tests/filter deleted file mode 100755 index fa2cad0f..00000000 --- a/tests/filter +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash -# Copyright 2014 (c) Anna Schumaker - -. $(dirname $0)/_functions - -function test_add -{ - test_equal "./src/filter.run -a $1" "$2" -} - -function test_lowercase -{ - test_equal "./src/filter.run -l $1" "$2" -} - -function test_text -{ - test_add "$1" "$2" - test_lowercase "$1" "$2" -} - -function test_search -{ - num=$(cat -b $DATA_DIR/filter.txt | tail -n 1 | awk '{print $1}') - let num=$num-1 - test_equal "./src/filter.run -s $num $1" "$2" -} - - - -new_test "Filter Add and Lowercase Test" - -test_text " " "" -test_text " test -text" "test text" -test_text "test text" "test text" -test_text "Test Text" "test text" -test_text "Test? Text!" "test text" -test_text "Test?123 Text!456" "test123 text456" -test_text "Test?123 Text!456" "test123 text456" -test_text "Test(text);123-456" "test text 123 456" -test_text "Test((text));;123--456" "test text 123 456" - - - -echo -new_test "Filter Search Test" - -file=$DATA_DIR/filter.txt -mkdir -p $DATA_DIR - -echo "0" > $file -echo "It's dangerous to go alone! Take this..." >> $file -echo "DODONGO DISLIKES SMOKE." >> $file -echo "I am Error." >> $file -echo "Error knows a secret." >> $file -echo "Hey, you pay, then you can open the chests!" >> $file -echo "And the Master Sword sleeps again... FOREVER!" >> $file -echo "Link checked the chest. Wow! This is a nice chest!" >> $file -echo "Hey! Listen! Hey! Listen! Watch out!" >> $file -echo "You killed the Deku Tree? How could you?!" >> $file -echo "You've met with a terrible fate, haven't you?" >> $file -echo "Believe in your strengths... Believe..." >> $file -echo "Tingle! Tingle! Kooloo-Limpah!" >> $file -echo "Well excuse me, Princess!" >> $file - -test_search "error" "2 3" -test_search "the" "4 5 6 8" -test_search "the ch" "4 6" -test_search "the CH" "4 6" -test_search "the ch y" "4" diff --git a/tests/filter.cpp b/tests/filter.cpp new file mode 100644 index 00000000..88c02737 --- /dev/null +++ b/tests/filter.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + * Test the filtering code + */ + +#include +#include "test.h" + +static void do_test_lowercase(const std::string &text, const std::string &lc) +{ + test_equal(filter :: lowercase(text), lc); +} + +static void test_lowercase() +{ + do_test_lowercase(" ", ""); + do_test_lowercase(" test \ + text", "test text"); + do_test_lowercase("test text", "test text"); + do_test_lowercase("Test Text", "test text"); + do_test_lowercase("Test? Text!", "test text"); + do_test_lowercase("Test?123 Text!456", "test123 text456"); + do_test_lowercase("Test?123 Text!456", "test123 text456"); + do_test_lowercase("Test(text);123-456", "test text 123 456"); + do_test_lowercase("Test((text));;123--456", "test text 123 456"); +} + +static void do_test_add(const std::string &text, const std::string &lc) +{ + static unsigned int i = 0; + test_equal(filter :: add(text, i++), lc); +} + +static void test_add() +{ + do_test_add("It's dangerous to go alone! Take this...", + "its dangerous to go alone take this"); + do_test_add("DODONGO DISLIKES SMOKE.", + "dodongo dislikes smoke"); + do_test_add("I am Error.", + "i am error"); + do_test_add("Error knows a secret.", + "error knows a secret"); + do_test_add("Hey, you pay, then you can open the chests!", + "hey you pay then you can open the chests"); + do_test_add("And the Master Sword sleeps again... FOREVER!", + "and the master sword sleeps again forever"); + do_test_add("Link checked the chest. Wow! This is a nice chest!", + "link checked the chest wow this is a nice chest"); + do_test_add("Hey! Listen! Hey! Listen! Watch out!", + "hey listen hey listen watch out"); + do_test_add("You killed the Deku Tree? How could you?!", + "you killed the deku tree how could you"); + do_test_add("You've met with a terrible fate, haven't you?", + "youve met with a terrible fate havent you"); + do_test_add("Believe in your strengths... Believe...", + "believe in your strengths believe"); + do_test_add("Tingle! Tingle! Kooloo-Limpah!", + "tingle tingle kooloo limpah"); + do_test_add("Well excuse me, Princess!", + "well excuse me princess"); +} + +static void do_test_search(const std::string &text, unsigned int len, + unsigned int *ids) +{ + std::set res; + std::set::iterator it; + + filter :: search(text, res); + test_equal(res.size(), (size_t)len); + + test :: begin(); + it = res.begin(); + for (unsigned int i = 0; i < len; i++) { + check_equal(*it, ids[i]); + it++; + } + test :: success(); +} + +static void test_search() +{ + unsigned int res1[] = {2, 3}; + do_test_search("error", 2, res1); + + unsigned int res2[] = {4, 5, 6, 8}; + do_test_search("the", 4, res2); + + unsigned int res3[] = {4, 6}; + do_test_search("the ch", 2, res3); + do_test_search("the CH", 2, res3); + + unsigned int res4[] = {4}; + do_test_search("the ch y", 1, res4); + + unsigned int res5[] = {}; + do_test_search("unknown terms", 0, res5); +} + +int main(int argc, char **argv) +{ + run_test("Filter Lowercase Test", test_lowercase); + run_test("Filter Add Test", test_add); + run_test("Filter Search Test", test_search); + return 0; +} diff --git a/tests/gen_library.sh b/tests/gen_library.sh new file mode 100755 index 00000000..9255cfc8 --- /dev/null +++ b/tests/gen_library.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# +# Copyright 2013 (c) Anna Schumaker. +# +# Generate a test library in /tmp +# + +# $1 - File, $2 - Directory number +function tag_file() +{ + artist="Artist $2" + album="Album $2" + let date=2008+$2 + vorbiscomment -w $1 -t "ARTIST=$artist" -t "ALBUM=$album" -t "DATE=$date" +} + + +mkdir -p /tmp/ocarina/dir{1..5} + +for i in $(seq 5); do + cp tests/Music/* /tmp/ocarina/dir$i/ + + for f in /tmp/ocarina/dir$i/*; do + tag_file $f $i + done +done diff --git a/tests/idle b/tests/idle deleted file mode 100755 index d03b706e..00000000 --- a/tests/idle +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# Copyright 2014 (c) Anna Schumaker - -. $(dirname $0)/_functions - -new_test "Idle Queue Test" -src/idle.run diff --git a/tests/idle.cpp b/tests/idle.cpp new file mode 100644 index 00000000..489cc8af --- /dev/null +++ b/tests/idle.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + * Test the idle queue + */ + +#include +#include "test.h" + +#include + +static float cur = -1; + +static void inc_cur(float &expected) +{ + cur++; + check_equal(cur, expected); +} + +static void test_idle_queue(float n) +{ + test_equal(idle :: get_progress(), (float)1.0); + test_equal(idle :: run_task(), false); + + for (float i = 0; i < n; i++) + idle :: schedule(inc_cur, i); + test_equal(idle :: get_progress(), (float)0.0); + + test :: begin(); + for (float i = 0; i < (n - 1); i++) { + check_equal(idle :: run_task(), true); + check_equal(idle :: get_progress(), (i + 1) / n); + } + test :: success(); + + test_equal(idle :: run_task(), false); + test_equal(idle :: get_progress(), (float)1.0); +} + +static void do_test(float n) +{ + std::stringstream ss; + ss << " (n = " << n << ")"; + const std::string n_str = ss.str(); + + cur = -1; + run_test("Idle Queue Test" + n_str, test_idle_queue, n); +} + +int main(int argc, char **argv) +{ + do_test(10); + do_test(100); + do_test(1000); + do_test(10000); + do_test(100000); + return 0; +} diff --git a/tests/index b/tests/index deleted file mode 100755 index 00a01b6d..00000000 --- a/tests/index +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# Copyright 2014 (c) Anna Schumaker - -. $(dirname $0)/_functions - -function test_autosave -{ - new_test "Index Test (n = $1, autosave = true)" - src/index.run -a $1 - if [ ! -f $DATA_DIR/index.idx ]; then - echo "ERROR: $DATA_DIR/index.db doesn't exist!" - exit 1 - fi -} - -function test_noautosave -{ - new_test "Index Test (n = $1, autosave = false)" - src/index.run $1 - if [ -f $DATA_DIR/index.idx ]; then - echo "ERROR: $DATA_DIR/index.idx exists!" - exit 1 - fi -} - -function run_test -{ - rm $DATA_DIR/* 2>/dev/null || true - - if [ $1 -lt 1000 ]; then - test_autosave $1 - else - test_noautosave $1 - fi -} - -run_test 10 -echo -run_test 100 -echo -run_test 1000 -echo -run_test 10000 -echo -run_test 100000 diff --git a/tests/index.cpp b/tests/index.cpp new file mode 100644 index 00000000..dfc9fc2a --- /dev/null +++ b/tests/index.cpp @@ -0,0 +1,171 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + * Test a Database + */ + +#include +#include "test.h" + +#include + +static IndexEntry *IDX_NULL = NULL; + +/*** + * + * Struct for passing around arguments to tests + * + */ + +struct TestArgs { + const unsigned int n; + Index *index; + + TestArgs(const unsigned int, Index *); +}; + +TestArgs :: TestArgs(const unsigned int _n, Index *_index) + : n(_n), index(_index) +{} + + + +/*** + * + * Run tests with varying index sizes + * + */ + +static void test_single_item() +{ + Index index("index.idx", false); + + index.insert("a", 0); + IndexEntry *it = index.find("a"); + + test_not_equal(it, IDX_NULL); + test_equal(index.size(), (unsigned)1); + test_equal(it->values.size(), (size_t)1); + test_equal(*(it->values.begin()), (unsigned)0); + + index.remove("a", 0); + test_equal(index.size(), (unsigned)1); + test_equal(it->values.size(), (size_t)0); +} + +static void test_insertion(struct TestArgs *args) +{ + std::string key; + IndexEntry *it; + + test :: begin(); + for (char c = 'a'; c <= 'z'; c++) { + key = c; + + for (unsigned int i = 0; i < args->n; i++) + args->index->insert(key, i); + } + test :: success(); + + if (args->n == 0) + test_equal(args->index->size(), (unsigned int)0); + else + test_equal(args->index->size(), (unsigned int)26); + + test :: begin(); + for (char c = 'a'; c <= 'z'; c++) { + key = c; + it = args->index->find(key); + + if (args->n == 0) + check_equal(it, IDX_NULL); + else { + check_not_equal(it, IDX_NULL); + check_equal(it->values.size(), (size_t)args->n); + } + } + test :: success(); +} + +static void test_removal(struct TestArgs *args) +{ + std::string key; + IndexEntry *it; + + test :: begin(); + for (char c = 'a'; c <= 'z'; c++) { + key = c; + + for (unsigned int i = 0; i < args->n; i++) + args->index->remove(key, i); + it = args->index->find(key); + if (args->n == 0) + check_equal(it, IDX_NULL); + else { + check_not_equal(it, IDX_NULL); + check_equal(it->values.size(), (size_t)0); + } + } + test :: success(); +} + +static void test_saving(struct TestArgs *args) +{ + std::string key; + IndexEntry *it1, *it2; + + Index idx2("index.idx", false); + idx2.load(); + test_equal(idx2.size(), (unsigned)0); + + args->index->save(); + idx2.load(); + + test_equal(idx2.size(), args->index->size()); + test_equal(idx2.actual_size(), args->index->actual_size()); + + test :: begin(); + for (char c = 'a'; c <= 'z'; c++) { + key = c; + it1 = args->index->find(key); + it2 = idx2.find(key); + + if (args->n == 0) { + check_equal(it1, IDX_NULL); + check_equal(it2, IDX_NULL); + } else { + check_not_equal(it1, IDX_NULL); + check_not_equal(it2, IDX_NULL); + check_equal(it1->values.size(), it2->values.size()); + } + } + test :: success(); +} + +static void index_test(unsigned int n) +{ + test :: rm_data_dir(); + + std::stringstream ss; + ss << " (n = " << n << ")"; + const std::string n_str = ss.str(); + + Index index("index.idx", false); + struct TestArgs args(n, &index); + + if (n == 0) + run_test("Index Single Item Test", test_single_item); + run_test("Index Insertion Test" + n_str, test_insertion, &args); + run_test("Index Removal Test" + n_str, test_removal, &args); + run_test("Index Save and Load Test" + n_str, test_saving, &args); +} + +int main(int argc, char **argv) +{ + index_test(0); + index_test(10); + index_test(100); + index_test(1000); + index_test(10000); + index_test(100000); + return 0; +} diff --git a/tests/library.cpp b/tests/library.cpp new file mode 100644 index 00000000..75ce55f2 --- /dev/null +++ b/tests/library.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include +#include "test.h" + +static Queue *Q_NULL = NULL; +static Library *LIB_NULL = NULL; + +static void test_init() +{ + Queue *q = library :: get_queue(); + + test_not_equal(q, Q_NULL); + test_equal(q->has_flag(Q_ENABLED), true); + test_equal(q->has_flag(Q_REPEAT), true); + + tagdb :: init(); + library :: init(); + + test_equal(q->size(), (unsigned)24); +} + +static void test_enable() +{ + Queue *q = library :: get_queue(); + Library *library = tagdb :: lookup_library(0); + + library :: set_enabled(LIB_NULL, true); + test_equal(q->size(), (unsigned)24); + + library :: set_enabled(library, false); + test_equal(q->size(), (unsigned)0); + + library :: set_enabled(library, true); + test_equal(q->size(), (unsigned)24); + + library :: set_enabled(library, true); + test_equal(q->size(), (unsigned)24); + + library :: set_enabled(library, false); + test_equal(q->size(), (unsigned)0); + + library :: set_enabled(library, true); + test_equal(q->size(), (unsigned)24); +} + +static void test_remove() +{ + Queue *q = library :: get_queue(); + Library *library = tagdb :: lookup_library(0); + + library :: remove(LIB_NULL); + test_equal(q->size(), (unsigned)24); + + library :: remove(library); + test_equal(q->size(), (unsigned)0); + + library :: remove(library); + test_equal(q->size(), (unsigned)0); +} + +static void test_add() +{ + Queue *q = library :: get_queue(); + + test :: gen_library(); + library :: add("/tmp/ocarina/"); + + test_equal(q->size(), (unsigned)0); + test_equal(idle :: run_task(), true); + test_equal(q->size(), (unsigned)0); + + for (unsigned int i = 0; i < 6; i++) { + test_equal(idle :: run_task(), (i < 5) ? true : false); + test_equal(q->size(), i * 7); + } +} + +static void test_update() +{ + Queue *q = library :: get_queue(); + test :: rm_library_dirs(); + + library :: update_all(); + test_equal(idle :: run_task(), true); + test_equal(q->size(), (unsigned)21); + + for (unsigned int i = 0; i < 4; i++) + test_equal(idle :: run_task(), (i < 3) ? true : false); + test_equal(q->size(), (unsigned)21); + + + test :: gen_library(); + + library :: update_all(); + test_equal(idle :: run_task(), true); + test_equal(q->size(), (unsigned)21); + + for (unsigned int i = 0; i < 6; i++) + test_equal(idle :: run_task(), (i < 5) ? true : false); + test_equal(q->size(), (unsigned)35); +} + +int main(int argc, char **argv) +{ + test :: cp_data_dir(); + + run_test("Library Init Test", test_init); + run_test("Library Enable and Disable Test", test_enable); + run_test("Library Delete Path Test", test_remove); + run_test("Library Add Path Test", test_add); + run_test("Library Update Path Test", test_update); +} diff --git a/tests/library/0 b/tests/library/0 deleted file mode 100644 index d00491fd..00000000 --- a/tests/library/0 +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/library/1 b/tests/library/1 deleted file mode 100644 index 62cbc85f..00000000 --- a/tests/library/1 +++ /dev/null @@ -1,1203 +0,0 @@ -2 -/tmp/library/2 -0 1 150 150 -/tmp/library/2/Artist 14/Album 2/10 - Track 10.ogg -Track 10 -Artist 14 -Album 2 - -Tryout -1:00 -0 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 14/Album 2/9 - Track 9.ogg -Track 9 -Artist 14 -Album 2 - -Tryout -0:10 -1 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 14/Album 2/8 - Track 8.ogg -Track 8 -Artist 14 -Album 2 - -Tryout -0:01 -2 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 14/Album 2/7 - Track 7.ogg -Track 7 -Artist 14 -Album 2 - -Tryout -10:00 -3 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 14/Album 2/6 - Track 6.ogg -Track 6 -Artist 14 -Album 2 - -Tryout -1:00 -4 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 14/Album 2/5 - Track 5.ogg -Track 5 -Artist 14 -Album 2 - -Tryout -0:10 -5 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 14/Album 2/4 - Track 4.ogg -Track 4 -Artist 14 -Album 2 - -Tryout -0:01 -6 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 14/Album 2/3 - Track 3.ogg -Track 3 -Artist 14 -Album 2 - -Tryout -10:00 -7 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 14/Album 2/2 - Track 2.ogg -Track 2 -Artist 14 -Album 2 - -Tryout -1:00 -8 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 14/Album 2/1 - Track 1.ogg -Track 1 -Artist 14 -Album 2 - -Tryout -0:10 -9 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 14/Album 1/10 - Track 10.ogg -Track 10 -Artist 14 -Album 1 - -Trial -1:00 -10 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 14/Album 1/9 - Track 9.ogg -Track 9 -Artist 14 -Album 1 - -Trial -0:10 -11 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 14/Album 1/8 - Track 8.ogg -Track 8 -Artist 14 -Album 1 - -Trial -0:01 -12 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 14/Album 1/7 - Track 7.ogg -Track 7 -Artist 14 -Album 1 - -Trial -10:00 -13 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 14/Album 1/6 - Track 6.ogg -Track 6 -Artist 14 -Album 1 - -Trial -1:00 -14 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 14/Album 1/5 - Track 5.ogg -Track 5 -Artist 14 -Album 1 - -Trial -0:10 -15 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 14/Album 1/4 - Track 4.ogg -Track 4 -Artist 14 -Album 1 - -Trial -0:01 -16 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 14/Album 1/3 - Track 3.ogg -Track 3 -Artist 14 -Album 1 - -Trial -10:00 -17 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 14/Album 1/2 - Track 2.ogg -Track 2 -Artist 14 -Album 1 - -Trial -1:00 -18 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 14/Album 1/1 - Track 1.ogg -Track 1 -Artist 14 -Album 1 - -Trial -0:10 -19 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 14/Album 0/10 - Track 10.ogg -Track 10 -Artist 14 -Album 0 - -Test -1:00 -20 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 14/Album 0/9 - Track 9.ogg -Track 9 -Artist 14 -Album 0 - -Test -0:10 -21 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 14/Album 0/8 - Track 8.ogg -Track 8 -Artist 14 -Album 0 - -Test -0:01 -22 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 14/Album 0/7 - Track 7.ogg -Track 7 -Artist 14 -Album 0 - -Test -10:00 -23 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 14/Album 0/6 - Track 6.ogg -Track 6 -Artist 14 -Album 0 - -Test -1:00 -24 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 14/Album 0/5 - Track 5.ogg -Track 5 -Artist 14 -Album 0 - -Test -0:10 -25 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 14/Album 0/4 - Track 4.ogg -Track 4 -Artist 14 -Album 0 - -Test -0:01 -26 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 14/Album 0/3 - Track 3.ogg -Track 3 -Artist 14 -Album 0 - -Test -10:00 -27 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 14/Album 0/2 - Track 2.ogg -Track 2 -Artist 14 -Album 0 - -Test -1:00 -28 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 14/Album 0/1 - Track 1.ogg -Track 1 -Artist 14 -Album 0 - -Test -0:10 -29 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 13/Album 2/10 - Track 10.ogg -Track 10 -Artist 13 -Album 2 - -Tryout -1:00 -30 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 13/Album 2/9 - Track 9.ogg -Track 9 -Artist 13 -Album 2 - -Tryout -0:10 -31 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 13/Album 2/8 - Track 8.ogg -Track 8 -Artist 13 -Album 2 - -Tryout -0:01 -32 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 13/Album 2/7 - Track 7.ogg -Track 7 -Artist 13 -Album 2 - -Tryout -10:00 -33 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 13/Album 2/6 - Track 6.ogg -Track 6 -Artist 13 -Album 2 - -Tryout -1:00 -34 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 13/Album 2/5 - Track 5.ogg -Track 5 -Artist 13 -Album 2 - -Tryout -0:10 -35 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 13/Album 2/4 - Track 4.ogg -Track 4 -Artist 13 -Album 2 - -Tryout -0:01 -36 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 13/Album 2/3 - Track 3.ogg -Track 3 -Artist 13 -Album 2 - -Tryout -10:00 -37 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 13/Album 2/2 - Track 2.ogg -Track 2 -Artist 13 -Album 2 - -Tryout -1:00 -38 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 13/Album 2/1 - Track 1.ogg -Track 1 -Artist 13 -Album 2 - -Tryout -0:10 -39 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 13/Album 1/10 - Track 10.ogg -Track 10 -Artist 13 -Album 1 - -Trial -1:00 -40 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 13/Album 1/9 - Track 9.ogg -Track 9 -Artist 13 -Album 1 - -Trial -0:10 -41 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 13/Album 1/8 - Track 8.ogg -Track 8 -Artist 13 -Album 1 - -Trial -0:01 -42 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 13/Album 1/7 - Track 7.ogg -Track 7 -Artist 13 -Album 1 - -Trial -10:00 -43 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 13/Album 1/6 - Track 6.ogg -Track 6 -Artist 13 -Album 1 - -Trial -1:00 -44 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 13/Album 1/5 - Track 5.ogg -Track 5 -Artist 13 -Album 1 - -Trial -0:10 -45 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 13/Album 1/4 - Track 4.ogg -Track 4 -Artist 13 -Album 1 - -Trial -0:01 -46 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 13/Album 1/3 - Track 3.ogg -Track 3 -Artist 13 -Album 1 - -Trial -10:00 -47 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 13/Album 1/2 - Track 2.ogg -Track 2 -Artist 13 -Album 1 - -Trial -1:00 -48 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 13/Album 1/1 - Track 1.ogg -Track 1 -Artist 13 -Album 1 - -Trial -0:10 -49 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 13/Album 0/10 - Track 10.ogg -Track 10 -Artist 13 -Album 0 - -Test -1:00 -50 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 13/Album 0/9 - Track 9.ogg -Track 9 -Artist 13 -Album 0 - -Test -0:10 -51 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 13/Album 0/8 - Track 8.ogg -Track 8 -Artist 13 -Album 0 - -Test -0:01 -52 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 13/Album 0/7 - Track 7.ogg -Track 7 -Artist 13 -Album 0 - -Test -10:00 -53 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 13/Album 0/6 - Track 6.ogg -Track 6 -Artist 13 -Album 0 - -Test -1:00 -54 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 13/Album 0/5 - Track 5.ogg -Track 5 -Artist 13 -Album 0 - -Test -0:10 -55 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 13/Album 0/4 - Track 4.ogg -Track 4 -Artist 13 -Album 0 - -Test -0:01 -56 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 13/Album 0/3 - Track 3.ogg -Track 3 -Artist 13 -Album 0 - -Test -10:00 -57 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 13/Album 0/2 - Track 2.ogg -Track 2 -Artist 13 -Album 0 - -Test -1:00 -58 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 13/Album 0/1 - Track 1.ogg -Track 1 -Artist 13 -Album 0 - -Test -0:10 -59 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 12/Album 2/10 - Track 10.ogg -Track 10 -Artist 12 -Album 2 - -Tryout -1:00 -60 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 12/Album 2/9 - Track 9.ogg -Track 9 -Artist 12 -Album 2 - -Tryout -0:10 -61 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 12/Album 2/8 - Track 8.ogg -Track 8 -Artist 12 -Album 2 - -Tryout -0:01 -62 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 12/Album 2/7 - Track 7.ogg -Track 7 -Artist 12 -Album 2 - -Tryout -10:00 -63 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 12/Album 2/6 - Track 6.ogg -Track 6 -Artist 12 -Album 2 - -Tryout -1:00 -64 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 12/Album 2/5 - Track 5.ogg -Track 5 -Artist 12 -Album 2 - -Tryout -0:10 -65 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 12/Album 2/4 - Track 4.ogg -Track 4 -Artist 12 -Album 2 - -Tryout -0:01 -66 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 12/Album 2/3 - Track 3.ogg -Track 3 -Artist 12 -Album 2 - -Tryout -10:00 -67 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 12/Album 2/2 - Track 2.ogg -Track 2 -Artist 12 -Album 2 - -Tryout -1:00 -68 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 12/Album 2/1 - Track 1.ogg -Track 1 -Artist 12 -Album 2 - -Tryout -0:10 -69 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 12/Album 1/10 - Track 10.ogg -Track 10 -Artist 12 -Album 1 - -Trial -1:00 -70 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 12/Album 1/9 - Track 9.ogg -Track 9 -Artist 12 -Album 1 - -Trial -0:10 -71 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 12/Album 1/8 - Track 8.ogg -Track 8 -Artist 12 -Album 1 - -Trial -0:01 -72 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 12/Album 1/7 - Track 7.ogg -Track 7 -Artist 12 -Album 1 - -Trial -10:00 -73 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 12/Album 1/6 - Track 6.ogg -Track 6 -Artist 12 -Album 1 - -Trial -1:00 -74 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 12/Album 1/5 - Track 5.ogg -Track 5 -Artist 12 -Album 1 - -Trial -0:10 -75 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 12/Album 1/4 - Track 4.ogg -Track 4 -Artist 12 -Album 1 - -Trial -0:01 -76 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 12/Album 1/3 - Track 3.ogg -Track 3 -Artist 12 -Album 1 - -Trial -10:00 -77 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 12/Album 1/2 - Track 2.ogg -Track 2 -Artist 12 -Album 1 - -Trial -1:00 -78 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 12/Album 1/1 - Track 1.ogg -Track 1 -Artist 12 -Album 1 - -Trial -0:10 -79 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 12/Album 0/10 - Track 10.ogg -Track 10 -Artist 12 -Album 0 - -Test -1:00 -80 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 12/Album 0/9 - Track 9.ogg -Track 9 -Artist 12 -Album 0 - -Test -0:10 -81 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 12/Album 0/8 - Track 8.ogg -Track 8 -Artist 12 -Album 0 - -Test -0:01 -82 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 12/Album 0/7 - Track 7.ogg -Track 7 -Artist 12 -Album 0 - -Test -10:00 -83 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 12/Album 0/6 - Track 6.ogg -Track 6 -Artist 12 -Album 0 - -Test -1:00 -84 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 12/Album 0/5 - Track 5.ogg -Track 5 -Artist 12 -Album 0 - -Test -0:10 -85 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 12/Album 0/4 - Track 4.ogg -Track 4 -Artist 12 -Album 0 - -Test -0:01 -86 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 12/Album 0/3 - Track 3.ogg -Track 3 -Artist 12 -Album 0 - -Test -10:00 -87 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 12/Album 0/2 - Track 2.ogg -Track 2 -Artist 12 -Album 0 - -Test -1:00 -88 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 12/Album 0/1 - Track 1.ogg -Track 1 -Artist 12 -Album 0 - -Test -0:10 -89 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 11/Album 2/10 - Track 10.ogg -Track 10 -Artist 11 -Album 2 - -Tryout -1:00 -90 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 11/Album 2/9 - Track 9.ogg -Track 9 -Artist 11 -Album 2 - -Tryout -0:10 -91 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 11/Album 2/8 - Track 8.ogg -Track 8 -Artist 11 -Album 2 - -Tryout -0:01 -92 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 11/Album 2/7 - Track 7.ogg -Track 7 -Artist 11 -Album 2 - -Tryout -10:00 -93 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 11/Album 2/6 - Track 6.ogg -Track 6 -Artist 11 -Album 2 - -Tryout -1:00 -94 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 11/Album 2/5 - Track 5.ogg -Track 5 -Artist 11 -Album 2 - -Tryout -0:10 -95 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 11/Album 2/4 - Track 4.ogg -Track 4 -Artist 11 -Album 2 - -Tryout -0:01 -96 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 11/Album 2/3 - Track 3.ogg -Track 3 -Artist 11 -Album 2 - -Tryout -10:00 -97 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 11/Album 2/2 - Track 2.ogg -Track 2 -Artist 11 -Album 2 - -Tryout -1:00 -98 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 11/Album 2/1 - Track 1.ogg -Track 1 -Artist 11 -Album 2 - -Tryout -0:10 -99 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 11/Album 1/10 - Track 10.ogg -Track 10 -Artist 11 -Album 1 - -Trial -1:00 -100 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 11/Album 1/9 - Track 9.ogg -Track 9 -Artist 11 -Album 1 - -Trial -0:10 -101 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 11/Album 1/8 - Track 8.ogg -Track 8 -Artist 11 -Album 1 - -Trial -0:01 -102 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 11/Album 1/7 - Track 7.ogg -Track 7 -Artist 11 -Album 1 - -Trial -10:00 -103 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 11/Album 1/6 - Track 6.ogg -Track 6 -Artist 11 -Album 1 - -Trial -1:00 -104 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 11/Album 1/5 - Track 5.ogg -Track 5 -Artist 11 -Album 1 - -Trial -0:10 -105 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 11/Album 1/4 - Track 4.ogg -Track 4 -Artist 11 -Album 1 - -Trial -0:01 -106 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 11/Album 1/3 - Track 3.ogg -Track 3 -Artist 11 -Album 1 - -Trial -10:00 -107 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 11/Album 1/2 - Track 2.ogg -Track 2 -Artist 11 -Album 1 - -Trial -1:00 -108 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 11/Album 1/1 - Track 1.ogg -Track 1 -Artist 11 -Album 1 - -Trial -0:10 -109 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 11/Album 0/10 - Track 10.ogg -Track 10 -Artist 11 -Album 0 - -Test -1:00 -110 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 11/Album 0/9 - Track 9.ogg -Track 9 -Artist 11 -Album 0 - -Test -0:10 -111 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 11/Album 0/8 - Track 8.ogg -Track 8 -Artist 11 -Album 0 - -Test -0:01 -112 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 11/Album 0/7 - Track 7.ogg -Track 7 -Artist 11 -Album 0 - -Test -10:00 -113 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 11/Album 0/6 - Track 6.ogg -Track 6 -Artist 11 -Album 0 - -Test -1:00 -114 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 11/Album 0/5 - Track 5.ogg -Track 5 -Artist 11 -Album 0 - -Test -0:10 -115 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 11/Album 0/4 - Track 4.ogg -Track 4 -Artist 11 -Album 0 - -Test -0:01 -116 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 11/Album 0/3 - Track 3.ogg -Track 3 -Artist 11 -Album 0 - -Test -10:00 -117 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 11/Album 0/2 - Track 2.ogg -Track 2 -Artist 11 -Album 0 - -Test -1:00 -118 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 11/Album 0/1 - Track 1.ogg -Track 1 -Artist 11 -Album 0 - -Test -0:10 -119 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 10/Album 2/10 - Track 10.ogg -Track 10 -Artist 10 -Album 2 - -Tryout -1:00 -120 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 10/Album 2/9 - Track 9.ogg -Track 9 -Artist 10 -Album 2 - -Tryout -0:10 -121 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 10/Album 2/8 - Track 8.ogg -Track 8 -Artist 10 -Album 2 - -Tryout -0:01 -122 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 10/Album 2/7 - Track 7.ogg -Track 7 -Artist 10 -Album 2 - -Tryout -10:00 -123 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 10/Album 2/6 - Track 6.ogg -Track 6 -Artist 10 -Album 2 - -Tryout -1:00 -124 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 10/Album 2/5 - Track 5.ogg -Track 5 -Artist 10 -Album 2 - -Tryout -0:10 -125 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 10/Album 2/4 - Track 4.ogg -Track 4 -Artist 10 -Album 2 - -Tryout -0:01 -126 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 10/Album 2/3 - Track 3.ogg -Track 3 -Artist 10 -Album 2 - -Tryout -10:00 -127 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 10/Album 2/2 - Track 2.ogg -Track 2 -Artist 10 -Album 2 - -Tryout -1:00 -128 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 10/Album 2/1 - Track 1.ogg -Track 1 -Artist 10 -Album 2 - -Tryout -0:10 -129 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 10/Album 1/10 - Track 10.ogg -Track 10 -Artist 10 -Album 1 - -Trial -1:00 -130 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 10/Album 1/9 - Track 9.ogg -Track 9 -Artist 10 -Album 1 - -Trial -0:10 -131 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 10/Album 1/8 - Track 8.ogg -Track 8 -Artist 10 -Album 1 - -Trial -0:01 -132 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 10/Album 1/7 - Track 7.ogg -Track 7 -Artist 10 -Album 1 - -Trial -10:00 -133 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 10/Album 1/6 - Track 6.ogg -Track 6 -Artist 10 -Album 1 - -Trial -1:00 -134 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 10/Album 1/5 - Track 5.ogg -Track 5 -Artist 10 -Album 1 - -Trial -0:10 -135 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 10/Album 1/4 - Track 4.ogg -Track 4 -Artist 10 -Album 1 - -Trial -0:01 -136 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 10/Album 1/3 - Track 3.ogg -Track 3 -Artist 10 -Album 1 - -Trial -10:00 -137 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 10/Album 1/2 - Track 2.ogg -Track 2 -Artist 10 -Album 1 - -Trial -1:00 -138 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 10/Album 1/1 - Track 1.ogg -Track 1 -Artist 10 -Album 1 - -Trial -0:10 -139 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 10/Album 0/10 - Track 10.ogg -Track 10 -Artist 10 -Album 0 - -Test -1:00 -140 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 10/Album 0/9 - Track 9.ogg -Track 9 -Artist 10 -Album 0 - -Test -0:10 -141 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 10/Album 0/8 - Track 8.ogg -Track 8 -Artist 10 -Album 0 - -Test -0:01 -142 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/2/Artist 10/Album 0/7 - Track 7.ogg -Track 7 -Artist 10 -Album 0 - -Test -10:00 -143 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 10/Album 0/6 - Track 6.ogg -Track 6 -Artist 10 -Album 0 - -Test -1:00 -144 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 10/Album 0/5 - Track 5.ogg -Track 5 -Artist 10 -Album 0 - -Test -0:10 -145 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/2/Artist 10/Album 0/4 - Track 4.ogg -Track 4 -Artist 10 -Album 0 - -Test -0:01 -146 2011 4 1 1 1 2014 1 22 8000 1 0 -/tmp/library/2/Artist 10/Album 0/3 - Track 3.ogg -Track 3 -Artist 10 -Album 0 - -Test -10:00 -147 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/2/Artist 10/Album 0/2 - Track 2.ogg -Track 2 -Artist 10 -Album 0 - -Test -1:00 -148 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/2/Artist 10/Album 0/1 - Track 1.ogg -Track 1 -Artist 10 -Album 0 - -Test -0:10 -149 2011 1 0 0 0 0 10 22 8000 1 0 diff --git a/tests/library/1.ogg b/tests/library/1.ogg deleted file mode 100644 index 64d19c5a..00000000 Binary files a/tests/library/1.ogg and /dev/null differ diff --git a/tests/library/10.ogg b/tests/library/10.ogg deleted file mode 100644 index 19652343..00000000 Binary files a/tests/library/10.ogg and /dev/null differ diff --git a/tests/library/2 b/tests/library/2 deleted file mode 100644 index efa978c0..00000000 --- a/tests/library/2 +++ /dev/null @@ -1,1203 +0,0 @@ -2 -/tmp/library/3 -1 1 150 150 -/tmp/library/3/Artist 19/Album 2/10 - Track 10.ogg -Track 10 -Artist 19 -Album 2 - -Tryout -1:00 -0 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 19/Album 2/9 - Track 9.ogg -Track 9 -Artist 19 -Album 2 - -Tryout -0:10 -1 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 19/Album 2/8 - Track 8.ogg -Track 8 -Artist 19 -Album 2 - -Tryout -0:01 -2 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 19/Album 2/7 - Track 7.ogg -Track 7 -Artist 19 -Album 2 - -Tryout -10:00 -3 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 19/Album 2/6 - Track 6.ogg -Track 6 -Artist 19 -Album 2 - -Tryout -1:00 -4 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 19/Album 2/5 - Track 5.ogg -Track 5 -Artist 19 -Album 2 - -Tryout -0:10 -5 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 19/Album 2/4 - Track 4.ogg -Track 4 -Artist 19 -Album 2 - -Tryout -0:01 -6 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 19/Album 2/3 - Track 3.ogg -Track 3 -Artist 19 -Album 2 - -Tryout -10:00 -7 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 19/Album 2/2 - Track 2.ogg -Track 2 -Artist 19 -Album 2 - -Tryout -1:00 -8 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 19/Album 2/1 - Track 1.ogg -Track 1 -Artist 19 -Album 2 - -Tryout -0:10 -9 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 19/Album 1/10 - Track 10.ogg -Track 10 -Artist 19 -Album 1 - -Trial -1:00 -10 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 19/Album 1/9 - Track 9.ogg -Track 9 -Artist 19 -Album 1 - -Trial -0:10 -11 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 19/Album 1/8 - Track 8.ogg -Track 8 -Artist 19 -Album 1 - -Trial -0:01 -12 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 19/Album 1/7 - Track 7.ogg -Track 7 -Artist 19 -Album 1 - -Trial -10:00 -13 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 19/Album 1/6 - Track 6.ogg -Track 6 -Artist 19 -Album 1 - -Trial -1:00 -14 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 19/Album 1/5 - Track 5.ogg -Track 5 -Artist 19 -Album 1 - -Trial -0:10 -15 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 19/Album 1/4 - Track 4.ogg -Track 4 -Artist 19 -Album 1 - -Trial -0:01 -16 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 19/Album 1/3 - Track 3.ogg -Track 3 -Artist 19 -Album 1 - -Trial -10:00 -17 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 19/Album 1/2 - Track 2.ogg -Track 2 -Artist 19 -Album 1 - -Trial -1:00 -18 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 19/Album 1/1 - Track 1.ogg -Track 1 -Artist 19 -Album 1 - -Trial -0:10 -19 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 19/Album 0/10 - Track 10.ogg -Track 10 -Artist 19 -Album 0 - -Test -1:00 -20 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 19/Album 0/9 - Track 9.ogg -Track 9 -Artist 19 -Album 0 - -Test -0:10 -21 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 19/Album 0/8 - Track 8.ogg -Track 8 -Artist 19 -Album 0 - -Test -0:01 -22 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 19/Album 0/7 - Track 7.ogg -Track 7 -Artist 19 -Album 0 - -Test -10:00 -23 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 19/Album 0/6 - Track 6.ogg -Track 6 -Artist 19 -Album 0 - -Test -1:00 -24 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 19/Album 0/5 - Track 5.ogg -Track 5 -Artist 19 -Album 0 - -Test -0:10 -25 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 19/Album 0/4 - Track 4.ogg -Track 4 -Artist 19 -Album 0 - -Test -0:01 -26 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 19/Album 0/3 - Track 3.ogg -Track 3 -Artist 19 -Album 0 - -Test -10:00 -27 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 19/Album 0/2 - Track 2.ogg -Track 2 -Artist 19 -Album 0 - -Test -1:00 -28 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 19/Album 0/1 - Track 1.ogg -Track 1 -Artist 19 -Album 0 - -Test -0:10 -29 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 18/Album 2/10 - Track 10.ogg -Track 10 -Artist 18 -Album 2 - -Tryout -1:00 -30 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 18/Album 2/9 - Track 9.ogg -Track 9 -Artist 18 -Album 2 - -Tryout -0:10 -31 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 18/Album 2/8 - Track 8.ogg -Track 8 -Artist 18 -Album 2 - -Tryout -0:01 -32 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 18/Album 2/7 - Track 7.ogg -Track 7 -Artist 18 -Album 2 - -Tryout -10:00 -33 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 18/Album 2/6 - Track 6.ogg -Track 6 -Artist 18 -Album 2 - -Tryout -1:00 -34 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 18/Album 2/5 - Track 5.ogg -Track 5 -Artist 18 -Album 2 - -Tryout -0:10 -35 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 18/Album 2/4 - Track 4.ogg -Track 4 -Artist 18 -Album 2 - -Tryout -0:01 -36 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 18/Album 2/3 - Track 3.ogg -Track 3 -Artist 18 -Album 2 - -Tryout -10:00 -37 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 18/Album 2/2 - Track 2.ogg -Track 2 -Artist 18 -Album 2 - -Tryout -1:00 -38 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 18/Album 2/1 - Track 1.ogg -Track 1 -Artist 18 -Album 2 - -Tryout -0:10 -39 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 18/Album 1/10 - Track 10.ogg -Track 10 -Artist 18 -Album 1 - -Trial -1:00 -40 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 18/Album 1/9 - Track 9.ogg -Track 9 -Artist 18 -Album 1 - -Trial -0:10 -41 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 18/Album 1/8 - Track 8.ogg -Track 8 -Artist 18 -Album 1 - -Trial -0:01 -42 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 18/Album 1/7 - Track 7.ogg -Track 7 -Artist 18 -Album 1 - -Trial -10:00 -43 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 18/Album 1/6 - Track 6.ogg -Track 6 -Artist 18 -Album 1 - -Trial -1:00 -44 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 18/Album 1/5 - Track 5.ogg -Track 5 -Artist 18 -Album 1 - -Trial -0:10 -45 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 18/Album 1/4 - Track 4.ogg -Track 4 -Artist 18 -Album 1 - -Trial -0:01 -46 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 18/Album 1/3 - Track 3.ogg -Track 3 -Artist 18 -Album 1 - -Trial -10:00 -47 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 18/Album 1/2 - Track 2.ogg -Track 2 -Artist 18 -Album 1 - -Trial -1:00 -48 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 18/Album 1/1 - Track 1.ogg -Track 1 -Artist 18 -Album 1 - -Trial -0:10 -49 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 18/Album 0/10 - Track 10.ogg -Track 10 -Artist 18 -Album 0 - -Test -1:00 -50 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 18/Album 0/9 - Track 9.ogg -Track 9 -Artist 18 -Album 0 - -Test -0:10 -51 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 18/Album 0/8 - Track 8.ogg -Track 8 -Artist 18 -Album 0 - -Test -0:01 -52 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 18/Album 0/7 - Track 7.ogg -Track 7 -Artist 18 -Album 0 - -Test -10:00 -53 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 18/Album 0/6 - Track 6.ogg -Track 6 -Artist 18 -Album 0 - -Test -1:00 -54 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 18/Album 0/5 - Track 5.ogg -Track 5 -Artist 18 -Album 0 - -Test -0:10 -55 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 18/Album 0/4 - Track 4.ogg -Track 4 -Artist 18 -Album 0 - -Test -0:01 -56 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 18/Album 0/3 - Track 3.ogg -Track 3 -Artist 18 -Album 0 - -Test -10:00 -57 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 18/Album 0/2 - Track 2.ogg -Track 2 -Artist 18 -Album 0 - -Test -1:00 -58 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 18/Album 0/1 - Track 1.ogg -Track 1 -Artist 18 -Album 0 - -Test -0:10 -59 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 17/Album 2/10 - Track 10.ogg -Track 10 -Artist 17 -Album 2 - -Tryout -1:00 -60 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 17/Album 2/9 - Track 9.ogg -Track 9 -Artist 17 -Album 2 - -Tryout -0:10 -61 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 17/Album 2/8 - Track 8.ogg -Track 8 -Artist 17 -Album 2 - -Tryout -0:01 -62 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 17/Album 2/7 - Track 7.ogg -Track 7 -Artist 17 -Album 2 - -Tryout -10:00 -63 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 17/Album 2/6 - Track 6.ogg -Track 6 -Artist 17 -Album 2 - -Tryout -1:00 -64 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 17/Album 2/5 - Track 5.ogg -Track 5 -Artist 17 -Album 2 - -Tryout -0:10 -65 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 17/Album 2/4 - Track 4.ogg -Track 4 -Artist 17 -Album 2 - -Tryout -0:01 -66 2013 4 1 1 1 2014 1 22 8000 1 0 -/tmp/library/3/Artist 17/Album 2/3 - Track 3.ogg -Track 3 -Artist 17 -Album 2 - -Tryout -10:00 -67 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 17/Album 2/2 - Track 2.ogg -Track 2 -Artist 17 -Album 2 - -Tryout -1:00 -68 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 17/Album 2/1 - Track 1.ogg -Track 1 -Artist 17 -Album 2 - -Tryout -0:10 -69 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 17/Album 1/10 - Track 10.ogg -Track 10 -Artist 17 -Album 1 - -Trial -1:00 -70 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 17/Album 1/9 - Track 9.ogg -Track 9 -Artist 17 -Album 1 - -Trial -0:10 -71 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 17/Album 1/8 - Track 8.ogg -Track 8 -Artist 17 -Album 1 - -Trial -0:01 -72 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 17/Album 1/7 - Track 7.ogg -Track 7 -Artist 17 -Album 1 - -Trial -10:00 -73 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 17/Album 1/6 - Track 6.ogg -Track 6 -Artist 17 -Album 1 - -Trial -1:00 -74 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 17/Album 1/5 - Track 5.ogg -Track 5 -Artist 17 -Album 1 - -Trial -0:10 -75 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 17/Album 1/4 - Track 4.ogg -Track 4 -Artist 17 -Album 1 - -Trial -0:01 -76 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 17/Album 1/3 - Track 3.ogg -Track 3 -Artist 17 -Album 1 - -Trial -10:00 -77 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 17/Album 1/2 - Track 2.ogg -Track 2 -Artist 17 -Album 1 - -Trial -1:00 -78 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 17/Album 1/1 - Track 1.ogg -Track 1 -Artist 17 -Album 1 - -Trial -0:10 -79 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 17/Album 0/10 - Track 10.ogg -Track 10 -Artist 17 -Album 0 - -Test -1:00 -80 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 17/Album 0/9 - Track 9.ogg -Track 9 -Artist 17 -Album 0 - -Test -0:10 -81 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 17/Album 0/8 - Track 8.ogg -Track 8 -Artist 17 -Album 0 - -Test -0:01 -82 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 17/Album 0/7 - Track 7.ogg -Track 7 -Artist 17 -Album 0 - -Test -10:00 -83 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 17/Album 0/6 - Track 6.ogg -Track 6 -Artist 17 -Album 0 - -Test -1:00 -84 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 17/Album 0/5 - Track 5.ogg -Track 5 -Artist 17 -Album 0 - -Test -0:10 -85 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 17/Album 0/4 - Track 4.ogg -Track 4 -Artist 17 -Album 0 - -Test -0:01 -86 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 17/Album 0/3 - Track 3.ogg -Track 3 -Artist 17 -Album 0 - -Test -10:00 -87 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 17/Album 0/2 - Track 2.ogg -Track 2 -Artist 17 -Album 0 - -Test -1:00 -88 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 17/Album 0/1 - Track 1.ogg -Track 1 -Artist 17 -Album 0 - -Test -0:10 -89 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 16/Album 2/10 - Track 10.ogg -Track 10 -Artist 16 -Album 2 - -Tryout -1:00 -90 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 16/Album 2/9 - Track 9.ogg -Track 9 -Artist 16 -Album 2 - -Tryout -0:10 -91 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 16/Album 2/8 - Track 8.ogg -Track 8 -Artist 16 -Album 2 - -Tryout -0:01 -92 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 16/Album 2/7 - Track 7.ogg -Track 7 -Artist 16 -Album 2 - -Tryout -10:00 -93 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 16/Album 2/6 - Track 6.ogg -Track 6 -Artist 16 -Album 2 - -Tryout -1:00 -94 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 16/Album 2/5 - Track 5.ogg -Track 5 -Artist 16 -Album 2 - -Tryout -0:10 -95 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 16/Album 2/4 - Track 4.ogg -Track 4 -Artist 16 -Album 2 - -Tryout -0:01 -96 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 16/Album 2/3 - Track 3.ogg -Track 3 -Artist 16 -Album 2 - -Tryout -10:00 -97 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 16/Album 2/2 - Track 2.ogg -Track 2 -Artist 16 -Album 2 - -Tryout -1:00 -98 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 16/Album 2/1 - Track 1.ogg -Track 1 -Artist 16 -Album 2 - -Tryout -0:10 -99 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 16/Album 1/10 - Track 10.ogg -Track 10 -Artist 16 -Album 1 - -Trial -1:00 -100 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 16/Album 1/9 - Track 9.ogg -Track 9 -Artist 16 -Album 1 - -Trial -0:10 -101 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 16/Album 1/8 - Track 8.ogg -Track 8 -Artist 16 -Album 1 - -Trial -0:01 -102 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 16/Album 1/7 - Track 7.ogg -Track 7 -Artist 16 -Album 1 - -Trial -10:00 -103 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 16/Album 1/6 - Track 6.ogg -Track 6 -Artist 16 -Album 1 - -Trial -1:00 -104 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 16/Album 1/5 - Track 5.ogg -Track 5 -Artist 16 -Album 1 - -Trial -0:10 -105 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 16/Album 1/4 - Track 4.ogg -Track 4 -Artist 16 -Album 1 - -Trial -0:01 -106 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 16/Album 1/3 - Track 3.ogg -Track 3 -Artist 16 -Album 1 - -Trial -10:00 -107 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 16/Album 1/2 - Track 2.ogg -Track 2 -Artist 16 -Album 1 - -Trial -1:00 -108 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 16/Album 1/1 - Track 1.ogg -Track 1 -Artist 16 -Album 1 - -Trial -0:10 -109 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 16/Album 0/10 - Track 10.ogg -Track 10 -Artist 16 -Album 0 - -Test -1:00 -110 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 16/Album 0/9 - Track 9.ogg -Track 9 -Artist 16 -Album 0 - -Test -0:10 -111 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 16/Album 0/8 - Track 8.ogg -Track 8 -Artist 16 -Album 0 - -Test -0:01 -112 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 16/Album 0/7 - Track 7.ogg -Track 7 -Artist 16 -Album 0 - -Test -10:00 -113 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 16/Album 0/6 - Track 6.ogg -Track 6 -Artist 16 -Album 0 - -Test -1:00 -114 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 16/Album 0/5 - Track 5.ogg -Track 5 -Artist 16 -Album 0 - -Test -0:10 -115 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 16/Album 0/4 - Track 4.ogg -Track 4 -Artist 16 -Album 0 - -Test -0:01 -116 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 16/Album 0/3 - Track 3.ogg -Track 3 -Artist 16 -Album 0 - -Test -10:00 -117 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 16/Album 0/2 - Track 2.ogg -Track 2 -Artist 16 -Album 0 - -Test -1:00 -118 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 16/Album 0/1 - Track 1.ogg -Track 1 -Artist 16 -Album 0 - -Test -0:10 -119 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 15/Album 2/10 - Track 10.ogg -Track 10 -Artist 15 -Album 2 - -Tryout -1:00 -120 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 15/Album 2/9 - Track 9.ogg -Track 9 -Artist 15 -Album 2 - -Tryout -0:10 -121 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 15/Album 2/8 - Track 8.ogg -Track 8 -Artist 15 -Album 2 - -Tryout -0:01 -122 2013 8 1 1 1 2014 1 22 8000 1 0 -/tmp/library/3/Artist 15/Album 2/7 - Track 7.ogg -Track 7 -Artist 15 -Album 2 - -Tryout -10:00 -123 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 15/Album 2/6 - Track 6.ogg -Track 6 -Artist 15 -Album 2 - -Tryout -1:00 -124 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 15/Album 2/5 - Track 5.ogg -Track 5 -Artist 15 -Album 2 - -Tryout -0:10 -125 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 15/Album 2/4 - Track 4.ogg -Track 4 -Artist 15 -Album 2 - -Tryout -0:01 -126 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 15/Album 2/3 - Track 3.ogg -Track 3 -Artist 15 -Album 2 - -Tryout -10:00 -127 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 15/Album 2/2 - Track 2.ogg -Track 2 -Artist 15 -Album 2 - -Tryout -1:00 -128 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 15/Album 2/1 - Track 1.ogg -Track 1 -Artist 15 -Album 2 - -Tryout -0:10 -129 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 15/Album 1/10 - Track 10.ogg -Track 10 -Artist 15 -Album 1 - -Trial -1:00 -130 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 15/Album 1/9 - Track 9.ogg -Track 9 -Artist 15 -Album 1 - -Trial -0:10 -131 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 15/Album 1/8 - Track 8.ogg -Track 8 -Artist 15 -Album 1 - -Trial -0:01 -132 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 15/Album 1/7 - Track 7.ogg -Track 7 -Artist 15 -Album 1 - -Trial -10:00 -133 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 15/Album 1/6 - Track 6.ogg -Track 6 -Artist 15 -Album 1 - -Trial -1:00 -134 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 15/Album 1/5 - Track 5.ogg -Track 5 -Artist 15 -Album 1 - -Trial -0:10 -135 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 15/Album 1/4 - Track 4.ogg -Track 4 -Artist 15 -Album 1 - -Trial -0:01 -136 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 15/Album 1/3 - Track 3.ogg -Track 3 -Artist 15 -Album 1 - -Trial -10:00 -137 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 15/Album 1/2 - Track 2.ogg -Track 2 -Artist 15 -Album 1 - -Trial -1:00 -138 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 15/Album 1/1 - Track 1.ogg -Track 1 -Artist 15 -Album 1 - -Trial -0:10 -139 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 15/Album 0/10 - Track 10.ogg -Track 10 -Artist 15 -Album 0 - -Test -1:00 -140 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 15/Album 0/9 - Track 9.ogg -Track 9 -Artist 15 -Album 0 - -Test -0:10 -141 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 15/Album 0/8 - Track 8.ogg -Track 8 -Artist 15 -Album 0 - -Test -0:01 -142 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 15/Album 0/7 - Track 7.ogg -Track 7 -Artist 15 -Album 0 - -Test -10:00 -143 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 15/Album 0/6 - Track 6.ogg -Track 6 -Artist 15 -Album 0 - -Test -1:00 -144 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 15/Album 0/5 - Track 5.ogg -Track 5 -Artist 15 -Album 0 - -Test -0:10 -145 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/3/Artist 15/Album 0/4 - Track 4.ogg -Track 4 -Artist 15 -Album 0 - -Test -0:01 -146 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/3/Artist 15/Album 0/3 - Track 3.ogg -Track 3 -Artist 15 -Album 0 - -Test -10:00 -147 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/3/Artist 15/Album 0/2 - Track 2.ogg -Track 2 -Artist 15 -Album 0 - -Test -1:00 -148 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/3/Artist 15/Album 0/1 - Track 1.ogg -Track 1 -Artist 15 -Album 0 - -Test -0:10 -149 2011 1 0 0 0 0 10 22 8000 1 0 diff --git a/tests/library/3 b/tests/library/3 deleted file mode 100644 index 6e467d22..00000000 --- a/tests/library/3 +++ /dev/null @@ -1,1203 +0,0 @@ -2 -/tmp/library/4 -2 1 150 150 -/tmp/library/4/Artist 24/Album 2/10 - Track 10.ogg -Track 10 -Artist 24 -Album 2 - -Tryout -1:00 -0 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 24/Album 2/9 - Track 9.ogg -Track 9 -Artist 24 -Album 2 - -Tryout -0:10 -1 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 24/Album 2/8 - Track 8.ogg -Track 8 -Artist 24 -Album 2 - -Tryout -0:01 -2 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 24/Album 2/7 - Track 7.ogg -Track 7 -Artist 24 -Album 2 - -Tryout -10:00 -3 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 24/Album 2/6 - Track 6.ogg -Track 6 -Artist 24 -Album 2 - -Tryout -1:00 -4 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 24/Album 2/5 - Track 5.ogg -Track 5 -Artist 24 -Album 2 - -Tryout -0:10 -5 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 24/Album 2/4 - Track 4.ogg -Track 4 -Artist 24 -Album 2 - -Tryout -0:01 -6 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 24/Album 2/3 - Track 3.ogg -Track 3 -Artist 24 -Album 2 - -Tryout -10:00 -7 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 24/Album 2/2 - Track 2.ogg -Track 2 -Artist 24 -Album 2 - -Tryout -1:00 -8 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 24/Album 2/1 - Track 1.ogg -Track 1 -Artist 24 -Album 2 - -Tryout -0:10 -9 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 24/Album 1/10 - Track 10.ogg -Track 10 -Artist 24 -Album 1 - -Trial -1:00 -10 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 24/Album 1/9 - Track 9.ogg -Track 9 -Artist 24 -Album 1 - -Trial -0:10 -11 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 24/Album 1/8 - Track 8.ogg -Track 8 -Artist 24 -Album 1 - -Trial -0:01 -12 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 24/Album 1/7 - Track 7.ogg -Track 7 -Artist 24 -Album 1 - -Trial -10:00 -13 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 24/Album 1/6 - Track 6.ogg -Track 6 -Artist 24 -Album 1 - -Trial -1:00 -14 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 24/Album 1/5 - Track 5.ogg -Track 5 -Artist 24 -Album 1 - -Trial -0:10 -15 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 24/Album 1/4 - Track 4.ogg -Track 4 -Artist 24 -Album 1 - -Trial -0:01 -16 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 24/Album 1/3 - Track 3.ogg -Track 3 -Artist 24 -Album 1 - -Trial -10:00 -17 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 24/Album 1/2 - Track 2.ogg -Track 2 -Artist 24 -Album 1 - -Trial -1:00 -18 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 24/Album 1/1 - Track 1.ogg -Track 1 -Artist 24 -Album 1 - -Trial -0:10 -19 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 24/Album 0/10 - Track 10.ogg -Track 10 -Artist 24 -Album 0 - -Test -1:00 -20 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 24/Album 0/9 - Track 9.ogg -Track 9 -Artist 24 -Album 0 - -Test -0:10 -21 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 24/Album 0/8 - Track 8.ogg -Track 8 -Artist 24 -Album 0 - -Test -0:01 -22 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 24/Album 0/7 - Track 7.ogg -Track 7 -Artist 24 -Album 0 - -Test -10:00 -23 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 24/Album 0/6 - Track 6.ogg -Track 6 -Artist 24 -Album 0 - -Test -1:00 -24 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 24/Album 0/5 - Track 5.ogg -Track 5 -Artist 24 -Album 0 - -Test -0:10 -25 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 24/Album 0/4 - Track 4.ogg -Track 4 -Artist 24 -Album 0 - -Test -0:01 -26 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 24/Album 0/3 - Track 3.ogg -Track 3 -Artist 24 -Album 0 - -Test -10:00 -27 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 24/Album 0/2 - Track 2.ogg -Track 2 -Artist 24 -Album 0 - -Test -1:00 -28 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 24/Album 0/1 - Track 1.ogg -Track 1 -Artist 24 -Album 0 - -Test -0:10 -29 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 23/Album 2/10 - Track 10.ogg -Track 10 -Artist 23 -Album 2 - -Tryout -1:00 -30 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 23/Album 2/9 - Track 9.ogg -Track 9 -Artist 23 -Album 2 - -Tryout -0:10 -31 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 23/Album 2/8 - Track 8.ogg -Track 8 -Artist 23 -Album 2 - -Tryout -0:01 -32 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 23/Album 2/7 - Track 7.ogg -Track 7 -Artist 23 -Album 2 - -Tryout -10:00 -33 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 23/Album 2/6 - Track 6.ogg -Track 6 -Artist 23 -Album 2 - -Tryout -1:00 -34 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 23/Album 2/5 - Track 5.ogg -Track 5 -Artist 23 -Album 2 - -Tryout -0:10 -35 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 23/Album 2/4 - Track 4.ogg -Track 4 -Artist 23 -Album 2 - -Tryout -0:01 -36 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 23/Album 2/3 - Track 3.ogg -Track 3 -Artist 23 -Album 2 - -Tryout -10:00 -37 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 23/Album 2/2 - Track 2.ogg -Track 2 -Artist 23 -Album 2 - -Tryout -1:00 -38 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 23/Album 2/1 - Track 1.ogg -Track 1 -Artist 23 -Album 2 - -Tryout -0:10 -39 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 23/Album 1/10 - Track 10.ogg -Track 10 -Artist 23 -Album 1 - -Trial -1:00 -40 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 23/Album 1/9 - Track 9.ogg -Track 9 -Artist 23 -Album 1 - -Trial -0:10 -41 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 23/Album 1/8 - Track 8.ogg -Track 8 -Artist 23 -Album 1 - -Trial -0:01 -42 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 23/Album 1/7 - Track 7.ogg -Track 7 -Artist 23 -Album 1 - -Trial -10:00 -43 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 23/Album 1/6 - Track 6.ogg -Track 6 -Artist 23 -Album 1 - -Trial -1:00 -44 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 23/Album 1/5 - Track 5.ogg -Track 5 -Artist 23 -Album 1 - -Trial -0:10 -45 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 23/Album 1/4 - Track 4.ogg -Track 4 -Artist 23 -Album 1 - -Trial -0:01 -46 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 23/Album 1/3 - Track 3.ogg -Track 3 -Artist 23 -Album 1 - -Trial -10:00 -47 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 23/Album 1/2 - Track 2.ogg -Track 2 -Artist 23 -Album 1 - -Trial -1:00 -48 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 23/Album 1/1 - Track 1.ogg -Track 1 -Artist 23 -Album 1 - -Trial -0:10 -49 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 23/Album 0/10 - Track 10.ogg -Track 10 -Artist 23 -Album 0 - -Test -1:00 -50 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 23/Album 0/9 - Track 9.ogg -Track 9 -Artist 23 -Album 0 - -Test -0:10 -51 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 23/Album 0/8 - Track 8.ogg -Track 8 -Artist 23 -Album 0 - -Test -0:01 -52 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 23/Album 0/7 - Track 7.ogg -Track 7 -Artist 23 -Album 0 - -Test -10:00 -53 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 23/Album 0/6 - Track 6.ogg -Track 6 -Artist 23 -Album 0 - -Test -1:00 -54 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 23/Album 0/5 - Track 5.ogg -Track 5 -Artist 23 -Album 0 - -Test -0:10 -55 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 23/Album 0/4 - Track 4.ogg -Track 4 -Artist 23 -Album 0 - -Test -0:01 -56 2011 4 1 1 1 2014 1 22 8000 1 0 -/tmp/library/4/Artist 23/Album 0/3 - Track 3.ogg -Track 3 -Artist 23 -Album 0 - -Test -10:00 -57 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 23/Album 0/2 - Track 2.ogg -Track 2 -Artist 23 -Album 0 - -Test -1:00 -58 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 23/Album 0/1 - Track 1.ogg -Track 1 -Artist 23 -Album 0 - -Test -0:10 -59 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 22/Album 2/10 - Track 10.ogg -Track 10 -Artist 22 -Album 2 - -Tryout -1:00 -60 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 22/Album 2/9 - Track 9.ogg -Track 9 -Artist 22 -Album 2 - -Tryout -0:10 -61 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 22/Album 2/8 - Track 8.ogg -Track 8 -Artist 22 -Album 2 - -Tryout -0:01 -62 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 22/Album 2/7 - Track 7.ogg -Track 7 -Artist 22 -Album 2 - -Tryout -10:00 -63 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 22/Album 2/6 - Track 6.ogg -Track 6 -Artist 22 -Album 2 - -Tryout -1:00 -64 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 22/Album 2/5 - Track 5.ogg -Track 5 -Artist 22 -Album 2 - -Tryout -0:10 -65 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 22/Album 2/4 - Track 4.ogg -Track 4 -Artist 22 -Album 2 - -Tryout -0:01 -66 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 22/Album 2/3 - Track 3.ogg -Track 3 -Artist 22 -Album 2 - -Tryout -10:00 -67 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 22/Album 2/2 - Track 2.ogg -Track 2 -Artist 22 -Album 2 - -Tryout -1:00 -68 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 22/Album 2/1 - Track 1.ogg -Track 1 -Artist 22 -Album 2 - -Tryout -0:10 -69 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 22/Album 1/10 - Track 10.ogg -Track 10 -Artist 22 -Album 1 - -Trial -1:00 -70 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 22/Album 1/9 - Track 9.ogg -Track 9 -Artist 22 -Album 1 - -Trial -0:10 -71 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 22/Album 1/8 - Track 8.ogg -Track 8 -Artist 22 -Album 1 - -Trial -0:01 -72 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 22/Album 1/7 - Track 7.ogg -Track 7 -Artist 22 -Album 1 - -Trial -10:00 -73 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 22/Album 1/6 - Track 6.ogg -Track 6 -Artist 22 -Album 1 - -Trial -1:00 -74 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 22/Album 1/5 - Track 5.ogg -Track 5 -Artist 22 -Album 1 - -Trial -0:10 -75 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 22/Album 1/4 - Track 4.ogg -Track 4 -Artist 22 -Album 1 - -Trial -0:01 -76 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 22/Album 1/3 - Track 3.ogg -Track 3 -Artist 22 -Album 1 - -Trial -10:00 -77 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 22/Album 1/2 - Track 2.ogg -Track 2 -Artist 22 -Album 1 - -Trial -1:00 -78 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 22/Album 1/1 - Track 1.ogg -Track 1 -Artist 22 -Album 1 - -Trial -0:10 -79 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 22/Album 0/10 - Track 10.ogg -Track 10 -Artist 22 -Album 0 - -Test -1:00 -80 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 22/Album 0/9 - Track 9.ogg -Track 9 -Artist 22 -Album 0 - -Test -0:10 -81 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 22/Album 0/8 - Track 8.ogg -Track 8 -Artist 22 -Album 0 - -Test -0:01 -82 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 22/Album 0/7 - Track 7.ogg -Track 7 -Artist 22 -Album 0 - -Test -10:00 -83 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 22/Album 0/6 - Track 6.ogg -Track 6 -Artist 22 -Album 0 - -Test -1:00 -84 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 22/Album 0/5 - Track 5.ogg -Track 5 -Artist 22 -Album 0 - -Test -0:10 -85 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 22/Album 0/4 - Track 4.ogg -Track 4 -Artist 22 -Album 0 - -Test -0:01 -86 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 22/Album 0/3 - Track 3.ogg -Track 3 -Artist 22 -Album 0 - -Test -10:00 -87 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 22/Album 0/2 - Track 2.ogg -Track 2 -Artist 22 -Album 0 - -Test -1:00 -88 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 22/Album 0/1 - Track 1.ogg -Track 1 -Artist 22 -Album 0 - -Test -0:10 -89 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 21/Album 2/10 - Track 10.ogg -Track 10 -Artist 21 -Album 2 - -Tryout -1:00 -90 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 21/Album 2/9 - Track 9.ogg -Track 9 -Artist 21 -Album 2 - -Tryout -0:10 -91 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 21/Album 2/8 - Track 8.ogg -Track 8 -Artist 21 -Album 2 - -Tryout -0:01 -92 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 21/Album 2/7 - Track 7.ogg -Track 7 -Artist 21 -Album 2 - -Tryout -10:00 -93 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 21/Album 2/6 - Track 6.ogg -Track 6 -Artist 21 -Album 2 - -Tryout -1:00 -94 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 21/Album 2/5 - Track 5.ogg -Track 5 -Artist 21 -Album 2 - -Tryout -0:10 -95 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 21/Album 2/4 - Track 4.ogg -Track 4 -Artist 21 -Album 2 - -Tryout -0:01 -96 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 21/Album 2/3 - Track 3.ogg -Track 3 -Artist 21 -Album 2 - -Tryout -10:00 -97 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 21/Album 2/2 - Track 2.ogg -Track 2 -Artist 21 -Album 2 - -Tryout -1:00 -98 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 21/Album 2/1 - Track 1.ogg -Track 1 -Artist 21 -Album 2 - -Tryout -0:10 -99 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 21/Album 1/10 - Track 10.ogg -Track 10 -Artist 21 -Album 1 - -Trial -1:00 -100 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 21/Album 1/9 - Track 9.ogg -Track 9 -Artist 21 -Album 1 - -Trial -0:10 -101 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 21/Album 1/8 - Track 8.ogg -Track 8 -Artist 21 -Album 1 - -Trial -0:01 -102 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 21/Album 1/7 - Track 7.ogg -Track 7 -Artist 21 -Album 1 - -Trial -10:00 -103 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 21/Album 1/6 - Track 6.ogg -Track 6 -Artist 21 -Album 1 - -Trial -1:00 -104 2012 6 1 1 1 2014 60 22 8000 1 0 -/tmp/library/4/Artist 21/Album 1/5 - Track 5.ogg -Track 5 -Artist 21 -Album 1 - -Trial -0:10 -105 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 21/Album 1/4 - Track 4.ogg -Track 4 -Artist 21 -Album 1 - -Trial -0:01 -106 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 21/Album 1/3 - Track 3.ogg -Track 3 -Artist 21 -Album 1 - -Trial -10:00 -107 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 21/Album 1/2 - Track 2.ogg -Track 2 -Artist 21 -Album 1 - -Trial -1:00 -108 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 21/Album 1/1 - Track 1.ogg -Track 1 -Artist 21 -Album 1 - -Trial -0:10 -109 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 21/Album 0/10 - Track 10.ogg -Track 10 -Artist 21 -Album 0 - -Test -1:00 -110 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 21/Album 0/9 - Track 9.ogg -Track 9 -Artist 21 -Album 0 - -Test -0:10 -111 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 21/Album 0/8 - Track 8.ogg -Track 8 -Artist 21 -Album 0 - -Test -0:01 -112 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 21/Album 0/7 - Track 7.ogg -Track 7 -Artist 21 -Album 0 - -Test -10:00 -113 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 21/Album 0/6 - Track 6.ogg -Track 6 -Artist 21 -Album 0 - -Test -1:00 -114 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 21/Album 0/5 - Track 5.ogg -Track 5 -Artist 21 -Album 0 - -Test -0:10 -115 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 21/Album 0/4 - Track 4.ogg -Track 4 -Artist 21 -Album 0 - -Test -0:01 -116 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 21/Album 0/3 - Track 3.ogg -Track 3 -Artist 21 -Album 0 - -Test -10:00 -117 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 21/Album 0/2 - Track 2.ogg -Track 2 -Artist 21 -Album 0 - -Test -1:00 -118 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 21/Album 0/1 - Track 1.ogg -Track 1 -Artist 21 -Album 0 - -Test -0:10 -119 2011 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 20/Album 2/10 - Track 10.ogg -Track 10 -Artist 20 -Album 2 - -Tryout -1:00 -120 2013 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 20/Album 2/9 - Track 9.ogg -Track 9 -Artist 20 -Album 2 - -Tryout -0:10 -121 2013 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 20/Album 2/8 - Track 8.ogg -Track 8 -Artist 20 -Album 2 - -Tryout -0:01 -122 2013 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 20/Album 2/7 - Track 7.ogg -Track 7 -Artist 20 -Album 2 - -Tryout -10:00 -123 2013 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 20/Album 2/6 - Track 6.ogg -Track 6 -Artist 20 -Album 2 - -Tryout -1:00 -124 2013 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 20/Album 2/5 - Track 5.ogg -Track 5 -Artist 20 -Album 2 - -Tryout -0:10 -125 2013 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 20/Album 2/4 - Track 4.ogg -Track 4 -Artist 20 -Album 2 - -Tryout -0:01 -126 2013 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 20/Album 2/3 - Track 3.ogg -Track 3 -Artist 20 -Album 2 - -Tryout -10:00 -127 2013 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 20/Album 2/2 - Track 2.ogg -Track 2 -Artist 20 -Album 2 - -Tryout -1:00 -128 2013 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 20/Album 2/1 - Track 1.ogg -Track 1 -Artist 20 -Album 2 - -Tryout -0:10 -129 2013 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 20/Album 1/10 - Track 10.ogg -Track 10 -Artist 20 -Album 1 - -Trial -1:00 -130 2012 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 20/Album 1/9 - Track 9.ogg -Track 9 -Artist 20 -Album 1 - -Trial -0:10 -131 2012 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 20/Album 1/8 - Track 8.ogg -Track 8 -Artist 20 -Album 1 - -Trial -0:01 -132 2012 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 20/Album 1/7 - Track 7.ogg -Track 7 -Artist 20 -Album 1 - -Trial -10:00 -133 2012 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 20/Album 1/6 - Track 6.ogg -Track 6 -Artist 20 -Album 1 - -Trial -1:00 -134 2012 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 20/Album 1/5 - Track 5.ogg -Track 5 -Artist 20 -Album 1 - -Trial -0:10 -135 2012 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 20/Album 1/4 - Track 4.ogg -Track 4 -Artist 20 -Album 1 - -Trial -0:01 -136 2012 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 20/Album 1/3 - Track 3.ogg -Track 3 -Artist 20 -Album 1 - -Trial -10:00 -137 2012 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 20/Album 1/2 - Track 2.ogg -Track 2 -Artist 20 -Album 1 - -Trial -1:00 -138 2012 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 20/Album 1/1 - Track 1.ogg -Track 1 -Artist 20 -Album 1 - -Trial -0:10 -139 2012 1 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 20/Album 0/10 - Track 10.ogg -Track 10 -Artist 20 -Album 0 - -Test -1:00 -140 2011 10 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 20/Album 0/9 - Track 9.ogg -Track 9 -Artist 20 -Album 0 - -Test -0:10 -141 2011 9 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 20/Album 0/8 - Track 8.ogg -Track 8 -Artist 20 -Album 0 - -Test -0:01 -142 2011 8 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 20/Album 0/7 - Track 7.ogg -Track 7 -Artist 20 -Album 0 - -Test -10:00 -143 2011 7 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 20/Album 0/6 - Track 6.ogg -Track 6 -Artist 20 -Album 0 - -Test -1:00 -144 2011 6 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 20/Album 0/5 - Track 5.ogg -Track 5 -Artist 20 -Album 0 - -Test -0:10 -145 2011 5 0 0 0 0 10 22 8000 1 0 -/tmp/library/4/Artist 20/Album 0/4 - Track 4.ogg -Track 4 -Artist 20 -Album 0 - -Test -0:01 -146 2011 4 0 0 0 0 1 22 8000 1 0 -/tmp/library/4/Artist 20/Album 0/3 - Track 3.ogg -Track 3 -Artist 20 -Album 0 - -Test -10:00 -147 2011 3 0 0 0 0 600 22 8000 1 0 -/tmp/library/4/Artist 20/Album 0/2 - Track 2.ogg -Track 2 -Artist 20 -Album 0 - -Test -1:00 -148 2011 2 0 0 0 0 60 22 8000 1 0 -/tmp/library/4/Artist 20/Album 0/1 - Track 1.ogg -Track 1 -Artist 20 -Album 0 - -Test -0:10 -149 2011 1 0 0 0 0 10 22 8000 1 0 diff --git a/tests/library/60.ogg b/tests/library/60.ogg deleted file mode 100644 index 2b8b01ff..00000000 Binary files a/tests/library/60.ogg and /dev/null differ diff --git a/tests/library/600.ogg b/tests/library/600.ogg deleted file mode 100644 index 524cc002..00000000 Binary files a/tests/library/600.ogg and /dev/null differ diff --git a/tests/library/Sconscript b/tests/library/Sconscript deleted file mode 100644 index 433b4906..00000000 --- a/tests/library/Sconscript +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/python -Import("Test", "CONFIG") - -CONFIG.LIBRARY = True - -Test("library", "library.cpp") diff --git a/tests/library/gen_library.sh b/tests/library/gen_library.sh deleted file mode 100755 index ff213e77..00000000 --- a/tests/library/gen_library.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/bin/bash -# -# Copyright 2013 (c) Anna Schumaker. -# -# Generate a test library in /tmp -# - -declare -A genres -genres["Album 0"]=Test -genres["Album 1"]=Trial -genres["Album 2"]=Tryout - -declare -A dates -dates["Album 0"]=2011 -dates["Album 1"]=2012 -dates["Album 2"]=2013 - -# -# gen_tracks() $library $artist $album -# -function gen_tracks() -{ - library="library/$1" - artist="Artist $2" - album="Album $3" - - mkdir -p "/tmp/$library/$artist/$album" - - for i in $(seq 10); do - track="Track $i" - let remainder=$i%4 - out="/tmp/$library/$artist/$album/$i - $track.ogg" - - if [ -f "$out" ]; then - continue - fi - - case $remainder in - 0) OGG="1.ogg" ;; - 1) OGG="10.ogg" ;; - 2) OGG="60.ogg" ;; - 3) OGG="600.ogg" ;; - esac - - vorbiscomment -a -q -t "ARTIST=$artist" -t "ALBUM=$album" \ - -t "GENRE=${genres[$album]}" -t "DATE=${dates[$album]}" \ - -t "TRACKNUMBER=$i" -t "TITLE=$track" "tests/library/$OGG" \ - "/tmp/$library/$artist/$album/$i - $track.ogg" - done -} - -# -# gen_albums() $library $artist -# -function gen_albums() -{ - for i in $(seq 0 2); do - gen_tracks $1 $2 $i - done -} - -# -# gen_artists() $library -# -function gen_artists() -{ - let begin=$1*5 - let end=$begin+4 - - for i in $(seq $begin $end); do - gen_albums $1 $i - done -} - -for i in $(seq 0 4); do - echo "Generating library: $i" - gen_artists $i -done - -touch /tmp/library/file - -## -# Set up legacy library files -# -mkdir -p $HOME/.ocarina-test/library/ -cp tests/library/0 tests/library/1 tests/library/2 tests/library/3 $HOME/.ocarina-test/library/ diff --git a/tests/library/library.cpp b/tests/library/library.cpp deleted file mode 100644 index 17e7ff2e..00000000 --- a/tests/library/library.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include -#include -#include - -#include - -void gen_library() -{ - system("tests/library/gen_library.sh"); - 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 library_added(unsigned int id, library :: Library *path) -{ - print("Added library %u: %s\n", id, path->root_path.c_str()); -} - -void library_updated(unsigned int id, library :: Library *path) -{ - print("Updated library %u: %s (size: %u)\n", - id, path->root_path.c_str(), path->size); -} - -void test_add_dir(const std::string &test, const std::string &dir, bool expected) -{ - print("Test %s: ", test.c_str()); - if (check_add_dir(dir.c_str()) == expected) - print("PASSED\n"); - else - print("FAILED\n"); - - run_idle_tasks(); - library :: print_db(library :: DB_LIBRARY); -} - -void test_del_dir(const std::string &test, const unsigned int path_id) -{ - print("Test %s\n", test.c_str()); - library :: del_path(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; - bool res; - - print("Test %s (track_id == %u): ", test.c_str(), track_id); - - res = check_lookup(track_id, &song); - if (res == expected) - print("PASSED\n"); - else - print("FAILED\n"); - - if (res == true) { - 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()); - } -} - -void test_path_lookup(const std::string &test, unsigned int lib_id, bool expected) -{ - library :: Library *lib; - - print("Test %s (lib_id == %u): ", test.c_str(), lib_id); - - try { - lib = library :: lookup_path(lib_id); - if (expected == true) - print("PASSED\n"); - else - print("FAILED, no error produced\n"); - - print("%s (", lib->root_path.c_str()); - if (lib->enabled) - print("enabled"); - else - print("disabled"); - print(") size = %u\n", lib->size); - } catch (int error) { - if (expected == true) - print("FAILED, unexpected error %d\n", error); - else - print("PASSED\n"); - } -} - -void test_print_dbs(const std::string &test) -{ - print("Test %s\n", test.c_str()); - library :: print_db(library :: DB_GENRE); - print("\n"); -} - - -/* Add paths library that SHOULD fail */ -void test_0() -{ - test_add_dir("0a", "/tmp/library/error", false); - test_add_dir("0b", "/tmp/library/file", false); - print("\n"); -} - -/* Simple library path operations */ -void test_1() -{ - test_add_dir("1a", "/tmp/library/0", true); - test_del_dir("1b", 0); - print("\n"); -} - -/* Test multiple paths */ -void test_2() -{ - library :: reset(); - test_add_dir("2a", "/tmp/library/0", true); - test_add_dir("2b", "/tmp/library/1", true); - test_add_dir("2c", "/tmp/library/2", true); - - test_del_dir("2d", 1); - test_del_dir("2e", 0); - test_del_dir("2f", 2); - print("\n"); -} - -/* Test load and save of library db */ -void test_3() -{ - library :: reset(); - test_add_dir("3a", "/tmp/library/0", true); - test_add_dir("3b", "/tmp/library/1", true); - test_add_dir("3c", "/tmp/library/2", true); - - print("Test 3d\n"); - library :: reset(); - library :: print_db(library :: DB_LIBRARY); - - print("Test 3e\n"); - library :: init(); - library :: print_db(library :: DB_LIBRARY); - print("\n"); -} - -/* Test scanning a single path */ -void test_4() -{ - library :: reset(); - test_add_dir("4a", "/tmp/library/0", true); - - library :: print_db(library :: DB_ARTIST); - print("\n"); - library :: print_db(library :: DB_ALBUM); - print("\n"); - library :: print_db(library :: DB_GENRE); - print("\n"); - library :: print_db(library :: DB_TRACK); - print("\n"); -} - -/* Test lookup() */ -void test_5() -{ - library :: reset(); - - /* Lookup on empty DB */ - test_lookup("5a", 0, false); - test_add_dir("5b", "/tmp/library/0", true); - /* Lookup on DB[0] */ - test_lookup("5c", 0, true); - /* Lookup on DB[10] */ - test_lookup("5d", 42, true); - /* Lookup beyond db */ - test_lookup("5e", 100000, false); - - /* Lookup path id = 0 */ - test_path_lookup("5f", 0, true); - /* Lookup path id that doesn't exist */ - test_path_lookup("5g", 1, false); - print("\n"); -} - -/* Test validation code */ -void test_6() -{ - library :: reset(); - - test_add_dir("6a", "/tmp/library/0", true); - print("\n"); - - 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_LIBRARY); - library :: print_db(library :: DB_TRACK); - print("\n"); - - print("6d: Regenerate Artist 2\n"); - fflush(stdout); - gen_library(); - fflush(stdout); - library :: update_all(); - run_idle_tasks(); - library :: print_db(library :: DB_TRACK); - print("\n"); -} - -/* Test importing Ocarina 5.11 libraries */ -void test_7() -{ - library :: reset(); - - test_add_dir("7a", "/tmp/library/2", true); - - print("\n"); - library :: import(); - run_idle_tasks(); - library :: print_db(library :: DB_LIBRARY); - print("\n"); -} - -/* Test disabling libraries */ -void test_8() -{ - library :: reset(); - - test_add_dir("8a", "/tmp/library/0", true); - print("\n"); - library :: set_enabled(0, false); - library :: print_db(library :: DB_LIBRARY); - library :: set_enabled(0, true); - library :: print_db(library :: DB_LIBRARY); - library :: set_enabled(0, false); - library :: print_db(library :: DB_LIBRARY); - library :: set_enabled(0, true); - library :: print_db(library :: DB_LIBRARY); -} - - -int main(int argc, char **argv) -{ - struct Callbacks *cb; - gen_library(); - - cb = get_callbacks(); - cb->on_library_add = library_added; - cb->on_library_update = library_updated; - - test_0(); - test_1(); - test_2(); - test_3(); - test_4(); - test_5(); - test_6(); - test_7(); - test_8(); - return 0; -} diff --git a/tests/library/library.good b/tests/library/library.good deleted file mode 100644 index 309c7cc2..00000000 --- a/tests/library/library.good +++ /dev/null @@ -1,2704 +0,0 @@ -Generating library: 0 -Generating library: 1 -Generating library: 2 -Generating library: 3 -Generating library: 4 - -Test 0a: PASSED -Allocated rows: 0 -Valid rows: 0 -Test 0b: PASSED -Allocated rows: 0 -Valid rows: 0 - -Test 1a: Added library 0: /tmp/library/0 -PASSED -Updated library 0: /tmp/library/0 (size: 10) -Updated library 0: /tmp/library/0 (size: 20) -Updated library 0: /tmp/library/0 (size: 30) -Updated library 0: /tmp/library/0 (size: 40) -Updated library 0: /tmp/library/0 (size: 50) -Updated library 0: /tmp/library/0 (size: 60) -Updated library 0: /tmp/library/0 (size: 70) -Updated library 0: /tmp/library/0 (size: 80) -Updated library 0: /tmp/library/0 (size: 90) -Updated library 0: /tmp/library/0 (size: 100) -Updated library 0: /tmp/library/0 (size: 110) -Updated library 0: /tmp/library/0 (size: 120) -Updated library 0: /tmp/library/0 (size: 130) -Updated library 0: /tmp/library/0 (size: 140) -Updated library 0: /tmp/library/0 (size: 150) -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (enabled), size = 150 -Test 1b -Allocated rows: 1 -Valid rows: 0 - -Test 2a: Added library 0: /tmp/library/0 -PASSED -Updated library 0: /tmp/library/0 (size: 10) -Updated library 0: /tmp/library/0 (size: 20) -Updated library 0: /tmp/library/0 (size: 30) -Updated library 0: /tmp/library/0 (size: 40) -Updated library 0: /tmp/library/0 (size: 50) -Updated library 0: /tmp/library/0 (size: 60) -Updated library 0: /tmp/library/0 (size: 70) -Updated library 0: /tmp/library/0 (size: 80) -Updated library 0: /tmp/library/0 (size: 90) -Updated library 0: /tmp/library/0 (size: 100) -Updated library 0: /tmp/library/0 (size: 110) -Updated library 0: /tmp/library/0 (size: 120) -Updated library 0: /tmp/library/0 (size: 130) -Updated library 0: /tmp/library/0 (size: 140) -Updated library 0: /tmp/library/0 (size: 150) -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (enabled), size = 150 -Test 2b: Added library 1: /tmp/library/1 -PASSED -Updated library 1: /tmp/library/1 (size: 10) -Updated library 1: /tmp/library/1 (size: 20) -Updated library 1: /tmp/library/1 (size: 30) -Updated library 1: /tmp/library/1 (size: 40) -Updated library 1: /tmp/library/1 (size: 50) -Updated library 1: /tmp/library/1 (size: 60) -Updated library 1: /tmp/library/1 (size: 70) -Updated library 1: /tmp/library/1 (size: 80) -Updated library 1: /tmp/library/1 (size: 90) -Updated library 1: /tmp/library/1 (size: 100) -Updated library 1: /tmp/library/1 (size: 110) -Updated library 1: /tmp/library/1 (size: 120) -Updated library 1: /tmp/library/1 (size: 130) -Updated library 1: /tmp/library/1 (size: 140) -Updated library 1: /tmp/library/1 (size: 150) -Allocated rows: 2 -Valid rows: 2 -db[0] = /tmp/library/0 (enabled), size = 150 -db[1] = /tmp/library/1 (enabled), size = 150 -Test 2c: Added library 2: /tmp/library/2 -PASSED -Updated library 2: /tmp/library/2 (size: 10) -Updated library 2: /tmp/library/2 (size: 20) -Updated library 2: /tmp/library/2 (size: 30) -Updated library 2: /tmp/library/2 (size: 40) -Updated library 2: /tmp/library/2 (size: 50) -Updated library 2: /tmp/library/2 (size: 60) -Updated library 2: /tmp/library/2 (size: 70) -Updated library 2: /tmp/library/2 (size: 80) -Updated library 2: /tmp/library/2 (size: 90) -Updated library 2: /tmp/library/2 (size: 100) -Updated library 2: /tmp/library/2 (size: 110) -Updated library 2: /tmp/library/2 (size: 120) -Updated library 2: /tmp/library/2 (size: 130) -Updated library 2: /tmp/library/2 (size: 140) -Updated library 2: /tmp/library/2 (size: 150) -Allocated rows: 3 -Valid rows: 3 -db[0] = /tmp/library/0 (enabled), size = 150 -db[1] = /tmp/library/1 (enabled), size = 150 -db[2] = /tmp/library/2 (enabled), size = 150 -Test 2d -Allocated rows: 3 -Valid rows: 2 -db[0] = /tmp/library/0 (enabled), size = 150 -db[2] = /tmp/library/2 (enabled), size = 150 -Test 2e -Allocated rows: 3 -Valid rows: 1 -db[2] = /tmp/library/2 (enabled), size = 150 -Test 2f -Allocated rows: 3 -Valid rows: 0 - -Test 3a: Added library 0: /tmp/library/0 -PASSED -Updated library 0: /tmp/library/0 (size: 10) -Updated library 0: /tmp/library/0 (size: 20) -Updated library 0: /tmp/library/0 (size: 30) -Updated library 0: /tmp/library/0 (size: 40) -Updated library 0: /tmp/library/0 (size: 50) -Updated library 0: /tmp/library/0 (size: 60) -Updated library 0: /tmp/library/0 (size: 70) -Updated library 0: /tmp/library/0 (size: 80) -Updated library 0: /tmp/library/0 (size: 90) -Updated library 0: /tmp/library/0 (size: 100) -Updated library 0: /tmp/library/0 (size: 110) -Updated library 0: /tmp/library/0 (size: 120) -Updated library 0: /tmp/library/0 (size: 130) -Updated library 0: /tmp/library/0 (size: 140) -Updated library 0: /tmp/library/0 (size: 150) -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (enabled), size = 150 -Test 3b: Added library 1: /tmp/library/1 -PASSED -Updated library 1: /tmp/library/1 (size: 10) -Updated library 1: /tmp/library/1 (size: 20) -Updated library 1: /tmp/library/1 (size: 30) -Updated library 1: /tmp/library/1 (size: 40) -Updated library 1: /tmp/library/1 (size: 50) -Updated library 1: /tmp/library/1 (size: 60) -Updated library 1: /tmp/library/1 (size: 70) -Updated library 1: /tmp/library/1 (size: 80) -Updated library 1: /tmp/library/1 (size: 90) -Updated library 1: /tmp/library/1 (size: 100) -Updated library 1: /tmp/library/1 (size: 110) -Updated library 1: /tmp/library/1 (size: 120) -Updated library 1: /tmp/library/1 (size: 130) -Updated library 1: /tmp/library/1 (size: 140) -Updated library 1: /tmp/library/1 (size: 150) -Allocated rows: 2 -Valid rows: 2 -db[0] = /tmp/library/0 (enabled), size = 150 -db[1] = /tmp/library/1 (enabled), size = 150 -Test 3c: Added library 2: /tmp/library/2 -PASSED -Updated library 2: /tmp/library/2 (size: 10) -Updated library 2: /tmp/library/2 (size: 20) -Updated library 2: /tmp/library/2 (size: 30) -Updated library 2: /tmp/library/2 (size: 40) -Updated library 2: /tmp/library/2 (size: 50) -Updated library 2: /tmp/library/2 (size: 60) -Updated library 2: /tmp/library/2 (size: 70) -Updated library 2: /tmp/library/2 (size: 80) -Updated library 2: /tmp/library/2 (size: 90) -Updated library 2: /tmp/library/2 (size: 100) -Updated library 2: /tmp/library/2 (size: 110) -Updated library 2: /tmp/library/2 (size: 120) -Updated library 2: /tmp/library/2 (size: 130) -Updated library 2: /tmp/library/2 (size: 140) -Updated library 2: /tmp/library/2 (size: 150) -Allocated rows: 3 -Valid rows: 3 -db[0] = /tmp/library/0 (enabled), size = 150 -db[1] = /tmp/library/1 (enabled), size = 150 -db[2] = /tmp/library/2 (enabled), size = 150 -Test 3d -Allocated rows: 0 -Valid rows: 0 -Test 3e -Added library 0: /tmp/library/0 -Added library 1: /tmp/library/1 -Added library 2: /tmp/library/2 -Allocated rows: 3 -Valid rows: 3 -db[0] = /tmp/library/0 (enabled), size = 150 -db[1] = /tmp/library/1 (enabled), size = 150 -db[2] = /tmp/library/2 (enabled), size = 150 - -Test 4a: Added library 0: /tmp/library/0 -PASSED -Updated library 0: /tmp/library/0 (size: 10) -Updated library 0: /tmp/library/0 (size: 20) -Updated library 0: /tmp/library/0 (size: 30) -Updated library 0: /tmp/library/0 (size: 40) -Updated library 0: /tmp/library/0 (size: 50) -Updated library 0: /tmp/library/0 (size: 60) -Updated library 0: /tmp/library/0 (size: 70) -Updated library 0: /tmp/library/0 (size: 80) -Updated library 0: /tmp/library/0 (size: 90) -Updated library 0: /tmp/library/0 (size: 100) -Updated library 0: /tmp/library/0 (size: 110) -Updated library 0: /tmp/library/0 (size: 120) -Updated library 0: /tmp/library/0 (size: 130) -Updated library 0: /tmp/library/0 (size: 140) -Updated library 0: /tmp/library/0 (size: 150) -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (enabled), size = 150 -Allocated rows: 5 -Valid rows: 5 -db[0] = Artist: Artist 2 -db[1] = Artist: Artist 4 -db[2] = Artist: Artist 3 -db[3] = Artist: Artist 1 -db[4] = Artist: Artist 0 - -Allocated rows: 15 -Valid rows: 15 -db[0] = Album: Album 2 (2013) by Artist 2 -db[1] = Album: Album 1 (2012) by Artist 2 -db[2] = Album: Album 0 (2011) by Artist 2 -db[3] = Album: Album 2 (2013) by Artist 4 -db[4] = Album: Album 1 (2012) by Artist 4 -db[5] = Album: Album 0 (2011) by Artist 4 -db[6] = Album: Album 2 (2013) by Artist 3 -db[7] = Album: Album 1 (2012) by Artist 3 -db[8] = Album: Album 0 (2011) by Artist 3 -db[9] = Album: Album 2 (2013) by Artist 1 -db[10] = Album: Album 1 (2012) by Artist 1 -db[11] = Album: Album 0 (2011) by Artist 1 -db[12] = Album: Album 2 (2013) by Artist 0 -db[13] = Album: Album 1 (2012) by Artist 0 -db[14] = Album: Album 0 (2011) by Artist 0 - -Allocated rows: 3 -Valid rows: 3 -db[0] = Genre: Tryout -db[1] = Genre: Trial -db[2] = Genre: Test - -Allocated rows: 150 -Valid rows: 150 -db[0] = 10. Track 10 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/10 - Track 10.ogg -db[1] = 9. Track 9 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/9 - Track 9.ogg -db[2] = 8. Track 8 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/8 - Track 8.ogg -db[3] = 7. Track 7 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/7 - Track 7.ogg -db[4] = 6. Track 6 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/6 - Track 6.ogg -db[5] = 5. Track 5 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/5 - Track 5.ogg -db[6] = 4. Track 4 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/4 - Track 4.ogg -db[7] = 3. Track 3 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/3 - Track 3.ogg -db[8] = 2. Track 2 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/2 - Track 2.ogg -db[9] = 1. Track 1 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/1 - Track 1.ogg -db[10] = 10. Track 10 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/10 - Track 10.ogg -db[11] = 9. Track 9 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/9 - Track 9.ogg -db[12] = 8. Track 8 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/8 - Track 8.ogg -db[13] = 7. Track 7 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/7 - Track 7.ogg -db[14] = 6. Track 6 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/6 - Track 6.ogg -db[15] = 5. Track 5 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/5 - Track 5.ogg -db[16] = 4. Track 4 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/4 - Track 4.ogg -db[17] = 3. Track 3 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/3 - Track 3.ogg -db[18] = 2. Track 2 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/2 - Track 2.ogg -db[19] = 1. Track 1 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/1 - Track 1.ogg -db[20] = 10. Track 10 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/10 - Track 10.ogg -db[21] = 9. Track 9 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/9 - Track 9.ogg -db[22] = 8. Track 8 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/8 - Track 8.ogg -db[23] = 7. Track 7 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/7 - Track 7.ogg -db[24] = 6. Track 6 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/6 - Track 6.ogg -db[25] = 5. Track 5 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/5 - Track 5.ogg -db[26] = 4. Track 4 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/4 - Track 4.ogg -db[27] = 3. Track 3 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/3 - Track 3.ogg -db[28] = 2. Track 2 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/2 - Track 2.ogg -db[29] = 1. Track 1 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/1 - Track 1.ogg -db[30] = 10. Track 10 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/10 - Track 10.ogg -db[31] = 9. Track 9 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/9 - Track 9.ogg -db[32] = 8. Track 8 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/8 - Track 8.ogg -db[33] = 7. Track 7 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/7 - Track 7.ogg -db[34] = 6. Track 6 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/6 - Track 6.ogg -db[35] = 5. Track 5 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/5 - Track 5.ogg -db[36] = 4. Track 4 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/4 - Track 4.ogg -db[37] = 3. Track 3 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/3 - Track 3.ogg -db[38] = 2. Track 2 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/2 - Track 2.ogg -db[39] = 1. Track 1 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/1 - Track 1.ogg -db[40] = 10. Track 10 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/10 - Track 10.ogg -db[41] = 9. Track 9 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/9 - Track 9.ogg -db[42] = 8. Track 8 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/8 - Track 8.ogg -db[43] = 7. Track 7 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/7 - Track 7.ogg -db[44] = 6. Track 6 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/6 - Track 6.ogg -db[45] = 5. Track 5 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/5 - Track 5.ogg -db[46] = 4. Track 4 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/4 - Track 4.ogg -db[47] = 3. Track 3 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/3 - Track 3.ogg -db[48] = 2. Track 2 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/2 - Track 2.ogg -db[49] = 1. Track 1 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/1 - Track 1.ogg -db[50] = 10. Track 10 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/10 - Track 10.ogg -db[51] = 9. Track 9 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/9 - Track 9.ogg -db[52] = 8. Track 8 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/8 - Track 8.ogg -db[53] = 7. Track 7 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/7 - Track 7.ogg -db[54] = 6. Track 6 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/6 - Track 6.ogg -db[55] = 5. Track 5 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/5 - Track 5.ogg -db[56] = 4. Track 4 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/4 - Track 4.ogg -db[57] = 3. Track 3 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/3 - Track 3.ogg -db[58] = 2. Track 2 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/2 - Track 2.ogg -db[59] = 1. Track 1 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/1 - Track 1.ogg -db[60] = 10. Track 10 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/10 - Track 10.ogg -db[61] = 9. Track 9 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/9 - Track 9.ogg -db[62] = 8. Track 8 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/8 - Track 8.ogg -db[63] = 7. Track 7 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/7 - Track 7.ogg -db[64] = 6. Track 6 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/6 - Track 6.ogg -db[65] = 5. Track 5 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/5 - Track 5.ogg -db[66] = 4. Track 4 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/4 - Track 4.ogg -db[67] = 3. Track 3 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/3 - Track 3.ogg -db[68] = 2. Track 2 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/2 - Track 2.ogg -db[69] = 1. Track 1 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/1 - Track 1.ogg -db[70] = 10. Track 10 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/10 - Track 10.ogg -db[71] = 9. Track 9 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/9 - Track 9.ogg -db[72] = 8. Track 8 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/8 - Track 8.ogg -db[73] = 7. Track 7 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/7 - Track 7.ogg -db[74] = 6. Track 6 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/6 - Track 6.ogg -db[75] = 5. Track 5 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/5 - Track 5.ogg -db[76] = 4. Track 4 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/4 - Track 4.ogg -db[77] = 3. Track 3 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/3 - Track 3.ogg -db[78] = 2. Track 2 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/2 - Track 2.ogg -db[79] = 1. Track 1 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/1 - Track 1.ogg -db[80] = 10. Track 10 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/10 - Track 10.ogg -db[81] = 9. Track 9 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/9 - Track 9.ogg -db[82] = 8. Track 8 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/8 - Track 8.ogg -db[83] = 7. Track 7 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/7 - Track 7.ogg -db[84] = 6. Track 6 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/6 - Track 6.ogg -db[85] = 5. Track 5 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/5 - Track 5.ogg -db[86] = 4. Track 4 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/4 - Track 4.ogg -db[87] = 3. Track 3 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/3 - Track 3.ogg -db[88] = 2. Track 2 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/2 - Track 2.ogg -db[89] = 1. Track 1 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/1 - Track 1.ogg -db[90] = 10. Track 10 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/10 - Track 10.ogg -db[91] = 9. Track 9 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/9 - Track 9.ogg -db[92] = 8. Track 8 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/8 - Track 8.ogg -db[93] = 7. Track 7 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/7 - Track 7.ogg -db[94] = 6. Track 6 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/6 - Track 6.ogg -db[95] = 5. Track 5 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/5 - Track 5.ogg -db[96] = 4. Track 4 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/4 - Track 4.ogg -db[97] = 3. Track 3 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/3 - Track 3.ogg -db[98] = 2. Track 2 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/2 - Track 2.ogg -db[99] = 1. Track 1 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/1 - Track 1.ogg -db[100] = 10. Track 10 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/10 - Track 10.ogg -db[101] = 9. Track 9 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/9 - Track 9.ogg -db[102] = 8. Track 8 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/8 - Track 8.ogg -db[103] = 7. Track 7 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/7 - Track 7.ogg -db[104] = 6. Track 6 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/6 - Track 6.ogg -db[105] = 5. Track 5 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/5 - Track 5.ogg -db[106] = 4. Track 4 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/4 - Track 4.ogg -db[107] = 3. Track 3 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/3 - Track 3.ogg -db[108] = 2. Track 2 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/2 - Track 2.ogg -db[109] = 1. Track 1 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/1 - Track 1.ogg -db[110] = 10. Track 10 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/10 - Track 10.ogg -db[111] = 9. Track 9 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/9 - Track 9.ogg -db[112] = 8. Track 8 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/8 - Track 8.ogg -db[113] = 7. Track 7 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/7 - Track 7.ogg -db[114] = 6. Track 6 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/6 - Track 6.ogg -db[115] = 5. Track 5 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/5 - Track 5.ogg -db[116] = 4. Track 4 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/4 - Track 4.ogg -db[117] = 3. Track 3 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/3 - Track 3.ogg -db[118] = 2. Track 2 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/2 - Track 2.ogg -db[119] = 1. Track 1 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/1 - Track 1.ogg -db[120] = 10. Track 10 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/10 - Track 10.ogg -db[121] = 9. Track 9 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/9 - Track 9.ogg -db[122] = 8. Track 8 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/8 - Track 8.ogg -db[123] = 7. Track 7 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/7 - Track 7.ogg -db[124] = 6. Track 6 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/6 - Track 6.ogg -db[125] = 5. Track 5 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/5 - Track 5.ogg -db[126] = 4. Track 4 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/4 - Track 4.ogg -db[127] = 3. Track 3 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/3 - Track 3.ogg -db[128] = 2. Track 2 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/2 - Track 2.ogg -db[129] = 1. Track 1 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/1 - Track 1.ogg -db[130] = 10. Track 10 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/10 - Track 10.ogg -db[131] = 9. Track 9 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/9 - Track 9.ogg -db[132] = 8. Track 8 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/8 - Track 8.ogg -db[133] = 7. Track 7 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/7 - Track 7.ogg -db[134] = 6. Track 6 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/6 - Track 6.ogg -db[135] = 5. Track 5 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/5 - Track 5.ogg -db[136] = 4. Track 4 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/4 - Track 4.ogg -db[137] = 3. Track 3 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/3 - Track 3.ogg -db[138] = 2. Track 2 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/2 - Track 2.ogg -db[139] = 1. Track 1 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/1 - Track 1.ogg -db[140] = 10. Track 10 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/10 - Track 10.ogg -db[141] = 9. Track 9 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/9 - Track 9.ogg -db[142] = 8. Track 8 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/8 - Track 8.ogg -db[143] = 7. Track 7 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/7 - Track 7.ogg -db[144] = 6. Track 6 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/6 - Track 6.ogg -db[145] = 5. Track 5 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/5 - Track 5.ogg -db[146] = 4. Track 4 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/4 - Track 4.ogg -db[147] = 3. Track 3 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/3 - Track 3.ogg -db[148] = 2. Track 2 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/2 - Track 2.ogg -db[149] = 1. Track 1 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/1 - Track 1.ogg - -Test 5a (track_id == 0): PASSED -Test 5b: Added library 0: /tmp/library/0 -PASSED -Updated library 0: /tmp/library/0 (size: 10) -Updated library 0: /tmp/library/0 (size: 20) -Updated library 0: /tmp/library/0 (size: 30) -Updated library 0: /tmp/library/0 (size: 40) -Updated library 0: /tmp/library/0 (size: 50) -Updated library 0: /tmp/library/0 (size: 60) -Updated library 0: /tmp/library/0 (size: 70) -Updated library 0: /tmp/library/0 (size: 80) -Updated library 0: /tmp/library/0 (size: 90) -Updated library 0: /tmp/library/0 (size: 100) -Updated library 0: /tmp/library/0 (size: 110) -Updated library 0: /tmp/library/0 (size: 120) -Updated library 0: /tmp/library/0 (size: 130) -Updated library 0: /tmp/library/0 (size: 140) -Updated library 0: /tmp/library/0 (size: 150) -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (enabled), size = 150 -Test 5c (track_id == 0): PASSED - 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 (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 5f (lib_id == 0): PASSED -/tmp/library/0 (enabled) size = 150 -Test 5g (lib_id == 1): PASSED - -Test 6a: Added library 0: /tmp/library/0 -PASSED -Updated library 0: /tmp/library/0 (size: 10) -Updated library 0: /tmp/library/0 (size: 20) -Updated library 0: /tmp/library/0 (size: 30) -Updated library 0: /tmp/library/0 (size: 40) -Updated library 0: /tmp/library/0 (size: 50) -Updated library 0: /tmp/library/0 (size: 60) -Updated library 0: /tmp/library/0 (size: 70) -Updated library 0: /tmp/library/0 (size: 80) -Updated library 0: /tmp/library/0 (size: 90) -Updated library 0: /tmp/library/0 (size: 100) -Updated library 0: /tmp/library/0 (size: 110) -Updated library 0: /tmp/library/0 (size: 120) -Updated library 0: /tmp/library/0 (size: 130) -Updated library 0: /tmp/library/0 (size: 140) -Updated library 0: /tmp/library/0 (size: 150) -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (enabled), size = 150 - -6b: Updating library 0 (nothing should change) -Allocated rows: 150 -Valid rows: 150 -db[0] = 10. Track 10 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/10 - Track 10.ogg -db[1] = 9. Track 9 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/9 - Track 9.ogg -db[2] = 8. Track 8 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/8 - Track 8.ogg -db[3] = 7. Track 7 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/7 - Track 7.ogg -db[4] = 6. Track 6 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/6 - Track 6.ogg -db[5] = 5. Track 5 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/5 - Track 5.ogg -db[6] = 4. Track 4 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/4 - Track 4.ogg -db[7] = 3. Track 3 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/3 - Track 3.ogg -db[8] = 2. Track 2 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/2 - Track 2.ogg -db[9] = 1. Track 1 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/1 - Track 1.ogg -db[10] = 10. Track 10 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/10 - Track 10.ogg -db[11] = 9. Track 9 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/9 - Track 9.ogg -db[12] = 8. Track 8 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/8 - Track 8.ogg -db[13] = 7. Track 7 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/7 - Track 7.ogg -db[14] = 6. Track 6 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/6 - Track 6.ogg -db[15] = 5. Track 5 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/5 - Track 5.ogg -db[16] = 4. Track 4 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/4 - Track 4.ogg -db[17] = 3. Track 3 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/3 - Track 3.ogg -db[18] = 2. Track 2 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/2 - Track 2.ogg -db[19] = 1. Track 1 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/1 - Track 1.ogg -db[20] = 10. Track 10 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/10 - Track 10.ogg -db[21] = 9. Track 9 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/9 - Track 9.ogg -db[22] = 8. Track 8 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/8 - Track 8.ogg -db[23] = 7. Track 7 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/7 - Track 7.ogg -db[24] = 6. Track 6 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/6 - Track 6.ogg -db[25] = 5. Track 5 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/5 - Track 5.ogg -db[26] = 4. Track 4 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/4 - Track 4.ogg -db[27] = 3. Track 3 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/3 - Track 3.ogg -db[28] = 2. Track 2 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/2 - Track 2.ogg -db[29] = 1. Track 1 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/1 - Track 1.ogg -db[30] = 10. Track 10 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/10 - Track 10.ogg -db[31] = 9. Track 9 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/9 - Track 9.ogg -db[32] = 8. Track 8 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/8 - Track 8.ogg -db[33] = 7. Track 7 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/7 - Track 7.ogg -db[34] = 6. Track 6 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/6 - Track 6.ogg -db[35] = 5. Track 5 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/5 - Track 5.ogg -db[36] = 4. Track 4 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/4 - Track 4.ogg -db[37] = 3. Track 3 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/3 - Track 3.ogg -db[38] = 2. Track 2 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/2 - Track 2.ogg -db[39] = 1. Track 1 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/1 - Track 1.ogg -db[40] = 10. Track 10 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/10 - Track 10.ogg -db[41] = 9. Track 9 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/9 - Track 9.ogg -db[42] = 8. Track 8 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/8 - Track 8.ogg -db[43] = 7. Track 7 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/7 - Track 7.ogg -db[44] = 6. Track 6 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/6 - Track 6.ogg -db[45] = 5. Track 5 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/5 - Track 5.ogg -db[46] = 4. Track 4 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/4 - Track 4.ogg -db[47] = 3. Track 3 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/3 - Track 3.ogg -db[48] = 2. Track 2 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/2 - Track 2.ogg -db[49] = 1. Track 1 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/1 - Track 1.ogg -db[50] = 10. Track 10 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/10 - Track 10.ogg -db[51] = 9. Track 9 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/9 - Track 9.ogg -db[52] = 8. Track 8 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/8 - Track 8.ogg -db[53] = 7. Track 7 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/7 - Track 7.ogg -db[54] = 6. Track 6 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/6 - Track 6.ogg -db[55] = 5. Track 5 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/5 - Track 5.ogg -db[56] = 4. Track 4 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/4 - Track 4.ogg -db[57] = 3. Track 3 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/3 - Track 3.ogg -db[58] = 2. Track 2 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/2 - Track 2.ogg -db[59] = 1. Track 1 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/1 - Track 1.ogg -db[60] = 10. Track 10 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/10 - Track 10.ogg -db[61] = 9. Track 9 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/9 - Track 9.ogg -db[62] = 8. Track 8 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/8 - Track 8.ogg -db[63] = 7. Track 7 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/7 - Track 7.ogg -db[64] = 6. Track 6 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/6 - Track 6.ogg -db[65] = 5. Track 5 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/5 - Track 5.ogg -db[66] = 4. Track 4 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/4 - Track 4.ogg -db[67] = 3. Track 3 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/3 - Track 3.ogg -db[68] = 2. Track 2 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/2 - Track 2.ogg -db[69] = 1. Track 1 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/1 - Track 1.ogg -db[70] = 10. Track 10 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/10 - Track 10.ogg -db[71] = 9. Track 9 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/9 - Track 9.ogg -db[72] = 8. Track 8 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/8 - Track 8.ogg -db[73] = 7. Track 7 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/7 - Track 7.ogg -db[74] = 6. Track 6 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/6 - Track 6.ogg -db[75] = 5. Track 5 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/5 - Track 5.ogg -db[76] = 4. Track 4 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/4 - Track 4.ogg -db[77] = 3. Track 3 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/3 - Track 3.ogg -db[78] = 2. Track 2 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/2 - Track 2.ogg -db[79] = 1. Track 1 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/1 - Track 1.ogg -db[80] = 10. Track 10 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/10 - Track 10.ogg -db[81] = 9. Track 9 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/9 - Track 9.ogg -db[82] = 8. Track 8 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/8 - Track 8.ogg -db[83] = 7. Track 7 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/7 - Track 7.ogg -db[84] = 6. Track 6 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/6 - Track 6.ogg -db[85] = 5. Track 5 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/5 - Track 5.ogg -db[86] = 4. Track 4 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/4 - Track 4.ogg -db[87] = 3. Track 3 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/3 - Track 3.ogg -db[88] = 2. Track 2 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/2 - Track 2.ogg -db[89] = 1. Track 1 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/1 - Track 1.ogg -db[90] = 10. Track 10 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/10 - Track 10.ogg -db[91] = 9. Track 9 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/9 - Track 9.ogg -db[92] = 8. Track 8 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/8 - Track 8.ogg -db[93] = 7. Track 7 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/7 - Track 7.ogg -db[94] = 6. Track 6 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/6 - Track 6.ogg -db[95] = 5. Track 5 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/5 - Track 5.ogg -db[96] = 4. Track 4 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/4 - Track 4.ogg -db[97] = 3. Track 3 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/3 - Track 3.ogg -db[98] = 2. Track 2 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/2 - Track 2.ogg -db[99] = 1. Track 1 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/1 - Track 1.ogg -db[100] = 10. Track 10 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/10 - Track 10.ogg -db[101] = 9. Track 9 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/9 - Track 9.ogg -db[102] = 8. Track 8 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/8 - Track 8.ogg -db[103] = 7. Track 7 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/7 - Track 7.ogg -db[104] = 6. Track 6 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/6 - Track 6.ogg -db[105] = 5. Track 5 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/5 - Track 5.ogg -db[106] = 4. Track 4 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/4 - Track 4.ogg -db[107] = 3. Track 3 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/3 - Track 3.ogg -db[108] = 2. Track 2 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/2 - Track 2.ogg -db[109] = 1. Track 1 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/1 - Track 1.ogg -db[110] = 10. Track 10 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/10 - Track 10.ogg -db[111] = 9. Track 9 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/9 - Track 9.ogg -db[112] = 8. Track 8 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/8 - Track 8.ogg -db[113] = 7. Track 7 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/7 - Track 7.ogg -db[114] = 6. Track 6 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/6 - Track 6.ogg -db[115] = 5. Track 5 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/5 - Track 5.ogg -db[116] = 4. Track 4 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/4 - Track 4.ogg -db[117] = 3. Track 3 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/3 - Track 3.ogg -db[118] = 2. Track 2 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/2 - Track 2.ogg -db[119] = 1. Track 1 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/1 - Track 1.ogg -db[120] = 10. Track 10 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/10 - Track 10.ogg -db[121] = 9. Track 9 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/9 - Track 9.ogg -db[122] = 8. Track 8 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/8 - Track 8.ogg -db[123] = 7. Track 7 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/7 - Track 7.ogg -db[124] = 6. Track 6 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/6 - Track 6.ogg -db[125] = 5. Track 5 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/5 - Track 5.ogg -db[126] = 4. Track 4 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/4 - Track 4.ogg -db[127] = 3. Track 3 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/3 - Track 3.ogg -db[128] = 2. Track 2 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/2 - Track 2.ogg -db[129] = 1. Track 1 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/1 - Track 1.ogg -db[130] = 10. Track 10 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/10 - Track 10.ogg -db[131] = 9. Track 9 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/9 - Track 9.ogg -db[132] = 8. Track 8 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/8 - Track 8.ogg -db[133] = 7. Track 7 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/7 - Track 7.ogg -db[134] = 6. Track 6 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/6 - Track 6.ogg -db[135] = 5. Track 5 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/5 - Track 5.ogg -db[136] = 4. Track 4 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/4 - Track 4.ogg -db[137] = 3. Track 3 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/3 - Track 3.ogg -db[138] = 2. Track 2 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/2 - Track 2.ogg -db[139] = 1. Track 1 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/1 - Track 1.ogg -db[140] = 10. Track 10 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/10 - Track 10.ogg -db[141] = 9. Track 9 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/9 - Track 9.ogg -db[142] = 8. Track 8 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/8 - Track 8.ogg -db[143] = 7. Track 7 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/7 - Track 7.ogg -db[144] = 6. Track 6 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/6 - Track 6.ogg -db[145] = 5. Track 5 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/5 - Track 5.ogg -db[146] = 4. Track 4 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/4 - Track 4.ogg -db[147] = 3. Track 3 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/3 - Track 3.ogg -db[148] = 2. Track 2 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/2 - Track 2.ogg -db[149] = 1. Track 1 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/1 - Track 1.ogg - -6c: Delete /tmp/library/0/Artist 2 -Removing file: /tmp/library/0/Artist 2/Album 2/10 - Track 10.ogg -Removing file: /tmp/library/0/Artist 2/Album 2/9 - Track 9.ogg -Removing file: /tmp/library/0/Artist 2/Album 2/8 - Track 8.ogg -Removing file: /tmp/library/0/Artist 2/Album 2/7 - Track 7.ogg -Removing file: /tmp/library/0/Artist 2/Album 2/6 - Track 6.ogg -Removing file: /tmp/library/0/Artist 2/Album 2/5 - Track 5.ogg -Removing file: /tmp/library/0/Artist 2/Album 2/4 - Track 4.ogg -Removing file: /tmp/library/0/Artist 2/Album 2/3 - Track 3.ogg -Removing file: /tmp/library/0/Artist 2/Album 2/2 - Track 2.ogg -Removing file: /tmp/library/0/Artist 2/Album 2/1 - Track 1.ogg -Removing file: /tmp/library/0/Artist 2/Album 1/10 - Track 10.ogg -Removing file: /tmp/library/0/Artist 2/Album 1/9 - Track 9.ogg -Removing file: /tmp/library/0/Artist 2/Album 1/8 - Track 8.ogg -Removing file: /tmp/library/0/Artist 2/Album 1/7 - Track 7.ogg -Removing file: /tmp/library/0/Artist 2/Album 1/6 - Track 6.ogg -Removing file: /tmp/library/0/Artist 2/Album 1/5 - Track 5.ogg -Removing file: /tmp/library/0/Artist 2/Album 1/4 - Track 4.ogg -Removing file: /tmp/library/0/Artist 2/Album 1/3 - Track 3.ogg -Removing file: /tmp/library/0/Artist 2/Album 1/2 - Track 2.ogg -Removing file: /tmp/library/0/Artist 2/Album 1/1 - Track 1.ogg -Removing file: /tmp/library/0/Artist 2/Album 0/10 - Track 10.ogg -Removing file: /tmp/library/0/Artist 2/Album 0/9 - Track 9.ogg -Removing file: /tmp/library/0/Artist 2/Album 0/8 - Track 8.ogg -Removing file: /tmp/library/0/Artist 2/Album 0/7 - Track 7.ogg -Removing file: /tmp/library/0/Artist 2/Album 0/6 - Track 6.ogg -Removing file: /tmp/library/0/Artist 2/Album 0/5 - Track 5.ogg -Removing file: /tmp/library/0/Artist 2/Album 0/4 - Track 4.ogg -Removing file: /tmp/library/0/Artist 2/Album 0/3 - Track 3.ogg -Removing file: /tmp/library/0/Artist 2/Album 0/2 - Track 2.ogg -Removing file: /tmp/library/0/Artist 2/Album 0/1 - Track 1.ogg -Updated library 0: /tmp/library/0 (size: 120) -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (enabled), size = 120 -Allocated rows: 150 -Valid rows: 120 -db[30] = 10. Track 10 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/10 - Track 10.ogg -db[31] = 9. Track 9 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/9 - Track 9.ogg -db[32] = 8. Track 8 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/8 - Track 8.ogg -db[33] = 7. Track 7 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/7 - Track 7.ogg -db[34] = 6. Track 6 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/6 - Track 6.ogg -db[35] = 5. Track 5 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/5 - Track 5.ogg -db[36] = 4. Track 4 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/4 - Track 4.ogg -db[37] = 3. Track 3 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/3 - Track 3.ogg -db[38] = 2. Track 2 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/2 - Track 2.ogg -db[39] = 1. Track 1 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/1 - Track 1.ogg -db[40] = 10. Track 10 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/10 - Track 10.ogg -db[41] = 9. Track 9 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/9 - Track 9.ogg -db[42] = 8. Track 8 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/8 - Track 8.ogg -db[43] = 7. Track 7 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/7 - Track 7.ogg -db[44] = 6. Track 6 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/6 - Track 6.ogg -db[45] = 5. Track 5 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/5 - Track 5.ogg -db[46] = 4. Track 4 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/4 - Track 4.ogg -db[47] = 3. Track 3 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/3 - Track 3.ogg -db[48] = 2. Track 2 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/2 - Track 2.ogg -db[49] = 1. Track 1 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/1 - Track 1.ogg -db[50] = 10. Track 10 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/10 - Track 10.ogg -db[51] = 9. Track 9 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/9 - Track 9.ogg -db[52] = 8. Track 8 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/8 - Track 8.ogg -db[53] = 7. Track 7 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/7 - Track 7.ogg -db[54] = 6. Track 6 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/6 - Track 6.ogg -db[55] = 5. Track 5 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/5 - Track 5.ogg -db[56] = 4. Track 4 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/4 - Track 4.ogg -db[57] = 3. Track 3 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/3 - Track 3.ogg -db[58] = 2. Track 2 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/2 - Track 2.ogg -db[59] = 1. Track 1 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/1 - Track 1.ogg -db[60] = 10. Track 10 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/10 - Track 10.ogg -db[61] = 9. Track 9 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/9 - Track 9.ogg -db[62] = 8. Track 8 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/8 - Track 8.ogg -db[63] = 7. Track 7 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/7 - Track 7.ogg -db[64] = 6. Track 6 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/6 - Track 6.ogg -db[65] = 5. Track 5 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/5 - Track 5.ogg -db[66] = 4. Track 4 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/4 - Track 4.ogg -db[67] = 3. Track 3 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/3 - Track 3.ogg -db[68] = 2. Track 2 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/2 - Track 2.ogg -db[69] = 1. Track 1 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/1 - Track 1.ogg -db[70] = 10. Track 10 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/10 - Track 10.ogg -db[71] = 9. Track 9 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/9 - Track 9.ogg -db[72] = 8. Track 8 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/8 - Track 8.ogg -db[73] = 7. Track 7 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/7 - Track 7.ogg -db[74] = 6. Track 6 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/6 - Track 6.ogg -db[75] = 5. Track 5 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/5 - Track 5.ogg -db[76] = 4. Track 4 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/4 - Track 4.ogg -db[77] = 3. Track 3 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/3 - Track 3.ogg -db[78] = 2. Track 2 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/2 - Track 2.ogg -db[79] = 1. Track 1 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/1 - Track 1.ogg -db[80] = 10. Track 10 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/10 - Track 10.ogg -db[81] = 9. Track 9 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/9 - Track 9.ogg -db[82] = 8. Track 8 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/8 - Track 8.ogg -db[83] = 7. Track 7 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/7 - Track 7.ogg -db[84] = 6. Track 6 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/6 - Track 6.ogg -db[85] = 5. Track 5 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/5 - Track 5.ogg -db[86] = 4. Track 4 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/4 - Track 4.ogg -db[87] = 3. Track 3 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/3 - Track 3.ogg -db[88] = 2. Track 2 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/2 - Track 2.ogg -db[89] = 1. Track 1 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/1 - Track 1.ogg -db[90] = 10. Track 10 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/10 - Track 10.ogg -db[91] = 9. Track 9 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/9 - Track 9.ogg -db[92] = 8. Track 8 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/8 - Track 8.ogg -db[93] = 7. Track 7 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/7 - Track 7.ogg -db[94] = 6. Track 6 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/6 - Track 6.ogg -db[95] = 5. Track 5 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/5 - Track 5.ogg -db[96] = 4. Track 4 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/4 - Track 4.ogg -db[97] = 3. Track 3 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/3 - Track 3.ogg -db[98] = 2. Track 2 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/2 - Track 2.ogg -db[99] = 1. Track 1 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/1 - Track 1.ogg -db[100] = 10. Track 10 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/10 - Track 10.ogg -db[101] = 9. Track 9 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/9 - Track 9.ogg -db[102] = 8. Track 8 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/8 - Track 8.ogg -db[103] = 7. Track 7 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/7 - Track 7.ogg -db[104] = 6. Track 6 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/6 - Track 6.ogg -db[105] = 5. Track 5 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/5 - Track 5.ogg -db[106] = 4. Track 4 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/4 - Track 4.ogg -db[107] = 3. Track 3 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/3 - Track 3.ogg -db[108] = 2. Track 2 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/2 - Track 2.ogg -db[109] = 1. Track 1 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/1 - Track 1.ogg -db[110] = 10. Track 10 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/10 - Track 10.ogg -db[111] = 9. Track 9 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/9 - Track 9.ogg -db[112] = 8. Track 8 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/8 - Track 8.ogg -db[113] = 7. Track 7 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/7 - Track 7.ogg -db[114] = 6. Track 6 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/6 - Track 6.ogg -db[115] = 5. Track 5 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/5 - Track 5.ogg -db[116] = 4. Track 4 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/4 - Track 4.ogg -db[117] = 3. Track 3 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/3 - Track 3.ogg -db[118] = 2. Track 2 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/2 - Track 2.ogg -db[119] = 1. Track 1 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/1 - Track 1.ogg -db[120] = 10. Track 10 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/10 - Track 10.ogg -db[121] = 9. Track 9 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/9 - Track 9.ogg -db[122] = 8. Track 8 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/8 - Track 8.ogg -db[123] = 7. Track 7 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/7 - Track 7.ogg -db[124] = 6. Track 6 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/6 - Track 6.ogg -db[125] = 5. Track 5 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/5 - Track 5.ogg -db[126] = 4. Track 4 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/4 - Track 4.ogg -db[127] = 3. Track 3 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/3 - Track 3.ogg -db[128] = 2. Track 2 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/2 - Track 2.ogg -db[129] = 1. Track 1 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/1 - Track 1.ogg -db[130] = 10. Track 10 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/10 - Track 10.ogg -db[131] = 9. Track 9 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/9 - Track 9.ogg -db[132] = 8. Track 8 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/8 - Track 8.ogg -db[133] = 7. Track 7 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/7 - Track 7.ogg -db[134] = 6. Track 6 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/6 - Track 6.ogg -db[135] = 5. Track 5 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/5 - Track 5.ogg -db[136] = 4. Track 4 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/4 - Track 4.ogg -db[137] = 3. Track 3 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/3 - Track 3.ogg -db[138] = 2. Track 2 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/2 - Track 2.ogg -db[139] = 1. Track 1 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/1 - Track 1.ogg -db[140] = 10. Track 10 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/10 - Track 10.ogg -db[141] = 9. Track 9 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/9 - Track 9.ogg -db[142] = 8. Track 8 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/8 - Track 8.ogg -db[143] = 7. Track 7 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/7 - Track 7.ogg -db[144] = 6. Track 6 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/6 - Track 6.ogg -db[145] = 5. Track 5 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/5 - Track 5.ogg -db[146] = 4. Track 4 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/4 - Track 4.ogg -db[147] = 3. Track 3 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/3 - Track 3.ogg -db[148] = 2. Track 2 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/2 - Track 2.ogg -db[149] = 1. Track 1 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/1 - Track 1.ogg - -6d: Regenerate Artist 2 -Generating library: 0 -Generating library: 1 -Generating library: 2 -Generating library: 3 -Generating library: 4 - -Updated library 0: /tmp/library/0 (size: 130) -Updated library 0: /tmp/library/0 (size: 140) -Updated library 0: /tmp/library/0 (size: 150) -Allocated rows: 180 -Valid rows: 150 -db[30] = 10. Track 10 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/10 - Track 10.ogg -db[31] = 9. Track 9 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/9 - Track 9.ogg -db[32] = 8. Track 8 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/8 - Track 8.ogg -db[33] = 7. Track 7 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/7 - Track 7.ogg -db[34] = 6. Track 6 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/6 - Track 6.ogg -db[35] = 5. Track 5 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/5 - Track 5.ogg -db[36] = 4. Track 4 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/4 - Track 4.ogg -db[37] = 3. Track 3 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/3 - Track 3.ogg -db[38] = 2. Track 2 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/2 - Track 2.ogg -db[39] = 1. Track 1 by Artist 4 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 2/1 - Track 1.ogg -db[40] = 10. Track 10 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/10 - Track 10.ogg -db[41] = 9. Track 9 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/9 - Track 9.ogg -db[42] = 8. Track 8 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/8 - Track 8.ogg -db[43] = 7. Track 7 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/7 - Track 7.ogg -db[44] = 6. Track 6 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/6 - Track 6.ogg -db[45] = 5. Track 5 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/5 - Track 5.ogg -db[46] = 4. Track 4 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/4 - Track 4.ogg -db[47] = 3. Track 3 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/3 - Track 3.ogg -db[48] = 2. Track 2 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/2 - Track 2.ogg -db[49] = 1. Track 1 by Artist 4 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 1/1 - Track 1.ogg -db[50] = 10. Track 10 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/10 - Track 10.ogg -db[51] = 9. Track 9 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/9 - Track 9.ogg -db[52] = 8. Track 8 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/8 - Track 8.ogg -db[53] = 7. Track 7 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/7 - Track 7.ogg -db[54] = 6. Track 6 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/6 - Track 6.ogg -db[55] = 5. Track 5 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/5 - Track 5.ogg -db[56] = 4. Track 4 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/4 - Track 4.ogg -db[57] = 3. Track 3 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/3 - Track 3.ogg -db[58] = 2. Track 2 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/2 - Track 2.ogg -db[59] = 1. Track 1 by Artist 4 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 4/Album 0/1 - Track 1.ogg -db[60] = 10. Track 10 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/10 - Track 10.ogg -db[61] = 9. Track 9 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/9 - Track 9.ogg -db[62] = 8. Track 8 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/8 - Track 8.ogg -db[63] = 7. Track 7 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/7 - Track 7.ogg -db[64] = 6. Track 6 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/6 - Track 6.ogg -db[65] = 5. Track 5 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/5 - Track 5.ogg -db[66] = 4. Track 4 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/4 - Track 4.ogg -db[67] = 3. Track 3 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/3 - Track 3.ogg -db[68] = 2. Track 2 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/2 - Track 2.ogg -db[69] = 1. Track 1 by Artist 3 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 2/1 - Track 1.ogg -db[70] = 10. Track 10 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/10 - Track 10.ogg -db[71] = 9. Track 9 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/9 - Track 9.ogg -db[72] = 8. Track 8 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/8 - Track 8.ogg -db[73] = 7. Track 7 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/7 - Track 7.ogg -db[74] = 6. Track 6 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/6 - Track 6.ogg -db[75] = 5. Track 5 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/5 - Track 5.ogg -db[76] = 4. Track 4 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/4 - Track 4.ogg -db[77] = 3. Track 3 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/3 - Track 3.ogg -db[78] = 2. Track 2 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/2 - Track 2.ogg -db[79] = 1. Track 1 by Artist 3 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 1/1 - Track 1.ogg -db[80] = 10. Track 10 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/10 - Track 10.ogg -db[81] = 9. Track 9 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/9 - Track 9.ogg -db[82] = 8. Track 8 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/8 - Track 8.ogg -db[83] = 7. Track 7 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/7 - Track 7.ogg -db[84] = 6. Track 6 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/6 - Track 6.ogg -db[85] = 5. Track 5 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/5 - Track 5.ogg -db[86] = 4. Track 4 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/4 - Track 4.ogg -db[87] = 3. Track 3 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/3 - Track 3.ogg -db[88] = 2. Track 2 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/2 - Track 2.ogg -db[89] = 1. Track 1 by Artist 3 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 3/Album 0/1 - Track 1.ogg -db[90] = 10. Track 10 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/10 - Track 10.ogg -db[91] = 9. Track 9 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/9 - Track 9.ogg -db[92] = 8. Track 8 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/8 - Track 8.ogg -db[93] = 7. Track 7 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/7 - Track 7.ogg -db[94] = 6. Track 6 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/6 - Track 6.ogg -db[95] = 5. Track 5 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/5 - Track 5.ogg -db[96] = 4. Track 4 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/4 - Track 4.ogg -db[97] = 3. Track 3 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/3 - Track 3.ogg -db[98] = 2. Track 2 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/2 - Track 2.ogg -db[99] = 1. Track 1 by Artist 1 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 2/1 - Track 1.ogg -db[100] = 10. Track 10 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/10 - Track 10.ogg -db[101] = 9. Track 9 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/9 - Track 9.ogg -db[102] = 8. Track 8 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/8 - Track 8.ogg -db[103] = 7. Track 7 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/7 - Track 7.ogg -db[104] = 6. Track 6 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/6 - Track 6.ogg -db[105] = 5. Track 5 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/5 - Track 5.ogg -db[106] = 4. Track 4 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/4 - Track 4.ogg -db[107] = 3. Track 3 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/3 - Track 3.ogg -db[108] = 2. Track 2 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/2 - Track 2.ogg -db[109] = 1. Track 1 by Artist 1 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 1/1 - Track 1.ogg -db[110] = 10. Track 10 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/10 - Track 10.ogg -db[111] = 9. Track 9 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/9 - Track 9.ogg -db[112] = 8. Track 8 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/8 - Track 8.ogg -db[113] = 7. Track 7 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/7 - Track 7.ogg -db[114] = 6. Track 6 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/6 - Track 6.ogg -db[115] = 5. Track 5 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/5 - Track 5.ogg -db[116] = 4. Track 4 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/4 - Track 4.ogg -db[117] = 3. Track 3 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/3 - Track 3.ogg -db[118] = 2. Track 2 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/2 - Track 2.ogg -db[119] = 1. Track 1 by Artist 1 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 1/Album 0/1 - Track 1.ogg -db[120] = 10. Track 10 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/10 - Track 10.ogg -db[121] = 9. Track 9 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/9 - Track 9.ogg -db[122] = 8. Track 8 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/8 - Track 8.ogg -db[123] = 7. Track 7 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/7 - Track 7.ogg -db[124] = 6. Track 6 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/6 - Track 6.ogg -db[125] = 5. Track 5 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/5 - Track 5.ogg -db[126] = 4. Track 4 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/4 - Track 4.ogg -db[127] = 3. Track 3 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/3 - Track 3.ogg -db[128] = 2. Track 2 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/2 - Track 2.ogg -db[129] = 1. Track 1 by Artist 0 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 2/1 - Track 1.ogg -db[130] = 10. Track 10 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/10 - Track 10.ogg -db[131] = 9. Track 9 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/9 - Track 9.ogg -db[132] = 8. Track 8 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/8 - Track 8.ogg -db[133] = 7. Track 7 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/7 - Track 7.ogg -db[134] = 6. Track 6 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/6 - Track 6.ogg -db[135] = 5. Track 5 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/5 - Track 5.ogg -db[136] = 4. Track 4 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/4 - Track 4.ogg -db[137] = 3. Track 3 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/3 - Track 3.ogg -db[138] = 2. Track 2 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/2 - Track 2.ogg -db[139] = 1. Track 1 by Artist 0 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 1/1 - Track 1.ogg -db[140] = 10. Track 10 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/10 - Track 10.ogg -db[141] = 9. Track 9 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/9 - Track 9.ogg -db[142] = 8. Track 8 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/8 - Track 8.ogg -db[143] = 7. Track 7 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/7 - Track 7.ogg -db[144] = 6. Track 6 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/6 - Track 6.ogg -db[145] = 5. Track 5 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/5 - Track 5.ogg -db[146] = 4. Track 4 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/4 - Track 4.ogg -db[147] = 3. Track 3 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/3 - Track 3.ogg -db[148] = 2. Track 2 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/2 - Track 2.ogg -db[149] = 1. Track 1 by Artist 0 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 0/Album 0/1 - Track 1.ogg -db[150] = 10. Track 10 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/10 - Track 10.ogg -db[151] = 9. Track 9 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/9 - Track 9.ogg -db[152] = 8. Track 8 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/8 - Track 8.ogg -db[153] = 7. Track 7 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/7 - Track 7.ogg -db[154] = 6. Track 6 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/6 - Track 6.ogg -db[155] = 5. Track 5 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/5 - Track 5.ogg -db[156] = 4. Track 4 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/4 - Track 4.ogg -db[157] = 3. Track 3 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/3 - Track 3.ogg -db[158] = 2. Track 2 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/2 - Track 2.ogg -db[159] = 1. Track 1 by Artist 2 from Album 2 (2013) - Genre: Tryout, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 2/1 - Track 1.ogg -db[160] = 10. Track 10 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/10 - Track 10.ogg -db[161] = 9. Track 9 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/9 - Track 9.ogg -db[162] = 8. Track 8 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/8 - Track 8.ogg -db[163] = 7. Track 7 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/7 - Track 7.ogg -db[164] = 6. Track 6 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/6 - Track 6.ogg -db[165] = 5. Track 5 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/5 - Track 5.ogg -db[166] = 4. Track 4 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/4 - Track 4.ogg -db[167] = 3. Track 3 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/3 - Track 3.ogg -db[168] = 2. Track 2 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/2 - Track 2.ogg -db[169] = 1. Track 1 by Artist 2 from Album 1 (2012) - Genre: Trial, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 1/1 - Track 1.ogg -db[170] = 10. Track 10 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/10 - Track 10.ogg -db[171] = 9. Track 9 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/9 - Track 9.ogg -db[172] = 8. Track 8 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/8 - Track 8.ogg -db[173] = 7. Track 7 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/7 - Track 7.ogg -db[174] = 6. Track 6 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/6 - Track 6.ogg -db[175] = 5. Track 5 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/5 - Track 5.ogg -db[176] = 4. Track 4 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 1 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/4 - Track 4.ogg -db[177] = 3. Track 3 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 600 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/3 - Track 3.ogg -db[178] = 2. Track 2 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 60 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/2 - Track 2.ogg -db[179] = 1. Track 1 by Artist 2 from Album 0 (2011) - Genre: Test, Length: 10 (seconds) - Play count: 0, last played 0/0/0 - Artist 2/Album 0/1 - Track 1.ogg - -Test 7a: Added library 0: /tmp/library/2 -PASSED -Updated library 0: /tmp/library/2 (size: 10) -Updated library 0: /tmp/library/2 (size: 20) -Updated library 0: /tmp/library/2 (size: 30) -Updated library 0: /tmp/library/2 (size: 40) -Updated library 0: /tmp/library/2 (size: 50) -Updated library 0: /tmp/library/2 (size: 60) -Updated library 0: /tmp/library/2 (size: 70) -Updated library 0: /tmp/library/2 (size: 80) -Updated library 0: /tmp/library/2 (size: 90) -Updated library 0: /tmp/library/2 (size: 100) -Updated library 0: /tmp/library/2 (size: 110) -Updated library 0: /tmp/library/2 (size: 120) -Updated library 0: /tmp/library/2 (size: 130) -Updated library 0: /tmp/library/2 (size: 140) -Updated library 0: /tmp/library/2 (size: 150) -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/2 (enabled), size = 150 - -Importing: /home/anna/.ocarina-test/library/0 -Version mismatch: 1 != 2 -Importing: /home/anna/.ocarina-test/library/1 -Library already contains path: /tmp/library/2, skipping -Importing: /home/anna/.ocarina-test/library/2 -Adding path: /tmp/library/3 -Added library 1: /tmp/library/3 -Updated library 1: /tmp/library/3 (size: 150) -Importing: /home/anna/.ocarina-test/library/3 -Adding path: /tmp/library/4 -Added library 2: /tmp/library/4 -Updated library 2: /tmp/library/4 (size: 150) -Allocated rows: 3 -Valid rows: 3 -db[0] = /tmp/library/2 (enabled), size = 150 -db[1] = /tmp/library/3 (enabled), size = 150 -db[2] = /tmp/library/4 (enabled), size = 150 - -Test 8a: Added library 0: /tmp/library/0 -PASSED -Updated library 0: /tmp/library/0 (size: 10) -Updated library 0: /tmp/library/0 (size: 20) -Updated library 0: /tmp/library/0 (size: 30) -Updated library 0: /tmp/library/0 (size: 40) -Updated library 0: /tmp/library/0 (size: 50) -Updated library 0: /tmp/library/0 (size: 60) -Updated library 0: /tmp/library/0 (size: 70) -Updated library 0: /tmp/library/0 (size: 80) -Updated library 0: /tmp/library/0 (size: 90) -Updated library 0: /tmp/library/0 (size: 100) -Updated library 0: /tmp/library/0 (size: 110) -Updated library 0: /tmp/library/0 (size: 120) -Updated library 0: /tmp/library/0 (size: 130) -Updated library 0: /tmp/library/0 (size: 140) -Updated library 0: /tmp/library/0 (size: 150) -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (enabled), size = 150 - -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (disabled), size = 150 -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (enabled), size = 150 -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (disabled), size = 150 -Allocated rows: 1 -Valid rows: 1 -db[0] = /tmp/library/0 (enabled), size = 150 diff --git a/tests/playlist.cpp b/tests/playlist.cpp new file mode 100644 index 00000000..7c141071 --- /dev/null +++ b/tests/playlist.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include +#include "test.h" + +static IndexEntry *IDX_NULL = NULL; +static Queue *Q_NULL = NULL; + +static void test_init() +{ + IndexEntry *ent; + Queue *q = playlist :: get_queue(); + + test_not_equal(q, Q_NULL); + test_equal(q->has_flag(Q_ENABLED), true); + test_equal(q->has_flag(Q_REPEAT), true); + test_equal(q->has_flag(Q_NO_SORT), true); + + /* init should work even if playlist.db doesn't exist! */ + playlist :: init(); + + test :: cp_data_dir(); + tagdb :: init(); + library :: init(); + playlist :: init(); + + ent = playlist :: get_tracks("Banned"); + test_equal(ent->values.size(), (size_t)4); + test_equal(library :: get_queue()->size(), (unsigned)20); + ent = playlist :: get_tracks("Favorites"); + test_equal(ent->values.size(), (size_t)8); + ent = playlist :: get_tracks("No Such Playlist"); + test_equal(ent, IDX_NULL); +} + +static void test_queue() +{ + Queue *q = playlist :: get_queue(); + + playlist :: select("Banned"); + test_equal(q->size(), (unsigned)4); + + playlist :: select("Favorites"); + test_equal(q->size(), (unsigned)8); +} + +static void test_add() +{ + IndexEntry *ent; + Queue *q = playlist :: get_queue(); + Queue *l = library :: get_queue(); + + playlist :: add(tagdb :: lookup(5), "Banned"); + ent = playlist :: get_tracks("Banned"); + test_equal(ent->values.size(), (size_t)5); + test_equal(q->size(), (unsigned)8); + test_equal(l->size(), (unsigned)19); + + playlist :: add(tagdb :: lookup(16), "Favorites"); + playlist :: add(tagdb :: lookup(5), "Favorites"); + ent = playlist :: get_tracks("Favorites"); + test_equal(ent->values.size(), (size_t)9); + test_equal(q->size(), (unsigned)9); + + playlist :: add(tagdb :: lookup(6), "No Playlist"); + test_equal(playlist :: get_tracks("No Playlist"), IDX_NULL); +} + +static void test_delete() +{ + IndexEntry *ent; + Queue *q = playlist :: get_queue(); + Queue *l = library :: get_queue(); + + playlist :: del(tagdb :: lookup(5), "Banned"); + ent = playlist :: get_tracks("Banned"); + test_equal(ent->values.size(), (size_t)4); + test_equal(q->size(), (unsigned)9); + test_equal(l->size(), (unsigned)20); + + playlist :: del(tagdb :: lookup(5), "Favorites"); + ent = playlist :: get_tracks("Favorites"); + test_equal(ent->values.size(), (size_t)8); + test_equal(q->size(), (unsigned)8); + + playlist :: del(tagdb :: lookup(6), "No Playlist"); + test_equal(playlist :: get_tracks("No Playlist"), IDX_NULL); +} + +static void test_has() +{ + test :: begin(); + for (unsigned int i = 0; i < 24; i++) { + Track *track = tagdb :: lookup(i); + if (i <= 3) + check_equal(playlist :: has(track, "Banned"), true); + else + check_equal(playlist :: has(track, "Banned"), false); + } + test :: success(); + + test :: begin(); + for (unsigned int i = 0; i < 24; i++) { + Track *track = tagdb :: lookup(i); + if (i >= 16) + check_equal(playlist :: has(track, "Favorites"), true); + else + check_equal(playlist :: has(track, "Favorites"), false); + } + test :: success(); +} + +int main(int argc, char **argv) +{ + test :: rm_data_dir(); + + run_test("Playlist Initialization Test", test_init); + run_test("Playlist Queue Test", test_queue); + run_test("Playlist Add Test", test_add); + run_test("Playlist Delete Test", test_delete); + run_test("Playlist Has Test", test_has); + return 0; +} diff --git a/tests/playlist/Sconscript b/tests/playlist/Sconscript deleted file mode 100644 index 4c0ff2c3..00000000 --- a/tests/playlist/Sconscript +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/python -Import("Test", "CONFIG") - -CONFIG.PLAYLIST = True - -Test("playlist", "playlist.cpp") diff --git a/tests/playlist/playlist.cpp b/tests/playlist/playlist.cpp deleted file mode 100644 index 47ad6f87..00000000 --- a/tests/playlist/playlist.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include -#include - - -void list_tracks(const std::string &name) -{ - std::set tracks = playlist :: get_tracks(name); - std::set::iterator it; - - print("Playlist \"%s\": ", name.c_str()); - for (it = tracks.begin(); it != tracks.end(); it++) { - if (it != tracks.begin()) - print(", "); - print("%u", *it); - } - - print("\n"); -} - -void check_error(int expected, int error) -{ - if (expected != error) - print("Exception error: expected %d actual %d", expected, error); -} - -/* - * Add songs to different playlists - */ -void test_0() -{ - for (unsigned int i = 0; i < 128; i++) { - switch (i % 3) { - case 0: - playlist :: add("Banned", i); - break; - case 1: - playlist :: add("Favorites", i); - break; - default: - try { - playlist :: add("No Such Playlist", i); - } catch (int error) { - check_error(-E_EXIST, error); - } - } - } -} - -/* - * Find tracks in a playlist - */ -void test_1() -{ - list_tracks("Banned"); - list_tracks("Favorites"); - - try { - list_tracks("No Such Playlist"); - } catch (int error) { - check_error(-E_EXIST, error); - } -} - -/* - * Delete tracks from a playlist - */ -void test_2() -{ - print("\n"); - for (unsigned int i = 0; i < 30; i+=3) - playlist :: del("Banned", i); - list_tracks("Banned"); - - try { - playlist :: del("No Such Playlist", 2); - } catch (int error) { - check_error(-E_EXIST, error); - } -} - -/* - * Check persistence of playlists - */ -void test_3() -{ - print("\n"); - - playlist :: clear(); - playlist :: init(); - - list_tracks("Banned"); - list_tracks("Favorites"); - - try { - list_tracks("No Schu Playlist"); - } catch (int error) { - check_error(-E_EXIST, error); - } -} - -int main(int argc, char **argv) -{ - test_0(); - test_1(); - test_2(); - test_3(); - return 0; -} diff --git a/tests/playlist/playlist.good b/tests/playlist/playlist.good deleted file mode 100644 index 96e86e10..00000000 --- a/tests/playlist/playlist.good +++ /dev/null @@ -1,7 +0,0 @@ -Playlist "Banned": 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126 -Playlist "Favorites": 1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, 127 - -Playlist "Banned": 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126 - -Playlist "Banned": 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126 -Playlist "Favorites": 1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, 127 diff --git a/tests/playqueue/Sconscript b/tests/playqueue/Sconscript deleted file mode 100644 index f179ea30..00000000 --- a/tests/playqueue/Sconscript +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/python -Import("Test", "CONFIG") - -CONFIG.PLAYQUEUE = True - -Test("playqueue", "playqueue.cpp") diff --git a/tests/playqueue/playqueue.cpp b/tests/playqueue/playqueue.cpp deleted file mode 100644 index 4ec18e3b..00000000 --- a/tests/playqueue/playqueue.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include -#include - -#include -#include - -void test_flags(const std :: string &test, Playqueue &pqueue, unsigned int expected) -{ - print("Test %s: ", test.c_str()); - if (pqueue.get_flags() == expected) - print("SUCCESS\n"); - else - print("FAILED\n"); -} - -void test_add_tracks(const std :: string & test, Playqueue &pqueue, - unsigned int start, unsigned int end) -{ - bool passed = true; - print("Test %s: ", test.c_str()); - for (unsigned int i = start; i <= end; i++) { - if (pqueue.add(i) != (i - start)) - passed = false; - } - - if (passed == true) - print("SUCCESS\n"); - else - print("FAILED\n"); -} - -void test_add_tracks_front(const std :: string &test, Playqueue &pqueue, - unsigned int n) -{ - bool passed = true; - - print("Test %s: ", test.c_str()); - for (unsigned int i = 0; i < n; i++) { - if (pqueue.add_front(i) != 0) - passed = false; - } - - if (passed == true) - print("SUCCESS\n"); - else - print("FAILED\n"); -} - -void test_rm_tracks(const std :: string & test, Playqueue & pqueue, unsigned int n) -{ - unsigned int size = pqueue.size(); - unsigned int expected = size - n; - - print("Test %s: ", test.c_str()); - - for (unsigned int i = 0; i < n; i++) - pqueue.del(i); - - if (pqueue.size() == expected) - print("SUCCESS\n"); - else - print("FAILED\n"); -} - -void test_pqueue_status(const std :: string &test, Playqueue &pqueue) -{ - print("Test %s: size: %u, length: %u\n", test.c_str(), - pqueue.size(), pqueue.get_length()); -} - -/* Test flag setting / unsetting / getting */ -void test_0() -{ - Playqueue pqueue(PQ_ENABLED); - - test_flags("0a", pqueue, PQ_ENABLED); - pqueue.set_flag(PQ_RANDOM); - test_flags("0b", pqueue, PQ_ENABLED | PQ_RANDOM); - pqueue.set_flag(PQ_ENABLED); - test_flags("0c", pqueue, PQ_ENABLED | PQ_RANDOM); - pqueue.unset_flag(PQ_ENABLED); - test_flags("0d", pqueue, PQ_RANDOM); - pqueue.unset_flag(PQ_RANDOM); - test_flags("0f", pqueue, 0); - print("\n"); -} - -/* Test adding / deleting / size queries */ -void test_1() -{ - Playqueue pqueue(PQ_ENABLED); - test_add_tracks("1a", pqueue, 10, 49); - test_pqueue_status("1b", pqueue); - test_rm_tracks("1c", pqueue, 10); - test_pqueue_status("1d", pqueue); - test_add_tracks_front("1e", pqueue, 5); - test_pqueue_status("1f", pqueue); - print("\n"); -} - -/* Test read / write */ -void test_2() -{ - Playqueue pqueue(PQ_ENABLED); - Playqueue pqueue2(PQ_ENABLED); - - File f("pqueue.lst", FILE_TYPE_DATA); - - pqueue.set_flag(PQ_RANDOM); - test_add_tracks("2a", pqueue, 0, 99); - - f.open(OPEN_WRITE); - pqueue.write(f); - f.close(); - - f.open(OPEN_READ); - pqueue2.read(f); - f.close(); - - print("Test 2b: "); - if (pqueue.get_flags() != pqueue2.get_flags()) { - print("FAILED: flag mismatch\n"); - return; - } else if (pqueue.size() != pqueue2.size()) { - print("FAILED: size mismatch\n"); - return; - } - - print("SUCCESS\n\n"); -} - -/* Sequential next() without removing tracks */ -void test_3() -{ - Playqueue pqueue(PQ_ENABLED); - pqueue.set_flag(PQ_REPEAT); - test_add_tracks("3a", pqueue, 0, 15); - for (unsigned int i = 0; i < 25; i++) { - print("Selecting id: %u\n", pqueue.next()); - if (i == 4) - pqueue.set_cur(0); - } - test_pqueue_status("3b", pqueue); - print("\n"); -} - -/* Sequential next() with removal */ -void test_4() -{ - Playqueue pqueue(PQ_ENABLED); - test_add_tracks("4a", pqueue, 0, 15); - while (pqueue.size() > 0) - print("Selecting id: %u\n", pqueue.next()); - test_pqueue_status("4b", pqueue); - print("\n"); -} - -/* Random next() without removing tracks */ -void test_5() -{ - Playqueue pqueue(PQ_ENABLED); - pqueue.set_flag(PQ_RANDOM); - pqueue.set_flag(PQ_REPEAT); - test_add_tracks("5a", pqueue, 0, 15); - for (unsigned int i = 0; i < 30; i++) - print("Selecting id: %u\n", pqueue.next()); - test_pqueue_status("5b", pqueue); - print("\n"); -} - -/* Random next() with removal */ -void test_6() -{ - Playqueue pqueue(PQ_ENABLED); - pqueue.set_flag(PQ_RANDOM); - test_add_tracks("6a", pqueue, 0, 15); - while (pqueue.size() > 0) - print("Selecting id: %u\n", pqueue.next()); - test_pqueue_status("6b", pqueue); - print("\n"); -} - -void test_7() -{ - char test[] = "7a"; - Playqueue pqueue(PQ_ENABLED); - - for (unsigned int i = 0; i < 60; i++) - pqueue.add(i % 15); - test_pqueue_status(test, pqueue); - - for (unsigned int i = 0; i < 15; i++) { - pqueue.del_track(i); - if ((i + 1) % 3 == 0) { - test[1]++; - test_pqueue_status(test, pqueue); - } - } -} - -int main(int argc, char **argv) -{ - srand(42); - library :: init(); - library :: reset(); - library :: add_path("/tmp/library/0"); - while (idle :: run_task()); - - test_0(); - test_1(); - test_2(); - test_3(); - test_4(); - test_5(); - test_6(); - test_7(); - return 0; -} diff --git a/tests/playqueue/playqueue.good b/tests/playqueue/playqueue.good deleted file mode 100644 index 2bd5a7dc..00000000 --- a/tests/playqueue/playqueue.good +++ /dev/null @@ -1,121 +0,0 @@ -Test 0a: SUCCESS -Test 0b: SUCCESS -Test 0c: SUCCESS -Test 0d: SUCCESS -Test 0f: SUCCESS - -Test 1a: SUCCESS -Test 1b: size: 40, length: 5648 -Test 1c: SUCCESS -Test 1d: size: 30, length: 5284 -Test 1e: SUCCESS -Test 1f: size: 35, length: 6015 - -Test 2a: SUCCESS -Test 2b: SUCCESS - -Test 3a: SUCCESS -Selecting id: 0 -Selecting id: 1 -Selecting id: 2 -Selecting id: 3 -Selecting id: 4 -Selecting id: 1 -Selecting id: 2 -Selecting id: 3 -Selecting id: 4 -Selecting id: 5 -Selecting id: 6 -Selecting id: 7 -Selecting id: 8 -Selecting id: 9 -Selecting id: 10 -Selecting id: 11 -Selecting id: 12 -Selecting id: 13 -Selecting id: 14 -Selecting id: 15 -Selecting id: 0 -Selecting id: 1 -Selecting id: 2 -Selecting id: 3 -Selecting id: 4 -Test 3b: size: 16, length: 2153 - -Test 4a: SUCCESS -Selecting id: 0 -Selecting id: 1 -Selecting id: 2 -Selecting id: 3 -Selecting id: 4 -Selecting id: 5 -Selecting id: 6 -Selecting id: 7 -Selecting id: 8 -Selecting id: 9 -Selecting id: 10 -Selecting id: 11 -Selecting id: 12 -Selecting id: 13 -Selecting id: 14 -Selecting id: 15 -Test 4b: size: 0, length: 0 - -Test 5a: SUCCESS -Selecting id: 6 -Selecting id: 11 -Selecting id: 13 -Selecting id: 15 -Selecting id: 4 -Selecting id: 11 -Selecting id: 1 -Selecting id: 6 -Selecting id: 14 -Selecting id: 6 -Selecting id: 9 -Selecting id: 1 -Selecting id: 7 -Selecting id: 8 -Selecting id: 9 -Selecting id: 12 -Selecting id: 1 -Selecting id: 2 -Selecting id: 5 -Selecting id: 9 -Selecting id: 13 -Selecting id: 14 -Selecting id: 0 -Selecting id: 2 -Selecting id: 10 -Selecting id: 14 -Selecting id: 0 -Selecting id: 1 -Selecting id: 7 -Selecting id: 8 -Test 5b: size: 16, length: 2153 - -Test 6a: SUCCESS -Selecting id: 1 -Selecting id: 2 -Selecting id: 4 -Selecting id: 9 -Selecting id: 10 -Selecting id: 11 -Selecting id: 0 -Selecting id: 5 -Selecting id: 7 -Selecting id: 8 -Selecting id: 13 -Selecting id: 15 -Selecting id: 6 -Selecting id: 12 -Selecting id: 14 -Selecting id: 3 -Test 6b: size: 0, length: 0 - -Test 7a: size: 60, length: 8572 -Test 7b: size: 48, length: 8288 -Test 7c: size: 36, length: 5608 -Test 7d: size: 24, length: 2964 -Test 7e: size: 12, length: 2644 -Test 7f: size: 0, length: 0 diff --git a/tests/queue.cpp b/tests/queue.cpp new file mode 100644 index 00000000..7bcfe8ca --- /dev/null +++ b/tests/queue.cpp @@ -0,0 +1,389 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + */ +#include +#include +#include +#include +#include "test.h" + + +unsigned int count_add = 0; +unsigned int count_del = 0; +unsigned int count_updated = 0; +unsigned int last_update = 0; +Track *TRACK_NULL = NULL; + + +class TestQueue : public Queue +{ +public: + TestQueue() : Queue() {} + TestQueue(unsigned int f) : Queue(f) {} + unsigned int get_cur() { return _cur; } + unsigned int get_flags() { return _flags; } + unsigned int get_length() { return _length; } + std::vector get_sorder() { return _sort_order; }; +}; + +void test_add_cb_noop(Queue *q, unsigned int id) { } +void test_del_cb_noop(Queue *q, unsigned int id) { } + + +void test_default() +{ + TestQueue q; + + test_equal(q.get_cur(), (unsigned)-1); + test_equal(q.get_flags(), (unsigned)0); + test_equal(q.get_length(), (unsigned)0); + test_equal(q.get_sorder().size(), (size_t)0); + test_equal(q.next(), (Track *)NULL); +} + +void test_constructor(unsigned int flags) +{ + TestQueue q(flags | (1 << 30)); + + test_equal(q.get_cur(), (unsigned)-1); + test_equal(q.get_flags(), flags); + test_equal(q.get_length(), (unsigned)0); + test_equal(q.get_sorder().size(), (size_t)0); + test_equal(q.next(), (Track *)NULL); +} + +void test_flags() +{ + TestQueue q(0); + + test_equal(q.get_flags(), (unsigned)0); + + q.set_flag(Q_ENABLED); + test_equal(q.get_flags(), (unsigned)Q_ENABLED); + + q.unset_flag(Q_ENABLED); + test_equal(q.get_flags(), (unsigned)0); + + q.set_flag(Q_REPEAT); + q.set_flag(Q_RANDOM); + test_equal(q.has_flag(Q_ENABLED), false); + test_equal(q.has_flag(Q_RANDOM), true); + test_equal(q.has_flag(Q_REPEAT), true); + test_equal(q.has_flag(Q_NO_SORT), false); +} + +void test_add_cb(Queue *q, unsigned int id) +{ + check_equal(id, count_add); + count_add++; +} + +void test_del_cb(Queue *q, unsigned int id) +{ + if (count_del % 2 == 0) + check_equal(id, (unsigned)0); + else + check_equal(id, 24 - ((count_del + 1) / 2)); + count_del++; +} + +void test_del_cb2(Queue *q, unsigned int id) +{ + check_equal(id, 23 - count_del); + count_del++; +} + +void test_del_cb3(Queue *q, unsigned int id) +{ + check_equal(id, (unsigned)0); +} + +void test_add_remove() +{ + Track *track; + TestQueue q(0); + unsigned int expected = 0; + + get_callbacks()->on_queue_track_add = test_add_cb; + get_callbacks()->on_queue_track_del = test_del_cb; + + test_equal(q.get_length(), expected); + test_equal(q.length_str(), (std::string)""); + test_equal(q.size(), (unsigned)0); + test_equal(q.size_str(), (std::string)"0"); + + + /* Add tracks */ + test :: begin(); + for (unsigned int i = 0; i < 24; i++) { + check_equal(q.add(tagdb :: lookup(i)), i); + expected += 100 + (i * 10); + } + test :: success(); + test_equal(q.get_length(), expected); + test_equal(q.length_str(), (std::string)"1 hour, 26 minutes"); + test_equal(q.size(), (unsigned)24); + test_equal(q.size_str(), (std::string)"24"); + + + /* Add everything again */ + test :: begin(); + for (unsigned int i = 0; i < 24; i++) { + check_equal(q.add(tagdb :: lookup(i)), i + 24); + expected += 100 + (i * 10); + } + test :: success(); + test_equal(q.get_length(), expected); + test_equal(q.length_str(), (std::string)"2 hours, 52 minutes"); + test_equal(q.size(), (unsigned)48); + test_equal(q.size_str(), (std::string)"48"); + + + /* Test removing multiple tracks at once */ + test :: begin(); + for (unsigned int i = 0; i < 12; i++) { + track = tagdb :: lookup(i); + q.del(track); + expected -= 2 * (100 + (i * 10)); + } + test :: success(); + test_equal(q.get_length(), expected); + test_equal(q.length_str(), (std::string)"1 hour, 50 minutes"); + test_equal(q.size(), (unsigned)24); + test_equal(q.size_str(), (std::string)"24"); + + + /* Test removing tracks one at a time */ + count_del = 0; + get_callbacks()->on_queue_track_del = test_del_cb2; + + test :: begin(); + for (unsigned int i = 23; i >= 12; i--) { + expected -= q[i]->length; + q.del(i); + } + test :: success(); + test_equal(q.get_length(), expected); + test_equal(q.length_str(), (std::string)"55 minutes"); + test_equal(q.size(), (unsigned)12); + test_equal(q.size_str(), (std::string)"12"); + + + /* Remove remaining tracks */ + get_callbacks()->on_queue_track_del = test_del_cb3; + + test :: begin(); + while (q.size() > 0) + q.del((unsigned)0); + test :: success(); + test_equal(q.get_length(), (unsigned)0); + test_equal(q.length_str(), (std::string)""); + test_equal(q.size(), (unsigned)0); + test_equal(q.size_str(), (std::string)"0"); +} + +void test_updated_cb(Queue *q, unsigned int row) +{ + last_update = row; +} + +void test_updated_cb2(Queue *q, unsigned int row) +{ + switch (row) { + case 0: + case 24: + case 25: + count_updated++; + default: + break; + } +} + +void test_updated() +{ + Track *track; + TestQueue q(0); + + get_callbacks()->on_queue_track_add = test_add_cb_noop; + get_callbacks()->on_queue_track_changed = test_updated_cb; + + /* Add tracks */ + test :: begin(); + for (unsigned int i = 0; i < 24; i++) + check_equal(q.add(tagdb :: lookup(i)), i); + test :: success(); + + test :: begin(); + for (unsigned int i = 0; i < 24; i++) + q.updated(tagdb :: lookup(i)); + test :: success(); + + get_callbacks()->on_queue_track_changed = test_updated_cb2; + track = tagdb :: lookup(0); + q.add(track); + q.add(track); + q.updated(track); + test_equal(count_updated, (unsigned)3); +} + +static void test_fill_q(TestQueue *q) +{ + for (unsigned int i = 0; i < 24; i++) + q->add(tagdb :: lookup(i)); +} + +unsigned int expected_rand[] = { 1, 4, 8, 13, 19, 3, 14, 16, 20, 2, 11, 17, + 23, 6, 12, 18, 0, 5, 9, 10, 15, 21, 22, 7 }; + +void test_next() +{ + Track *track; + TestQueue q(0); + + get_callbacks()->on_queue_track_del = test_del_cb_noop; + + test_fill_q(&q); + test :: begin(); + for (unsigned int i = 0; i < 24; i++) { + track = q.next(); + check_not_equal(track, TRACK_NULL); + check_equal(track->id, i); + } + test :: success(); + test_equal(q.size(), (unsigned)0); + test_equal(q.length_str(), (std::string)""); + test_equal(q.next(), TRACK_NULL); + + + q.set_flag(Q_RANDOM); + random_seed(0); + + test_fill_q(&q); + test :: begin(); + for (unsigned int i = 0; i < 24; i++) { + track = q.next(); + check_not_equal(track, TRACK_NULL); + check_equal(track->id, expected_rand[i]); + } + test :: success(); + test_equal(q.size(), (unsigned)0); + test_equal(q.length_str(), (std::string)""); + test_equal(q.next(), TRACK_NULL); + + + q.set_flag(Q_REPEAT); + q.unset_flag(Q_RANDOM); + test_fill_q(&q); + test :: begin(); + for (unsigned int i = 0; i < 48; i++) { + track = q.next(); + check_not_equal(track, TRACK_NULL); + check_equal(track->id, i % 24); + } + test :: success(); + test_equal(q.size(), (unsigned)24); +} + +void test_select() +{ + TestQueue q(0); + test_fill_q(&q); + + test_equal(q.size(), (unsigned)24); + q.track_selected(10); + test_equal(q.size(), (unsigned)23); + test_equal(q.next()->id, (unsigned)11); + + q.set_flag(Q_REPEAT); + q.track_selected(0); + test_equal(q.size(), (unsigned)22); + test_equal(q.next()->id, (unsigned)1); +} + +unsigned int exp_sort_title[] = { 1, 18, 19, 16, 20, 8, 2, 9, 23, 10, 17, 11, + 3, 21, 4, 0, 5, 22, 6, 12, 7, 13, 14, 15 }; +unsigned int exp_sort_ye_ti[] = { 0, 3, 2, 1, 7, 6, 5, 4, 11, 10, 9, 8, + 22, 21, 17, 23, 20, 16, 19, 18, 15, 14, 13, 12 }; + +void test_sorting() +{ + TestQueue q(0); + + q.sort(SORT_TITLE, true); + test_equal(q.get_sorder().size(), (size_t)1); + test_fill_q(&q); + test :: begin(); + for (unsigned int i = 0; i < 24; i++) + check_equal(q[i]->id, exp_sort_title[i]); + test :: success(); + + q.sort(SORT_TITLE, false); + test_equal(q.get_sorder().size(), (size_t)1); + test :: begin(); + for (unsigned int i = 0; i < 24; i++) + check_equal(q[i]->id, exp_sort_title[23 - i]); + test :: success(); + + q.sort(SORT_LENGTH, true); + test :: begin(); + for (unsigned int i = 0; i < 24; i++) + check_equal(q[i]->id, i); + test :: success(); + + q.sort(SORT_YEAR, true); + q.sort(SORT_TITLE, false); + q.sort(SORT_TITLE, false); + test_equal(q.get_sorder().size(), (size_t)2); + test :: begin(); + for (unsigned int i = 0; i < 24; i++) + check_equal(q[i]->id, exp_sort_ye_ti[i]); + test :: success(); +} + +void test_saving() +{ + TestQueue q(Q_RANDOM); + TestQueue r(0); + File f("test.q", 0); + + test_fill_q(&q); + + test :: begin(); + f.open(OPEN_WRITE); + q.write(f); + f.close(); + test :: success(); + + test :: begin(); + f.open(OPEN_READ); + r.read(f); + f.close(); + test :: success(); + + test_equal(r.has_flag(Q_RANDOM), q.has_flag(Q_RANDOM)); + test_equal(r.size(), q.size()); + test_equal(r.size_str(), q.size_str()); + test_equal(r.length_str(), q.length_str()); + + test :: begin(); + for (unsigned int i = 0; i < q.size(); i++) + check_equal(r[i]->id, q[i]->id); + test :: success(); +} + +int main(int argc, char **argv) +{ + test :: cp_data_dir(); + tagdb :: init(); + + run_test("Queue Default Constructor Test", test_default); + run_test("Queue Constructor Test", test_constructor, Q_ENABLED | Q_RANDOM); + run_test("Queue Flag Test", test_flags); + run_test("Queue Add and Remove Test", test_add_remove); + run_test("Queue Track Updated Test", test_updated); + run_test("Queue Pick Next Test", test_next); + run_test("Queue Select Track Test", test_select); + run_test("Queue Sorting Test", test_sorting); + run_test("Queue Save and Load Test", test_saving); + return 0; +} diff --git a/tests/random.cpp b/tests/random.cpp new file mode 100644 index 00000000..45a161fd --- /dev/null +++ b/tests/random.cpp @@ -0,0 +1,36 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + */ + +#include +#include "test.h" + +#include + +static void do_test_rng(unsigned int seed) +{ + random_seed(seed); + + for (unsigned int i = 0; i <= 10; i++) { + if (i <= seed) + test_equal(random(seed, i), seed); + else + test_equal(random(seed, i), seed + (i % (i - seed))); + } +} + +static void test_rng(unsigned int seed) +{ + std::stringstream ss; + ss << " (seed = " << seed << ")"; + std::string seed_str = ss.str(); + + run_test("Random Number Generator Test" + seed_str, do_test_rng, seed); +} + +int main(int argc, char **argv) +{ + for (unsigned int i = 0; i < 10; i++) + test_rng(i); + return 0; +} diff --git a/tests/src/Sconscript b/tests/src/Sconscript deleted file mode 100644 index 23a9e6ed..00000000 --- a/tests/src/Sconscript +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/python -Import("env", "lib") - -build = [] -for f in Glob("*.cpp"): - src = str(f) - run = src.split(".")[0] + ".run" - build += [ env.Program(run, [src] + lib) ] - -Return("build") diff --git a/tests/src/database.cpp b/tests/src/database.cpp deleted file mode 100644 index b99bfb84..00000000 --- a/tests/src/database.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2014 (c) Anna Schumaker. - * Test a Database - */ - -#include -#include - -#include -#include -#include - - -unsigned int test_num = 0; - - -class IntEntry : public DatabaseEntry { -public: - unsigned int val; - - IntEntry(); - IntEntry(unsigned int); - const std::string primary_key(); - void write(File &); - void read(File &); - void print(); -}; - -IntEntry :: IntEntry() : val(0) {} -IntEntry :: IntEntry(unsigned int v) : val(v) {} -const std::string IntEntry :: primary_key() -{ - std::stringstream ss; - ss << val; - return ss.str(); -} - -void IntEntry :: write(File &f) { f << val; } -void IntEntry :: read(File &f) { f >> val; } -void IntEntry :: print() { :: print(primary_key().c_str()); } - -void test_results(bool success, unsigned int line) -{ - print(" %u: ", test_num); - if (success) - print("Success!\n"); - else { - print("FAILED (%u) =(\n", line); - exit(1); - } - test_num++; -} - -void test_size(Database &db, unsigned int size, - unsigned int actual, unsigned int line) -{ - test_results( (db.size() == size) && (db.actual_size() == actual), line ); -} - -int main(int argc, char **argv) -{ - bool autosave = false; - unsigned int n = 0, size = 0, actual = 0; - char c; - - while ((c = getopt(argc, argv, "a")) != -1) { - switch (c) { - case 'a': - autosave = true; - break; - } - } - - n = atoi(argv[optind]); - Database db("database.db", autosave); - - - /** - * 0: Test initial size - */ - test_size(db, size, actual, __LINE__); - - - /** - * 1: Test insertion - */ - for (unsigned int i = 0; i < n; i++) { - if (db.insert(IntEntry(i)) != i) - test_results(false, __LINE__); - } - test_results(true, __LINE__); - size += n; - actual += n; - - - /** - * 2: Test that size changes - */ - test_size(db, size, actual, __LINE__); - - - /** - * 3: Test inserting ... again. - */ - for (unsigned int i = 0; i < n; i++) { - if (db.insert(IntEntry(i)) != i) - test_results(false, __LINE__); - } - test_results(true, __LINE__); - - - /** - * 4: Test that size didn't change - */ - test_size(db, size, actual, __LINE__); - - - /** - * 5: Test that size changes when removing - * Also test out-of-bounds removal - * - * Note: This test removes all even-index entries - */ - for (unsigned int i = 0; i < n + 10; i+=2) { - db.remove(i); - if (i < n) - size--; - } - test_size(db, size, actual, __LINE__); - - - /** - * 6: Test that removing again doesn't change anything - */ - for (unsigned int i = 0; i < n; i+=2) - db.remove(i); - test_size(db, size, actual, __LINE__); - - - /** - * 7: Test iterating and setting ID - */ - Database::iterator it; - unsigned int index = 1; - for (it = db.begin(); it != db.end(); it = db.next(it)) { - if ((*it).val != index) - test_results(false, __LINE__); - if ((*it).id != index) - test_results(false, __LINE__); - index+=2; - }; - test_results(true, __LINE__); - - - /** - * 8. Test access by id - */ - for (unsigned int i = 0; i < n + 10; i++) { - Database::iterator it = db.at(i); - if (((i % 2) == 0) && (it != db.end())) - test_results(false, __LINE__); - if ((i >= n) && (it != db.end())) - test_results(false, __LINE__); - } - test_results(true, __LINE__); - - - /** - * 9. Test inserting once again - */ - for (unsigned int i = 0; i < n; i++) { - index = db.insert(i); - if ((i % 2) == 0) { - size++; - actual++; - if (index != (n + (i / 2))) - test_results(false, __LINE__); - } else { - if (index != i) - test_results(false, __LINE__); - } - } - test_results(true, __LINE__); - - - /** - * 10. Test that size changed for every other insert - */ - test_size(db, size, actual, __LINE__); - - - /** - * Everything after this point tests loading from a file - */ - if (autosave == false) - return 0; - - Database db2("database.db", autosave); - db2.load(); - - - /** - * 11. Sizes should match - */ - test_size(db2, db.size(), db.actual_size(), __LINE__); - - - /** - * 12. Values should match - */ - Database::iterator it2 = db2.begin(); - for (it = db.begin(); it != db.end(); it++) { - if (it->valid != it2->valid) - test_results(false, __LINE__); - if (it->valid == true) { - if (it->val != it2->val) - test_results(false, __LINE__); - if (it->id != it2->id) - test_results(false, __LINE__); - } - it2++; - } - test_results(true, __LINE__); - - return 0; -} diff --git a/tests/src/db_entry.cpp b/tests/src/db_entry.cpp deleted file mode 100644 index f5aac956..00000000 --- a/tests/src/db_entry.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2014 (c) Anna Schumaker. - * Test a DatabaseEntry - */ - -#include -#include -#include - -#include -#include -#include - -enum action_t { PRIMARY_KEY, PRINT, READ, WRITE }; - -class IntEntry : public DatabaseEntry { -public: - unsigned int val; - std::string key; - - IntEntry(unsigned int, const std::string &); - const std::string primary_key(); - void write(File &); - void read(File &); - void print(); -}; - -IntEntry :: IntEntry(unsigned int i, const std::string &s) -{ - val = i; - key = s; -} - -const std::string IntEntry :: primary_key() -{ - return key; -} - -void IntEntry :: write(File &f) -{ - f << val << " " << key; -} - -void IntEntry :: read(File &f) -{ - f >> val >> key; -} - -void IntEntry :: print() -{ - :: print("Value: %u Key: %s Valid: %d Id: %u\n", val, key.c_str(), valid, id); -} - - - -int main(int argc, char **argv) -{ - action_t action = PRINT; - char c; - - while ((c = getopt(argc, argv, "prw")) != -1) { - switch (c) { - case 'p': - action = PRIMARY_KEY; - break; - case 'r': - action = READ; - break; - case 'w': - action = WRITE; - break; - } - } - - if (optind >= argc) { - print("ERROR: Not enough arguments\n"); - return 1; - } - - unsigned int i = atoi(argv[optind++]); - std::string key = argv[optind]; - - - File f("db_entry.txt", FILE_TYPE_DATA); - IntEntry ient(i, key); - - switch (action) { - case PRIMARY_KEY: - print("Primary key: %s\n", ient.primary_key().c_str()); - break; - case PRINT: - ient.print(); - break; - case READ: - f.open(OPEN_READ); - ient.read(f); - ient.print(); - f.close(); - break; - case WRITE: - f.open(OPEN_WRITE); - ient.write(f); - f.close(); - break; - } - - return 0; -} diff --git a/tests/src/file.cpp b/tests/src/file.cpp deleted file mode 100644 index df651726..00000000 --- a/tests/src/file.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2014 (c) Anna Schumaker. - * Do stuff with files - * - * Usage: files -D|-L [-c -g -o {R, W, N} -O -r -v -w] name [DATA] - * - * -D: FILE_TYPE_DATA - * -L: FILE_TYPE_LEGACY - * - * -c: Test closing the file - * -g: Read file using getline() - * -o: Open the file for READ, WRITE, or NOT_OPEN - * -O: Open the file a second time - * -r: Read data from file - * -v: Print version and exit - * -w: Write data to file - * - */ -#include -#include - -#include - -enum action_t { CLOSE, OPEN, PATHS, READ, VERSION, WRITE }; - -int print_version(File &f) -{ - print("%u\n", f.get_version()); - return 0; -} - -int print_filepath(File &f) -{ - print("%s\n", f.get_filepath()); - return 0; -} - -int open_file(File &f, OpenMode mode) -{ - if (f.open(mode) == true) - return 0; - return 1; -} - -int main(int argc, char **argv) -{ - int c, ret; - action_t action = PATHS; - FileLocHint hint = FILE_TYPE_INVALID; - OpenMode mode = NOT_OPEN; - bool second_open = false; - bool getline = false; - std::string file; - std::string data; - - while ((c = getopt(argc, argv, "cDgLo:Orvw")) != -1) { - switch (c) { - case 'c': - action = CLOSE; - break; - case 'D': - hint = FILE_TYPE_DATA; - break; - case 'g': - getline = true; - break; - case 'L': - hint = FILE_TYPE_LEGACY; - break; - case 'o': - action = OPEN; - switch (optarg[0]) { - case 'R': - mode = OPEN_READ; - break; - case 'W': - mode = OPEN_WRITE; - break; - case 'N': - mode = NOT_OPEN; - break; - default: - print("Invalid open mode\n"); - return 1; - } - break; - case 'O': - second_open = true; - break; - case 'r': - action = READ; - break; - case 'v': - action = VERSION; - break; - case 'w': - action = WRITE; - break; - default: - return 1; - } - } - - if (optind < argc) - file = argv[optind++]; - if (optind < argc) - data = argv[optind++]; - - File f(file, hint); - switch (action) { - case CLOSE: - ret = open_file(f, OPEN_WRITE); - if (ret == 0) { - f.close(); - ret = open_file(f, OPEN_WRITE); - } - return ret; - case OPEN: - ret = open_file(f, mode); - if ((ret == 0) && (second_open == true)) - ret = open_file(f, mode); - return ret; - case PATHS: - return print_filepath(f); - case READ: - ret = open_file(f, OPEN_READ); - if (ret == 0) { - do { - if (getline == true) - data = f.getline(); - else - f >> data; - if (f.good() == false) - break; - print("%s\n", data.c_str()); - } while (true); - } - return ret; - case VERSION: - return print_version(f); - case WRITE: - ret = open_file(f, OPEN_WRITE); - if (ret == 0) - f << data << std::endl; - return ret; - } -} diff --git a/tests/src/filter.cpp b/tests/src/filter.cpp deleted file mode 100644 index a2dfb97d..00000000 --- a/tests/src/filter.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2014 (c) Anna Schumaker. - * Test the filtering code - */ - -#include -#include -#include - -#include -#include -#include - -enum action_t { ADD, LOWERCASE, SEARCH }; - -void add_text(const std::string &text) -{ - std::string lc = filter :: add(text, 0); - print("%s\n", lc.c_str()); -} - -void to_lowercase(const std::string &text) -{ - std::string lc = filter :: lowercase(text); - print("%s\n", lc.c_str()); -} - -void read_file(unsigned int n) -{ - File f("filter.txt", FILE_TYPE_DATA); - if (f.open(OPEN_READ)) { - for (unsigned int i = 0; i < n; i++) { - std::string text = f.getline(); - filter :: add(text, i); - } - f.close(); - } -} - -void do_search(const std::string &text) -{ - std::set res; - std::set::iterator it; - - filter :: search(text, res); - - it = res.begin(); - if (it == res.end()) - return; - - print("%u", *it); - for (it++; it != res.end(); it++) - print(" %u", *it); - print("\n"); -} - -int main(int argc, char **argv) -{ - char c; - unsigned int n; - action_t action = ADD; - - while ((c = getopt(argc, argv, "als:")) != -1) { - switch (c) { - case 'a': - action = ADD; - break; - case 'l': - action = LOWERCASE; - break; - case 's': - action = SEARCH; - n = atoi(optarg); - break; - } - } - - std::string text; - for (int i = optind; i < argc; i++) { - text += " "; - text += argv[i]; - } - - switch (action) { - case ADD: - add_text(text); - break; - case LOWERCASE: - to_lowercase(text); - break; - case SEARCH: - read_file(n); - do_search(text); - break; - } - - return 0; -} diff --git a/tests/src/idle.cpp b/tests/src/idle.cpp deleted file mode 100644 index 4e386cf9..00000000 --- a/tests/src/idle.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014 (c) Anna Schumaker. - * Test the idle queue - */ - -#include -#include - -#include - -static unsigned int test_num = 0; -static unsigned int cur = -1; - -void test_results(bool success, unsigned int line) -{ - print(" %u: ", test_num); - if (success) - print("Success!\n"); - else { - print("FAILED (%u) =(", line); - exit(1); - } - test_num++; -} - -void test_progress(unsigned int expected, unsigned int multiplier, - unsigned int line) -{ - unsigned int prog = (idle :: get_progress() * multiplier); - test_results(prog == expected, line); -} - -void inc_cur(unsigned int &expected) -{ - cur++; - test_results(cur == expected, __LINE__); -} - -int main(int argc, char **argv) -{ - unsigned int i, num = 10; - - test_progress(num, num, __LINE__); - for (i = 0; i < num; i++) - idle :: schedule(inc_cur, i); - test_progress(0, num, __LINE__); - test_results(idle :: get_progress() == 0.0, __LINE__); - - for (i = 0; i < (num - 1); i++) { - test_results(idle :: run_task(), __LINE__); - test_progress(i + 1, num, __LINE__); - } - - test_results(idle :: run_task() == false, __LINE__); - test_progress(i + 1, num, __LINE__); - - test_results(idle :: run_task() == false, __LINE__); - test_progress(num, num, __LINE__); - return 0; -} diff --git a/tests/src/index.cpp b/tests/src/index.cpp deleted file mode 100644 index 090eb416..00000000 --- a/tests/src/index.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2014 (c) Anna Schumaker. - * Test a Database - */ - -#include -#include - -#include -#include - - -unsigned int test_num = 0; - -void test_results(bool success, unsigned int line) -{ - print(" %u: ", test_num); - if (success) - print("Success!\n"); - else { - print("FAILED (%u) =(\n", line); - exit(1); - } - test_num++; -} - -int main(int argc, char **argv) -{ - bool autosave = false; - unsigned int n; - char c; - - while ((c = getopt(argc, argv, "a")) != -1) { - switch (c) { - case 'a': - autosave = true; - break; - } - } - - n = atoi(argv[optind]); - Index index("index.idx", autosave); - - - /** - * 0: Test inserting when there is no key - */ - index.insert("a", 0); - Index :: iterator it = index.find("a"); - if (it == index.end()) - test_results(false, __LINE__); - if (it->values.size() == 0) - test_results(false, __LINE__); - test_results(*(it->values.begin()) == 0, __LINE__); - - - /** - * 1: Test removing - */ - index.remove("a", 0); - test_results((index.size() == 1) && (index.find("a")->values.size() == 0), __LINE__); - - - /** - * 2: Test adding multiple values - */ - for (c = 'a'; c <= 'z'; c++) { - std::string key = ""; - key += c; - - for (unsigned int i = 0; i < n; i++) - index.insert(key, i); - } - - test_results(index.size() == 26, __LINE__); - - - /** - * 3: Make sure we have multiple values for each key - */ - for (c = 'a'; c <= 'z'; c++) { - std::string key = ""; - key += c; - if (index.find(key)->values.size() != n) - test_results(false, __LINE__); - } - test_results(true, __LINE__); - - - /** - * 4: Test removing multiple values - */ - for (c = 'a'; c <= 'z'; c+=2) { - std::string key = ""; - key += c; - - for (unsigned int i = 0; i < n; i++) - index.remove(key, i); - - if (index.find(key)->values.size() > 0) - test_results(false, __LINE__); - } - - test_results(index.size() == 26, __LINE__); - - - /** - * Everything after this point tests loading from a file - */ - if (autosave == false) - return 0; - - Index index2("index.idx", autosave); - index2.load(); - - - /** - * 5: Load database from file - */ - test_results(index.size() == index2.size(), __LINE__); - - - /** - * 6: Check that everything is the right size - */ - for (c = 'a'; c <= 'z'; c++) { - std::string key = ""; - key += c; - - if (index.find(key)->values.size() != index2.find(key)->values.size()) - test_results(false, __LINE__); - } - test_results(true, __LINE__); - - return 0; -} diff --git a/tests/src/version.cpp b/tests/src/version.cpp deleted file mode 100644 index faa6d393..00000000 --- a/tests/src/version.cpp +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - * Prints out version info - */ -#include -#include - -int main(int argc, char **argv) -{ - print("%s\n", get_version()); - return 0; -} diff --git a/tests/tags.cpp b/tests/tags.cpp new file mode 100644 index 00000000..efd307bd --- /dev/null +++ b/tests/tags.cpp @@ -0,0 +1,310 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + * Test a DatabaseEntry + */ + +#include +#include "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_all_tracks() +{ + struct TagArgs expected; + + expected.library = tagdb :: add_library("tests/Music"); + 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); +} + +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.db", false); + Database album("album.db", false); + Database 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.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 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.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; +} diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 00000000..ea9dbd42 --- /dev/null +++ b/tests/test.h @@ -0,0 +1,166 @@ +/* + * Copyright 2014 (c) Anna Schumaker. + */ + +#include +#include +#include + +namespace test +{ + + + static unsigned int test_num; + static unsigned int failed; + + void new_test(const std::string &name) + { + std::cout << name << std::endl; + test_num = 0; + failed = 0; + } + + void begin() + { + std::cout << " " << test_num << ": "; + test_num++; + } + + void end() + { + std::cout << std::endl; + if (failed > 0) { + std::cout << failed << " tests failed =(" << std::endl; + std::cout << std::endl; + exit(failed); + } + } + + void success() + { + std::cout << "Success!" << std::endl; + } + + template + void failure(const T &lhs, const T &rhs, unsigned int line) + { + std::cout << "Failed at line " << line << ":" << std::endl; + std::cout << " Actual: " << lhs << std::endl; + std::cout << " Expected: " << rhs << std::endl; + failed++; + } + + template + void check_equal(const T &lhs, const T &rhs, unsigned int line) + { + if (lhs == rhs) + success(); + else + failure(lhs, rhs, line); + } + + template + void check_not_equal(const T &lhs, const T &rhs, unsigned int line) + { + if (lhs != rhs) + success(); + else + failure(lhs, rhs, line); + } + + template + void equal(const T &lhs, const T &rhs, unsigned int line) + { + begin(); + check_equal(lhs, rhs, line); + } + + template + void not_equal(const T &lhs, const T &rhs, unsigned int line) + { + begin(); + check_not_equal(lhs, rhs, line); + } + + std::string data_dir() + { + std::string res = g_get_user_data_dir(); + res += "/ocarina-test"; + return res; + } + + std::string data_file(const std::string &name) + { + return data_dir() + "/" + name; + } + + bool data_dir_exists() + { + return g_file_test(data_dir().c_str(), G_FILE_TEST_IS_DIR); + } + + bool data_file_exists(const std::string &name) + { + return g_file_test(data_file(name).c_str(), G_FILE_TEST_EXISTS); + } + + void rm_data_dir() + { + std::string cmd = "rm -r " + data_dir() + " 2>/dev/null"; + if (data_dir_exists()) + system(cmd.c_str()); + } + + void reset_data_dir() + { + std::string cmd = "mkdir -p " + data_dir(); + rm_data_dir(); + system(cmd.c_str()); + } + + void cp_data_dir() + { + reset_data_dir(); + std::string cmd = "cp -r tests/Data/* " + data_dir(); + system(cmd.c_str()); + } + + void gen_library() + { + system("tests/gen_library.sh"); + } + + void rm_library_dirs() + { + system("rm -r /tmp/ocarina/dir2 /tmp/ocarina/dir4"); + } +} + +#define run_test(name, func, ...) \ + do { \ + test :: new_test(name); \ + func( __VA_ARGS__ ); \ + test :: end(); \ + } while (0) + +#define test_equal(lhs, rhs) \ + do { \ + test :: equal(lhs, rhs, __LINE__); \ + } while (0) + +#define test_not_equal(lhs, rhs) \ + do { \ + test :: not_equal(lhs, rhs, __LINE__); \ + } while (0) + +#define check_equal(lhs, rhs) \ + do { \ + if (lhs != rhs) \ + test :: failure(lhs, rhs, __LINE__); \ + } while (0) + +#define check_not_equal(lhs, rhs) \ + do { \ + if (lhs == rhs) \ + test :: failure(lhs, rhs, __LINE__); \ + } while (0) diff --git a/tests/version b/tests/version deleted file mode 100755 index 75b56540..00000000 --- a/tests/version +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -# Copyright 2014 (c) Anna Schumaker. - -. $(dirname $0)/_functions - - -# Find version -version=$(config_version) -[ $(config_debug) == "True" ] && version="$version-debug" - - -# Run test -echo -n "Version test ... " -assert_equal "$(./src/version.run)" "$version" diff --git a/tests/version.cpp b/tests/version.cpp new file mode 100644 index 00000000..8c31017a --- /dev/null +++ b/tests/version.cpp @@ -0,0 +1,22 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include "test.h" + +#ifdef CONFIG_DEBUG +const std::string expected = "6.1-debug"; +#else +const std::string expected = "6.1"; +#endif /* CONFIG_DEBUG */ + +static void test_version() +{ + test_equal((std::string)get_version(), expected); +} + +int main(int argc, char **argv) +{ + run_test("Version Test", test_version); + return 0; +}