diff --git a/Makefile.common b/Makefile.common
index 7a3298e887..1cd69e2283 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -1682,7 +1682,8 @@ ifeq ($(HAVE_NETWORKING), 1)
tasks/task_netplay_lan_scan.o \
tasks/task_netplay_nat_traversal.o \
tasks/task_wifi.o \
- tasks/task_netplay_find_content.o
+ tasks/task_netplay_find_content.o \
+ tasks/task_pl_thumbnail_download.o
ifeq ($(HAVE_SSL), 1)
OBJ += $(LIBRETRO_COMM_DIR)/net/net_socket_ssl.o
diff --git a/file_path_special.h b/file_path_special.h
index 481528dd47..f2db2208ee 100644
--- a/file_path_special.h
+++ b/file_path_special.h
@@ -57,6 +57,7 @@ enum file_path_enum
FILE_PATH_CHEATS_ZIP,
FILE_PATH_LAKKA_URL,
FILE_PATH_CORE_THUMBNAILS_URL,
+ FILE_PATH_CORE_THUMBNAILPACKS_URL,
FILE_PATH_INDEX_DIRS_URL,
FILE_PATH_NETPLAY_ROOM_LIST_URL,
FILE_PATH_INDEX_URL,
diff --git a/file_path_str.c b/file_path_str.c
index 43515babc9..d1b0c781c1 100644
--- a/file_path_str.c
+++ b/file_path_str.c
@@ -165,6 +165,9 @@ const char *file_path_str(enum file_path_enum enum_idx)
str = "registry.lpl";
break;
case FILE_PATH_CORE_THUMBNAILS_URL:
+ str = "http://thumbnails.libretro.com";
+ break;
+ case FILE_PATH_CORE_THUMBNAILPACKS_URL:
str = "http://thumbnailpacks.libretro.com";
break;
case FILE_PATH_LAKKA_URL:
diff --git a/griffin/griffin.c b/griffin/griffin.c
index c4ac6be037..c6d55d2a37 100644
--- a/griffin/griffin.c
+++ b/griffin/griffin.c
@@ -1201,6 +1201,9 @@ DATA RUNLOOP
#include "../tasks/task_database.c"
#include "../tasks/task_database_cue.c"
#endif
+#ifdef HAVE_NETWORKING
+#include "../tasks/task_pl_thumbnail_download.c"
+#endif
/*============================================================
SCREENSHOTS
diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h
index dd183a4f9c..fa8d69d982 100644
--- a/intl/msg_hash_lbl.h
+++ b/intl/msg_hash_lbl.h
@@ -387,6 +387,8 @@ MSG_HASH(MENU_ENUM_LABEL_DEFERRED_SAVING_SETTINGS_LIST,
"deferred_saving_settings_list")
MSG_HASH(MENU_ENUM_LABEL_DEFERRED_THUMBNAILS_UPDATER_LIST,
"deferred_thumbnails_updater_list")
+MSG_HASH(MENU_ENUM_LABEL_DEFERRED_PL_THUMBNAILS_UPDATER_LIST,
+ "deferred_pl_thumbnails_updater_list")
MSG_HASH(MENU_ENUM_LABEL_DEFERRED_UPDATER_SETTINGS_LIST,
"deferred_updater_settings_list")
MSG_HASH(MENU_ENUM_LABEL_DEFERRED_USER_BINDS_LIST,
@@ -1155,6 +1157,8 @@ MSG_HASH(MENU_ENUM_LABEL_THUMBNAILS_DIRECTORY,
"thumbnails_directory")
MSG_HASH(MENU_ENUM_LABEL_THUMBNAILS_UPDATER_LIST,
"thumbnails_updater_list")
+MSG_HASH(MENU_ENUM_LABEL_PL_THUMBNAILS_UPDATER_LIST,
+ "pl_thumbnails_updater_list")
MSG_HASH(MENU_ENUM_LABEL_TIMEDATE_ENABLE,
"menu_timedate_enable")
MSG_HASH(MENU_ENUM_LABEL_TIMEDATE_STYLE,
diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h
index 1f0177c7df..c540209e32 100644
--- a/intl/msg_hash_us.h
+++ b/intl/msg_hash_us.h
@@ -3134,6 +3134,18 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_THUMBNAILS_UPDATER_LIST,
"Thumbnails Updater"
)
+MSG_HASH(
+ MENU_ENUM_SUBLABEL_THUMBNAILS_UPDATER_LIST,
+ "Download complete thumbnail package for selected system."
+ )
+MSG_HASH(
+ MENU_ENUM_LABEL_VALUE_PL_THUMBNAILS_UPDATER_LIST,
+ "Playlist Thumbnails Updater"
+ )
+MSG_HASH(
+ MENU_ENUM_SUBLABEL_PL_THUMBNAILS_UPDATER_LIST,
+ "Download individual thumbnails for each entry of selected playlist."
+ )
MSG_HASH(
MENU_ENUM_LABEL_VALUE_THUMBNAIL_MODE_BOXARTS,
"Boxarts"
diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c
index a8e3b470d0..52de59e374 100644
--- a/menu/cbs/menu_cbs_deferred_push.c
+++ b/menu/cbs/menu_cbs_deferred_push.c
@@ -188,6 +188,7 @@ generic_deferred_push(deferred_push_rgui_theme_preset, DISPLAYLIST_
#ifdef HAVE_NETWORKING
generic_deferred_push(deferred_push_thumbnails_updater_list, DISPLAYLIST_THUMBNAILS_UPDATER)
+generic_deferred_push(deferred_push_pl_thumbnails_updater_list, DISPLAYLIST_PL_THUMBNAILS_UPDATER)
generic_deferred_push(deferred_push_core_updater_list, DISPLAYLIST_CORES_UPDATER)
generic_deferred_push(deferred_push_core_content_list, DISPLAYLIST_CORE_CONTENT)
generic_deferred_push(deferred_push_core_content_dirs_list, DISPLAYLIST_CORE_CONTENT_DIRS)
@@ -977,6 +978,11 @@ static int menu_cbs_init_bind_deferred_push_compare_label(
{
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_thumbnails_updater_list);
}
+ else if (strstr(label,
+ msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_PL_THUMBNAILS_UPDATER_LIST)))
+ {
+ BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_pl_thumbnails_updater_list);
+ }
else if (strstr(label,
msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_CORE_CONTENT_LIST)))
{
@@ -1091,6 +1097,11 @@ static int menu_cbs_init_bind_deferred_push_compare_label(
case MENU_ENUM_LABEL_DEFERRED_THUMBNAILS_UPDATER_LIST:
#ifdef HAVE_NETWORKING
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_thumbnails_updater_list);
+#endif
+ break;
+ case MENU_ENUM_LABEL_DEFERRED_PL_THUMBNAILS_UPDATER_LIST:
+#ifdef HAVE_NETWORKING
+ BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_pl_thumbnails_updater_list);
#endif
break;
case MENU_ENUM_LABEL_DEFERRED_LAKKA_LIST:
diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c
index 80ab8d37ac..4e50822413 100644
--- a/menu/cbs/menu_cbs_ok.c
+++ b/menu/cbs/menu_cbs_ok.c
@@ -771,6 +771,15 @@ int generic_action_ok_displaylist_push(const char *path,
info.enum_idx = MENU_ENUM_LABEL_DEFERRED_THUMBNAILS_UPDATER_LIST;
dl_type = DISPLAYLIST_PENDING_CLEAR;
break;
+ case ACTION_OK_DL_PL_THUMBNAILS_UPDATER_LIST:
+ info.type = type;
+ info.directory_ptr = idx;
+ info_path = path;
+ info_label = msg_hash_to_str(
+ MENU_ENUM_LABEL_DEFERRED_PL_THUMBNAILS_UPDATER_LIST);
+ info.enum_idx = MENU_ENUM_LABEL_DEFERRED_PL_THUMBNAILS_UPDATER_LIST;
+ dl_type = DISPLAYLIST_PENDING_CLEAR;
+ break;
case ACTION_OK_DL_CORE_CONTENT_DIRS_SUBDIR_LIST:
fill_pathname_join_delim(tmp, path, label, ';',
sizeof(tmp));
@@ -3412,7 +3421,7 @@ static int generic_action_ok_network(const char *path,
break;
case MENU_ENUM_LABEL_CB_THUMBNAILS_UPDATER_LIST:
fill_pathname_join(url_path,
- file_path_str(FILE_PATH_CORE_THUMBNAILS_URL),
+ file_path_str(FILE_PATH_CORE_THUMBNAILPACKS_URL),
file_path_str(FILE_PATH_INDEX_URL), sizeof(url_path));
url_label = msg_hash_to_str(enum_idx);
type_id2 = ACTION_OK_DL_THUMBNAILS_UPDATER_LIST;
@@ -3567,6 +3576,8 @@ void cb_generic_download(retro_task_t *task,
dir_path = buf;
break;
}
+ case MENU_ENUM_LABEL_CB_SINGLE_THUMBNAIL:
+ break;
default:
RARCH_WARN("Unknown transfer type '%s' bailing out.\n",
msg_hash_to_str(transf->enum_idx));
@@ -3576,8 +3587,18 @@ void cb_generic_download(retro_task_t *task,
if (!string_is_empty(dir_path))
fill_pathname_join(output_path, dir_path,
transf->path, sizeof(output_path));
+ else if (transf->enum_idx == MENU_ENUM_LABEL_CB_SINGLE_THUMBNAIL)
+ {
+ /* In this particular case we have the whole path
+ * already built from the task */
+ strlcpy(output_path, transf->path, sizeof(output_path));
+ }
- /* Make sure the directory exists */
+ /* Make sure the directory exists
+ * This function is horrible. It mutates the original path
+ * so after operating we'll have to set the path to the intended
+ * location again...
+ */
path_basedir_wrapper(output_path);
if (!path_mkdir(output_path))
@@ -3589,14 +3610,20 @@ void cb_generic_download(retro_task_t *task,
if (!string_is_empty(dir_path))
fill_pathname_join(output_path, dir_path,
transf->path, sizeof(output_path));
+ else if (transf->enum_idx == MENU_ENUM_LABEL_CB_SINGLE_THUMBNAIL)
+ {
+ /* In this particular case we have the whole path
+ * already built from the task */
+ strlcpy(output_path, transf->path, sizeof(output_path));
+ }
#ifdef HAVE_COMPRESSION
if (path_is_compressed_file(output_path))
{
if (task_check_decompress(output_path))
{
- err = msg_hash_to_str(MSG_DECOMPRESSION_ALREADY_IN_PROGRESS);
- goto finish;
+ err = msg_hash_to_str(MSG_DECOMPRESSION_ALREADY_IN_PROGRESS);
+ goto finish;
}
}
#endif
@@ -3622,8 +3649,8 @@ void cb_generic_download(retro_task_t *task,
msg_hash_calculate(msg_hash_to_str(transf->enum_idx)),
frontend_userdata))
{
- err = msg_hash_to_str(MSG_DECOMPRESSION_FAILED);
- goto finish;
+ err = msg_hash_to_str(MSG_DECOMPRESSION_FAILED);
+ goto finish;
}
}
#else
@@ -3731,7 +3758,7 @@ static int action_ok_download_generic(const char *path,
path = file_path_str(FILE_PATH_SHADERS_CG_ZIP);
break;
case MENU_ENUM_LABEL_CB_CORE_THUMBNAILS_DOWNLOAD:
- strlcpy(s, file_path_str(FILE_PATH_CORE_THUMBNAILS_URL), sizeof(s));
+ strlcpy(s, file_path_str(FILE_PATH_CORE_THUMBNAILPACKS_URL), sizeof(s));
break;
default:
strlcpy(s, settings->paths.network_buildbot_url, sizeof(s));
@@ -4294,6 +4321,7 @@ default_action_ok_func(action_ok_push_accounts_youtube_list, ACTION_OK_DL_ACCOUN
default_action_ok_func(action_ok_push_accounts_twitch_list, ACTION_OK_DL_ACCOUNTS_TWITCH_LIST)
default_action_ok_func(action_ok_open_archive, ACTION_OK_DL_OPEN_ARCHIVE)
default_action_ok_func(action_ok_rgui_menu_theme_preset, ACTION_OK_DL_RGUI_MENU_THEME_PRESET)
+default_action_ok_func(action_ok_pl_thumbnails_updater_list, ACTION_OK_DL_PL_THUMBNAILS_UPDATER_LIST)
static int action_ok_open_uwp_permission_settings(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
@@ -5337,6 +5365,33 @@ static int action_ok_core_delete(const char *path,
return 0;
}
+static int action_ok_pl_content_thumbnails(const char *path,
+ const char *label, unsigned type, size_t idx, size_t entry_idx)
+{
+#ifdef HAVE_NETWORKING
+ settings_t *settings = config_get_ptr();
+ char playlist_path[PATH_MAX_LENGTH];
+
+ playlist_path[0] = '\0';
+
+ if (!settings)
+ return -1;
+
+ if (string_is_empty(settings->paths.directory_playlist))
+ return -1;
+
+ fill_pathname_join(
+ playlist_path,
+ settings->paths.directory_playlist, label,
+ sizeof(playlist_path));
+
+ task_push_pl_thumbnail_download(path, playlist_path);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
static int is_rdb_entry(enum msg_hash_enums enum_idx)
{
switch (enum_idx)
@@ -5635,6 +5690,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_THUMBNAILS_UPDATER_LIST:
BIND_ACTION_OK(cbs, action_ok_thumbnails_updater_list);
break;
+ case MENU_ENUM_LABEL_PL_THUMBNAILS_UPDATER_LIST:
+ BIND_ACTION_OK(cbs, action_ok_pl_thumbnails_updater_list);
+ break;
case MENU_ENUM_LABEL_UPDATE_LAKKA:
BIND_ACTION_OK(cbs, action_ok_lakka_list);
break;
@@ -6324,6 +6382,9 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs,
case FILE_TYPE_DOWNLOAD_THUMBNAIL_CONTENT:
BIND_ACTION_OK(cbs, action_ok_core_content_thumbnails);
break;
+ case FILE_TYPE_DOWNLOAD_PL_THUMBNAIL_CONTENT:
+ BIND_ACTION_OK(cbs, action_ok_pl_content_thumbnails);
+ break;
case FILE_TYPE_DOWNLOAD_CORE:
BIND_ACTION_OK(cbs, action_ok_core_updater_download);
break;
diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c
index ae82770b2e..9d570d8dce 100644
--- a/menu/cbs/menu_cbs_sublabel.c
+++ b/menu/cbs/menu_cbs_sublabel.c
@@ -568,6 +568,8 @@ default_sublabel_macro(action_bind_sublabel_playlist_sort_alphabetical,
default_sublabel_macro(action_bind_sublabel_playlist_fuzzy_archive_match, MENU_ENUM_SUBLABEL_PLAYLIST_FUZZY_ARCHIVE_MATCH)
default_sublabel_macro(action_bind_sublabel_menu_rgui_full_width_layout, MENU_ENUM_SUBLABEL_MENU_RGUI_FULL_WIDTH_LAYOUT)
default_sublabel_macro(action_bind_sublabel_menu_rgui_extended_ascii, MENU_ENUM_SUBLABEL_MENU_RGUI_EXTENDED_ASCII)
+default_sublabel_macro(action_bind_sublabel_thumbnails_updater_list, MENU_ENUM_SUBLABEL_THUMBNAILS_UPDATER_LIST)
+default_sublabel_macro(action_bind_sublabel_pl_thumbnails_updater_list, MENU_ENUM_SUBLABEL_PL_THUMBNAILS_UPDATER_LIST)
default_sublabel_macro(action_bind_sublabel_help_send_debug_info, MENU_ENUM_SUBLABEL_HELP_SEND_DEBUG_INFO)
static int action_bind_sublabel_systeminfo_controller_entry(
@@ -2533,6 +2535,12 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_MENU_RGUI_EXTENDED_ASCII:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_rgui_extended_ascii);
break;
+ case MENU_ENUM_LABEL_THUMBNAILS_UPDATER_LIST:
+ BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_thumbnails_updater_list);
+ break;
+ case MENU_ENUM_LABEL_PL_THUMBNAILS_UPDATER_LIST:
+ BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_pl_thumbnails_updater_list);
+ break;
case MENU_ENUM_LABEL_HELP_SEND_DEBUG_INFO:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_help_send_debug_info);
break;
diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c
index 0f99d56ec5..e4f9b20ffa 100644
--- a/menu/cbs/menu_cbs_title.c
+++ b/menu/cbs/menu_cbs_title.c
@@ -110,6 +110,7 @@ default_title_macro(action_get_sideload_core_list, MENU_ENUM_LABEL_
default_title_macro(action_get_online_updater_list, MENU_ENUM_LABEL_VALUE_ONLINE_UPDATER)
default_title_macro(action_get_netplay_list, MENU_ENUM_LABEL_VALUE_NETPLAY)
default_title_macro(action_get_online_thumbnails_updater_list, MENU_ENUM_LABEL_VALUE_THUMBNAILS_UPDATER_LIST)
+default_title_macro(action_get_online_pl_thumbnails_updater_list, MENU_ENUM_LABEL_VALUE_PL_THUMBNAILS_UPDATER_LIST)
default_title_macro(action_get_core_updater_list, MENU_ENUM_LABEL_VALUE_CORE_UPDATER_LIST)
default_title_macro(action_get_add_content_list, MENU_ENUM_LABEL_VALUE_ADD_CONTENT_LIST)
default_title_macro(action_get_configurations_list, MENU_ENUM_LABEL_VALUE_CONFIGURATIONS_LIST)
@@ -773,6 +774,9 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_DEFERRED_THUMBNAILS_UPDATER_LIST:
BIND_ACTION_GET_TITLE(cbs, action_get_online_thumbnails_updater_list);
break;
+ case MENU_ENUM_LABEL_DEFERRED_PL_THUMBNAILS_UPDATER_LIST:
+ BIND_ACTION_GET_TITLE(cbs, action_get_online_pl_thumbnails_updater_list);
+ break;
case MENU_ENUM_LABEL_DEFERRED_CORE_UPDATER_LIST:
BIND_ACTION_GET_TITLE(cbs, action_get_core_updater_list);
break;
@@ -1118,6 +1122,9 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs,
case MENU_LABEL_DEFERRED_THUMBNAILS_UPDATER_LIST:
BIND_ACTION_GET_TITLE(cbs, action_get_online_thumbnails_updater_list);
break;
+ case MENU_LABEL_DEFERRED_PL_THUMBNAILS_UPDATER_LIST:
+ BIND_ACTION_GET_TITLE(cbs, action_get_online_pl_thumbnails_updater_list);
+ break;
case MENU_LABEL_DEFERRED_CORE_UPDATER_LIST:
BIND_ACTION_GET_TITLE(cbs, action_get_core_updater_list);
break;
diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h
index 1853a34bae..1d4ad1acee 100644
--- a/menu/menu_cbs.h
+++ b/menu/menu_cbs.h
@@ -115,6 +115,7 @@ enum
ACTION_OK_DL_CURSOR_MANAGER_LIST,
ACTION_OK_DL_CORE_UPDATER_LIST,
ACTION_OK_DL_THUMBNAILS_UPDATER_LIST,
+ ACTION_OK_DL_PL_THUMBNAILS_UPDATER_LIST,
ACTION_OK_DL_BROWSE_URL_LIST,
ACTION_OK_DL_CORE_CONTENT_LIST,
ACTION_OK_DL_CORE_CONTENT_DIRS_LIST,
diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c
index 2f28e119d4..f17b941da5 100644
--- a/menu/menu_displaylist.c
+++ b/menu/menu_displaylist.c
@@ -2562,6 +2562,56 @@ static void menu_displaylist_parse_playlist_associations(
string_list_free(str_list);
}
+static unsigned menu_displaylist_parse_pl_thumbnail_download_list(
+ menu_displaylist_info_t *info)
+{
+ settings_t *settings = config_get_ptr();
+ unsigned count = 0;
+ struct string_list *str_list = NULL;
+
+ if (!settings)
+ return count;
+
+ str_list = dir_list_new_special(
+ settings->paths.directory_playlist,
+ DIR_LIST_COLLECTIONS, NULL);
+
+ if (str_list && str_list->size)
+ {
+ unsigned i;
+
+ dir_list_sort(str_list, true);
+
+ for (i = 0; i < str_list->size; i++)
+ {
+ char path_base[PATH_MAX_LENGTH];
+ const char *path =
+ path_basename(str_list->elems[i].data);
+
+ path_base[0] = '\0';
+
+ if (string_is_empty(path))
+ continue;
+
+ strlcpy(path_base, path, sizeof(path_base));
+ path_remove_extension(path_base);
+
+ menu_entries_append_enum(info->list,
+ path_base,
+ path,
+ MENU_ENUM_LABEL_PLAYLIST_ENTRY,
+ FILE_TYPE_DOWNLOAD_PL_THUMBNAIL_CONTENT,
+ 0, 0);
+ count++;
+ }
+ }
+
+ /* Not necessary to check for NULL here */
+ string_list_free(str_list);
+
+ return count;
+}
+
static bool menu_displaylist_push_internal(
const char *label,
menu_displaylist_ctx_entry_t *entry,
@@ -4919,6 +4969,23 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
(int)menu->core_len, FILE_TYPE_DOWNLOAD_THUMBNAIL_CONTENT,
true, false);
+ if (count == 0)
+ menu_entries_append_enum(info->list,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY),
+ msg_hash_to_str(MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY),
+ MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY,
+ FILE_TYPE_NONE, 0, 0);
+
+ info->need_push = true;
+ info->need_refresh = true;
+ info->need_clear = true;
+#endif
+ break;
+ case DISPLAYLIST_PL_THUMBNAILS_UPDATER:
+#ifdef HAVE_NETWORKING
+ menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
+ count = menu_displaylist_parse_pl_thumbnail_download_list(info);
+
if (count == 0)
menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY),
@@ -6208,6 +6275,12 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
MENU_ENUM_LABEL_THUMBNAILS_UPDATER_LIST,
MENU_SETTING_ACTION, 0, 0))
count++;
+ if (menu_entries_append_enum(info->list,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PL_THUMBNAILS_UPDATER_LIST),
+ msg_hash_to_str(MENU_ENUM_LABEL_PL_THUMBNAILS_UPDATER_LIST),
+ MENU_ENUM_LABEL_PL_THUMBNAILS_UPDATER_LIST,
+ MENU_SETTING_ACTION, 0, 0))
+ count++;
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DOWNLOAD_CORE_CONTENT),
msg_hash_to_str(MENU_ENUM_LABEL_DOWNLOAD_CORE_CONTENT_DIRS),
@@ -6235,6 +6308,13 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
MENU_SETTING_ACTION, 0, 0))
count++;
+ if (menu_entries_append_enum(info->list,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PL_THUMBNAILS_UPDATER_LIST),
+ msg_hash_to_str(MENU_ENUM_LABEL_PL_THUMBNAILS_UPDATER_LIST),
+ MENU_ENUM_LABEL_PL_THUMBNAILS_UPDATER_LIST,
+ MENU_SETTING_ACTION, 0, 0))
+ count++;
+
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DOWNLOAD_CORE_CONTENT),
msg_hash_to_str(MENU_ENUM_LABEL_DOWNLOAD_CORE_CONTENT_DIRS),
diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h
index c0dd6a4bcf..e0283a6460 100644
--- a/menu/menu_displaylist.h
+++ b/menu/menu_displaylist.h
@@ -88,6 +88,7 @@ enum menu_displaylist_ctl_state
DISPLAYLIST_CORES_COLLECTION_SUPPORTED,
DISPLAYLIST_CORES_UPDATER,
DISPLAYLIST_THUMBNAILS_UPDATER,
+ DISPLAYLIST_PL_THUMBNAILS_UPDATER,
DISPLAYLIST_LAKKA,
DISPLAYLIST_CORES_DETECTED,
DISPLAYLIST_CORE_OPTIONS,
diff --git a/menu/menu_thumbnail_path.c b/menu/menu_thumbnail_path.c
index 4cc7a4af6d..6babac1f61 100644
--- a/menu/menu_thumbnail_path.c
+++ b/menu/menu_thumbnail_path.c
@@ -87,6 +87,34 @@ void menu_thumbnail_path_reset(menu_thumbnail_path_data_t *path_data)
/* Utility Functions */
+/* Fetches the thumbnail subdirectory (Named_Snaps,
+ * Named_Titles, Named_Boxarts) corresponding to the
+ * specified 'type index' (1, 2, 3).
+ * Returns true if 'type index' is valid */
+bool menu_thumbnail_get_sub_directory(unsigned type_idx, const char **sub_directory)
+{
+ if (!sub_directory)
+ return false;
+
+ switch (type_idx)
+ {
+ case 1:
+ *sub_directory = "Named_Snaps";
+ return true;
+ case 2:
+ *sub_directory = "Named_Titles";
+ return true;
+ case 3:
+ *sub_directory = "Named_Boxarts";
+ return true;
+ case 0:
+ default:
+ break;
+ }
+
+ return false;
+}
+
/* Returns currently set thumbnail 'type' (Named_Snaps,
* Named_Titles, Named_Boxarts) for specified thumbnail
* identifier (right, left) */
@@ -569,6 +597,42 @@ bool menu_thumbnail_get_path(menu_thumbnail_path_data_t *path_data, enum menu_th
return true;
}
+/* Fetches current 'system' (default database name).
+ * Returns true if 'system' is valid. */
+bool menu_thumbnail_get_system(menu_thumbnail_path_data_t *path_data, const char **system)
+{
+ if (!path_data)
+ return false;
+
+ if (!system)
+ return false;
+
+ if (string_is_empty(path_data->system))
+ return false;
+
+ *system = path_data->system;
+
+ return true;
+}
+
+/* Fetches current content path.
+ * Returns true if content path is valid. */
+bool menu_thumbnail_get_content_path(menu_thumbnail_path_data_t *path_data, const char **content_path)
+{
+ if (!path_data)
+ return false;
+
+ if (!content_path)
+ return false;
+
+ if (string_is_empty(path_data->content_path))
+ return false;
+
+ *content_path = path_data->content_path;
+
+ return true;
+}
+
/* Fetches current thumbnail label.
* Returns true if label is valid. */
bool menu_thumbnail_get_label(menu_thumbnail_path_data_t *path_data, const char **label)
@@ -604,3 +668,40 @@ bool menu_thumbnail_get_core_name(menu_thumbnail_path_data_t *path_data, const c
return true;
}
+
+/* Fetches current database name.
+ * Returns true if database name is valid. */
+bool menu_thumbnail_get_db_name(menu_thumbnail_path_data_t *path_data, const char **db_name)
+{
+ if (!path_data)
+ return false;
+
+ if (!db_name)
+ return false;
+
+ if (string_is_empty(path_data->content_db_name))
+ return false;
+
+ *db_name = path_data->content_db_name;
+
+ return true;
+}
+
+/* Fetches current thumbnail image name
+ * (name is the same for all thumbnail types).
+ * Returns true if image name is valid. */
+bool menu_thumbnail_get_img_name(menu_thumbnail_path_data_t *path_data, const char **img_name)
+{
+ if (!path_data)
+ return false;
+
+ if (!img_name)
+ return false;
+
+ if (string_is_empty(path_data->content_img))
+ return false;
+
+ *img_name = path_data->content_img;
+
+ return true;
+}
diff --git a/menu/menu_thumbnail_path.h b/menu/menu_thumbnail_path.h
index 8ff215d382..d96df13706 100644
--- a/menu/menu_thumbnail_path.h
+++ b/menu/menu_thumbnail_path.h
@@ -64,6 +64,12 @@ void menu_thumbnail_path_reset(menu_thumbnail_path_data_t *path_data);
/* Utility Functions */
+/* Fetches the thumbnail subdirectory (Named_Snaps,
+ * Named_Titles, Named_Boxarts) corresponding to the
+ * specified 'type index' (1, 2, 3).
+ * Returns true if 'type index' is valid */
+bool menu_thumbnail_get_sub_directory(unsigned type_idx, const char **sub_directory);
+
/* Returns currently set thumbnail 'type' (Named_Snaps,
* Named_Titles, Named_Boxarts) for specified thumbnail
* identifier (right, left) */
@@ -116,6 +122,14 @@ bool menu_thumbnail_update_path(menu_thumbnail_path_data_t *path_data, enum menu
* Returns true if path is valid. */
bool menu_thumbnail_get_path(menu_thumbnail_path_data_t *path_data, enum menu_thumbnail_id thumbnail_id, const char **path);
+/* Fetches current 'system' (default database name).
+ * Returns true if 'system' is valid. */
+bool menu_thumbnail_get_system(menu_thumbnail_path_data_t *path_data, const char **system);
+
+/* Fetches current content path.
+ * Returns true if content path is valid. */
+bool menu_thumbnail_get_content_path(menu_thumbnail_path_data_t *path_data, const char **content_path);
+
/* Fetches current thumbnail label.
* Returns true if label is valid. */
bool menu_thumbnail_get_label(menu_thumbnail_path_data_t *path_data, const char **label);
@@ -124,6 +138,15 @@ bool menu_thumbnail_get_label(menu_thumbnail_path_data_t *path_data, const char
* Returns true if core name is valid. */
bool menu_thumbnail_get_core_name(menu_thumbnail_path_data_t *path_data, const char **core_name);
+/* Fetches current database name.
+ * Returns true if database name is valid. */
+bool menu_thumbnail_get_db_name(menu_thumbnail_path_data_t *path_data, const char **db_name);
+
+/* Fetches current thumbnail image name
+ * (name is the same for all thumbnail types).
+ * Returns true if image name is valid. */
+bool menu_thumbnail_get_img_name(menu_thumbnail_path_data_t *path_data, const char **img_name);
+
RETRO_END_DECLS
#endif
diff --git a/msg_hash.h b/msg_hash.h
index a6f083d464..8f1f817098 100644
--- a/msg_hash.h
+++ b/msg_hash.h
@@ -147,6 +147,10 @@ enum msg_file_type
FILE_TYPE_GONG,
+ /* Note: New entries must be added at the end, otherwise
+ * menu_cbs_init_bind_get_string_representation_compare_type() breaks... */
+ FILE_TYPE_DOWNLOAD_PL_THUMBNAIL_CONTENT,
+
FILE_TYPE_LAST
};
@@ -1056,6 +1060,7 @@ enum msg_hash_enums
MENU_LABEL(QUICK_MENU_VIEWS_SETTINGS),
MENU_LABEL(MENU_SETTINGS),
MENU_LABEL(THUMBNAILS_UPDATER_LIST),
+ MENU_LABEL(PL_THUMBNAILS_UPDATER_LIST),
MENU_LABEL(USER_INTERFACE_SETTINGS),
MENU_LABEL(POWER_MANAGEMENT_SETTINGS),
MENU_LABEL(RETRO_ACHIEVEMENTS_SETTINGS),
@@ -1170,6 +1175,7 @@ enum msg_hash_enums
MENU_ENUM_LABEL_DEFERRED_CORE_LIST,
MENU_ENUM_LABEL_DEFERRED_CORE_UPDATER_LIST,
MENU_ENUM_LABEL_DEFERRED_THUMBNAILS_UPDATER_LIST,
+ MENU_ENUM_LABEL_DEFERRED_PL_THUMBNAILS_UPDATER_LIST,
MENU_ENUM_LABEL_DEFERRED_RECORDING_SETTINGS_LIST,
MENU_ENUM_LABEL_DEFERRED_PLAYLIST_SETTINGS_LIST,
MENU_ENUM_LABEL_DEFERRED_INPUT_SETTINGS_LIST,
@@ -2068,6 +2074,7 @@ enum msg_hash_enums
MENU_ENUM_LABEL_CB_MENU_LEFT_THUMBNAIL,
MENU_ENUM_LABEL_CB_MENU_SAVESTATE_THUMBNAIL,
MENU_ENUM_LABEL_CB_MENU_WALLPAPER,
+ MENU_ENUM_LABEL_CB_PL_THUMBNAILS_DOWNLOAD,
MENU_ENUM_LABEL_CB_THUMBNAILS_UPDATER_DOWNLOAD,
MENU_ENUM_LABEL_CB_THUMBNAILS_UPDATER_LIST,
MENU_ENUM_LABEL_CB_UPDATE_ASSETS,
@@ -2080,6 +2087,7 @@ enum msg_hash_enums
MENU_ENUM_LABEL_CB_UPDATE_SHADERS_GLSL,
MENU_ENUM_LABEL_CB_UPDATE_SHADERS_SLANG,
MENU_ENUM_LABEL_CB_DISCORD_AVATAR,
+ MENU_ENUM_LABEL_CB_SINGLE_THUMBNAIL,
/* Sublabels */
MENU_ENUM_SUBLABEL_MIXER_ACTION_PLAY,
@@ -2392,6 +2400,7 @@ enum msg_hash_enums
/* Deferred */
#define MENU_LABEL_DEFERRED_THUMBNAILS_UPDATER_LIST 0x364dfa2bU
+#define MENU_LABEL_DEFERRED_PL_THUMBNAILS_UPDATER_LIST 0x428E8466U
#define MENU_LABEL_DEFERRED_VIDEO_FILTER 0x966ad201U
#define MENU_LABEL_DEFERRED_CORE_LIST_SET 0xa6d5fdb4U
#define MENU_LABEL_DEFERRED_DATABASE_MANAGER_LIST 0x7c0b704fU
diff --git a/tasks/task_pl_thumbnail_download.c b/tasks/task_pl_thumbnail_download.c
new file mode 100644
index 0000000000..850b320405
--- /dev/null
+++ b/tasks/task_pl_thumbnail_download.c
@@ -0,0 +1,362 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2011-2017 - Daniel De Matteis
+ * Copyright (C) 2014-2017 - Jean-André Santoni
+ * Copyright (C) 2016-2019 - Brad Parker
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "tasks_internal.h"
+#include "task_file_transfer.h"
+
+#include "../configuration.h"
+#include "../file_path_special.h"
+#include "../playlist.h"
+#include "../menu/menu_thumbnail_path.h"
+#include "../menu/menu_cbs.h"
+
+#ifndef COLLECTION_SIZE
+#define COLLECTION_SIZE 99999
+#endif
+
+enum pl_thumb_status
+{
+ PL_THUMB_BEGIN = 0,
+ PL_THUMB_ITERATE_ENTRY,
+ PL_THUMB_ITERATE_TYPE,
+ PL_THUMB_END
+};
+
+typedef struct pl_thumb_handle
+{
+ char *system;
+ char *playlist_path;
+ playlist_t *playlist;
+ menu_thumbnail_path_data_t *thumbnail_path_data;
+ retro_task_t *http_task;
+ size_t list_size;
+ size_t list_index;
+ unsigned type_idx;
+ enum pl_thumb_status status;
+} pl_thumb_handle_t;
+
+/* Fetches local and remote paths for current thumbnail
+ * of current type */
+static bool get_thumbnail_paths(
+ pl_thumb_handle_t *pl_thumb,
+ char *path, size_t path_size,
+ char *url, size_t url_size)
+{
+ settings_t *settings = config_get_ptr();
+ const char *system = NULL;
+ const char *db_name = NULL;
+ const char *img_name = NULL;
+ const char *sub_dir = NULL;
+ const char *system_name = NULL;
+ char raw_url[2048];
+ char tmp_buf[PATH_MAX_LENGTH];
+
+ raw_url[0] = '\0';
+ tmp_buf[0] = '\0';
+
+ /* Sanity check */
+ if (!pl_thumb || !settings)
+ return false;
+
+ if (!pl_thumb->thumbnail_path_data)
+ return false;
+
+ if (string_is_empty(settings->paths.directory_thumbnails))
+ return false;
+
+ /* Extract required strings */
+ menu_thumbnail_get_system(pl_thumb->thumbnail_path_data, &system);
+ menu_thumbnail_get_db_name(pl_thumb->thumbnail_path_data, &db_name);
+ if (!menu_thumbnail_get_img_name(pl_thumb->thumbnail_path_data, &img_name))
+ return false;
+ if (!menu_thumbnail_get_sub_directory(pl_thumb->type_idx, &sub_dir))
+ return false;
+
+ /* Dermine system name */
+ system_name = string_is_empty(db_name) ? system : db_name;
+ if (string_is_empty(system_name))
+ return false;
+
+ /* Generate local path */
+ fill_pathname_join(path, settings->paths.directory_thumbnails,
+ system_name, path_size);
+ fill_pathname_join(tmp_buf, path, sub_dir, sizeof(tmp_buf));
+ fill_pathname_join(path, tmp_buf, img_name, path_size);
+
+ if (string_is_empty(path))
+ return false;
+
+ /* Generate remote path */
+ strlcpy(raw_url, file_path_str(FILE_PATH_CORE_THUMBNAILS_URL), sizeof(raw_url));
+ strlcat(raw_url, "/", sizeof(raw_url));
+ strlcat(raw_url, system_name, sizeof(raw_url));
+ strlcat(raw_url, "/", sizeof(raw_url));
+ strlcat(raw_url, sub_dir, sizeof(raw_url));
+ strlcat(raw_url, "/", sizeof(raw_url));
+ strlcat(raw_url, img_name, sizeof(raw_url));
+
+ if (string_is_empty(raw_url))
+ return false;
+
+ net_http_urlencode_full(url, raw_url, url_size);
+
+ if (string_is_empty(url))
+ return false;
+
+ return true;
+}
+
+/* Download thumbnail of the current type for the current
+ * playlist entry */
+static void download_pl_thumbnail(pl_thumb_handle_t *pl_thumb)
+{
+ char path[PATH_MAX_LENGTH];
+ char url[2048];
+
+ path[0] = '\0';
+ url[0] = '\0';
+
+ /* Sanity check */
+ if (!pl_thumb)
+ return;
+
+ /* Check if paths are valid */
+ if (get_thumbnail_paths(pl_thumb, path, sizeof(path), url, sizeof(url)))
+ {
+ /* Only download missing thumbnails */
+ if (!filestream_exists(path))
+ {
+ file_transfer_t *transf = (file_transfer_t*)calloc(1, sizeof(file_transfer_t));
+ if (!transf)
+ return; /* If this happens then everything is broken anyway... */
+
+ /* Initialise file transfer */
+ transf->enum_idx = MENU_ENUM_LABEL_CB_SINGLE_THUMBNAIL;
+ strlcpy(transf->path, path, sizeof(transf->path));
+
+ /* Note: We don't actually care if this fails since that
+ * just means the file is missing from the server, so it's
+ * not something we can handle here... */
+ pl_thumb->http_task = task_push_http_transfer(url, true, NULL, cb_generic_download, transf);
+ }
+ }
+}
+
+static void task_pl_thumbnail_download_handler(retro_task_t *task)
+{
+ pl_thumb_handle_t *pl_thumb = NULL;
+
+ if (!task)
+ goto task_finished;
+
+ pl_thumb = (pl_thumb_handle_t*)task->state;
+
+ if (!pl_thumb)
+ goto task_finished;
+
+ if (task_get_cancelled(task))
+ goto task_finished;
+
+ switch (pl_thumb->status)
+ {
+ case PL_THUMB_BEGIN:
+ {
+ /* Load playlist */
+ if (!filestream_exists(pl_thumb->playlist_path))
+ goto task_finished;
+
+ pl_thumb->playlist = playlist_init(pl_thumb->playlist_path, COLLECTION_SIZE);
+
+ if (!pl_thumb->playlist)
+ goto task_finished;
+
+ pl_thumb->list_size = playlist_size(pl_thumb->playlist);
+
+ if (pl_thumb->list_size < 1)
+ goto task_finished;
+
+ /* Initialise thumbnail path data */
+ pl_thumb->thumbnail_path_data = menu_thumbnail_path_init();
+
+ if (!pl_thumb->thumbnail_path_data)
+ goto task_finished;
+
+ if (!menu_thumbnail_set_system(pl_thumb->thumbnail_path_data, pl_thumb->system))
+ goto task_finished;
+
+ /* All good - can start iterating */
+ pl_thumb->status = PL_THUMB_ITERATE_ENTRY;
+ }
+ break;
+ case PL_THUMB_ITERATE_ENTRY:
+ {
+ /* Set current thumbnail content */
+ if (menu_thumbnail_set_content_playlist(
+ pl_thumb->thumbnail_path_data, pl_thumb->playlist, pl_thumb->list_index))
+ {
+ const char *label = NULL;
+
+ /* Update progress display */
+ task_free_title(task);
+ if (menu_thumbnail_get_label(pl_thumb->thumbnail_path_data, &label))
+ task_set_title(task, strdup(label));
+ else
+ task_set_title(task, strdup(""));
+ task_set_progress(task, (pl_thumb->list_index * 100) / pl_thumb->list_size);
+
+ /* Start iterating over thumbnail type */
+ pl_thumb->type_idx = 1;
+ pl_thumb->status = PL_THUMB_ITERATE_TYPE;
+ }
+ }
+ break;
+ case PL_THUMB_ITERATE_TYPE:
+ {
+ /* Ensure that we only enqueue one transfer
+ * at a time... */
+ if (pl_thumb->http_task)
+ {
+ if (task_get_finished(pl_thumb->http_task))
+ pl_thumb->http_task = NULL;
+ else
+ break;
+ }
+
+ /* Download current thumbnail */
+ download_pl_thumbnail(pl_thumb);
+
+ /* Increment thumbnail type */
+ pl_thumb->type_idx++;
+ if (pl_thumb->type_idx > 3)
+ {
+ /* Time to move on to the next entry */
+ pl_thumb->list_index++;
+ if (pl_thumb->list_index < pl_thumb->list_size)
+ pl_thumb->status = PL_THUMB_ITERATE_ENTRY;
+ else
+ pl_thumb->status = PL_THUMB_END;
+ }
+ }
+ break;
+ case PL_THUMB_END:
+ default:
+ task_set_progress(task, 100);
+ goto task_finished;
+ break;
+ }
+
+ return;
+
+task_finished:
+
+ if (task)
+ task_set_finished(task, true);
+
+ if (pl_thumb)
+ {
+ if (!string_is_empty(pl_thumb->system))
+ {
+ free(pl_thumb->system);
+ pl_thumb->system = NULL;
+ }
+
+ if (!string_is_empty(pl_thumb->playlist_path))
+ {
+ free(pl_thumb->playlist_path);
+ pl_thumb->playlist_path = NULL;
+ }
+
+ if (pl_thumb->playlist)
+ {
+ playlist_free(pl_thumb->playlist);
+ pl_thumb->playlist = NULL;
+ }
+
+ if (pl_thumb->thumbnail_path_data)
+ {
+ free(pl_thumb->thumbnail_path_data);
+ pl_thumb->thumbnail_path_data = NULL;
+ }
+
+ free(pl_thumb);
+ pl_thumb = NULL;
+ }
+}
+
+bool task_push_pl_thumbnail_download(
+ const char *system, const char *playlist_path)
+{
+ retro_task_t *task = task_init();
+ pl_thumb_handle_t *pl_thumb = (pl_thumb_handle_t*)calloc(1, sizeof(pl_thumb_handle_t));
+
+ /* Sanity check */
+ if (!task || !pl_thumb || string_is_empty(system) || string_is_empty(playlist_path))
+ goto error;
+
+ if (string_is_equal(system, "history") ||
+ string_is_equal(system, "favorites") ||
+ string_is_equal(system, "images_history"))
+ goto error;
+
+ /* Configure task */
+ task->handler = task_pl_thumbnail_download_handler;
+ task->state = pl_thumb;
+ task->title = strdup(system);
+ task->alternative_look = true;
+ task->progress = 0;
+
+ /* Configure handle */
+ pl_thumb->system = strdup(system);
+ pl_thumb->playlist_path = strdup(playlist_path);
+ pl_thumb->playlist = NULL;
+ pl_thumb->thumbnail_path_data = NULL;
+ pl_thumb->http_task = NULL;
+ pl_thumb->list_size = 0;
+ pl_thumb->list_index = 0;
+ pl_thumb->type_idx = 1;
+ pl_thumb->status = PL_THUMB_BEGIN;
+
+ task_queue_push(task);
+
+ return true;
+
+error:
+
+ if (task)
+ {
+ free(task);
+ task = NULL;
+ }
+
+ if (pl_thumb)
+ {
+ free(pl_thumb);
+ pl_thumb = NULL;
+ }
+
+ return false;
+}
diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h
index 9ffa6e6920..ca17bd48a9 100644
--- a/tasks/tasks_internal.h
+++ b/tasks/tasks_internal.h
@@ -56,6 +56,8 @@ bool task_push_netplay_lan_scan_rooms(retro_task_callback_t cb);
bool task_push_netplay_nat_traversal(void *nat_traversal_state, uint16_t port);
+bool task_push_pl_thumbnail_download(const char *system, const char *playlist_path);
+
#endif
bool task_push_image_load(const char *fullpath,