ocarina/core/playlists/library.c
Anna Schumaker ef8a764780 core/playlists: Allocate artist and library playlists during startup
Rather than waiting for them to load with an idle task.  This speeds up
Ocarina startup dramatically, since playlists can be added to the UI
with the correct size instead of needing extra callbacks to update the
size each time a track is added.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2016-09-26 08:07:36 -04:00

328 lines
7.7 KiB
C

/*
* Copyright 2016 (c) Anna Schumaker.
*/
#include <core/idle.h>
#include <core/playlists/artist.h>
#include <core/playlists/library.h>
#include <core/playlists/system.h>
#include <unistd.h>
struct scan_data {
struct library *sd_library;
gchar *sd_path;
};
static bool __lib_pl_scan_dir(void *);
static struct queue_ops *lib_ops = NULL;
static struct file lib_file = FILE_INIT("playlist.library", 0);
static struct playlist *__lib_pl_alloc(struct library *library)
{
struct playlist *playlist = g_malloc(sizeof(struct playlist));
playlist->pl_name = library->li_path;
playlist->pl_type = PL_LIBRARY;
playlist_generic_init(playlist, Q_REPEAT, lib_ops);
return playlist;
}
static void __lib_pl_free(struct playlist *playlist)
{
if (playlist) {
queue_deinit(&playlist->pl_queue);
g_free(playlist);
}
}
static bool __lib_pl_add(void *data)
{
struct playlist *playlist = (struct playlist *)data;
struct library *library = library_lookup(playlist->pl_name);
struct db_entry *dbe, *next;
queue_set_flag(&playlist->pl_queue, Q_ADD_FRONT);
db_for_each(dbe, next, track_db_get()) {
if (TRACK(dbe)->tr_library == library)
queue_add(&playlist->pl_queue, TRACK(dbe));
}
queue_unset_flag(&playlist->pl_queue, Q_ADD_FRONT);
return true;
}
static struct playlist *__lib_pl_lookup(const gchar *name)
{
struct library *library = library_lookup(name);
return library ? library->li_playlist : NULL;
}
static bool __lib_pl_load(void *data)
{
struct playlist *playlist;
unsigned int i, n;
gchar *name;
if (!file_open(&lib_file, OPEN_READ))
return true;
file_readf(&lib_file, "%u\n", &n);
for (i = 0; i < n; i++) {
name = file_readl(&lib_file);
playlist = __lib_pl_lookup(name);
if (!playlist)
continue;
queue_load_flags(&playlist->pl_queue, &lib_file, true);
queue_iter_set(&playlist->pl_queue, &playlist->pl_queue.q_cur,
playlist->pl_queue.q_cur.it_pos);
g_free(name);
}
file_close(&lib_file);
return true;
}
static void __lib_pl_scan_dir_idle(struct library *library, const gchar *path)
{
struct scan_data *scan = g_malloc(sizeof(struct scan_data));
scan->sd_library = library;
scan->sd_path = g_strdup(path);
/* scan data is freed by __lib_pl_scan_dir() */
idle_schedule(IDLE_SYNC, __lib_pl_scan_dir, scan);
}
static void __lib_pl_read_path(struct scan_data *scan, const gchar *name)
{
gchar *path = g_build_filename(scan->sd_path, name, NULL);
struct playlist *playlist = scan->sd_library->li_playlist;
struct track *track;
if (g_file_test(path, G_FILE_TEST_IS_DIR))
__lib_pl_scan_dir_idle(scan->sd_library, path);
else {
track = track_add(scan->sd_library, path);
if (track) {
queue_add(&playlist->pl_queue, track);
pl_system_new_track(track);
pl_artist_new_track(track);
}
}
}
static bool __lib_pl_scan_dir(void *data)
{
struct scan_data *scan = (struct scan_data *)data;
const gchar *name;
GDir *dir;
dir = g_dir_open(scan->sd_path, 0, NULL);
if (!dir)
goto out;
name = g_dir_read_name(dir);
while (name != NULL) {
__lib_pl_read_path(scan, name);
name = g_dir_read_name(dir);
}
g_dir_close(dir);
track_db_commit();
out:
/* Allocated by __lib_pl_scan_dir_idle() */
g_free(scan->sd_path);
g_free(scan);
return true;
}
static bool __lib_pl_update(void *data)
{
struct playlist *playlist = (struct playlist *)data;
struct library *library = library_lookup(playlist->pl_name);
struct db_entry *dbe, *next;
gchar *path;
db_for_each(dbe, next, track_db_get()) {
if (TRACK(dbe)->tr_library != library)
continue;
path = track_path(TRACK(dbe));
if (g_access(path, F_OK) < 0) {
pl_system_delete_track(TRACK(dbe));
pl_artist_delete_track(TRACK(dbe));
queue_remove_all(&playlist->pl_queue, TRACK(dbe));
track_remove(TRACK(dbe));
}
g_free(path);
}
track_db_commit();
__lib_pl_scan_dir_idle(library, library->li_path);
return true;
}
static void pl_library_save(void)
{
struct db_entry *dbe, *next;
struct playlist *playlist;
if (!file_open(&lib_file, OPEN_WRITE))
return;
file_writef(&lib_file, "%u\n", library_db_get()->db_size);
db_for_each(dbe, next, library_db_get()) {
playlist = LIBRARY(dbe)->li_playlist;
file_writef(&lib_file, "%s\n", playlist->pl_name);
queue_save_flags(&playlist->pl_queue, &lib_file, true);
}
file_close(&lib_file);
}
static struct queue *pl_library_get_queue(const gchar *name)
{
struct playlist *playlist = __lib_pl_lookup(name);
return playlist ? &playlist->pl_queue : NULL;
}
static unsigned int pl_library_get_id(const gchar *name)
{
struct library *library = library_find(name);
return library ? library->li_dbe.dbe_index : -1;
}
static gchar *pl_library_get_name(unsigned int id)
{
struct library *library = LIBRARY(db_at(library_db_get(), id));
return library ? g_strdup(library->li_path) : NULL;
}
static bool pl_library_can_select(const gchar *name)
{
struct playlist *playlist = __lib_pl_lookup(name);
return playlist ? playlist_generic_can_select(playlist) : false;
}
static bool pl_library_new(const gchar *name)
{
struct library *library;
if (__lib_pl_lookup(name) || !g_file_test(name, G_FILE_TEST_IS_DIR))
return false;
library = library_find(name);
library->li_playlist = __lib_pl_alloc(library);
__lib_pl_scan_dir_idle(library, name);
return true;
}
static bool pl_library_delete(const gchar *name)
{
struct playlist *playlist = __lib_pl_lookup(name);
struct library *library = library_lookup(name);
struct queue_iter it;
if (!library)
return false;
queue_for_each(&playlist->pl_queue, &it) {
pl_system_delete_track(queue_iter_val(&it));
pl_artist_delete_track(queue_iter_val(&it));
}
__lib_pl_free(playlist);
track_remove_all(library);
library_remove(library);
pl_library_save();
return true;
}
static bool pl_library_add_rm(const gchar *name, struct track *track)
{
return false;
}
static void pl_library_update(const gchar *name)
{
struct playlist *playlist = __lib_pl_lookup(name);
if (playlist)
idle_schedule(IDLE_SYNC, __lib_pl_update, playlist);
}
static void pl_library_set_flag(const gchar *name, enum queue_flags flag,
bool enabled)
{
struct playlist *playlist = __lib_pl_lookup(name);
playlist_generic_set_flag(playlist, flag, enabled);
pl_library_save();
}
static void pl_library_sort(const gchar *name, enum compare_t sort, bool reset)
{
struct playlist *playlist = __lib_pl_lookup(name);
playlist_generic_sort(playlist, sort, reset);
pl_library_save();
}
static struct track *pl_library_next(const gchar *name)
{
struct playlist *playlist = __lib_pl_lookup(name);
struct track *track = playlist_generic_next(playlist);
pl_library_save();
return track;
}
struct playlist_type pl_library = {
.pl_save = pl_library_save,
.pl_get_queue = pl_library_get_queue,
.pl_get_id = pl_library_get_id,
.pl_get_name = pl_library_get_name,
.pl_can_select = pl_library_can_select,
.pl_new = pl_library_new,
.pl_delete = pl_library_delete,
.pl_add_track = pl_library_add_rm,
.pl_remove_track = pl_library_add_rm,
.pl_update = pl_library_update,
.pl_set_flag = pl_library_set_flag,
.pl_sort = pl_library_sort,
.pl_next = pl_library_next,
};
void pl_library_init(struct queue_ops *ops)
{
struct db_entry *dbe, *next;
struct playlist *playlist;
lib_ops = ops;
db_for_each(dbe, next, library_db_get()) {
playlist = __lib_pl_alloc(LIBRARY(dbe));
LIBRARY(dbe)->li_playlist = playlist;
idle_schedule(IDLE_SYNC, __lib_pl_add, playlist);
pl_library_update(playlist->pl_name);
}
idle_schedule(IDLE_SYNC, __lib_pl_load, NULL);
}
void pl_library_deinit()
{
struct db_entry *dbe, *next;
struct playlist *playlist;
db_for_each(dbe, next, library_db_get()) {
playlist = LIBRARY(dbe)->li_playlist;
LIBRARY(dbe)->li_playlist = NULL;
__lib_pl_free(playlist);
}
}