From 8d08b8f3d4a627c3190c485bf43690e414862d83 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 11 May 2021 14:57:45 +0200 Subject: Youtube-dl cant properly extract remote file name, use our own extraction code when not using mpv. Fix close button not working for download menu --- include/DownloadUtils.hpp | 2 ++ src/DownloadUtils.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++ src/QuickMedia.cpp | 63 ++++++++++++++++++++++------------------ 3 files changed, 111 insertions(+), 27 deletions(-) diff --git a/include/DownloadUtils.hpp b/include/DownloadUtils.hpp index 964e74b..620b936 100644 --- a/include/DownloadUtils.hpp +++ b/include/DownloadUtils.hpp @@ -22,6 +22,8 @@ namespace QuickMedia { using DownloadErrorHandler = std::function; DownloadResult download_head_to_string(const std::string &url, std::string &result, bool use_browser_useragent = false, bool fail_on_error = true); + // Returns the remote name from the content-disposition header or tries to extract the file name from url. Can return empty name + DownloadResult url_get_remote_name(const std::string &url, std::string &result, bool use_browser_useragent); DownloadResult download_to_string(const std::string &url, std::string &result, const std::vector &additional_args, bool use_browser_useragent = false, bool fail_on_error = true); // Note: This function saves the content to the file atomically DownloadResult download_to_string_cache(const std::string &url, std::string &result, const std::vector &additional_args, bool use_browser_useragent = false, DownloadErrorHandler error_handler = nullptr, Path cache_path = ""); diff --git a/src/DownloadUtils.cpp b/src/DownloadUtils.cpp index 248fda0..a054454 100644 --- a/src/DownloadUtils.cpp +++ b/src/DownloadUtils.cpp @@ -49,6 +49,79 @@ namespace QuickMedia { return DownloadResult::OK; } + DownloadResult url_get_remote_name(const std::string &url, std::string &result, bool use_browser_useragent) { + sf::Clock timer; + std::vector args; + args.insert(args.end(), { "curl", "-I", "-g", "-H", "Accept-Language: en-US,en;q=0.5", "-H", "Connection: keep-alive", "--compressed", "-s" }); + if(use_browser_useragent) { + args.push_back("-H"); + args.push_back(useragent_str); + } + args.push_back("--"); + args.push_back(url.c_str()); + args.push_back(nullptr); + if(debug_download) { + for(const char *arg : args) { + if(arg) + fprintf(stderr, "'%s' ", arg); + } + fprintf(stderr, "\n"); + } + std::string header; + if(exec_program(args.data(), accumulate_string, &header) != 0) + return DownloadResult::NET_ERR; + fprintf(stderr, "Download duration for %s: %d ms\n", url.c_str(), timer.getElapsedTime().asMilliseconds()); + + std::string content_disposition = header_extract_value(header, "content-disposition"); + if(content_disposition.empty()) { + size_t filename_start = url.rfind('/'); + if(filename_start == std::string::npos) { + result = ""; + return DownloadResult::OK; + } + + ++filename_start; + size_t filename_end = url.size(); + for(size_t i = filename_start; i < url.size(); ++i) { + char c = url[i]; + if(c == '/' || c == '&' || c == '?') { + filename_end = i; + break; + } + } + + result = url.substr(filename_start, filename_end - filename_start); + return DownloadResult::OK; + } else { + size_t filename_start = content_disposition.find("filename="); + if(filename_start == std::string::npos) { + result = ""; + return DownloadResult::OK; + } + + filename_start += 9; + for(size_t i = filename_start; i < content_disposition.size(); ++i) { + char c = content_disposition[i]; + if(c != '"' && c != ' ') { + filename_start = i; + break; + } + } + + size_t filename_end = content_disposition.size(); + for(int i = filename_end - 1; i >= (int)filename_start; --i) { + char c = content_disposition[i]; + if(c != '"' && c != ' ' && c != '\n' && c != '\r') { + filename_end = i + 1; + break; + } + } + + result = content_disposition.substr(filename_start, filename_end - filename_start); + return DownloadResult::OK; + } + } + // TODO: Add timeout DownloadResult download_to_string(const std::string &url, std::string &result, const std::vector &additional_args, bool use_browser_useragent, bool fail_on_error) { sf::Clock timer; diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 79b9207..c515719 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -5945,37 +5945,44 @@ namespace QuickMedia { window.setTitle("QuickMedia - Select where you want to save " + std::string(url)); std::string filename; - TaskResult task_result = run_task_with_loading_screen([this, url, &filename]{ - std::string json_str; - std::vector args = { "youtube-dl", "--skip-download", "--print-json", "--no-warnings" }; - if(no_video) - args.push_back("-x"); - args.insert(args.end(), { "--", url, nullptr }); - if(exec_program(args.data(), accumulate_string, &json_str) != 0) - return false; + TaskResult task_result; + if(download_use_youtube_dl) { + task_result = run_task_with_loading_screen([this, url, &filename]{ + std::string json_str; + std::vector args = { "youtube-dl", "--skip-download", "--print-json", "--no-warnings" }; + if(no_video) + args.push_back("-x"); + args.insert(args.end(), { "--", url, nullptr }); + if(exec_program(args.data(), accumulate_string, &json_str) != 0) + return false; - Json::Value result; - Json::CharReaderBuilder json_builder; - std::unique_ptr json_reader(json_builder.newCharReader()); - std::string json_errors; - if(!json_reader->parse(json_str.data(), json_str.data() + json_str.size(), &result, &json_errors)) { - fprintf(stderr, "Failed to json response, error: %s\n", json_errors.c_str()); - return false; - } + Json::Value result; + Json::CharReaderBuilder json_builder; + std::unique_ptr json_reader(json_builder.newCharReader()); + std::string json_errors; + if(!json_reader->parse(json_str.data(), json_str.data() + json_str.size(), &result, &json_errors)) { + fprintf(stderr, "Failed to json response, error: %s\n", json_errors.c_str()); + return false; + } - const Json::Value &title_json = result["title"]; - const Json::Value &ext_json = result["ext"]; - if(title_json.isString()) - filename = title_json.asString(); + const Json::Value &title_json = result["title"]; + const Json::Value &ext_json = result["ext"]; + if(title_json.isString()) + filename = title_json.asString(); - if(ext_json.isString()) { - if(ext_json.asCString()[0] != '.' && (filename.empty() || filename.back() != '.')) - filename += "."; - filename += ext_json.asString(); - } + if(ext_json.isString()) { + if(ext_json.asCString()[0] != '.' && (filename.empty() || filename.back() != '.')) + filename += "."; + filename += ext_json.asString(); + } - return !filename.empty(); - }); + return !filename.empty(); + }); + } else { + task_result = run_task_with_loading_screen([url, &filename]{ + return url_get_remote_name(url, filename, true) == DownloadResult::OK; + }); + } if(task_result == TaskResult::CANCEL) { exit_code = 1; @@ -6304,6 +6311,8 @@ namespace QuickMedia { std::string save_path = save_file(); if(!save_path.empty()) return save_path; + } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { + window.close(); } } -- cgit v1.2.3