9928c3239f
Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
305 lines
6.7 KiB
C
305 lines
6.7 KiB
C
/*
|
|
* Copyright 2014 (c) Anna Schumaker.
|
|
*/
|
|
#include <core/idle.h>
|
|
#include <core/string.h>
|
|
#include <core/version.h>
|
|
#include <core/tags/album.h>
|
|
#include <coverart/caa_c.h>
|
|
#include <musicbrainz5/mb5_c.h>
|
|
|
|
static struct database album_db;
|
|
|
|
|
|
static inline void __album_init_file(struct album *al, struct cache_file *f)
|
|
{
|
|
gchar *name = g_uri_escape_string(al->al_name, " ", true);
|
|
cache_file_init(f, g_strdup_printf("%d", al->al_year),
|
|
g_strdup_printf("%s.jpg", name));
|
|
g_free(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)
|
|
{
|
|
struct cache_file file;
|
|
CaaImageData image;
|
|
CaaCoverArt *caa;
|
|
gchar error[256];
|
|
|
|
caa = caa_coverart_new(OCARINA_NAME);
|
|
if (!caa)
|
|
return false;
|
|
|
|
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);
|
|
goto out;
|
|
}
|
|
|
|
__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);
|
|
out:
|
|
caa_coverart_delete(caa);
|
|
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;
|
|
unsigned int i;
|
|
|
|
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));
|
|
if (__album_fetch_cover(album, releaseid))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool __album_run_query(struct album *album, gchar *term1,
|
|
gchar *term2, gchar *term3)
|
|
{
|
|
gchar *param, *query = "query";
|
|
Mb5Metadata data = NULL;
|
|
unsigned int code;
|
|
gchar error[256];
|
|
bool ret = false;
|
|
Mb5Query *mb5;
|
|
|
|
param = g_strjoin(" AND ", term1, term2, term3, NULL);
|
|
|
|
do {
|
|
mb5 = mb5_query_new(OCARINA_NAME, NULL, 0);
|
|
if (!mb5)
|
|
break;
|
|
|
|
data = mb5_query_query(mb5, "release", "", "", 1, &query, ¶m);
|
|
code = mb5_query_get_lasthttpcode(mb5);
|
|
if (mb5_query_get_lastresult(mb5) != 0) {
|
|
mb5_query_get_lasterrormessage(mb5, error, sizeof(error));
|
|
g_printf("MusicBrainz: %s\n", error);
|
|
}
|
|
|
|
mb5_query_delete(mb5);
|
|
} while (code == 503);
|
|
|
|
if (data) {
|
|
ret = __album_foreach_fetch(album, data);
|
|
mb5_metadata_delete(data);
|
|
}
|
|
|
|
g_free(param);
|
|
return ret;
|
|
}
|
|
|
|
static bool __album_query_artist(struct album *album, struct artist *al_artist)
|
|
{
|
|
gchar *release, *artist, *year;
|
|
bool found;
|
|
|
|
if (!al_artist || strncmp(al_artist->ar_lower, "various", 7) == 0)
|
|
return false;
|
|
|
|
release = g_strdup_printf("release:\"%s\"~", album->al_lower);
|
|
artist = g_strdup_printf("artist:\"%s\"~", al_artist->ar_name);
|
|
year = g_strdup_printf("date:%d*", album->al_year);
|
|
|
|
if (album->al_year > 0)
|
|
found = __album_run_query(album, release, artist, year);
|
|
if (!found)
|
|
found = __album_run_query(album, release, artist, NULL);
|
|
if (!found && album->al_year > 0)
|
|
found = __album_run_query(album, album->al_lower, artist, year);
|
|
if (!found)
|
|
found = __album_run_query(album, album->al_lower, artist, NULL);
|
|
|
|
g_free(release);
|
|
g_free(artist);
|
|
g_free(year);
|
|
return found;
|
|
}
|
|
|
|
static bool __album_fetch_artwork(struct album *album)
|
|
{
|
|
if (album_artwork_exists(album))
|
|
return true;
|
|
if (string_length(album->al_name) == 0)
|
|
return true;
|
|
|
|
if (!__album_query_artist(album, album->al_artist))
|
|
__album_run_query(album, album->al_lower, NULL, NULL);
|
|
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;
|
|
}
|
|
|
|
bool album_artwork_import(struct album *album, gchar *path)
|
|
{
|
|
struct cache_file file;
|
|
bool ret = false;
|
|
|
|
__album_init_file(album, &file);
|
|
if (path && cache_file_open(&file, OPEN_WRITE)) {
|
|
ret = cache_file_import(&file, path);
|
|
cache_file_close(&file);
|
|
}
|
|
__album_deinit_file(&file);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_TESTING
|
|
const struct db_ops *test_album_ops() { return &album_ops; }
|
|
#endif /* CONFIG_TESTING */
|