aboutsummaryrefslogtreecommitdiff
path: root/plugins/Youtube.hpp
blob: d7008ae3359ad5de4d7d16d40a938f6b9be1e370 (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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#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);
    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<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;
    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, 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<Tab> &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<Tab> &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<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:
        std::string url;
        std::string continuation_token;
        const std::string title;
        int current_page = 0;
        Type type;
    };

    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 YoutubePlaylistPage : public LazyFetchPage {
    public:
        YoutubePlaylistPage(Program *program, const std::string &url, std::string title);
        const char* get_title() const override { return title.c_str(); }
        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:
        std::string playlist_id;
        std::string title;
        std::string continuation_token;
        bool reached_end = false;
    };

    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<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_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<MediaChapter> &chapters, std::vector<SponsorSegment> &sponsor_segments, 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;
        bool goto_next_item;
        bool use_youtube_dl_fallback = false;
        std::string youtube_dl_video_fallback_url;
        std::string youtube_dl_audio_fallback_url;
    };
}