#pragma once #include "Page.hpp" #include "../include/AsyncTask.hpp" #include #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; }; struct YoutubeVideoDetails { std::string title; std::string author; std::string views; std::string description; double duration = 0.0; }; // 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); bool youtube_url_extract_channel_id(const std::string &youtube_url, std::string &channel_id, std::string &channel_url); // |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); // fill_recommended_items_from_json(plugin_name, load_recommended_json(plugin_name), result_items); 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(bool empty_search) override { return !empty_search; } 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 SubmitArgs &args, std::vector &result_tabs) override; PluginResult submit_suggestion(const SubmitArgs &args, BodyItems &result_items) override; PluginResult lazy_fetch(BodyItems &result_items) override; bool lazy_fetch_is_loader() override { return true; } bool reload_on_page_change() override; 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 YoutubeDescriptionPage : public Page { public: YoutubeDescriptionPage(Program *program) : Page(program) {} const char* get_title() const override { return "Description"; } }; 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 SubmitArgs &args, 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, const BodyItem *replied_to_body_item) : LazyFetchPage(program), video_url(video_url), continuation_token(continuation_token), replied_to_body_item(replied_to_body_item) {} 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 SubmitArgs &args, std::vector &result_tabs) override; PluginResult lazy_fetch(BodyItems &result_items) override; private: PluginResult lazy_fetch(BodyItems &result_items, bool first_fetch); private: int current_page = 0; std::string video_url; std::string continuation_token; const BodyItem *replied_to_body_item; }; class YoutubeChannelPage : public LazyFetchPage, public TrackablePage { public: enum class Type { VIDEOS, SHORTS, LIVE, PLAYLISTS }; static void create_each_type(Program *program, std::string url, std::string continuation_token, std::string title, std::vector &tabs); YoutubeChannelPage(Program *program, std::string url, std::string continuation_token, std::string title, Type type); 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 SubmitArgs &args, 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: std::string url; std::string continuation_token; const std::string title; int current_page = 0; Type type; }; 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 SubmitArgs &args, 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 YoutubeRelatedVideosPage : public RelatedVideosPage { public: YoutubeRelatedVideosPage(Program *program) : RelatedVideosPage(program) {} PluginResult submit(const SubmitArgs &args, std::vector &result_tabs) override; }; class YoutubePlaylistPage : public LazyFetchPage { public: YoutubePlaylistPage(Program *program, std::string url, std::string title) : LazyFetchPage(program), url(std::move(url)), title(std::move(title)) {} const char* get_title() const override { return title.c_str(); } PluginResult submit(const SubmitArgs &args, std::vector &result_tabs) override; PluginResult lazy_fetch(BodyItems &result_items) override; private: std::string url; std::string title; }; class YoutubeVideoPage : public VideoPage { public: YoutubeVideoPage(Program *program, std::string url, bool autoplay = true, bool autoplay_next_item = false); 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; int get_related_pages_first_tab() override { return 1; } void set_url(std::string new_url) override; std::string get_url_timestamp() override; std::string get_download_url(int max_height) override; 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(const SubmitArgs &args, VideoInfo &video_info, std::string &err_str) override; void mark_watched() override; void get_subtitles(SubtitleData &subtitle_data) override; void set_watch_progress(int64_t time_pos_sec, int64_t duration_sec) override; bool autoplay_next_item() override { return goto_next_item; } 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; YoutubeVideoDetails video_details; bool goto_next_item; }; }