mirror of
https://github.com/libretro/RetroArch
synced 2025-03-18 13:20:57 +00:00
Merge pull request #12963 from jdgleaver/playlist-refresh
(Playlist Manager) Add 'Refresh Playlist' option
This commit is contained in:
commit
27bd9868d7
@ -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"
|
||||
|
@ -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: "
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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},
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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];
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
11
msg_hash.h
11
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,
|
||||
|
437
playlist.c
437
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) */
|
||||
|
31
playlist.h
31
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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user