ocarina/core/tags/album.c

264 lines
5.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)
{
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 Mb5Metadata __album_query_releaseid(struct album *album,
Mb5Query *mb5)
{
gchar *param, *query = "query";
Mb5Metadata metadata = NULL;
tQueryResult result;
gchar error[256];
if (album->al_artist != NULL)
param = g_strdup_printf("release:\"%s\" AND artist:%s",
album->al_lower, album->al_artist->ar_lower);
else if (album->al_year != 0)
param = g_strdup_printf("release:\"%s\" AND date:%d",
album->al_lower, album->al_year);
else
param = g_strdup_printf("release:\"%s\"", album->al_lower);
metadata = mb5_query_query(mb5, "release", "", "", 1, &query, &param);
result = mb5_query_get_lastresult(mb5);
if (result != 0) {
mb5_query_get_lasterrormessage(mb5, error, sizeof(error));
g_printf("MusicBrainz: %s\n", error);
}
g_free(param);
return metadata;
}
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 void __album_foreach_fetch(struct album *album, Mb5Metadata metadata)
{
Mb5ReleaseList list = mb5_metadata_get_releaselist(metadata);
gchar releaseid[40];
Mb5Release release;
CaaCoverArt *caa;
unsigned int i;
caa = caa_coverart_new(OCARINA_NAME);
if (!caa)
return;
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, caa))
break;
}
caa_coverart_delete(caa);
}
static bool __album_fetch_artwork(struct album *album)
{
Mb5Metadata metadata;
Mb5Query *mb5;
bool ret;
if (album_artwork_exists(album))
return true;
mb5 = mb5_query_new(OCARINA_NAME, NULL, 0);
if (!mb5)
return true;
metadata = __album_query_releaseid(album, mb5);
if (metadata) {
__album_foreach_fetch(album, metadata);
mb5_metadata_delete(metadata);
}
ret = mb5_query_get_lasthttpcode(mb5) != 503;
mb5_query_delete(mb5);
return ret;
}
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 */