Organize routes
This commit is contained in:
parent
f189cd7c48
commit
f68b27da7c
384
src/invidious.cr
384
src/invidious.cr
|
@ -670,144 +670,6 @@ get "/signout" do |env|
|
|||
env.redirect referer
|
||||
end
|
||||
|
||||
get "/redirect" do |env|
|
||||
if env.params.query["q"]?
|
||||
env.redirect env.params.query["q"]
|
||||
else
|
||||
env.redirect "/"
|
||||
end
|
||||
end
|
||||
|
||||
get "/api/manifest/dash/id/:id" do |env|
|
||||
env.response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
env.response.content_type = "application/dash+xml"
|
||||
|
||||
local = env.params.query["local"]?.try &.== "true"
|
||||
id = env.params.url["id"]
|
||||
|
||||
client = make_client(YT_URL)
|
||||
begin
|
||||
video = get_video(id, client, PG_DB)
|
||||
rescue ex
|
||||
halt env, status_code: 403
|
||||
end
|
||||
|
||||
if video.info["dashmpd"]?
|
||||
manifest = client.get(video.info["dashmpd"]).body
|
||||
|
||||
manifest = manifest.gsub(/<BaseURL>[^<]+<\/BaseURL>/) do |baseurl|
|
||||
url = baseurl.lchop("<BaseURL>")
|
||||
url = url.rchop("</BaseURL>")
|
||||
|
||||
if local
|
||||
if Kemal.config.ssl
|
||||
scheme = "https://"
|
||||
end
|
||||
scheme ||= "http://"
|
||||
|
||||
url = scheme + env.request.headers["Host"] + URI.parse(url).full_path
|
||||
end
|
||||
|
||||
"<BaseURL>#{url}</BaseURL>"
|
||||
end
|
||||
|
||||
next manifest
|
||||
end
|
||||
|
||||
adaptive_fmts = [] of HTTP::Params
|
||||
if video.info.has_key?("adaptive_fmts")
|
||||
video.info["adaptive_fmts"].split(",") do |string|
|
||||
adaptive_fmts << HTTP::Params.parse(string)
|
||||
end
|
||||
else
|
||||
halt env, status_code: 403
|
||||
end
|
||||
|
||||
if local
|
||||
adaptive_fmts.each do |fmt|
|
||||
if Kemal.config.ssl
|
||||
scheme = "https://"
|
||||
end
|
||||
scheme ||= "http://"
|
||||
|
||||
fmt["url"] = scheme + env.request.headers["Host"] + URI.parse(fmt["url"]).full_path
|
||||
end
|
||||
end
|
||||
|
||||
if adaptive_fmts[0]? && adaptive_fmts[0]["s"]?
|
||||
adaptive_fmts.each do |fmt|
|
||||
fmt["url"] += "&signature=" + decrypt_signature(fmt["s"])
|
||||
end
|
||||
end
|
||||
|
||||
video_streams = adaptive_fmts.compact_map { |s| s["type"].starts_with?("video/mp4") ? s : nil }
|
||||
|
||||
audio_streams = adaptive_fmts.compact_map { |s| s["type"].starts_with?("audio/mp4") ? s : nil }
|
||||
audio_streams.sort_by! { |s| s["bitrate"].to_i }.reverse!
|
||||
audio_streams.each do |fmt|
|
||||
fmt["bitrate"] = (fmt["bitrate"].to_f64/1000).to_i.to_s
|
||||
end
|
||||
|
||||
manifest = XML.build(indent: " ", encoding: "UTF-8") do |xml|
|
||||
xml.element("MPD", "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "xmlns": "urn:mpeg:DASH:schema:MPD:2011",
|
||||
"xmlns:yt": "http://youtube.com/yt/2012/10/10", "xsi:schemaLocation": "urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd",
|
||||
minBufferTime: "PT1.5S", profiles: "urn:mpeg:dash:profile:isoff-main:2011", type: "static",
|
||||
mediaPresentationDuration: "PT#{video.info["length_seconds"]}S") do
|
||||
xml.element("Period") do
|
||||
xml.element("AdaptationSet", id: 0, mimeType: "audio/mp4", subsegmentAlignment: true) do
|
||||
xml.element("Role", schemeIdUri: "urn:mpeg:DASH:role:2011", value: "main")
|
||||
audio_streams.each do |fmt|
|
||||
mimetype, codecs = fmt["type"].split(";")
|
||||
codecs = codecs[9..-2]
|
||||
fmt_type = mimetype.split("/")[0]
|
||||
bandwidth = fmt["bitrate"]
|
||||
itag = fmt["itag"]
|
||||
url = fmt["url"]
|
||||
url = url.gsub("?", "/")
|
||||
url = url.gsub("&", "/")
|
||||
url = url.gsub("=", "/")
|
||||
|
||||
xml.element("Representation", id: fmt["itag"], codecs: codecs, bandwidth: bandwidth) do
|
||||
xml.element("AudioChannelConfiguration", schemeIdUri: "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", value: "2")
|
||||
xml.element("BaseURL") { xml.text url }
|
||||
xml.element("SegmentBase", indexRange: fmt["init"]) do
|
||||
xml.element("Initialization", range: fmt["index"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xml.element("AdaptationSet", id: 1, mimeType: "video/mp4", subsegmentAlignment: true) do
|
||||
xml.element("Role", schemeIdUri: "urn:mpeg:DASH:role:2011", value: "main")
|
||||
video_streams.each do |fmt|
|
||||
mimetype, codecs = fmt["type"].split(";")
|
||||
codecs = codecs[9..-2]
|
||||
bandwidth = fmt["bitrate"]
|
||||
itag = fmt["itag"]
|
||||
url = fmt["url"]
|
||||
url = url.gsub("?", "/")
|
||||
url = url.gsub("&", "/")
|
||||
url = url.gsub("=", "/")
|
||||
height, width = fmt["size"].split("x")
|
||||
|
||||
xml.element("Representation", id: itag, codecs: codecs, width: width, startWithSAP: "1", maxPlayoutRate: "1",
|
||||
height: height, bandwidth: bandwidth, frameRate: fmt["fps"]) do
|
||||
xml.element("BaseURL") { xml.text url }
|
||||
xml.element("SegmentBase", indexRange: fmt["init"]) do
|
||||
xml.element("Initialization", range: fmt["index"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
manifest = manifest.gsub(%(<?xml version="1.0" encoding="UTF-8U"?>), %(<?xml version="1.0" encoding="UTF-8"?>))
|
||||
manifest = manifest.gsub(%(<?xml version="1.0" encoding="UTF-8V"?>), %(<?xml version="1.0" encoding="UTF-8"?>))
|
||||
manifest
|
||||
end
|
||||
|
||||
# Get subscriptions for authorized user
|
||||
get "/feed/subscriptions" do |env|
|
||||
authorized = env.get? "authorized"
|
||||
|
@ -979,6 +841,54 @@ get "/subscription_ajax" do |env|
|
|||
env.redirect referer
|
||||
end
|
||||
|
||||
get "/user/:user" do |env|
|
||||
user = env.params.url["user"]
|
||||
env.redirect "/channel/#{user}"
|
||||
end
|
||||
|
||||
get "/channel/:ucid" do |env|
|
||||
authorized = env.get? "authorized"
|
||||
if authorized
|
||||
sid = env.get("sid").as(String)
|
||||
|
||||
subscriptions = PG_DB.query_one?("SELECT subscriptions FROM users WHERE id = $1", sid, as: Array(String))
|
||||
end
|
||||
subscriptions ||= [] of String
|
||||
|
||||
ucid = env.params.url["ucid"]
|
||||
|
||||
page = env.params.query["page"]?.try &.to_i
|
||||
page ||= 1
|
||||
|
||||
client = make_client(YT_URL)
|
||||
|
||||
if !ucid.starts_with? "UC"
|
||||
rss = client.get("/feeds/videos.xml?user=#{ucid}").body
|
||||
rss = XML.parse_html(rss)
|
||||
|
||||
ucid = rss.xpath_node("//feed/channelid").not_nil!.content
|
||||
env.redirect "/channel/#{ucid}"
|
||||
end
|
||||
|
||||
url = produce_playlist_url(ucid, (page - 1) * 100)
|
||||
response = client.get(url)
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
document = XML.parse_html(json["content_html"].as_s)
|
||||
author = document.xpath_node(%q(//div[@class="pl-video-owner"]/a)).not_nil!.content
|
||||
|
||||
videos = [] of ChannelVideo
|
||||
document.xpath_nodes(%q(//a[contains(@class,"pl-video-title-link")])).each do |item|
|
||||
href = URI.parse(item["href"])
|
||||
id = HTTP::Params.parse(href.query.not_nil!)["v"]
|
||||
title = item.content
|
||||
|
||||
videos << ChannelVideo.new(id, title, Time.now, Time.now, ucid, author)
|
||||
end
|
||||
|
||||
templated "channel"
|
||||
end
|
||||
|
||||
get "/modify_theme" do |env|
|
||||
referer = env.request.headers["referer"]?
|
||||
referer ||= "/"
|
||||
|
@ -993,6 +903,150 @@ get "/modify_theme" do |env|
|
|||
env.redirect referer
|
||||
end
|
||||
|
||||
get "/redirect" do |env|
|
||||
if env.params.query["q"]?
|
||||
env.redirect env.params.query["q"]
|
||||
else
|
||||
env.redirect "/"
|
||||
end
|
||||
end
|
||||
|
||||
get "/api/manifest/dash/id/:id" do |env|
|
||||
env.response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
env.response.content_type = "application/dash+xml"
|
||||
|
||||
local = env.params.query["local"]?.try &.== "true"
|
||||
id = env.params.url["id"]
|
||||
|
||||
client = make_client(YT_URL)
|
||||
begin
|
||||
video = get_video(id, client, PG_DB)
|
||||
rescue ex
|
||||
halt env, status_code: 403
|
||||
end
|
||||
|
||||
if video.info["dashmpd"]?
|
||||
manifest = client.get(video.info["dashmpd"]).body
|
||||
|
||||
manifest = manifest.gsub(/<BaseURL>[^<]+<\/BaseURL>/) do |baseurl|
|
||||
url = baseurl.lchop("<BaseURL>")
|
||||
url = url.rchop("</BaseURL>")
|
||||
|
||||
if local
|
||||
if Kemal.config.ssl
|
||||
scheme = "https://"
|
||||
end
|
||||
scheme ||= "http://"
|
||||
|
||||
url = scheme + env.request.headers["Host"] + URI.parse(url).full_path
|
||||
end
|
||||
|
||||
"<BaseURL>#{url}</BaseURL>"
|
||||
end
|
||||
|
||||
next manifest
|
||||
end
|
||||
|
||||
adaptive_fmts = [] of HTTP::Params
|
||||
if video.info.has_key?("adaptive_fmts")
|
||||
video.info["adaptive_fmts"].split(",") do |string|
|
||||
adaptive_fmts << HTTP::Params.parse(string)
|
||||
end
|
||||
else
|
||||
halt env, status_code: 403
|
||||
end
|
||||
|
||||
if local
|
||||
adaptive_fmts.each do |fmt|
|
||||
if Kemal.config.ssl
|
||||
scheme = "https://"
|
||||
end
|
||||
scheme ||= "http://"
|
||||
|
||||
fmt["url"] = scheme + env.request.headers["Host"] + URI.parse(fmt["url"]).full_path
|
||||
end
|
||||
end
|
||||
|
||||
if adaptive_fmts[0]? && adaptive_fmts[0]["s"]?
|
||||
adaptive_fmts.each do |fmt|
|
||||
fmt["url"] += "&signature=" + decrypt_signature(fmt["s"])
|
||||
end
|
||||
end
|
||||
|
||||
video_streams = adaptive_fmts.compact_map { |s| s["type"].starts_with?("video/mp4") ? s : nil }
|
||||
|
||||
audio_streams = adaptive_fmts.compact_map { |s| s["type"].starts_with?("audio/mp4") ? s : nil }
|
||||
audio_streams.sort_by! { |s| s["bitrate"].to_i }.reverse!
|
||||
audio_streams.each do |fmt|
|
||||
fmt["bitrate"] = (fmt["bitrate"].to_f64/1000).to_i.to_s
|
||||
end
|
||||
|
||||
manifest = XML.build(indent: " ", encoding: "UTF-8") do |xml|
|
||||
xml.element("MPD", "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "xmlns": "urn:mpeg:DASH:schema:MPD:2011",
|
||||
"xmlns:yt": "http://youtube.com/yt/2012/10/10", "xsi:schemaLocation": "urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd",
|
||||
minBufferTime: "PT1.5S", profiles: "urn:mpeg:dash:profile:isoff-main:2011", type: "static",
|
||||
mediaPresentationDuration: "PT#{video.info["length_seconds"]}S") do
|
||||
xml.element("Period") do
|
||||
xml.element("AdaptationSet", id: 0, mimeType: "audio/mp4", subsegmentAlignment: true) do
|
||||
xml.element("Role", schemeIdUri: "urn:mpeg:DASH:role:2011", value: "main")
|
||||
audio_streams.each do |fmt|
|
||||
mimetype, codecs = fmt["type"].split(";")
|
||||
codecs = codecs[9..-2]
|
||||
fmt_type = mimetype.split("/")[0]
|
||||
bandwidth = fmt["bitrate"]
|
||||
itag = fmt["itag"]
|
||||
url = fmt["url"]
|
||||
url = url.gsub("?", "/")
|
||||
url = url.gsub("&", "/")
|
||||
url = url.gsub("=", "/")
|
||||
|
||||
xml.element("Representation", id: fmt["itag"], codecs: codecs, bandwidth: bandwidth) do
|
||||
xml.element("AudioChannelConfiguration", schemeIdUri: "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", value: "2")
|
||||
xml.element("BaseURL") { xml.text url }
|
||||
xml.element("SegmentBase", indexRange: fmt["init"]) do
|
||||
xml.element("Initialization", range: fmt["index"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xml.element("AdaptationSet", id: 1, mimeType: "video/mp4", subsegmentAlignment: true) do
|
||||
xml.element("Role", schemeIdUri: "urn:mpeg:DASH:role:2011", value: "main")
|
||||
video_streams.each do |fmt|
|
||||
mimetype, codecs = fmt["type"].split(";")
|
||||
codecs = codecs[9..-2]
|
||||
bandwidth = fmt["bitrate"]
|
||||
itag = fmt["itag"]
|
||||
url = fmt["url"]
|
||||
url = url.gsub("?", "/")
|
||||
url = url.gsub("&", "/")
|
||||
url = url.gsub("=", "/")
|
||||
height, width = fmt["size"].split("x")
|
||||
|
||||
xml.element("Representation", id: itag, codecs: codecs, width: width, startWithSAP: "1", maxPlayoutRate: "1",
|
||||
height: height, bandwidth: bandwidth, frameRate: fmt["fps"]) do
|
||||
xml.element("BaseURL") { xml.text url }
|
||||
xml.element("SegmentBase", indexRange: fmt["init"]) do
|
||||
xml.element("Initialization", range: fmt["index"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
manifest = manifest.gsub(%(<?xml version="1.0" encoding="UTF-8U"?>), %(<?xml version="1.0" encoding="UTF-8"?>))
|
||||
manifest = manifest.gsub(%(<?xml version="1.0" encoding="UTF-8V"?>), %(<?xml version="1.0" encoding="UTF-8"?>))
|
||||
manifest
|
||||
end
|
||||
|
||||
options "/videoplayback*" do |env|
|
||||
env.response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
env.response.headers["Access-Control-Allow-Methods"] = "GET"
|
||||
env.response.headers["Access-Control-Allow-Headers"] = "Content-Type, range"
|
||||
end
|
||||
|
||||
get "/videoplayback*" do |env|
|
||||
path = env.request.path
|
||||
if path != "/videoplayback"
|
||||
|
@ -1066,60 +1120,6 @@ get "/videoplayback*" do |env|
|
|||
end
|
||||
end
|
||||
|
||||
get "/user/:user" do |env|
|
||||
user = env.params.url["user"]
|
||||
env.redirect "/channel/#{user}"
|
||||
end
|
||||
|
||||
get "/channel/:ucid" do |env|
|
||||
authorized = env.get? "authorized"
|
||||
if authorized
|
||||
sid = env.get("sid").as(String)
|
||||
|
||||
subscriptions = PG_DB.query_one?("SELECT subscriptions FROM users WHERE id = $1", sid, as: Array(String))
|
||||
end
|
||||
subscriptions ||= [] of String
|
||||
|
||||
ucid = env.params.url["ucid"]
|
||||
|
||||
page = env.params.query["page"]?.try &.to_i
|
||||
page ||= 1
|
||||
|
||||
client = make_client(YT_URL)
|
||||
|
||||
if !ucid.starts_with? "UC"
|
||||
rss = client.get("/feeds/videos.xml?user=#{ucid}").body
|
||||
rss = XML.parse_html(rss)
|
||||
|
||||
ucid = rss.xpath_node("//feed/channelid").not_nil!.content
|
||||
env.redirect "/channel/#{ucid}"
|
||||
end
|
||||
|
||||
url = produce_playlist_url(ucid, (page - 1) * 100)
|
||||
response = client.get(url)
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
document = XML.parse_html(json["content_html"].as_s)
|
||||
author = document.xpath_node(%q(//div[@class="pl-video-owner"]/a)).not_nil!.content
|
||||
|
||||
videos = [] of ChannelVideo
|
||||
document.xpath_nodes(%q(//a[contains(@class,"pl-video-title-link")])).each do |item|
|
||||
href = URI.parse(item["href"])
|
||||
id = HTTP::Params.parse(href.query.not_nil!)["v"]
|
||||
title = item.content
|
||||
|
||||
videos << ChannelVideo.new(id, title, Time.now, Time.now, ucid, author)
|
||||
end
|
||||
|
||||
templated "channel"
|
||||
end
|
||||
|
||||
options "/videoplayback*" do |env|
|
||||
env.response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
env.response.headers["Access-Control-Allow-Methods"] = "GET"
|
||||
env.response.headers["Access-Control-Allow-Headers"] = "Content-Type, range"
|
||||
end
|
||||
|
||||
error 404 do |env|
|
||||
error_message = "404 Page not found"
|
||||
templated "error"
|
||||
|
|
Loading…
Reference in a new issue