From df8839d1f018644afecb15e144f228d811708f8f Mon Sep 17 00:00:00 2001 From: syeopite <70992037+syeopite@users.noreply.github.com> Date: Wed, 28 May 2025 20:18:51 +0000 Subject: [PATCH] Make base-Invidious video info extraction more resilient (#5312) Try next fallback client if one raises Convert `dig` to `dig?` Optimize companionless stream retrieval --- src/invidious/videos/parser.cr | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index 15bd00b6..feb58440 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -82,7 +82,7 @@ def extract_video_info(video_id : String) "reason" => JSON::Any.new(reason), } end - elsif video_id != player_response.dig("videoDetails", "videoId") + elsif video_id != player_response.dig?("videoDetails", "videoId") # YouTube may return a different video player response than expected. # See: https://github.com/TeamNewPipe/NewPipe/issues/8713 # Line to be reverted if one day we solve the video not available issue. @@ -109,21 +109,33 @@ def extract_video_info(video_id : String) params["reason"] = JSON::Any.new(reason) if reason if !CONFIG.invidious_companion.present? - if player_response["streamingData"]? && player_response.dig?("streamingData", "adaptiveFormats", 0, "url").nil? + if 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::TvHtml5, YoutubeAPI::ClientType::WebMobile] + players_fallback = {YoutubeAPI::ClientType::TvHtml5, YoutubeAPI::ClientType::WebMobile} + players_fallback.each do |player_fallback| client_config.client_type = player_fallback - player_fallback_response = try_fetch_streaming_data(video_id, client_config) - if player_fallback_response && player_fallback_response["streamingData"]? && - player_fallback_response.dig?("streamingData", "adaptiveFormats", 0, "url") + + next if !(player_fallback_response = try_fetch_streaming_data(video_id, client_config)) + + if 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 + rescue InfoException + next LOGGER.warn("Failed to fetch streams with #{player_fallback}") end end + + # Seems like video page can still render even without playable streams. + # its better than nothing. + # + # # Were we able to find playable video streams? + # if player_response.dig?("streamingData", "adaptiveFormats", 0, "url").nil? + # # No :( + # end end {"captions", "playabilityStatus", "playerConfig", "storyboards"}.each do |f| @@ -154,7 +166,7 @@ def try_fetch_streaming_data(id : String, client_config : YoutubeAPI::ClientConf playability_status = response["playabilityStatus"]["status"] LOGGER.debug("try_fetch_streaming_data: [#{id}] Got playabilityStatus == #{playability_status}.") - if id != response.dig("videoDetails", "videoId") + if id != response.dig?("videoDetails", "videoId") # YouTube may return a different video player response than expected. # See: https://github.com/TeamNewPipe/NewPipe/issues/8713 raise InfoException.new(