Implement workaround for broken shorts objects

This commit is contained in:
Samantaz Fox 2023-01-08 13:50:52 +01:00
parent f9eb839c7a
commit a37522a03d
No known key found for this signature in database
GPG key ID: F42821059186176E
3 changed files with 46 additions and 15 deletions

View file

@ -127,16 +127,38 @@ module Invidious::Channel::Tabs
# Shorts # Shorts
# ------------------- # -------------------
def get_shorts(channel : AboutChannel, continuation : String? = nil) private def fetch_shorts_data(ucid : String, continuation : String? = nil)
if continuation.nil? if continuation.nil?
# EgZzaG9ydHPyBgUKA5oBAA%3D%3D is the protobuf object to load "shorts" # EgZzaG9ydHPyBgUKA5oBAA%3D%3D is the protobuf object to load "shorts"
# TODO: try to extract the continuation tokens that allows other sorting options # TODO: try to extract the continuation tokens that allows other sorting options
initial_data = YoutubeAPI.browse(channel.ucid, params: "EgZzaG9ydHPyBgUKA5oBAA%3D%3D") return YoutubeAPI.browse(ucid, params: "EgZzaG9ydHPyBgUKA5oBAA%3D%3D")
else else
initial_data = YoutubeAPI.browse(continuation: continuation) return YoutubeAPI.browse(continuation: continuation)
end end
end
return extract_items(initial_data, channel.author, channel.ucid) def get_shorts(channel : AboutChannel, continuation : String? = nil)
initial_data = self.fetch_shorts_data(channel.ucid, continuation)
begin
# Try to parse the initial data fetched above
return extract_items(initial_data, channel.author, channel.ucid)
rescue ex : RetryOnceException
# Sometimes, for a completely unknown reason, the "reelItemRenderer"
# object is missing some critical information (it happens once in about
# 20 subsequent requests). Refreshing the page is required to properly
# show the "shorts" tab.
#
# In order to make the experience smoother for the user, we simulate
# said page refresh by fetching again the JSON. If that still doesn't
# work, we raise a BrokenTubeException, as something is really broken.
begin
initial_data = self.fetch_shorts_data(channel.ucid, continuation)
return extract_items(initial_data, channel.author, channel.ucid)
rescue ex : RetryOnceException
raise BrokenTubeException.new "reelPlayerHeaderSupportedRenderers"
end
end
end end
# ------------------- # -------------------

View file

@ -33,3 +33,8 @@ end
class VideoNotAvailableException < Exception class VideoNotAvailableException < Exception
end end
# Exception used to indicate that the JSON response from YT is missing
# some important informations, and that the query should be sent again.
class RetryOnceException < Exception
end

View file

@ -408,19 +408,23 @@ private module Parsers
private def self.parse(item_contents, author_fallback) private def self.parse(item_contents, author_fallback)
video_id = item_contents["videoId"].as_s video_id = item_contents["videoId"].as_s
begin reel_player_overlay = item_contents.dig(
video_details_container = item_contents.dig( "navigationEndpoint", "reelWatchEndpoint",
"navigationEndpoint", "reelWatchEndpoint", "overlay", "reelPlayerOverlayRenderer"
"overlay", "reelPlayerOverlayRenderer", )
"reelPlayerHeaderSupportedRenderers",
"reelPlayerHeaderRenderer" # Sometimes, the "reelPlayerOverlayRenderer" object is missing the
) # important part of the response. We use this exception to tell
rescue ex : KeyError # the calling function to fetch the content again.
# Extract key name from original message if !reel_player_overlay.as_h.has_key?("reelPlayerHeaderSupportedRenderers")
key = /"([^"]+)"/.match(ex.message || "").try &.[1]? raise RetryOnceException.new
raise BrokenTubeException.new(key || "reelPlayerOverlayRenderer")
end end
video_details_container = reel_player_overlay.dig(
"reelPlayerHeaderSupportedRenderers",
"reelPlayerHeaderRenderer"
)
# Author infos # Author infos
author = video_details_container author = video_details_container