ocarina/core/playlists/library.c
Anna Schumaker b17585237a core/playlist: Add a playlist_played() function
This is used to notify when tracks have been played so dynamic playlists
can be updated, and so the model can display the correct playcount.

The old system playlist tests are mostly unnecessary at this point, so I
remove them as part of this patch.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2017-04-28 09:40:02 -04:00

304 lines
6.9 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 <core/playlists/user.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_ops pl_library_ops;
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->pl_id = library_index(library);
playlist->pl_ops = &pl_library_ops;
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;
db_for_each(dbe, next, track_db_get()) {
if (TRACK(dbe)->tr_library == library)
queue_add_front(&playlist->pl_queue, TRACK(dbe));
}
queue_resort(&playlist->pl_queue);
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);
g_free(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);
}
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);
}
}
g_free(path);
}
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 bool pl_library_delete(struct playlist *playlist)
{
struct library *library = library_lookup(playlist->pl_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));
pl_user_delete_track(queue_iter_val(&it));
}
__lib_pl_free(playlist);
track_remove_all(library);
library_remove(library);
return true;
}
static struct playlist_ops pl_library_ops = {
.pl_can_select = playlist_generic_can_select,
.pl_delete = pl_library_delete,
.pl_next = playlist_generic_next,
.pl_set_flag = playlist_generic_set_flag,
.pl_sort = playlist_generic_sort,
};
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 playlist *pl_library_lookup(const gchar *name)
{
return __lib_pl_lookup(name);
}
static struct playlist *pl_library_get(unsigned int id)
{
struct library *library = LIBRARY(db_at(library_db_get(), id));
return library ? library->li_playlist : NULL;
}
static struct playlist *pl_library_new(const gchar *name)
{
struct library *library;
if (__lib_pl_lookup(name) || !g_file_test(name, G_FILE_TEST_IS_DIR))
return NULL;
library = library_find(name);
library->li_playlist = __lib_pl_alloc(library);
__lib_pl_scan_dir_idle(library, name);
return library->li_playlist;
}
static void pl_library_played(struct track *track)
{
struct library *library = track->tr_library;
struct playlist *playlist = (struct playlist *)library->li_playlist;
if (playlist)
queue_updated(&playlist->pl_queue, track);
}
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);
}
struct playlist_type pl_library = {
.pl_save = pl_library_save,
.pl_lookup = pl_library_lookup,
.pl_get = pl_library_get,
.pl_new = pl_library_new,
.pl_played = pl_library_played,
.pl_update = pl_library_update,
};
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);
}
}