/** * Copyright 2014 (c) Anna Schumaker. */ extern "C" { #include #include #include } #include #include static struct database track_db; static gchar *__track_key(struct library *library, const std::string &path) { if (!library) return g_strdup(""); return g_strdup_printf("%u/%s", library->li_dbe.dbe_index, path.c_str()); } struct db_entry *track_alloc(const gchar *key) { const TagLib_AudioProperties *audio; struct library *library; struct track *track = NULL; unsigned int lib_id; TagLib_File *file; TagLib_Tag *tag; char *fullpath, *path, *lower; sscanf(key, "%u/%m[^\n]", &lib_id, &path); library = library_get(lib_id); fullpath = library_file(library, path); file = taglib_file_new(fullpath); if (!file || !taglib_file_is_valid(file)) { printf("WARNING: Could not read tags for: %s\n", fullpath); goto out; } track = new struct track; dbe_init(&track->tr_dbe, track); tag = taglib_file_tag(file); audio = taglib_file_audioproperties(file); track->tr_album = album_find(taglib_tag_album(tag), taglib_tag_year(tag)); track->tr_artist = artist_find(taglib_tag_artist(tag)); track->tr_genre = genre_find(taglib_tag_genre(tag)); track->tr_library = library; track->tr_count = 0; track->tr_length = taglib_audioproperties_length(audio); track->tr_track = taglib_tag_track(tag); date_set(&track->tr_date, 0, 0, 0); track->tr_path = path; track->tr_title = taglib_tag_title(tag); lower = string_lowercase(track->tr_title.c_str()); track->tr_lower = lower; taglib_tag_free_strings(); taglib_file_free(file); g_free(lower); out: g_free(fullpath); g_free(path); return track ? &track->tr_dbe : NULL; } static void track_free(struct db_entry *dbe) { struct track *track = TRACK(dbe); if (track->tr_library) track->tr_library->li_size--; delete track; } static void track_setup(struct db_entry *dbe) { struct track *track = TRACK(dbe); filter_add(track->tr_lower.c_str(), dbe->dbe_index); filter_add(track->tr_artist->ar_lower, dbe->dbe_index); filter_add(track->tr_album->al_lower, dbe->dbe_index); track->tr_library->li_size++; } static gchar *track_key(struct db_entry *dbe) { return __track_key(TRACK(dbe)->tr_library, TRACK(dbe)->tr_path); } static struct db_entry *track_read(struct file *file) { unsigned int library_id, artist_id, album_id, genre_id; struct track *track = new struct track; gchar *path, *name, *lower; dbe_init(&track->tr_dbe, track); file_readf(file, "%u %u %u %u %u", &library_id, &artist_id, &album_id, &genre_id, &track->tr_track); date_read(file, &track->tr_date); file_readf(file, "%u %u", &track->tr_count, &track->tr_length); name = file_readl(file); path = file_readl(file); lower = string_lowercase(name); track->tr_title = name; track->tr_path = path; track->tr_lower = lower; g_free(name); g_free(path); g_free(lower); track->tr_library = library_get(library_id); track->tr_artist = artist_get(artist_id); track->tr_album = album_get(album_id); track->tr_genre = genre_get(genre_id); return &track->tr_dbe; } static void track_write(struct file *file, struct db_entry *dbe) { struct track *track = TRACK(dbe); file_writef(file, "%u %u %u %u %u ", track->tr_library->li_dbe.dbe_index, track->tr_artist->ar_dbe.dbe_index, track->tr_album->al_dbe.dbe_index, track->tr_genre->ge_dbe.dbe_index, track->tr_track); date_write(file, &track->tr_date); file_writef(file, " %u %u %s\n%s\n", track->tr_count, track->tr_length, track->tr_title.c_str(), track->tr_path.c_str()); } static const struct db_ops track_ops = { track_alloc, track_free, track_key, track_read, track_setup, track_write, }; void track_db_init() { db_init(&track_db, "track.db", false, &track_ops); db_load(&track_db); } void track_db_deinit() { db_deinit(&track_db); } void track_db_commit() { db_save(&track_db); } const struct database *track_db_get() { return &track_db; } struct track *track_add(struct library *library, const std::string &filepath) { std::string path = filepath.substr(strlen(library->li_path) + 1); gchar *key = __track_key(library, path); struct track *track = NULL; if (!db_get(&track_db, key)) track = TRACK(db_insert(&track_db, key)); g_free(key); return track; } void track_remove(struct track *track) { db_remove(&track_db, &track->tr_dbe); } void track_remove_all(struct library *library) { struct db_entry *it, *next; db_for_each(it, next, &track_db) { if (TRACK(it)->tr_library == library) db_remove(&track_db, it); } track_db_commit(); } struct track *track_get(const unsigned int index) { return TRACK(db_at(&track_db, index)); } int track_compare(struct track *lhs, struct track *rhs) { return string_compare(lhs->tr_lower.c_str(), rhs->tr_lower.c_str()); } const std::string track_path(struct track *track) { std::string ret = ""; if (track->tr_library) { gchar *g_ret = library_file(track->tr_library, track->tr_path.c_str()); ret = g_ret; g_free(g_ret); } return ret; } void track_played(struct track *track) { track->tr_count++; date_today(&track->tr_date); track_db_commit(); } const std::string track_last_play(struct track *track) { std::string res = "Never"; char *buf; if (track->tr_count > 0) { buf = date_string(&track->tr_date); res = buf; g_free(buf); } return res; } #ifdef CONFIG_TESTING const struct db_ops *test_track_ops() { return &track_ops; } #endif /* CONFIG_TESTING */