This commit is contained in:
parent
8a3fd5751a
commit
945769bf0f
3 changed files with 33 additions and 27 deletions
|
@ -324,7 +324,7 @@ rescue DB::Error
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_video(id, region)
|
def fetch_video(id, region)
|
||||||
info = extract_video_info(video_id: id)
|
info = extract_video_info(video_id: id, level: 0)
|
||||||
|
|
||||||
if reason = info["reason"]?
|
if reason = info["reason"]?
|
||||||
if reason == "Video unavailable"
|
if reason == "Video unavailable"
|
||||||
|
|
|
@ -50,13 +50,22 @@ def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)?
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def extract_video_info(video_id : String)
|
def extract_video_info(video_id : String, *, level = 0, client_type = YoutubeAPI::ClientType::WebMobile)
|
||||||
|
# Infinite recursion prevention
|
||||||
|
level += 1
|
||||||
|
if level >= 3
|
||||||
|
return {
|
||||||
|
"version" => JSON::Any.new(Video::SCHEMA_VERSION.to_i64),
|
||||||
|
"reason" => JSON::Any.new("All counter-measures exhausted"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
# Init client config for the API
|
# Init client config for the API
|
||||||
client_config = YoutubeAPI::ClientConfig.new
|
client_config = YoutubeAPI::ClientConfig.new
|
||||||
|
client_config.client_type = client_type
|
||||||
|
|
||||||
# Fetch data from the player endpoint
|
# Fetch data from the player endpoint
|
||||||
player_response = YoutubeAPI.player(video_id: video_id, params: "2AMB", client_config: client_config)
|
player_response = YoutubeAPI.player(video_id: video_id, params: "2AMB", client_config: client_config)
|
||||||
|
|
||||||
playability_status = player_response.dig?("playabilityStatus", "status").try &.as_s
|
playability_status = player_response.dig?("playabilityStatus", "status").try &.as_s
|
||||||
|
|
||||||
if playability_status != "OK"
|
if playability_status != "OK"
|
||||||
|
@ -65,10 +74,12 @@ def extract_video_info(video_id : String)
|
||||||
reason ||= subreason.try &.[]("runs").as_a.map(&.[]("text")).join("")
|
reason ||= subreason.try &.[]("runs").as_a.map(&.[]("text")).join("")
|
||||||
reason ||= player_response.dig("playabilityStatus", "reason").as_s
|
reason ||= player_response.dig("playabilityStatus", "reason").as_s
|
||||||
|
|
||||||
|
if playability_status == "UNPLAYABLE" && reason.includes?("Get the YouTube app")
|
||||||
|
return extract_video_info(video_id: video_id, level: level, client_type: YoutubeAPI::ClientType::IOS)
|
||||||
|
elsif !{"LIVE_STREAM_OFFLINE", "LOGIN_REQUIRED"}.any?(playability_status) ||
|
||||||
|
playability_status == "LOGIN_REQUIRED" && !player_response.dig?("videoDetails")
|
||||||
# Stop here if video is not a scheduled livestream or
|
# Stop here if video is not a scheduled livestream or
|
||||||
# for LOGIN_REQUIRED when videoDetails element is not found because retrying won't help
|
# for LOGIN_REQUIRED when videoDetails element is not found because retrying won't help
|
||||||
if !{"LIVE_STREAM_OFFLINE", "LOGIN_REQUIRED"}.any?(playability_status) ||
|
|
||||||
playability_status == "LOGIN_REQUIRED" && !player_response.dig?("videoDetails")
|
|
||||||
return {
|
return {
|
||||||
"version" => JSON::Any.new(Video::SCHEMA_VERSION.to_i64),
|
"version" => JSON::Any.new(Video::SCHEMA_VERSION.to_i64),
|
||||||
"reason" => JSON::Any.new(reason),
|
"reason" => JSON::Any.new(reason),
|
||||||
|
@ -92,7 +103,7 @@ def extract_video_info(video_id : String)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Don't fetch the next endpoint if the video is unavailable.
|
# Don't fetch the next endpoint if the video is unavailable.
|
||||||
if {"OK", "LIVE_STREAM_OFFLINE", "LOGIN_REQUIRED"}.any?(playability_status)
|
if {"OK", "LIVE_STREAM_OFFLINE", "LOGIN_REQUIRED", "UNPLAYABLE"}.any?(playability_status)
|
||||||
next_response = YoutubeAPI.next({"videoId": video_id, "params": ""})
|
next_response = YoutubeAPI.next({"videoId": video_id, "params": ""})
|
||||||
player_response = player_response.merge(next_response)
|
player_response = player_response.merge(next_response)
|
||||||
end
|
end
|
||||||
|
@ -102,22 +113,12 @@ def extract_video_info(video_id : String)
|
||||||
|
|
||||||
new_player_response = nil
|
new_player_response = nil
|
||||||
|
|
||||||
# Don't use Android client if po_token is passed because po_token doesn't
|
if reason || DECRYPT_FUNCTION.nil? || CONFIG.po_token.nil?
|
||||||
# work for Android client.
|
client_config.client_type = YoutubeAPI::ClientType::IOS
|
||||||
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)
|
new_player_response = try_fetch_streaming_data(video_id, client_config)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Last hope
|
if reason && !DECRYPT_FUNCTION.nil? && CONFIG.po_token.nil?
|
||||||
# Only trigger if reason found and po_token or didn't work wth Android client.
|
|
||||||
# TvHtml5ScreenEmbed now requires sig helper for it to work but po_token is not required
|
|
||||||
# if the IP address is not blocked.
|
|
||||||
if CONFIG.po_token && reason || CONFIG.po_token.nil? && new_player_response.nil?
|
|
||||||
client_config.client_type = YoutubeAPI::ClientType::TvHtml5ScreenEmbed
|
client_config.client_type = YoutubeAPI::ClientType::TvHtml5ScreenEmbed
|
||||||
new_player_response = try_fetch_streaming_data(video_id, client_config)
|
new_player_response = try_fetch_streaming_data(video_id, client_config)
|
||||||
end
|
end
|
||||||
|
@ -470,12 +471,16 @@ private def convert_url(fmt)
|
||||||
params = url.query_params
|
params = url.query_params
|
||||||
end
|
end
|
||||||
|
|
||||||
n = DECRYPT_FUNCTION.try &.decrypt_nsig(params["n"])
|
if old_n = params["n"]?
|
||||||
|
n = DECRYPT_FUNCTION.try &.decrypt_nsig(old_n)
|
||||||
params["n"] = n if n
|
params["n"] = n if n
|
||||||
|
end
|
||||||
|
|
||||||
if token = CONFIG.po_token
|
if token = CONFIG.po_token
|
||||||
|
if {"WEB", "TVHTML5"}.any? { |x| params["c"].starts_with?(x) }
|
||||||
params["pot"] = token
|
params["pot"] = token
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
url.query_params = params
|
url.query_params = params
|
||||||
LOGGER.trace("convert_url: new url is '#{url}'")
|
LOGGER.trace("convert_url: new url is '#{url}'")
|
||||||
|
|
|
@ -4,9 +4,6 @@
|
||||||
<% if params.autoplay %>autoplay<% end %>
|
<% if params.autoplay %>autoplay<% end %>
|
||||||
<% if params.video_loop %>loop<% end %>
|
<% if params.video_loop %>loop<% end %>
|
||||||
<% if params.controls %>controls<% end %>>
|
<% if params.controls %>controls<% end %>>
|
||||||
<% if (hlsvp = video.hls_manifest_url) && !CONFIG.disabled?("livestreams") %>
|
|
||||||
<source src="<%= URI.parse(hlsvp).request_target %><% if params.local %>?local=true<% end %>" type="application/x-mpegURL" label="livestream">
|
|
||||||
<% else %>
|
|
||||||
<% if params.listen %>
|
<% if params.listen %>
|
||||||
<% # default to 128k m4a stream
|
<% # default to 128k m4a stream
|
||||||
best_m4a_stream_index = 0
|
best_m4a_stream_index = 0
|
||||||
|
@ -57,6 +54,11 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<% if (hlsvp = video.hls_manifest_url) && !CONFIG.disabled?("livestreams") %>
|
||||||
|
<source src="<%= URI.parse(hlsvp).request_target %><% if params.local %>?local=true<% end %>" type="application/x-mpegURL" label="livestream">
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
<% preferred_captions.each do |caption| %>
|
<% preferred_captions.each do |caption| %>
|
||||||
<track kind="captions" src="/api/v1/captions/<%= video.id %>?label=<%= caption.name %>" label="<%= caption.name %>">
|
<track kind="captions" src="/api/v1/captions/<%= video.id %>?label=<%= caption.name %>" label="<%= caption.name %>">
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -64,7 +66,6 @@
|
||||||
<% captions.each do |caption| %>
|
<% captions.each do |caption| %>
|
||||||
<track kind="captions" src="/api/v1/captions/<%= video.id %>?label=<%= caption.name %>" label="<%= caption.name %>">
|
<track kind="captions" src="/api/v1/captions/<%= video.id %>?label=<%= caption.name %>" label="<%= caption.name %>">
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
|
||||||
</video>
|
</video>
|
||||||
|
|
||||||
<script id="player_data" type="application/json">
|
<script id="player_data" type="application/json">
|
||||||
|
|
Loading…
Reference in a new issue