mirror of
https://github.com/libretro/RetroArch
synced 2025-02-02 14:54:10 +00:00
Merge pull request #10013 from jdgleaver/clean-playlists
(Playlist Management) Add 'Clean Playlist' option
This commit is contained in:
commit
1af0ede1a6
@ -974,6 +974,8 @@ MSG_HASH(MENU_ENUM_LABEL_PLAYLIST_MANAGER_RIGHT_THUMBNAIL_MODE,
|
||||
"playlist_manager_right_thumbnail_mode")
|
||||
MSG_HASH(MENU_ENUM_LABEL_PLAYLIST_MANAGER_LEFT_THUMBNAIL_MODE,
|
||||
"playlist_manager_left_thumbnail_mode")
|
||||
MSG_HASH(MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST,
|
||||
"playlist_manager_clean_playlist")
|
||||
MSG_HASH(MENU_ENUM_LABEL_PLAYLIST_SETTINGS_BEGIN,
|
||||
"playlist_settings_begin")
|
||||
MSG_HASH(MENU_ENUM_LABEL_POINTER_ENABLE,
|
||||
|
@ -2507,6 +2507,22 @@ MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_THUMBNAIL_MODE_DEFAULT,
|
||||
"System Default"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_CLEAN_PLAYLIST,
|
||||
"Clean Playlist"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST,
|
||||
"Remove invalid/duplicate entries and validate core associations."
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_PLAYLIST_MANAGER_CLEANING_PLAYLIST,
|
||||
"Cleaning playlist: "
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_PLAYLIST_MANAGER_PLAYLIST_CLEANED,
|
||||
"Playlist cleaned: "
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_POINTER_ENABLE,
|
||||
"Touch Support"
|
||||
@ -10094,6 +10110,10 @@ MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_DELETE_PLAYLIST,
|
||||
"Delete Playlist"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_DELETE_PLAYLIST,
|
||||
"Remove playlist from filesystem."
|
||||
)
|
||||
#ifdef HAVE_LAKKA
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_LOCALAP_ENABLE,
|
||||
|
@ -6370,6 +6370,25 @@ static int action_ok_playlist_reset_cores(const char *path,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int action_ok_playlist_clean(const char *path,
|
||||
const char *label, unsigned type, size_t idx, size_t entry_idx)
|
||||
{
|
||||
playlist_t *playlist = playlist_get_cached();
|
||||
const char *playlist_path = NULL;
|
||||
|
||||
if (!playlist)
|
||||
return -1;
|
||||
|
||||
playlist_path = playlist_get_conf_path(playlist);
|
||||
|
||||
if (string_is_empty(playlist_path))
|
||||
return -1;
|
||||
|
||||
task_push_pl_manager_clean_playlist(playlist_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_rdb_entry(enum msg_hash_enums enum_idx)
|
||||
{
|
||||
switch (enum_idx)
|
||||
@ -6785,6 +6804,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
|
||||
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES:
|
||||
BIND_ACTION_OK(cbs, action_ok_playlist_reset_cores);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST:
|
||||
BIND_ACTION_OK(cbs, action_ok_playlist_clean);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_RECORDING_SETTINGS:
|
||||
BIND_ACTION_OK(cbs, action_ok_push_recording_settings_list);
|
||||
break;
|
||||
|
@ -148,6 +148,8 @@ default_sublabel_macro(action_bind_sublabel_playlist_manager_list, MENU_
|
||||
default_sublabel_macro(action_bind_sublabel_playlist_manager_default_core, MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_DEFAULT_CORE)
|
||||
default_sublabel_macro(action_bind_sublabel_playlist_manager_reset_cores, MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_RESET_CORES)
|
||||
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_clean_playlist, MENU_ENUM_SUBLABEL_PLAYLIST_MANAGER_CLEAN_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)
|
||||
default_sublabel_macro(action_bind_sublabel_user_settings_list, MENU_ENUM_SUBLABEL_USER_SETTINGS)
|
||||
@ -2796,6 +2798,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_LABEL_DISPLAY_MODE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_playlist_manager_label_display_mode);
|
||||
break;
|
||||
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_RIGHT_THUMBNAIL_MODE:
|
||||
settings = config_get_ptr();
|
||||
/* Uses same sublabels as MENU_ENUM_LABEL_THUMBNAILS */
|
||||
@ -2832,6 +2837,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_left_thumbnails);
|
||||
}
|
||||
break;
|
||||
case MENU_ENUM_LABEL_DELETE_PLAYLIST:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_delete_playlist);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_AI_SERVICE_URL:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_ai_service_url);
|
||||
break;
|
||||
|
@ -7511,7 +7511,8 @@ static void materialui_list_insert(
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_RENAME_ENTRY)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_RESET_CORE_ASSOCIATION)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES)))
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST)))
|
||||
{
|
||||
node->icon_texture_index = MUI_TEXTURE_RENAME;
|
||||
node->has_icon = true;
|
||||
@ -7618,7 +7619,8 @@ static void materialui_list_insert(
|
||||
node->icon_texture_index = MUI_TEXTURE_LOAD_CONTENT;
|
||||
node->has_icon = true;
|
||||
}
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DELETE_ENTRY)))
|
||||
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DELETE_ENTRY)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DELETE_PLAYLIST)))
|
||||
{
|
||||
node->icon_texture_index = MUI_TEXTURE_REMOVE;
|
||||
node->has_icon = true;
|
||||
@ -7751,11 +7753,8 @@ static void materialui_list_insert(
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CHEAT_SEARCH_SETTINGS)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CHEAT_START_OR_CONT)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_INPUT_META_CHEAT_SEARCH)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_DEFAULT_CORE)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_LABEL_DISPLAY_MODE)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_THUMBNAILS_MATERIALUI)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_LEFT_THUMBNAILS_MATERIALUI)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DELETE_PLAYLIST)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_SHADER_NUM_PASSES)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CHEAT_FILE_LOAD)) ||
|
||||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CHEAT_FILE_LOAD_APPEND)) ||
|
||||
|
@ -285,6 +285,7 @@ menu_texture_item ozone_entries_icon_get_texture(ozone_handle_t *ozone,
|
||||
case MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE:
|
||||
case MENU_ENUM_LABEL_AUTOSAVE_INTERVAL:
|
||||
case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_SETTINGS:
|
||||
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST:
|
||||
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD];
|
||||
case MENU_ENUM_LABEL_SHUTDOWN:
|
||||
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SHUTDOWN];
|
||||
|
@ -2353,6 +2353,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb,
|
||||
case MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE:
|
||||
case MENU_ENUM_LABEL_AUTOSAVE_INTERVAL:
|
||||
case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_SETTINGS:
|
||||
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST:
|
||||
return xmb->textures.list[XMB_TEXTURE_RELOAD];
|
||||
case MENU_ENUM_LABEL_RENAME_ENTRY:
|
||||
return xmb->textures.list[XMB_TEXTURE_RENAME];
|
||||
|
@ -2869,7 +2869,7 @@ static bool menu_displaylist_parse_playlist_manager_settings(
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_RESET_CORES),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES),
|
||||
MENU_ENUM_LABEL_PLAYLIST_MANAGER_RESET_CORES,
|
||||
FILE_TYPE_PLAYLIST_ENTRY, 0, 0);
|
||||
MENU_SETTING_ACTION_PLAYLIST_MANAGER_RESET_CORES, 0, 0);
|
||||
|
||||
/* Label display mode */
|
||||
menu_entries_append_enum(info->list,
|
||||
@ -2916,9 +2916,14 @@ static bool menu_displaylist_parse_playlist_manager_settings(
|
||||
MENU_ENUM_LABEL_PLAYLIST_MANAGER_LEFT_THUMBNAIL_MODE,
|
||||
MENU_SETTING_PLAYLIST_MANAGER_LEFT_THUMBNAIL_MODE, 0, 0);
|
||||
|
||||
/* TODO - Add:
|
||||
* - Remove invalid entries */
|
||||
/* Clean playlist */
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_CLEAN_PLAYLIST),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST),
|
||||
MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST,
|
||||
MENU_SETTING_ACTION_PLAYLIST_MANAGER_CLEAN_PLAYLIST, 0, 0);
|
||||
|
||||
/* Delete playlist */
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DELETE_PLAYLIST),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_DELETE_PLAYLIST),
|
||||
|
@ -213,6 +213,8 @@ enum menu_settings_type
|
||||
MENU_SET_LOAD_CDROM_LIST,
|
||||
MENU_SET_CDROM_INFO,
|
||||
MENU_SETTING_ACTION_DELETE_PLAYLIST,
|
||||
MENU_SETTING_ACTION_PLAYLIST_MANAGER_RESET_CORES,
|
||||
MENU_SETTING_ACTION_PLAYLIST_MANAGER_CLEAN_PLAYLIST,
|
||||
|
||||
MENU_SETTING_MANUAL_CONTENT_SCAN_DIR,
|
||||
MENU_SETTING_MANUAL_CONTENT_SCAN_SYSTEM_NAME,
|
||||
|
@ -2027,6 +2027,11 @@ enum msg_hash_enums
|
||||
|
||||
MENU_ENUM_LABEL_VALUE_PLAYLIST_MANAGER_THUMBNAIL_MODE_DEFAULT,
|
||||
|
||||
MENU_LABEL(PLAYLIST_MANAGER_CLEAN_PLAYLIST),
|
||||
|
||||
MSG_PLAYLIST_MANAGER_CLEANING_PLAYLIST,
|
||||
MSG_PLAYLIST_MANAGER_PLAYLIST_CLEANED,
|
||||
|
||||
MENU_LABEL(CORE_UPDATER_SETTINGS),
|
||||
MENU_LABEL(LAKKA_SERVICES),
|
||||
MENU_LABEL(SHADER_APPLY_CHANGES),
|
||||
|
43
playlist.c
43
playlist.c
@ -2498,6 +2498,49 @@ bool playlist_index_is_valid(playlist_t *playlist, size_t idx,
|
||||
string_is_equal(path_basename(playlist->entries[idx].core_path), path_basename(core_path));
|
||||
}
|
||||
|
||||
bool playlist_entries_are_equal(
|
||||
const struct playlist_entry *entry_a,
|
||||
const struct playlist_entry *entry_b,
|
||||
bool fuzzy_archive_match)
|
||||
{
|
||||
char real_path_a[PATH_MAX_LENGTH];
|
||||
char real_core_path_a[PATH_MAX_LENGTH];
|
||||
|
||||
real_path_a[0] = '\0';
|
||||
real_core_path_a[0] = '\0';
|
||||
|
||||
/* Sanity check */
|
||||
if (!entry_a || !entry_b)
|
||||
return false;
|
||||
|
||||
if (string_is_empty(entry_a->path) &&
|
||||
string_is_empty(entry_a->core_path) &&
|
||||
string_is_empty(entry_b->path) &&
|
||||
string_is_empty(entry_b->core_path))
|
||||
return true;
|
||||
|
||||
/* Check content paths */
|
||||
if (!string_is_empty(entry_a->path))
|
||||
{
|
||||
strlcpy(real_path_a, entry_a->path, sizeof(real_path_a));
|
||||
path_resolve_realpath(real_path_a, sizeof(real_path_a), true);
|
||||
}
|
||||
|
||||
if (!playlist_path_equal(
|
||||
real_path_a, entry_b->path, fuzzy_archive_match))
|
||||
return false;
|
||||
|
||||
/* Check core paths */
|
||||
if (!string_is_empty(entry_a->core_path))
|
||||
{
|
||||
strlcpy(real_core_path_a, entry_a->core_path, sizeof(real_core_path_a));
|
||||
if (!string_is_equal(real_core_path_a, "DETECT"))
|
||||
path_resolve_realpath(real_core_path_a, sizeof(real_core_path_a), true);
|
||||
}
|
||||
|
||||
return playlist_core_path_equal(real_core_path_a, entry_b->core_path);
|
||||
}
|
||||
|
||||
void playlist_get_crc32(playlist_t *playlist, size_t idx,
|
||||
const char **crc32)
|
||||
{
|
||||
|
@ -252,6 +252,13 @@ void command_playlist_update_write(
|
||||
bool playlist_index_is_valid(playlist_t *playlist, size_t idx,
|
||||
const char *path, const char *core_path);
|
||||
|
||||
/* Returns true if specified playlist entries have
|
||||
* identical content and core paths */
|
||||
bool playlist_entries_are_equal(
|
||||
const struct playlist_entry *entry_a,
|
||||
const struct playlist_entry *entry_b,
|
||||
bool fuzzy_archive_match);
|
||||
|
||||
void playlist_get_crc32(playlist_t *playlist, size_t idx,
|
||||
const char **crc32);
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include <string/stdstring.h>
|
||||
#include <file/file_path.h>
|
||||
#include <file/archive_file.h>
|
||||
|
||||
#include "tasks_internal.h"
|
||||
|
||||
@ -29,11 +30,15 @@
|
||||
#include "../msg_hash.h"
|
||||
#include "../file_path_special.h"
|
||||
#include "../playlist.h"
|
||||
#include "../core_info.h"
|
||||
|
||||
enum pl_manager_status
|
||||
{
|
||||
PL_MANAGER_BEGIN = 0,
|
||||
PL_MANAGER_ITERATE_ENTRY,
|
||||
PL_MANAGER_ITERATE_ENTRY_RESET_CORE,
|
||||
PL_MANAGER_ITERATE_ENTRY_VALIDATE,
|
||||
PL_MANAGER_VALIDATE_END,
|
||||
PL_MANAGER_ITERATE_ENTRY_CHECK_DUPLICATE,
|
||||
PL_MANAGER_END
|
||||
};
|
||||
|
||||
@ -46,11 +51,12 @@ typedef struct pl_manager_handle
|
||||
size_t list_index;
|
||||
enum pl_manager_status status;
|
||||
bool use_old_format;
|
||||
bool fuzzy_archive_match;
|
||||
} pl_manager_handle_t;
|
||||
|
||||
/**************************/
|
||||
/* Reset Associated Cores */
|
||||
/**************************/
|
||||
/*********************/
|
||||
/* Utility Functions */
|
||||
/*********************/
|
||||
|
||||
static void free_pl_manager_handle(pl_manager_handle_t *pl_manager)
|
||||
{
|
||||
@ -79,6 +85,36 @@ static void free_pl_manager_handle(pl_manager_handle_t *pl_manager)
|
||||
pl_manager = NULL;
|
||||
}
|
||||
|
||||
static void pl_manager_write_playlist(
|
||||
playlist_t *playlist, const char *playlist_path, bool use_old_format)
|
||||
{
|
||||
playlist_t *cached_playlist = playlist_get_cached();
|
||||
|
||||
/* Sanity check */
|
||||
if (!playlist || string_is_empty(playlist_path))
|
||||
return;
|
||||
|
||||
/* Write any changes to playlist file */
|
||||
playlist_write_file(playlist, use_old_format);
|
||||
|
||||
/* If this is the currently cached playlist, then
|
||||
* it must be re-cached (otherwise changes will be
|
||||
* lost if the currently cached playlist is saved
|
||||
* to disk for any reason...) */
|
||||
if (cached_playlist)
|
||||
{
|
||||
if (string_is_equal(playlist_path, playlist_get_conf_path(cached_playlist)))
|
||||
{
|
||||
playlist_free_cached();
|
||||
playlist_init_cached(playlist_path, COLLECTION_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**************************/
|
||||
/* Reset Associated Cores */
|
||||
/**************************/
|
||||
|
||||
static void task_pl_manager_reset_cores_handler(retro_task_t *task)
|
||||
{
|
||||
pl_manager_handle_t *pl_manager = NULL;
|
||||
@ -113,10 +149,10 @@ static void task_pl_manager_reset_cores_handler(retro_task_t *task)
|
||||
goto task_finished;
|
||||
|
||||
/* All good - can start iterating */
|
||||
pl_manager->status = PL_MANAGER_ITERATE_ENTRY;
|
||||
pl_manager->status = PL_MANAGER_ITERATE_ENTRY_RESET_CORE;
|
||||
}
|
||||
break;
|
||||
case PL_MANAGER_ITERATE_ENTRY:
|
||||
case PL_MANAGER_ITERATE_ENTRY_RESET_CORE:
|
||||
{
|
||||
const struct playlist_entry *entry = NULL;
|
||||
|
||||
@ -128,10 +164,8 @@ static void task_pl_manager_reset_cores_handler(retro_task_t *task)
|
||||
{
|
||||
struct playlist_entry update_entry = {0};
|
||||
char task_title[PATH_MAX_LENGTH];
|
||||
char detect_string[PATH_MAX_LENGTH];
|
||||
|
||||
task_title[0] = '\0';
|
||||
detect_string[0] = '\0';
|
||||
task_title[0] = '\0';
|
||||
|
||||
/* Update progress display */
|
||||
task_free_title(task);
|
||||
@ -154,17 +188,11 @@ static void task_pl_manager_reset_cores_handler(retro_task_t *task)
|
||||
task_set_title(task, strdup(task_title));
|
||||
task_set_progress(task, (pl_manager->list_index * 100) / pl_manager->list_size);
|
||||
|
||||
/* Reset core association */
|
||||
detect_string[0] = 'D';
|
||||
detect_string[1] = 'E';
|
||||
detect_string[2] = 'T';
|
||||
detect_string[3] = 'E';
|
||||
detect_string[4] = 'C';
|
||||
detect_string[5] = 'T';
|
||||
detect_string[6] = '\0';
|
||||
|
||||
update_entry.core_path = detect_string;
|
||||
update_entry.core_name = detect_string;
|
||||
/* Reset core association
|
||||
* > The update function reads our entry as const,
|
||||
* so these casts are safe */
|
||||
update_entry.core_path = (char*)"DETECT";
|
||||
update_entry.core_name = (char*)"DETECT";
|
||||
|
||||
playlist_update(
|
||||
pl_manager->playlist, pl_manager->list_index, &update_entry);
|
||||
@ -178,26 +206,15 @@ static void task_pl_manager_reset_cores_handler(retro_task_t *task)
|
||||
break;
|
||||
case PL_MANAGER_END:
|
||||
{
|
||||
playlist_t *cached_playlist = playlist_get_cached();
|
||||
char task_title[PATH_MAX_LENGTH];
|
||||
|
||||
task_title[0] = '\0';
|
||||
|
||||
/* Save playlist changes to disk */
|
||||
playlist_write_file(pl_manager->playlist, pl_manager->use_old_format);
|
||||
|
||||
/* If this is the currently cached playlist, then
|
||||
* it must be re-cached (otherwise changes will be
|
||||
* lost if the currently cached playlist is saved
|
||||
* to disk for any reason...) */
|
||||
if (cached_playlist)
|
||||
{
|
||||
if (string_is_equal(pl_manager->playlist_path, playlist_get_conf_path(cached_playlist)))
|
||||
{
|
||||
playlist_free_cached();
|
||||
playlist_init_cached(pl_manager->playlist_path, COLLECTION_SIZE);
|
||||
}
|
||||
}
|
||||
pl_manager_write_playlist(
|
||||
pl_manager->playlist,
|
||||
pl_manager->playlist_path,
|
||||
pl_manager->use_old_format);
|
||||
|
||||
/* Update progress display */
|
||||
task_free_title(task);
|
||||
@ -255,7 +272,7 @@ bool task_push_pl_manager_reset_cores(const char *playlist_path)
|
||||
task_title[0] = '\0';
|
||||
|
||||
/* Sanity check */
|
||||
if (!task || !pl_manager)
|
||||
if (!task || !pl_manager || !settings)
|
||||
goto error;
|
||||
|
||||
if (string_is_empty(playlist_path))
|
||||
@ -287,13 +304,455 @@ bool task_push_pl_manager_reset_cores(const char *playlist_path)
|
||||
task->progress = 0;
|
||||
|
||||
/* Configure handle */
|
||||
pl_manager->playlist_path = strdup(playlist_path);
|
||||
pl_manager->playlist_name = strdup(playlist_name);
|
||||
pl_manager->playlist = NULL;
|
||||
pl_manager->list_size = 0;
|
||||
pl_manager->list_index = 0;
|
||||
pl_manager->status = PL_MANAGER_BEGIN;
|
||||
pl_manager->use_old_format = settings->bools.playlist_use_old_format;
|
||||
pl_manager->playlist_path = strdup(playlist_path);
|
||||
pl_manager->playlist_name = strdup(playlist_name);
|
||||
pl_manager->playlist = NULL;
|
||||
pl_manager->list_size = 0;
|
||||
pl_manager->list_index = 0;
|
||||
pl_manager->status = PL_MANAGER_BEGIN;
|
||||
pl_manager->use_old_format = settings->bools.playlist_use_old_format;
|
||||
pl_manager->fuzzy_archive_match = false; /* Not relevant here */
|
||||
|
||||
task_queue_push(task);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
|
||||
if (task)
|
||||
{
|
||||
free(task);
|
||||
task = NULL;
|
||||
}
|
||||
|
||||
if (pl_manager)
|
||||
{
|
||||
free(pl_manager);
|
||||
pl_manager = NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/******************/
|
||||
/* 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)
|
||||
{
|
||||
struct playlist_entry update_entry = {0};
|
||||
|
||||
/* Sanity check */
|
||||
if (!playlist)
|
||||
return;
|
||||
|
||||
if (entry_index >= playlist_size(playlist))
|
||||
return;
|
||||
|
||||
if (string_is_empty(core_path))
|
||||
goto reset_core;
|
||||
|
||||
/* Handle 'DETECT' entries */
|
||||
if (string_is_equal(core_path, "DETECT"))
|
||||
{
|
||||
if (!string_is_equal(core_name, "DETECT"))
|
||||
goto reset_core;
|
||||
}
|
||||
/* Handle 'builtin' entries */
|
||||
else if (string_is_equal(core_path, "builtin"))
|
||||
{
|
||||
if (string_is_empty(core_name))
|
||||
goto reset_core;
|
||||
}
|
||||
/* Handle file path entries */
|
||||
else if (!path_is_valid(core_path))
|
||||
goto reset_core;
|
||||
else
|
||||
{
|
||||
const char *core_path_basename = path_basename(core_path);
|
||||
core_info_list_t *core_info = NULL;
|
||||
char core_display_name[PATH_MAX_LENGTH];
|
||||
size_t i;
|
||||
|
||||
core_display_name[0] = '\0';
|
||||
|
||||
if (string_is_empty(core_path_basename))
|
||||
goto reset_core;
|
||||
|
||||
/* Final check - search core info */
|
||||
core_info_get_list(&core_info);
|
||||
|
||||
if (core_info)
|
||||
{
|
||||
for (i = 0; i < core_info->count; i++)
|
||||
{
|
||||
const char *info_display_name = core_info->list[i].display_name;
|
||||
|
||||
if (!string_is_equal(
|
||||
path_basename(core_info->list[i].path), core_path_basename))
|
||||
continue;
|
||||
|
||||
if (!string_is_empty(info_display_name))
|
||||
strlcpy(core_display_name, info_display_name, sizeof(core_display_name));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If core_display_name string is empty, it means the
|
||||
* core wasn't found -> reset association */
|
||||
if (string_is_empty(core_display_name))
|
||||
goto reset_core;
|
||||
|
||||
/* ...Otherwise, check that playlist entry
|
||||
* core name is correct */
|
||||
if (!string_is_equal(core_name, core_display_name))
|
||||
{
|
||||
update_entry.core_name = core_display_name;
|
||||
playlist_update(playlist, entry_index, &update_entry);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
reset_core:
|
||||
/* The update function reads our entry as const,
|
||||
* so these casts are safe */
|
||||
update_entry.core_path = (char*)"DETECT";
|
||||
update_entry.core_name = (char*)"DETECT";
|
||||
|
||||
playlist_update(playlist, entry_index, &update_entry);
|
||||
}
|
||||
|
||||
static void task_pl_manager_clean_playlist_handler(retro_task_t *task)
|
||||
{
|
||||
pl_manager_handle_t *pl_manager = NULL;
|
||||
|
||||
if (!task)
|
||||
goto task_finished;
|
||||
|
||||
pl_manager = (pl_manager_handle_t*)task->state;
|
||||
|
||||
if (!pl_manager)
|
||||
goto task_finished;
|
||||
|
||||
if (task_get_cancelled(task))
|
||||
goto task_finished;
|
||||
|
||||
switch (pl_manager->status)
|
||||
{
|
||||
case PL_MANAGER_BEGIN:
|
||||
{
|
||||
/* Load playlist */
|
||||
if (!path_is_valid(pl_manager->playlist_path))
|
||||
goto task_finished;
|
||||
|
||||
pl_manager->playlist = playlist_init(pl_manager->playlist_path, COLLECTION_SIZE);
|
||||
|
||||
if (!pl_manager->playlist)
|
||||
goto task_finished;
|
||||
|
||||
pl_manager->list_size = playlist_size(pl_manager->playlist);
|
||||
|
||||
if (pl_manager->list_size < 1)
|
||||
goto task_finished;
|
||||
|
||||
/* All good - can start iterating */
|
||||
pl_manager->status = PL_MANAGER_ITERATE_ENTRY_VALIDATE;
|
||||
}
|
||||
break;
|
||||
case PL_MANAGER_ITERATE_ENTRY_VALIDATE:
|
||||
{
|
||||
const struct playlist_entry *entry = NULL;
|
||||
bool entry_deleted = false;
|
||||
|
||||
/* Update progress display */
|
||||
task_set_progress(task, (pl_manager->list_index * 50) / pl_manager->list_size);
|
||||
|
||||
/* Get current entry */
|
||||
playlist_get_index(
|
||||
pl_manager->playlist, pl_manager->list_index, &entry);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
/* Check whether playlist content exists on
|
||||
* the filesystem */
|
||||
if (!pl_manager_content_exists(entry->path))
|
||||
{
|
||||
/* Invalid content - delete entry */
|
||||
playlist_delete_index(pl_manager->playlist, pl_manager->list_index);
|
||||
entry_deleted = true;
|
||||
|
||||
/* Update list_size */
|
||||
pl_manager->list_size = playlist_size(pl_manager->playlist);
|
||||
}
|
||||
/* Content is valid - check if core is valid */
|
||||
else
|
||||
pl_manager_validate_core_association(
|
||||
pl_manager->playlist, pl_manager->list_index,
|
||||
entry->core_path, entry->core_name);
|
||||
}
|
||||
|
||||
/* Increment entry index *if* current entry still
|
||||
* exists (i.e. if entry was deleted, current index
|
||||
* will already point to the *next* entry) */
|
||||
if (!entry_deleted)
|
||||
pl_manager->list_index++;
|
||||
|
||||
if (pl_manager->list_index >= pl_manager->list_size)
|
||||
pl_manager->status = PL_MANAGER_VALIDATE_END;
|
||||
}
|
||||
break;
|
||||
case PL_MANAGER_VALIDATE_END:
|
||||
{
|
||||
/* Sanity check - if all (or all but one)
|
||||
* playlist entries were removed during the
|
||||
* 'validate' phase, we can stop now */
|
||||
if (pl_manager->list_size < 2)
|
||||
{
|
||||
pl_manager->status = PL_MANAGER_END;
|
||||
break;
|
||||
}
|
||||
|
||||
/* ...otherwise, reset index counter and
|
||||
* start the duplicates check */
|
||||
pl_manager->list_index = 0;
|
||||
pl_manager->status = PL_MANAGER_ITERATE_ENTRY_CHECK_DUPLICATE;
|
||||
}
|
||||
break;
|
||||
case PL_MANAGER_ITERATE_ENTRY_CHECK_DUPLICATE:
|
||||
{
|
||||
const struct playlist_entry *entry = NULL;
|
||||
bool entry_deleted = false;
|
||||
|
||||
/* Update progress display */
|
||||
task_set_progress(task, 50 + (pl_manager->list_index * 50) / pl_manager->list_size);
|
||||
|
||||
/* Get current entry */
|
||||
playlist_get_index(
|
||||
pl_manager->playlist, pl_manager->list_index, &entry);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* Loop over all subsequent entries, and check
|
||||
* whether content + core paths are the same */
|
||||
for (i = pl_manager->list_index + 1; i < pl_manager->list_size; i++)
|
||||
{
|
||||
const struct playlist_entry *next_entry = NULL;
|
||||
|
||||
/* Get next entry */
|
||||
playlist_get_index(pl_manager->playlist, i, &next_entry);
|
||||
|
||||
if (!next_entry)
|
||||
continue;
|
||||
|
||||
if (playlist_entries_are_equal(
|
||||
entry, next_entry, pl_manager->fuzzy_archive_match))
|
||||
{
|
||||
/* Duplicate found - delete entry */
|
||||
playlist_delete_index(pl_manager->playlist, pl_manager->list_index);
|
||||
entry_deleted = true;
|
||||
|
||||
/* Update list_size */
|
||||
pl_manager->list_size = playlist_size(pl_manager->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 (!entry_deleted)
|
||||
pl_manager->list_index++;
|
||||
|
||||
if (pl_manager->list_index + 1 >= pl_manager->list_size)
|
||||
pl_manager->status = PL_MANAGER_END;
|
||||
}
|
||||
break;
|
||||
case PL_MANAGER_END:
|
||||
{
|
||||
char task_title[PATH_MAX_LENGTH];
|
||||
|
||||
task_title[0] = '\0';
|
||||
|
||||
/* Save playlist changes to disk */
|
||||
pl_manager_write_playlist(
|
||||
pl_manager->playlist,
|
||||
pl_manager->playlist_path,
|
||||
pl_manager->use_old_format);
|
||||
|
||||
/* Update progress display */
|
||||
task_free_title(task);
|
||||
|
||||
strlcpy(
|
||||
task_title, msg_hash_to_str(MSG_PLAYLIST_MANAGER_PLAYLIST_CLEANED),
|
||||
sizeof(task_title));
|
||||
strlcat(task_title, pl_manager->playlist_name, sizeof(task_title));
|
||||
|
||||
task_set_title(task, strdup(task_title));
|
||||
}
|
||||
/* fall-through */
|
||||
default:
|
||||
task_set_progress(task, 100);
|
||||
goto task_finished;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
task_finished:
|
||||
|
||||
if (task)
|
||||
task_set_finished(task, true);
|
||||
|
||||
free_pl_manager_handle(pl_manager);
|
||||
}
|
||||
|
||||
static bool task_pl_manager_clean_playlist_finder(retro_task_t *task, void *user_data)
|
||||
{
|
||||
pl_manager_handle_t *pl_manager = NULL;
|
||||
|
||||
if (!task || !user_data)
|
||||
return false;
|
||||
|
||||
if (task->handler != task_pl_manager_clean_playlist_handler)
|
||||
return false;
|
||||
|
||||
pl_manager = (pl_manager_handle_t*)task->state;
|
||||
if (!pl_manager)
|
||||
return false;
|
||||
|
||||
return string_is_equal((const char*)user_data, pl_manager->playlist_path);
|
||||
}
|
||||
|
||||
bool task_push_pl_manager_clean_playlist(const char *playlist_path)
|
||||
{
|
||||
task_finder_data_t find_data;
|
||||
char playlist_name[PATH_MAX_LENGTH];
|
||||
char task_title[PATH_MAX_LENGTH];
|
||||
settings_t *settings = config_get_ptr();
|
||||
retro_task_t *task = task_init();
|
||||
pl_manager_handle_t *pl_manager = (pl_manager_handle_t*)calloc(1, sizeof(pl_manager_handle_t));
|
||||
|
||||
playlist_name[0] = '\0';
|
||||
task_title[0] = '\0';
|
||||
|
||||
/* Sanity check */
|
||||
if (!task || !pl_manager || !settings)
|
||||
goto error;
|
||||
|
||||
if (string_is_empty(playlist_path))
|
||||
goto error;
|
||||
|
||||
fill_pathname_base_noext(playlist_name, playlist_path, sizeof(playlist_name));
|
||||
|
||||
if (string_is_empty(playlist_name))
|
||||
goto error;
|
||||
|
||||
/* Concurrent management of the same playlist
|
||||
* is not allowed */
|
||||
find_data.func = task_pl_manager_clean_playlist_finder;
|
||||
find_data.userdata = (void*)playlist_path;
|
||||
|
||||
if (task_queue_find(&find_data))
|
||||
goto error;
|
||||
|
||||
/* Configure task */
|
||||
strlcpy(
|
||||
task_title, msg_hash_to_str(MSG_PLAYLIST_MANAGER_CLEANING_PLAYLIST),
|
||||
sizeof(task_title));
|
||||
strlcat(task_title, playlist_name, sizeof(task_title));
|
||||
|
||||
task->handler = task_pl_manager_clean_playlist_handler;
|
||||
task->state = pl_manager;
|
||||
task->title = strdup(task_title);
|
||||
task->alternative_look = true;
|
||||
task->progress = 0;
|
||||
|
||||
/* Configure handle */
|
||||
pl_manager->playlist_path = strdup(playlist_path);
|
||||
pl_manager->playlist_name = strdup(playlist_name);
|
||||
pl_manager->playlist = NULL;
|
||||
pl_manager->list_size = 0;
|
||||
pl_manager->list_index = 0;
|
||||
pl_manager->status = PL_MANAGER_BEGIN;
|
||||
pl_manager->use_old_format = settings->bools.playlist_use_old_format;
|
||||
pl_manager->fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
|
||||
|
||||
task_queue_push(task);
|
||||
|
||||
|
@ -95,6 +95,7 @@ bool task_push_pl_entry_thumbnail_download(
|
||||
#endif
|
||||
|
||||
bool task_push_pl_manager_reset_cores(const char *playlist_path);
|
||||
bool task_push_pl_manager_clean_playlist(const char *playlist_path);
|
||||
|
||||
bool task_push_image_load(const char *fullpath,
|
||||
bool supports_rgba, unsigned upscale_threshold,
|
||||
|
Loading…
x
Reference in New Issue
Block a user