ocarina/core/tags/album.c
Anna Schumaker e30f7f8115 core/tags/album: Add support for importing album artwork
This is needed so the user can manually set album artwork in the cases
where either we don't fetch the right image or no image is found.

Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
2016-07-12 14:36:40 -04:00

314 lines
6.9 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 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,
&param);
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 quoted)
{
gchar *fmt = "release:%s%s%s%s";
gchar *param = g_strdup_printf(fmt, quoted ? "\"" : "",
quoted ? album->al_name : album->al_lower,
quoted ? "\"" : "",
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, bool quoted)
{
struct artist *artist = album->al_artist;
gchar *fmt = " AND artist:%s%s%s";
gchar *param = g_strdup_printf(fmt, quoted ? "\"" : "",
quoted ? artist->ar_name : artist->ar_lower,
quoted ? "\"" : "");
bool ret = __album_query_name(album, mb5, param, !quoted);
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, false))
goto out;
if (album->al_artist && __album_query_artist(album, mb5, true))
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;
}
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 */