aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/utils/WatchProgress.cpp
blob: 805f6e734846e78fdc0bf8e4ebd2779db6464b8a (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
#include "../../../plugins/utils/WatchProgress.hpp"
#include "../../../include/Storage.hpp"
#include "../../../include/Notification.hpp"
#include <json/value.h>

namespace QuickMedia {
    double WatchProgress::get_watch_ratio() const {
        if(duration_sec == 0)
            return 0;
        return (double)time_pos_sec / (double)duration_sec;
    }

    // We consider having watched the video if the user stopped watching 90% in, because they might skip the ending theme/credits (especially in anime)
    bool WatchProgress::has_finished_watching() const {
        return get_watch_ratio() >= 0.9;
    }

    bool set_watch_progress_for_plugin(const char *plugin_name, const std::string &id, int64_t time_pos_sec, int64_t duration_sec, const std::string &thumbnail_url) {
        Path watch_progress_dir = get_storage_dir().join("watch-progress");
        if(create_directory_recursive(watch_progress_dir) != 0) {
            show_notification("QuickMedia", "Failed to create " + watch_progress_dir.data + " to set watch progress for " + id, Urgency::CRITICAL);
            return false;
        }

        Path progress_path = watch_progress_dir;
        progress_path.join(plugin_name);

        Json::Value json_root;
        if(!read_file_as_json(progress_path, json_root) || !json_root.isObject())
            json_root = Json::Value(Json::objectValue);

        Json::Value watch_progress_json(Json::objectValue);
        watch_progress_json["time"] = (int64_t)time_pos_sec;
        watch_progress_json["duration"] = (int64_t)duration_sec;
        watch_progress_json["thumbnail_url"] = thumbnail_url;
        watch_progress_json["timestamp"] = (int64_t)time(nullptr);
        json_root[id] = std::move(watch_progress_json);

        if(!save_json_to_file_atomic(progress_path, json_root)) {
            show_notification("QuickMedia", "Failed to set watch progress for " + id, Urgency::CRITICAL);
            return false;
        }

        fprintf(stderr, "Set watch progress for \"%s\" to %d/%d\n", id.c_str(), (int)time_pos_sec, (int)duration_sec);
        return true;
    }

    std::unordered_map<std::string, WatchProgress> get_watch_progress_for_plugin(const char *plugin_name) {
        std::unordered_map<std::string, WatchProgress> watch_progress_map;
        Path progress_path = get_storage_dir().join("watch-progress").join(plugin_name);

        Json::Value json_root;
        if(!read_file_as_json(progress_path, json_root) || !json_root.isObject())
            return watch_progress_map;

        for(Json::Value::const_iterator it = json_root.begin(); it != json_root.end(); ++it) {
            Json::Value key = it.key();
            if(!key.isString())
                continue;

            const Json::Value &time_json = (*it)["time"];
            const Json::Value &duration_json = (*it)["duration"];
            const Json::Value &timestamp_json = (*it)["timestamp"];
            if(!time_json.isInt64() || !duration_json.isInt64() || !timestamp_json.isInt64())
                continue;

            WatchProgress watch_progress;
            watch_progress.time_pos_sec = time_json.asInt64();
            watch_progress.duration_sec = duration_json.asInt64();
            watch_progress.timestamp = timestamp_json.asInt64();

            const Json::Value &thumbnail_url_json = (*it)["thumbnail_url"];
            if(thumbnail_url_json.isString())
                watch_progress.thumbnail_url = thumbnail_url_json.asString();

            watch_progress_map[key.asString()] = std::move(watch_progress);
        }

        return watch_progress_map;
    }

    bool toggle_watched_for_plugin_save_to_file(const char *plugin_name, const std::string &id, int64_t duration_sec, const std::string &thumbnail_url, WatchedStatus &watched_status) {
        Path local_anime_progress_path = get_storage_dir().join("watch-progress").join(plugin_name);

        Json::Value json_root;
        if(!read_file_as_json(local_anime_progress_path, json_root) || !json_root.isObject())
            json_root = Json::Value(Json::objectValue);

        bool watched = false;
        Json::Value &watched_item = json_root[id];
        if(watched_item.isObject()) {
            const Json::Value &time_json = watched_item["time"];
            const Json::Value &duration_json = watched_item["duration"];
            if(time_json.isInt64() && duration_json.isInt64()) {
                WatchProgress watch_progress;
                watch_progress.time_pos_sec = time_json.asInt64();
                watch_progress.duration_sec = duration_json.asInt64();
                watched = watch_progress.has_finished_watching();
            } else {
                watched = false;
            }
        } else {
            watched_item = Json::Value(Json::objectValue);
            watched = false;
        }

        if(watched) {
            json_root.removeMember(id.c_str());
        } else {
            watched_item["time"] = (int64_t)duration_sec;
            watched_item["duration"] = (int64_t)duration_sec;
            watched_item["thumbnail_url"] = thumbnail_url;
            watched_item["timestamp"] = (int64_t)time(nullptr);
        }

        if(!save_json_to_file_atomic(local_anime_progress_path, json_root)) {
            show_notification("QuickMedia", "Failed to mark " + id + " as " + (watched ? "not watched" : "watched"), Urgency::CRITICAL);
            return false;
        }

        watched_status = watched ? WatchedStatus::NOT_WATCHED : WatchedStatus::WATCHED;
        return true;
    }
}