diff --git a/CHANGELOG.md b/CHANGELOG.md index f84dc790..c0718686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## vX.Y.0 (future) +## v2.20250504.0 + +Small release with quick workaround fix for issue #4251 (Nil assertion failed). + +PR: https://github.com/iv-org/invidious/issues/5263 + ## v2.20250314.0 ### Wrap-up diff --git a/config/config.example.yml b/config/config.example.yml index 33bda8cb..c4aaf9b8 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -90,14 +90,14 @@ db: ## ## API key for Invidious companion, used for securing the communication ## between Invidious and Invidious companion. -## The size of the key needs to be more or equal to 16. +## The key needs to be exactly 16 characters long. ## ## Note: This parameter is mandatory when Invidious companion is enabled ## and should be a random string. ## Such random string can be generated on linux with the following ## command: `pwgen 16 1` ## -## Accepted values: a string +## Accepted values: a string (of length 16) ## Default: ## #invidious_companion_key: "CHANGE_ME!!" @@ -921,9 +921,9 @@ default_user_preferences: ## Default video quality. ## ## Accepted values: dash, hd720, medium, small - ## Default: hd720 + ## Default: dash ## - #quality: hd720 + #quality: dash ## ## Default dash video quality. diff --git a/src/invidious/config.cr b/src/invidious/config.cr index 5fdc687c..471c72f0 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -35,7 +35,7 @@ struct ConfigPreferences property max_results : Int32 = 40 property notifications_only : Bool = false property player_style : String = "invidious" - property quality : String = "hd720" + property quality : String = "dash" property quality_dash : String = "auto" property default_home : String? = "Popular" property feed_menu : Array(String) = ["Popular", "Trending", "Subscriptions", "Playlists", "History"] diff --git a/src/invidious/frontend/watch_page.cr b/src/invidious/frontend/watch_page.cr index 2e2f6ad0..15d925e3 100644 --- a/src/invidious/frontend/watch_page.cr +++ b/src/invidious/frontend/watch_page.cr @@ -23,10 +23,16 @@ module Invidious::Frontend::WatchPage return "

#{translate(locale, "Download is disabled")}

" end + url = "/download" + if (CONFIG.invidious_companion.present?) + invidious_companion = CONFIG.invidious_companion.sample + url = "#{invidious_companion.public_url}/download?check=#{invidious_companion_encrypt(video.id)}" + end + return String.build(4000) do |str| str << "" diff --git a/src/invidious/routes/video_playback.cr b/src/invidious/routes/video_playback.cr index ce7b43dc..76d929a2 100644 --- a/src/invidious/routes/video_playback.cr +++ b/src/invidious/routes/video_playback.cr @@ -31,7 +31,7 @@ module Invidious::Routes::VideoPlayback end # Sanity check, to avoid being used as an open proxy - if !host.matches?(/[\w-]+.googlevideo.com/) && !host.matches?(/[\w-]+.c.youtube.com/) + if !host.matches?(/[\w-]+\.(?:googlevideo|c\.youtube)\.com/) return error_template(400, "Invalid \"host\" parameter.") end @@ -47,7 +47,8 @@ module Invidious::Routes::VideoPlayback # See: https://github.com/iv-org/invidious/issues/3302 range_header = env.request.headers["Range"]? - if range_header.nil? + sq = query_params["sq"]? + if range_header.nil? && sq.nil? range_for_head = query_params["range"]? || "0-640" headers["Range"] = "bytes=#{range_for_head}" end diff --git a/src/invidious/routes/watch.cr b/src/invidious/routes/watch.cr index b3d25e8e..0d55bab7 100644 --- a/src/invidious/routes/watch.cr +++ b/src/invidious/routes/watch.cr @@ -311,6 +311,9 @@ module Invidious::Routes::Watch if CONFIG.disabled?("downloads") return error_template(403, "Administrator has disabled this endpoint.") end + if CONFIG.invidious_companion.present? + return error_template(403, "Downloads should be routed through Companion when present") + end title = env.params.body["title"]? || "" video_id = env.params.body["id"]? || "" @@ -346,13 +349,7 @@ module Invidious::Routes::Watch env.params.query["title"] = filename env.params.query["local"] = "true" - if (CONFIG.invidious_companion.present?) - video = get_video(video_id, env: env) - companion_public_url = env.get("companion_public_url").as(String) - return env.redirect "#{companion_public_url}/latest_version?#{env.params.query}" - else - return Invidious::Routes::VideoPlayback.latest_version(env) - end + return Invidious::Routes::VideoPlayback.latest_version(env) else return error_template(400, "Invalid label or itag") end diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index f7853ec4..6f9bc565 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -109,27 +109,20 @@ def extract_video_info(video_id : String, env : HTTP::Server::Context | Nil = ni params["reason"] = JSON::Any.new(reason) if reason if !CONFIG.invidious_companion.present? - new_player_response = nil - - # Don't use Android test suite client if po_token is passed because po_token doesn't - # work for Android test suite client. - if reason.nil? && CONFIG.po_token.nil? - # Fetch the video streams using an Android client in order to get the - # decrypted URLs and maybe fix throttling issues (#2194). See the - # following issue for an explanation about decrypted URLs: - # https://github.com/TeamNewPipe/NewPipeExtractor/issues/562 - client_config.client_type = YoutubeAPI::ClientType::AndroidTestSuite - new_player_response = try_fetch_streaming_data(video_id, client_config, env) - end - - # Replace player response and reset reason - if !new_player_response.nil? - # Preserve captions & storyboard data before replacement - new_player_response["storyboards"] = player_response["storyboards"] if player_response["storyboards"]? - new_player_response["captions"] = player_response["captions"] if player_response["captions"]? - - player_response = new_player_response - params.delete("reason") + if player_response["streamingData"]? && player_response.dig?("streamingData", "adaptiveFormats", 0, "url").nil? + LOGGER.warn("Missing URLs for adaptive formats, falling back to other YT clients.") + players_fallback = [YoutubeAPI::ClientType::WebMobile, YoutubeAPI::ClientType::TvHtml5] + players_fallback.each do |player_fallback| + client_config.client_type = player_fallback + player_fallback_response = try_fetch_streaming_data(video_id, client_config, env) + if player_fallback_response && player_fallback_response["streamingData"]? && + player_fallback_response.dig?("streamingData", "adaptiveFormats", 0, "url") + streaming_data = player_response["streamingData"].as_h + streaming_data["adaptiveFormats"] = player_fallback_response["streamingData"]["adaptiveFormats"] + player_response["streamingData"] = JSON::Any.new(streaming_data) + break + end + end end end