241 lines
5.3 KiB
C++
241 lines
5.3 KiB
C++
/**
|
|
* Copyright 2014 (c) Anna Schumaker.
|
|
*/
|
|
#include <core/filter.h>
|
|
extern "C" {
|
|
#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, const std::string &path)
|
|
{
|
|
if (!library)
|
|
return g_strdup("");
|
|
return g_strdup_printf("%u/%s", library->dbe_index, path.c_str());
|
|
}
|
|
|
|
track :: track()
|
|
: tr_album(NULL), tr_artist(NULL), tr_genre(NULL), tr_library(NULL),
|
|
tr_count(0), tr_length(0), tr_track(0)
|
|
{}
|
|
|
|
track :: track(const std::string &key)
|
|
{
|
|
const TagLib_AudioProperties *audio;
|
|
struct library *library;
|
|
unsigned int lib_id;
|
|
TagLib_File *file;
|
|
TagLib_Tag *tag;
|
|
std::string full;
|
|
char *path, *lower;
|
|
|
|
sscanf(key.c_str(), "%u/%m[^\n]", &lib_id, &path);
|
|
library = library_get(lib_id);
|
|
full = library_file(library, path);
|
|
file = taglib_file_new(full.c_str());
|
|
if (!file || !taglib_file_is_valid(file)) {
|
|
printf("WARNING: Could not read tags for: %s\n", full.c_str());
|
|
goto out;
|
|
}
|
|
|
|
tag = taglib_file_tag(file);
|
|
audio = taglib_file_audioproperties(file);
|
|
|
|
tr_album = album_find(taglib_tag_album(tag), taglib_tag_year(tag));
|
|
tr_artist = artist_find(taglib_tag_artist(tag));
|
|
tr_genre = genre_find(taglib_tag_genre(tag));
|
|
tr_library = library;
|
|
|
|
tr_count = 0;
|
|
tr_length = taglib_audioproperties_length(audio);
|
|
tr_track = taglib_tag_track(tag);
|
|
date_set(&tr_date, 0, 0, 0);
|
|
|
|
tr_path = path;
|
|
tr_title = taglib_tag_title(tag);
|
|
lower = string_lowercase(tr_title.c_str());
|
|
tr_lower = lower;
|
|
|
|
taglib_tag_free_strings();
|
|
taglib_file_free(file);
|
|
g_free(lower);
|
|
out:
|
|
g_free(path);
|
|
}
|
|
|
|
static void track_free(struct db_entry *dbe)
|
|
{
|
|
struct track *track = (struct track *)dbe;
|
|
|
|
if (track->tr_library)
|
|
track->tr_library->li_size--;
|
|
delete track;
|
|
}
|
|
|
|
static void track_setup(struct db_entry *dbe)
|
|
{
|
|
struct track *track = (struct track *)dbe;
|
|
|
|
filter :: add(track->tr_title, track->dbe_index);
|
|
filter :: add(track->tr_artist->ar_name, track->dbe_index);
|
|
filter :: add(track->tr_album->al_name, track->dbe_index);
|
|
track->tr_library->li_size++;
|
|
}
|
|
|
|
static gchar *track_key(struct db_entry *dbe)
|
|
{
|
|
struct track *track = (struct track *)dbe;
|
|
return __track_key(track->tr_library, track->tr_path);
|
|
}
|
|
|
|
static struct db_entry *track_read(struct file *file)
|
|
{
|
|
unsigned int library_id, artist_id, album_id, genre_id;
|
|
struct track *track = new struct track;
|
|
gchar *path, *name, *lower;
|
|
|
|
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);
|
|
|
|
name = file_readl(file);
|
|
path = file_readl(file);
|
|
lower = string_lowercase(name);
|
|
track->tr_title = name;
|
|
track->tr_path = path;
|
|
track->tr_lower = lower;
|
|
g_free(name);
|
|
g_free(path);
|
|
g_free(lower);
|
|
|
|
track->tr_library = library_get(library_id);
|
|
track->tr_artist = artist_get(artist_id);
|
|
track->tr_album = album_get(album_id);
|
|
track->tr_genre = genre_get(genre_id);
|
|
return track;
|
|
}
|
|
|
|
static void track_write(struct file *file, struct db_entry *dbe)
|
|
{
|
|
struct track *track = (struct track *)dbe;
|
|
file_writef(file, "%u %u %u %u %u ", track->tr_library->dbe_index,
|
|
track->tr_artist->dbe_index,
|
|
track->tr_album->dbe_index,
|
|
track->tr_genre->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.c_str(),
|
|
track->tr_path.c_str());
|
|
}
|
|
|
|
|
|
static const struct db_ops track_ops = {
|
|
NULL,
|
|
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 std::string &filepath)
|
|
{
|
|
std::string path = filepath.substr(library->li_path.size() + 1);
|
|
gchar *key = __track_key(library, path);
|
|
struct track *track = NULL;
|
|
|
|
if (!db_get(&track_db, key))
|
|
track = TRACK(db_insert(&track_db, new struct track(key)));
|
|
|
|
g_free(key);
|
|
return track;
|
|
}
|
|
|
|
void track_remove(struct track *track)
|
|
{
|
|
db_remove(&track_db, track);
|
|
}
|
|
|
|
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.c_str(), rhs->tr_lower.c_str());
|
|
}
|
|
|
|
const std::string track_path(struct track *track)
|
|
{
|
|
if (track->tr_library)
|
|
return library_file(track->tr_library, track->tr_path);
|
|
return "";
|
|
}
|
|
|
|
void track_played(struct track *track)
|
|
{
|
|
track->tr_count++;
|
|
date_today(&track->tr_date);
|
|
track_db_commit();
|
|
}
|
|
|
|
const std::string track_last_play(struct track *track)
|
|
{
|
|
std::string res = "Never";
|
|
char *buf;
|
|
|
|
if (track->tr_count > 0) {
|
|
buf = date_string(&track->tr_date);
|
|
res = buf;
|
|
g_free(buf);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#ifdef CONFIG_TESTING
|
|
const struct db_ops *test_track_ops() { return &track_ops; }
|
|
#endif /* CONFIG_TESTING */
|