ocarina/core/tags/track.cpp

259 lines
5.5 KiB
C++
Raw Normal View History

/**
* Copyright 2014 (c) Anna Schumaker.
*/
extern "C" {
#include <core/filter.h>
#include <core/string.h>
#include <taglib/tag_c.h>
}
#include <core/tags/track.h>
#include <glib.h>
static struct database track_db;
static gchar *__track_key(struct library *library, gchar *path)
{
gchar *res;
if (library)
res = g_strdup_printf("%u/%s", library->li_dbe.dbe_index, path);
else
res = g_strdup("");
g_free(path);
return res;
}
static gchar *__track_path(struct track *track)
{
gchar *path;
sscanf(track->tr_path, "%*u/%m[^\n]", &path);
return path;
}
static struct track *__track_alloc()
{
struct track *track = new struct track;
dbe_init(&track->tr_dbe, track);
return track;
}
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;
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 = __track_alloc();
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 = g_strdup(key);
track->tr_title = g_strdup(taglib_tag_title(tag));
track->tr_lower = string_lowercase(track->tr_title);
taglib_tag_free_strings();
taglib_file_free(file);
out:
g_free(path);
g_free(fullpath);
return track ? &track->tr_dbe : NULL;
}
static void track_free(struct db_entry *dbe)
{
struct track *track = TRACK(dbe);
g_free(track->tr_title);
g_free(track->tr_lower);
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, 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(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 = __track_alloc();
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);
track->tr_album = album_get(album_id);
track->tr_artist = artist_get(artist_id);
track->tr_genre = genre_get(genre_id);
track->tr_library = library_get(library_id);
track->tr_title = file_readl(file);
track->tr_lower = string_lowercase(track->tr_title);
track->tr_path = __track_key(track->tr_library, file_readl(file));
return &track->tr_dbe;
}
static void track_write(struct file *file, struct db_entry *dbe)
{
struct track *track = TRACK(dbe);
gchar *path = __track_path(track);
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,
path);
g_free(path);
}
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 gchar *filepath)
{
unsigned int offset = strlen(library->li_path) + 1;
gchar *key = __track_key(library, g_strdup(filepath + offset));
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, rhs->tr_lower);
}
gchar *track_path(struct track *track)
{
gchar *path, *res;
if (track->tr_library) {
path = __track_path(track);
res = library_file(track->tr_library, path);
g_free(path);
return res;
}
return g_strdup("");
}
void track_played(struct track *track)
{
track->tr_count++;
date_today(&track->tr_date);
track_db_commit();
}
gchar *track_last_play(struct track *track)
{
if (track->tr_count > 0)
return date_string(&track->tr_date);
return g_strdup("Never");
}
#ifdef CONFIG_TESTING
const struct db_ops *test_track_ops() { return &track_ops; }
#endif /* CONFIG_TESTING */