mirror of
https://github.com/iv-org/invidious.git
synced 2025-07-09 21:55:47 -05:00
Crystal 1.16.0 fixed the parsing of some weird-looking paths meaning that `//api/v1/storyboards/sb/i/...` will no longer get parsed into a request target of `/v1/storyboards/sb/i/...` within requests. But the fix is a bit more convoluted than just changing a path. The new presence of double slash means that `StaticFileHandler` will process and redirect the request to a normalized path with only a single slash. This redirect however does not preserve url query parameters which are needed to request storyboards from YouTube. Then there's the fact that the status code error handlers are disabled for the API routes, meaning that there is no handler to actually process the request with. This PR patches Kemal's `StaticFileHandler` to preserve query parameters when redirecting, and adds the `/api/v1/storyboard/sb/*` -> `/sb/i/*/` redirect directly into Kemal's `ExceptionHandler` for now. In the future a dedicated handler should be created to properly handle http status code conditions on the API routes.
64 lines
2.0 KiB
Crystal
64 lines
2.0 KiB
Crystal
module Invidious::Routes::ErrorRoutes
|
|
def self.error_404(env)
|
|
# Workaround for #3117 on versions prior to 1.16.0
|
|
#
|
|
# Crystal 1.16.0 fixed the parsing of some weird-looking paths
|
|
# meaning that `//api/v1/storyboards/sb/i/...` will no longer
|
|
# get parsed into a request target of `/v1/storyboards/sb/i/...`
|
|
#
|
|
# This also means that we won't be able to handle the logic here
|
|
# because status code error handles are disabled for the API routes
|
|
|
|
# We only need to include this workaround on versions prior to 1.16.0
|
|
{% if compare_versions(Crystal::VERSION, "1.16.0") < 0 %}
|
|
if HOST_URL.empty? && env.request.path.starts_with?("/v1/storyboards/sb")
|
|
return env.redirect "#{env.request.path[15..]}?#{env.params.query}"
|
|
end
|
|
{% end %}
|
|
|
|
if md = env.request.path.match(/^\/(?<id>([a-zA-Z0-9_-]{11})|(\w+))$/)
|
|
item = md["id"]
|
|
|
|
# Check if item is branding URL e.g. https://youtube.com/gaming
|
|
response = YT_POOL.client &.get("/#{item}")
|
|
|
|
if response.status_code == 301
|
|
response = YT_POOL.client &.get(URI.parse(response.headers["Location"]).request_target)
|
|
end
|
|
|
|
if response.body.empty?
|
|
env.response.headers["Location"] = "/"
|
|
haltf env, status_code: 302
|
|
end
|
|
|
|
html = XML.parse_html(response.body)
|
|
ucid = html.xpath_node(%q(//link[@rel="canonical"])).try &.["href"].split("/")[-1]
|
|
|
|
if ucid
|
|
env.response.headers["Location"] = "/channel/#{ucid}"
|
|
haltf env, status_code: 302
|
|
end
|
|
|
|
params = [] of String
|
|
env.params.query.each do |k, v|
|
|
params << "#{k}=#{v}"
|
|
end
|
|
params = params.join("&")
|
|
|
|
url = "/watch?v=#{item}"
|
|
if !params.empty?
|
|
url += "&#{params}"
|
|
end
|
|
|
|
# Check if item is video ID
|
|
if item.match(/^[a-zA-Z0-9_-]{11}$/) && YT_POOL.client &.head("/watch?v=#{item}").status_code != 404
|
|
env.response.headers["Location"] = url
|
|
haltf env, status_code: 302
|
|
end
|
|
end
|
|
|
|
env.response.headers["Location"] = "/"
|
|
haltf env, status_code: 302
|
|
end
|
|
end
|