/* * Copyright 2016 (c) Anna Schumaker. */ #include #include #include static struct playlist *pl_system_lookup(const gchar *); static struct playlist *pl_system_get(unsigned int); static void pl_system_save(); static struct file sys_file = FILE_INIT("playlist.db", 0); static struct file sys_deck_f = FILE_INIT("deck", 1); static struct file sys_collection_f = FILE_INIT("library.q", 0); static struct file sys_pl_file = FILE_INIT("playlist.system", 0); /* * Generic system playlist operations. */ static bool sys_pl_delete_clear(struct playlist *playlist) { playlist_generic_clear(playlist); pl_system_save(); return false; } static bool sys_pl_update_check(struct playlist *playlist, struct track *track) { switch (playlist->pl_id) { case SYS_PL_UNPLAYED: return track->tr_count == 0; case SYS_PL_LEAST_PLAYED: return track->tr_count <= track_db_average_plays() && track->tr_count > 0; case SYS_PL_MOST_PLAYED: return track->tr_count > track_db_average_plays(); } return true; } static bool sys_pl_update_func(void *data) { struct playlist *playlist = (struct playlist *)data; struct db_entry *dbe, *next; db_for_each(dbe, next, track_db_get()) { struct track *track = TRACK(dbe); if (sys_pl_update_check(playlist, track) && !queue_has(&pl_system_get(SYS_PL_HIDDEN)->pl_queue, track)) playlist_generic_add_track_front(playlist, track); else playlist_generic_remove_track(playlist, track); } queue_resort(&playlist->pl_queue); return true; } static void sys_pl_update(struct playlist *playlist) { switch (playlist->pl_id) { case SYS_PL_COLLECTION: case SYS_PL_UNPLAYED: case SYS_PL_LEAST_PLAYED: case SYS_PL_MOST_PLAYED: idle_schedule(IDLE_SYNC, sys_pl_update_func, playlist); default: break; } } /* * Favorite tracks playlist operations. */ static struct playlist_ops favorites_ops = { .pl_add = playlist_generic_add_track, .pl_can_select = playlist_generic_can_select, .pl_delete = sys_pl_delete_clear, .pl_next = playlist_generic_next, .pl_remove = playlist_generic_remove_track, .pl_set_flag = playlist_generic_set_flag, .pl_sort = playlist_generic_sort, }; /* * Hidden tracks playlist operations. */ static bool sys_pl_hidden_add(struct playlist *playlist, struct track *track) { bool ret = playlist_generic_add_track(pl_system_get(SYS_PL_HIDDEN), track); playlist_generic_remove_track(pl_system_get(SYS_PL_COLLECTION), track); playlist_generic_remove_track(pl_system_get(SYS_PL_UNPLAYED), track); playlist_generic_remove_track(pl_system_get(SYS_PL_MOST_PLAYED), track); playlist_generic_remove_track(pl_system_get(SYS_PL_LEAST_PLAYED), track); return ret; } static bool sys_pl_hidden_remove(struct playlist *playlist, struct track *track) { bool ret = playlist_generic_remove_track(playlist, track); unsigned int average = track_db_average_plays(); unsigned int add_id = SYS_PL_LEAST_PLAYED; add_id = (track->tr_count == 0) ? SYS_PL_UNPLAYED : add_id; add_id = (track->tr_count > average) ? SYS_PL_MOST_PLAYED : add_id; playlist_generic_add_track(pl_system_get(SYS_PL_COLLECTION), track); playlist_generic_add_track(pl_system_get(add_id), track); return ret; } static bool sys_pl_hidden_clear(struct playlist *playlist) { struct track *track; while (queue_size(&playlist->pl_queue) > 0) { track = queue_at(&playlist->pl_queue, 0); sys_pl_hidden_remove(playlist, track); } pl_system_save(); return false; } static struct playlist_ops hidden_ops = { .pl_add = sys_pl_hidden_add, .pl_can_select = playlist_generic_can_select, .pl_delete = sys_pl_hidden_clear, .pl_next = playlist_generic_next, .pl_remove = sys_pl_hidden_remove, .pl_set_flag = playlist_generic_set_flag, .pl_sort = playlist_generic_sort, }; /* * Queued tracks playlist operations. */ static bool sys_pl_queued_load() { struct queue *queue = &pl_system_get(SYS_PL_QUEUED)->pl_queue; unsigned int num, i, flags = 0; if (!file_open(&sys_deck_f, OPEN_READ)) return true; file_readf(&sys_deck_f, "%u", &num); for (i = 0; i < num; i++) { file_readf(&sys_deck_f, "%u", &flags); flags &= Q_VALID_FLAGS; if (i == 0) queue->q_flags |= flags; queue_load_tracks(queue, &sys_deck_f); } file_close(&sys_deck_f); file_remove(&sys_deck_f); return true; } static struct track *sys_pl_queued_next(struct playlist *playlist) { struct track *track = playlist_generic_next(playlist); unsigned int pos = playlist->pl_queue.q_cur.it_pos; queue_iter_prev(&playlist->pl_queue.q_cur); queue_remove(&playlist->pl_queue, pos); return track; } static struct playlist_ops queued_ops = { .pl_add = playlist_generic_add_track, .pl_can_select = playlist_generic_can_select, .pl_delete = sys_pl_delete_clear, .pl_next = sys_pl_queued_next, .pl_remove = playlist_generic_remove_track, .pl_set_flag = playlist_generic_set_flag, .pl_sort = playlist_generic_sort, }; /* * Collection playlist operations. */ static bool sys_pl_collection_load() { struct playlist *playlist = pl_system_get(SYS_PL_COLLECTION); if (file_open(&sys_collection_f, OPEN_READ)) { queue_load_flags(&playlist->pl_queue, &sys_collection_f, false); file_close(&sys_collection_f); file_remove(&sys_collection_f); } return true; } static struct playlist_ops collection_ops = { .pl_can_select = playlist_generic_can_select, .pl_next = playlist_generic_next, .pl_remove = sys_pl_hidden_add, .pl_set_flag = playlist_generic_set_flag, .pl_sort = playlist_generic_sort, }; /* * History playlist operations. */ static bool sys_pl_history_add(struct playlist *playlist, struct track *track) { queue_add_front(&playlist->pl_queue, track); queue_iter_set(&playlist->pl_queue, &playlist->pl_queue.q_cur, 0); return true; } static struct playlist_ops history_ops = { .pl_add = sys_pl_history_add, .pl_next = playlist_generic_next, }; /* * Unplayed, Most Played, and Least Played tracks playlist operations. */ static struct playlist_ops dynamic_ops = { .pl_can_select = playlist_generic_can_select, .pl_next = playlist_generic_next, .pl_set_flag = playlist_generic_set_flag, .pl_sort = playlist_generic_sort, }; #define SYS_PLAYLIST(id, name, ops) \ [id] = DEFINE_PLAYLIST(PL_SYSTEM, name, id, ops) static struct playlist sys_playlists[SYS_PL_NUM_PLAYLISTS] = { SYS_PLAYLIST(SYS_PL_FAVORITES, "Favorites", &favorites_ops), SYS_PLAYLIST(SYS_PL_HIDDEN, "Hidden", &hidden_ops), SYS_PLAYLIST(SYS_PL_QUEUED, "Queued Tracks", &queued_ops), SYS_PLAYLIST(SYS_PL_COLLECTION, "Collection", &collection_ops), SYS_PLAYLIST(SYS_PL_HISTORY, "History", &history_ops), SYS_PLAYLIST(SYS_PL_UNPLAYED, "Unplayed", &dynamic_ops), SYS_PLAYLIST(SYS_PL_MOST_PLAYED, "Most Played", &dynamic_ops), SYS_PLAYLIST(SYS_PL_LEAST_PLAYED, "Least Played", &dynamic_ops), }; static bool __sys_pl_update_save() { pl_system_save(); return true; } static bool __sys_pl_load() { struct playlist *playlist; unsigned int i, n; gchar *name; if (!file_open(&sys_file, OPEN_READ)) return true; file_readf(&sys_file, "%u\n", &n); for (i = 0; i < n; i++) { file_readf(&sys_file, "%*u %m[^\n]\n", &name); if (string_match(name, "Banned")) { g_free(name); name = g_strdup("Hidden"); } playlist = pl_system_lookup(name); g_free(name); if (playlist) queue_load_tracks(&playlist->pl_queue, &sys_file); } file_close(&sys_file); file_remove(&sys_file); return true; } static bool __sys_pl_load_new() { struct playlist *playlist; unsigned int i, n; gchar *name; if (!file_open(&sys_pl_file, OPEN_READ)) { __sys_pl_load(); sys_pl_collection_load(); sys_pl_queued_load(); __sys_pl_update_save(); return true; } file_readf(&sys_pl_file, "%u\n", &n); for (i = 0; i < n; i++) { name = file_readl(&sys_pl_file); playlist = pl_system_lookup(name); g_free(name); queue_load_flags(&playlist->pl_queue, &sys_pl_file, true); switch (i) { case SYS_PL_FAVORITES: case SYS_PL_HIDDEN: case SYS_PL_QUEUED: queue_load_tracks(&playlist->pl_queue, &sys_pl_file); default: break; } } file_close(&sys_pl_file); return true; } static void pl_system_save(void) { struct playlist *playlist; unsigned int i; if (!file_open(&sys_pl_file, OPEN_WRITE)) return; file_writef(&sys_pl_file, "%u\n", SYS_PL_NUM_PLAYLISTS); for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) { playlist = pl_system_get(i); file_writef(&sys_pl_file, "%s\n", playlist->pl_name); queue_save_flags(&playlist->pl_queue, &sys_pl_file, true); switch (i) { case SYS_PL_FAVORITES: case SYS_PL_HIDDEN: case SYS_PL_QUEUED: queue_save_tracks(&playlist->pl_queue, &sys_pl_file); default: break; } } file_close(&sys_pl_file); } static struct playlist *pl_system_lookup(const gchar *name) { unsigned int i; for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) { if (string_match(name, pl_system_get(i)->pl_name)) return pl_system_get(i); } return NULL; } static struct playlist *pl_system_get(unsigned int id) { return (id < SYS_PL_NUM_PLAYLISTS) ? &sys_playlists[id] : NULL; } static void pl_system_played(struct track *track) { unsigned int i; for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) queue_updated(&pl_system_get(i)->pl_queue, track); sys_pl_update(pl_system_lookup("Unplayed")); sys_pl_update(pl_system_lookup("Most Played")); sys_pl_update(pl_system_lookup("Least Played")); } struct playlist_type pl_system = { .pl_save = pl_system_save, .pl_lookup = pl_system_lookup, .pl_get = pl_system_get, .pl_played = pl_system_played, }; void pl_system_init(struct queue_ops *ops) { struct playlist *playlist; unsigned int i; idle_schedule(IDLE_SYNC, __sys_pl_load_new, NULL); for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) { playlist = pl_system_get(i); switch (i) { case SYS_PL_QUEUED: case SYS_PL_HISTORY: queue_init(&playlist->pl_queue, 0, ops, playlist); break; case SYS_PL_COLLECTION: case SYS_PL_UNPLAYED: case SYS_PL_MOST_PLAYED: case SYS_PL_LEAST_PLAYED: sys_pl_update(playlist); case SYS_PL_FAVORITES: case SYS_PL_HIDDEN: playlist_generic_init(playlist, 0, ops); break; } } } void pl_system_deinit() { for (unsigned int i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) queue_deinit(&pl_system_get(i)->pl_queue); } void pl_system_new_track(struct track *track) { playlist_generic_add_track(pl_system_lookup("Collection"), track); playlist_generic_add_track(pl_system_lookup("Unplayed"), track); } void pl_system_delete_track(struct track *track) { for (unsigned int i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) playlist_generic_remove_track(pl_system_get(i), track); }