aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-06-25 22:52:47 +0200
committerdec05eba <dec05eba@protonmail.com>2021-06-25 22:52:47 +0200
commitdfa4e24f72996d507e710fc6839367536237c501 (patch)
treed1d053d064738b7f188f1c5fc760886afec430f2
parente616d1cc3ad60e314c92a8266a996244e02aaa6f (diff)
Rework youtube redirect code
-rw-r--r--include/Program.hpp3
-rw-r--r--plugins/Youtube.hpp4
-rw-r--r--src/Program.cpp18
-rw-r--r--src/QuickMedia.cpp136
-rw-r--r--src/plugins/Youtube.cpp64
5 files changed, 127 insertions, 98 deletions
diff --git a/include/Program.hpp b/include/Program.hpp
index 4007a24..92defa1 100644
--- a/include/Program.hpp
+++ b/include/Program.hpp
@@ -40,7 +40,8 @@ int wait_program_non_blocking(pid_t process_id, int *status);
int exec_program_async(const char **args, pid_t *result_process_id);
void program_clear_current_thread();
-void program_kill_in_thread(const std::thread::id &thread_id);
+void program_kill_in_thread(std::thread::id thread_id);
+bool program_is_dead_in_thread(std::thread::id thread_id);
bool program_is_dead_in_current_thread();
#endif /* QUICKMEDIA_PROGRAM_HPP */
diff --git a/plugins/Youtube.hpp b/plugins/Youtube.hpp
index 5428de0..1b17c7b 100644
--- a/plugins/Youtube.hpp
+++ b/plugins/Youtube.hpp
@@ -24,7 +24,9 @@ namespace QuickMedia {
};
bool youtube_url_extract_id(const std::string &youtube_url, std::string &youtube_video_id);
- void youtube_custom_redirect(std::string &video_url, std::string &audio_url);
+ // |video_url| or |audio_url| will be empty if there is an error and false will be returned.
+ // If false is returned from |active_handler|, then this function is cancelled.
+ bool youtube_custom_redirect(std::string &video_url, std::string &audio_url, int &video_content_length, int &audio_content_length, std::function<bool()> active_handler);
class YoutubeSearchPage : public LazyFetchPage {
public:
diff --git a/src/Program.cpp b/src/Program.cpp
index 57d7c61..c0e0eca 100644
--- a/src/Program.cpp
+++ b/src/Program.cpp
@@ -57,9 +57,11 @@ public:
void kill_in_thread(const std::thread::id &thread_id) {
std::lock_guard<std::mutex> lock(thread_current_program_mutex);
auto it = thread_current_program.find(thread_id);
- if(it != thread_current_program.end() && it->second.read_program.pid != -1 && it->second.read_program.read_fd != -1) {
- close(it->second.read_program.read_fd);
- kill(it->second.read_program.pid, SIGTERM);
+ if(it != thread_current_program.end()) {
+ if(it->second.read_program.read_fd != -1)
+ close(it->second.read_program.read_fd);
+ if(it->second.read_program.pid != -1)
+ kill(it->second.read_program.pid, SIGTERM);
it->second.killed = true;
}
}
@@ -282,10 +284,18 @@ void program_clear_current_thread() {
current_thread_program.clear();
}
-void program_kill_in_thread(const std::thread::id &thread_id) {
+void program_kill_in_thread(std::thread::id thread_id) {
current_thread_program.kill_in_thread(thread_id);
}
+bool program_is_dead_in_thread(std::thread::id thread_id) {
+ std::lock_guard<std::mutex> lock(thread_current_program_mutex);
+ auto it = thread_current_program.find(thread_id);
+ if(it != thread_current_program.end())
+ return it->second.killed;
+ return false;
+}
+
bool program_is_dead_in_current_thread() {
return current_thread_program.is_killed();
}
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index e6e4719..97900d3 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -2532,8 +2532,6 @@ namespace QuickMedia {
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
- const int num_load_tries_max = 3;
- int load_try = 0;
std::string prev_start_time;
std::vector<MediaChapter> media_chapters;
@@ -2553,73 +2551,70 @@ namespace QuickMedia {
audio_url.clear();
has_embedded_audio = true;
- 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;
-
- if(!no_video)
- video_url = video_page->get_video_url(largest_monitor_height, has_embedded_audio);
-
- if(video_url.empty() || no_video) {
- video_url = video_page->get_audio_url();
- if(video_url.empty()) {
- video_url = video_page->get_url();
- has_embedded_audio = true;
- } else {
- is_audio_only = true;
- has_embedded_audio = false;
- }
- } else if(!has_embedded_audio) {
- audio_url = video_page->get_audio_url();
- }
-
- if(!is_youtube && download_if_streaming_fails) {
- if(!video_download_if_non_streamable(video_url, audio_url, is_audio_only, has_embedded_audio, previous_page))
+ const int num_retries = is_youtube ? 3 : 1;
+ bool load_successful = false;
+ for(int i = 0; i < num_retries; ++i) {
+ bool cancelled = false;
+ TaskResult load_result = run_task_with_loading_screen([this, video_page, &cancelled, &new_title, &channel_url, &media_chapters, &youtube_video_content_length, &youtube_audio_content_length, 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;
- }
- 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;
- }
+ if(!no_video)
+ video_url = video_page->get_video_url(largest_monitor_height, has_embedded_audio);
- std::string headers;
- if(download_head_to_string(*media_url_content_lengths[i].first, headers) != DownloadResult::OK)
- return false;
+ if(video_url.empty() || no_video) {
+ video_url = video_page->get_audio_url();
+ if(video_url.empty()) {
+ video_url = video_page->get_url();
+ has_embedded_audio = true;
+ } else {
+ is_audio_only = true;
+ has_embedded_audio = false;
+ }
+ } else if(!has_embedded_audio) {
+ audio_url = video_page->get_audio_url();
+ }
- std::string content_length = header_extract_value(headers, "content-length");
- if(content_length.empty())
+ if(!is_youtube && download_if_streaming_fails) {
+ if(!video_download_if_non_streamable(video_url, audio_url, is_audio_only, has_embedded_audio, previous_page))
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 {
+ if(is_youtube && !youtube_url_is_live_stream(video_url) && !youtube_url_is_live_stream(audio_url)) {
+ youtube_video_content_length = 0;
+ youtube_audio_content_length = 0;
+ std::string new_video_url = video_url;
+ std::string new_audio_url = audio_url;
+ auto current_thread_id = std::this_thread::get_id();
+ if(!youtube_custom_redirect(new_video_url, new_audio_url, youtube_video_content_length, youtube_audio_content_length, [current_thread_id]{ return !program_is_dead_in_thread(current_thread_id); })) {
+ if(program_is_dead_in_current_thread())
+ cancelled = true;
return false;
}
+
+ video_url = std::move(new_video_url);
+ audio_url = std::move(new_audio_url);
}
- }
- return true;
- });
+ return true;
+ });
- if(!new_title.empty())
- video_title = std::move(new_title);
+ if(!new_title.empty())
+ video_title = std::move(new_title);
- if(load_result == TaskResult::CANCEL) {
- current_page = previous_page;
- go_to_previous_page = true;
- return;
- } else if(load_result == TaskResult::FALSE) {
+ if(load_result == TaskResult::CANCEL || cancelled) {
+ current_page = previous_page;
+ go_to_previous_page = true;
+ return;
+ } else if(load_result == TaskResult::FALSE) {
+ continue;
+ }
+
+ load_successful = true;
+ break;
+ }
+
+ if(!load_successful) {
show_notification("QuickMedia", "Failed to load media", Urgency::CRITICAL);
current_page = previous_page;
go_to_previous_page = true;
@@ -2762,7 +2757,7 @@ namespace QuickMedia {
}
};
- video_event_callback = [&load_try, &video_loaded, &in_seeking, &seeking_start_timer](const char *event_name) mutable {
+ 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();
@@ -2775,17 +2770,14 @@ namespace QuickMedia {
} else if(strcmp(event_name, "playback-restart") == 0) {
//video_player->set_paused(false);
in_seeking = false;
- load_try = 0;
} else if(strcmp(event_name, "file-loaded") == 0) {
video_loaded = true;
in_seeking = false;
- load_try = 0;
} else if(strcmp(event_name, "video-reconfig") == 0 || strcmp(event_name, "audio-reconfig") == 0) {
if(!video_loaded) {
video_loaded = true;
}
in_seeking = false;
- load_try = 0;
}
//fprintf(stderr, "event name: %s\n", event_name);
@@ -3064,24 +3056,10 @@ namespace QuickMedia {
load_video_error_check();
} else if(update_err != VideoPlayer::Error::OK) {
- ++load_try;
- if(load_try == 1 && num_load_tries_max > 1 && is_youtube) {
- fprintf(stderr, "Failed to play the media, retrying (try %d out of %d)\n", 1 + load_try, num_load_tries_max);
- std::string prev_video_url = video_url;
- std::string prev_audio_url = audio_url;
- youtube_custom_redirect(video_url, audio_url);
- load_video_error_check(prev_start_time, video_url != prev_video_url || audio_url != prev_audio_url);
- } else {
- if(load_try < num_load_tries_max) {
- fprintf(stderr, "Failed to play the media, retrying (try %d out of %d)\n", 1 + load_try, num_load_tries_max);
- load_video_error_check(prev_start_time);
- } else {
- show_notification("QuickMedia", "Failed to play the video (error code " + std::to_string((int)update_err) + ")", Urgency::CRITICAL);
- current_page = previous_page;
- go_to_previous_page = true;
- break;
- }
- }
+ show_notification("QuickMedia", "Failed to play the video (error code " + std::to_string((int)update_err) + ")", Urgency::CRITICAL);
+ current_page = previous_page;
+ go_to_previous_page = true;
+ break;
}
AsyncImageLoader::get_instance().update();
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index 05fe5c5..f20f38e 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -220,7 +220,7 @@ R"END(
// Sometimes youtube returns a redirect url (not in the header but in the body...).
// TODO: Find why this happens and if there is a way bypass it.
- static std::string get_playback_url_recursive(std::string playback_url) {
+ static std::string get_playback_url_recursive(std::string playback_url, int &content_length) {
std::vector<CommandArg> additional_args = get_cookies();
additional_args.push_back({ "-r", "0-4096" });
@@ -232,16 +232,27 @@ R"END(
std::string content_type = header_extract_value(response_headers, "content-type");
if(content_type.empty()) {
- fprintf(stderr, "Failed to find content-type in youtube video header. Trying to play the video anyways\n");
- return playback_url;
+ fprintf(stderr, "Failed to find content-type in youtube video header\n");
+ return "";
}
- if(string_starts_with(content_type, "video") || string_starts_with(content_type, "audio"))
+ if(string_starts_with(content_type, "video") || string_starts_with(content_type, "audio")) {
+ std::string content_length_str = header_extract_value(response_headers, "content-length");
+ if(content_length_str.empty())
+ return "";
+
+ errno = 0;
+ char *endptr;
+ content_length = strtol(content_length_str.c_str(), &endptr, 10);
+ if(endptr == content_length_str.c_str() || errno != 0)
+ return "";
+
return playback_url;
+ }
if(response_body.empty()) {
- fprintf(stderr, "Failed to redirect youtube video. Trying to play the video anyways\n");
- return playback_url;
+ fprintf(stderr, "Failed to redirect youtube video\n");
+ return "";
}
playback_url = std::move(response_body);
@@ -250,19 +261,46 @@ R"END(
return playback_url;
}
- void youtube_custom_redirect(std::string &video_url, std::string &audio_url) {
+ bool youtube_custom_redirect(std::string &video_url, std::string &audio_url, int &video_content_length, int &audio_content_length, std::function<bool()> active_handler) {
// TODO: Do this without threads
+ int num_total_tasks = 0;
AsyncTask<std::string> tasks[2];
- if(!video_url.empty())
- tasks[0] = AsyncTask<std::string>([video_url]() { return get_playback_url_recursive(std::move(video_url)); });
- if(!audio_url.empty())
- tasks[1] = AsyncTask<std::string>([audio_url]() { return get_playback_url_recursive(std::move(audio_url)); });
+ if(!video_url.empty()) {
+ tasks[0] = AsyncTask<std::string>([video_url, &video_content_length]() { return get_playback_url_recursive(std::move(video_url), video_content_length); });
+ ++num_total_tasks;
+ }
+
+ if(!audio_url.empty()) {
+ tasks[1] = AsyncTask<std::string>([audio_url, &audio_content_length]() { return get_playback_url_recursive(std::move(audio_url), audio_content_length); });
+ ++num_total_tasks;
+ }
+
+ if(num_total_tasks == 0)
+ return false;
+
+ int num_finished_tasks = 0;
std::string *strings[2] = { &video_url, &audio_url };
- for(int i = 0; i < 2; ++i) {
- if(tasks[i].valid())
+ while(true) {
+ for(int i = 0; i < 2; ++i) {
+ if(!tasks[i].ready())
+ continue;
+
*strings[i] = tasks[i].get();
+ if(strings[i]->empty())
+ return false;
+
+ ++num_finished_tasks;
+ if(num_finished_tasks == num_total_tasks)
+ return true;
+ }
+
+ if(!active_handler())
+ return false;
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
+ return true;
}
// This is a common setup of text in the youtube json