From d0f54de4520990c8465bf2b78a1ce8c0161d46d7 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 17 Jun 2021 10:24:55 +0200 Subject: Properly support youtube timestamp in url --- plugins/Page.hpp | 4 +++- plugins/Youtube.hpp | 5 ++++- src/QuickMedia.cpp | 18 ++---------------- src/plugins/Youtube.cpp | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/plugins/Page.hpp b/plugins/Page.hpp index 0ed4933..39222b1 100644 --- a/plugins/Page.hpp +++ b/plugins/Page.hpp @@ -112,8 +112,10 @@ namespace QuickMedia { virtual std::unique_ptr create_related_videos_page(Program *program) = 0; // Return nullptr if the service doesn't support channels page virtual std::unique_ptr create_channels_page(Program *program, const std::string &channel_url) = 0; - void set_url(std::string new_url) { url = std::move(new_url); } + virtual void set_url(std::string new_url) { url = std::move(new_url); } 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 virtual std::string get_video_url(int max_height, bool &has_embedded_audio) { (void)max_height; diff --git a/plugins/Youtube.hpp b/plugins/Youtube.hpp index 28b50bb..d8c4cc2 100644 --- a/plugins/Youtube.hpp +++ b/plugins/Youtube.hpp @@ -132,13 +132,15 @@ namespace QuickMedia { class YoutubeVideoPage : public VideoPage { public: - YoutubeVideoPage(Program *program, std::string url) : VideoPage(program, std::move(url)) {} + YoutubeVideoPage(Program *program, std::string url); const char* get_title() const override { return ""; } BodyItems get_related_media(const std::string &url) override; std::unique_ptr create_search_page(Program *program, int &search_delay) override; std::unique_ptr create_comments_page(Program *program) override; std::unique_ptr create_related_videos_page(Program *program) override; std::unique_ptr create_channels_page(Program *program, const std::string &channel_url) override; + void set_url(std::string new_url) override; + std::string get_url_timestamp() override { return timestamp; } std::string get_video_url(int max_height, bool &has_embedded_audio) override; std::string get_audio_url() override; PluginResult load(std::string &title, std::string &channel_url) override; @@ -147,6 +149,7 @@ namespace QuickMedia { void parse_format(const Json::Value &format_json, bool is_adaptive); void parse_formats(const Json::Value &streaming_data_json); private: + std::string timestamp; std::string xsrf_token; std::string comments_continuation_token; std::string hls_manifest_url; diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 77098a3..5316d3f 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -2383,19 +2383,6 @@ namespace QuickMedia { download_async_gui(url, file_manager_start_dir.string(), true, audio_only); } - static std::string youtube_url_extract_timestamp(const std::string &url) { - size_t timestamp_start = url.find("t="); - if(timestamp_start == std::string::npos) - return ""; - - timestamp_start += 2; - size_t timestamp_end = url.find('&'); - if(timestamp_end == std::string::npos) - timestamp_end = url.size(); - - return url.substr(timestamp_start, timestamp_end - timestamp_start); - } - bool Program::video_download_if_non_streamable(std::string &video_url, std::string &audio_url, bool &is_audio_only, bool &has_embedded_audio, PageType previous_page) { Path video_cache_dir = get_cache_dir().join("media"); Path video_path = video_cache_dir; @@ -2562,8 +2549,8 @@ namespace QuickMedia { } const bool is_resume_go_back = !start_time.empty(); - if(is_youtube && start_time.empty()) - start_time = youtube_url_extract_timestamp(video_page->get_url()); + if(start_time.empty()) + start_time = video_page->get_url_timestamp(); prev_start_time = start_time; watched_videos.insert(video_page->get_url()); @@ -2665,7 +2652,6 @@ namespace QuickMedia { auto save_video_url_to_clipboard = [this, video_page]() { std::string url = video_page->get_url(); if(video_url_supports_timestamp(url)) { - // TODO: Remove timestamp (&t= or ?t=) from video_url double time_in_file = 0.0; if(video_player && (video_player->get_time_in_file(&time_in_file) != VideoPlayer::Error::OK)) time_in_file = 0.0; diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index d9d9239..924bf14 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -1783,6 +1783,43 @@ R"END( return ""; } + static int youtube_url_timestamp_to_seconds(const std::string ×tamp) { + int hours = 0; + int minutes = 0; + int seconds = 0; + if(sscanf(timestamp.c_str(), "%dh%dm%d", &hours, &minutes, &seconds) == 3) + return (hours * 60 * 60) + (minutes * 60) + seconds; + if(sscanf(timestamp.c_str(), "%dm%d", &minutes, &seconds) == 2) + return (minutes * 60) + seconds; + if(sscanf(timestamp.c_str(), "%d", &seconds) == 1) + return seconds; + return 0; + } + + static void youtube_url_remove_timestamp(std::string &url, std::string ×tamp) { + size_t timestamp_start = url.find("&t="); + if(timestamp_start == std::string::npos) + return; + + size_t timestamp_end = url.find("&", timestamp_start + 3); + if(timestamp_end == std::string::npos) + timestamp_end = url.size(); + + int timestamp_seconds = youtube_url_timestamp_to_seconds(url.substr(timestamp_start + 3, timestamp_end - (timestamp_start + 3))); + timestamp = std::to_string(timestamp_seconds); + url.erase(timestamp_start, timestamp_end - timestamp_start); + return; + } + + YoutubeVideoPage::YoutubeVideoPage(Program *program, std::string url) : VideoPage(program, "") { + set_url(std::move(url)); + } + + void YoutubeVideoPage::set_url(std::string new_url) { + youtube_url_remove_timestamp(new_url, timestamp); + VideoPage::set_url(std::move(new_url)); + } + BodyItems YoutubeVideoPage::get_related_media(const std::string &url) { BodyItems result_items; -- cgit v1.2.3