aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-08-05 10:22:41 +0200
committerdec05eba <dec05eba@protonmail.com>2021-08-05 10:29:32 +0200
commita0c0f0bd551155270b11ac23c7f1c526a20a44b9 (patch)
tree5058c250fce097684d75aa900d060c9d1519b591 /src
parent6c85194c3b1baef0eaa011c4f1b8e48e11860f45 (diff)
Youtube: attempt to auto-detect throttling and restart download
Diffstat (limited to 'src')
-rw-r--r--src/QuickMedia.cpp48
-rw-r--r--src/VideoPlayer.cpp5
-rw-r--r--src/plugins/youtube/YoutubeMediaProxy.cpp57
3 files changed, 75 insertions, 35 deletions
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 2db5537..bb92484 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -2550,9 +2550,6 @@ namespace QuickMedia {
PageType previous_page = pop_page_stack();
bool video_loaded = false;
- bool in_seeking = false;
- sf::Clock seeking_start_timer;
- const float seeking_restart_timeout_sec = 10.0f; // TODO: Test if this timeout is good on slow hardware such as pinephone and slow internet
std::string youtube_video_id_dummy;
const bool is_youtube = youtube_url_extract_id(video_page->get_url(), youtube_video_id_dummy);
@@ -2569,7 +2566,7 @@ namespace QuickMedia {
}
sf::WindowHandle video_player_window = None;
- auto on_window_create = [this, &video_player_window, &video_loaded, &in_seeking](sf::WindowHandle _video_player_window) mutable {
+ auto on_window_create = [this, &video_player_window, &video_loaded](sf::WindowHandle _video_player_window) mutable {
video_player_window = _video_player_window;
XSelectInput(disp, video_player_window, KeyPressMask | PointerMotionMask);
XSync(disp, False);
@@ -2578,10 +2575,8 @@ namespace QuickMedia {
// TODO: This is an issue just because of ubuntu shit that uses old mpv that doesn't support ipc over file descriptors.
double time_in_file = 0.0;
video_player->get_time_in_file(&time_in_file);
- if(time_in_file > 0.00001) {
+ if(time_in_file > 0.00001)
video_loaded = true;
- in_seeking = false;
- }
};
std::unique_ptr<YoutubeMediaProxy> youtube_video_media_proxy;
@@ -2602,11 +2597,15 @@ namespace QuickMedia {
std::string prev_start_time;
std::vector<MediaChapter> media_chapters;
- auto load_video_error_check = [this, &youtube_downloader_task, &youtube_video_media_proxy, &youtube_audio_media_proxy, &youtube_video_content_length, &youtube_audio_content_length, &prev_start_time, &media_chapters, &in_seeking, &video_url, &audio_url, &has_embedded_audio, &video_title, &video_tasks, &channel_url, previous_page, &go_to_previous_page, &video_loaded, video_page, &video_event_callback, &on_window_create, &video_player_window, is_youtube, is_matrix, download_if_streaming_fails](std::string start_time = "", bool reuse_media_source = false) mutable {
+ bool throttled = false;
+ auto throttle_handler = [&throttled] {
+ throttled = true;
+ };
+
+ auto load_video_error_check = [this, &throttle_handler, &throttled, &youtube_downloader_task, &youtube_video_media_proxy, &youtube_audio_media_proxy, &youtube_video_content_length, &youtube_audio_content_length, &prev_start_time, &media_chapters, &video_url, &audio_url, &has_embedded_audio, &video_title, &video_tasks, &channel_url, previous_page, &go_to_previous_page, &video_loaded, video_page, &video_event_callback, &on_window_create, &video_player_window, is_youtube, is_matrix, download_if_streaming_fails](std::string start_time = "", bool reuse_media_source = false) mutable {
video_player.reset();
channel_url.clear();
video_loaded = false;
- in_seeking = false;
video_player_window = None;
bool is_audio_only = no_video;
@@ -2713,6 +2712,7 @@ namespace QuickMedia {
youtube_downloader_task.cancel();
youtube_video_media_proxy.reset();
youtube_audio_media_proxy.reset();
+ throttled = false;
struct MediaProxyMetadata {
std::unique_ptr<YoutubeMediaProxy> *media_proxy;
@@ -2729,7 +2729,7 @@ namespace QuickMedia {
if(media_proxies[i].url->empty() || youtube_url_is_live_stream(*media_proxies[i].url))
continue;
- *media_proxies[i].media_proxy = std::make_unique<YoutubeStaticMediaProxy>();
+ *media_proxies[i].media_proxy = std::make_unique<YoutubeStaticMediaProxy>(throttle_handler);
if(!(*media_proxies[i].media_proxy)->start(*media_proxies[i].url, media_proxies[i].content_length)) {
show_notification("QuickMedia", "Failed to load start youtube media proxy", Urgency::CRITICAL);
current_page = previous_page;
@@ -2823,27 +2823,17 @@ namespace QuickMedia {
}
};
- video_event_callback = [&video_loaded, &in_seeking, &seeking_start_timer](const char *event_name) mutable {
- if(strcmp(event_name, "seek") == 0) {
- in_seeking = true;
- seeking_start_timer.restart();
- }
-
+ video_event_callback = [&video_loaded](const char *event_name) mutable {
if(strcmp(event_name, "pause") == 0) {
//double time_remaining = 9999.0;
//if(video_player->get_time_remaining(&time_remaining) == VideoPlayer::Error::OK && time_remaining <= 1.0)
// end_of_file = true;
} else if(strcmp(event_name, "playback-restart") == 0) {
//video_player->set_paused(false);
- in_seeking = false;
} else if(strcmp(event_name, "file-loaded") == 0) {
video_loaded = true;
- in_seeking = false;
} else if(strcmp(event_name, "video-reconfig") == 0 || strcmp(event_name, "audio-reconfig") == 0) {
- if(!video_loaded) {
- video_loaded = true;
- }
- in_seeking = false;
+ video_loaded = true;
}
//fprintf(stderr, "event name: %s\n", event_name);
@@ -2918,7 +2908,6 @@ namespace QuickMedia {
} else if(pressed_keysym == XK_s && pressing_ctrl) {
video_page_download_video(video_page->get_url(), video_player_window);
} else if(pressed_keysym == XK_F5) {
- in_seeking = false;
double resume_start_time = 0.0;
video_player->get_time_in_file(&resume_start_time);
load_video_error_check(std::to_string((int)resume_start_time));
@@ -3017,15 +3006,12 @@ namespace QuickMedia {
cursor_visible = true;
}
- // TODO: Remove the need for this. This is needed right now because mpv sometimes gets stuck when playing youtube videos after seeking too much
- if(is_youtube && video_player && in_seeking && seeking_start_timer.getElapsedTime().asSeconds() >= seeking_restart_timeout_sec) {
- in_seeking = false;
+ if(is_youtube && video_player && throttled) {
+ throttled = false;
+ fprintf(stderr, "Throttled media download detected, reconnecting...\n");
double resume_start_time = 0.0;
- if(video_player->get_time_in_file(&resume_start_time) == VideoPlayer::Error::OK) {
- fprintf(stderr, "Video seems to be stuck after seeking, reloading...\n");
- // TODO: Set the second argument to false if the video url is no longer valid (or always?)
- load_video_error_check(std::to_string((int)resume_start_time), true);
- }
+ video_player->get_time_in_file(&resume_start_time);
+ load_video_error_check(std::to_string((int)resume_start_time));
}
VideoPlayer::Error update_err = video_player ? video_player->update() : VideoPlayer::Error::OK;
diff --git a/src/VideoPlayer.cpp b/src/VideoPlayer.cpp
index 7f01912..3e22b8b 100644
--- a/src/VideoPlayer.cpp
+++ b/src/VideoPlayer.cpp
@@ -133,6 +133,7 @@ namespace QuickMedia {
wid_arg += parent_window_str;
std::string input_conf = "--input-conf=" + resource_root + "input.conf";
+ std::string cache_dir = "--cache-dir=" + std::move(get_cache_dir().join("media").data);
// TODO: Resume playback if the last video played matches the first video played next time QuickMedia is launched
args.insert(args.end(), {
@@ -149,6 +150,10 @@ namespace QuickMedia {
"--force-seekable=yes",
"--image-display-duration=5",
"--cache-pause=yes",
+ "--cache=yes",
+ "--cache-on-disk=yes",
+ "--cache-secs=86400", // 24 hours
+ cache_dir.c_str(),
input_conf.c_str(),
wid_arg.c_str()
});
diff --git a/src/plugins/youtube/YoutubeMediaProxy.cpp b/src/plugins/youtube/YoutubeMediaProxy.cpp
index 6642b86..ed38981 100644
--- a/src/plugins/youtube/YoutubeMediaProxy.cpp
+++ b/src/plugins/youtube/YoutubeMediaProxy.cpp
@@ -19,6 +19,8 @@
namespace QuickMedia {
static const int MAX_BUFFER_SIZE = 65536;
static const int RANGE = 524287;
+ static const int64_t THROTTLED_DOWNLOAD_LIMIT_KB = 80; // TODO: What about people with really slow internet? What if the video player cache is not working and download is stuck, leading to false download speed calculation?
+ static const int64_t THROTTLED_DURATION_SECS = 3;
static const char download_error_response_msg[] =
"HTTP/1.1 500 Internal Server Error\r\n"
"Content-Length: 0\r\n\r\n";
@@ -73,6 +75,10 @@ namespace QuickMedia {
return true;
}
+ YoutubeStaticMediaProxy::YoutubeStaticMediaProxy(ThrottleHandler throttle_handler) : throttle_handler(std::move(throttle_handler)) {
+
+ }
+
YoutubeStaticMediaProxy::~YoutubeStaticMediaProxy() {
stop();
}
@@ -193,6 +199,8 @@ namespace QuickMedia {
void YoutubeStaticMediaProxy::on_client_disconnect() {
client_request_buffer.clear();
client_request_finished = false;
+ download_started = false;
+ throttle_started = false;
if(client_fd != -1) {
close(client_fd);
@@ -248,6 +256,8 @@ namespace QuickMedia {
if(header_end != std::string::npos) {
client_request_buffer.erase(header_end + 4);
client_request_finished = true;
+ download_started = false;
+ throttle_started = false;
int new_start_range = header_extract_start_range(client_request_buffer);
//fprintf(stderr, "got new range from client: %d\n", new_start_range);
@@ -284,7 +294,7 @@ namespace QuickMedia {
if(client_disconnected) {
wait_program(downloader_read_program.pid);
} else {
- if(wait_program_non_blocking(downloader_read_program.pid, &program_status) == 0)
+ if(wait_program_non_blocking(downloader_read_program.pid, &program_status) == 0 && program_status == 0)
return Error::OK;
}
downloader_read_program.pid = -1;
@@ -444,6 +454,45 @@ namespace QuickMedia {
Error err = update_download_program_status(false, -1, true);
if(err != Error::OK)
return err;
+ } else {
+ if(!download_started) {
+ total_downloaded_bytes = 0;
+ download_started = true;
+ throttle_started = false;
+
+ struct timespec tp;
+ clock_gettime(CLOCK_BOOTTIME, &tp);
+ download_start_time = tp.tv_sec;
+ }
+ total_downloaded_bytes += downloader_num_read_bytes;
+ }
+ }
+
+ if(download_started) {
+ struct timespec tp;
+ clock_gettime(CLOCK_BOOTTIME, &tp);
+
+ int64_t time_elapsed = tp.tv_sec - download_start_time;
+ int64_t download_speed_kb_sec = 0;
+ if(time_elapsed > 0)
+ download_speed_kb_sec = (total_downloaded_bytes / time_elapsed) / 1024;
+
+ if(download_speed_kb_sec < THROTTLED_DOWNLOAD_LIMIT_KB) {
+ if(throttle_started) {
+ if(tp.tv_sec - throttle_start_time >= THROTTLED_DURATION_SECS && !throttle_callback_called) {
+ total_downloaded_bytes = 0;
+ download_started = false;
+ throttle_started = false;
+ throttle_callback_called = true;
+ if(throttle_handler)
+ throttle_handler();
+ }
+ } else {
+ throttle_started = true;
+ throttle_start_time = tp.tv_sec;
+ }
+ } else {
+ throttle_started = false;
}
}
@@ -587,7 +636,7 @@ namespace QuickMedia {
YoutubeMediaProxy::Error YoutubeLiveStreamMediaProxy::update_download_program_status() {
int program_status = 0;
- if(wait_program_non_blocking(downloader_read_program.pid, &program_status) == 0)
+ if(wait_program_non_blocking(downloader_read_program.pid, &program_status) == 0 && program_status == 0)
return Error::OK;
downloader_read_program.pid = -1;
@@ -688,13 +737,13 @@ namespace QuickMedia {
std::string sequence_num = header_extract_value(download_header, "x-sequence-num");
fprintf(stderr, "server sequence num: |%s|\n", sequence_num.c_str());
if(sequence_num.empty())
- fprintf(stderr, "YoutubeLiveStreamMediaProxy::handle_download: missing sequence num from server\n");
+ fprintf(stderr, "YoutubeLiveStreamMediaProxy::update: missing sequence num from server\n");
else
livestream_sequence_num = strtoll(sequence_num.c_str(), nullptr, 10);
}
} else {
if(download_header.size() > MAX_BUFFER_SIZE) {
- fprintf(stderr, "YoutubeLiveStreamMediaProxy::handle_download: buffer is full (malicious server?)\n");
+ fprintf(stderr, "YoutubeLiveStreamMediaProxy::update: buffer is full (malicious server?)\n");
if(downloader_read_program.pid != -1) {
kill(downloader_read_program.pid, SIGTERM);
wait_program(downloader_read_program.pid);