From 47bb22a4aee886deb54ca432bdb14747bf2e9160 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 30 Apr 2021 16:33:07 +0200 Subject: Support webp thumbnails --- src/AsyncImageLoader.cpp | 25 ++++++++++++++++++++----- src/FileAnalyzer.cpp | 10 ++++++---- src/plugins/Matrix.cpp | 2 +- src/plugins/Youtube.cpp | 15 ++++++++++++++- 4 files changed, 41 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp index 34df062..6504992 100644 --- a/src/AsyncImageLoader.cpp +++ b/src/AsyncImageLoader.cpp @@ -1,5 +1,4 @@ #include "../include/AsyncImageLoader.hpp" -#include "../include/FileAnalyzer.hpp" #include "../include/DownloadUtils.hpp" #include "../include/Program.hpp" #include "../include/ImageUtils.hpp" @@ -11,18 +10,34 @@ #include namespace QuickMedia { - bool create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, sf::Vector2i resize_target_size) { + bool create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, sf::Vector2i resize_target_size, ContentType content_type) { + Path input_path = thumbnail_path; + + // TODO: Remove this when imagemagick supports webp + // Convert webp to png + if(content_type == ContentType::IMAGE_WEBP) { + Path result_path_tmp = input_path; + result_path_tmp.append(".tmp.png"); + + const char *args[] = { "ffmpeg", "-y", "-v", "quiet", "-i", input_path.data.c_str(), "--", result_path_tmp.data.c_str(), nullptr}; + int res = exec_program(args, nullptr, nullptr); + if(res != 0) + return false; + + input_path = std::move(result_path_tmp); + } + // > is to only shrink image if smaller than the target size std::string new_size = std::to_string(resize_target_size.x) + "x" + std::to_string(resize_target_size.y) + ">"; // We only want the first frame if its a gif - Path thumbnail_path_first_frame = thumbnail_path; + Path thumbnail_path_first_frame = std::move(input_path); thumbnail_path_first_frame.append("[0]"); Path result_path_tmp = thumbnail_path_resized; result_path_tmp.append(".tmp"); - const char *args[] = { "convert", thumbnail_path_first_frame.data.c_str(), "-thumbnail", new_size.c_str(), result_path_tmp.data.c_str(), nullptr}; + const char *args[] = { "convert", thumbnail_path_first_frame.data.c_str(), "-thumbnail", new_size.c_str(), result_path_tmp.data.c_str(), nullptr}; int convert_res = exec_program(args, nullptr, nullptr); if(convert_res == 0 && rename_atomic(result_path_tmp.data.c_str(), thumbnail_path_resized.data.c_str()) == 0) return true; @@ -46,7 +61,7 @@ namespace QuickMedia { return; } - if(create_thumbnail(thumbnail_path, thumbnail_path_resized, resize_target_size)) { + if(create_thumbnail(thumbnail_path, thumbnail_path_resized, resize_target_size, file_analyzer.get_content_type())) { load_image_from_file(*thumbnail_data->image, thumbnail_path_resized.data); } else { load_image_from_file(*thumbnail_data->image, thumbnail_path.data); diff --git a/src/FileAnalyzer.cpp b/src/FileAnalyzer.cpp index 9f05919..bd13a01 100644 --- a/src/FileAnalyzer.cpp +++ b/src/FileAnalyzer.cpp @@ -21,7 +21,7 @@ namespace QuickMedia { // What about audio ogg files that are not opus? // TODO: Test all of these - static const std::array magic_numbers = { + static const std::array magic_numbers = { MagicNumber{ {'R', 'I', 'F', 'F', -1, -1, -1, -1, 'A', 'V', 'I', ' '}, 12, ContentType::VIDEO_AVI }, MagicNumber{ {0x00, 0x00, 0x00, -1, 'f', 't', 'y', 'p', 'i', 's', 'o', 'm'}, 12, ContentType::VIDEO_MP4 }, MagicNumber{ {0x00, 0x00, 0x00, -1, 'f', 't', 'y', 'p', 'm', 'p', '4', '2'}, 12, ContentType::VIDEO_MP4 }, @@ -47,7 +47,8 @@ namespace QuickMedia { MagicNumber{ {'G', 'I', 'F', '8', '7', 'a'}, 6, ContentType::IMAGE_GIF }, MagicNumber{ {'G', 'I', 'F', '8', '9', 'a'}, 6, ContentType::IMAGE_GIF }, MagicNumber{ {'B', 'M'}, 2, ContentType::IMAGE_BMP }, - MagicNumber{ {'R', 'I', 'F', 'F', -1, -1, -1, -1, 'W', 'E', 'B', 'V', 'P'}, 6, ContentType::IMAGE_WEBP } + MagicNumber{ {'R', 'I', 'F', 'F', -1, -1, -1, -1, 'W', 'E', 'B', 'P'}, 12, ContentType::IMAGE_WEBP }, + MagicNumber{ {'R', 'I', 'F', 'F', -1, -1, -1, -1, 'W', 'E', 'B', 'V', 'P'}, 13, ContentType::IMAGE_WEBP } }; bool is_content_type_video(ContentType content_type) { @@ -121,7 +122,7 @@ namespace QuickMedia { Path destination_path_tmp = destination_path; destination_path_tmp.append(".ftmp"); - const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-i", filepath, "-vframes", "1", "-f", "singlejpeg", destination_path_tmp.data.c_str(), nullptr }; + const char *program_args[] = { "ffmpeg", "-y", "-v", "quiet", "-i", filepath, "-vframes", "1", "-f", "singlejpeg", "--", destination_path_tmp.data.c_str(), nullptr }; std::string ffmpeg_result; if(exec_program(program_args, nullptr, nullptr) != 0) { fprintf(stderr, "Failed to execute ffmpeg, maybe its not installed?\n"); @@ -129,7 +130,8 @@ namespace QuickMedia { } if(width > 0 && height > 0) { - if(create_thumbnail(destination_path_tmp, destination_path, sf::Vector2i(width, height))) { + FileAnalyzer file_analyzer; + if(file_analyzer.load_file(destination_path_tmp.data.c_str(), false) && create_thumbnail(destination_path_tmp, destination_path, sf::Vector2i(width, height), file_analyzer.get_content_type())) { remove(destination_path_tmp.data.c_str()); return true; } diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 77d8f32..6f8b30b 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -3278,7 +3278,7 @@ namespace QuickMedia { int tmp_file = mkstemp(tmp_filename); if(tmp_file != -1) { std::string thumbnail_path; - if(create_thumbnail(filepath, tmp_filename, thumbnail_max_size)) + if(create_thumbnail(filepath, tmp_filename, thumbnail_max_size, file_analyzer.get_content_type())) thumbnail_path = tmp_filename; else thumbnail_path = filepath; diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index 993c861..cf3bbe4 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -53,10 +53,10 @@ namespace QuickMedia { enum class ThumbnailSize { SMALLEST, + MEDIUM, LARGEST }; - // TODO: Use this in |parse_common_video_item| when QuickMedia supports webp static std::optional yt_json_get_thumbnail(const Json::Value &thumbnail_json, ThumbnailSize thumbnail_size) { if(!thumbnail_json.isObject()) return std::nullopt; @@ -85,6 +85,9 @@ namespace QuickMedia { thumbnails.push_back({ url_json.asCString(), width_json.asInt(), height_json.asInt() }); } + if(thumbnails.empty()) + return std::nullopt; + switch(thumbnail_size) { case ThumbnailSize::SMALLEST: return *std::min_element(thumbnails.begin(), thumbnails.end(), [](const Thumbnail &thumbnail1, const Thumbnail &thumbnail2) { @@ -92,6 +95,14 @@ namespace QuickMedia { int size2 = thumbnail2.width * thumbnail2.height; return size1 < size2; }); + case ThumbnailSize::MEDIUM: { + std::sort(thumbnails.begin(), thumbnails.end(), [](const Thumbnail &thumbnail1, const Thumbnail &thumbnail2) { + int size1 = thumbnail1.width * thumbnail1.height; + int size2 = thumbnail2.width * thumbnail2.height; + return size1 < size2; + }); + return thumbnails[thumbnails.size() / 2]; + } case ThumbnailSize::LARGEST: return *std::max_element(thumbnails.begin(), thumbnails.end(), [](const Thumbnail &thumbnail1, const Thumbnail &thumbnail2) { int size1 = thumbnail1.width * thumbnail1.height; @@ -184,8 +195,10 @@ namespace QuickMedia { body_item->set_description_color(sf::Color(179, 179, 179)); if(scheduled_text.empty()) body_item->url = "https://www.youtube.com/watch?v=" + video_id_str; + body_item->thumbnail_url = "https://img.youtube.com/vi/" + video_id_str + "/hqdefault.jpg"; body_item->thumbnail_size = sf::Vector2i(175, 131); + added_videos.insert(video_id_str); return body_item; } -- cgit v1.2.3