From 5ec1811fa7499386f324f13a3a49dbe7d8ea6741 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 19 Jun 2021 13:51:37 +0200 Subject: Fix youtube sometimes needing a redirect for media url --- include/QuickMedia.hpp | 1 + plugins/Page.hpp | 6 ++-- src/QuickMedia.cpp | 77 +++++++++++++++++++++++++++---------------------- src/plugins/Youtube.cpp | 30 +++++++++++++++++-- 4 files changed, 75 insertions(+), 39 deletions(-) diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp index a1656aa..7078791 100644 --- a/include/QuickMedia.hpp +++ b/include/QuickMedia.hpp @@ -192,6 +192,7 @@ namespace QuickMedia { bool fit_image_to_window = false; RoomData *current_chat_room = nullptr; bool go_to_previous_page = false; + bool running_task_with_loading_screen = false; sf::Text tab_text; sf::Vertex gradient_points[4]; sf::Vector2f body_pos; diff --git a/plugins/Page.hpp b/plugins/Page.hpp index 0d45b9e..2d10d50 100644 --- a/plugins/Page.hpp +++ b/plugins/Page.hpp @@ -117,13 +117,15 @@ namespace QuickMedia { std::string get_url() { return url; } // Returns empty string for no timestamp or if the video doesn't support timestamps virtual std::string get_url_timestamp() { return ""; } - // Falls back to |get_url| if this and |get_audio_url| returns empty strings + // Falls back to |get_url| if this and |get_audio_url| returns empty strings. + // Might do a network request. virtual std::string get_video_url(int max_height, bool &has_embedded_audio) { (void)max_height; has_embedded_audio = true; return ""; } - // Only used if |get_video_url| sets |has_embedded_audio| to false + // Only used if |get_video_url| sets |has_embedded_audio| to false. + // Might do a network request. virtual std::string get_audio_url() { return ""; } virtual std::string url_get_playable_url(const std::string &url) { return url; } virtual bool video_should_be_skipped(const std::string &url) { (void)url; return false; } diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index a4ea258..c02b9ab 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -2295,10 +2295,13 @@ namespace QuickMedia { TaskResult Program::run_task_with_loading_screen(std::function callback) { assert(std::this_thread::get_id() == main_thread_id); + if(running_task_with_loading_screen) + return callback() ? TaskResult::TRUE : TaskResult::FALSE; + running_task_with_loading_screen = true; idle_active_handler(); - AsyncTask task = callback; + TaskResult task_result = TaskResult::TRUE; window_size.x = window.getSize().x; window_size.y = window.getSize().y; @@ -2312,20 +2315,20 @@ namespace QuickMedia { window.setView(sf::View(visible_area)); } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { task.cancel(); - return TaskResult::CANCEL; + task_result = TaskResult::CANCEL; + goto task_end; } } if(handle_window_close()) { task.cancel(); - return TaskResult::CANCEL; + task_result = TaskResult::CANCEL; + goto task_end; } if(task.ready()) { - if(task.get()) - return TaskResult::TRUE; - else - return TaskResult::FALSE; + task_result = task.get() ? TaskResult::TRUE : TaskResult::FALSE; + goto task_end; } window.clear(back_color); @@ -2335,7 +2338,9 @@ namespace QuickMedia { window.display(); } - return TaskResult::TRUE; + task_end: + running_task_with_loading_screen = false; + return task_result; } static bool video_url_supports_timestamp(const std::string &url) { @@ -2507,8 +2512,35 @@ namespace QuickMedia { if(!reuse_media_source) { std::string new_title; - TaskResult load_result = run_task_with_loading_screen([video_page, &new_title, &channel_url, &media_chapters]() { - return video_page->load(new_title, channel_url, media_chapters) == PluginResult::OK; + video_url.clear(); + audio_url.clear(); + + TaskResult load_result = run_task_with_loading_screen([this, video_page, &new_title, &channel_url, &media_chapters, largest_monitor_height, &has_embedded_audio, &video_url, &audio_url, &is_audio_only, &previous_page, is_youtube, download_if_streaming_fails]() { + if(video_page->load(new_title, channel_url, media_chapters) != PluginResult::OK) + return false; + + if(!no_video) + video_url = video_page->get_video_url(largest_monitor_height, has_embedded_audio); + + if(video_url.empty() || no_video) { + video_url = video_page->get_audio_url(); + if(video_url.empty()) { + video_url = video_page->get_url(); + has_embedded_audio = true; + } else { + is_audio_only = true; + has_embedded_audio = false; + } + } else if(!has_embedded_audio) { + audio_url = video_page->get_audio_url(); + } + + if(!is_youtube && download_if_streaming_fails) { + if(!video_download_if_non_streamable(video_url, audio_url, is_audio_only, has_embedded_audio, previous_page)) + return false; + } + + return true; }); if(!new_title.empty()) @@ -2524,31 +2556,6 @@ namespace QuickMedia { go_to_previous_page = true; return; } - - video_url.clear(); - audio_url.clear(); - if(!no_video) - video_url = video_page->get_video_url(largest_monitor_height, has_embedded_audio); - - if(video_url.empty() || no_video) { - video_url = video_page->get_audio_url(); - if(video_url.empty()) { - video_url = video_page->get_url(); - has_embedded_audio = true; - } else { - is_audio_only = true; - has_embedded_audio = false; - } - } else if(!has_embedded_audio) { - audio_url = video_page->get_audio_url(); - } - - if(!is_youtube && download_if_streaming_fails) { - if(!video_download_if_non_streamable(video_url, audio_url, is_audio_only, has_embedded_audio, previous_page)) { - go_to_previous_page = true; - return; - } - } } const bool is_resume_go_back = !start_time.empty(); diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index b45b5fe..717bc8f 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -1981,6 +1981,32 @@ R"END( return nullptr; } + // Sometimes youtube returns a redirect url (not in the header but in the body...). + // TODO: Find why this happens and if there is a way bypass it. + static std::string get_playback_url_recursive(std::string playback_url) { + const int max_redirects = 5; + for(int i = 0; i < max_redirects; ++i) { + std::string response_headers; + if(download_head_to_string(playback_url, response_headers, true) != DownloadResult::OK) + return ""; + + std::string content_type = header_extract_value(response_headers, "content-type"); + if(content_type.empty()) + return ""; + + if(string_starts_with(content_type, "video") || string_starts_with(content_type, "audio")) + return playback_url; + + // TODO: Download head and body in one request + std::string new_url; + if(download_to_string(playback_url, new_url, {}, true) != DownloadResult::OK) + return ""; + + playback_url = std::move(new_url); + } + return playback_url; + } + std::string YoutubeVideoPage::get_video_url(int max_height, bool &has_embedded_audio) { if(!hls_manifest_url.empty()) { has_embedded_audio = true; @@ -2007,7 +2033,7 @@ R"END( print_chosen_format(*chosen_video_format); has_embedded_audio = chosen_video_format->has_embedded_audio; - return chosen_video_format->base.url; + return get_playback_url_recursive(chosen_video_format->base.url); } std::string YoutubeVideoPage::get_audio_url() { @@ -2017,7 +2043,7 @@ R"END( // TODO: The "worst" (but still good) quality audio is chosen right now because youtube seeking freezes for up to 15 seconds when choosing the best quality const YoutubeAudioFormat *chosen_audio_format = &audio_formats.back(); fprintf(stderr, "Choosing youtube audio format: bitrate: %d, mime type: %s\n", chosen_audio_format->base.bitrate, chosen_audio_format->base.mime_type.c_str()); - return chosen_audio_format->base.url; + return get_playback_url_recursive(chosen_audio_format->base.url); } // Returns -1 if timestamp is in an invalid format -- cgit v1.2.3