diff options
author | dec05eba <dec05eba@protonmail.com> | 2023-10-14 18:07:16 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2023-10-14 18:21:32 +0200 |
commit | 664de0fac4f92f38b1b1659fdd693655516eeb16 (patch) | |
tree | 10c4a208704294e1a4bfe8baf201fc2d76c51260 | |
parent | 9369463c70b2afb74f03b46cedfc424c9cabe6f2 (diff) |
Remove dependency on yt-dlp for youtube
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | mpv/input.conf | 4 | ||||
-rw-r--r-- | plugins/Youtube.hpp | 1 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 109 | ||||
-rw-r--r-- | src/plugins/Youtube.cpp | 107 |
6 files changed, 126 insertions, 100 deletions
@@ -36,7 +36,7 @@ Installing `lld` (the LLVM linker) can improve compile times. `noto-fonts` (when `use_system_fonts` config is not set to `true`) ### Optional `noto-fonts-cjk` needs to be installed to view chinese, japanese and korean characters (when `use_system_fonts` config is not set to `true`).\ -`yt-dlp/youtube-dl` needs to be installed to play/download xxx videos. It's temporary required right now for youtube as well.\ +`yt-dlp/youtube-dl` needs to be installed to play/download xxx videos.\ `notify-send` (which is part of `libnotify`) needs to be installed to show notifications (on Linux and other systems that uses d-bus notification system).\ [automedia](https://git.dec05eba.com/AutoMedia/) needs to be installed when tracking anime/manga with `Ctrl + T`.\ `waifu2x-ncnn-vulkan` needs to be installed when using the `--upscale-images` or `--upscale-images-always` option.\ @@ -286,4 +286,5 @@ Add option to get notified on any matrix event type (maybe have a config list wi This could be used to get notified when somebody calls us (m.call.invite) with quickmedia doesn't implement, but we could know when somebody calls us. Also mark such room the same way as a mention - it should be put at the top of the room list. -Add command to ignore/hide a room (should also not get notified when you are mentioned in the room).
\ No newline at end of file +Add command to ignore/hide a room (should also not get notified when you are mentioned in the room). +Improve youtube live streaming performance. It's slow because mpv (ffmpeg) is slow at playing m3u8.
\ No newline at end of file diff --git a/mpv/input.conf b/mpv/input.conf index 26b7ef6..d308547 100644 --- a/mpv/input.conf +++ b/mpv/input.conf @@ -1,9 +1,9 @@ Ctrl+c ignore Ctrl+s ignore f ignore -BS ignore +BS quit q ignore WHEEL_UP ignore WHEEL_DOWN ignore WHEEL_LEFT ignore -WHEEL_RIGHT ignore
\ No newline at end of file +WHEEL_RIGHT ignore diff --git a/plugins/Youtube.hpp b/plugins/Youtube.hpp index 67a2f74..0393739 100644 --- a/plugins/Youtube.hpp +++ b/plugins/Youtube.hpp @@ -165,6 +165,7 @@ namespace QuickMedia { std::string get_url_timestamp() override; 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(const SubmitArgs &args, VideoInfo &video_info, std::string &err_str) override; void mark_watched() override; void get_subtitles(SubtitleData &subtitle_data) override; diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 279cb28..7641f79 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -1346,8 +1346,6 @@ namespace QuickMedia { pipe_body->set_items(std::move(body_items)); tabs.push_back(Tab{std::move(pipe_body), std::make_unique<PipePage>(this), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); } else if(strcmp(plugin_name, "youtube") == 0) { - check_youtube_dl_installed(plugin_name); - use_youtube_dl = true; if(launch_url_type == LaunchUrlType::YOUTUBE_CHANNEL) { YoutubeChannelPage::create_each_type(this, std::move(launch_url), "", "Channel", tabs); } else if(launch_url_type == LaunchUrlType::YOUTUBE_VIDEO) { @@ -3058,8 +3056,6 @@ namespace QuickMedia { return url.find("pornhub.com") != std::string::npos || url.find("xhamster.com") != std::string::npos || url.find("spankbang.com") != std::string::npos - || url.find("youtube.com") != std::string::npos - || url.find("youtu.be") != std::string::npos // TODO: Remove when youtube-dl is no longer required to download soundcloud music || is_soundcloud(url); } @@ -3244,11 +3240,9 @@ namespace QuickMedia { mgl::Clock update_window_focus_time; // HACK! std::string youtube_video_id_dummy; - //const bool is_youtube = youtube_url_extract_id(video_page->get_url(), youtube_video_id_dummy); + const bool is_youtube = youtube_url_extract_id(video_page->get_url(), youtube_video_id_dummy); const bool is_matrix = strcmp(plugin_name, "matrix") == 0; const bool is_youtube_plugin = strcmp(plugin_name, "youtube") == 0; - const bool is_youtube = false; - const bool is_youtube_rel = youtube_url_extract_id(video_page->get_url(), youtube_video_id_dummy); bool added_recommendations = false; mgl::Clock time_watched_timer; @@ -3270,11 +3264,10 @@ namespace QuickMedia { redirect_focus_to_video_player_window(video_player_window); XSync(disp, False); - // TODO: Readd when we no longer depend on yt-dlp - //SubtitleData subtitle_data; - //video_page->get_subtitles(subtitle_data); - //if(!subtitle_data.url.empty()) - // video_player->add_subtitle(subtitle_data.url, subtitle_data.title, "eng"); + SubtitleData subtitle_data; + video_page->get_subtitles(subtitle_data); + if(!subtitle_data.url.empty()) + video_player->add_subtitle(subtitle_data.url, subtitle_data.title, "eng"); update_time_pos = true; update_window_focus_timer = true; @@ -3479,7 +3472,7 @@ namespace QuickMedia { startup_args.resume = false; startup_args.resource_root = resources_root; startup_args.monitor_height = video_max_height; - startup_args.use_youtube_dl = (use_youtube_dl || is_youtube_rel) && !video_page->is_local(); + startup_args.use_youtube_dl = use_youtube_dl && !video_page->is_local(); startup_args.title = video_title; startup_args.start_time = start_time; startup_args.chapters = std::move(video_info.chapters); @@ -3775,7 +3768,7 @@ namespace QuickMedia { current_page = previous_page; go_to_previous_page = true; break; - } else if(update_err == VideoPlayer::Error::EXITED && video_player->exit_status == 0 && (!is_matrix || is_youtube_rel)) { + } else if(update_err == VideoPlayer::Error::EXITED && video_player->exit_status == 0 && (!is_matrix || is_youtube)) { std::string new_video_url; if(!video_page->should_autoplay()) { @@ -8025,14 +8018,14 @@ namespace QuickMedia { const bool download_use_youtube_dl = url_should_download_with_youtube_dl(url); std::string filename; std::string video_id; - //const bool url_is_youtube = youtube_url_extract_id(url, video_id); + const bool url_is_youtube = youtube_url_extract_id(url, video_id); //const bool url_is_youtube = false; std::unique_ptr<YoutubeVideoPage> youtube_video_page; std::string video_url; std::string audio_url; - //int64_t video_content_length = 0; - //int64_t audio_content_length = 0; + int64_t video_content_length = 0; + int64_t audio_content_length = 0; TaskResult task_result = TaskResult::TRUE; if(download_use_youtube_dl) { @@ -8082,6 +8075,78 @@ namespace QuickMedia { return !filename.empty(); }); + } else if(url_is_youtube) { + youtube_video_page = std::make_unique<YoutubeVideoPage>(this, url); + bool cancelled = false; + bool load_successful = false; + const int video_max_height = video_get_max_height(); + + std::string err_str; + for(int i = 0; i < 3; ++i) { + task_result = run_task_with_loading_screen([&]{ + VideoInfo video_info; + SubmitArgs submit_args; + if(youtube_video_page->load(submit_args, video_info, err_str) != PluginResult::OK) + return false; + + filename = video_info.title; + std::string ext; + bool has_embedded_audio = true; + video_url = no_video ? "" : youtube_video_page->get_video_url(video_max_height, has_embedded_audio, ext); + audio_url.clear(); + + if(!has_embedded_audio || no_video) + audio_url = youtube_video_page->get_audio_url(ext); + + if(video_url.empty() && audio_url.empty()) + return false; + + if(!youtube_url_is_live_stream(video_url) && !youtube_url_is_live_stream(audio_url)) { + video_content_length = 0; + 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, video_content_length, 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); + } + + if(!video_url.empty() && !audio_url.empty()) + filename += ".mkv"; + else + filename += ext; + + return true; + }); + + if(task_result == TaskResult::CANCEL || cancelled) { + exit_code = 1; + return; + } else if(task_result == TaskResult::FALSE) { + continue; + } + + load_successful = true; + break; + } + + if(!load_successful) { + show_notification("QuickMedia", "Download failed" + (err_str.empty() ? "" : ", error: " + err_str), Urgency::CRITICAL); + exit_code = 1; + return; + } + + if(youtube_url_is_live_stream(video_url) || youtube_url_is_live_stream(audio_url)) { + show_notification("QuickMedia", "Downloading youtube live streams is currently not supported", Urgency::CRITICAL); + exit_code = 1; + return; + } } else { if(download_filename.empty()) { task_result = run_task_with_loading_screen([url, &filename]{ @@ -8173,6 +8238,16 @@ namespace QuickMedia { std::unique_ptr<Downloader> downloader; if(download_use_youtube_dl) { downloader = std::make_unique<YoutubeDlDownloader>(yt_dl_name, url, output_filepath, no_video); + } else if(url_is_youtube) { + MediaMetadata video_metadata; + video_metadata.url = std::move(video_url); + video_metadata.content_length = video_content_length; + + MediaMetadata audio_metadata; + audio_metadata.url = std::move(audio_url); + audio_metadata.content_length = audio_content_length; + + downloader = std::make_unique<YoutubeDownloader>(video_metadata, audio_metadata, output_filepath); } else { downloader = std::make_unique<CurlDownloader>(url, output_filepath); } diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index f3226de..0c9580f 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -2396,11 +2396,7 @@ namespace QuickMedia { } std::string YoutubeVideoPage::get_video_url(int max_height, bool &has_embedded_audio, std::string &ext) { - has_embedded_audio = true; - ext = ".mp4"; - return url; -#if 0 - if(!livestream_url.empty()) { + if(!livestream_url.empty() && video_formats.empty() && audio_formats.empty()) { has_embedded_audio = true; return livestream_url; } @@ -2432,7 +2428,23 @@ namespace QuickMedia { ext = ".webm"; return chosen_video_format->base.url; -#endif + } + + std::string YoutubeVideoPage::get_audio_url(std::string &ext) { + if(audio_formats.empty()) + return ""; + + const YoutubeAudioFormat *chosen_audio_format = &audio_formats.front(); + fprintf(stderr, "Choosing youtube audio format: bitrate: %d, mime type: %s\n", chosen_audio_format->base.bitrate, chosen_audio_format->base.mime_type.c_str()); + + if(chosen_audio_format->base.mime_type.find("mp4") != std::string::npos) + ext = ".m4a"; + else if(chosen_audio_format->base.mime_type.find("webm") != std::string::npos) + ext = ".opus"; // TODO: Detect if vorbis (.ogg) or opus (.opus) + else if(chosen_audio_format->base.mime_type.find("opus") != std::string::npos) + ext = ".opus"; + + return chosen_audio_format->base.url; } // Returns -1 if timestamp is in an invalid format @@ -2532,8 +2544,10 @@ namespace QuickMedia { static void subtitle_url_set_vtt_format(std::string &subtitle_url) { const size_t index = subtitle_url.find("&fmt="); - if(index == std::string::npos) + if(index == std::string::npos) { + subtitle_url += "&fmt=vtt"; return; + } size_t end_index = subtitle_url.find('&'); if(end_index == std::string::npos) @@ -2622,11 +2636,9 @@ namespace QuickMedia { } */ - if(livestream_url.empty()) { - parse_formats(*streaming_data_json); - if(video_formats.empty() && audio_formats.empty()) - return PluginResult::ERR; - } + parse_formats(*streaming_data_json); + if(video_formats.empty() && audio_formats.empty() && livestream_url.empty()) + return PluginResult::ERR; const Json::Value &video_details_json = json_root["videoDetails"]; if(video_details_json.isObject()) { @@ -2715,82 +2727,19 @@ namespace QuickMedia { // The second one works for age restricted videos and regular videos but only if they can be embedded. It doesn't work for copyrighted videos. // The third one works for all non-copyrighted, non-age restricted videos, embeddable or not. - const int num_request_types = 3; + const int num_request_types = 1; std::string request_data[num_request_types] = { R"END( -{ - "context": { - "client": { - "clientName": "ANDROID", - "clientVersion": "16.20", - "hl": "en", - "clientScreen": "EMBED" - }, - "thirdParty": { - "embedUrl": "https://google.com" - } - }, - "videoId": "%VIDEO_ID%", - "playbackContext": { - "contentPlaybackContext": { - "html5Preference": "HTML5_PREF_WANTS" - } - }, - "contentCheckOk": true, - "racyCheckOk": true -} -)END", -R"END( -{ - "context": { - "client": { - "clientName": "TVHTML5_SIMPLY_EMBEDDED_PLAYER", - "clientVersion": "2.0", - "hl": "en" - }, - "thirdParty": { - "embedUrl": "https://youtube.com/" - } - }, - "videoId": "%VIDEO_ID%", - "contentCheckOk": true, - "racyCheckOk": true -} -)END", -R"END( -{ - "context": { - "client": { - "clientName": "ANDROID", - "clientVersion": "16.20", - "hl": "en" - }, - "thirdParty": { - "embedUrl": "https://google.com" - } - }, - "videoId": "%VIDEO_ID%", - "playbackContext": { - "contentPlaybackContext": { - "html5Preference": "HTML5_PREF_WANTS" - } - }, - "contentCheckOk": true, - "racyCheckOk": true -} + {"context":{"client":{"hl":"en","gl":"US","clientName":"IOS","clientVersion":"17.33.2","deviceModel":"iPhone14,3"}},"videoId":"%VIDEO_ID%"} )END", }; std::string client_names[num_request_types] = { - "3", - "85", - "3" + "1" }; std::string client_versions[num_request_types] = { - "16.20", - "2.0", - "16.20" + "2.20210622.10.00" }; for(int i = 0; i < num_request_types; ++i) { |