aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2022-03-08 16:39:55 +0100
committerdec05eba <dec05eba@protonmail.com>2022-03-08 16:39:55 +0100
commita26d0fcc0a30a28ce0e458ea275fc0787c693bc6 (patch)
treeffe2212b81d9fdc93b49383c102e7c0ece0d225a
parent21c50903a68c253fa5fcb9ed5ac8ba5abb1142b9 (diff)
Save youtube watch progress and resume next time the video is played
-rw-r--r--TODO5
-rw-r--r--include/VideoPlayer.hpp1
-rw-r--r--plugins/Lbry.hpp2
-rw-r--r--plugins/LocalAnime.hpp2
-rw-r--r--plugins/MediaGeneric.hpp2
-rw-r--r--plugins/Page.hpp6
-rw-r--r--plugins/Peertube.hpp2
-rw-r--r--plugins/Soundcloud.hpp2
-rw-r--r--plugins/Youtube.hpp6
-rw-r--r--src/QuickMedia.cpp30
-rw-r--r--src/VideoPlayer.cpp19
-rw-r--r--src/plugins/Lbry.cpp5
-rw-r--r--src/plugins/LocalAnime.cpp2
-rw-r--r--src/plugins/MediaGeneric.cpp3
-rw-r--r--src/plugins/Peertube.cpp6
-rw-r--r--src/plugins/Soundcloud.cpp3
-rw-r--r--src/plugins/Youtube.cpp54
-rw-r--r--video_player/README.md27
-rw-r--r--video_player/src/main.cpp19
19 files changed, 141 insertions, 55 deletions
diff --git a/TODO b/TODO
index 458297e..e8c962a 100644
--- a/TODO
+++ b/TODO
@@ -217,8 +217,9 @@ Add ctrl+o keybind to open the selected media in an external application (video,
Local anime bookmark.
Local anime history.
Replace youtube subscriptions page (rss) with youtube api. This allows us to get get video status for videos (and description).
-Add watch progress to youtube (and maybe also lbry and peertube?).
+Add watch progress to lbry and peertube.
Ctrl+R set chapter to finished reading in chapters page.
Zero clear password memory after use.
Periodically cleanup old cache files (especially manga images, thumbnails and media). Maybe have a file that says when we last checked for old files,
- and if its X days old then check and remove old files again and update the file. \ No newline at end of file
+ and if its X days old then check and remove old files again and update the file.
+Render watch progress in youtube. \ No newline at end of file
diff --git a/include/VideoPlayer.hpp b/include/VideoPlayer.hpp
index 8764aa8..0006bfd 100644
--- a/include/VideoPlayer.hpp
+++ b/include/VideoPlayer.hpp
@@ -65,6 +65,7 @@ namespace QuickMedia {
// Returns time in seconds
Error get_time_in_file(double *result);
+ Error get_duration_in_file(double *result);
Error add_subtitle(const std::string &url, const std::string &title, const std::string &lang);
Error cycle_fullscreen();
diff --git a/plugins/Lbry.hpp b/plugins/Lbry.hpp
index 224e43b..d21778a 100644
--- a/plugins/Lbry.hpp
+++ b/plugins/Lbry.hpp
@@ -41,7 +41,7 @@ namespace QuickMedia {
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(std::string &title, std::string &channel_url, std::vector<MediaChapter> &chapters, std::string &err_str) override;
+ PluginResult load(std::string &title, std::string &channel_url, double &duration, std::vector<MediaChapter> &chapters, std::string &err_str) override;
private:
std::string title;
std::string streaming_url;
diff --git a/plugins/LocalAnime.hpp b/plugins/LocalAnime.hpp
index 0313231..4925767 100644
--- a/plugins/LocalAnime.hpp
+++ b/plugins/LocalAnime.hpp
@@ -54,7 +54,7 @@ namespace QuickMedia {
std::string get_video_url(int max_height, bool &has_embedded_audio, std::string &ext) override;
std::string get_url_timestamp() override;
bool is_local() const override { return true; }
- void set_watch_progress(int64_t time_pos_sec) override;
+ void set_watch_progress(int64_t time_pos_sec, int64_t duration_sec) override;
private:
LocalAnimeSearchPage *search_page;
WatchProgress *watch_progress;
diff --git a/plugins/MediaGeneric.hpp b/plugins/MediaGeneric.hpp
index 684f319..68ad22f 100644
--- a/plugins/MediaGeneric.hpp
+++ b/plugins/MediaGeneric.hpp
@@ -96,7 +96,7 @@ namespace QuickMedia {
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;
- PluginResult load(std::string &title, std::string &channel_url, std::vector<MediaChapter> &chapters, std::string &err_str) override;
+ PluginResult load(std::string &title, std::string &channel_url, double &duration, std::vector<MediaChapter> &chapters, std::string &err_str) override;
private:
MediaGenericSearchPage *search_page;
std::string video_url;
diff --git a/plugins/Page.hpp b/plugins/Page.hpp
index e25cb84..1b8a391 100644
--- a/plugins/Page.hpp
+++ b/plugins/Page.hpp
@@ -168,8 +168,8 @@ namespace QuickMedia {
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(std::string &title, std::string &channel_url, std::vector<MediaChapter> &chapters, std::string &err_str) {
- (void)title; (void)channel_url; (void)chapters; (void)err_str;
+ virtual PluginResult load(std::string &title, std::string &channel_url, double &duration, std::vector<MediaChapter> &chapters, std::string &err_str) {
+ (void)title; (void)duration; (void)channel_url; (void)chapters; (void)err_str;
return PluginResult::OK;
}
virtual void mark_watched() {};
@@ -178,7 +178,7 @@ namespace QuickMedia {
virtual bool is_local() const { return false; }
- virtual void set_watch_progress(int64_t time_pos_sec) { (void)time_pos_sec; }
+ 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;
};
diff --git a/plugins/Peertube.hpp b/plugins/Peertube.hpp
index 1f51555..dfd5601 100644
--- a/plugins/Peertube.hpp
+++ b/plugins/Peertube.hpp
@@ -81,7 +81,7 @@ namespace QuickMedia {
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(std::string &title, std::string &channel_url, std::vector<MediaChapter> &chapters, std::string &err_str) override;
+ PluginResult load(std::string &title, std::string &channel_url, double &duration, std::vector<MediaChapter> &chapters, std::string &err_str) override;
bool autoplay_next_item() override { return autoplay_next; }
//void get_subtitles(SubtitleData &subtitle_data) override;
private:
diff --git a/plugins/Soundcloud.hpp b/plugins/Soundcloud.hpp
index 7e65cc1..0ec0ae4 100644
--- a/plugins/Soundcloud.hpp
+++ b/plugins/Soundcloud.hpp
@@ -60,7 +60,7 @@ namespace QuickMedia {
public:
SoundcloudAudioPage(Program *program, std::string title, const std::string &url, std::string permalink_url) : VideoPage(program, url), title(std::move(title)), permalink_url(std::move(permalink_url)) {}
const char* get_title() const override { return ""; }
- PluginResult load(std::string &title, std::string &channel_url, std::vector<MediaChapter> &chapters, std::string &err_str) override;
+ PluginResult load(std::string &title, std::string &channel_url, double &duration, std::vector<MediaChapter> &chapters, std::string &err_str) override;
bool autoplay_next_item() override { return true; }
std::string url_get_playable_url(const std::string &url) override;
std::string get_download_url(int max_height) override;
diff --git a/plugins/Youtube.hpp b/plugins/Youtube.hpp
index 4772aa1..8844c5b 100644
--- a/plugins/Youtube.hpp
+++ b/plugins/Youtube.hpp
@@ -29,6 +29,7 @@ namespace QuickMedia {
std::string author;
std::string views;
std::string description;
+ double duration = 0.0;
};
// Returns |url| if the url is already a youtube url
@@ -144,12 +145,13 @@ namespace QuickMedia {
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 { return timestamp; }
+ 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(std::string &title, std::string &channel_url, std::vector<MediaChapter> &chapters, std::string &err_str) override;
+ PluginResult load(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);
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index b9dd0f0..0da90f9 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -3034,7 +3034,9 @@ namespace QuickMedia {
bool video_loaded = false;
double video_time_pos = 0.0; // Time in media in seconds. Updates every 5 seconds and when starting to watch the video and when seeking.
+ double video_duration = 0.0; // Time in seconds. 0 if unknown
bool update_time_pos = false;
+ bool update_duration = false;
mgl::Clock video_time_pos_clock;
std::string youtube_video_id_dummy;
@@ -3066,8 +3068,7 @@ namespace QuickMedia {
if(!subtitle_data.url.empty())
video_player->add_subtitle(subtitle_data.url, subtitle_data.title, "eng");
- if(video_page->is_local())
- update_time_pos = true;
+ update_time_pos = true;
};
std::unique_ptr<YoutubeMediaProxy> youtube_video_media_proxy;
@@ -3109,7 +3110,8 @@ namespace QuickMedia {
for(int i = 0; i < num_retries; ++i) {
bool cancelled = false;
TaskResult load_result = run_task_with_loading_screen([&]() {
- if(video_page->load(new_title, channel_url, media_chapters, err_str) != PluginResult::OK)
+ video_duration = 0.0;
+ if(video_page->load(new_title, channel_url, video_duration, media_chapters, err_str) != PluginResult::OK)
return false;
std::string ext;
@@ -3328,18 +3330,17 @@ namespace QuickMedia {
} else if(strcmp(event_name, "playback-restart") == 0) {
//video_player->set_paused(false);
} else if(strcmp(event_name, "start-file") == 0) {
+ update_duration = true;
added_recommendations = false;
time_watched_timer.restart();
video_loaded = true;
- if(video_page->is_local())
- update_time_pos = true;
+ update_time_pos = true;
} else if(strcmp(event_name, "file-loaded") == 0) {
video_loaded = true;
} else if(strcmp(event_name, "video-reconfig") == 0 || strcmp(event_name, "audio-reconfig") == 0) {
video_loaded = true;
} else if(strcmp(event_name, "seek") == 0) {
- if(video_page->is_local())
- update_time_pos = true;
+ update_time_pos = true;
} else if(strcmp(event_name, "fullscreen") == 0 && args.size() == 1) {
window_set_fullscreen(disp, window.get_system_handle(), args[0] == "yes" ? WindowFullscreenState::SET : WindowFullscreenState::UNSET);
}
@@ -3465,6 +3466,8 @@ namespace QuickMedia {
XSync(disp, False);
show_notification("QuickMedia", "Failed to get related pages", Urgency::CRITICAL);
} else if(related_pages_result == TaskResult::TRUE && !related_pages.empty()) {
+ video_page->set_watch_progress(video_time_pos, video_duration);
+
bool page_changed = false;
double resume_start_time = 0.0;
page_loop(related_pages, video_page->get_related_pages_first_tab(), [&](const std::vector<Tab> &new_tabs) {
@@ -3654,6 +3657,13 @@ namespace QuickMedia {
update_time_pos = false;
video_player->get_time_in_file(&video_time_pos);
}
+
+ if(update_duration) {
+ update_duration = false;
+ double file_duration = 0.0;
+ video_player->get_duration_in_file(&file_duration);
+ video_duration = std::max(video_duration, file_duration);
+ }
}
if(video_player_window) {
@@ -3679,8 +3689,7 @@ namespace QuickMedia {
window_size.x = window_size_u.x;
window_size.y = window_size_u.y;
- if(video_page->is_local())
- video_page->set_watch_progress(video_time_pos);
+ video_page->set_watch_progress(video_time_pos, video_duration);
}
void Program::select_episode(BodyItem *item, bool start_from_beginning) {
@@ -7525,7 +7534,8 @@ namespace QuickMedia {
std::string channel_url;
std::vector<MediaChapter> chapters;
filename.clear();
- if(youtube_video_page->load(filename, channel_url, chapters, err_str) != PluginResult::OK)
+ double duration;
+ if(youtube_video_page->load(filename, channel_url, duration, chapters, err_str) != PluginResult::OK)
return false;
std::string ext;
diff --git a/src/VideoPlayer.cpp b/src/VideoPlayer.cpp
index 3d3fcc7..5e34c8e 100644
--- a/src/VideoPlayer.cpp
+++ b/src/VideoPlayer.cpp
@@ -28,12 +28,12 @@ static ssize_t read_eintr(int fd, void *buffer, size_t size) {
}
}
-static ssize_t write_all(int fd, const void *buffer, size_t size) {
+static ssize_t write_all_blocking(int fd, const void *buffer, size_t size) {
ssize_t bytes_written = 0;
while((size_t)bytes_written < size) {
ssize_t written = write(fd, (char*)buffer + bytes_written, size - bytes_written);
if(written == -1) {
- if(errno != EINTR)
+ if(errno != EINTR && errno != EWOULDBLOCK)
return -1;
} else {
bytes_written += written;
@@ -451,6 +451,19 @@ namespace QuickMedia {
return err;
}
+ VideoPlayer::Error VideoPlayer::get_duration_in_file(double *result) {
+ Json::Value json_root(Json::objectValue);
+ json_root["command"] = "duration";
+
+ Json::Value duration_json;
+ Error err = send_command(json_root, &duration_json, Json::ValueType::realValue);
+ if(err != Error::OK)
+ return err;
+
+ *result = duration_json.asDouble();
+ return err;
+ }
+
VideoPlayer::Error VideoPlayer::add_subtitle(const std::string &url, const std::string &title, const std::string &lang) {
Json::Value data_json(Json::objectValue);
data_json["file"] = url;
@@ -496,7 +509,7 @@ namespace QuickMedia {
builder["indentation"] = "";
const std::string cmd_str = Json::writeString(builder, json_root) + "\n";
- if(write_all(ipc_socket, cmd_str.data(), cmd_str.size()) == -1) {
+ if(write_all_blocking(ipc_socket, cmd_str.data(), cmd_str.size()) == -1) {
fprintf(stderr, "Failed to send to ipc socket, error: %s, command: %.*s\n", strerror(errno), (int)cmd_str.size(), cmd_str.c_str());
return Error::FAIL_TO_SEND;
}
diff --git a/src/plugins/Lbry.cpp b/src/plugins/Lbry.cpp
index 73c37ba..feac2c5 100644
--- a/src/plugins/Lbry.cpp
+++ b/src/plugins/Lbry.cpp
@@ -328,7 +328,7 @@ namespace QuickMedia {
}
static PluginResult video_get_stream_url(Page *page, const std::string &video_url, std::string &streaming_url, std::string &err_str) {
- std::string url = "https://api.na-backend.odysee.com/api/v1/proxy?m=resolve";
+ std::string url = "https://api.na-backend.odysee.com/api/v1/proxy?m=get";
Json::Value request_params_json(Json::objectValue);
request_params_json["save_file"] = false;
@@ -391,9 +391,10 @@ namespace QuickMedia {
return "";
}
- PluginResult LbryVideoPage::load(std::string &title, std::string&, std::vector<MediaChapter>&, std::string &err_str) {
+ PluginResult LbryVideoPage::load(std::string &title, std::string&, double &duration, std::vector<MediaChapter>&, std::string &err_str) {
streaming_url.clear();
title = this->title;
+ duration = 0.0;
return video_get_stream_url(this, url, streaming_url, err_str);
}
} \ No newline at end of file
diff --git a/src/plugins/LocalAnime.cpp b/src/plugins/LocalAnime.cpp
index 0de33b4..1bc8ca8 100644
--- a/src/plugins/LocalAnime.cpp
+++ b/src/plugins/LocalAnime.cpp
@@ -490,7 +490,7 @@ namespace QuickMedia {
}
}
- void LocalAnimeVideoPage::set_watch_progress(int64_t time_pos_sec) {
+ void LocalAnimeVideoPage::set_watch_progress(int64_t time_pos_sec, int64_t) {
std::string filename_relative_to_anime_dir = anime_path_to_item_name(url);
FileAnalyzer file_analyzer;
diff --git a/src/plugins/MediaGeneric.cpp b/src/plugins/MediaGeneric.cpp
index 5ee5fbc..c1044b0 100644
--- a/src/plugins/MediaGeneric.cpp
+++ b/src/plugins/MediaGeneric.cpp
@@ -224,8 +224,9 @@ namespace QuickMedia {
return video_url;
}
- PluginResult MediaGenericVideoPage::load(std::string&, std::string&, std::vector<MediaChapter>&, std::string &err_msg) {
+ PluginResult MediaGenericVideoPage::load(std::string&, std::string&, double &duration, std::vector<MediaChapter>&, std::string &err_msg) {
video_url.clear();
+ duration = 0.0;
if(!search_page->video_custom_handler) {
video_url = url;
return PluginResult::OK;
diff --git a/src/plugins/Peertube.cpp b/src/plugins/Peertube.cpp
index a5c5865..a9620f2 100644
--- a/src/plugins/Peertube.cpp
+++ b/src/plugins/Peertube.cpp
@@ -370,7 +370,7 @@ namespace QuickMedia {
}
// TODO: Media chapters
- PluginResult PeertubeVideoPage::load(std::string &title, std::string &channel_url, std::vector<MediaChapter>&, std::string &err_str) {
+ PluginResult PeertubeVideoPage::load(std::string &title, std::string &channel_url, double &duration, std::vector<MediaChapter>&, std::string &err_str) {
Json::Value json_root;
std::string err_msg;
DownloadResult download_result = download_json(json_root, server + "/api/v1/videos/" + url, {}, true, &err_msg);
@@ -386,6 +386,10 @@ namespace QuickMedia {
if(name_json.isString())
title = name_json.asString();
+ const Json::Value &duration_json = json_root["duration"];
+ if(duration_json.isInt64())
+ duration = duration_json.asInt64();
+
const Json::Value &channel_json = json_root["channel"];
if(channel_json.isObject()) {
const Json::Value &channel_url_json = channel_json["url"];
diff --git a/src/plugins/Soundcloud.cpp b/src/plugins/Soundcloud.cpp
index 9da65ee..0412aa6 100644
--- a/src/plugins/Soundcloud.cpp
+++ b/src/plugins/Soundcloud.cpp
@@ -468,8 +468,9 @@ namespace QuickMedia {
return PluginResult::OK;
}
- PluginResult SoundcloudAudioPage::load(std::string &title, std::string&, std::vector<MediaChapter>&, std::string&) {
+ PluginResult SoundcloudAudioPage::load(std::string &title, std::string&, double &duration, std::vector<MediaChapter>&, std::string&) {
title = this->title;
+ duration = 0.0;
return PluginResult::OK;
}
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index d80d86c..2d92cd8 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -7,6 +7,7 @@
#include "../../include/VideoPlayer.hpp"
#include "../../include/Utils.hpp"
#include "../../include/Theme.hpp"
+#include "../../plugins/WatchProgress.hpp"
#include <optional>
#include <json/reader.h>
extern "C" {
@@ -1858,6 +1859,30 @@ namespace QuickMedia {
VideoPage::set_url(std::move(new_url));
}
+ std::string YoutubeVideoPage::get_url_timestamp() {
+ if(!timestamp.empty())
+ return timestamp;
+
+ std::string video_id;
+ if(!youtube_url_extract_id(url, video_id)) {
+ fprintf(stderr, "Failed to extract youtube id from %s\n", url.c_str());
+ return "";
+ }
+
+ std::unordered_map<std::string, WatchProgress> watch_progress = get_watch_progress_for_plugin("youtube");
+ auto it = watch_progress.find(video_id);
+ if(it == watch_progress.end())
+ return "";
+
+ // If we are very close to the end then start from the beginning.
+ // This is the same behavior as mpv.
+ // This is better because we dont want the video player to stop immediately after we start playing and we dont get any chance to seek.
+ if(it->second.time_pos_sec + 10.0 >= it->second.duration_sec)
+ return "";
+ else
+ return std::to_string(it->second.time_pos_sec);
+ }
+
BodyItems YoutubeVideoPage::get_related_media(const std::string &url) {
comments_continuation_token.clear();
BodyItems result_items;
@@ -2213,6 +2238,7 @@ namespace QuickMedia {
video_details.author.clear();
video_details.views.clear();
video_details.description.clear();
+ video_details.duration = 0.0;
}
PluginResult YoutubeVideoPage::parse_video_response(const Json::Value &json_root, std::string &title, std::string &channel_url, std::vector<MediaChapter> &chapters, std::string &err_str) {
@@ -2274,11 +2300,18 @@ namespace QuickMedia {
const Json::Value &author_json = video_details_json["author"];
const Json::Value &view_count_json = video_details_json["viewCount"];
const Json::Value &short_description_json = video_details_json["shortDescription"];
+ const Json::Value &length_seconds_json = video_details_json["lengthSeconds"];
if(title_json.isString()) video_details.title = title_json.asString();
if(author_json.isString()) video_details.author = author_json.asString();
if(view_count_json.isString()) video_details.views = view_count_json.asString();
if(short_description_json.isString()) video_details.description = short_description_json.asString();
+ if(length_seconds_json.isString()) {
+ const char *length_seconds_str = length_seconds_json.asCString();
+ int duration = 0;
+ to_num(length_seconds_str, strlen(length_seconds_str), duration);
+ video_details.duration = duration;
+ }
title = video_details.title;
if(!video_details.description.empty())
@@ -2333,7 +2366,7 @@ namespace QuickMedia {
return PluginResult::OK;
}
- PluginResult YoutubeVideoPage::load(std::string &title, std::string &channel_url, std::vector<MediaChapter> &chapters, std::string &err_str) {
+ PluginResult YoutubeVideoPage::load(std::string &title, std::string &channel_url, double &duration, std::vector<MediaChapter> &chapters, std::string &err_str) {
std::string video_id;
if(!youtube_url_extract_id(url, video_id)) {
fprintf(stderr, "Failed to extract youtube id from %s\n", url.c_str());
@@ -2408,6 +2441,7 @@ R"END(
PluginResult result = parse_video_response(json_root, title, channel_url, chapters, err_str);
if(result == PluginResult::OK) {
err_str.clear();
+ duration = video_details.duration;
return PluginResult::OK;
}
}
@@ -2483,15 +2517,6 @@ R"END(
if(!format.isObject())
continue;
- if(is_adaptive) {
- // TODO: Fix. Some streams use &sq=num instead of index
- const Json::Value &index_range_json = format["indexRange"];
- if(index_range_json.isNull()) {
- fprintf(stderr, "Ignoring adaptive stream without indexRange\n");
- continue;
- }
- }
-
// TODO: Support HDR?
const Json::Value &quality_label_json = format["qualityLabel"];
if(quality_label_json.isString() && strstr(quality_label_json.asCString(), "HDR")) continue;
@@ -2574,4 +2599,13 @@ R"END(
const Json::Value &adaptive_formats_json = streaming_data_json["adaptiveFormats"];
parse_format(adaptive_formats_json, true);
}
+
+ void YoutubeVideoPage::set_watch_progress(int64_t time_pos_sec, int64_t duration_sec) {
+ std::string video_id;
+ if(!youtube_url_extract_id(url, video_id)) {
+ show_notification("QuickMedia", "Failed to extract youtube id from " + url);
+ return;
+ }
+ set_watch_progress_for_plugin("youtube", video_id, time_pos_sec, duration_sec, video_id);
+ }
}
diff --git a/video_player/README.md b/video_player/README.md
index 0d0adeb..247edff 100644
--- a/video_player/README.md
+++ b/video_player/README.md
@@ -19,12 +19,21 @@ Return seeking position in file in seconds
"request_id": 232, // Optional. Its provided if request_id was provided in the request
}
```
-### response on error
+## duration
+Return duration of file in seconds
+### request
```
{
- "status": "error",
- "message": "error message",
- "request_id": 233 // Optional. Its provided if request_id was provided in the request
+ "command": "duration",
+ "request_id": 232 // Optional
+}
+```
+### response on success
+```
+{
+ "status": "success",
+ "data": 112.432,
+ "request_id": 232, // Optional. Its provided if request_id was provided in the request
}
```
## sub-add
@@ -48,14 +57,6 @@ Add a subtitle file/url that is loaded asynchronously
"request_id": 233 // Optional. Its provided if request_id was provided in the request
}
```
-### response on error
-```
-{
- "status": "error",
- "message": "error message",
- "request_id": 233 // Optional. Its provided if request_id was provided in the request
-}
-```
## cycle-fullscreen
Return seeking position in file in seconds
### request
@@ -72,7 +73,7 @@ Return seeking position in file in seconds
"request_id": 232, // Optional. Its provided if request_id was provided in the request
}
```
-### response on error
+## Response on error in every command
```
{
"status": "error",
diff --git a/video_player/src/main.cpp b/video_player/src/main.cpp
index 33c4d96..91f2d4b 100644
--- a/video_player/src/main.cpp
+++ b/video_player/src/main.cpp
@@ -46,6 +46,21 @@ static Json::Value handle_json_command_time_pos(mpv_handle *mpv_ctx) {
return response_json;
}
+static Json::Value handle_json_command_duration(mpv_handle *mpv_ctx) {
+ double duration = 0.0;
+ const int res = mpv_get_property(mpv_ctx, "duration", MPV_FORMAT_DOUBLE, &duration);
+
+ Json::Value response_json(Json::objectValue);
+ if(res < 0) {
+ response_json["status"] = "error";
+ response_json["message"] = mpv_error_string(res);
+ } else {
+ response_json["status"] = "success";
+ response_json["data"] = duration;
+ }
+ return response_json;
+}
+
static Json::Value handle_json_command_sub_add(mpv_handle *mpv_ctx, const Json::Value &json_root) {
Json::Value response_json(Json::objectValue);
@@ -163,6 +178,8 @@ static void handle_json_command(mpv_handle *mpv_ctx, const Json::Value &json_roo
Json::Value response_json;
if(strcmp(command_json.asCString(), "time-pos") == 0) {
response_json = handle_json_command_time_pos(mpv_ctx);
+ } else if(strcmp(command_json.asCString(), "duration") == 0) {
+ response_json = handle_json_command_duration(mpv_ctx);
} else if(strcmp(command_json.asCString(), "sub-add") == 0) {
response_json = handle_json_command_sub_add(mpv_ctx, json_root);
} else if(strcmp(command_json.asCString(), "cycle-fullscreen") == 0) {
@@ -170,7 +187,7 @@ static void handle_json_command(mpv_handle *mpv_ctx, const Json::Value &json_roo
} else {
response_json = Json::Value(Json::objectValue);
response_json["status"] = "error";
- response_json["message"] = "invalid command " + command_json.asString() + ", expected time-pos, sub-add or cycle-fullscreen";
+ response_json["message"] = "invalid command " + command_json.asString() + ", expected time-pos, duration, sub-add or cycle-fullscreen";
}
if(request_id)