aboutsummaryrefslogtreecommitdiff
path: root/plugins/Page.hpp
blob: 97b03406e8d31f46080773b56888cab11c91cb87 (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
211
212
213
214
215
216
217
218
219
220
221
222
223
#pragma once

#include "Plugin.hpp"

#include "../include/Tab.hpp"
#include "../include/SearchBar.hpp"
#include "../include/Body.hpp"
#include "../include/MediaChapter.hpp"

namespace QuickMedia {
    class Program;
    constexpr int SEARCH_DELAY_FILTER = 50;

    // TODO: Rename to PageType when the other PageType is removed
    enum class PageTypez {
        REGULAR,
        MANGA_IMAGES,
        IMAGE_BOARD_THREAD,
        VIDEO,
        CHAT
    };

    struct SubmitArgs {
        std::string title;
        std::string url;
        std::string thumbnail_url;
        void *userdata = nullptr;
        std::shared_ptr<BodyItemExtra> extra;
    };

    class Page {
    public:
        Page(Program *program) : program(program) {}
        virtual ~Page() = default;

        virtual const char* get_title() const = 0;
        virtual bool search_is_filter() { return true; }
        // This show be overriden if search_is_filter is overriden to return false
        virtual SearchResult search(const std::string &str, BodyItems &result_items) { (void)str; (void)result_items; return SearchResult::ERR; }
        // If this returns true then |submit_suggestion| is called when submitting the selected item instead of |submit|
        // and |submit| is called when submitting the response of |submit_suggestion|
        virtual bool search_is_suggestion(bool empty_search) { (void)empty_search; return false; }

        // Return empty |result_tabs| and PluginResult::OK to do nothing; which is useful for implementing custom actions on item submit
        virtual PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) {
            (void)args;
            (void)result_tabs;
            return PluginResult::ERR;
        }
        // Override and return false to make submit run in the main (ui) thread
        virtual bool submit_is_async() const { return true; }
        virtual bool clear_search_after_submit() { return false; }
        virtual PluginResult submit_suggestion(const SubmitArgs &args, BodyItems &result_items) {
            (void)args;
            (void)result_items;
            return PluginResult::ERR;
        }
        // Note: If pagination is done by fetching the next page until we get to |page|, then the "current page" should be reset everytime |search| is called.
        // Note: the first page fetched is 1 (search/lazy fetch page is 0)
        virtual PluginResult get_page(const std::string &str, int page, BodyItems &result_items) { (void)str; (void)page; (void)result_items; return PluginResult::OK; }

        DownloadResult download_json(Json::Value &result, const std::string &url, std::vector<CommandArg> additional_args, bool use_browser_useragent = false, std::string *err_msg = nullptr);

        virtual PageTypez get_type() const { return PageTypez::REGULAR; }
        // Mutually exclusive with |get_type| when |get_type| is not PageTypez::REGULAR
        virtual bool is_single_page() const { return false; }
        virtual bool is_trackable() const { return false; }
        // Return nullptr if bookmark is not supported by this page
        virtual const char* get_bookmark_name() const { return nullptr; }
        // If this returns nullptr then the currently selected body item is used instead.
        // |selected_item| may be nullptr.
        virtual std::shared_ptr<BodyItem> get_bookmark_body_item(BodyItem *selected_item) { (void)selected_item; return nullptr; }
        virtual bool is_bookmark_page() const { return false; }
        // |selected_item| may be nullptr.
        virtual void toggle_read(BodyItem *selected_item) { (void)selected_item; }
        virtual bool is_lazy_fetch_page() const { return false; }
        // Note: If submit is done without any selection, then the search term is sent as the |title| and |url|. Submit will only be sent if the input text is not empty or if an item is selected
        virtual bool allow_submit_no_selection() const { return false; }
        // This is used to delay loading of the page. For example if the page relies on an external factor to start loading
        virtual bool is_ready() { return true; }

        // This is called both when first navigating to page and when going back to page
        virtual void on_navigate_to_page(Body *body) { (void)body; }
        virtual void cancel_operation() {}

        virtual void copy_to_clipboard(const BodyItem *body_item);

        std::unique_ptr<Body> create_body(bool plain_text_list = false, bool prefer_card_view = false);
        std::unique_ptr<SearchBar> create_search_bar(const std::string &placeholder_text, int search_delay);

        bool load_manga_content_storage(const char *service_name, const std::string &manga_title, const std::string &manga_url, const std::string &manga_id);

        void set_clipboard(const std::string &str);
    
        Program *program;
        bool needs_refresh = false; // Set to true to refresh the page. Note: only works for search pages and lazy fetch pages
    };

    enum class TrackResult {
        OK,
        ERR
    };

    class TrackablePage {
    public:
        TrackablePage(std::string content_title, std::string content_url) : content_title(std::move(content_title)), content_url(std::move(content_url)) {}
        virtual ~TrackablePage() = default;
        virtual TrackResult track(const std::string &str) = 0;

        const std::string content_title;
        const std::string content_url;
    };

    class LazyFetchPage : public Page {
    public:
        LazyFetchPage(Program *program) : Page(program) {}
        virtual bool search_is_filter() override { return true; }
        bool is_lazy_fetch_page() const override { return true; }
        virtual PluginResult lazy_fetch(BodyItems &result_items) = 0;
        // If this returns true then |lazy_fetch| is not meant to return results but async background load the page. This can be used to fetch API keys for example
        virtual bool lazy_fetch_is_loader() { return false; }
        virtual bool reload_on_page_change() { return false; }
        virtual bool reseek_to_body_item_by_url() { return false; }
    };

    class RelatedVideosPage : public Page {
    public:
        RelatedVideosPage(Program *program) : Page(program) {}
        const char* get_title() const override { return "Related videos"; }
    };

    struct SubtitleData {
        std::string url;
        std::string title;
    };

    struct VideoInfo {
        std::string title;
        std::string channel_url;
        double duration = 0.0;
        std::vector<MediaChapter> chapters;
        std::string referer;
    };

    class VideoPage : public Page {
    public:
        VideoPage(Program *program, std::string url, bool autoplay = true) : Page(program), url(std::move(url)), autoplay(autoplay) {}
        virtual PageTypez get_type() const override { return PageTypez::VIDEO; }
        virtual bool autoplay_next_item() { return false; }
        bool should_autoplay() const { return autoplay; }
        virtual BodyItems get_related_media(const std::string &url) { (void)url; return {}; }
        virtual PluginResult get_related_pages(const BodyItems &related_videos, const std::string &channel_url, std::vector<Tab> &result_tabs) {
            (void)related_videos;
            (void)channel_url;
            (void)result_tabs;
            return PluginResult::OK;
        }
        virtual int get_related_pages_first_tab() { return 0; }
        virtual void set_url(std::string new_url) { url = std::move(new_url); }
        std::string get_url() { return url; }
        virtual std::string get_download_url(int max_height) { (void)max_height; return url; }
        // Returns empty string for no timestamp or if the video doesn't support timestamps.
        // Timestamp is in seconds.
        virtual std::string get_url_timestamp() { return ""; }
        // Falls back to |get_url| if this and |get_audio_url| returns empty strings.
        // Might do a network request.
        virtual std::string get_video_url(int max_height, bool &has_embedded_audio, std::string &ext) {
            (void)max_height;
            (void)ext;
            has_embedded_audio = true;
            return "";
        }
        // Only used if |get_video_url| sets |has_embedded_audio| to false.
        // Might do a network request.
        virtual std::string get_audio_url(std::string &ext) { (void)ext; return ""; }
        // Might do a network request
        virtual std::string url_get_playable_url(const std::string &url) { return url; }
        virtual bool video_should_be_skipped(const std::string &url) { (void)url; return false; }
        // This needs to be called before the other functions are called
        virtual PluginResult load(const SubmitArgs &args, VideoInfo &video_info, std::string &err_str) {
            (void)args; (void)video_info; (void)err_str;
            return PluginResult::OK;
        }
        virtual void mark_watched() {};
        // Should not do any network request to not slow down video loading
        virtual void get_subtitles(SubtitleData &subtitle_data) { (void)subtitle_data; }
        virtual std::string get_filename() { return ""; }

        virtual bool is_local() const { return false; }

        virtual void set_watch_progress(int64_t time_pos_sec, int64_t duration_sec) { (void)time_pos_sec; (void)duration_sec; }
    protected:
        std::string url;
        bool autoplay;
    };

    class BookmarksPage : public LazyFetchPage {
    public:
        BookmarksPage(Program *program, Page *redirect_page, bool local_thumbnail = false) : LazyFetchPage(program), redirect_page(redirect_page), local_thumbnail(local_thumbnail) {}
        const char* get_title() const override { return "Bookmarks"; }
        PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
        PluginResult lazy_fetch(BodyItems &result_items) override;
        bool reload_on_page_change() override { return true; }
        const char* get_bookmark_name() const override { return redirect_page->get_bookmark_name(); }
        bool is_bookmark_page() const override { return true; }

        mgl::vec2i thumbnail_size = {101, 141};
    private:
        Page *redirect_page;
        bool local_thumbnail;
    };

    class LoginPage : public Page {
    public:
        LoginPage(Program *program) : Page(program) {}
        bool submit_is_async() const override { return true; }
        bool allow_submit_no_selection() const override { return true; }
        void login_finish();
        bool logged_in() const;
    private:
        bool login_finished = false;
    };
}