aboutsummaryrefslogtreecommitdiff
path: root/src/QuickMedia.cpp
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-06-25 12:44:53 +0200
committerdec05eba <dec05eba@protonmail.com>2021-06-25 12:44:53 +0200
commit38202de4f953fca28aa884246ced0aadf0d25a4d (patch)
tree7a0a35a32404f1929238444d13a6c626856cc791 /src/QuickMedia.cpp
parent738f2b1a89a5445a1f0f94229f2fc0637b7c4e71 (diff)
Add a http server proxy for better youtube downloading (bypassing rate limit cased by http range header). Fix youtube live streams
Diffstat (limited to 'src/QuickMedia.cpp')
-rw-r--r--src/QuickMedia.cpp159
1 files changed, 150 insertions, 9 deletions
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 7305788..e6e4719 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -29,6 +29,7 @@
#include "../include/Utils.hpp"
#include "../include/Tabs.hpp"
#include "../include/Theme.hpp"
+#include "../plugins/youtube/YoutubeMediaProxy.hpp"
#include "../include/gui/Button.hpp"
#include "../external/hash-library/sha256.h"
@@ -1049,6 +1050,13 @@ namespace QuickMedia {
return PluginResult::OK;
}
+ static void check_youtube_dl_installed(const std::string &plugin_name) {
+ if(!is_program_executable_by_name("youtube-dl")) {
+ show_notification("QuickMedia", "youtube-dl needs to be installed to play " + plugin_name + " videos", Urgency::CRITICAL);
+ abort();
+ }
+ }
+
void Program::load_plugin_by_name(std::vector<Tab> &tabs, int &start_tab_index, FileManagerMimeType fm_mime_type, FileSelectionHandler file_selection_handler) {
if(!plugin_name || plugin_name[0] == '\0')
return;
@@ -1208,18 +1216,22 @@ namespace QuickMedia {
video_content_page(nullptr, youtube_video_page.get(), "", false, nullptr, body_items, 0);
}
} else if(strcmp(plugin_name, "pornhub") == 0) {
+ check_youtube_dl_installed(plugin_name);
auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://www.pornhub.com/", sf::Vector2i(320/1.5f, 180/1.5f));
add_pornhub_handlers(search_page.get());
tabs.push_back(Tab{create_body(false, true), std::move(search_page), create_search_bar("Search...", 500)});
} else if(strcmp(plugin_name, "spankbang") == 0) {
+ check_youtube_dl_installed(plugin_name);
auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://spankbang.com/", sf::Vector2i(500/2.5f, 281/2.5f));
add_spankbang_handlers(search_page.get());
tabs.push_back(Tab{create_body(false, true), std::move(search_page), create_search_bar("Search...", 500)});
} else if(strcmp(plugin_name, "xvideos") == 0) {
+ check_youtube_dl_installed(plugin_name);
auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://www.xvideos.com/", sf::Vector2i(352/1.5f, 198/1.5f));
add_xvideos_handlers(search_page.get());
tabs.push_back(Tab{create_body(false, true), std::move(search_page), create_search_bar("Search...", 500)});
} else if(strcmp(plugin_name, "xhamster") == 0) {
+ check_youtube_dl_installed(plugin_name);
auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://xhamster.com/", sf::Vector2i(240, 135));
add_xhamster_handlers(search_page.get());
tabs.push_back(Tab{create_body(false, true), std::move(search_page), create_search_bar("Search...", 500)});
@@ -2469,6 +2481,11 @@ namespace QuickMedia {
return true;
}
+ // TODO: Test with video that has hlsManifestUrl
+ static bool youtube_url_is_live_stream(const std::string &url) {
+ return url.find("yt_live_broadcast") != std::string::npos;
+ }
+
#define CLEANMASK(mask) ((mask) & (ShiftMask|ControlMask|Mod1Mask|Mod4Mask|Mod5Mask))
void Program::video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, Body *parent_body, BodyItems &next_play_items, int play_index, int *parent_body_page, const std::string &parent_page_search) {
@@ -2496,6 +2513,12 @@ namespace QuickMedia {
XSync(disp, False);
};
+ std::unique_ptr<YoutubeMediaProxy> youtube_video_media_proxy;
+ std::unique_ptr<YoutubeMediaProxy> youtube_audio_media_proxy;
+ AsyncTask<void> youtube_downloader_task;
+ int youtube_video_content_length = 0;
+ int youtube_audio_content_length = 0;
+
std::string channel_url;
AsyncTask<void> video_tasks;
std::function<void(const char*)> video_event_callback;
@@ -2514,7 +2537,7 @@ namespace QuickMedia {
std::string prev_start_time;
std::vector<MediaChapter> media_chapters;
- auto load_video_error_check = [this, &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 {
+ 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 {
video_player.reset();
channel_url.clear();
video_loaded = false;
@@ -2530,7 +2553,7 @@ namespace QuickMedia {
audio_url.clear();
has_embedded_audio = true;
- TaskResult load_result = run_task_with_loading_screen([this, video_page, &new_title, &channel_url, &media_chapters, largest_monitor_height, &has_embedded_audio, &video_url, &audio_url, &is_audio_only, &previous_page, is_youtube, download_if_streaming_fails]() {
+ TaskResult load_result = run_task_with_loading_screen([this, video_page, &youtube_video_content_length, &youtube_audio_content_length, &new_title, &channel_url, &media_chapters, largest_monitor_height, &has_embedded_audio, &video_url, &audio_url, &is_audio_only, &previous_page, is_youtube, download_if_streaming_fails]() {
if(video_page->load(new_title, channel_url, media_chapters) != PluginResult::OK)
return false;
@@ -2555,6 +2578,37 @@ namespace QuickMedia {
return false;
}
+ if(is_youtube) {
+ // TODO: Do these requests in parallel
+ std::pair<std::string*, int*> media_url_content_lengths[2] = {
+ std::make_pair(&video_url, &youtube_video_content_length),
+ std::make_pair(&audio_url, &youtube_audio_content_length),
+ };
+ for(int i = 0; i < 2; ++i) {
+ if(media_url_content_lengths[i].first->empty() || youtube_url_is_live_stream(*media_url_content_lengths[i].first)) {
+ *media_url_content_lengths[i].second = 0;
+ continue;
+ }
+
+ std::string headers;
+ if(download_head_to_string(*media_url_content_lengths[i].first, headers) != DownloadResult::OK)
+ return false;
+
+ std::string content_length = header_extract_value(headers, "content-length");
+ if(content_length.empty())
+ return false;
+
+ errno = 0;
+ char *endptr;
+ const long content_length_tmp = strtol(content_length.c_str(), &endptr, 10);
+ if(endptr != content_length.c_str() && errno == 0) {
+ *media_url_content_lengths[i].second = content_length_tmp;
+ } else {
+ return false;
+ }
+ }
+ }
+
return true;
});
@@ -2579,9 +2633,82 @@ namespace QuickMedia {
prev_start_time = start_time;
watched_videos.insert(video_page->get_url());
+ // TODO: Sync sequences
+ //audio_url.clear();
+ //video_url.clear();
+ //is_audio_only = true;
+
+ std::string v = video_url;
+ std::string a = audio_url;
+ if(is_youtube) {
+ if(youtube_video_media_proxy)
+ youtube_video_media_proxy->stop();
+
+ if(youtube_audio_media_proxy)
+ youtube_audio_media_proxy->stop();
+
+ if(youtube_downloader_task.valid())
+ youtube_downloader_task.cancel();
+
+ youtube_video_media_proxy.reset();
+ youtube_audio_media_proxy.reset();
+
+ struct MediaProxyMetadata {
+ std::unique_ptr<YoutubeMediaProxy> *media_proxy;
+ std::string *url;
+ int content_length;
+ };
+
+ MediaProxyMetadata media_proxies[2] = {
+ { &youtube_video_media_proxy, &v, youtube_video_content_length },
+ { &youtube_audio_media_proxy, &a, youtube_audio_content_length }
+ };
+ int num_proxied_media = 0;
+ for(int i = 0; i < 2; ++i) {
+ 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>();
+ 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;
+ go_to_previous_page = true;
+ return;
+ }
+
+ std::string media_proxy_addr;
+ if(!(*media_proxies[i].media_proxy)->get_address(media_proxy_addr)) {
+ show_notification("QuickMedia", "Failed to load start youtube media proxy", Urgency::CRITICAL);
+ current_page = previous_page;
+ go_to_previous_page = true;
+ return;
+ }
+
+ *media_proxies[i].url = std::move(media_proxy_addr);
+ ++num_proxied_media;
+ }
+
+ if(num_proxied_media > 0) {
+ youtube_downloader_task = AsyncTask<void>([&youtube_video_media_proxy, &youtube_audio_media_proxy]() {
+ sf::Clock timer;
+ const double sleep_time_millisec = 1;
+ while(!program_is_dead_in_current_thread()) {
+ if(youtube_video_media_proxy)
+ youtube_video_media_proxy->update();
+
+ if(youtube_audio_media_proxy)
+ youtube_audio_media_proxy->update();
+
+ const int sleep_time = sleep_time_millisec - timer.restart().asMilliseconds();
+ if(sleep_time > 0)
+ std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));
+ }
+ });
+ }
+ }
video_player = std::make_unique<VideoPlayer>(is_audio_only, use_system_mpv_config, is_matrix && !is_youtube, video_event_callback, on_window_create, resources_root, largest_monitor_height, plugin_name);
- VideoPlayer::Error err = video_player->load_video(video_url.c_str(), audio_url.c_str(), window.getSystemHandle(), is_youtube, video_title, start_time, media_chapters);
+ VideoPlayer::Error err = video_player->load_video(v.c_str(), a.c_str(), window.getSystemHandle(), is_youtube, video_title, start_time, media_chapters);
if(err != VideoPlayer::Error::OK) {
std::string err_msg = "Failed to play url: ";
err_msg += video_page->get_url();
@@ -2787,11 +2914,23 @@ namespace QuickMedia {
bool page_changed = false;
double resume_start_time = 0.0;
- page_loop(tabs, 1, [this, &page_changed, &resume_start_time](const std::vector<Tab> &new_tabs) {
+ page_loop(tabs, 1, [this, &page_changed, &resume_start_time, &youtube_video_media_proxy, &youtube_audio_media_proxy, &youtube_downloader_task](const std::vector<Tab> &new_tabs) {
if(!page_changed && new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::VIDEO) {
video_player->get_time_in_file(&resume_start_time);
video_player.reset();
page_changed = true;
+
+ if(youtube_video_media_proxy)
+ youtube_video_media_proxy->stop();
+
+ if(youtube_audio_media_proxy)
+ youtube_audio_media_proxy->stop();
+
+ if(youtube_downloader_task.valid())
+ youtube_downloader_task.cancel();
+
+ youtube_video_media_proxy.reset();
+ youtube_audio_media_proxy.reset();
}
});
@@ -6339,6 +6478,7 @@ namespace QuickMedia {
if(exec_program_pipe(args, &read_program) != 0)
return false;
+ // TODO: Remove this async task and make the fd non blocking instead
header_reader = AsyncTask<bool>([this]{
char tmp_buf[1024];
while(true) {
@@ -6368,10 +6508,10 @@ namespace QuickMedia {
}
void stop(bool download_completed) override {
- if(read_program.pid != -1)
- close(read_program.pid);
if(read_program.read_fd != -1)
- kill(read_program.read_fd, SIGTERM);
+ close(read_program.read_fd);
+ if(read_program.pid != -1)
+ kill(read_program.pid, SIGTERM);
if(!download_completed)
remove(output_filepath_tmp.data.c_str());
//header_reader.cancel();
@@ -6476,6 +6616,7 @@ namespace QuickMedia {
return false;
}
+ // TODO: Remove this async task and make the fd non blocking instead
youtube_dl_output_reader = AsyncTask<bool>([this]{
char line[128];
char progress_c[10];
@@ -6524,8 +6665,8 @@ namespace QuickMedia {
void stop(bool) override {
if(read_program_file)
fclose(read_program_file);
- if(read_program.read_fd != -1)
- kill(read_program.read_fd, SIGTERM);
+ if(read_program.pid != -1)
+ kill(read_program.pid, SIGTERM);
// TODO: Remove the temporary files created by youtube-dl (if !download_completed)
//header_reader.cancel();
}