mirror of
				https://github.com/iv-org/invidious.git
				synced 2025-11-03 22:21:55 +00:00 
			
		
		
		
	Make HOST_URL constant
This commit is contained in:
		
							
								
								
									
										123
									
								
								src/invidious.cr
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								src/invidious.cr
									
									
									
									
									
								
							@@ -50,6 +50,7 @@ PUBSUB_URL      = URI.parse("https://pubsubhubbub.appspot.com")
 | 
				
			|||||||
REDDIT_URL      = URI.parse("https://www.reddit.com")
 | 
					REDDIT_URL      = URI.parse("https://www.reddit.com")
 | 
				
			||||||
TEXTCAPTCHA_URL = URI.parse("https://textcaptcha.com")
 | 
					TEXTCAPTCHA_URL = URI.parse("https://textcaptcha.com")
 | 
				
			||||||
YT_URL          = URI.parse("https://www.youtube.com")
 | 
					YT_URL          = URI.parse("https://www.youtube.com")
 | 
				
			||||||
 | 
					HOST_URL        = make_host_url(CONFIG, Kemal.config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CHARS_SAFE         = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
 | 
					CHARS_SAFE         = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
 | 
				
			||||||
TEST_IDS           = {"AgbeGFYluEA", "BaW_jenozKc", "a9LDPn-MO4I", "ddFvjfvPnqk", "iqKdEhx-dD4"}
 | 
					TEST_IDS           = {"AgbeGFYluEA", "BaW_jenozKc", "a9LDPn-MO4I", "ddFvjfvPnqk", "iqKdEhx-dD4"}
 | 
				
			||||||
@@ -202,10 +203,11 @@ spawn do
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
decrypt_function = [] of {SigProc, Int32}
 | 
					DECRYPT_FUNCTION = [] of {SigProc, Int32}
 | 
				
			||||||
spawn do
 | 
					spawn do
 | 
				
			||||||
  update_decrypt_function do |function|
 | 
					  update_decrypt_function do |function|
 | 
				
			||||||
    decrypt_function = function
 | 
					    DECRYPT_FUNCTION.clear
 | 
				
			||||||
 | 
					    function.each { |i| DECRYPT_FUNCTION << i }
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1351,16 +1353,14 @@ get "/opensearch.xml" do |env|
 | 
				
			|||||||
  locale = LOCALES[env.get("preferences").as(Preferences).locale]?
 | 
					  locale = LOCALES[env.get("preferences").as(Preferences).locale]?
 | 
				
			||||||
  env.response.content_type = "application/opensearchdescription+xml"
 | 
					  env.response.content_type = "application/opensearchdescription+xml"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  host = make_host_url(config, Kemal.config)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  XML.build(indent: "  ", encoding: "UTF-8") do |xml|
 | 
					  XML.build(indent: "  ", encoding: "UTF-8") do |xml|
 | 
				
			||||||
    xml.element("OpenSearchDescription", xmlns: "http://a9.com/-/spec/opensearch/1.1/") do
 | 
					    xml.element("OpenSearchDescription", xmlns: "http://a9.com/-/spec/opensearch/1.1/") do
 | 
				
			||||||
      xml.element("ShortName") { xml.text "Invidious" }
 | 
					      xml.element("ShortName") { xml.text "Invidious" }
 | 
				
			||||||
      xml.element("LongName") { xml.text "Invidious Search" }
 | 
					      xml.element("LongName") { xml.text "Invidious Search" }
 | 
				
			||||||
      xml.element("Description") { xml.text "Search for videos, channels, and playlists on Invidious" }
 | 
					      xml.element("Description") { xml.text "Search for videos, channels, and playlists on Invidious" }
 | 
				
			||||||
      xml.element("InputEncoding") { xml.text "UTF-8" }
 | 
					      xml.element("InputEncoding") { xml.text "UTF-8" }
 | 
				
			||||||
      xml.element("Image", width: 48, height: 48, type: "image/x-icon") { xml.text "#{host}/favicon.ico" }
 | 
					      xml.element("Image", width: 48, height: 48, type: "image/x-icon") { xml.text "#{HOST_URL}/favicon.ico" }
 | 
				
			||||||
      xml.element("Url", type: "text/html", method: "get", template: "#{host}/search?q={searchTerms}")
 | 
					      xml.element("Url", type: "text/html", method: "get", template: "#{HOST_URL}/search?q={searchTerms}")
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
@@ -2473,8 +2473,6 @@ get "/subscription_manager" do |env|
 | 
				
			|||||||
  subscriptions.sort_by! { |channel| channel.author.downcase }
 | 
					  subscriptions.sort_by! { |channel| channel.author.downcase }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if action_takeout
 | 
					  if action_takeout
 | 
				
			||||||
    host_url = make_host_url(config, Kemal.config)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if format == "json"
 | 
					    if format == "json"
 | 
				
			||||||
      env.response.content_type = "application/json"
 | 
					      env.response.content_type = "application/json"
 | 
				
			||||||
      env.response.headers["content-disposition"] = "attachment"
 | 
					      env.response.headers["content-disposition"] = "attachment"
 | 
				
			||||||
@@ -2500,7 +2498,7 @@ get "/subscription_manager" do |env|
 | 
				
			|||||||
                if format == "newpipe"
 | 
					                if format == "newpipe"
 | 
				
			||||||
                  xmlUrl = "https://www.youtube.com/feeds/videos.xml?channel_id=#{channel.id}"
 | 
					                  xmlUrl = "https://www.youtube.com/feeds/videos.xml?channel_id=#{channel.id}"
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                  xmlUrl = "#{host_url}/feed/channel/#{channel.id}"
 | 
					                  xmlUrl = "#{HOST_URL}/feed/channel/#{channel.id}"
 | 
				
			||||||
                end
 | 
					                end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                xml.element("outline", text: channel.author, title: channel.author,
 | 
					                xml.element("outline", text: channel.author, title: channel.author,
 | 
				
			||||||
@@ -3179,25 +3177,23 @@ get "/feed/channel/:ucid" do |env|
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  host_url = make_host_url(config, Kemal.config)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  XML.build(indent: "  ", encoding: "UTF-8") do |xml|
 | 
					  XML.build(indent: "  ", encoding: "UTF-8") do |xml|
 | 
				
			||||||
    xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
 | 
					    xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
 | 
				
			||||||
      "xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
 | 
					      "xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
 | 
				
			||||||
      "xml:lang": "en-US") do
 | 
					      "xml:lang": "en-US") do
 | 
				
			||||||
      xml.element("link", rel: "self", href: "#{host_url}#{env.request.resource}")
 | 
					      xml.element("link", rel: "self", href: "#{HOST_URL}#{env.request.resource}")
 | 
				
			||||||
      xml.element("id") { xml.text "yt:channel:#{channel.ucid}" }
 | 
					      xml.element("id") { xml.text "yt:channel:#{channel.ucid}" }
 | 
				
			||||||
      xml.element("yt:channelId") { xml.text channel.ucid }
 | 
					      xml.element("yt:channelId") { xml.text channel.ucid }
 | 
				
			||||||
      xml.element("title") { xml.text channel.author }
 | 
					      xml.element("title") { xml.text channel.author }
 | 
				
			||||||
      xml.element("link", rel: "alternate", href: "#{host_url}/channel/#{channel.ucid}")
 | 
					      xml.element("link", rel: "alternate", href: "#{HOST_URL}/channel/#{channel.ucid}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      xml.element("author") do
 | 
					      xml.element("author") do
 | 
				
			||||||
        xml.element("name") { xml.text channel.author }
 | 
					        xml.element("name") { xml.text channel.author }
 | 
				
			||||||
        xml.element("uri") { xml.text "#{host_url}/channel/#{channel.ucid}" }
 | 
					        xml.element("uri") { xml.text "#{HOST_URL}/channel/#{channel.ucid}" }
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      videos.each do |video|
 | 
					      videos.each do |video|
 | 
				
			||||||
        video.to_xml(host_url, channel.auto_generated, params, xml)
 | 
					        video.to_xml(channel.auto_generated, params, xml)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -3231,19 +3227,18 @@ get "/feed/private" do |env|
 | 
				
			|||||||
  params = HTTP::Params.parse(env.params.query["params"]? || "")
 | 
					  params = HTTP::Params.parse(env.params.query["params"]? || "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  videos, notifications = get_subscription_feed(PG_DB, user, max_results, page)
 | 
					  videos, notifications = get_subscription_feed(PG_DB, user, max_results, page)
 | 
				
			||||||
  host_url = make_host_url(config, Kemal.config)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  XML.build(indent: "  ", encoding: "UTF-8") do |xml|
 | 
					  XML.build(indent: "  ", encoding: "UTF-8") do |xml|
 | 
				
			||||||
    xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
 | 
					    xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
 | 
				
			||||||
      "xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
 | 
					      "xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
 | 
				
			||||||
      "xml:lang": "en-US") do
 | 
					      "xml:lang": "en-US") do
 | 
				
			||||||
      xml.element("link", "type": "text/html", rel: "alternate", href: "#{host_url}/feed/subscriptions")
 | 
					      xml.element("link", "type": "text/html", rel: "alternate", href: "#{HOST_URL}/feed/subscriptions")
 | 
				
			||||||
      xml.element("link", "type": "application/atom+xml", rel: "self",
 | 
					      xml.element("link", "type": "application/atom+xml", rel: "self",
 | 
				
			||||||
        href: "#{host_url}#{env.request.resource}")
 | 
					        href: "#{HOST_URL}#{env.request.resource}")
 | 
				
			||||||
      xml.element("title") { xml.text translate(locale, "Invidious Private Feed for `x`", user.email) }
 | 
					      xml.element("title") { xml.text translate(locale, "Invidious Private Feed for `x`", user.email) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      (notifications + videos).each do |video|
 | 
					      (notifications + videos).each do |video|
 | 
				
			||||||
        video.to_xml(locale, host_url, params, xml)
 | 
					        video.to_xml(locale, params, xml)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -3257,8 +3252,6 @@ get "/feed/playlist/:plid" do |env|
 | 
				
			|||||||
  plid = env.params.url["plid"]
 | 
					  plid = env.params.url["plid"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  params = HTTP::Params.parse(env.params.query["params"]? || "")
 | 
					  params = HTTP::Params.parse(env.params.query["params"]? || "")
 | 
				
			||||||
 | 
					 | 
				
			||||||
  host_url = make_host_url(config, Kemal.config)
 | 
					 | 
				
			||||||
  path = env.request.path
 | 
					  path = env.request.path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if plid.starts_with? "IV"
 | 
					  if plid.starts_with? "IV"
 | 
				
			||||||
@@ -3269,18 +3262,18 @@ get "/feed/playlist/:plid" do |env|
 | 
				
			|||||||
        xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
 | 
					        xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
 | 
				
			||||||
          "xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
 | 
					          "xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
 | 
				
			||||||
          "xml:lang": "en-US") do
 | 
					          "xml:lang": "en-US") do
 | 
				
			||||||
          xml.element("link", rel: "self", href: "#{host_url}#{env.request.resource}")
 | 
					          xml.element("link", rel: "self", href: "#{HOST_URL}#{env.request.resource}")
 | 
				
			||||||
          xml.element("id") { xml.text "iv:playlist:#{plid}" }
 | 
					          xml.element("id") { xml.text "iv:playlist:#{plid}" }
 | 
				
			||||||
          xml.element("iv:playlistId") { xml.text plid }
 | 
					          xml.element("iv:playlistId") { xml.text plid }
 | 
				
			||||||
          xml.element("title") { xml.text playlist.title }
 | 
					          xml.element("title") { xml.text playlist.title }
 | 
				
			||||||
          xml.element("link", rel: "alternate", href: "#{host_url}/playlist?list=#{plid}")
 | 
					          xml.element("link", rel: "alternate", href: "#{HOST_URL}/playlist?list=#{plid}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          xml.element("author") do
 | 
					          xml.element("author") do
 | 
				
			||||||
            xml.element("name") { xml.text playlist.author }
 | 
					            xml.element("name") { xml.text playlist.author }
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          videos.each do |video|
 | 
					          videos.each do |video|
 | 
				
			||||||
            video.to_xml(host_url, false, xml)
 | 
					            video.to_xml(false, xml)
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
@@ -3299,7 +3292,7 @@ get "/feed/playlist/:plid" do |env|
 | 
				
			|||||||
      when "url", "href"
 | 
					      when "url", "href"
 | 
				
			||||||
        full_path = URI.parse(node[attribute.name]).full_path
 | 
					        full_path = URI.parse(node[attribute.name]).full_path
 | 
				
			||||||
        query_string_opt = full_path.starts_with?("/watch?v=") ? "&#{params}" : ""
 | 
					        query_string_opt = full_path.starts_with?("/watch?v=") ? "&#{params}" : ""
 | 
				
			||||||
        node[attribute.name] = "#{host_url}#{full_path}#{query_string_opt}"
 | 
					        node[attribute.name] = "#{HOST_URL}#{full_path}#{query_string_opt}"
 | 
				
			||||||
      else nil # Skip
 | 
					      else nil # Skip
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
@@ -3308,7 +3301,7 @@ get "/feed/playlist/:plid" do |env|
 | 
				
			|||||||
  document = document.to_xml(options: XML::SaveOptions::NO_DECL)
 | 
					  document = document.to_xml(options: XML::SaveOptions::NO_DECL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  document.scan(/<uri>(?<url>[^<]+)<\/uri>/).each do |match|
 | 
					  document.scan(/<uri>(?<url>[^<]+)<\/uri>/).each do |match|
 | 
				
			||||||
    content = "#{host_url}#{URI.parse(match["url"]).full_path}"
 | 
					    content = "#{HOST_URL}#{URI.parse(match["url"]).full_path}"
 | 
				
			||||||
    document = document.gsub(match[0], "<uri>#{content}</uri>")
 | 
					    document = document.gsub(match[0], "<uri>#{content}</uri>")
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3684,7 +3677,7 @@ get "/channel/:ucid/community" do |env|
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  begin
 | 
					  begin
 | 
				
			||||||
    items = JSON.parse(fetch_channel_community(ucid, continuation, locale, config, Kemal.config, "json", thin_mode))
 | 
					    items = JSON.parse(fetch_channel_community(ucid, continuation, locale, "json", thin_mode))
 | 
				
			||||||
  rescue ex
 | 
					  rescue ex
 | 
				
			||||||
    env.response.status_code = 500
 | 
					    env.response.status_code = 500
 | 
				
			||||||
    error_message = ex.message
 | 
					    error_message = ex.message
 | 
				
			||||||
@@ -3737,7 +3730,6 @@ get "/api/v1/storyboards/:id" do |env|
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  storyboards = video.storyboards
 | 
					  storyboards = video.storyboards
 | 
				
			||||||
 | 
					 | 
				
			||||||
  width = env.params.query["width"]?
 | 
					  width = env.params.query["width"]?
 | 
				
			||||||
  height = env.params.query["height"]?
 | 
					  height = env.params.query["height"]?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3745,7 +3737,7 @@ get "/api/v1/storyboards/:id" do |env|
 | 
				
			|||||||
    response = JSON.build do |json|
 | 
					    response = JSON.build do |json|
 | 
				
			||||||
      json.object do
 | 
					      json.object do
 | 
				
			||||||
        json.field "storyboards" do
 | 
					        json.field "storyboards" do
 | 
				
			||||||
          generate_storyboards(json, id, storyboards, config, Kemal.config)
 | 
					          generate_storyboards(json, id, storyboards)
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
@@ -3775,8 +3767,7 @@ get "/api/v1/storyboards/:id" do |env|
 | 
				
			|||||||
    end_time = storyboard[:interval].milliseconds
 | 
					    end_time = storyboard[:interval].milliseconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    storyboard[:storyboard_count].times do |i|
 | 
					    storyboard[:storyboard_count].times do |i|
 | 
				
			||||||
      host_url = make_host_url(config, Kemal.config)
 | 
					      url = storyboard[:url].gsub("$M", i).gsub("https://i9.ytimg.com", HOST_URL)
 | 
				
			||||||
      url = storyboard[:url].gsub("$M", i).gsub("https://i9.ytimg.com", host_url)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      storyboard[:storyboard_height].times do |j|
 | 
					      storyboard[:storyboard_height].times do |j|
 | 
				
			||||||
        storyboard[:storyboard_width].times do |k|
 | 
					        storyboard[:storyboard_width].times do |k|
 | 
				
			||||||
@@ -4099,7 +4090,7 @@ get "/api/v1/videos/:id" do |env|
 | 
				
			|||||||
    next error_message
 | 
					    next error_message
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  video.to_json(locale, config, Kemal.config, decrypt_function)
 | 
					  video.to_json(locale)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
get "/api/v1/trending" do |env|
 | 
					get "/api/v1/trending" do |env|
 | 
				
			||||||
@@ -4121,7 +4112,7 @@ get "/api/v1/trending" do |env|
 | 
				
			|||||||
  videos = JSON.build do |json|
 | 
					  videos = JSON.build do |json|
 | 
				
			||||||
    json.array do
 | 
					    json.array do
 | 
				
			||||||
      trending.each do |video|
 | 
					      trending.each do |video|
 | 
				
			||||||
        video.to_json(locale, config, Kemal.config, json)
 | 
					        video.to_json(locale, json)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -4137,7 +4128,7 @@ get "/api/v1/popular" do |env|
 | 
				
			|||||||
  JSON.build do |json|
 | 
					  JSON.build do |json|
 | 
				
			||||||
    json.array do
 | 
					    json.array do
 | 
				
			||||||
      popular_videos.each do |video|
 | 
					      popular_videos.each do |video|
 | 
				
			||||||
        video.to_json(locale, config, Kemal.config, json)
 | 
					        video.to_json(locale, json)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -4178,7 +4169,7 @@ get "/api/v1/channels/:ucid" do |env|
 | 
				
			|||||||
    count = 0
 | 
					    count = 0
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    begin
 | 
					    begin
 | 
				
			||||||
      videos, count = get_60_videos(channel.ucid, channel.author, page, channel.auto_generated, sort_by)
 | 
					      count, videos = get_60_videos(channel.ucid, channel.author, page, channel.auto_generated, sort_by)
 | 
				
			||||||
    rescue ex
 | 
					    rescue ex
 | 
				
			||||||
      error_message = {"error" => ex.message}.to_json
 | 
					      error_message = {"error" => ex.message}.to_json
 | 
				
			||||||
      env.response.status_code = 500
 | 
					      env.response.status_code = 500
 | 
				
			||||||
@@ -4247,7 +4238,7 @@ get "/api/v1/channels/:ucid" do |env|
 | 
				
			|||||||
      json.field "latestVideos" do
 | 
					      json.field "latestVideos" do
 | 
				
			||||||
        json.array do
 | 
					        json.array do
 | 
				
			||||||
          videos.each do |video|
 | 
					          videos.each do |video|
 | 
				
			||||||
            video.to_json(locale, config, Kemal.config, json)
 | 
					            video.to_json(locale, json)
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
@@ -4308,7 +4299,7 @@ end
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    begin
 | 
					    begin
 | 
				
			||||||
      videos, count = get_60_videos(channel.ucid, channel.author, page, channel.auto_generated, sort_by)
 | 
					      count, videos = get_60_videos(channel.ucid, channel.author, page, channel.auto_generated, sort_by)
 | 
				
			||||||
    rescue ex
 | 
					    rescue ex
 | 
				
			||||||
      error_message = {"error" => ex.message}.to_json
 | 
					      error_message = {"error" => ex.message}.to_json
 | 
				
			||||||
      env.response.status_code = 500
 | 
					      env.response.status_code = 500
 | 
				
			||||||
@@ -4318,7 +4309,7 @@ end
 | 
				
			|||||||
    JSON.build do |json|
 | 
					    JSON.build do |json|
 | 
				
			||||||
      json.array do
 | 
					      json.array do
 | 
				
			||||||
        videos.each do |video|
 | 
					        videos.each do |video|
 | 
				
			||||||
          video.to_json(locale, config, Kemal.config, json)
 | 
					          video.to_json(locale, json)
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
@@ -4344,7 +4335,7 @@ end
 | 
				
			|||||||
    JSON.build do |json|
 | 
					    JSON.build do |json|
 | 
				
			||||||
      json.array do
 | 
					      json.array do
 | 
				
			||||||
        videos.each do |video|
 | 
					        videos.each do |video|
 | 
				
			||||||
          video.to_json(locale, config, Kemal.config, json)
 | 
					          video.to_json(locale, json)
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
@@ -4359,9 +4350,9 @@ end
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    ucid = env.params.url["ucid"]
 | 
					    ucid = env.params.url["ucid"]
 | 
				
			||||||
    continuation = env.params.query["continuation"]?
 | 
					    continuation = env.params.query["continuation"]?
 | 
				
			||||||
    sort_by = env.params.query["sort"]?.try &.downcase
 | 
					    sort_by = env.params.query["sort"]?.try &.downcase ||
 | 
				
			||||||
    sort_by ||= env.params.query["sort_by"]?.try &.downcase
 | 
					              env.params.query["sort_by"]?.try &.downcase ||
 | 
				
			||||||
    sort_by ||= "last"
 | 
					              "last"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    begin
 | 
					    begin
 | 
				
			||||||
      channel = get_about_info(ucid, locale)
 | 
					      channel = get_about_info(ucid, locale)
 | 
				
			||||||
@@ -4383,9 +4374,7 @@ end
 | 
				
			|||||||
        json.field "playlists" do
 | 
					        json.field "playlists" do
 | 
				
			||||||
          json.array do
 | 
					          json.array do
 | 
				
			||||||
            items.each do |item|
 | 
					            items.each do |item|
 | 
				
			||||||
              if item.is_a?(SearchPlaylist)
 | 
					              item.to_json(locale, json) if item.is_a?(SearchPlaylist)
 | 
				
			||||||
                item.to_json(locale, config, Kemal.config, json)
 | 
					 | 
				
			||||||
              end
 | 
					 | 
				
			||||||
            end
 | 
					            end
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
@@ -4414,7 +4403,7 @@ end
 | 
				
			|||||||
    # sort_by = env.params.query["sort_by"]?.try &.downcase
 | 
					    # sort_by = env.params.query["sort_by"]?.try &.downcase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    begin
 | 
					    begin
 | 
				
			||||||
      fetch_channel_community(ucid, continuation, locale, config, Kemal.config, format, thin_mode)
 | 
					      fetch_channel_community(ucid, continuation, locale, format, thin_mode)
 | 
				
			||||||
    rescue ex
 | 
					    rescue ex
 | 
				
			||||||
      env.response.status_code = 400
 | 
					      env.response.status_code = 400
 | 
				
			||||||
      error_message = {"error" => ex.message}.to_json
 | 
					      error_message = {"error" => ex.message}.to_json
 | 
				
			||||||
@@ -4440,7 +4429,7 @@ get "/api/v1/channels/search/:ucid" do |env|
 | 
				
			|||||||
  JSON.build do |json|
 | 
					  JSON.build do |json|
 | 
				
			||||||
    json.array do
 | 
					    json.array do
 | 
				
			||||||
      search_results.each do |item|
 | 
					      search_results.each do |item|
 | 
				
			||||||
        item.to_json(locale, config, Kemal.config, json)
 | 
					        item.to_json(locale, json)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -4485,7 +4474,7 @@ get "/api/v1/search" do |env|
 | 
				
			|||||||
  JSON.build do |json|
 | 
					  JSON.build do |json|
 | 
				
			||||||
    json.array do
 | 
					    json.array do
 | 
				
			||||||
      search_results.each do |item|
 | 
					      search_results.each do |item|
 | 
				
			||||||
        item.to_json(locale, config, Kemal.config, json)
 | 
					        item.to_json(locale, json)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -4562,7 +4551,7 @@ end
 | 
				
			|||||||
      next error_message
 | 
					      next error_message
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    response = playlist.to_json(offset, locale, config, Kemal.config, continuation: continuation)
 | 
					    response = playlist.to_json(offset, locale, continuation: continuation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if format == "html"
 | 
					    if format == "html"
 | 
				
			||||||
      response = JSON.parse(response)
 | 
					      response = JSON.parse(response)
 | 
				
			||||||
@@ -4626,7 +4615,7 @@ get "/api/v1/mixes/:rdid" do |env|
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
              json.field "videoThumbnails" do
 | 
					              json.field "videoThumbnails" do
 | 
				
			||||||
                json.array do
 | 
					                json.array do
 | 
				
			||||||
                  generate_thumbnails(json, video.id, config, Kemal.config)
 | 
					                  generate_thumbnails(json, video.id)
 | 
				
			||||||
                end
 | 
					                end
 | 
				
			||||||
              end
 | 
					              end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -4661,7 +4650,7 @@ get "/api/v1/auth/notifications" do |env|
 | 
				
			|||||||
  topics = env.params.query["topics"]?.try &.split(",").uniq.first(1000)
 | 
					  topics = env.params.query["topics"]?.try &.split(",").uniq.first(1000)
 | 
				
			||||||
  topics ||= [] of String
 | 
					  topics ||= [] of String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  create_notification_stream(env, config, Kemal.config, decrypt_function, topics, connection_channel)
 | 
					  create_notification_stream(env, topics, connection_channel)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
post "/api/v1/auth/notifications" do |env|
 | 
					post "/api/v1/auth/notifications" do |env|
 | 
				
			||||||
@@ -4670,7 +4659,7 @@ post "/api/v1/auth/notifications" do |env|
 | 
				
			|||||||
  topics = env.params.body["topics"]?.try &.split(",").uniq.first(1000)
 | 
					  topics = env.params.body["topics"]?.try &.split(",").uniq.first(1000)
 | 
				
			||||||
  topics ||= [] of String
 | 
					  topics ||= [] of String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  create_notification_stream(env, config, Kemal.config, decrypt_function, topics, connection_channel)
 | 
					  create_notification_stream(env, topics, connection_channel)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
get "/api/v1/auth/preferences" do |env|
 | 
					get "/api/v1/auth/preferences" do |env|
 | 
				
			||||||
@@ -4714,7 +4703,7 @@ get "/api/v1/auth/feed" do |env|
 | 
				
			|||||||
      json.field "notifications" do
 | 
					      json.field "notifications" do
 | 
				
			||||||
        json.array do
 | 
					        json.array do
 | 
				
			||||||
          notifications.each do |video|
 | 
					          notifications.each do |video|
 | 
				
			||||||
            video.to_json(locale, config, Kemal.config, json)
 | 
					            video.to_json(locale, json)
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
@@ -4722,7 +4711,7 @@ get "/api/v1/auth/feed" do |env|
 | 
				
			|||||||
      json.field "videos" do
 | 
					      json.field "videos" do
 | 
				
			||||||
        json.array do
 | 
					        json.array do
 | 
				
			||||||
          videos.each do |video|
 | 
					          videos.each do |video|
 | 
				
			||||||
            video.to_json(locale, config, Kemal.config, json)
 | 
					            video.to_json(locale, json)
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
@@ -4794,7 +4783,7 @@ get "/api/v1/auth/playlists" do |env|
 | 
				
			|||||||
  JSON.build do |json|
 | 
					  JSON.build do |json|
 | 
				
			||||||
    json.array do
 | 
					    json.array do
 | 
				
			||||||
      playlists.each do |playlist|
 | 
					      playlists.each do |playlist|
 | 
				
			||||||
        playlist.to_json(0, locale, config, Kemal.config, json)
 | 
					        playlist.to_json(0, locale, json)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -4825,10 +4814,8 @@ post "/api/v1/auth/playlists" do |env|
 | 
				
			|||||||
    next error_message
 | 
					    next error_message
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  host_url = make_host_url(config, Kemal.config)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  playlist = create_playlist(PG_DB, title, privacy, user)
 | 
					  playlist = create_playlist(PG_DB, title, privacy, user)
 | 
				
			||||||
  env.response.headers["Location"] = "#{host_url}/api/v1/auth/playlists/#{playlist.id}"
 | 
					  env.response.headers["Location"] = "#{HOST_URL}/api/v1/auth/playlists/#{playlist.id}"
 | 
				
			||||||
  env.response.status_code = 201
 | 
					  env.response.status_code = 201
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "title"      => title,
 | 
					    "title"      => title,
 | 
				
			||||||
@@ -4958,11 +4945,9 @@ post "/api/v1/auth/playlists/:plid/videos" do |env|
 | 
				
			|||||||
  PG_DB.exec("INSERT INTO playlist_videos VALUES (#{args})", args: video_array)
 | 
					  PG_DB.exec("INSERT INTO playlist_videos VALUES (#{args})", args: video_array)
 | 
				
			||||||
  PG_DB.exec("UPDATE playlists SET index = array_append(index, $1), video_count = video_count + 1, updated = $2 WHERE id = $3", playlist_video.index, Time.utc, plid)
 | 
					  PG_DB.exec("UPDATE playlists SET index = array_append(index, $1), video_count = video_count + 1, updated = $2 WHERE id = $3", playlist_video.index, Time.utc, plid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  host_url = make_host_url(config, Kemal.config)
 | 
					  env.response.headers["Location"] = "#{HOST_URL}/api/v1/auth/playlists/#{plid}/videos/#{playlist_video.index.to_u64.to_s(16).upcase}"
 | 
				
			||||||
 | 
					 | 
				
			||||||
  env.response.headers["Location"] = "#{host_url}/api/v1/auth/playlists/#{plid}/videos/#{playlist_video.index.to_u64.to_s(16).upcase}"
 | 
					 | 
				
			||||||
  env.response.status_code = 201
 | 
					  env.response.status_code = 201
 | 
				
			||||||
  playlist_video.to_json(locale, config, Kemal.config, index: playlist.index.size)
 | 
					  playlist_video.to_json(locale, index: playlist.index.size)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
delete "/api/v1/auth/playlists/:plid/videos/:index" do |env|
 | 
					delete "/api/v1/auth/playlists/:plid/videos/:index" do |env|
 | 
				
			||||||
@@ -5251,12 +5236,10 @@ get "/api/manifest/hls_variant/*" do |env|
 | 
				
			|||||||
  env.response.content_type = "application/x-mpegURL"
 | 
					  env.response.content_type = "application/x-mpegURL"
 | 
				
			||||||
  env.response.headers.add("Access-Control-Allow-Origin", "*")
 | 
					  env.response.headers.add("Access-Control-Allow-Origin", "*")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  host_url = make_host_url(config, Kemal.config)
 | 
					  manifest = response.body
 | 
				
			||||||
 | 
					 | 
				
			||||||
  manifest = manifest.body
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if local
 | 
					  if local
 | 
				
			||||||
    manifest = manifest.gsub("https://www.youtube.com", host_url)
 | 
					    manifest = manifest.gsub("https://www.youtube.com", HOST_URL)
 | 
				
			||||||
    manifest = manifest.gsub("index.m3u8", "index.m3u8?local=true")
 | 
					    manifest = manifest.gsub("index.m3u8", "index.m3u8?local=true")
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -5276,9 +5259,7 @@ get "/api/manifest/hls_playlist/*" do |env|
 | 
				
			|||||||
  env.response.content_type = "application/x-mpegURL"
 | 
					  env.response.content_type = "application/x-mpegURL"
 | 
				
			||||||
  env.response.headers.add("Access-Control-Allow-Origin", "*")
 | 
					  env.response.headers.add("Access-Control-Allow-Origin", "*")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  host_url = make_host_url(config, Kemal.config)
 | 
					  manifest = response.body
 | 
				
			||||||
 | 
					 | 
				
			||||||
  manifest = manifest.body
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if local
 | 
					  if local
 | 
				
			||||||
    manifest = manifest.gsub(/^https:\/\/r\d---.{11}\.c\.youtube\.com[^\n]*/m) do |match|
 | 
					    manifest = manifest.gsub(/^https:\/\/r\d---.{11}\.c\.youtube\.com[^\n]*/m) do |match|
 | 
				
			||||||
@@ -5313,7 +5294,7 @@ get "/api/manifest/hls_playlist/*" do |env|
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      raw_params["local"] = "true"
 | 
					      raw_params["local"] = "true"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      "#{host_url}/videoplayback?#{raw_params}"
 | 
					      "#{HOST_URL}/videoplayback?#{raw_params}"
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -5784,7 +5765,7 @@ get "/vi/:id/:name" do |env|
 | 
				
			|||||||
  headers = HTTP::Headers{":authority" => "i.ytimg.com"}
 | 
					  headers = HTTP::Headers{":authority" => "i.ytimg.com"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if name == "maxres.jpg"
 | 
					  if name == "maxres.jpg"
 | 
				
			||||||
    build_thumbnails(id, config, Kemal.config).each do |thumb|
 | 
					    build_thumbnails(id).each do |thumb|
 | 
				
			||||||
      if YT_POOL.client &.head("/vi/#{id}/#{thumb[:url]}.jpg", headers).status_code == 200
 | 
					      if YT_POOL.client &.head("/vi/#{id}/#{thumb[:url]}.jpg", headers).status_code == 200
 | 
				
			||||||
        name = thumb[:url] + ".jpg"
 | 
					        name = thumb[:url] + ".jpg"
 | 
				
			||||||
        break
 | 
					        break
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,14 +9,14 @@ struct InvidiousChannel
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ChannelVideo
 | 
					struct ChannelVideo
 | 
				
			||||||
  def to_json(locale, config, kemal_config, json : JSON::Builder)
 | 
					  def to_json(locale, json : JSON::Builder)
 | 
				
			||||||
    json.object do
 | 
					    json.object do
 | 
				
			||||||
      json.field "type", "shortVideo"
 | 
					      json.field "type", "shortVideo"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      json.field "title", self.title
 | 
					      json.field "title", self.title
 | 
				
			||||||
      json.field "videoId", self.id
 | 
					      json.field "videoId", self.id
 | 
				
			||||||
      json.field "videoThumbnails" do
 | 
					      json.field "videoThumbnails" do
 | 
				
			||||||
        generate_thumbnails(json, self.id, config, Kemal.config)
 | 
					        generate_thumbnails(json, self.id)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      json.field "lengthSeconds", self.length_seconds
 | 
					      json.field "lengthSeconds", self.length_seconds
 | 
				
			||||||
@@ -31,17 +31,17 @@ struct ChannelVideo
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_json(locale, config, kemal_config, json : JSON::Builder | Nil = nil)
 | 
					  def to_json(locale, json : JSON::Builder | Nil = nil)
 | 
				
			||||||
    if json
 | 
					    if json
 | 
				
			||||||
      to_json(locale, config, kemal_config, json)
 | 
					      to_json(locale, json)
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      JSON.build do |json|
 | 
					      JSON.build do |json|
 | 
				
			||||||
        to_json(locale, config, kemal_config, json)
 | 
					        to_json(locale, json)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_xml(locale, host_url, query_params, xml : XML::Builder)
 | 
					  def to_xml(locale, query_params, xml : XML::Builder)
 | 
				
			||||||
    query_params["v"] = self.id
 | 
					    query_params["v"] = self.id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    xml.element("entry") do
 | 
					    xml.element("entry") do
 | 
				
			||||||
@@ -49,17 +49,17 @@ struct ChannelVideo
 | 
				
			|||||||
      xml.element("yt:videoId") { xml.text self.id }
 | 
					      xml.element("yt:videoId") { xml.text self.id }
 | 
				
			||||||
      xml.element("yt:channelId") { xml.text self.ucid }
 | 
					      xml.element("yt:channelId") { xml.text self.ucid }
 | 
				
			||||||
      xml.element("title") { xml.text self.title }
 | 
					      xml.element("title") { xml.text self.title }
 | 
				
			||||||
      xml.element("link", rel: "alternate", href: "#{host_url}/watch?#{query_params}")
 | 
					      xml.element("link", rel: "alternate", href: "#{HOST_URL}/watch?#{query_params}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      xml.element("author") do
 | 
					      xml.element("author") do
 | 
				
			||||||
        xml.element("name") { xml.text self.author }
 | 
					        xml.element("name") { xml.text self.author }
 | 
				
			||||||
        xml.element("uri") { xml.text "#{host_url}/channel/#{self.ucid}" }
 | 
					        xml.element("uri") { xml.text "#{HOST_URL}/channel/#{self.ucid}" }
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      xml.element("content", type: "xhtml") do
 | 
					      xml.element("content", type: "xhtml") do
 | 
				
			||||||
        xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
 | 
					        xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
 | 
				
			||||||
          xml.element("a", href: "#{host_url}/watch?#{query_params}") do
 | 
					          xml.element("a", href: "#{HOST_URL}/watch?#{query_params}") do
 | 
				
			||||||
            xml.element("img", src: "#{host_url}/vi/#{self.id}/mqdefault.jpg")
 | 
					            xml.element("img", src: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg")
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
@@ -69,18 +69,18 @@ struct ChannelVideo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      xml.element("media:group") do
 | 
					      xml.element("media:group") do
 | 
				
			||||||
        xml.element("media:title") { xml.text self.title }
 | 
					        xml.element("media:title") { xml.text self.title }
 | 
				
			||||||
        xml.element("media:thumbnail", url: "#{host_url}/vi/#{self.id}/mqdefault.jpg",
 | 
					        xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg",
 | 
				
			||||||
          width: "320", height: "180")
 | 
					          width: "320", height: "180")
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_xml(locale, config, kemal_config, xml : XML::Builder | Nil = nil)
 | 
					  def to_xml(locale, xml : XML::Builder | Nil = nil)
 | 
				
			||||||
    if xml
 | 
					    if xml
 | 
				
			||||||
      to_xml(locale, config, kemal_config, xml)
 | 
					      to_xml(locale, xml)
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      XML.build do |xml|
 | 
					      XML.build do |xml|
 | 
				
			||||||
        to_xml(locale, config, kemal_config, xml)
 | 
					        to_xml(locale, xml)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -557,7 +557,7 @@ def extract_channel_playlists_cursor(url, auto_generated)
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# TODO: Add "sort_by"
 | 
					# TODO: Add "sort_by"
 | 
				
			||||||
def fetch_channel_community(ucid, continuation, locale, config, kemal_config, format, thin_mode)
 | 
					def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
 | 
				
			||||||
  response = YT_POOL.client &.get("/channel/#{ucid}/community?gl=US&hl=en")
 | 
					  response = YT_POOL.client &.get("/channel/#{ucid}/community?gl=US&hl=en")
 | 
				
			||||||
  if response.status_code != 200
 | 
					  if response.status_code != 200
 | 
				
			||||||
    response = YT_POOL.client &.get("/user/#{ucid}/community?gl=US&hl=en")
 | 
					    response = YT_POOL.client &.get("/user/#{ucid}/community?gl=US&hl=en")
 | 
				
			||||||
@@ -708,7 +708,7 @@ def fetch_channel_community(ucid, continuation, locale, config, kemal_config, fo
 | 
				
			|||||||
                        json.field "title", attachment["title"]["simpleText"].as_s
 | 
					                        json.field "title", attachment["title"]["simpleText"].as_s
 | 
				
			||||||
                        json.field "videoId", video_id
 | 
					                        json.field "videoId", video_id
 | 
				
			||||||
                        json.field "videoThumbnails" do
 | 
					                        json.field "videoThumbnails" do
 | 
				
			||||||
                          generate_thumbnails(json, video_id, config, kemal_config)
 | 
					                          generate_thumbnails(json, video_id)
 | 
				
			||||||
                        end
 | 
					                        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        json.field "lengthSeconds", decode_length_seconds(attachment["lengthText"]["simpleText"].as_s)
 | 
					                        json.field "lengthSeconds", decode_length_seconds(attachment["lengthText"]["simpleText"].as_s)
 | 
				
			||||||
@@ -956,33 +956,17 @@ def get_about_info(ucid, locale)
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_60_videos(ucid, author, page, auto_generated, sort_by = "newest")
 | 
					def get_60_videos(ucid, author, page, auto_generated, sort_by = "newest")
 | 
				
			||||||
  count = 0
 | 
					 | 
				
			||||||
  videos = [] of SearchVideo
 | 
					  videos = [] of SearchVideo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  2.times do |i|
 | 
					  2.times do |i|
 | 
				
			||||||
    url = produce_channel_videos_url(ucid, page * 2 + (i - 1), auto_generated: auto_generated, sort_by: sort_by)
 | 
					    url = produce_channel_videos_url(ucid, page * 2 + (i - 1), auto_generated: auto_generated, sort_by: sort_by)
 | 
				
			||||||
    response = YT_POOL.client &.get(url)
 | 
					    response = YT_POOL.client &.get(url, headers)
 | 
				
			||||||
    json = JSON.parse(response.body)
 | 
					    initial_data = JSON.parse(response.body).as_a.find &.["response"]?
 | 
				
			||||||
 | 
					    break if !initial_data
 | 
				
			||||||
    if json["content_html"]? && !json["content_html"].as_s.empty?
 | 
					    videos.concat extract_videos(initial_data.as_h)
 | 
				
			||||||
      document = XML.parse_html(json["content_html"].as_s)
 | 
					 | 
				
			||||||
      nodeset = document.xpath_nodes(%q(//li[contains(@class, "feed-item-container")]))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if !json["load_more_widget_html"]?.try &.as_s.empty?
 | 
					 | 
				
			||||||
        count += 30
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if auto_generated
 | 
					 | 
				
			||||||
        videos += extract_videos(nodeset)
 | 
					 | 
				
			||||||
      else
 | 
					 | 
				
			||||||
        videos += extract_videos(nodeset, ucid, author)
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      break
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return videos, count
 | 
					  return videos.size, videos
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_latest_videos(ucid)
 | 
					def get_latest_videos(ucid)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -727,13 +727,10 @@ def cache_annotation(db, id, annotations)
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if has_legacy_annotations
 | 
					  db.exec("INSERT INTO annotations VALUES ($1, $2) ON CONFLICT DO NOTHING", id, annotations) if has_legacy_annotations
 | 
				
			||||||
    # TODO: Update on conflict?
 | 
					 | 
				
			||||||
    db.exec("INSERT INTO annotations VALUES ($1, $2) ON CONFLICT DO NOTHING", id, annotations)
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def create_notification_stream(env, config, kemal_config, decrypt_function, topics, connection_channel)
 | 
					def create_notification_stream(env, topics, connection_channel)
 | 
				
			||||||
  connection = Channel(PQ::Notification).new(8)
 | 
					  connection = Channel(PQ::Notification).new(8)
 | 
				
			||||||
  connection_channel.send({true, connection})
 | 
					  connection_channel.send({true, connection})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -753,7 +750,7 @@ def create_notification_stream(env, config, kemal_config, decrypt_function, topi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          video = get_video(video_id, PG_DB)
 | 
					          video = get_video(video_id, PG_DB)
 | 
				
			||||||
          video.published = published
 | 
					          video.published = published
 | 
				
			||||||
          response = JSON.parse(video.to_json(locale, config, kemal_config, decrypt_function))
 | 
					          response = JSON.parse(video.to_json(locale))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if fields_text = env.params.query["fields"]?
 | 
					          if fields_text = env.params.query["fields"]?
 | 
				
			||||||
            begin
 | 
					            begin
 | 
				
			||||||
@@ -787,7 +784,7 @@ def create_notification_stream(env, config, kemal_config, decrypt_function, topi
 | 
				
			|||||||
          when .match(/UC[A-Za-z0-9_-]{22}/)
 | 
					          when .match(/UC[A-Za-z0-9_-]{22}/)
 | 
				
			||||||
            PG_DB.query_all("SELECT * FROM channel_videos WHERE ucid = $1 AND published > $2 ORDER BY published DESC LIMIT 15",
 | 
					            PG_DB.query_all("SELECT * FROM channel_videos WHERE ucid = $1 AND published > $2 ORDER BY published DESC LIMIT 15",
 | 
				
			||||||
              topic, Time.unix(since.not_nil!), as: ChannelVideo).each do |video|
 | 
					              topic, Time.unix(since.not_nil!), as: ChannelVideo).each do |video|
 | 
				
			||||||
              response = JSON.parse(video.to_json(locale, config, Kemal.config))
 | 
					              response = JSON.parse(video.to_json(locale))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              if fields_text = env.params.query["fields"]?
 | 
					              if fields_text = env.params.query["fields"]?
 | 
				
			||||||
                begin
 | 
					                begin
 | 
				
			||||||
@@ -829,7 +826,7 @@ def create_notification_stream(env, config, kemal_config, decrypt_function, topi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        video = get_video(video_id, PG_DB)
 | 
					        video = get_video(video_id, PG_DB)
 | 
				
			||||||
        video.published = Time.unix(published)
 | 
					        video.published = Time.unix(published)
 | 
				
			||||||
        response = JSON.parse(video.to_json(locale, config, Kemal.config, decrypt_function))
 | 
					        response = JSON.parse(video.to_json(locale))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if fields_text = env.params.query["fields"]?
 | 
					        if fields_text = env.params.query["fields"]?
 | 
				
			||||||
          begin
 | 
					          begin
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,12 +40,12 @@ def fetch_decrypt_function(id = "CvFH_6DNRCY")
 | 
				
			|||||||
  return decrypt_function
 | 
					  return decrypt_function
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def decrypt_signature(fmt, op)
 | 
					def decrypt_signature(fmt : Hash(String, JSON::Any))
 | 
				
			||||||
  return "" if !fmt["s"]? || !fmt["sp"]?
 | 
					  return "" if !fmt["s"]? || !fmt["sp"]?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sp = fmt["sp"]
 | 
					  sp = fmt["sp"].as_s
 | 
				
			||||||
  sig = fmt["s"].split("")
 | 
					  sig = fmt["s"].as_s.split("")
 | 
				
			||||||
  op.each do |proc, value|
 | 
					  DECRYPT_FUNCTION.each do |proc, value|
 | 
				
			||||||
    sig = proc.call(sig, value)
 | 
					    sig = proc.call(sig, value)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -351,10 +351,8 @@ def subscribe_pubsub(topic, key, config)
 | 
				
			|||||||
  nonce = Random::Secure.hex(4)
 | 
					  nonce = Random::Secure.hex(4)
 | 
				
			||||||
  signature = "#{time}:#{nonce}"
 | 
					  signature = "#{time}:#{nonce}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  host_url = make_host_url(config, Kemal.config)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  body = {
 | 
					  body = {
 | 
				
			||||||
    "hub.callback"      => "#{host_url}/feed/webhook/v1:#{time}:#{nonce}:#{OpenSSL::HMAC.hexdigest(:sha1, key, signature)}",
 | 
					    "hub.callback"      => "#{HOST_URL}/feed/webhook/v1:#{time}:#{nonce}:#{OpenSSL::HMAC.hexdigest(:sha1, key, signature)}",
 | 
				
			||||||
    "hub.topic"         => "https://www.youtube.com/xml/feeds/videos.xml?#{topic}",
 | 
					    "hub.topic"         => "https://www.youtube.com/xml/feeds/videos.xml?#{topic}",
 | 
				
			||||||
    "hub.verify"        => "async",
 | 
					    "hub.verify"        => "async",
 | 
				
			||||||
    "hub.mode"          => "subscribe",
 | 
					    "hub.mode"          => "subscribe",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +1,26 @@
 | 
				
			|||||||
struct PlaylistVideo
 | 
					struct PlaylistVideo
 | 
				
			||||||
  def to_xml(host_url, auto_generated, xml : XML::Builder)
 | 
					  def to_xml(auto_generated, xml : XML::Builder)
 | 
				
			||||||
    xml.element("entry") do
 | 
					    xml.element("entry") do
 | 
				
			||||||
      xml.element("id") { xml.text "yt:video:#{self.id}" }
 | 
					      xml.element("id") { xml.text "yt:video:#{self.id}" }
 | 
				
			||||||
      xml.element("yt:videoId") { xml.text self.id }
 | 
					      xml.element("yt:videoId") { xml.text self.id }
 | 
				
			||||||
      xml.element("yt:channelId") { xml.text self.ucid }
 | 
					      xml.element("yt:channelId") { xml.text self.ucid }
 | 
				
			||||||
      xml.element("title") { xml.text self.title }
 | 
					      xml.element("title") { xml.text self.title }
 | 
				
			||||||
      xml.element("link", rel: "alternate", href: "#{host_url}/watch?v=#{self.id}")
 | 
					      xml.element("link", rel: "alternate", href: "#{HOST_URL}/watch?v=#{self.id}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      xml.element("author") do
 | 
					      xml.element("author") do
 | 
				
			||||||
        if auto_generated
 | 
					        if auto_generated
 | 
				
			||||||
          xml.element("name") { xml.text self.author }
 | 
					          xml.element("name") { xml.text self.author }
 | 
				
			||||||
          xml.element("uri") { xml.text "#{host_url}/channel/#{self.ucid}" }
 | 
					          xml.element("uri") { xml.text "#{HOST_URL}/channel/#{self.ucid}" }
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
          xml.element("name") { xml.text author }
 | 
					          xml.element("name") { xml.text author }
 | 
				
			||||||
          xml.element("uri") { xml.text "#{host_url}/channel/#{ucid}" }
 | 
					          xml.element("uri") { xml.text "#{HOST_URL}/channel/#{ucid}" }
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      xml.element("content", type: "xhtml") do
 | 
					      xml.element("content", type: "xhtml") do
 | 
				
			||||||
        xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
 | 
					        xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
 | 
				
			||||||
          xml.element("a", href: "#{host_url}/watch?v=#{self.id}") do
 | 
					          xml.element("a", href: "#{HOST_URL}/watch?v=#{self.id}") do
 | 
				
			||||||
            xml.element("img", src: "#{host_url}/vi/#{self.id}/mqdefault.jpg")
 | 
					            xml.element("img", src: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg")
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
@@ -29,23 +29,23 @@ struct PlaylistVideo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      xml.element("media:group") do
 | 
					      xml.element("media:group") do
 | 
				
			||||||
        xml.element("media:title") { xml.text self.title }
 | 
					        xml.element("media:title") { xml.text self.title }
 | 
				
			||||||
        xml.element("media:thumbnail", url: "#{host_url}/vi/#{self.id}/mqdefault.jpg",
 | 
					        xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg",
 | 
				
			||||||
          width: "320", height: "180")
 | 
					          width: "320", height: "180")
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_xml(host_url, auto_generated, xml : XML::Builder? = nil)
 | 
					  def to_xml(auto_generated, xml : XML::Builder? = nil)
 | 
				
			||||||
    if xml
 | 
					    if xml
 | 
				
			||||||
      to_xml(host_url, auto_generated, xml)
 | 
					      to_xml(auto_generated, xml)
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      XML.build do |json|
 | 
					      XML.build do |json|
 | 
				
			||||||
        to_xml(host_url, auto_generated, xml)
 | 
					        to_xml(auto_generated, xml)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_json(locale, config, kemal_config, json : JSON::Builder, index : Int32?)
 | 
					  def to_json(locale, json : JSON::Builder, index : Int32?)
 | 
				
			||||||
    json.object do
 | 
					    json.object do
 | 
				
			||||||
      json.field "title", self.title
 | 
					      json.field "title", self.title
 | 
				
			||||||
      json.field "videoId", self.id
 | 
					      json.field "videoId", self.id
 | 
				
			||||||
@@ -55,7 +55,7 @@ struct PlaylistVideo
 | 
				
			|||||||
      json.field "authorUrl", "/channel/#{self.ucid}"
 | 
					      json.field "authorUrl", "/channel/#{self.ucid}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      json.field "videoThumbnails" do
 | 
					      json.field "videoThumbnails" do
 | 
				
			||||||
        generate_thumbnails(json, self.id, config, kemal_config)
 | 
					        generate_thumbnails(json, self.id)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if index
 | 
					      if index
 | 
				
			||||||
@@ -69,12 +69,12 @@ struct PlaylistVideo
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_json(locale, config, kemal_config, json : JSON::Builder? = nil, index : Int32? = nil)
 | 
					  def to_json(locale, json : JSON::Builder? = nil, index : Int32? = nil)
 | 
				
			||||||
    if json
 | 
					    if json
 | 
				
			||||||
      to_json(locale, config, kemal_config, json, index: index)
 | 
					      to_json(locale, json, index: index)
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      JSON.build do |json|
 | 
					      JSON.build do |json|
 | 
				
			||||||
        to_json(locale, config, kemal_config, json, index: index)
 | 
					        to_json(locale, json, index: index)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -93,7 +93,7 @@ struct PlaylistVideo
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Playlist
 | 
					struct Playlist
 | 
				
			||||||
  def to_json(offset, locale, config, kemal_config, json : JSON::Builder, continuation : String? = nil)
 | 
					  def to_json(offset, locale, json : JSON::Builder, continuation : String? = nil)
 | 
				
			||||||
    json.object do
 | 
					    json.object do
 | 
				
			||||||
      json.field "type", "playlist"
 | 
					      json.field "type", "playlist"
 | 
				
			||||||
      json.field "title", self.title
 | 
					      json.field "title", self.title
 | 
				
			||||||
@@ -130,19 +130,19 @@ struct Playlist
 | 
				
			|||||||
        json.array do
 | 
					        json.array do
 | 
				
			||||||
          videos = get_playlist_videos(PG_DB, self, offset: offset, locale: locale, continuation: continuation)
 | 
					          videos = get_playlist_videos(PG_DB, self, offset: offset, locale: locale, continuation: continuation)
 | 
				
			||||||
          videos.each_with_index do |video, index|
 | 
					          videos.each_with_index do |video, index|
 | 
				
			||||||
            video.to_json(locale, config, Kemal.config, json)
 | 
					            video.to_json(locale, json)
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_json(offset, locale, config, kemal_config, json : JSON::Builder? = nil, continuation : String? = nil)
 | 
					  def to_json(offset, locale, json : JSON::Builder? = nil, continuation : String? = nil)
 | 
				
			||||||
    if json
 | 
					    if json
 | 
				
			||||||
      to_json(offset, locale, config, kemal_config, json, continuation: continuation)
 | 
					      to_json(offset, locale, json, continuation: continuation)
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      JSON.build do |json|
 | 
					      JSON.build do |json|
 | 
				
			||||||
        to_json(offset, locale, config, kemal_config, json, continuation: continuation)
 | 
					        to_json(offset, locale, json, continuation: continuation)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -172,7 +172,7 @@ enum PlaylistPrivacy
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct InvidiousPlaylist
 | 
					struct InvidiousPlaylist
 | 
				
			||||||
  def to_json(offset, locale, config, kemal_config, json : JSON::Builder, continuation : String? = nil)
 | 
					  def to_json(offset, locale, json : JSON::Builder, continuation : String? = nil)
 | 
				
			||||||
    json.object do
 | 
					    json.object do
 | 
				
			||||||
      json.field "type", "invidiousPlaylist"
 | 
					      json.field "type", "invidiousPlaylist"
 | 
				
			||||||
      json.field "title", self.title
 | 
					      json.field "title", self.title
 | 
				
			||||||
@@ -195,19 +195,19 @@ struct InvidiousPlaylist
 | 
				
			|||||||
        json.array do
 | 
					        json.array do
 | 
				
			||||||
          videos = get_playlist_videos(PG_DB, self, offset: offset, locale: locale, continuation: continuation)
 | 
					          videos = get_playlist_videos(PG_DB, self, offset: offset, locale: locale, continuation: continuation)
 | 
				
			||||||
          videos.each_with_index do |video, index|
 | 
					          videos.each_with_index do |video, index|
 | 
				
			||||||
            video.to_json(locale, config, Kemal.config, json, offset + index)
 | 
					            video.to_json(locale, json, offset + index)
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_json(offset, locale, config, kemal_config, json : JSON::Builder? = nil, continuation : String? = nil)
 | 
					  def to_json(offset, locale, json : JSON::Builder? = nil, continuation : String? = nil)
 | 
				
			||||||
    if json
 | 
					    if json
 | 
				
			||||||
      to_json(offset, locale, config, kemal_config, json, continuation: continuation)
 | 
					      to_json(offset, locale, json, continuation: continuation)
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      JSON.build do |json|
 | 
					      JSON.build do |json|
 | 
				
			||||||
        to_json(offset, locale, config, kemal_config, json, continuation: continuation)
 | 
					        to_json(offset, locale, json, continuation: continuation)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
struct SearchVideo
 | 
					struct SearchVideo
 | 
				
			||||||
  def to_xml(host_url, auto_generated, query_params, xml : XML::Builder)
 | 
					  def to_xml(auto_generated, query_params, xml : XML::Builder)
 | 
				
			||||||
    query_params["v"] = self.id
 | 
					    query_params["v"] = self.id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    xml.element("entry") do
 | 
					    xml.element("entry") do
 | 
				
			||||||
@@ -7,22 +7,22 @@ struct SearchVideo
 | 
				
			|||||||
      xml.element("yt:videoId") { xml.text self.id }
 | 
					      xml.element("yt:videoId") { xml.text self.id }
 | 
				
			||||||
      xml.element("yt:channelId") { xml.text self.ucid }
 | 
					      xml.element("yt:channelId") { xml.text self.ucid }
 | 
				
			||||||
      xml.element("title") { xml.text self.title }
 | 
					      xml.element("title") { xml.text self.title }
 | 
				
			||||||
      xml.element("link", rel: "alternate", href: "#{host_url}/watch?#{query_params}")
 | 
					      xml.element("link", rel: "alternate", href: "#{HOST_URL}/watch?#{query_params}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      xml.element("author") do
 | 
					      xml.element("author") do
 | 
				
			||||||
        if auto_generated
 | 
					        if auto_generated
 | 
				
			||||||
          xml.element("name") { xml.text self.author }
 | 
					          xml.element("name") { xml.text self.author }
 | 
				
			||||||
          xml.element("uri") { xml.text "#{host_url}/channel/#{self.ucid}" }
 | 
					          xml.element("uri") { xml.text "#{HOST_URL}/channel/#{self.ucid}" }
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
          xml.element("name") { xml.text author }
 | 
					          xml.element("name") { xml.text author }
 | 
				
			||||||
          xml.element("uri") { xml.text "#{host_url}/channel/#{ucid}" }
 | 
					          xml.element("uri") { xml.text "#{HOST_URL}/channel/#{ucid}" }
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      xml.element("content", type: "xhtml") do
 | 
					      xml.element("content", type: "xhtml") do
 | 
				
			||||||
        xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
 | 
					        xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
 | 
				
			||||||
          xml.element("a", href: "#{host_url}/watch?#{query_params}") do
 | 
					          xml.element("a", href: "#{HOST_URL}/watch?#{query_params}") do
 | 
				
			||||||
            xml.element("img", src: "#{host_url}/vi/#{self.id}/mqdefault.jpg")
 | 
					            xml.element("img", src: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg")
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          xml.element("p", style: "word-break:break-word;white-space:pre-wrap") { xml.text html_to_content(self.description_html) }
 | 
					          xml.element("p", style: "word-break:break-word;white-space:pre-wrap") { xml.text html_to_content(self.description_html) }
 | 
				
			||||||
@@ -33,7 +33,7 @@ struct SearchVideo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      xml.element("media:group") do
 | 
					      xml.element("media:group") do
 | 
				
			||||||
        xml.element("media:title") { xml.text self.title }
 | 
					        xml.element("media:title") { xml.text self.title }
 | 
				
			||||||
        xml.element("media:thumbnail", url: "#{host_url}/vi/#{self.id}/mqdefault.jpg",
 | 
					        xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg",
 | 
				
			||||||
          width: "320", height: "180")
 | 
					          width: "320", height: "180")
 | 
				
			||||||
        xml.element("media:description") { xml.text html_to_content(self.description_html) }
 | 
					        xml.element("media:description") { xml.text html_to_content(self.description_html) }
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
@@ -44,17 +44,17 @@ struct SearchVideo
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_xml(host_url, auto_generated, query_params, xml : XML::Builder | Nil = nil)
 | 
					  def to_xml(auto_generated, query_params, xml : XML::Builder | Nil = nil)
 | 
				
			||||||
    if xml
 | 
					    if xml
 | 
				
			||||||
      to_xml(host_url, auto_generated, query_params, xml)
 | 
					      to_xml(HOST_URL, auto_generated, query_params, xml)
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      XML.build do |json|
 | 
					      XML.build do |json|
 | 
				
			||||||
        to_xml(host_url, auto_generated, query_params, xml)
 | 
					        to_xml(HOST_URL, auto_generated, query_params, xml)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_json(locale, config, kemal_config, json : JSON::Builder)
 | 
					  def to_json(locale, json : JSON::Builder)
 | 
				
			||||||
    json.object do
 | 
					    json.object do
 | 
				
			||||||
      json.field "type", "video"
 | 
					      json.field "type", "video"
 | 
				
			||||||
      json.field "title", self.title
 | 
					      json.field "title", self.title
 | 
				
			||||||
@@ -65,7 +65,7 @@ struct SearchVideo
 | 
				
			|||||||
      json.field "authorUrl", "/channel/#{self.ucid}"
 | 
					      json.field "authorUrl", "/channel/#{self.ucid}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      json.field "videoThumbnails" do
 | 
					      json.field "videoThumbnails" do
 | 
				
			||||||
        generate_thumbnails(json, self.id, config, kemal_config)
 | 
					        generate_thumbnails(json, self.id)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      json.field "description", html_to_content(self.description_html)
 | 
					      json.field "description", html_to_content(self.description_html)
 | 
				
			||||||
@@ -78,15 +78,20 @@ struct SearchVideo
 | 
				
			|||||||
      json.field "liveNow", self.live_now
 | 
					      json.field "liveNow", self.live_now
 | 
				
			||||||
      json.field "paid", self.paid
 | 
					      json.field "paid", self.paid
 | 
				
			||||||
      json.field "premium", self.premium
 | 
					      json.field "premium", self.premium
 | 
				
			||||||
 | 
					      json.field "isUpcoming", self.is_upcoming
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if self.premiere_timestamp
 | 
				
			||||||
 | 
					        json.field "premiereTimestamp", self.premiere_timestamp.try &.to_unix
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_json(locale, config, kemal_config, json : JSON::Builder | Nil = nil)
 | 
					  def to_json(locale, json : JSON::Builder | Nil = nil)
 | 
				
			||||||
    if json
 | 
					    if json
 | 
				
			||||||
      to_json(locale, config, kemal_config, json)
 | 
					      to_json(locale, json)
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      JSON.build do |json|
 | 
					      JSON.build do |json|
 | 
				
			||||||
        to_json(locale, config, kemal_config, json)
 | 
					        to_json(locale, json)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -116,7 +121,7 @@ struct SearchPlaylistVideo
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct SearchPlaylist
 | 
					struct SearchPlaylist
 | 
				
			||||||
  def to_json(locale, config, kemal_config, json : JSON::Builder)
 | 
					  def to_json(locale, json : JSON::Builder)
 | 
				
			||||||
    json.object do
 | 
					    json.object do
 | 
				
			||||||
      json.field "type", "playlist"
 | 
					      json.field "type", "playlist"
 | 
				
			||||||
      json.field "title", self.title
 | 
					      json.field "title", self.title
 | 
				
			||||||
@@ -137,7 +142,7 @@ struct SearchPlaylist
 | 
				
			|||||||
              json.field "lengthSeconds", video.length_seconds
 | 
					              json.field "lengthSeconds", video.length_seconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              json.field "videoThumbnails" do
 | 
					              json.field "videoThumbnails" do
 | 
				
			||||||
                generate_thumbnails(json, video.id, config, Kemal.config)
 | 
					                generate_thumbnails(json, video.id)
 | 
				
			||||||
              end
 | 
					              end
 | 
				
			||||||
            end
 | 
					            end
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
@@ -146,12 +151,12 @@ struct SearchPlaylist
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_json(locale, config, kemal_config, json : JSON::Builder | Nil = nil)
 | 
					  def to_json(locale, json : JSON::Builder | Nil = nil)
 | 
				
			||||||
    if json
 | 
					    if json
 | 
				
			||||||
      to_json(locale, config, kemal_config, json)
 | 
					      to_json(locale, json)
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      JSON.build do |json|
 | 
					      JSON.build do |json|
 | 
				
			||||||
        to_json(locale, config, kemal_config, json)
 | 
					        to_json(locale, json)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -168,7 +173,7 @@ struct SearchPlaylist
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct SearchChannel
 | 
					struct SearchChannel
 | 
				
			||||||
  def to_json(locale, config, kemal_config, json : JSON::Builder)
 | 
					  def to_json(locale, json : JSON::Builder)
 | 
				
			||||||
    json.object do
 | 
					    json.object do
 | 
				
			||||||
      json.field "type", "channel"
 | 
					      json.field "type", "channel"
 | 
				
			||||||
      json.field "author", self.author
 | 
					      json.field "author", self.author
 | 
				
			||||||
@@ -198,12 +203,12 @@ struct SearchChannel
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_json(locale, config, kemal_config, json : JSON::Builder | Nil = nil)
 | 
					  def to_json(locale, json : JSON::Builder | Nil = nil)
 | 
				
			||||||
    if json
 | 
					    if json
 | 
				
			||||||
      to_json(locale, config, kemal_config, json)
 | 
					      to_json(locale, json)
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      JSON.build do |json|
 | 
					      JSON.build do |json|
 | 
				
			||||||
        to_json(locale, config, kemal_config, json)
 | 
					        to_json(locale, json)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -255,17 +255,20 @@ struct Video
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_json(locale, config, kemal_config, decrypt_function, json : JSON::Builder)
 | 
					  def to_json(locale, json : JSON::Builder)
 | 
				
			||||||
    json.object do
 | 
					    json.object do
 | 
				
			||||||
      json.field "type", "video"
 | 
					      json.field "type", "video"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      json.field "title", self.title
 | 
					      json.field "title", self.title
 | 
				
			||||||
      json.field "videoId", self.id
 | 
					      json.field "videoId", self.id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      json.field "error", info["reason"] if info["reason"]?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      json.field "videoThumbnails" do
 | 
					      json.field "videoThumbnails" do
 | 
				
			||||||
        generate_thumbnails(json, self.id, config, kemal_config)
 | 
					        generate_thumbnails(json, self.id)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
      json.field "storyboards" do
 | 
					      json.field "storyboards" do
 | 
				
			||||||
        generate_storyboards(json, self.id, self.storyboards, config, kemal_config)
 | 
					        generate_storyboards(json, self.id, self.storyboards)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      json.field "description", html_to_content(self.description_html)
 | 
					      json.field "description", html_to_content(self.description_html)
 | 
				
			||||||
@@ -316,16 +319,12 @@ struct Video
 | 
				
			|||||||
        json.field "premiereTimestamp", self.premiere_timestamp.not_nil!.to_unix
 | 
					        json.field "premiereTimestamp", self.premiere_timestamp.not_nil!.to_unix
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if player_response["streamingData"]?.try &.["hlsManifestUrl"]?
 | 
					      if hlsvp = self.hls_manifest_url
 | 
				
			||||||
        host_url = make_host_url(config, kemal_config)
 | 
					        hlsvp = hlsvp.gsub("https://manifest.googlevideo.com", HOST_URL)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        hlsvp = player_response["streamingData"]["hlsManifestUrl"].as_s
 | 
					 | 
				
			||||||
        hlsvp = hlsvp.gsub("https://manifest.googlevideo.com", host_url)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        json.field "hlsUrl", hlsvp
 | 
					        json.field "hlsUrl", hlsvp
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      json.field "dashUrl", "#{make_host_url(config, kemal_config)}/api/manifest/dash/id/#{id}"
 | 
					      json.field "dashUrl", "#{HOST_URL}/api/manifest/dash/id/#{id}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      json.field "adaptiveFormats" do
 | 
					      json.field "adaptiveFormats" do
 | 
				
			||||||
        json.array do
 | 
					        json.array do
 | 
				
			||||||
@@ -424,7 +423,7 @@ struct Video
 | 
				
			|||||||
                json.field "videoId", rv["id"]
 | 
					                json.field "videoId", rv["id"]
 | 
				
			||||||
                json.field "title", rv["title"]
 | 
					                json.field "title", rv["title"]
 | 
				
			||||||
                json.field "videoThumbnails" do
 | 
					                json.field "videoThumbnails" do
 | 
				
			||||||
                  generate_thumbnails(json, rv["id"], config, kemal_config)
 | 
					                  generate_thumbnails(json, rv["id"])
 | 
				
			||||||
                end
 | 
					                end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                json.field "author", rv["author"]
 | 
					                json.field "author", rv["author"]
 | 
				
			||||||
@@ -457,12 +456,12 @@ struct Video
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def to_json(locale, config, kemal_config, decrypt_function, json : JSON::Builder | Nil = nil)
 | 
					  def to_json(locale, json : JSON::Builder | Nil = nil)
 | 
				
			||||||
    if json
 | 
					    if json
 | 
				
			||||||
      to_json(locale, config, kemal_config, decrypt_function, json)
 | 
					      to_json(locale, json)
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      JSON.build do |json|
 | 
					      JSON.build do |json|
 | 
				
			||||||
        to_json(locale, config, kemal_config, decrypt_function, json)
 | 
					        to_json(locale, json)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -1391,9 +1390,9 @@ def process_video_params(query, preferences)
 | 
				
			|||||||
  return params
 | 
					  return params
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def build_thumbnails(id, config, kemal_config)
 | 
					def build_thumbnails(id)
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    {name: "maxres", host: "#{make_host_url(config, kemal_config)}", url: "maxres", height: 720, width: 1280},
 | 
					    {name: "maxres", host: "#{HOST_URL}", url: "maxres", height: 720, width: 1280},
 | 
				
			||||||
    {name: "maxresdefault", host: "https://i.ytimg.com", url: "maxresdefault", height: 720, width: 1280},
 | 
					    {name: "maxresdefault", host: "https://i.ytimg.com", url: "maxresdefault", height: 720, width: 1280},
 | 
				
			||||||
    {name: "sddefault", host: "https://i.ytimg.com", url: "sddefault", height: 480, width: 640},
 | 
					    {name: "sddefault", host: "https://i.ytimg.com", url: "sddefault", height: 480, width: 640},
 | 
				
			||||||
    {name: "high", host: "https://i.ytimg.com", url: "hqdefault", height: 360, width: 480},
 | 
					    {name: "high", host: "https://i.ytimg.com", url: "hqdefault", height: 360, width: 480},
 | 
				
			||||||
@@ -1405,9 +1404,9 @@ def build_thumbnails(id, config, kemal_config)
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def generate_thumbnails(json, id, config, kemal_config)
 | 
					def generate_thumbnails(json, id)
 | 
				
			||||||
  json.array do
 | 
					  json.array do
 | 
				
			||||||
    build_thumbnails(id, config, kemal_config).each do |thumbnail|
 | 
					    build_thumbnails(id).each do |thumbnail|
 | 
				
			||||||
      json.object do
 | 
					      json.object do
 | 
				
			||||||
        json.field "quality", thumbnail[:name]
 | 
					        json.field "quality", thumbnail[:name]
 | 
				
			||||||
        json.field "url", "#{thumbnail[:host]}/vi/#{id}/#{thumbnail["url"]}.jpg"
 | 
					        json.field "url", "#{thumbnail[:host]}/vi/#{id}/#{thumbnail["url"]}.jpg"
 | 
				
			||||||
@@ -1418,7 +1417,7 @@ def generate_thumbnails(json, id, config, kemal_config)
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def generate_storyboards(json, id, storyboards, config, kemal_config)
 | 
					def generate_storyboards(json, id, storyboards)
 | 
				
			||||||
  json.array do
 | 
					  json.array do
 | 
				
			||||||
    storyboards.each do |storyboard|
 | 
					    storyboards.each do |storyboard|
 | 
				
			||||||
      json.object do
 | 
					      json.object do
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,23 +3,23 @@
 | 
				
			|||||||
<meta name="description" content="<%= HTML.escape(video.short_description) %>">
 | 
					<meta name="description" content="<%= HTML.escape(video.short_description) %>">
 | 
				
			||||||
<meta name="keywords" content="<%= video.keywords.join(",") %>">
 | 
					<meta name="keywords" content="<%= video.keywords.join(",") %>">
 | 
				
			||||||
<meta property="og:site_name" content="Invidious">
 | 
					<meta property="og:site_name" content="Invidious">
 | 
				
			||||||
<meta property="og:url" content="<%= host_url %>/watch?v=<%= video.id %>">
 | 
					<meta property="og:url" content="<%= HOST_URL %>/watch?v=<%= video.id %>">
 | 
				
			||||||
<meta property="og:title" content="<%= HTML.escape(video.title) %>">
 | 
					<meta property="og:title" content="<%= HTML.escape(video.title) %>">
 | 
				
			||||||
<meta property="og:image" content="/vi/<%= video.id %>/maxres.jpg">
 | 
					<meta property="og:image" content="/vi/<%= video.id %>/maxres.jpg">
 | 
				
			||||||
<meta property="og:description" content="<%= HTML.escape(video.short_description) %>">
 | 
					<meta property="og:description" content="<%= video.short_description %>">
 | 
				
			||||||
<meta property="og:type" content="video.other">
 | 
					<meta property="og:type" content="video.other">
 | 
				
			||||||
<meta property="og:video:url" content="<%= host_url %>/embed/<%= video.id %>">
 | 
					<meta property="og:video:url" content="<%= HOST_URL %>/embed/<%= video.id %>">
 | 
				
			||||||
<meta property="og:video:secure_url" content="<%= host_url %>/embed/<%= video.id %>">
 | 
					<meta property="og:video:secure_url" content="<%= HOST_URL %>/embed/<%= video.id %>">
 | 
				
			||||||
<meta property="og:video:type" content="text/html">
 | 
					<meta property="og:video:type" content="text/html">
 | 
				
			||||||
<meta property="og:video:width" content="1280">
 | 
					<meta property="og:video:width" content="1280">
 | 
				
			||||||
<meta property="og:video:height" content="720">
 | 
					<meta property="og:video:height" content="720">
 | 
				
			||||||
<meta name="twitter:card" content="player">
 | 
					<meta name="twitter:card" content="player">
 | 
				
			||||||
<meta name="twitter:site" content="@omarroth1">
 | 
					<meta name="twitter:site" content="@omarroth1">
 | 
				
			||||||
<meta name="twitter:url" content="<%= host_url %>/watch?v=<%= video.id %>">
 | 
					<meta name="twitter:url" content="<%= HOST_URL %>/watch?v=<%= video.id %>">
 | 
				
			||||||
<meta name="twitter:title" content="<%= HTML.escape(video.title) %>">
 | 
					<meta name="twitter:title" content="<%= HTML.escape(video.title) %>">
 | 
				
			||||||
<meta name="twitter:description" content="<%= HTML.escape(video.short_description) %>">
 | 
					<meta name="twitter:description" content="<%= video.short_description %>">
 | 
				
			||||||
<meta name="twitter:image" content="<%= host_url %>/vi/<%= video.id %>/maxres.jpg">
 | 
					<meta name="twitter:image" content="<%= HOST_URL %>/vi/<%= video.id %>/maxres.jpg">
 | 
				
			||||||
<meta name="twitter:player" content="<%= host_url %>/embed/<%= video.id %>">
 | 
					<meta name="twitter:player" content="<%= HOST_URL %>/embed/<%= video.id %>">
 | 
				
			||||||
<meta name="twitter:player:width" content="1280">
 | 
					<meta name="twitter:player:width" content="1280">
 | 
				
			||||||
<meta name="twitter:player:height" content="720">
 | 
					<meta name="twitter:player:height" content="720">
 | 
				
			||||||
<%= rendered "components/player_sources" %>
 | 
					<%= rendered "components/player_sources" %>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user