/** * @file * Copyright 2014 (c) Anna Schumaker. */ #include #include #include #include #include #include #include Database track_db("track.db", false); /** * * Track tag * */ Track :: Track(const std::string &f, Library *library) : _artist(NULL), _library(library), album(NULL), genre(NULL), play_count(0), last_year(0), last_month(0), last_day(0), filepath(f.substr(library->primary_key().size() + 1)) { library->inc_size(); } Track :: ~Track() { if (_library) _library->dec_size(); } 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 = tags :: get_library(library_id); _artist = tags :: get_artist(artist_id); album = tags :: get_album(album_id); genre = tags :: get_genre(genre_id); title_lower = filter :: add(title, index()); filter :: add(_artist->name(), index()); filter :: add(album->name(), index()); _library->inc_size(); set_length_str(); } void Track :: write(File &f) { f << _library->index() << " " << _artist->index() << " "; f << album->index() << " " << genre->index() << " " << 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; } bool Track :: tag() { TagLib :: Tag *tag; TagLib :: AudioProperties *audio; TagLib :: FileRef ref(path().c_str(), true, TagLib::AudioProperties::Fast); _library->inc_size(); if (ref.isNull()) { print("WARNING: Could not read tags for file %s\n", path().c_str()); return false; } tag = ref.tag(); audio = ref.audioProperties(); _artist = tags :: get_artist(format_tag(tag->artist())); album = tags :: get_album(format_tag(tag->album()), tag->year()); genre = tags :: get_genre(format_tag(tag->genre())); track = tag->track(); length = audio->length(); title = format_tag(tag->title()); title_lower = filter :: add(title, index()); set_length_str(); filter :: add(_artist->name(), index()); filter :: add(album->name(), index()); return true; } const std::string Track :: path() const { if (!_library) return ""; return _library->primary_key() + "/" + 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 _artist->compare(rhs->artist()); case SORT_ALBUM: return album->compare(rhs->album); case SORT_COUNT: return compare_uint(play_count, rhs->play_count); case SORT_GENRE: return genre->compare(rhs->genre); 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() { tags :: init(); track_db.load(); } void tagdb :: commit() { track_db.save(); } Track *tagdb :: add_track(const std::string &filepath, Library *library) { Track *track = track_db.insert(Track(filepath, library)); if (track && !track->tag()) { remove_track(track->index()); track = NULL; } return track; } 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()->index() == library_id) track_db.remove((*it)->index()); } tagdb :: commit(); tags :: remove_library(tags :: get_library(library_id)); } Track *tagdb :: lookup(unsigned int track_id) { return track_db.at(track_id); } Database &tagdb :: get_track_db() { return track_db; }