diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 21467bc466..900dcbc64e 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -2314,6 +2314,10 @@ MSG_HASH( MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST, "playlist_manager_clean_playlist" ) +MSG_HASH( + MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST, + "playlist_manager_refresh_playlist" + ) MSG_HASH( MENU_ENUM_LABEL_PLAYLIST_SETTINGS_BEGIN, "playlist_settings_begin" @@ -5006,6 +5010,10 @@ MSG_HASH( MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_OVERWRITE, "manual_content_scan_overwrite" ) +MSG_HASH( + MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_VALIDATE_ENTRIES, + "manual_content_scan_validate_entries" + ) MSG_HASH( MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_START, "manual_content_scan_start" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index ef34cf36e7..31710d76e5 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -5559,6 +5559,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST, "Validate core associations and remove invalid and duplicate entries." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_REFRESH_PLAYLIST, + "Refresh Playlist" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST, + "Add new content and remove invalid entries by repeating the 'Manual Scan' operation last used to create or edit the playlist." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_DELETE_PLAYLIST, "Delete Playlist" @@ -6151,6 +6159,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_OVERWRITE, "When enabled, any existing playlist will be deleted before scanning content. When disabled, existing playlist entries are preserved and only content currently missing from the playlist will be added." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_VALIDATE_ENTRIES, + "Validate Existing Entries" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_VALIDATE_ENTRIES, + "When enabled, entries in any existing playlist will be verified before before scanning new content. Entries referring to missing content and/or files with invalid extensions will be removed." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_START, "Start Scan" @@ -10893,6 +10909,30 @@ MSG_HASH( MSG_PLAYLIST_MANAGER_PLAYLIST_CLEANED, "Playlist cleaned: " ) +MSG_HASH( + MSG_PLAYLIST_MANAGER_REFRESH_MISSING_CONFIG, + "Refresh failed - playlist contains no valid scan record: " + ) +MSG_HASH( + MSG_PLAYLIST_MANAGER_REFRESH_INVALID_CONTENT_DIR, + "Refresh failed - invalid/missing content directory: " + ) +MSG_HASH( + MSG_PLAYLIST_MANAGER_REFRESH_INVALID_SYSTEM_NAME, + "Refresh failed - invalid/missing system name: " + ) +MSG_HASH( + MSG_PLAYLIST_MANAGER_REFRESH_INVALID_CORE, + "Refresh failed - invalid core: " + ) +MSG_HASH( + MSG_PLAYLIST_MANAGER_REFRESH_INVALID_DAT_FILE, + "Refresh failed - invalid/missing arcade DAT file: " + ) +MSG_HASH( + MSG_PLAYLIST_MANAGER_REFRESH_DAT_FILE_TOO_LARGE, + "Refresh failed - arcade DAT file too large (insufficient memory): " + ) MSG_HASH( MSG_ADDED_TO_FAVORITES, "Added to favorites" @@ -12085,6 +12125,10 @@ MSG_HASH( MSG_MANUAL_CONTENT_SCAN_START, "Scanning content: " ) +MSG_HASH( + MSG_MANUAL_CONTENT_SCAN_PLAYLIST_CLEANUP, + "Checking current entries: " + ) MSG_HASH( MSG_MANUAL_CONTENT_SCAN_IN_PROGRESS, "Scanning: " diff --git a/manual_content_scan.c b/manual_content_scan.c index 84cfa2047b..466c92ffb5 100644 --- a/manual_content_scan.c +++ b/manual_content_scan.c @@ -56,6 +56,7 @@ typedef struct bool search_archives; bool filter_dat_content; bool overwrite_playlist; + bool validate_entries; } scan_settings_t; /* TODO/FIXME - static public global variables */ @@ -83,7 +84,8 @@ static scan_settings_t scan_settings = { true, /* search_recursively */ false, /* search_archives */ false, /* filter_dat_content */ - false /* overwrite_playlist */ + false, /* overwrite_playlist */ + false /* validate_entries */ }; /*****************/ @@ -176,6 +178,13 @@ bool *manual_content_scan_get_overwrite_playlist_ptr(void) return &scan_settings.overwrite_playlist; } +/* Returns a pointer to the internal + * 'validate_entries' bool */ +bool *manual_content_scan_get_validate_entries_ptr(void) +{ + return &scan_settings.validate_entries; +} + /* Sanitisation */ /* Sanitises file extensions list string: @@ -493,6 +502,234 @@ error: return false; } +/* Sets all parameters for the next manual scan + * operation according the to recorded values in + * the specified playlist. + * Returns MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK + * if playlist contains a valid scan record. */ +enum manual_content_scan_playlist_refresh_status + manual_content_scan_set_menu_from_playlist(playlist_t *playlist, + const char *path_content_database, bool show_hidden_files) +{ + const char *playlist_path = NULL; + const char *playlist_file = NULL; + const char *content_dir = NULL; + const char *core_name = NULL; + const char *file_exts = NULL; + const char *dat_file_path = NULL; + bool search_recursively = false; + bool search_archives = false; + bool filter_dat_content = false; +#ifdef HAVE_LIBRETRODB + struct string_list *rdb_list = NULL; +#endif + enum manual_content_scan_system_name_type + system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR; + enum manual_content_scan_core_type + core_type = MANUAL_CONTENT_SCAN_CORE_DETECT; + enum manual_content_scan_playlist_refresh_status + playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK; + char system_name[PATH_MAX_LENGTH]; + + system_name[0] = '\0'; + + if (!playlist_scan_refresh_enabled(playlist)) + { + playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_MISSING_CONFIG; + goto end; + } + + /* Read scan parameters from playlist */ + playlist_path = playlist_get_conf_path(playlist); + content_dir = playlist_get_scan_content_dir(playlist); + core_name = playlist_get_default_core_name(playlist); + file_exts = playlist_get_scan_file_exts(playlist); + dat_file_path = playlist_get_scan_dat_file_path(playlist); + + search_recursively = playlist_get_scan_search_recursively(playlist); + search_archives = playlist_get_scan_search_archives(playlist); + filter_dat_content = playlist_get_scan_filter_dat_content(playlist); + + /* Determine system name (playlist basename + * without extension) */ + if (string_is_empty(playlist_path)) + { + /* Cannot happen, but would constitute a + * 'system name' error */ + playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_SYSTEM_NAME; + goto end; + } + + if ((playlist_file = path_basename(playlist_path))) + { + strlcpy(system_name, playlist_file, sizeof(system_name)); + path_remove_extension(system_name); + } + + if (string_is_empty(system_name)) + { + /* Cannot happen, but would constitute a + * 'system name' error */ + playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_SYSTEM_NAME; + goto end; + } + + /* Set content directory */ + if (!manual_content_scan_set_menu_content_dir(content_dir)) + { + playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CONTENT_DIR; + goto end; + } + + /* Set system name */ +#ifdef HAVE_LIBRETRODB + /* > If platform has database support, get names + * of all installed database files */ + rdb_list = dir_list_new_special( + path_content_database, + DIR_LIST_DATABASES, NULL, show_hidden_files); + + if (rdb_list && rdb_list->size) + { + size_t i; + + /* Loop over database files */ + for (i = 0; i < rdb_list->size; i++) + { + const char *rdb_path = rdb_list->elems[i].data; + const char *rdb_file = NULL; + char rdb_name[PATH_MAX_LENGTH]; + + rdb_name[0] = '\0'; + + /* Sanity check */ + if (string_is_empty(rdb_path)) + continue; + + rdb_file = path_basename(rdb_path); + + if (string_is_empty(rdb_file)) + continue; + + /* Remove file extension */ + strlcpy(rdb_name, rdb_file, sizeof(rdb_name)); + path_remove_extension(rdb_name); + + if (string_is_empty(rdb_name)) + continue; + + /* Check whether playlist system name + * matches current database file */ + if (string_is_equal(system_name, rdb_name)) + { + system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE; + break; + } + } + } + + string_list_free(rdb_list); +#endif + + /* > If system name does not match a database + * file, then check whether it matches the + * content directory name */ + if (system_name_type != + MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE) + { + /* system_name_type is set to + * MANUAL_CONTENT_SCAN_SYSTEM_NAME_CONTENT_DIR + * by default - so if a match is found just + * reset 'custom name' field */ + if (string_is_equal(system_name, + scan_settings.system_name_content_dir)) + scan_settings.system_name_custom[0] = '\0'; + else + { + /* Playlist is using a custom system name */ + system_name_type = MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM; + strlcpy(scan_settings.system_name_custom, system_name, + sizeof(scan_settings.system_name_custom)); + } + } + + if (!manual_content_scan_set_menu_system_name( + system_name_type, system_name)) + { + playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_SYSTEM_NAME; + goto end; + } + + /* Set core path/name */ + if (!string_is_empty(core_name) && + !string_is_equal(core_name, FILE_PATH_DETECT)) + core_type = MANUAL_CONTENT_SCAN_CORE_SET; + + if (!manual_content_scan_set_menu_core_name( + core_type, core_name)) + { + playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CORE; + goto end; + } + + /* Set custom file extensions */ + if (string_is_empty(file_exts)) + scan_settings.file_exts_custom[0] = '\0'; + else + { + strlcpy(scan_settings.file_exts_custom, file_exts, + sizeof(scan_settings.file_exts_custom)); + + /* File extensions read from playlist should + * be correctly formatted, with '|' characters + * as delimiters + * > For menu purposes, must replace these + * delimiters with space characters + * > Additionally scrub the resultant string, + * to handle the case where a user has + * 'corrupted' it by manually tampering with + * the playlist file */ + string_replace_all_chars(scan_settings.file_exts_custom, '|', ' '); + manual_content_scan_scrub_file_exts(scan_settings.file_exts_custom); + } + + /* Set DAT file path */ + if (string_is_empty(dat_file_path)) + scan_settings.dat_file_path[0] = '\0'; + else + { + strlcpy(scan_settings.dat_file_path, dat_file_path, + sizeof(scan_settings.dat_file_path)); + + switch (manual_content_scan_validate_dat_file_path()) + { + case MANUAL_CONTENT_SCAN_DAT_FILE_INVALID: + playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_DAT_FILE; + goto end; + case MANUAL_CONTENT_SCAN_DAT_FILE_TOO_LARGE: + playlist_status = MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_DAT_FILE_TOO_LARGE; + goto end; + default: + /* No action required */ + break; + } + } + + /* Set remaining boolean parameters */ + scan_settings.search_recursively = search_recursively; + scan_settings.search_archives = search_archives; + scan_settings.filter_dat_content = filter_dat_content; + /* When refreshing a playlist: + * > We never overwrite the existing file + * > We always validate entries in the + * existing file */ + scan_settings.overwrite_playlist = false; + scan_settings.validate_entries = true; + +end: + return playlist_status; +} + /* Menu getters */ /* Fetches content directory for next manual scan @@ -858,11 +1095,15 @@ bool manual_content_scan_get_task_config( } /* Get file extensions list */ + task_config->file_exts_custom_set = false; if (!string_is_empty(scan_settings.file_exts_custom)) + { + task_config->file_exts_custom_set = true; strlcpy( task_config->file_exts, scan_settings.file_exts_custom, sizeof(task_config->file_exts)); + } else if (scan_settings.core_type == MANUAL_CONTENT_SCAN_CORE_SET) if (!string_is_empty(scan_settings.file_exts_core)) strlcpy( @@ -890,15 +1131,14 @@ bool manual_content_scan_get_task_config( /* Copy 'search recursively' setting */ task_config->search_recursively = scan_settings.search_recursively; - /* Copy 'search inside archives' setting */ - task_config->search_archives = scan_settings.search_archives; - + task_config->search_archives = scan_settings.search_archives; /* Copy 'DAT file filter' setting */ task_config->filter_dat_content = scan_settings.filter_dat_content; - /* Copy 'overwrite playlist' setting */ task_config->overwrite_playlist = scan_settings.overwrite_playlist; + /* Copy 'validate_entries' setting */ + task_config->validate_entries = scan_settings.validate_entries; return true; } @@ -907,7 +1147,8 @@ bool manual_content_scan_get_task_config( * content directory * > Returns NULL in the event of failure * > Returned string list must be free()'d */ -struct string_list *manual_content_scan_get_content_list(manual_content_scan_task_config_t *task_config) +struct string_list *manual_content_scan_get_content_list( + manual_content_scan_task_config_t *task_config) { struct string_list *dir_list = NULL; bool filter_exts; @@ -1155,8 +1396,8 @@ void manual_content_scan_add_content_to_playlist( * so these casts are safe */ entry.path = (char*)playlist_content_path; entry.label = label; - entry.core_path = (char*)"DETECT"; - entry.core_name = (char*)"DETECT"; + entry.core_path = (char*)FILE_PATH_DETECT; + entry.core_name = (char*)FILE_PATH_DETECT; entry.crc32 = (char*)"00000000|crc"; entry.db_name = task_config->database_name; diff --git a/manual_content_scan.h b/manual_content_scan.h index 64f4eb3b07..23b0818543 100644 --- a/manual_content_scan.h +++ b/manual_content_scan.h @@ -65,6 +65,19 @@ enum manual_content_scan_dat_file_path_status MANUAL_CONTENT_SCAN_DAT_FILE_TOO_LARGE }; +/* Defines all possible return values for + * manual_content_scan_set_menu_from_playlist() */ +enum manual_content_scan_playlist_refresh_status +{ + MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK = 0, + MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_MISSING_CONFIG, + MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CONTENT_DIR, + MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_SYSTEM_NAME, + MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CORE, + MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_DAT_FILE, + MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_DAT_FILE_TOO_LARGE +}; + /* Holds all configuration parameters required * for a manual content scan task */ typedef struct @@ -78,10 +91,12 @@ typedef struct char file_exts[PATH_MAX_LENGTH]; char dat_file_path[PATH_MAX_LENGTH]; bool core_set; + bool file_exts_custom_set; bool search_recursively; bool search_archives; bool filter_dat_content; bool overwrite_playlist; + bool validate_entries; } manual_content_scan_task_config_t; /*****************/ @@ -142,6 +157,10 @@ bool *manual_content_scan_get_filter_dat_content_ptr(void); * 'overwrite_playlist' bool */ bool *manual_content_scan_get_overwrite_playlist_ptr(void); +/* Returns a pointer to the internal + * 'validate_entries' bool */ +bool *manual_content_scan_get_validate_entries_ptr(void); + /* Sanitisation */ /* Removes invalid characters from @@ -187,6 +206,15 @@ bool manual_content_scan_set_menu_core_name( enum manual_content_scan_core_type core_type, const char *core_name); +/* Sets all parameters for the next manual scan + * operation according the to recorded values in + * the specified playlist. + * Returns MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK + * if playlist contains a valid scan record. */ +enum manual_content_scan_playlist_refresh_status + manual_content_scan_set_menu_from_playlist(playlist_t *playlist, + const char *path_content_database, bool show_hidden_files); + /* Menu getters */ /* Fetches content directory for next manual scan @@ -239,7 +267,8 @@ bool manual_content_scan_get_task_config( * content directory * > Returns NULL in the event of failure * > Returned string list must be free()'d */ -struct string_list *manual_content_scan_get_content_list(manual_content_scan_task_config_t *task_config); +struct string_list *manual_content_scan_get_content_list( + manual_content_scan_task_config_t *task_config); /* Adds specified content to playlist, if not already * present */ diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 486b1f9d7f..0788050618 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -7023,7 +7023,9 @@ static int action_ok_manual_content_scan_start(const char *path, playlist_config.old_format = settings->bools.playlist_use_old_format; playlist_config.compress = settings->bools.playlist_compression; playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match; - playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL); + playlist_config_set_base_content_directory(&playlist_config, + settings->bools.playlist_portable_paths ? + settings->paths.directory_menu_content : NULL); task_push_manual_content_scan(&playlist_config, directory_playlist); return 0; @@ -7460,6 +7462,120 @@ static int action_ok_playlist_clean(const char *path, return 0; } +static int action_ok_playlist_refresh(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx) +{ + playlist_config_t *playlist_config = NULL; + playlist_t *playlist = playlist_get_cached(); + settings_t *settings = config_get_ptr(); + bool scan_record_valid = false; + const char *msg_prefix = NULL; + const char *msg_subject = NULL; + const char *log_text = NULL; + char system_name[256]; + + system_name[0] = '\0'; + + if (!playlist || !settings) + return -1; + + playlist_config = playlist_get_config(playlist); + + if (!playlist_config || string_is_empty(playlist_config->path)) + return -1; + + /* Configure manual scan using playlist record */ + switch (manual_content_scan_set_menu_from_playlist(playlist, + settings->paths.path_content_database, + settings->bools.show_hidden_files)) + { + case MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK: + scan_record_valid = true; + break; + case MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CONTENT_DIR: + msg_prefix = msg_hash_to_str(MSG_PLAYLIST_MANAGER_REFRESH_INVALID_CONTENT_DIR); + msg_subject = playlist_get_scan_content_dir(playlist); + log_text = "[Playlist Refresh]: Invalid content directory: %s\n"; + break; + case MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_SYSTEM_NAME: + { + const char *playlist_file = NULL; + + if ((playlist_file = path_basename(playlist_config->path))) + { + strlcpy(system_name, playlist_file, sizeof(system_name)); + path_remove_extension(system_name); + } + + msg_prefix = msg_hash_to_str(MSG_PLAYLIST_MANAGER_REFRESH_INVALID_SYSTEM_NAME); + msg_subject = system_name; + log_text = "[Playlist Refresh]: Invalid system name: %s\n"; + } + break; + case MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CORE: + msg_prefix = msg_hash_to_str(MSG_PLAYLIST_MANAGER_REFRESH_INVALID_CORE); + msg_subject = playlist_get_default_core_name(playlist); + log_text = "[Playlist Refresh]: Invalid core name: %s\n"; + break; + case MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_DAT_FILE: + msg_prefix = msg_hash_to_str(MSG_PLAYLIST_MANAGER_REFRESH_INVALID_DAT_FILE); + msg_subject = playlist_get_scan_dat_file_path(playlist); + log_text = "[Playlist Refresh]: Invalid arcade dat file: %s\n"; + break; + case MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_DAT_FILE_TOO_LARGE: + msg_prefix = msg_hash_to_str(MSG_PLAYLIST_MANAGER_REFRESH_DAT_FILE_TOO_LARGE); + msg_subject = playlist_get_scan_dat_file_path(playlist); + log_text = "[Playlist Refresh]: Arcade dat file too large: %s\n"; + break; + case MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_MISSING_CONFIG: + default: + msg_prefix = msg_hash_to_str(MSG_PLAYLIST_MANAGER_REFRESH_MISSING_CONFIG); + msg_subject = path_basename(playlist_config->path); + log_text = "[Playlist Refresh]: No scan record found: %s\n"; + break; + } + + /* Log errors in the event of an invalid + * scan record */ + if (!scan_record_valid) + { + char msg[PATH_MAX_LENGTH]; + msg[0] = '\0'; + + if (string_is_empty(msg_subject)) + msg_subject = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE); + + snprintf(msg, sizeof(msg), "%s%s", msg_prefix, msg_subject); + + RARCH_ERR(log_text, msg_subject); + runloop_msg_queue_push(msg, 1, 150, true, + NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + + /* Even though this is a failure condition, we + * return 0 here to suppress any refreshing of + * the menu (this can appear ugly, depending + * on the active menu driver...) */ + return 0; + } + + /* Perform manual scan + * > Since we are refreshing the playlist, + * additionally ensure that all pertinent + * 'playlist_config' parameters are synchronised + * with the current settings struct */ + playlist_config->capacity = COLLECTION_SIZE; + playlist_config->old_format = settings->bools.playlist_use_old_format; + playlist_config->compress = settings->bools.playlist_compression; + playlist_config->fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match; + playlist_config_set_base_content_directory(playlist_config, + settings->bools.playlist_portable_paths ? + settings->paths.directory_menu_content : NULL); + + task_push_manual_content_scan(playlist_config, + settings->paths.directory_playlist); + return 0; +} + static int is_rdb_entry(enum msg_hash_enums enum_idx) { switch (enum_idx) @@ -7740,6 +7856,7 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_PLAYLIST_MANAGER_SETTINGS, action_ok_push_playlist_manager_settings}, {MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES, action_ok_playlist_reset_cores}, {MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST, action_ok_playlist_clean}, + {MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST, action_ok_playlist_refresh}, {MENU_ENUM_LABEL_RECORDING_SETTINGS, action_ok_push_recording_settings_list}, {MENU_ENUM_LABEL_INPUT_HOTKEY_BINDS, action_ok_push_input_hotkey_binds_list}, {MENU_ENUM_LABEL_ACCOUNTS_RETRO_ACHIEVEMENTS, action_ok_push_accounts_cheevos_list}, diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 8aad58186a..fc230d1872 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -168,6 +168,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_playlist_manager_reset_cores, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_playlist_manager_label_display_mode, MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_LABEL_DISPLAY_MODE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_playlist_manager_sort_mode, MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_SORT_MODE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_playlist_manager_clean_playlist, MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_playlist_manager_refresh_playlist, MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_delete_playlist, MENU_ENUM_SUBLABEL_DELETE_PLAYLIST) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_network_settings_list, MENU_ENUM_SUBLABEL_NETWORK_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_network_on_demand_thumbnails, MENU_ENUM_SUBLABEL_NETWORK_ON_DEMAND_THUMBNAILS) @@ -985,6 +986,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_search_archives, DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_dat_file, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_DAT_FILE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_dat_file_filter, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_DAT_FILE_FILTER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_overwrite, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_OVERWRITE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_validate_entries, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_VALIDATE_ENTRIES) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_start, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_START) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_create_backup, MENU_ENUM_SUBLABEL_CORE_CREATE_BACKUP) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_restore_backup_list, MENU_ENUM_SUBLABEL_CORE_RESTORE_BACKUP_LIST) @@ -3960,6 +3962,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_playlist_manager_clean_playlist); break; + case MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_playlist_manager_refresh_playlist); + break; case MENU_ENUM_LABEL_PLAYLIST_MANAGER_RIGHT_THUMBNAIL_MODE: { const char *menu_ident = menu_driver_ident(); @@ -4400,6 +4405,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_OVERWRITE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_overwrite); break; + case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_VALIDATE_ENTRIES: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_validate_entries); + break; case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_START: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_start); break; diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 184cc44bd6..50248d8bcd 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -10453,7 +10453,8 @@ static void materialui_list_insert( string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_OVERLAYS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_CG_SHADERS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_GLSL_SHADERS)) || - string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_SLANG_SHADERS)) + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_UPDATE_SLANG_SHADERS)) || + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST)) ) { node->icon_texture_index = MUI_TEXTURE_UPDATER; diff --git a/menu/drivers/ozone.c b/menu/drivers/ozone.c index 4a5533847b..4a89cd6206 100644 --- a/menu/drivers/ozone.c +++ b/menu/drivers/ozone.c @@ -1743,6 +1743,7 @@ static uintptr_t ozone_entries_icon_get_texture(ozone_handle_t *ozone, case MENU_ENUM_LABEL_AUTOSAVE_INTERVAL: case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_SETTINGS: case MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST: + case MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST: return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD]; case MENU_ENUM_LABEL_SHUTDOWN: return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHUTDOWN]; diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 9b4de964cb..7f821ad51b 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -2601,6 +2601,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, case MENU_ENUM_LABEL_AUTOSAVE_INTERVAL: case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_SETTINGS: case MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST: + case MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST: return xmb->textures.list[XMB_TEXTURE_RELOAD]; case MENU_ENUM_LABEL_RENAME_ENTRY: return xmb->textures.list[XMB_TEXTURE_RENAME]; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 7c5883c43c..7c9fc696f0 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -4035,6 +4035,14 @@ static bool menu_displaylist_parse_playlist_manager_settings( MENU_ENUM_LABEL_PLAYLIST_MANAGER_SORT_MODE, MENU_SETTING_PLAYLIST_MANAGER_SORT_MODE, 0, 0); + /* Refresh playlist */ + if (playlist_scan_refresh_enabled(playlist)) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_REFRESH_PLAYLIST), + msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST), + MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST, + MENU_SETTING_ACTION_PLAYLIST_MANAGER_REFRESH_PLAYLIST, 0, 0); + /* Clean playlist */ menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_CLEAN_PLAYLIST), @@ -5240,6 +5248,13 @@ static bool menu_displaylist_parse_manual_content_scan_list( false) == 0) count++; + /* Validate existing entries */ + if (!(*manual_content_scan_get_overwrite_playlist_ptr()) && + MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(info->list, + MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_VALIDATE_ENTRIES, PARSE_ONLY_BOOL, + false) == 0) + count++; + /* Start scan */ if (menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_START), diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 8b78d02e42..e768780214 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -243,6 +243,7 @@ enum menu_settings_type MENU_SETTING_ACTION_DELETE_PLAYLIST, MENU_SETTING_ACTION_PLAYLIST_MANAGER_RESET_CORES, MENU_SETTING_ACTION_PLAYLIST_MANAGER_CLEAN_PLAYLIST, + MENU_SETTING_ACTION_PLAYLIST_MANAGER_REFRESH_PLAYLIST, MENU_SETTING_MANUAL_CONTENT_SCAN_DIR, MENU_SETTING_MANUAL_CONTENT_SCAN_SYSTEM_NAME, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index dfd68e12f1..a2d41038dd 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -20428,6 +20428,24 @@ static bool setting_append_list( general_write_handler, general_read_handler, SD_FLAG_NONE); + (*list)[list_info->index - 1].action_ok = setting_bool_action_left_with_refresh; + (*list)[list_info->index - 1].action_left = setting_bool_action_left_with_refresh; + (*list)[list_info->index - 1].action_right = setting_bool_action_right_with_refresh; + + CONFIG_BOOL( + list, list_info, + manual_content_scan_get_validate_entries_ptr(), + MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_VALIDATE_ENTRIES, + MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_VALIDATE_ENTRIES, + false, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); END_SUB_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group); diff --git a/msg_hash.h b/msg_hash.h index 3f59f58015..506dee6399 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -2411,6 +2411,15 @@ enum msg_hash_enums MSG_PLAYLIST_MANAGER_CLEANING_PLAYLIST, MSG_PLAYLIST_MANAGER_PLAYLIST_CLEANED, + MENU_LABEL(PLAYLIST_MANAGER_REFRESH_PLAYLIST), + + MSG_PLAYLIST_MANAGER_REFRESH_MISSING_CONFIG, + MSG_PLAYLIST_MANAGER_REFRESH_INVALID_CONTENT_DIR, + MSG_PLAYLIST_MANAGER_REFRESH_INVALID_SYSTEM_NAME, + MSG_PLAYLIST_MANAGER_REFRESH_INVALID_CORE, + MSG_PLAYLIST_MANAGER_REFRESH_INVALID_DAT_FILE, + MSG_PLAYLIST_MANAGER_REFRESH_DAT_FILE_TOO_LARGE, + MENU_LABEL(CORE_UPDATER_SETTINGS), MENU_LABEL(LAKKA_SERVICES), MENU_LABEL(SHADER_APPLY_CHANGES), @@ -3203,6 +3212,7 @@ enum msg_hash_enums MENU_LABEL(MANUAL_CONTENT_SCAN_DAT_FILE), MENU_LABEL(MANUAL_CONTENT_SCAN_DAT_FILE_FILTER), MENU_LABEL(MANUAL_CONTENT_SCAN_OVERWRITE), + MENU_LABEL(MANUAL_CONTENT_SCAN_VALIDATE_ENTRIES), MENU_LABEL(MANUAL_CONTENT_SCAN_START), MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SYSTEM_NAME_USE_CONTENT_DIR, @@ -3216,6 +3226,7 @@ enum msg_hash_enums MSG_MANUAL_CONTENT_SCAN_INVALID_CONFIG, MSG_MANUAL_CONTENT_SCAN_INVALID_CONTENT, MSG_MANUAL_CONTENT_SCAN_START, + MSG_MANUAL_CONTENT_SCAN_PLAYLIST_CLEANUP, MSG_MANUAL_CONTENT_SCAN_IN_PROGRESS, MSG_MANUAL_CONTENT_SCAN_M3U_CLEANUP, MSG_MANUAL_CONTENT_SCAN_END, diff --git a/playlist.c b/playlist.c index 4860b4c46b..8391dcf115 100644 --- a/playlist.c +++ b/playlist.c @@ -28,6 +28,7 @@ #include <string/stdstring.h> #include <streams/interface_stream.h> #include <file/file_path.h> +#include <file/archive_file.h> #include <lists/string_list.h> #include <formats/rjson.h> #include <array/rbuf.h> @@ -56,6 +57,19 @@ #define USING_POSIX_FILE_SYSTEM #endif +/* Holds all configuration parameters required + * to repeat a manual content scan for a + * previously manual-scan-generated playlist */ +typedef struct +{ + char *content_dir; + char *file_exts; + char *dat_file_path; + bool search_recursively; + bool search_archives; + bool filter_dat_content; +} playlist_manual_scan_record_t; + struct content_playlist { char *default_core_path; @@ -64,7 +78,8 @@ struct content_playlist struct playlist_entry *entries; - playlist_config_t config; /* size_t alignment */ + playlist_manual_scan_record_t scan_record; /* ptr alignment */ + playlist_config_t config; /* size_t alignment */ enum playlist_label_display_mode label_display_mode; enum playlist_thumbnail_mode right_thumbnail_mode; @@ -85,6 +100,7 @@ typedef struct enum playlist_label_display_mode *current_meta_label_display_mode_val; enum playlist_thumbnail_mode *current_meta_thumbnail_mode_val; enum playlist_sort_mode *current_meta_sort_mode_val; + bool *current_meta_bool_val; playlist_t *playlist; unsigned array_depth; @@ -1123,6 +1139,89 @@ void playlist_resolve_path(enum playlist_file_mode mode, #endif } +/** + * playlist_content_path_is_valid: + * @path : Content path + * + * Checks whether specified playlist content path + * refers to an existent file. Handles all playlist + * content path 'types' (i.e. can validate paths + * referencing files inside archives). + * + * Returns true if file referenced by content + * path exists on the host filesystem. + **/ +bool playlist_content_path_is_valid(const char *path) +{ + /* Sanity check */ + if (string_is_empty(path)) + return false; + + /* If content is inside an archive, special + * handling is required... */ + if (path_contains_compressed_file(path)) + { + const char *delim = path_get_archive_delim(path); + char archive_path[PATH_MAX_LENGTH] = {0}; + size_t len = 0; + struct string_list *archive_list = NULL; + const char *content_file = NULL; + bool content_found = false; + + if (!delim) + return false; + + /* Get path of 'parent' archive file */ + len = (size_t)(1 + delim - path); + strlcpy(archive_path, path, + (len < PATH_MAX_LENGTH ? len : PATH_MAX_LENGTH) * sizeof(char)); + + /* Check if archive itself exists */ + if (!path_is_valid(archive_path)) + return false; + + /* Check if file exists inside archive */ + archive_list = file_archive_get_file_list(archive_path, NULL); + + if (!archive_list) + return false; + + /* > Get playlist entry content file name + * (sans archive file path) */ + content_file = delim; + content_file++; + + if (!string_is_empty(content_file)) + { + size_t i; + + /* > Loop over archive file contents */ + for (i = 0; i < archive_list->size; i++) + { + const char *archive_file = archive_list->elems[i].data; + + if (string_is_empty(archive_file)) + continue; + + if (string_is_equal(content_file, archive_file)) + { + content_found = true; + break; + } + } + } + + /* Clean up */ + string_list_free(archive_list); + + return content_found; + } + /* This is a 'normal' path - just check if + * it's valid */ + else + return path_is_valid(path); +} + /** * playlist_push: * @playlist : Playlist handle. @@ -1640,7 +1739,7 @@ void playlist_write_file(playlist_t *playlist) rjsonwriter_add_string(writer, "version"); rjsonwriter_add_colon(writer); rjsonwriter_add_space(writer); - rjsonwriter_add_string(writer, "1.4"); + rjsonwriter_add_string(writer, "1.5"); rjsonwriter_add_comma(writer); rjsonwriter_add_newline(writer); @@ -1703,6 +1802,57 @@ void playlist_write_file(playlist_t *playlist) rjsonwriter_add_comma(writer); rjsonwriter_add_newline(writer); + if (!string_is_empty(playlist->scan_record.content_dir)) + { + rjsonwriter_add_spaces(writer, 2); + rjsonwriter_add_string(writer, "scan_content_dir"); + rjsonwriter_add_colon(writer); + rjsonwriter_add_space(writer); + rjsonwriter_add_string(writer, playlist->scan_record.content_dir); + rjsonwriter_add_comma(writer); + rjsonwriter_add_newline(writer); + + rjsonwriter_add_spaces(writer, 2); + rjsonwriter_add_string(writer, "scan_file_exts"); + rjsonwriter_add_colon(writer); + rjsonwriter_add_space(writer); + rjsonwriter_add_string(writer, playlist->scan_record.file_exts); + rjsonwriter_add_comma(writer); + rjsonwriter_add_newline(writer); + + rjsonwriter_add_spaces(writer, 2); + rjsonwriter_add_string(writer, "scan_dat_file_path"); + rjsonwriter_add_colon(writer); + rjsonwriter_add_space(writer); + rjsonwriter_add_string(writer, playlist->scan_record.dat_file_path); + rjsonwriter_add_comma(writer); + rjsonwriter_add_newline(writer); + + rjsonwriter_add_spaces(writer, 2); + rjsonwriter_add_string(writer, "scan_search_recursively"); + rjsonwriter_add_colon(writer); + rjsonwriter_add_space(writer); + rjsonwriter_add_bool(writer, playlist->scan_record.search_recursively); + rjsonwriter_add_comma(writer); + rjsonwriter_add_newline(writer); + + rjsonwriter_add_spaces(writer, 2); + rjsonwriter_add_string(writer, "scan_search_archives"); + rjsonwriter_add_colon(writer); + rjsonwriter_add_space(writer); + rjsonwriter_add_bool(writer, playlist->scan_record.search_archives); + rjsonwriter_add_comma(writer); + rjsonwriter_add_newline(writer); + + rjsonwriter_add_spaces(writer, 2); + rjsonwriter_add_string(writer, "scan_filter_dat_content"); + rjsonwriter_add_colon(writer); + rjsonwriter_add_space(writer); + rjsonwriter_add_bool(writer, playlist->scan_record.filter_dat_content); + rjsonwriter_add_comma(writer); + rjsonwriter_add_newline(writer); + } + rjsonwriter_add_spaces(writer, 2); rjsonwriter_add_string(writer, "items"); rjsonwriter_add_colon(writer); @@ -1878,6 +2028,18 @@ void playlist_free(playlist_t *playlist) free(playlist->base_content_directory); playlist->base_content_directory = NULL; + if (playlist->scan_record.content_dir) + free(playlist->scan_record.content_dir); + playlist->scan_record.content_dir = NULL; + + if (playlist->scan_record.file_exts) + free(playlist->scan_record.file_exts); + playlist->scan_record.file_exts = NULL; + + if (playlist->scan_record.dat_file_path) + free(playlist->scan_record.dat_file_path); + playlist->scan_record.dat_file_path = NULL; + if (playlist->entries) { for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++) @@ -2118,6 +2280,21 @@ static bool JSONNumberHandler(void *context, const char *pValue, size_t length) return true; } +static bool JSONBoolHandler(void *context, bool value) +{ + JSONContext *pCtx = (JSONContext *)context; + + if (!pCtx->in_items && + (pCtx->object_depth == 1) && + (pCtx->array_depth == 0) && + pCtx->current_meta_bool_val) + *pCtx->current_meta_bool_val = value; + + pCtx->current_meta_bool_val = NULL; + + return true; +} + static bool JSONObjectMemberHandler(void *context, const char *pValue, size_t length) { JSONContext *pCtx = (JSONContext *)context; @@ -2197,7 +2374,9 @@ static bool JSONObjectMemberHandler(void *context, const char *pValue, size_t le pCtx->current_meta_label_display_mode_val = NULL; pCtx->current_meta_thumbnail_mode_val = NULL; pCtx->current_meta_sort_mode_val = NULL; + pCtx->current_meta_bool_val = NULL; pCtx->in_items = false; + switch (pValue[0]) { case 'b': @@ -2205,7 +2384,7 @@ static bool JSONObjectMemberHandler(void *context, const char *pValue, size_t le pCtx->current_string_val = &pCtx->playlist->base_content_directory; break; case 'd': - if (string_is_equal(pValue, "default_core_path")) + if (string_is_equal(pValue, "default_core_path")) pCtx->current_string_val = &pCtx->playlist->default_core_path; else if (string_is_equal(pValue, "default_core_name")) pCtx->current_string_val = &pCtx->playlist->default_core_name; @@ -2215,17 +2394,29 @@ static bool JSONObjectMemberHandler(void *context, const char *pValue, size_t le pCtx->in_items = true; break; case 'l': - if (string_is_equal(pValue, "label_display_mode")) + if (string_is_equal(pValue, "label_display_mode")) pCtx->current_meta_label_display_mode_val = &pCtx->playlist->label_display_mode; else if (string_is_equal(pValue, "left_thumbnail_mode")) - pCtx->current_meta_thumbnail_mode_val = &pCtx->playlist->left_thumbnail_mode; + pCtx->current_meta_thumbnail_mode_val = &pCtx->playlist->left_thumbnail_mode; break; case 'r': if (string_is_equal(pValue, "right_thumbnail_mode")) pCtx->current_meta_thumbnail_mode_val = &pCtx->playlist->right_thumbnail_mode; break; case 's': - if (string_is_equal(pValue, "sort_mode")) + if (string_is_equal(pValue, "scan_content_dir")) + pCtx->current_string_val = &pCtx->playlist->scan_record.content_dir; + else if (string_is_equal(pValue, "scan_file_exts")) + pCtx->current_string_val = &pCtx->playlist->scan_record.file_exts; + else if (string_is_equal(pValue, "scan_dat_file_path")) + pCtx->current_string_val = &pCtx->playlist->scan_record.dat_file_path; + else if (string_is_equal(pValue, "scan_search_recursively")) + pCtx->current_meta_bool_val = &pCtx->playlist->scan_record.search_recursively; + else if (string_is_equal(pValue, "scan_search_archives")) + pCtx->current_meta_bool_val = &pCtx->playlist->scan_record.search_archives; + else if (string_is_equal(pValue, "scan_filter_dat_content")) + pCtx->current_meta_bool_val = &pCtx->playlist->scan_record.filter_dat_content; + else if (string_is_equal(pValue, "sort_mode")) pCtx->current_meta_sort_mode_val = &pCtx->playlist->sort_mode; break; } @@ -2338,7 +2529,8 @@ static bool playlist_read_file(playlist_t *playlist) JSONEndObjectHandler, JSONStartArrayHandler, JSONEndArrayHandler, - NULL, NULL) /* unused boolean/null handlers */ + JSONBoolHandler, + NULL) /* Unused null handler */ != RJSON_DONE) { if (context.out_of_memory) @@ -2624,6 +2816,13 @@ playlist_t *playlist_init(const playlist_config_t *config) playlist->left_thumbnail_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT; playlist->sort_mode = PLAYLIST_SORT_MODE_DEFAULT; + playlist->scan_record.search_recursively = false; + playlist->scan_record.search_archives = false; + playlist->scan_record.filter_dat_content = false; + playlist->scan_record.content_dir = NULL; + playlist->scan_record.file_exts = NULL; + playlist->scan_record.dat_file_path = NULL; + /* Cache configuration parameters */ if (!playlist_config_copy(config, &playlist->config)) goto error; @@ -2634,7 +2833,9 @@ playlist_t *playlist_init(const playlist_config_t *config) /* Try auto-fixing paths if enabled, and playlist * base content directory is different */ - if (config->autofix_paths && !string_is_equal(playlist->base_content_directory, config->base_content_directory)) + if (config->autofix_paths && + !string_is_equal(playlist->base_content_directory, + config->base_content_directory)) { if (!string_is_empty(playlist->base_content_directory)) { @@ -2651,9 +2852,9 @@ playlist_t *playlist_init(const playlist_config_t *config) /* Fix entry path */ tmp_entry_path[0] = '\0'; path_replace_base_path_and_convert_to_local_file_system( - tmp_entry_path, entry->path, - playlist->base_content_directory, playlist->config.base_content_directory, - sizeof(tmp_entry_path)); + tmp_entry_path, entry->path, + playlist->base_content_directory, playlist->config.base_content_directory, + sizeof(tmp_entry_path)); free(entry->path); entry->path = strdup(tmp_entry_path); @@ -2676,9 +2877,9 @@ playlist_t *playlist_init(const playlist_config_t *config) tmp_entry_path[0] = '\0'; path_replace_base_path_and_convert_to_local_file_system( - tmp_entry_path, subsystem_rom_path, - playlist->base_content_directory, playlist->config.base_content_directory, - sizeof(tmp_entry_path)); + tmp_entry_path, subsystem_rom_path, + playlist->base_content_directory, playlist->config.base_content_directory, + sizeof(tmp_entry_path)); string_list_append(subsystem_roms_new_paths, tmp_entry_path, attributes); } @@ -2686,6 +2887,32 @@ playlist_t *playlist_init(const playlist_config_t *config) entry->subsystem_roms = subsystem_roms_new_paths; } } + + /* Fix scan record content directory */ + if (!string_is_empty(playlist->scan_record.content_dir)) + { + tmp_entry_path[0] = '\0'; + path_replace_base_path_and_convert_to_local_file_system( + tmp_entry_path, playlist->scan_record.content_dir, + playlist->base_content_directory, playlist->config.base_content_directory, + sizeof(tmp_entry_path)); + + free(playlist->scan_record.content_dir); + playlist->scan_record.content_dir = strdup(tmp_entry_path); + } + + /* Fix scan record arcade DAT file */ + if (!string_is_empty(playlist->scan_record.dat_file_path)) + { + tmp_entry_path[0] = '\0'; + path_replace_base_path_and_convert_to_local_file_system( + tmp_entry_path, playlist->scan_record.dat_file_path, + playlist->base_content_directory, playlist->config.base_content_directory, + sizeof(tmp_entry_path)); + + free(playlist->scan_record.dat_file_path); + playlist->scan_record.dat_file_path = strdup(tmp_entry_path); + } } /* Update playlist base content directory*/ @@ -2958,14 +3185,14 @@ void playlist_get_db_name(playlist_t *playlist, size_t idx, } } -char *playlist_get_default_core_path(playlist_t *playlist) +const char *playlist_get_default_core_path(playlist_t *playlist) { if (!playlist) return NULL; return playlist->default_core_path; } -char *playlist_get_default_core_name(playlist_t *playlist) +const char *playlist_get_default_core_name(playlist_t *playlist) { if (!playlist) return NULL; @@ -3001,6 +3228,55 @@ enum playlist_sort_mode playlist_get_sort_mode(playlist_t *playlist) return playlist->sort_mode; } +const char *playlist_get_scan_content_dir(playlist_t *playlist) +{ + if (!playlist) + return NULL; + return playlist->scan_record.content_dir; +} + +const char *playlist_get_scan_file_exts(playlist_t *playlist) +{ + if (!playlist) + return NULL; + return playlist->scan_record.file_exts; +} + +const char *playlist_get_scan_dat_file_path(playlist_t *playlist) +{ + if (!playlist) + return NULL; + return playlist->scan_record.dat_file_path; +} + +bool playlist_get_scan_search_recursively(playlist_t *playlist) +{ + if (!playlist) + return false; + return playlist->scan_record.search_recursively; +} + +bool playlist_get_scan_search_archives(playlist_t *playlist) +{ + if (!playlist) + return false; + return playlist->scan_record.search_archives; +} + +bool playlist_get_scan_filter_dat_content(playlist_t *playlist) +{ + if (!playlist) + return false; + return playlist->scan_record.filter_dat_content; +} + +bool playlist_scan_refresh_enabled(playlist_t *playlist) +{ + if (!playlist) + return false; + return !string_is_empty(playlist->scan_record.content_dir); +} + void playlist_set_default_core_path(playlist_t *playlist, const char *core_path) { char real_core_path[PATH_MAX_LENGTH]; @@ -3090,6 +3366,135 @@ void playlist_set_sort_mode(playlist_t *playlist, } } +void playlist_set_scan_content_dir(playlist_t *playlist, const char *content_dir) +{ + bool current_string_empty; + bool new_string_empty; + + if (!playlist) + return; + + current_string_empty = string_is_empty(playlist->scan_record.content_dir); + new_string_empty = string_is_empty(content_dir); + + /* Check whether string value has changed + * (note that a NULL or empty argument will + * unset the playlist value) */ + if (( current_string_empty && !new_string_empty) || + (!current_string_empty && new_string_empty) || + !string_is_equal(playlist->scan_record.content_dir, content_dir)) + playlist->modified = true; + else + return; /* Strings are identical; do nothing */ + + if (playlist->scan_record.content_dir) + { + free(playlist->scan_record.content_dir); + playlist->scan_record.content_dir = NULL; + } + + if (!new_string_empty) + playlist->scan_record.content_dir = strdup(content_dir); +} + +void playlist_set_scan_file_exts(playlist_t *playlist, const char *file_exts) +{ + bool current_string_empty; + bool new_string_empty; + + if (!playlist) + return; + + current_string_empty = string_is_empty(playlist->scan_record.file_exts); + new_string_empty = string_is_empty(file_exts); + + /* Check whether string value has changed + * (note that a NULL or empty argument will + * unset the playlist value) */ + if (( current_string_empty && !new_string_empty) || + (!current_string_empty && new_string_empty) || + !string_is_equal(playlist->scan_record.file_exts, file_exts)) + playlist->modified = true; + else + return; /* Strings are identical; do nothing */ + + if (playlist->scan_record.file_exts) + { + free(playlist->scan_record.file_exts); + playlist->scan_record.file_exts = NULL; + } + + if (!new_string_empty) + playlist->scan_record.file_exts = strdup(file_exts); +} + +void playlist_set_scan_dat_file_path(playlist_t *playlist, const char *dat_file_path) +{ + bool current_string_empty; + bool new_string_empty; + + if (!playlist) + return; + + current_string_empty = string_is_empty(playlist->scan_record.dat_file_path); + new_string_empty = string_is_empty(dat_file_path); + + /* Check whether string value has changed + * (note that a NULL or empty argument will + * unset the playlist value) */ + if (( current_string_empty && !new_string_empty) || + (!current_string_empty && new_string_empty) || + !string_is_equal(playlist->scan_record.dat_file_path, dat_file_path)) + playlist->modified = true; + else + return; /* Strings are identical; do nothing */ + + if (playlist->scan_record.dat_file_path) + { + free(playlist->scan_record.dat_file_path); + playlist->scan_record.dat_file_path = NULL; + } + + if (!new_string_empty) + playlist->scan_record.dat_file_path = strdup(dat_file_path); +} + +void playlist_set_scan_search_recursively(playlist_t *playlist, bool search_recursively) +{ + if (!playlist) + return; + + if (playlist->scan_record.search_recursively != search_recursively) + { + playlist->scan_record.search_recursively = search_recursively; + playlist->modified = true; + } +} + +void playlist_set_scan_search_archives(playlist_t *playlist, bool search_archives) +{ + if (!playlist) + return; + + if (playlist->scan_record.search_archives != search_archives) + { + playlist->scan_record.search_archives = search_archives; + playlist->modified = true; + } +} + +void playlist_set_scan_filter_dat_content(playlist_t *playlist, bool filter_dat_content) +{ + if (!playlist) + return; + + if (playlist->scan_record.filter_dat_content != filter_dat_content) + { + playlist->scan_record.filter_dat_content = filter_dat_content; + playlist->modified = true; + } +} + /* Returns true if specified entry has a valid * core association (i.e. a non-empty string * other than DETECT) */ diff --git a/playlist.h b/playlist.h index 09799d5eb3..cfd74fbc81 100644 --- a/playlist.h +++ b/playlist.h @@ -249,6 +249,20 @@ void playlist_delete_by_path(playlist_t *playlist, void playlist_resolve_path(enum playlist_file_mode mode, bool is_core, char *path, size_t len); +/** + * playlist_content_path_is_valid: + * @path : Content path + * + * Checks whether specified playlist content path + * refers to an existent file. Handles all playlist + * content path 'types' (i.e. can validate paths + * referencing files inside archives). + * + * Returns true if file referenced by content + * path exists on the host filesystem. + **/ +bool playlist_content_path_is_valid(const char *path); + /** * playlist_push: * @playlist : Playlist handle. @@ -338,12 +352,19 @@ void playlist_get_crc32(playlist_t *playlist, size_t idx, void playlist_get_db_name(playlist_t *playlist, size_t idx, const char **db_name); -char *playlist_get_default_core_path(playlist_t *playlist); -char *playlist_get_default_core_name(playlist_t *playlist); +const char *playlist_get_default_core_path(playlist_t *playlist); +const char *playlist_get_default_core_name(playlist_t *playlist); enum playlist_label_display_mode playlist_get_label_display_mode(playlist_t *playlist); enum playlist_thumbnail_mode playlist_get_thumbnail_mode( playlist_t *playlist, enum playlist_thumbnail_id thumbnail_id); enum playlist_sort_mode playlist_get_sort_mode(playlist_t *playlist); +const char *playlist_get_scan_content_dir(playlist_t *playlist); +const char *playlist_get_scan_file_exts(playlist_t *playlist); +const char *playlist_get_scan_dat_file_path(playlist_t *playlist); +bool playlist_get_scan_search_recursively(playlist_t *playlist); +bool playlist_get_scan_search_archives(playlist_t *playlist); +bool playlist_get_scan_filter_dat_content(playlist_t *playlist); +bool playlist_scan_refresh_enabled(playlist_t *playlist); void playlist_set_default_core_path(playlist_t *playlist, const char *core_path); void playlist_set_default_core_name(playlist_t *playlist, const char *core_name); @@ -351,6 +372,12 @@ void playlist_set_label_display_mode(playlist_t *playlist, enum playlist_label_d void playlist_set_thumbnail_mode( playlist_t *playlist, enum playlist_thumbnail_id thumbnail_id, enum playlist_thumbnail_mode thumbnail_mode); void playlist_set_sort_mode(playlist_t *playlist, enum playlist_sort_mode sort_mode); +void playlist_set_scan_content_dir(playlist_t *playlist, const char *content_dir); +void playlist_set_scan_file_exts(playlist_t *playlist, const char *file_exts); +void playlist_set_scan_dat_file_path(playlist_t *playlist, const char *dat_file_path); +void playlist_set_scan_search_recursively(playlist_t *playlist, bool search_recursively); +void playlist_set_scan_search_archives(playlist_t *playlist, bool search_archives); +void playlist_set_scan_filter_dat_content(playlist_t *playlist, bool filter_dat_content); /* Returns true if specified entry has a valid * core association (i.e. a non-empty string diff --git a/tasks/task_manual_content_scan.c b/tasks/task_manual_content_scan.c index 82748ec959..84826ae75e 100644 --- a/tasks/task_manual_content_scan.c +++ b/tasks/task_manual_content_scan.c @@ -45,6 +45,7 @@ enum manual_scan_status { MANUAL_SCAN_BEGIN = 0, + MANUAL_SCAN_ITERATE_CLEAN, MANUAL_SCAN_ITERATE_CONTENT, MANUAL_SCAN_ITERATE_M3U, MANUAL_SCAN_END @@ -54,12 +55,15 @@ typedef struct manual_scan_handle { manual_content_scan_task_config_t *task_config; playlist_t *playlist; + struct string_list *file_exts_list; struct string_list *content_list; logiqx_dat_t *dat_file; struct string_list *m3u_list; playlist_config_t playlist_config; /* size_t alignment */ - size_t list_size; - size_t list_index; + size_t playlist_size; + size_t playlist_index; + size_t content_list_size; + size_t content_list_index; size_t m3u_index; enum manual_scan_status status; } manual_scan_handle_t; @@ -82,6 +86,12 @@ static void free_manual_content_scan_handle(manual_scan_handle_t *manual_scan) manual_scan->playlist = NULL; } + if (manual_scan->file_exts_list) + { + string_list_free(manual_scan->file_exts_list); + manual_scan->file_exts_list = NULL; + } + if (manual_scan->content_list) { string_list_free(manual_scan->content_list); @@ -199,6 +209,11 @@ static void task_manual_content_scan_handler(retro_task_t *task) { case MANUAL_SCAN_BEGIN: { + /* Get allowed file extensions list */ + if (!string_is_empty(manual_scan->task_config->file_exts)) + manual_scan->file_exts_list = string_split( + manual_scan->task_config->file_exts, "|"); + /* Get content list */ manual_scan->content_list = manual_content_scan_get_content_list( manual_scan->task_config); @@ -212,7 +227,7 @@ static void task_manual_content_scan_handler(retro_task_t *task) goto task_finished; } - manual_scan->list_size = manual_scan->content_list->size; + manual_scan->content_list_size = manual_scan->content_list->size; /* Load DAT file, if required */ if (!string_is_empty(manual_scan->task_config->dat_file_path)) @@ -240,25 +255,118 @@ static void task_manual_content_scan_handler(retro_task_t *task) if (manual_scan->task_config->overwrite_playlist) playlist_clear(manual_scan->playlist); + /* Get initial playlist size */ + manual_scan->playlist_size = playlist_size(manual_scan->playlist); + /* Set default core, if required */ if (manual_scan->task_config->core_set) { - playlist_set_default_core_path( - manual_scan->playlist, manual_scan->task_config->core_path); - playlist_set_default_core_name( - manual_scan->playlist, manual_scan->task_config->core_name); + playlist_set_default_core_path(manual_scan->playlist, + manual_scan->task_config->core_path); + playlist_set_default_core_name(manual_scan->playlist, + manual_scan->task_config->core_name); } - /* All good - can start iterating */ - manual_scan->status = MANUAL_SCAN_ITERATE_CONTENT; + /* Record remaining scan parameters to enable + * subsequent 'refresh playlist' operations */ + playlist_set_scan_content_dir(manual_scan->playlist, + manual_scan->task_config->content_dir); + playlist_set_scan_file_exts(manual_scan->playlist, + manual_scan->task_config->file_exts_custom_set ? + manual_scan->task_config->file_exts : NULL); + playlist_set_scan_dat_file_path(manual_scan->playlist, + manual_scan->task_config->dat_file_path); + playlist_set_scan_search_recursively(manual_scan->playlist, + manual_scan->task_config->search_recursively); + playlist_set_scan_search_archives(manual_scan->playlist, + manual_scan->task_config->search_archives); + playlist_set_scan_filter_dat_content(manual_scan->playlist, + manual_scan->task_config->filter_dat_content); + + /* All good - can start iterating + * > If playlist has content and 'validate + * entries' is enabled, go to clean-up phase + * > Otherwise go straight to content scan phase */ + if (manual_scan->task_config->validate_entries && + (manual_scan->playlist_size > 0)) + manual_scan->status = MANUAL_SCAN_ITERATE_CLEAN; + else + manual_scan->status = MANUAL_SCAN_ITERATE_CONTENT; + } + break; + case MANUAL_SCAN_ITERATE_CLEAN: + { + const struct playlist_entry *entry = NULL; + bool delete_entry = false; + + /* Get current entry */ + playlist_get_index(manual_scan->playlist, + manual_scan->playlist_index, &entry); + + if (entry) + { + const char *entry_file = NULL; + const char *entry_file_ext = NULL; + char task_title[PATH_MAX_LENGTH]; + + task_title[0] = '\0'; + + /* Update progress display */ + task_free_title(task); + + strlcpy(task_title, + msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_PLAYLIST_CLEANUP), + sizeof(task_title)); + + if (!string_is_empty(entry->path) && + (entry_file = path_basename(entry->path))) + strlcat(task_title, entry_file, sizeof(task_title)); + + task_set_title(task, strdup(task_title)); + task_set_progress(task, (manual_scan->playlist_index * 100) / + manual_scan->playlist_size); + + /* Check whether playlist content exists on + * the filesystem */ + if (!playlist_content_path_is_valid(entry->path)) + delete_entry = true; + /* If file exists, check whether it has a + * permitted file extension */ + else if (manual_scan->file_exts_list && + (entry_file_ext = path_get_extension(entry->path)) && + !string_list_find_elem_prefix( + manual_scan->file_exts_list, + ".", entry_file_ext)) + delete_entry = true; + + if (delete_entry) + { + /* Invalid content - delete entry */ + playlist_delete_index(manual_scan->playlist, + manual_scan->playlist_index); + + /* Update playlist_size */ + manual_scan->playlist_size = playlist_size(manual_scan->playlist); + } + } + + /* Increment entry index *if* current entry still + * exists (i.e. if entry was deleted, current index + * will already point to the *next* entry) */ + if (!delete_entry) + manual_scan->playlist_index++; + + if (manual_scan->playlist_index >= + manual_scan->playlist_size) + manual_scan->status = MANUAL_SCAN_ITERATE_CONTENT; } break; case MANUAL_SCAN_ITERATE_CONTENT: { - const char *content_path = - manual_scan->content_list->elems[manual_scan->list_index].data; - int content_type = - manual_scan->content_list->elems[manual_scan->list_index].attr.i; + const char *content_path = manual_scan->content_list->elems[ + manual_scan->content_list_index].data; + int content_type = manual_scan->content_list->elems[ + manual_scan->content_list_index].attr.i; if (!string_is_empty(content_path)) { @@ -270,15 +378,16 @@ static void task_manual_content_scan_handler(retro_task_t *task) /* Update progress display */ task_free_title(task); - strlcpy( - task_title, msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_IN_PROGRESS), + strlcpy(task_title, + msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_IN_PROGRESS), sizeof(task_title)); if (!string_is_empty(content_file)) strlcat(task_title, content_file, sizeof(task_title)); task_set_title(task, strdup(task_title)); - task_set_progress(task, (manual_scan->list_index * 100) / manual_scan->list_size); + task_set_progress(task, (manual_scan->content_list_index * 100) / + manual_scan->content_list_size); /* Add content to playlist */ manual_content_scan_add_content_to_playlist( @@ -300,8 +409,9 @@ static void task_manual_content_scan_handler(retro_task_t *task) } /* Increment content index */ - manual_scan->list_index++; - if (manual_scan->list_index >= manual_scan->list_size) + manual_scan->content_list_index++; + if (manual_scan->content_list_index >= + manual_scan->content_list_size) { /* Check whether we have any M3U files * to process */ @@ -314,8 +424,8 @@ static void task_manual_content_scan_handler(retro_task_t *task) break; case MANUAL_SCAN_ITERATE_M3U: { - const char *m3u_path = - manual_scan->m3u_list->elems[manual_scan->m3u_index].data; + const char *m3u_path = manual_scan->m3u_list->elems[ + manual_scan->m3u_index].data; if (!string_is_empty(m3u_path)) { @@ -328,15 +438,16 @@ static void task_manual_content_scan_handler(retro_task_t *task) /* Update progress display */ task_free_title(task); - strlcpy( - task_title, msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_M3U_CLEANUP), + strlcpy(task_title, + msg_hash_to_str(MSG_MANUAL_CONTENT_SCAN_M3U_CLEANUP), sizeof(task_title)); if (!string_is_empty(m3u_name)) strlcat(task_title, m3u_name, sizeof(task_title)); task_set_title(task, strdup(task_title)); - task_set_progress(task, (manual_scan->m3u_index * 100) / manual_scan->m3u_list->size); + task_set_progress(task, (manual_scan->m3u_index * 100) / + manual_scan->m3u_list->size); /* Load M3U file */ m3u_file = m3u_file_init(m3u_path); @@ -445,10 +556,13 @@ bool task_push_manual_content_scan( /* Configure handle */ manual_scan->task_config = NULL; manual_scan->playlist = NULL; + manual_scan->file_exts_list = NULL; manual_scan->content_list = NULL; manual_scan->dat_file = NULL; - manual_scan->list_size = 0; - manual_scan->list_index = 0; + manual_scan->playlist_size = 0; + manual_scan->playlist_index = 0; + manual_scan->content_list_size = 0; + manual_scan->content_list_index = 0; manual_scan->m3u_list = string_list_new(); manual_scan->m3u_index = 0; manual_scan->status = MANUAL_SCAN_BEGIN; diff --git a/tasks/task_playlist_manager.c b/tasks/task_playlist_manager.c index d92b1ec163..e65b115cf0 100644 --- a/tasks/task_playlist_manager.c +++ b/tasks/task_playlist_manager.c @@ -23,7 +23,6 @@ #include <string/stdstring.h> #include <lists/string_list.h> #include <file/file_path.h> -#include <file/archive_file.h> #include <formats/m3u_file.h> #include "tasks_internal.h" @@ -368,78 +367,6 @@ error: /* Clean Playlist */ /******************/ -static bool pl_manager_content_exists(const char *path) -{ - /* Sanity check */ - if (string_is_empty(path)) - return false; - - /* If content is inside an archive, special - * handling is required... */ - if (path_contains_compressed_file(path)) - { - const char *delim = path_get_archive_delim(path); - char archive_path[PATH_MAX_LENGTH] = {0}; - size_t len = 0; - struct string_list *archive_list = NULL; - const char *content_file = NULL; - bool content_found = false; - - if (!delim) - return false; - - /* Get path of 'parent' archive file */ - len = (size_t)(1 + delim - path); - strlcpy( - archive_path, path, - (len < PATH_MAX_LENGTH ? len : PATH_MAX_LENGTH) * sizeof(char)); - - /* Check if archive itself exists */ - if (!path_is_valid(archive_path)) - return false; - - /* Check if file exists inside archive */ - archive_list = file_archive_get_file_list(archive_path, NULL); - - if (!archive_list) - return false; - - /* > Get playlist entry content file name - * (sans archive file path) */ - content_file = delim; - content_file++; - - if (!string_is_empty(content_file)) - { - size_t i; - - /* > Loop over archive file contents */ - for (i = 0; i < archive_list->size; i++) - { - const char *archive_file = archive_list->elems[i].data; - - if (string_is_empty(archive_file)) - continue; - - if (string_is_equal(content_file, archive_file)) - { - content_found = true; - break; - } - } - } - - /* Clean up */ - string_list_free(archive_list); - - return content_found; - } - /* This is a 'normal' path - just check if - * it's valid */ - else - return path_is_valid(path); -} - static void pl_manager_validate_core_association( playlist_t *playlist, size_t entry_index, const char *core_path, const char *core_name) @@ -562,7 +489,7 @@ static void task_pl_manager_clean_playlist_handler(retro_task_t *task) { /* Check whether playlist content exists on * the filesystem */ - if (!pl_manager_content_exists(entry->path)) + if (!playlist_content_path_is_valid(entry->path)) { /* Invalid content - delete entry */ playlist_delete_index(pl_manager->playlist, pl_manager->list_index);