/* * Copyright 2013 (c) Anna Schumaker. */ #include #include #include #include static GHashTable *playlist_db = NULL; static struct file playlist_f = FILE_INIT("playlist.db", 0, 0); static struct queue playlist_q; static enum playlist_t playlist_cur; static const gchar *playlist_names[2] = { "Favorites", "Banned" }; static struct queue_ops *playlist_ops = NULL; static inline struct queue *__playlist_lookup(const gchar *name) { return g_hash_table_lookup(playlist_db, name); } static inline struct queue *__playlist_alloc(const gchar *name) { struct queue *queue = __playlist_lookup(name); if (!queue) { queue = g_malloc(sizeof(struct queue)); queue_init(queue, Q_ENABLED | Q_REPEAT, playlist_ops); g_hash_table_insert(playlist_db, (gpointer)name, queue); } return queue; } static inline bool __playlist_is_static(enum playlist_t plist) { return (plist == PL_FAVORITED) || (plist == PL_HIDDEN); } static inline bool __playlist_is_dynamic(enum playlist_t plist) { return !__playlist_is_static(plist); } static void __playlist_fill_dynamic(enum playlist_t plist) { unsigned int count = 0, average = 0; struct db_entry *track, *next; if (plist == PL_MOST_PLAYED || plist == PL_LEAST_PLAYED) average = track_db_average_plays(); db_for_each(track, next, track_db_get()) { count = TRACK(track)->tr_count; if (plist == PL_UNPLAYED && count == 0) queue_add(&playlist_q, TRACK(track)); else if (plist == PL_MOST_PLAYED && count > average) queue_add(&playlist_q, TRACK(track)); else if (plist == PL_LEAST_PLAYED && count <= average && count > 0) queue_add(&playlist_q, TRACK(track)); } } static void __pl_system_load_queue() { struct queue *queue; unsigned int valid; gchar *name; file_readf(&playlist_f, "%u ", &valid); if (!valid) return; name = file_readl(&playlist_f); queue = __playlist_alloc(name); queue_load_tracks(queue, &playlist_f); } static void __pl_system_save_queue(gpointer key, gpointer value, gpointer data) { struct queue *queue = (struct queue *)value; file_writef(&playlist_f, "1 %s\n", (gchar *)key); queue_save_tracks(queue, &playlist_f); file_writef(&playlist_f, "\n"); } static void __pl_system_save() { if (!file_open(&playlist_f, OPEN_WRITE)) return; file_writef(&playlist_f, "%u\n", g_hash_table_size(playlist_db)); g_hash_table_foreach(playlist_db, __pl_system_save_queue, NULL); file_close(&playlist_f); } static bool __pl_system_init_idle() { unsigned int i, n; if (!file_open(&playlist_f, OPEN_READ)) return true; file_readf(&playlist_f, "%u\n", &n); for (i = 0; i < n; i++) __pl_system_load_queue(); file_close(&playlist_f); return true; } static void __pl_system_init() { playlist_db = g_hash_table_new(g_str_hash, g_str_equal); idle_schedule(IDLE_SYNC, __pl_system_init_idle, NULL); } static int __pl_system_deinit_queue(gpointer key, gpointer value, gpointer data) { struct queue *queue = (struct queue *)value; queue_deinit(queue); g_free(queue); return true; } static void __pl_system_deinit() { g_hash_table_foreach_remove(playlist_db, __pl_system_deinit_queue, NULL); g_hash_table_destroy(playlist_db); } static bool __pl_system_add(const gchar *name, struct track *track) { struct queue *queue = __playlist_alloc(name); if (queue_has(queue, track)) return false; queue_add(queue, track); __pl_system_save(); return true; } static bool __pl_system_remove(const gchar *name, struct track *track) { struct queue *queue = __playlist_lookup(name); if (!queue) return false; if (!queue_remove_all(queue, track)) return false; __pl_system_save(); return true; } static struct queue *__pl_system_get_queue(const gchar *name) { return __playlist_lookup(name); } struct playlist_type pl_system = { .pl_get_queue = __pl_system_get_queue, .pl_add_track = __pl_system_add, .pl_remove_track = __pl_system_remove, }; void playlist_init(struct queue_ops *ops) { queue_init(&playlist_q, Q_ENABLED | Q_REPEAT, ops); queue_sort(&playlist_q, COMPARE_ARTIST, true); queue_sort(&playlist_q, COMPARE_YEAR, false); queue_sort(&playlist_q, COMPARE_TRACK, false); queue_set_flag(&playlist_q, Q_NO_SORT); playlist_ops = ops; __pl_system_init(); } void playlist_deinit() { __pl_system_deinit(); queue_deinit(&playlist_q); } bool playlist_add(enum playlist_t plist, struct track *track) { if (!track || __playlist_is_dynamic(plist)) return false; return pl_system.pl_add_track(playlist_names[plist], track); } bool playlist_remove(enum playlist_t plist, struct track *track) { if (!track || __playlist_is_dynamic(plist)) return false; return pl_system.pl_remove_track(playlist_names[plist], track); } bool playlist_has(enum playlist_t plist, struct track *track) { struct queue *queue = pl_system.pl_get_queue(playlist_names[plist]); if (!track || !queue) return false; return queue_has(queue, track); } void playlist_select(enum playlist_t plist) { playlist_cur = plist; if (__playlist_is_static(plist)) return; queue_clear(&playlist_q); queue_set_flag(&playlist_q, Q_ADD_FRONT); __playlist_fill_dynamic(plist); queue_unset_flag(&playlist_q, Q_ADD_FRONT); queue_resort(&playlist_q); } struct queue *playlist_get_queue(enum playlist_t plist) { if (__playlist_is_static(plist)) return pl_system.pl_get_queue(playlist_names[plist]); return &playlist_q; }