diff --git a/src/invidious.cr b/src/invidious.cr index 56195632b..89292f05f 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -1962,9 +1962,9 @@ get "/api/v1/captions/:id" do |env| json.array do captions.each do |caption| json.object do - json.field "label", caption.name.simpleText + json.field "label", caption.name json.field "languageCode", caption.languageCode - json.field "url", "/api/v1/captions/#{id}?label=#{URI.encode_www_form(caption.name.simpleText)}" + json.field "url", "/api/v1/captions/#{id}?label=#{URI.encode_www_form(caption.name)}" end end end @@ -1980,7 +1980,7 @@ get "/api/v1/captions/:id" do |env| if lang caption = captions.select { |caption| caption.languageCode == lang } else - caption = captions.select { |caption| caption.name.simpleText == label } + caption = captions.select { |caption| caption.name == label } end if caption.empty? @@ -1994,7 +1994,7 @@ get "/api/v1/captions/:id" do |env| # Auto-generated captions often have cues that aren't aligned properly with the video, # as well as some other markup that makes it cumbersome, so we try to fix that here - if caption.name.simpleText.includes? "auto-generated" + if caption.name.includes? "auto-generated" caption_xml = YT_POOL.client &.get(url).body caption_xml = XML.parse(caption_xml) diff --git a/src/invidious/routes/embed.cr b/src/invidious/routes/embed.cr index 5db327885..5e1e94316 100644 --- a/src/invidious/routes/embed.cr +++ b/src/invidious/routes/embed.cr @@ -165,11 +165,11 @@ class Invidious::Routes::Embed < Invidious::Routes::BaseRoute captions = video.captions preferred_captions = captions.select { |caption| - params.preferred_captions.includes?(caption.name.simpleText) || + params.preferred_captions.includes?(caption.name) || params.preferred_captions.includes?(caption.languageCode.split("-")[0]) } preferred_captions.sort_by! { |caption| - (params.preferred_captions.index(caption.name.simpleText) || + (params.preferred_captions.index(caption.name) || params.preferred_captions.index(caption.languageCode.split("-")[0])).not_nil! } captions = captions - preferred_captions diff --git a/src/invidious/routes/watch.cr b/src/invidious/routes/watch.cr index d03388826..c6c7c154e 100644 --- a/src/invidious/routes/watch.cr +++ b/src/invidious/routes/watch.cr @@ -150,11 +150,11 @@ class Invidious::Routes::Watch < Invidious::Routes::BaseRoute captions = video.captions preferred_captions = captions.select { |caption| - params.preferred_captions.includes?(caption.name.simpleText) || + params.preferred_captions.includes?(caption.name) || params.preferred_captions.includes?(caption.languageCode.split("-")[0]) } preferred_captions.sort_by! { |caption| - (params.preferred_captions.index(caption.name.simpleText) || + (params.preferred_captions.index(caption.name) || params.preferred_captions.index(caption.languageCode.split("-")[0])).not_nil! } captions = captions - preferred_captions diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index 116aafc77..27c54b144 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -425,9 +425,9 @@ struct Video json.array do self.captions.each do |caption| json.object do - json.field "label", caption.name.simpleText + json.field "label", caption.name json.field "languageCode", caption.languageCode - json.field "url", "/api/v1/captions/#{id}?label=#{URI.encode_www_form(caption.name.simpleText)}" + json.field "url", "/api/v1/captions/#{id}?label=#{URI.encode_www_form(caption.name)}" end end end @@ -706,8 +706,12 @@ struct Video def captions : Array(Caption) return @captions.as(Array(Caption)) if @captions captions = info["captions"]?.try &.["playerCaptionsTracklistRenderer"]?.try &.["captionTracks"]?.try &.as_a.map do |caption| - caption = Caption.from_json(caption.to_json) - caption.name.simpleText = caption.name.simpleText.split(" - ")[0] + name = caption["name"]["simpleText"]? || caption["name"]["runs"][0]["text"] + languageCode = caption["languageCode"].to_s + baseUrl = caption["baseUrl"].to_s + + caption = Caption.new(name.to_s, languageCode, baseUrl) + caption.name = caption.name.split(" - ")[0] caption end captions ||= [] of Caption @@ -782,18 +786,19 @@ struct Video end end -struct CaptionName - include JSON::Serializable - - property simpleText : String -end - struct Caption - include JSON::Serializable + property name + property languageCode + property baseUrl - property name : CaptionName - property baseUrl : String - property languageCode : String + getter name : String + getter languageCode : String + getter baseUrl : String + + setter name + + def initialize(@name, @languageCode, @baseUrl) + end end class VideoRedirect < Exception @@ -989,9 +994,33 @@ def fetch_video(id, region) # Try to pull streams from embed URL if info["reason"]? - embed_page = YT_POOL.client &.get("/embed/#{id}").body - sts = embed_page.match(/"sts"\s*:\s*(?\d+)/).try &.["sts"]? || "" - embed_info = HTTP::Params.parse(YT_POOL.client &.get("/get_video_info?html5=1&video_id=#{id}&eurl=https://youtube.googleapis.com/v/#{id}&gl=US&hl=en&sts=#{sts}").body) + required_parameters = { + "video_id" => id, + "eurl" => "https://youtube.googleapis.com/v/#{id}", + "html5" => "1", + "gl" => "US", + "hl" => "en", + } + if info["reason"].as_s.includes?("inappropriate") + # The html5, c and cver parameters are required in order to extract age-restricted videos + # See https://github.com/yt-dlp/yt-dlp/commit/4e6767b5f2e2523ebd3dd1240584ead53e8c8905 + required_parameters.merge!({ + "c" => "TVHTML5", + "cver" => "6.20180913", + }) + + # In order to actually extract video info without error, the `x-youtube-client-version` + # has to be set to the same version as `cver` above. + additional_headers = HTTP::Headers{"x-youtube-client-version" => "6.20180913"} + else + embed_page = YT_POOL.client &.get("/embed/#{id}").body + sts = embed_page.match(/"sts"\s*:\s*(?\d+)/).try &.["sts"]? || "" + required_parameters["sts"] = sts + additional_headers = HTTP::Headers{} of String => String + end + + embed_info = HTTP::Params.parse(YT_POOL.client &.get("/get_video_info?#{URI::Params.encode(required_parameters)}", + headers: additional_headers).body) if embed_info["player_response"]? player_response = JSON.parse(embed_info["player_response"]) diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index cff3e60a5..c37d20d54 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -25,13 +25,13 @@ <% end %> <% preferred_captions.each do |caption| %> - " - label="<%= caption.name.simpleText %>"> + " + label="<%= caption.name %>"> <% end %> <% captions.each do |caption| %> - " - label="<%= caption.name.simpleText %>"> + " + label="<%= caption.name %>"> <% end %> <% end %> diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 7ee3fb1a9..c553dc0e6 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -178,8 +178,8 @@ we're going to need to do it here in order to allow for translations. <% end %> <% captions.each do |caption| %> - <% end %>