aboutsummaryrefslogtreecommitdiff
path: root/plugins/Youtube.hpp
blob: cea5d8d8624f39ad8ee4adf484625adae1538000 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#pragma once

#include "Page.hpp"
#include "../include/AsyncTask.hpp"
#include <unordered_set>
#include <map>

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);
    // |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<bool()> 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<Tab> &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 { 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<std::string> 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<Tab> &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 SubmitArgs &args, std::vector<Tab> &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 SubmitArgs &args, std::vector<Tab> &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<std::string> 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<BodyItem> 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<Tab> &result_tabs) override;
        PluginResult lazy_fetch(BodyItems &result_items) override;
    private:
        std::array<AsyncTask<std::vector<YoutubeSubscriptionTaskResult>>, 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<Tab> &result_tabs) override;
    };

    class YoutubeVideoPage : public VideoPage {
    public:
        YoutubeVideoPage(Program *program, std::string url, bool autoplay = true);
        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<Tab> &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_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, std::string &title, std::string &channel_url, double &duration, std::vector<MediaChapter> &chapters, 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;
    private:
        PluginResult parse_video_response(const Json::Value &json_root, std::string &title, std::string &channel_url, std::vector<MediaChapter> &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<YoutubeVideoFormat> video_formats;
        std::vector<YoutubeAudioFormat> audio_formats;
        std::map<std::string, SubtitleData> subtitle_urls_by_lang_code;

        std::string playback_url;
        std::string watchtime_url;
        std::string tracking_url;
        YoutubeVideoDetails video_details;
    };
}