/* * Copyright 2013 (c) Anna Schumaker. */ #include #include #include #include #include #include static struct file c_file = FILE_INIT("library.q", 0, 0); static struct queue c_queue; struct scan_data { struct library *sd_lib; gchar *sd_path; }; static void __scan_dir(void *); #ifdef CONFIG_TESTING bool test_collection_error = false; static inline int __g_access(const gchar *path) { if (test_collection_error) return -1; return g_access(path, F_OK); } #else #define __g_access(path) g_access(path, F_OK) #endif /* CONFIG_TESTING */ static void __scan_dir_later(struct library *library, const gchar *dir) { struct scan_data *data = g_malloc(sizeof(struct scan_data)); data->sd_lib = library; data->sd_path = g_strdup(dir); /* data is freed by __scan_dir() */ idle_schedule(__scan_dir, data); } static void __scan_path(struct scan_data *scan, const gchar *name) { gchar *path = g_strdup_printf("%s/%s", scan->sd_path, name); struct track *track; if (g_file_test(path, G_FILE_TEST_IS_DIR)) __scan_dir_later(scan->sd_lib, path); else { track = track_add(scan->sd_lib, path); if (track) queue_add(&c_queue, track); } g_free(path); } static void __scan_dir(void *data) { struct scan_data *scan = data; const char *name; GDir *dir; dir = g_dir_open(scan->sd_path, 0, NULL); if (dir == NULL) goto out; name = g_dir_read_name(dir); while (name != NULL) { __scan_path(scan, name); name = g_dir_read_name(dir); } g_dir_close(dir); track_db_commit(); out: /* Allocated by __scan_dir_later() */ g_free(scan->sd_path); g_free(scan); } static void __validate_library(void *data) { struct library *library = data; struct db_entry *dbe, *next; struct track *track; int access; gchar *path; db_for_each(dbe, next, track_db_get()) { track = TRACK(dbe); if (track->tr_library != library) continue; path = track_path(track); access = __g_access(path); g_free(path); if (access < 0) { if (collection_check_library(library) < 0) return; queue_remove_all(&c_queue, track); track_remove(track); } } } void __collection_init_idle(void *data) { struct db_entry *track, *next; unsigned int i, n = 0; int field, ascending; db_for_each(track, next, track_db_get()) { if (TRACK(track)->tr_library->li_enabled && !playlist_has(PL_HIDDEN, TRACK(track))) queue_add(&c_queue, TRACK(track)); } queue_unset_flag(&c_queue, Q_ADD_FRONT); if (file_open(&c_file, OPEN_READ)) file_readf(&c_file, "%u %u", &c_queue.q_flags, &n); for (i = 0; i < n; i++) { file_readf(&c_file, "%u %d", &field, &ascending); queue_sort(&c_queue, field + 1, (i == 0) ? true : false); if (ascending == false) queue_sort(&c_queue, field + 1, false); } file_close(&c_file); if (!c_queue.q_sort) { queue_sort(&c_queue, COMPARE_ARTIST, true); queue_sort(&c_queue, COMPARE_YEAR, false); queue_sort(&c_queue, COMPARE_TRACK, false); } queue_set_flag(&c_queue, Q_SAVE_SORT); queue_set_flag(&c_queue, Q_SAVE_FLAGS); collection_update_all(); } /* * External API begins here */ void collection_init(struct queue_ops *ops) { queue_init(&c_queue, Q_ENABLED | Q_REPEAT | Q_ADD_FRONT, ops); idle_schedule(__collection_init_idle, NULL); } void collection_deinit() { queue_deinit(&c_queue); } void collection_save(struct queue *queue, enum queue_flags flag) { GSList *cur = c_queue.q_sort; int field; if (&c_queue != queue) return; if (!file_open(&c_file, OPEN_WRITE)) return; file_writef(&c_file, "%u %u", c_queue.q_flags, g_slist_length(cur)); while (cur) { field = GPOINTER_TO_INT(cur->data); file_writef(&c_file, " %u %d", abs(field) - 1, field > 0); cur = g_slist_next(cur); } file_writef(&c_file, "\n"); file_close(&c_file); } struct library *collection_add(const gchar *path) { struct library *library = NULL; if (g_file_test(path, G_FILE_TEST_IS_DIR) == false) return library; library = library_find(path); if (library) collection_update(library); return library; } void collection_remove(struct library *library) { if (library) { collection_set_enabled(library, false); track_remove_all(library); library_remove(library); } } void collection_update(struct library *library) { if (!library) return; idle_schedule(__validate_library, library); __scan_dir_later(library, library->li_path); } void collection_update_all() { struct db_entry *library, *next; db_for_each(library, next, library_db_get()) collection_update(LIBRARY(library)); } bool collection_ban(struct track *track) { bool ret = playlist_add(PL_HIDDEN, track); if (ret) queue_remove_all(&c_queue, track); return ret; } bool collection_unban(struct track *track) { bool ret = playlist_remove(PL_HIDDEN, track); if (ret) queue_add(&c_queue, track); return ret; } void collection_set_enabled(struct library *library, bool enabled) { struct db_entry *dbe, *next; struct track *track; if (!library || (library->li_enabled == enabled)) return; if (enabled && (collection_check_library(library) < 0)) return; library_set_enabled(library, enabled); queue_set_flag(&c_queue, Q_ADD_FRONT); db_for_each(dbe, next, track_db_get()) { track = TRACK(dbe); if (track->tr_library == library) { if (enabled) { if (!playlist_has(PL_HIDDEN, track)) queue_add(&c_queue, track); } else queue_remove_all(&c_queue, track); } } queue_unset_flag(&c_queue, Q_ADD_FRONT); queue_resort(&c_queue); } int collection_check_library(struct library *library) { int ret = __g_access(library->li_path); if (ret < 0) { g_warning("Can't access library at %s/\n", library->li_path); collection_set_enabled(library, false); } return ret; } struct queue *collection_get_queue() { return &c_queue; }