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,