/* * Copyright 2014 (c) Anna Schumaker. */ #include #include #include #include #include #include static struct database album_db; static inline void __album_init_file(struct album *al, struct cache_file *f) { cache_file_init(f, g_strdup_printf("%d", al->al_year), g_strdup_printf("%s.jpg", al->al_name)); } static inline void __album_deinit_file(struct cache_file *f) { g_free(f->cf_subdir); g_free(f->cf_name); } static bool __album_fetch_cover(struct album *album, gchar *releaseid, CaaCoverArt *caa) { struct cache_file file; CaaImageData image; gchar error[256]; image = caa_coverart_fetch_front(caa, releaseid); if (!image) { caa_coverart_get_lasterrormessage(caa, error, sizeof(error)); g_printf("Cover Art Archive: %s\n", error); return false; } __album_init_file(album, &file); if (cache_file_open(&file, OPEN_WRITE)) { cache_file_write(&file, caa_imagedata_data(image), caa_imagedata_size(image)); cache_file_close(&file); } __album_deinit_file(&file); caa_imagedata_delete(image); return album_artwork_exists(album); } static bool __album_foreach_fetch(struct album *album, Mb5Metadata metadata) { Mb5ReleaseList list = mb5_metadata_get_releaselist(metadata); gchar releaseid[40]; Mb5Release release; CaaCoverArt *caa; bool ret = false; unsigned int i; caa = caa_coverart_new(OCARINA_NAME); if (!caa) return false; for (i = 0; i < mb5_release_list_size(list); i++) { release = mb5_release_list_item(list, i); if (!release) break; mb5_release_get_id(release, releaseid, sizeof(releaseid)); ret = __album_fetch_cover(album, releaseid, caa); if (ret) break; } caa_coverart_delete(caa); return ret; } static bool __album_query_releaseid(struct album *album, Mb5Query *mb5, gchar *param) { Mb5Metadata metadata = NULL; gchar *query = "query"; tQueryResult result; gchar error[256]; bool ret = false; do { metadata = mb5_query_query(mb5, "release", "", "", 1, &query, ¶m); result = mb5_query_get_lastresult(mb5); if (result != 0) { mb5_query_get_lasterrormessage(mb5, error, sizeof(error)); g_printf("MusicBrainz: %s\n", error); } } while (result == 503); if (metadata) { ret = __album_foreach_fetch(album, metadata); mb5_metadata_delete(metadata); } return ret; } static bool __album_query_name(struct album *album, Mb5Query *mb5, gchar *extra, bool quotes) { gchar *fmt = "release:%s%s%s%s"; gchar *param = g_strdup_printf(fmt, quotes ? "\"" : "", quotes ? album->al_name : album->al_lower, quotes ? "\"" : "", extra ? extra : ""); bool ret = __album_query_releaseid(album, mb5, param); g_free(param); return ret; } static bool __album_query_artist(struct album *album, Mb5Query *mb5) { gchar *fmt = " AND artist:\"%s\""; gchar *param = g_strdup_printf(fmt, album->al_artist->ar_lower); bool ret = __album_query_name(album, mb5, param, false); g_free(param); return ret; } static bool __album_query_year(struct album *album, Mb5Query *mb5) { gchar *param = g_strdup_printf(" AND date:%d", album->al_year); bool ret = __album_query_name(album, mb5, param, true); g_free(param); return ret; } static bool __album_fetch_artwork(struct album *album) { Mb5Query *mb5; if (album_artwork_exists(album)) return true; mb5 = mb5_query_new(OCARINA_NAME, NULL, 0); if (!mb5) return false; if (album->al_artist && __album_query_artist(album, mb5)) goto out; if (album->al_year && __album_query_year(album, mb5)) goto out; __album_query_name(album, mb5, NULL, true); out: mb5_query_delete(mb5); return true; } static gchar *__album_key(const gchar *name, unsigned int year) { return g_strdup_printf("%u/%s", year, name); } static struct album *__album_alloc(gchar *name, unsigned int year) { struct album *album = g_malloc(sizeof(struct album)); dbe_init(&album->al_dbe, album); album->al_year = year; album->al_name = name; album->al_lower = string_lowercase(album->al_name); album->al_artist = NULL; if (!album_artwork_exists(album)) idle_schedule(IDLE_ASYNC, IDLE_FUNC(__album_fetch_artwork), album); return album; } static struct db_entry *album_alloc(const gchar *key) { unsigned int year; gchar *name; if (sscanf(key, "%u/%m[^\n]", &year, &name) == 1) name = g_strdup(""); return &__album_alloc(name, year)->al_dbe; } static void album_free(struct db_entry *dbe) { g_free(ALBUM(dbe)->al_name); g_free(ALBUM(dbe)->al_lower); g_free(ALBUM(dbe)); } static gchar *album_key(struct db_entry *dbe) { return __album_key(ALBUM(dbe)->al_name, ALBUM(dbe)->al_year); } static struct db_entry *album_read(struct file *file) { unsigned int year; gchar *line, *name; line = file_readl(file); if (sscanf(line, "%u %m[^\n]", &year, &name) == 1) name = g_strdup(""); g_free(line); return &__album_alloc(name, year)->al_dbe; } static void album_write(struct file *file, struct db_entry *dbe) { file_writef(file, "%u %s", ALBUM(dbe)->al_year, ALBUM(dbe)->al_name); } static const struct db_ops album_ops = { album_alloc, album_free, album_key, album_read, NULL, album_write, }; void album_db_init() { db_init(&album_db, "album.db", true, &album_ops); db_load_idle(&album_db); } void album_db_deinit() { db_deinit(&album_db); } struct album *album_find(const gchar *name, unsigned int year) { gchar *key = __album_key(name, year); struct album *album = ALBUM(db_find(&album_db, key)); g_free(key); return album; } struct album *album_get(const unsigned int index) { return ALBUM(db_at(&album_db, index)); } int album_compare(struct album *lhs, struct album *rhs) { return string_compare(lhs->al_lower, rhs->al_lower); } int album_compare_year(struct album *lhs, struct album *rhs) { if (lhs->al_year - rhs->al_year == 0) return album_compare(lhs, rhs); return lhs->al_year - rhs->al_year; } bool album_artwork_exists(struct album *album) { struct cache_file file; bool ret; __album_init_file(album, &file); ret = cache_file_exists(&file); __album_deinit_file(&file); return ret; } gchar *album_artwork_path(struct album *album) { struct cache_file file; gchar *ret = NULL; __album_init_file(album, &file); if (cache_file_exists(&file)) ret = cache_file_path(&file); __album_deinit_file(&file); return ret; } #ifdef CONFIG_TESTING const struct db_ops *test_album_ops() { return &album_ops; } #endif /* CONFIG_TESTING */