/* * 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_DATA("", "playlist.db", 0); static struct file sys_deck_f = FILE_INIT_DATA("", "deck", 1); static struct file sys_collection_f = FILE_INIT_DATA("", "library.q", 0); static struct file sys_pl_file = FILE_INIT_DATA("", "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)) playlist_generic_remove(playlist, track); else if (!playlist_has(pl_system_get(SYS_PL_HIDDEN), track) && !playlist_has(playlist, track)) playlist_generic_add_front(playlist, track); } playlist_generic_resort(playlist); 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, .pl_can_select = playlist_generic_can_select, .pl_delete = sys_pl_delete_clear, .pl_remove = playlist_generic_remove, .pl_set_random = playlist_generic_set_random, .pl_sort = playlist_generic_sort, .pl_rearrange = playlist_generic_rearrange, }; /* * Hidden tracks playlist operations. */ static bool sys_pl_hidden_add(struct playlist *playlist, struct track *track) { bool ret = playlist_generic_add(pl_system_get(SYS_PL_HIDDEN), track); playlist_generic_remove(pl_system_get(SYS_PL_COLLECTION), track); playlist_generic_remove(pl_system_get(SYS_PL_UNPLAYED), track); playlist_generic_remove(pl_system_get(SYS_PL_MOST_PLAYED), track); playlist_generic_remove(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(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(pl_system_get(SYS_PL_COLLECTION), track); playlist_generic_add(pl_system_get(add_id), track); return ret; } static bool sys_pl_hidden_clear(struct playlist *playlist) { struct track *track; while (playlist_size(playlist) > 0) { track = playlist_at(playlist, 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_remove = sys_pl_hidden_remove, .pl_set_random = playlist_generic_set_random, .pl_sort = playlist_generic_sort, .pl_rearrange = playlist_generic_rearrange, }; /* * Queued tracks playlist operations. */ static bool sys_pl_queued_load() { struct playlist *playlist = pl_system_get(SYS_PL_QUEUED); unsigned int num, i, flags = 0; if (!file_open(&sys_deck_f, OPEN_READ)) return true; num = file_readu(&sys_deck_f); for (i = 0; i < num; i++) { flags = file_readu(&sys_deck_f); flags &= PL_RANDOM; if (i == 0) playlist_generic_set_random(playlist, flags == PL_RANDOM); playlist_generic_load(playlist, &sys_deck_f, PL_SAVE_TRACKS); } file_close(&sys_deck_f); file_remove(&sys_deck_f); return true; } static bool sys_pl_queued_delete(struct playlist *playlist) { playlist_generic_set_random(playlist, false); playlist_clear_sort(playlist); return sys_pl_delete_clear(playlist); } static bool sys_pl_queued_remove(struct playlist *playlist, struct track *track) { bool ret = playlist_generic_remove(playlist, track); if (playlist_size(playlist) == 0) sys_pl_queued_delete(playlist); return ret; } static struct playlist_ops queued_ops = { .pl_add = playlist_generic_add, .pl_can_select = playlist_generic_can_select, .pl_delete = sys_pl_queued_delete, .pl_remove = sys_pl_queued_remove, .pl_set_random = playlist_generic_set_random, .pl_sort = playlist_generic_sort, .pl_rearrange = playlist_generic_rearrange, }; /* * 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)) { playlist_generic_load(playlist, &sys_collection_f, PL_SAVE_FLAGS); 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_remove = sys_pl_hidden_add, .pl_set_random = playlist_generic_set_random, .pl_sort = playlist_generic_sort, .pl_rearrange = playlist_generic_rearrange, }; /* * History playlist operations. */ static bool sys_pl_history_add(struct playlist *playlist, struct track *track) { playlist_generic_add_front(playlist, track); playlist_current_set(playlist, 0); return true; } static struct playlist_ops history_ops = { .pl_add = sys_pl_history_add, }; /* * Unplayed, Most Played, and Least Played tracks playlist operations. */ static struct playlist_ops dynamic_ops = { .pl_can_select = playlist_generic_can_select, .pl_set_random = playlist_generic_set_random, .pl_sort = playlist_generic_sort, .pl_rearrange = playlist_generic_rearrange, }; #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; n = file_readu(&sys_file); for (i = 0; i < n; i++) { file_readu(&sys_file); name = file_readl(&sys_file); if (string_match(name, "Banned")) { g_free(name); name = g_strdup("Hidden"); } playlist = pl_system_lookup(name); g_free(name); if (playlist) playlist_generic_load(playlist, &sys_file, PL_SAVE_TRACKS); } file_close(&sys_file); file_remove(&sys_file); return true; } static bool __sys_pl_load_new() { struct playlist *playlist; unsigned int i, n, load; 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; } n = file_readu(&sys_pl_file); for (i = 0; i < n; i++) { load = PL_SAVE_METADATA; name = file_readl(&sys_pl_file); if (string_match(name, "Banned")) { g_free(name); name = g_strdup("Hidden"); } playlist = pl_system_lookup(name); g_free(name); switch (i) { case SYS_PL_FAVORITES: case SYS_PL_HIDDEN: case SYS_PL_QUEUED: load = PL_SAVE_ALL; } playlist_generic_load(playlist, &sys_pl_file, load); } file_close(&sys_pl_file); return true; } static void pl_system_save(void) { struct playlist *playlist; unsigned int i, save; 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++) { save = PL_SAVE_METADATA; playlist = pl_system_get(i); switch (i) { case SYS_PL_FAVORITES: case SYS_PL_HIDDEN: case SYS_PL_QUEUED: save = PL_SAVE_ALL; } file_writef(&sys_pl_file, "%s\n", playlist->pl_name); playlist_generic_save(playlist, &sys_pl_file, save); } 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++) playlist_generic_update(pl_system_get(i), track); sys_pl_update(pl_system_lookup("Unplayed")); sys_pl_update(pl_system_lookup("Most Played")); sys_pl_update(pl_system_lookup("Least Played")); } static void pl_system_selected(struct track *track) { unsigned int i; sys_pl_queued_remove(pl_system_get(SYS_PL_QUEUED), track); for (i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) playlist_generic_update(pl_system_get(i), track); } 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, .pl_selected = pl_system_selected, }; void pl_system_init(void) { 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: playlist_generic_init(playlist, 0); 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, 4, COMPARE_ARTIST, COMPARE_YEAR, COMPARE_ALBUM, COMPARE_TRACK); break; } } } void pl_system_deinit() { for (unsigned int i = 0; i < SYS_PL_NUM_PLAYLISTS; i++) playlist_generic_deinit(pl_system_get(i)); } void pl_system_new_track(struct track *track) { playlist_generic_add(pl_system_lookup("Collection"), track); playlist_generic_add(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(pl_system_get(i), track); }