/* * Copyright 2013 (c) Anna Schumaker. */ #include #include #include #include #include #include struct scan_data { struct library *sd_lib; gchar *sd_path; }; static bool __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(IDLE_SYNC, __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) { playlist_add("Collection", track); playlist_add("Unplayed", track); } } g_free(path); } static bool __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); return true; } static bool __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 true; playlist_remove("Collection", track); playlist_remove("Favorites", track); playlist_remove("Hidden", track); playlist_remove("Least Played", track); playlist_remove("Most Played", track); playlist_remove("Unplayed", track); track_remove(track); } } return true; } bool __collection_init_idle(void *data) { collection_update_all(); return true; } /* * External API begins here */ void collection_init() { idle_schedule(IDLE_SYNC, __collection_init_idle, NULL); } void collection_deinit() { } 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(IDLE_SYNC, __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)); } 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); db_for_each(dbe, next, track_db_get()) { track = TRACK(dbe); if (track->tr_library == library) { if (enabled) playlist_add("Collection", track); else playlist_remove("Collection", track); } } } 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; }