ocarina/core/collection.c

284 lines
5.7 KiB
C

/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <core/collection.h>
#include <core/idle.h>
#include <core/playlist.h>
#include <glib.h>
#include <stdlib.h>
#include <unistd.h>
static struct file c_file = FILE_INIT("library.q", 0, 0);
static struct queue c_queue;
struct scan_data {
struct library *sd_lib;
gchar *sd_path;
};
static void __scan_dir(void *);
#ifdef CONFIG_TESTING
bool test_collection_error = false;
static inline int __g_access(const gchar *path)
{
if (test_collection_error)
return -1;
return g_access(path, F_OK);
}
#else
#define __g_access(path) g_access(path, F_OK)
#endif /* CONFIG_TESTING */
static void __scan_dir_later(struct library *library, const gchar *dir)
{
struct scan_data *data = g_malloc(sizeof(struct scan_data));
data->sd_lib = library;
data->sd_path = g_strdup(dir);
/* data is freed by __scan_dir() */
idle_schedule(__scan_dir, data);
}
static void __scan_path(struct scan_data *scan, const gchar *name)
{
gchar *path = g_strdup_printf("%s/%s", scan->sd_path, name);
struct track *track;
if (g_file_test(path, G_FILE_TEST_IS_DIR))
__scan_dir_later(scan->sd_lib, path);
else {
track = track_add(scan->sd_lib, path);
if (track)
queue_add(&c_queue, track);
}
g_free(path);
}
static void __scan_dir(void *data)
{
struct scan_data *scan = data;
const char *name;
GDir *dir;
dir = g_dir_open(scan->sd_path, 0, NULL);
if (dir == NULL)
goto out;
name = g_dir_read_name(dir);
while (name != NULL) {
__scan_path(scan, name);
name = g_dir_read_name(dir);
}
g_dir_close(dir);
track_db_commit();
out:
/* Allocated by __scan_dir_later() */
g_free(scan->sd_path);
g_free(scan);
}
static void __validate_library(void *data)
{
struct library *library = data;
struct db_entry *dbe, *next;
struct track *track;
int access;
gchar *path;
db_for_each(dbe, next, track_db_get()) {
track = TRACK(dbe);
if (track->tr_library != library)
continue;
path = track_path(track);
access = __g_access(path);
g_free(path);
if (access < 0) {
if (collection_check_library(library) < 0)
return;
queue_remove_all(&c_queue, track);
track_remove(track);
}
}
}
void __collection_init_idle(void *data)
{
struct db_entry *track, *next;
unsigned int i, n = 0;
int field, ascending;
db_for_each(track, next, track_db_get()) {
if (TRACK(track)->tr_library->li_enabled &&
!playlist_has(PL_HIDDEN, TRACK(track)))
queue_add(&c_queue, TRACK(track));
}
queue_unset_flag(&c_queue, Q_ADD_FRONT);
if (file_open(&c_file, OPEN_READ))
file_readf(&c_file, "%u %u", &c_queue.q_flags, &n);
for (i = 0; i < n; i++) {
file_readf(&c_file, "%u %d", &field, &ascending);
queue_sort(&c_queue, field + 1, (i == 0) ? true : false);
if (ascending == false)
queue_sort(&c_queue, field + 1, false);
}
file_close(&c_file);
if (!c_queue.q_sort) {
queue_sort(&c_queue, COMPARE_ARTIST, true);
queue_sort(&c_queue, COMPARE_YEAR, false);
queue_sort(&c_queue, COMPARE_TRACK, false);
}
queue_set_flag(&c_queue, Q_SAVE_SORT);
queue_set_flag(&c_queue, Q_SAVE_FLAGS);
collection_update_all();
}
/*
* External API begins here
*/
void collection_init(struct queue_ops *ops)
{
queue_init(&c_queue, Q_ENABLED | Q_REPEAT | Q_ADD_FRONT, ops);
idle_schedule(__collection_init_idle, NULL);
}
void collection_deinit()
{
queue_deinit(&c_queue);
}
void collection_save(struct queue *queue, enum queue_flags flag)
{
GSList *cur = c_queue.q_sort;
int field;
if (&c_queue != queue)
return;
if (!file_open(&c_file, OPEN_WRITE))
return;
file_writef(&c_file, "%u %u", c_queue.q_flags, g_slist_length(cur));
while (cur) {
field = GPOINTER_TO_INT(cur->data);
file_writef(&c_file, " %u %d", abs(field) - 1, field > 0);
cur = g_slist_next(cur);
}
file_writef(&c_file, "\n");
file_close(&c_file);
}
struct library *collection_add(const gchar *path)
{
struct library *library = NULL;
if (g_file_test(path, G_FILE_TEST_IS_DIR) == false)
return library;
library = library_find(path);
if (library)
collection_update(library);
return library;
}
void collection_remove(struct library *library)
{
if (library) {
collection_set_enabled(library, false);
track_remove_all(library);
library_remove(library);
}
}
void collection_update(struct library *library)
{
if (!library)
return;
idle_schedule(__validate_library, library);
__scan_dir_later(library, library->li_path);
}
void collection_update_all()
{
struct db_entry *library, *next;
db_for_each(library, next, library_db_get())
collection_update(LIBRARY(library));
}
bool collection_ban(struct track *track)
{
bool ret = playlist_add(PL_HIDDEN, track);
if (ret)
queue_remove_all(&c_queue, track);
return ret;
}
bool collection_unban(struct track *track)
{
bool ret = playlist_remove(PL_HIDDEN, track);
if (ret)
queue_add(&c_queue, track);
return ret;
}
void collection_set_enabled(struct library *library, bool enabled)
{
struct db_entry *dbe, *next;
struct track *track;
if (!library || (library->li_enabled == enabled))
return;
if (enabled && (collection_check_library(library) < 0))
return;
library_set_enabled(library, enabled);
queue_set_flag(&c_queue, Q_ADD_FRONT);
db_for_each(dbe, next, track_db_get()) {
track = TRACK(dbe);
if (track->tr_library == library) {
if (enabled) {
if (!playlist_has(PL_HIDDEN, track))
queue_add(&c_queue, track);
} else
queue_remove_all(&c_queue, track);
}
}
queue_unset_flag(&c_queue, Q_ADD_FRONT);
queue_resort(&c_queue);
}
int collection_check_library(struct library *library)
{
int ret = __g_access(library->li_path);
if (ret < 0) {
g_warning("Can't access library at %s/\n", library->li_path);
collection_set_enabled(library, false);
}
return ret;
}
struct queue *collection_get_queue()
{
return &c_queue;
}