/* * Copyright 2013 (c) Anna Schumaker. */ #include #include #include #include static Database artist_db("artist.db"); static Database album_db("album.db"); static Database genre_db("genre.db"); static Database library_db("library.db"); static Database track_db("track.db"); /* * library :: Artist: Artist tag information */ library :: Artist :: Artist() : name("") { } library :: Artist :: Artist(TagLib :: Tag *tag) : name(tag->artist().stripWhiteSpace().to8Bit(true)) { primary_key = name; } void library :: Artist :: read(File &f) { name = f.getline(); } void library :: Artist :: write(File &f) { f << name; } #ifdef CONFIG_DEBUG void library :: Artist :: print() { :: print("Artist: %s", name.c_str()); } #endif /* CONFIG_DEBUG */ /* * library :: Album: Album tag information */ library :: Album :: Album() : name(""), year(0), artist_id(0) { } library :: Album :: Album(TagLib :: Tag *tag, unsigned int artist) : name(tag->album().stripWhiteSpace().to8Bit(true)), year(tag->year()), artist_id(artist) { std::stringstream ss; ss << name << "." << year << "." << artist_id; primary_key = ss.str(); } void library :: Album :: read(File &f) { f >> artist_id >> year; name = f.getline(); } void library :: Album :: write(File &f) { f << artist_id << " " << year << " " << name; } #ifdef CONFIG_DEBUG void library :: Album :: print() { :: print("Album: %s (%u) by %s", name.c_str(), year, artist_db[artist_id].name.c_str()); } #endif /* CONFIG_DEBUG */ /* * library :: Genre: Genre tag information */ library :: Genre :: Genre() : name("") { } library :: Genre :: Genre(TagLib :: Tag *tag) : name(tag->genre().stripWhiteSpace().to8Bit(true)) { primary_key = name; } void library :: Genre :: read(File &f) { name = f.getline(); } void library :: Genre :: write(File &f) { f << name; } #ifdef CONFIG_DEBUG void library :: Genre :: print() { :: print("Genre: %s", name.c_str()); } #endif /* CONFIG_DEBUG */ /* * library :: Library: Basic information about each directory in the library */ library :: Library :: Library() : root_path(""), enabled(false) { } library :: Library :: Library(const std::string &path, bool is_enabled) : root_path(path), enabled(is_enabled) { primary_key = root_path; } void library :: Library :: read(File &f) { f >> enabled; root_path = f.getline(); } void library :: Library :: write(File &f) { f << enabled << " " << root_path; } #ifdef CONFIG_DEBUG void library :: Library :: print() { :: print("%s", root_path.c_str()); if (enabled == true) :: print(" (enabled)"); else :: print(" (disabled)"); } #endif /* CONFIG_DEBUG */ /* * 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()), banned(false), title(tag->title().stripWhiteSpace().to8Bit(true)) { primary_key = path; filepath = path.substr(library_db[library_id].root_path.size() + 1); } void library :: Track :: read(File &f) { f >> library_id >> artist_id >> album_id >> genre_id; f >> track >> last_year >> last_month >> last_day; f >> play_count >> length >> banned; title = f.getline(); filepath = f.getline(); } void library :: Track :: write(File &f) { f << library_id << " " << artist_id << " " << album_id << " " << genre_id; f << " " << track << " " << last_year << " " << last_month << " " << last_day; f << " " << play_count << " " << length << " " << banned << std :: endl; f << title << std :: endl; f << filepath; } #ifdef CONFIG_DEBUG void library :: Track :: print() { :: print("%u. %s by %s from %s (%u)\n", track, title.c_str(), artist_db[artist_id].name.c_str(), album_db[album_id].name.c_str(), album_db[album_id].year); :: print(" Genre: %s, Length: %u (seconds)\n", genre_db[genre_id].name.c_str(), length); :: print(" Play count: %u, last played %u/%u/%u\n", play_count, last_day, last_month, last_year); :: print(" %s", filepath.c_str()); } #endif /* CONFIG_DEBUG */ /* * Internal library functions */ static void do_scan_path(unsigned int, const std :: string &); 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; if (ref.isNull()) { print("ERROR: Could not read tags for file %s", path.c_str()); return; } tag = ref.tag(); audio = ref.audioProperties(); artist_id = artist_db.insert(library :: Artist(tag)); album_id = album_db.insert(library :: Album(tag, artist_id)); genre_id = genre_db.insert(library :: Genre(tag)); track_db.insert(library :: Track(tag, audio, lib_id, artist_id, album_id, genre_id, path)); } static void process_path(unsigned int lib_id, const std :: string &dir, const std :: string &name) { std :: string path = dir + "/" + name; if (g_file_test(path.c_str(), G_FILE_TEST_IS_DIR) == true) do_scan_path(lib_id, path); else read_tags(lib_id, path); } static void save_all_dbs() { artist_db.save(); album_db.save(); genre_db.save(); track_db.save(); } static void do_scan_path(unsigned int lib_id, const std :: string &path) { GDir *dir; const char *name; dir = g_dir_open(path.c_str(), 0, NULL); if (dir == NULL) return; name = g_dir_read_name(dir); while (name != NULL) { process_path(lib_id, path, name); save_all_dbs(); name = g_dir_read_name(dir); } } static void do_validate_library(unsigned int lib_id) { std :: string path; if (track_db.size() == 0) return; for (unsigned int i = track_db.first(); i <= track_db.last(); i = track_db.next(i)) { if (track_db[i].library_id != lib_id) continue; path = library_db[lib_id].root_path + "/" + track_db[i].filepath; if (g_file_test(path.c_str(), G_FILE_TEST_EXISTS) == false) { dprint("Removing file: %s\n", path.c_str()); track_db.remove(i); } } } static void do_update_library(unsigned int lib_id) { do_validate_library(lib_id); do_scan_path(lib_id, library_db[lib_id].root_path); } /* * API used by the GUI begins here */ void library :: init() { library_db.load(); } bool library :: add_path(const std::string &dir) { unsigned int id; if (g_file_test(dir.c_str(), G_FILE_TEST_IS_DIR) == false) return false; id = library_db.insert(library :: Library(dir, true)); library_db.save(); update_path(id); return true; } void library :: del_path(unsigned int id) { library_db.remove(id); library_db.save(); } void library :: update_path(unsigned int id) { if (id > library_db.size()) return; if (library_db[id].valid == false) return; do_update_library(id); } bool library :: lookup(unsigned int id, library :: Song *song) { if (id >= track_db.num_rows()) return false; song->track = &track_db[id]; song->artist = &artist_db[song->track->artist_id]; song->album = &album_db[song->track->album_id]; song->genre = &genre_db[song->track->genre_id]; song->library = &library_db[song->track->library_id]; return true; } #ifdef CONFIG_DEBUG void library :: print_db(DB_Type type) { switch (type) { case DB_ALBUM: album_db.print(); break; case DB_ARTIST: artist_db.print(); break; case DB_GENRE: genre_db.print(); break; case DB_LIBRARY: library_db.print(); break; case DB_TRACK: track_db.print(); } } void library :: reset() { album_db.clear(); artist_db.clear(); genre_db.clear(); library_db.clear(); track_db.clear(); } #endif /* CONFIG_DEBUG */