#pragma once #include "Page.hpp" #include "../include/AsyncTask.hpp" #include namespace QuickMedia { struct YoutubeFormat { std::string url; int bitrate = 0; std::string mime_type; }; struct YoutubeVideoFormat { YoutubeFormat base; int width = 0; int height = 0; int fps = 0; bool has_embedded_audio = false; }; struct YoutubeAudioFormat { YoutubeFormat base; }; // Returns |url| if the url is already a youtube url std::string invidious_url_to_youtube_url(const std::string &url); bool youtube_url_extract_id(const std::string &youtube_url, std::string &youtube_video_id); // |video_url| or |audio_url| will be empty if there is an error and false will be returned. // If false is returned from |active_handler|, then this function is cancelled. bool youtube_custom_redirect(std::string &video_url, std::string &audio_url, int64_t &video_content_length, int64_t &audio_content_length, std::function active_handler); class YoutubeSearchPage : public LazyFetchPage { public: YoutubeSearchPage(Program *program, std::string video_id = "") : LazyFetchPage(program), video_id(std::move(video_id)) {} const char* get_title() const override { return "Search"; } bool search_is_suggestion() override { return true; } bool search_is_filter() override { return false; } SearchResult search(const std::string &str, BodyItems &result_items) override; PluginResult get_page(const std::string &str, int page, BodyItems &result_items) override; PluginResult submit(const std::string &title, const std::string &url, std::vector &result_tabs) override; PluginResult submit_suggestion(const std::string &title, const std::string &url, BodyItems &result_items) override; PluginResult lazy_fetch(BodyItems &result_items) override; bool lazy_fetch_is_loader() override { return true; } private: PluginResult search_get_continuation(const std::string &url, const std::string &continuation_token, BodyItems &result_items); private: std::string video_id; std::string search_url; std::string continuation_token; int current_page = 0; std::unordered_set added_videos; }; class YoutubeCommentsPage : public LazyFetchPage { public: YoutubeCommentsPage(Program *program, const std::string &video_url, const std::string &continuation_token) : LazyFetchPage(program), video_url(video_url), continuation_token(continuation_token) {} const char* get_title() const override { return "Comments"; } PluginResult get_page(const std::string &str, int page, BodyItems &result_items) override; PluginResult submit(const std::string &title, const std::string &url, std::vector &result_tabs) override; PluginResult lazy_fetch(BodyItems &result_items) override; private: int current_page = 0; std::string video_url; std::string continuation_token; }; class YoutubeCommentRepliesPage : public LazyFetchPage { public: YoutubeCommentRepliesPage(Program *program, const std::string &video_url, const std::string &continuation_token) : LazyFetchPage(program), video_url(video_url), continuation_token(continuation_token) {} const char* get_title() const override { return "Comment replies"; } PluginResult get_page(const std::string &str, int page, BodyItems &result_items) override; PluginResult submit(const std::string &title, const std::string &url, std::vector &result_tabs) override; PluginResult lazy_fetch(BodyItems &result_items) override; private: int current_page = 0; std::string video_url; std::string continuation_token; }; class YoutubeChannelPage : public LazyFetchPage, public TrackablePage { public: YoutubeChannelPage(Program *program, std::string url, std::string continuation_token, std::string title) : LazyFetchPage(program), TrackablePage(title, url), url(url), continuation_token(std::move(continuation_token)), title(title) {} const char* get_title() const override { return title.c_str(); } bool search_is_filter() override { return false; } SearchResult search(const std::string &str, BodyItems &result_items) override; PluginResult get_page(const std::string &str, int page, BodyItems &result_items) override; PluginResult submit(const std::string &title, const std::string &url, std::vector &result_tabs) override; PluginResult lazy_fetch(BodyItems &result_items) override; TrackResult track(const std::string &str) override; bool is_trackable() const override { return true; } std::unordered_set added_videos; private: PluginResult search_get_continuation(const std::string &url, const std::string &continuation_token, BodyItems &result_items); private: const std::string url; std::string continuation_token; const std::string title; int current_page = 0; }; struct YoutubeSubscriptionTaskResult { std::shared_ptr body_item; time_t timestamp = 0; }; class YoutubeSubscriptionsPage : public LazyFetchPage { public: YoutubeSubscriptionsPage(Program *program) : LazyFetchPage(program) {} const char* get_title() const override { return "Subscriptions"; } PluginResult submit(const std::string &title, const std::string &url, std::vector &result_tabs) override; PluginResult lazy_fetch(BodyItems &result_items) override; private: std::array>, 4> subscription_load_tasks; // TODO: Use multiple curl outputs instead? }; class YoutubeRecommendedPage : public LazyFetchPage { public: YoutubeRecommendedPage(Program *program) : LazyFetchPage(program) {} const char* get_title() const override { return "Recommended"; } PluginResult get_page(const std::string &str, int page, BodyItems &result_items) override; PluginResult submit(const std::string &title, const std::string &url, std::vector &result_tabs) override; PluginResult lazy_fetch(BodyItems &result_items) override; bool reload_on_page_change() override { return true; } private: PluginResult search_get_continuation(const std::string &continuation_token, BodyItems &result_items); private: int current_page = 0; std::string continuation_token; std::unordered_set added_videos; }; class YoutubeRelatedVideosPage : public RelatedVideosPage { public: YoutubeRelatedVideosPage(Program *program) : RelatedVideosPage(program) {} PluginResult submit(const std::string&, const std::string&, std::vector &result_tabs) override; }; class YoutubeVideoPage : public VideoPage { public: YoutubeVideoPage(Program *program, std::string url); const char* get_title() const override { return ""; } BodyItems get_related_media(const std::string &url) override; PluginResult get_related_pages(const BodyItems &related_videos, const std::string &channel_url, std::vector &result_tabs) 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, std::string &ext) override; std::string get_audio_url(std::string &ext) override; PluginResult load(std::string &title, std::string &channel_url, std::vector &chapters, std::string &err_str) override; void mark_watched() override; void get_subtitles(SubtitleData &subtitle_data) override; private: PluginResult parse_video_response(const Json::Value &json_root, std::string &title, std::string &channel_url, std::vector &chapters, std::string &err_str); 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 comments_continuation_token; std::string livestream_url; std::vector video_formats; std::vector audio_formats; std::map subtitle_urls_by_lang_code; std::string playback_url; std::string watchtime_url; std::string tracking_url; }; }