forked from midou/invidious
Merge pull request #3261 from SamantazFox/routing-cleanup
This commit is contained in:
commit
0d7e2afba4
298
src/invidious.cr
298
src/invidious.cr
@ -178,305 +178,19 @@ def popular_videos
|
|||||||
Invidious::Jobs::PullPopularVideosJob::POPULAR_VIDEOS.get
|
Invidious::Jobs::PullPopularVideosJob::POPULAR_VIDEOS.get
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Routing
|
||||||
|
|
||||||
before_all do |env|
|
before_all do |env|
|
||||||
preferences = Preferences.from_json("{}")
|
Invidious::Routes::BeforeAll.handle(env)
|
||||||
|
|
||||||
begin
|
|
||||||
if prefs_cookie = env.request.cookies["PREFS"]?
|
|
||||||
preferences = Preferences.from_json(URI.decode_www_form(prefs_cookie.value))
|
|
||||||
else
|
|
||||||
if language_header = env.request.headers["Accept-Language"]?
|
|
||||||
if language = ANG.language_negotiator.best(language_header, LOCALES.keys)
|
|
||||||
preferences.locale = language.header
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
preferences = Preferences.from_json("{}")
|
|
||||||
end
|
|
||||||
|
|
||||||
env.set "preferences", preferences
|
|
||||||
env.response.headers["X-XSS-Protection"] = "1; mode=block"
|
|
||||||
env.response.headers["X-Content-Type-Options"] = "nosniff"
|
|
||||||
|
|
||||||
# Allow media resources to be loaded from google servers
|
|
||||||
# TODO: check if *.youtube.com can be removed
|
|
||||||
if CONFIG.disabled?("local") || !preferences.local
|
|
||||||
extra_media_csp = " https://*.googlevideo.com:443 https://*.youtube.com:443"
|
|
||||||
else
|
|
||||||
extra_media_csp = ""
|
|
||||||
end
|
|
||||||
|
|
||||||
# Only allow the pages at /embed/* to be embedded
|
|
||||||
if env.request.resource.starts_with?("/embed")
|
|
||||||
frame_ancestors = "'self' http: https:"
|
|
||||||
else
|
|
||||||
frame_ancestors = "'none'"
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: Remove style-src's 'unsafe-inline', requires to remove all
|
|
||||||
# inline styles (<style> [..] </style>, style=" [..] ")
|
|
||||||
env.response.headers["Content-Security-Policy"] = {
|
|
||||||
"default-src 'none'",
|
|
||||||
"script-src 'self'",
|
|
||||||
"style-src 'self' 'unsafe-inline'",
|
|
||||||
"img-src 'self' data:",
|
|
||||||
"font-src 'self' data:",
|
|
||||||
"connect-src 'self'",
|
|
||||||
"manifest-src 'self'",
|
|
||||||
"media-src 'self' blob:" + extra_media_csp,
|
|
||||||
"child-src 'self' blob:",
|
|
||||||
"frame-src 'self'",
|
|
||||||
"frame-ancestors " + frame_ancestors,
|
|
||||||
}.join("; ")
|
|
||||||
|
|
||||||
env.response.headers["Referrer-Policy"] = "same-origin"
|
|
||||||
|
|
||||||
# Ask the chrom*-based browsers to disable FLoC
|
|
||||||
# See: https://blog.runcloud.io/google-floc/
|
|
||||||
env.response.headers["Permissions-Policy"] = "interest-cohort=()"
|
|
||||||
|
|
||||||
if (Kemal.config.ssl || CONFIG.https_only) && CONFIG.hsts
|
|
||||||
env.response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload"
|
|
||||||
end
|
|
||||||
|
|
||||||
next if {
|
|
||||||
"/sb/",
|
|
||||||
"/vi/",
|
|
||||||
"/s_p/",
|
|
||||||
"/yts/",
|
|
||||||
"/ggpht/",
|
|
||||||
"/api/manifest/",
|
|
||||||
"/videoplayback",
|
|
||||||
"/latest_version",
|
|
||||||
"/download",
|
|
||||||
}.any? { |r| env.request.resource.starts_with? r }
|
|
||||||
|
|
||||||
if env.request.cookies.has_key? "SID"
|
|
||||||
sid = env.request.cookies["SID"].value
|
|
||||||
|
|
||||||
if sid.starts_with? "v1:"
|
|
||||||
raise "Cannot use token as SID"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Invidious users only have SID
|
|
||||||
if !env.request.cookies.has_key? "SSID"
|
|
||||||
if email = Invidious::Database::SessionIDs.select_email(sid)
|
|
||||||
user = Invidious::Database::Users.select!(email: email)
|
|
||||||
csrf_token = generate_response(sid, {
|
|
||||||
":authorize_token",
|
|
||||||
":playlist_ajax",
|
|
||||||
":signout",
|
|
||||||
":subscription_ajax",
|
|
||||||
":token_ajax",
|
|
||||||
":watch_ajax",
|
|
||||||
}, HMAC_KEY, 1.week)
|
|
||||||
|
|
||||||
preferences = user.preferences
|
|
||||||
env.set "preferences", preferences
|
|
||||||
|
|
||||||
env.set "sid", sid
|
|
||||||
env.set "csrf_token", csrf_token
|
|
||||||
env.set "user", user
|
|
||||||
end
|
|
||||||
else
|
|
||||||
headers = HTTP::Headers.new
|
|
||||||
headers["Cookie"] = env.request.headers["Cookie"]
|
|
||||||
|
|
||||||
begin
|
|
||||||
user, sid = get_user(sid, headers, false)
|
|
||||||
csrf_token = generate_response(sid, {
|
|
||||||
":authorize_token",
|
|
||||||
":playlist_ajax",
|
|
||||||
":signout",
|
|
||||||
":subscription_ajax",
|
|
||||||
":token_ajax",
|
|
||||||
":watch_ajax",
|
|
||||||
}, HMAC_KEY, 1.week)
|
|
||||||
|
|
||||||
preferences = user.preferences
|
|
||||||
env.set "preferences", preferences
|
|
||||||
|
|
||||||
env.set "sid", sid
|
|
||||||
env.set "csrf_token", csrf_token
|
|
||||||
env.set "user", user
|
|
||||||
rescue ex
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
dark_mode = convert_theme(env.params.query["dark_mode"]?) || preferences.dark_mode.to_s
|
|
||||||
thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s
|
|
||||||
thin_mode = thin_mode == "true"
|
|
||||||
locale = env.params.query["hl"]? || preferences.locale
|
|
||||||
|
|
||||||
preferences.dark_mode = dark_mode
|
|
||||||
preferences.thin_mode = thin_mode
|
|
||||||
preferences.locale = locale
|
|
||||||
env.set "preferences", preferences
|
|
||||||
|
|
||||||
current_page = env.request.path
|
|
||||||
if env.request.query
|
|
||||||
query = HTTP::Params.parse(env.request.query.not_nil!)
|
|
||||||
|
|
||||||
if query["referer"]?
|
|
||||||
query["referer"] = get_referer(env, "/")
|
|
||||||
end
|
|
||||||
|
|
||||||
current_page += "?#{query}"
|
|
||||||
end
|
|
||||||
|
|
||||||
env.set "current_page", URI.encode_www_form(current_page)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
{% unless flag?(:api_only) %}
|
Invidious::Routing.register_all
|
||||||
Invidious::Routing.get "/", Invidious::Routes::Misc, :home
|
|
||||||
Invidious::Routing.get "/privacy", Invidious::Routes::Misc, :privacy
|
|
||||||
Invidious::Routing.get "/licenses", Invidious::Routes::Misc, :licenses
|
|
||||||
|
|
||||||
Invidious::Routing.get "/channel/:ucid", Invidious::Routes::Channels, :home
|
|
||||||
Invidious::Routing.get "/channel/:ucid/home", Invidious::Routes::Channels, :home
|
|
||||||
Invidious::Routing.get "/channel/:ucid/videos", Invidious::Routes::Channels, :videos
|
|
||||||
Invidious::Routing.get "/channel/:ucid/playlists", Invidious::Routes::Channels, :playlists
|
|
||||||
Invidious::Routing.get "/channel/:ucid/community", Invidious::Routes::Channels, :community
|
|
||||||
Invidious::Routing.get "/channel/:ucid/about", Invidious::Routes::Channels, :about
|
|
||||||
Invidious::Routing.get "/channel/:ucid/live", Invidious::Routes::Channels, :live
|
|
||||||
Invidious::Routing.get "/user/:user/live", Invidious::Routes::Channels, :live
|
|
||||||
Invidious::Routing.get "/c/:user/live", Invidious::Routes::Channels, :live
|
|
||||||
|
|
||||||
["", "/videos", "/playlists", "/community", "/about"].each do |path|
|
|
||||||
# /c/LinusTechTips
|
|
||||||
Invidious::Routing.get "/c/:user#{path}", Invidious::Routes::Channels, :brand_redirect
|
|
||||||
# /user/linustechtips | Not always the same as /c/
|
|
||||||
Invidious::Routing.get "/user/:user#{path}", Invidious::Routes::Channels, :brand_redirect
|
|
||||||
# /attribution_link?a=anything&u=/channel/UCZYTClx2T1of7BRZ86-8fow
|
|
||||||
Invidious::Routing.get "/attribution_link#{path}", Invidious::Routes::Channels, :brand_redirect
|
|
||||||
# /profile?user=linustechtips
|
|
||||||
Invidious::Routing.get "/profile/#{path}", Invidious::Routes::Channels, :profile
|
|
||||||
end
|
|
||||||
|
|
||||||
Invidious::Routing.get "/watch", Invidious::Routes::Watch, :handle
|
|
||||||
Invidious::Routing.post "/watch_ajax", Invidious::Routes::Watch, :mark_watched
|
|
||||||
Invidious::Routing.get "/watch/:id", Invidious::Routes::Watch, :redirect
|
|
||||||
Invidious::Routing.get "/shorts/:id", Invidious::Routes::Watch, :redirect
|
|
||||||
Invidious::Routing.get "/clip/:clip", Invidious::Routes::Watch, :clip
|
|
||||||
Invidious::Routing.get "/w/:id", Invidious::Routes::Watch, :redirect
|
|
||||||
Invidious::Routing.get "/v/:id", Invidious::Routes::Watch, :redirect
|
|
||||||
Invidious::Routing.get "/e/:id", Invidious::Routes::Watch, :redirect
|
|
||||||
Invidious::Routing.get "/redirect", Invidious::Routes::Misc, :cross_instance_redirect
|
|
||||||
|
|
||||||
Invidious::Routing.post "/download", Invidious::Routes::Watch, :download
|
|
||||||
|
|
||||||
Invidious::Routing.get "/embed/", Invidious::Routes::Embed, :redirect
|
|
||||||
Invidious::Routing.get "/embed/:id", Invidious::Routes::Embed, :show
|
|
||||||
|
|
||||||
Invidious::Routing.get "/create_playlist", Invidious::Routes::Playlists, :new
|
|
||||||
Invidious::Routing.post "/create_playlist", Invidious::Routes::Playlists, :create
|
|
||||||
Invidious::Routing.get "/subscribe_playlist", Invidious::Routes::Playlists, :subscribe
|
|
||||||
Invidious::Routing.get "/delete_playlist", Invidious::Routes::Playlists, :delete_page
|
|
||||||
Invidious::Routing.post "/delete_playlist", Invidious::Routes::Playlists, :delete
|
|
||||||
Invidious::Routing.get "/edit_playlist", Invidious::Routes::Playlists, :edit
|
|
||||||
Invidious::Routing.post "/edit_playlist", Invidious::Routes::Playlists, :update
|
|
||||||
Invidious::Routing.get "/add_playlist_items", Invidious::Routes::Playlists, :add_playlist_items_page
|
|
||||||
Invidious::Routing.post "/playlist_ajax", Invidious::Routes::Playlists, :playlist_ajax
|
|
||||||
Invidious::Routing.get "/playlist", Invidious::Routes::Playlists, :show
|
|
||||||
Invidious::Routing.get "/mix", Invidious::Routes::Playlists, :mix
|
|
||||||
Invidious::Routing.get "/watch_videos", Invidious::Routes::Playlists, :watch_videos
|
|
||||||
|
|
||||||
Invidious::Routing.get "/opensearch.xml", Invidious::Routes::Search, :opensearch
|
|
||||||
Invidious::Routing.get "/results", Invidious::Routes::Search, :results
|
|
||||||
Invidious::Routing.get "/search", Invidious::Routes::Search, :search
|
|
||||||
Invidious::Routing.get "/hashtag/:hashtag", Invidious::Routes::Search, :hashtag
|
|
||||||
|
|
||||||
# User routes
|
|
||||||
define_user_routes()
|
|
||||||
|
|
||||||
# Feeds
|
|
||||||
Invidious::Routing.get "/view_all_playlists", Invidious::Routes::Feeds, :view_all_playlists_redirect
|
|
||||||
Invidious::Routing.get "/feed/playlists", Invidious::Routes::Feeds, :playlists
|
|
||||||
Invidious::Routing.get "/feed/popular", Invidious::Routes::Feeds, :popular
|
|
||||||
Invidious::Routing.get "/feed/trending", Invidious::Routes::Feeds, :trending
|
|
||||||
Invidious::Routing.get "/feed/subscriptions", Invidious::Routes::Feeds, :subscriptions
|
|
||||||
Invidious::Routing.get "/feed/history", Invidious::Routes::Feeds, :history
|
|
||||||
|
|
||||||
# RSS Feeds
|
|
||||||
Invidious::Routing.get "/feed/channel/:ucid", Invidious::Routes::Feeds, :rss_channel
|
|
||||||
Invidious::Routing.get "/feed/private", Invidious::Routes::Feeds, :rss_private
|
|
||||||
Invidious::Routing.get "/feed/playlist/:plid", Invidious::Routes::Feeds, :rss_playlist
|
|
||||||
Invidious::Routing.get "/feeds/videos.xml", Invidious::Routes::Feeds, :rss_videos
|
|
||||||
|
|
||||||
# Support push notifications via PubSubHubbub
|
|
||||||
Invidious::Routing.get "/feed/webhook/:token", Invidious::Routes::Feeds, :push_notifications_get
|
|
||||||
Invidious::Routing.post "/feed/webhook/:token", Invidious::Routes::Feeds, :push_notifications_post
|
|
||||||
|
|
||||||
Invidious::Routing.get "/modify_notifications", Invidious::Routes::Notifications, :modify
|
|
||||||
|
|
||||||
Invidious::Routing.post "/subscription_ajax", Invidious::Routes::Subscriptions, :toggle_subscription
|
|
||||||
Invidious::Routing.get "/subscription_manager", Invidious::Routes::Subscriptions, :subscription_manager
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
Invidious::Routing.get "/ggpht/*", Invidious::Routes::Images, :ggpht
|
|
||||||
Invidious::Routing.options "/sb/:authority/:id/:storyboard/:index", Invidious::Routes::Images, :options_storyboard
|
|
||||||
Invidious::Routing.get "/sb/:authority/:id/:storyboard/:index", Invidious::Routes::Images, :get_storyboard
|
|
||||||
Invidious::Routing.get "/s_p/:id/:name", Invidious::Routes::Images, :s_p_image
|
|
||||||
Invidious::Routing.get "/yts/img/:name", Invidious::Routes::Images, :yts_image
|
|
||||||
Invidious::Routing.get "/vi/:id/:name", Invidious::Routes::Images, :thumbnails
|
|
||||||
|
|
||||||
# API routes (macro)
|
|
||||||
define_v1_api_routes()
|
|
||||||
|
|
||||||
# Video playback (macros)
|
|
||||||
define_api_manifest_routes()
|
|
||||||
define_video_playback_routes()
|
|
||||||
|
|
||||||
error 404 do |env|
|
error 404 do |env|
|
||||||
if md = env.request.path.match(/^\/(?<id>([a-zA-Z0-9_-]{11})|(\w+))$/)
|
Invidious::Routes::ErrorRoutes.error_404(env)
|
||||||
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"] = "/"
|
|
||||||
halt 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}"
|
|
||||||
halt 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
|
|
||||||
halt env, status_code: 302
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
env.response.headers["Location"] = "/"
|
|
||||||
halt env, status_code: 302
|
|
||||||
end
|
end
|
||||||
|
|
||||||
error 500 do |env, ex|
|
error 500 do |env, ex|
|
||||||
locale = env.get("preferences").as(Preferences).locale
|
|
||||||
error_template(500, ex)
|
error_template(500, ex)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -484,6 +198,8 @@ static_headers do |response|
|
|||||||
response.headers.add("Cache-Control", "max-age=2629800")
|
response.headers.add("Cache-Control", "max-age=2629800")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Init Kemal
|
||||||
|
|
||||||
public_folder "assets"
|
public_folder "assets"
|
||||||
|
|
||||||
Kemal.config.powered_by_header = false
|
Kemal.config.powered_by_header = false
|
||||||
|
152
src/invidious/routes/before_all.cr
Normal file
152
src/invidious/routes/before_all.cr
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
module Invidious::Routes::BeforeAll
|
||||||
|
def self.handle(env)
|
||||||
|
preferences = Preferences.from_json("{}")
|
||||||
|
|
||||||
|
begin
|
||||||
|
if prefs_cookie = env.request.cookies["PREFS"]?
|
||||||
|
preferences = Preferences.from_json(URI.decode_www_form(prefs_cookie.value))
|
||||||
|
else
|
||||||
|
if language_header = env.request.headers["Accept-Language"]?
|
||||||
|
if language = ANG.language_negotiator.best(language_header, LOCALES.keys)
|
||||||
|
preferences.locale = language.header
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
preferences = Preferences.from_json("{}")
|
||||||
|
end
|
||||||
|
|
||||||
|
env.set "preferences", preferences
|
||||||
|
env.response.headers["X-XSS-Protection"] = "1; mode=block"
|
||||||
|
env.response.headers["X-Content-Type-Options"] = "nosniff"
|
||||||
|
|
||||||
|
# Allow media resources to be loaded from google servers
|
||||||
|
# TODO: check if *.youtube.com can be removed
|
||||||
|
if CONFIG.disabled?("local") || !preferences.local
|
||||||
|
extra_media_csp = " https://*.googlevideo.com:443 https://*.youtube.com:443"
|
||||||
|
else
|
||||||
|
extra_media_csp = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
# Only allow the pages at /embed/* to be embedded
|
||||||
|
if env.request.resource.starts_with?("/embed")
|
||||||
|
frame_ancestors = "'self' http: https:"
|
||||||
|
else
|
||||||
|
frame_ancestors = "'none'"
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: Remove style-src's 'unsafe-inline', requires to remove all
|
||||||
|
# inline styles (<style> [..] </style>, style=" [..] ")
|
||||||
|
env.response.headers["Content-Security-Policy"] = {
|
||||||
|
"default-src 'none'",
|
||||||
|
"script-src 'self'",
|
||||||
|
"style-src 'self' 'unsafe-inline'",
|
||||||
|
"img-src 'self' data:",
|
||||||
|
"font-src 'self' data:",
|
||||||
|
"connect-src 'self'",
|
||||||
|
"manifest-src 'self'",
|
||||||
|
"media-src 'self' blob:" + extra_media_csp,
|
||||||
|
"child-src 'self' blob:",
|
||||||
|
"frame-src 'self'",
|
||||||
|
"frame-ancestors " + frame_ancestors,
|
||||||
|
}.join("; ")
|
||||||
|
|
||||||
|
env.response.headers["Referrer-Policy"] = "same-origin"
|
||||||
|
|
||||||
|
# Ask the chrom*-based browsers to disable FLoC
|
||||||
|
# See: https://blog.runcloud.io/google-floc/
|
||||||
|
env.response.headers["Permissions-Policy"] = "interest-cohort=()"
|
||||||
|
|
||||||
|
if (Kemal.config.ssl || CONFIG.https_only) && CONFIG.hsts
|
||||||
|
env.response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload"
|
||||||
|
end
|
||||||
|
|
||||||
|
return if {
|
||||||
|
"/sb/",
|
||||||
|
"/vi/",
|
||||||
|
"/s_p/",
|
||||||
|
"/yts/",
|
||||||
|
"/ggpht/",
|
||||||
|
"/api/manifest/",
|
||||||
|
"/videoplayback",
|
||||||
|
"/latest_version",
|
||||||
|
"/download",
|
||||||
|
}.any? { |r| env.request.resource.starts_with? r }
|
||||||
|
|
||||||
|
if env.request.cookies.has_key? "SID"
|
||||||
|
sid = env.request.cookies["SID"].value
|
||||||
|
|
||||||
|
if sid.starts_with? "v1:"
|
||||||
|
raise "Cannot use token as SID"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Invidious users only have SID
|
||||||
|
if !env.request.cookies.has_key? "SSID"
|
||||||
|
if email = Invidious::Database::SessionIDs.select_email(sid)
|
||||||
|
user = Invidious::Database::Users.select!(email: email)
|
||||||
|
csrf_token = generate_response(sid, {
|
||||||
|
":authorize_token",
|
||||||
|
":playlist_ajax",
|
||||||
|
":signout",
|
||||||
|
":subscription_ajax",
|
||||||
|
":token_ajax",
|
||||||
|
":watch_ajax",
|
||||||
|
}, HMAC_KEY, 1.week)
|
||||||
|
|
||||||
|
preferences = user.preferences
|
||||||
|
env.set "preferences", preferences
|
||||||
|
|
||||||
|
env.set "sid", sid
|
||||||
|
env.set "csrf_token", csrf_token
|
||||||
|
env.set "user", user
|
||||||
|
end
|
||||||
|
else
|
||||||
|
headers = HTTP::Headers.new
|
||||||
|
headers["Cookie"] = env.request.headers["Cookie"]
|
||||||
|
|
||||||
|
begin
|
||||||
|
user, sid = get_user(sid, headers, false)
|
||||||
|
csrf_token = generate_response(sid, {
|
||||||
|
":authorize_token",
|
||||||
|
":playlist_ajax",
|
||||||
|
":signout",
|
||||||
|
":subscription_ajax",
|
||||||
|
":token_ajax",
|
||||||
|
":watch_ajax",
|
||||||
|
}, HMAC_KEY, 1.week)
|
||||||
|
|
||||||
|
preferences = user.preferences
|
||||||
|
env.set "preferences", preferences
|
||||||
|
|
||||||
|
env.set "sid", sid
|
||||||
|
env.set "csrf_token", csrf_token
|
||||||
|
env.set "user", user
|
||||||
|
rescue ex
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
dark_mode = convert_theme(env.params.query["dark_mode"]?) || preferences.dark_mode.to_s
|
||||||
|
thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s
|
||||||
|
thin_mode = thin_mode == "true"
|
||||||
|
locale = env.params.query["hl"]? || preferences.locale
|
||||||
|
|
||||||
|
preferences.dark_mode = dark_mode
|
||||||
|
preferences.thin_mode = thin_mode
|
||||||
|
preferences.locale = locale
|
||||||
|
env.set "preferences", preferences
|
||||||
|
|
||||||
|
current_page = env.request.path
|
||||||
|
if env.request.query
|
||||||
|
query = HTTP::Params.parse(env.request.query.not_nil!)
|
||||||
|
|
||||||
|
if query["referer"]?
|
||||||
|
query["referer"] = get_referer(env, "/")
|
||||||
|
end
|
||||||
|
|
||||||
|
current_page += "?#{query}"
|
||||||
|
end
|
||||||
|
|
||||||
|
env.set "current_page", URI.encode_www_form(current_page)
|
||||||
|
end
|
||||||
|
end
|
47
src/invidious/routes/errors.cr
Normal file
47
src/invidious/routes/errors.cr
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
module Invidious::Routes::ErrorRoutes
|
||||||
|
def self.error_404(env)
|
||||||
|
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
|
@ -1,130 +1,273 @@
|
|||||||
module Invidious::Routing
|
module Invidious::Routing
|
||||||
{% for http_method in {"get", "post", "delete", "options", "patch", "put", "head"} %}
|
extend self
|
||||||
|
|
||||||
|
{% for http_method in {"get", "post", "delete", "options", "patch", "put"} %}
|
||||||
|
|
||||||
macro {{http_method.id}}(path, controller, method = :handle)
|
macro {{http_method.id}}(path, controller, method = :handle)
|
||||||
{{http_method.id}} \{{ path }} do |env|
|
unless !Kemal::Utils.path_starts_with_slash?(\{{path}})
|
||||||
|
raise Kemal::Exceptions::InvalidPathStartException.new({{http_method}}, \{{path}})
|
||||||
|
end
|
||||||
|
|
||||||
|
Kemal::RouteHandler::INSTANCE.add_route({{http_method.upcase}}, \{{path}}) do |env|
|
||||||
\{{ controller }}.\{{ method.id }}(env)
|
\{{ controller }}.\{{ method.id }}(env)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
|
||||||
|
def register_all
|
||||||
macro define_user_routes
|
{% unless flag?(:api_only) %}
|
||||||
# User login/out
|
get "/", Routes::Misc, :home
|
||||||
Invidious::Routing.get "/login", Invidious::Routes::Login, :login_page
|
get "/privacy", Routes::Misc, :privacy
|
||||||
Invidious::Routing.post "/login", Invidious::Routes::Login, :login
|
get "/licenses", Routes::Misc, :licenses
|
||||||
Invidious::Routing.post "/signout", Invidious::Routes::Login, :signout
|
get "/redirect", Routes::Misc, :cross_instance_redirect
|
||||||
Invidious::Routing.get "/Captcha", Invidious::Routes::Login, :captcha
|
|
||||||
|
self.register_channel_routes
|
||||||
# User preferences
|
self.register_watch_routes
|
||||||
Invidious::Routing.get "/preferences", Invidious::Routes::PreferencesRoute, :show
|
|
||||||
Invidious::Routing.post "/preferences", Invidious::Routes::PreferencesRoute, :update
|
self.register_iv_playlist_routes
|
||||||
Invidious::Routing.get "/toggle_theme", Invidious::Routes::PreferencesRoute, :toggle_theme
|
self.register_yt_playlist_routes
|
||||||
Invidious::Routing.get "/data_control", Invidious::Routes::PreferencesRoute, :data_control
|
|
||||||
Invidious::Routing.post "/data_control", Invidious::Routes::PreferencesRoute, :update_data_control
|
self.register_search_routes
|
||||||
|
|
||||||
# User account management
|
self.register_user_routes
|
||||||
Invidious::Routing.get "/change_password", Invidious::Routes::Account, :get_change_password
|
self.register_feed_routes
|
||||||
Invidious::Routing.post "/change_password", Invidious::Routes::Account, :post_change_password
|
|
||||||
Invidious::Routing.get "/delete_account", Invidious::Routes::Account, :get_delete
|
# Support push notifications via PubSubHubbub
|
||||||
Invidious::Routing.post "/delete_account", Invidious::Routes::Account, :post_delete
|
get "/feed/webhook/:token", Routes::Feeds, :push_notifications_get
|
||||||
Invidious::Routing.get "/clear_watch_history", Invidious::Routes::Account, :get_clear_history
|
post "/feed/webhook/:token", Routes::Feeds, :push_notifications_post
|
||||||
Invidious::Routing.post "/clear_watch_history", Invidious::Routes::Account, :post_clear_history
|
|
||||||
Invidious::Routing.get "/authorize_token", Invidious::Routes::Account, :get_authorize_token
|
get "/modify_notifications", Routes::Notifications, :modify
|
||||||
Invidious::Routing.post "/authorize_token", Invidious::Routes::Account, :post_authorize_token
|
{% end %}
|
||||||
Invidious::Routing.get "/token_manager", Invidious::Routes::Account, :token_manager
|
|
||||||
Invidious::Routing.post "/token_ajax", Invidious::Routes::Account, :token_ajax
|
self.register_image_routes
|
||||||
end
|
self.register_api_v1_routes
|
||||||
|
self.register_api_manifest_routes
|
||||||
macro define_v1_api_routes
|
self.register_video_playback_routes
|
||||||
{{namespace = Invidious::Routes::API::V1}}
|
end
|
||||||
# Videos
|
|
||||||
Invidious::Routing.get "/api/v1/videos/:id", {{namespace}}::Videos, :videos
|
# -------------------
|
||||||
Invidious::Routing.get "/api/v1/storyboards/:id", {{namespace}}::Videos, :storyboards
|
# Invidious routes
|
||||||
Invidious::Routing.get "/api/v1/captions/:id", {{namespace}}::Videos, :captions
|
# -------------------
|
||||||
Invidious::Routing.get "/api/v1/annotations/:id", {{namespace}}::Videos, :annotations
|
|
||||||
Invidious::Routing.get "/api/v1/comments/:id", {{namespace}}::Videos, :comments
|
def register_user_routes
|
||||||
|
# User login/out
|
||||||
# Feeds
|
get "/login", Routes::Login, :login_page
|
||||||
Invidious::Routing.get "/api/v1/trending", {{namespace}}::Feeds, :trending
|
post "/login", Routes::Login, :login
|
||||||
Invidious::Routing.get "/api/v1/popular", {{namespace}}::Feeds, :popular
|
post "/signout", Routes::Login, :signout
|
||||||
|
get "/Captcha", Routes::Login, :captcha
|
||||||
# Channels
|
|
||||||
Invidious::Routing.get "/api/v1/channels/:ucid", {{namespace}}::Channels, :home
|
# User preferences
|
||||||
{% for route in {"videos", "latest", "playlists", "community", "search"} %}
|
get "/preferences", Routes::PreferencesRoute, :show
|
||||||
Invidious::Routing.get "/api/v1/channels/#{{{route}}}/:ucid", {{namespace}}::Channels, :{{route}}
|
post "/preferences", Routes::PreferencesRoute, :update
|
||||||
Invidious::Routing.get "/api/v1/channels/:ucid/#{{{route}}}", {{namespace}}::Channels, :{{route}}
|
get "/toggle_theme", Routes::PreferencesRoute, :toggle_theme
|
||||||
{% end %}
|
get "/data_control", Routes::PreferencesRoute, :data_control
|
||||||
|
post "/data_control", Routes::PreferencesRoute, :update_data_control
|
||||||
# 301 redirects to new /api/v1/channels/community/:ucid and /:ucid/community
|
|
||||||
Invidious::Routing.get "/api/v1/channels/comments/:ucid", {{namespace}}::Channels, :channel_comments_redirect
|
# User account management
|
||||||
Invidious::Routing.get "/api/v1/channels/:ucid/comments", {{namespace}}::Channels, :channel_comments_redirect
|
get "/change_password", Routes::Account, :get_change_password
|
||||||
|
post "/change_password", Routes::Account, :post_change_password
|
||||||
|
get "/delete_account", Routes::Account, :get_delete
|
||||||
# Search
|
post "/delete_account", Routes::Account, :post_delete
|
||||||
Invidious::Routing.get "/api/v1/search", {{namespace}}::Search, :search
|
get "/clear_watch_history", Routes::Account, :get_clear_history
|
||||||
Invidious::Routing.get "/api/v1/search/suggestions", {{namespace}}::Search, :search_suggestions
|
post "/clear_watch_history", Routes::Account, :post_clear_history
|
||||||
|
get "/authorize_token", Routes::Account, :get_authorize_token
|
||||||
# Authenticated
|
post "/authorize_token", Routes::Account, :post_authorize_token
|
||||||
|
get "/token_manager", Routes::Account, :token_manager
|
||||||
# The notification APIs cannot be extracted yet! They require the *local* notifications constant defined in invidious.cr
|
post "/token_ajax", Routes::Account, :token_ajax
|
||||||
#
|
post "/subscription_ajax", Routes::Subscriptions, :toggle_subscription
|
||||||
# Invidious::Routing.get "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications
|
get "/subscription_manager", Routes::Subscriptions, :subscription_manager
|
||||||
# Invidious::Routing.post "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications
|
end
|
||||||
|
|
||||||
Invidious::Routing.get "/api/v1/auth/preferences", {{namespace}}::Authenticated, :get_preferences
|
def register_iv_playlist_routes
|
||||||
Invidious::Routing.post "/api/v1/auth/preferences", {{namespace}}::Authenticated, :set_preferences
|
get "/create_playlist", Routes::Playlists, :new
|
||||||
|
post "/create_playlist", Routes::Playlists, :create
|
||||||
Invidious::Routing.get "/api/v1/auth/feed", {{namespace}}::Authenticated, :feed
|
get "/subscribe_playlist", Routes::Playlists, :subscribe
|
||||||
|
get "/delete_playlist", Routes::Playlists, :delete_page
|
||||||
Invidious::Routing.get "/api/v1/auth/subscriptions", {{namespace}}::Authenticated, :get_subscriptions
|
post "/delete_playlist", Routes::Playlists, :delete
|
||||||
Invidious::Routing.post "/api/v1/auth/subscriptions/:ucid", {{namespace}}::Authenticated, :subscribe_channel
|
get "/edit_playlist", Routes::Playlists, :edit
|
||||||
Invidious::Routing.delete "/api/v1/auth/subscriptions/:ucid", {{namespace}}::Authenticated, :unsubscribe_channel
|
post "/edit_playlist", Routes::Playlists, :update
|
||||||
|
get "/add_playlist_items", Routes::Playlists, :add_playlist_items_page
|
||||||
|
post "/playlist_ajax", Routes::Playlists, :playlist_ajax
|
||||||
Invidious::Routing.get "/api/v1/auth/playlists", {{namespace}}::Authenticated, :list_playlists
|
end
|
||||||
Invidious::Routing.post "/api/v1/auth/playlists", {{namespace}}::Authenticated, :create_playlist
|
|
||||||
Invidious::Routing.patch "/api/v1/auth/playlists/:plid",{{namespace}}:: Authenticated, :update_playlist_attribute
|
def register_feed_routes
|
||||||
Invidious::Routing.delete "/api/v1/auth/playlists/:plid", {{namespace}}::Authenticated, :delete_playlist
|
# Feeds
|
||||||
|
get "/view_all_playlists", Routes::Feeds, :view_all_playlists_redirect
|
||||||
|
get "/feed/playlists", Routes::Feeds, :playlists
|
||||||
Invidious::Routing.post "/api/v1/auth/playlists/:plid/videos", {{namespace}}::Authenticated, :insert_video_into_playlist
|
get "/feed/popular", Routes::Feeds, :popular
|
||||||
Invidious::Routing.delete "/api/v1/auth/playlists/:plid/videos/:index", {{namespace}}::Authenticated, :delete_video_in_playlist
|
get "/feed/trending", Routes::Feeds, :trending
|
||||||
|
get "/feed/subscriptions", Routes::Feeds, :subscriptions
|
||||||
Invidious::Routing.get "/api/v1/auth/tokens", {{namespace}}::Authenticated, :get_tokens
|
get "/feed/history", Routes::Feeds, :history
|
||||||
Invidious::Routing.post "/api/v1/auth/tokens/register", {{namespace}}::Authenticated, :register_token
|
|
||||||
Invidious::Routing.post "/api/v1/auth/tokens/unregister", {{namespace}}::Authenticated, :unregister_token
|
# RSS Feeds
|
||||||
|
get "/feed/channel/:ucid", Routes::Feeds, :rss_channel
|
||||||
Invidious::Routing.get "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications
|
get "/feed/private", Routes::Feeds, :rss_private
|
||||||
Invidious::Routing.post "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications
|
get "/feed/playlist/:plid", Routes::Feeds, :rss_playlist
|
||||||
|
get "/feeds/videos.xml", Routes::Feeds, :rss_videos
|
||||||
# Misc
|
end
|
||||||
Invidious::Routing.get "/api/v1/stats", {{namespace}}::Misc, :stats
|
|
||||||
Invidious::Routing.get "/api/v1/playlists/:plid", {{namespace}}::Misc, :get_playlist
|
# -------------------
|
||||||
Invidious::Routing.get "/api/v1/auth/playlists/:plid", {{namespace}}::Misc, :get_playlist
|
# Youtube routes
|
||||||
Invidious::Routing.get "/api/v1/mixes/:rdid", {{namespace}}::Misc, :mixes
|
# -------------------
|
||||||
end
|
|
||||||
|
def register_channel_routes
|
||||||
macro define_api_manifest_routes
|
get "/channel/:ucid", Routes::Channels, :home
|
||||||
Invidious::Routing.get "/api/manifest/dash/id/:id", Invidious::Routes::API::Manifest, :get_dash_video_id
|
get "/channel/:ucid/home", Routes::Channels, :home
|
||||||
|
get "/channel/:ucid/videos", Routes::Channels, :videos
|
||||||
Invidious::Routing.get "/api/manifest/dash/id/videoplayback", Invidious::Routes::API::Manifest, :get_dash_video_playback
|
get "/channel/:ucid/playlists", Routes::Channels, :playlists
|
||||||
Invidious::Routing.get "/api/manifest/dash/id/videoplayback/*", Invidious::Routes::API::Manifest, :get_dash_video_playback_greedy
|
get "/channel/:ucid/community", Routes::Channels, :community
|
||||||
|
get "/channel/:ucid/about", Routes::Channels, :about
|
||||||
Invidious::Routing.options "/api/manifest/dash/id/videoplayback", Invidious::Routes::API::Manifest, :options_dash_video_playback
|
get "/channel/:ucid/live", Routes::Channels, :live
|
||||||
Invidious::Routing.options "/api/manifest/dash/id/videoplayback/*", Invidious::Routes::API::Manifest, :options_dash_video_playback
|
get "/user/:user/live", Routes::Channels, :live
|
||||||
|
get "/c/:user/live", Routes::Channels, :live
|
||||||
Invidious::Routing.get "/api/manifest/hls_playlist/*", Invidious::Routes::API::Manifest, :get_hls_playlist
|
|
||||||
Invidious::Routing.get "/api/manifest/hls_variant/*", Invidious::Routes::API::Manifest, :get_hls_variant
|
["", "/videos", "/playlists", "/community", "/about"].each do |path|
|
||||||
end
|
# /c/LinusTechTips
|
||||||
|
get "/c/:user#{path}", Routes::Channels, :brand_redirect
|
||||||
macro define_video_playback_routes
|
# /user/linustechtips | Not always the same as /c/
|
||||||
Invidious::Routing.get "/videoplayback", Invidious::Routes::VideoPlayback, :get_video_playback
|
get "/user/:user#{path}", Routes::Channels, :brand_redirect
|
||||||
Invidious::Routing.get "/videoplayback/*", Invidious::Routes::VideoPlayback, :get_video_playback_greedy
|
# /attribution_link?a=anything&u=/channel/UCZYTClx2T1of7BRZ86-8fow
|
||||||
|
get "/attribution_link#{path}", Routes::Channels, :brand_redirect
|
||||||
Invidious::Routing.options "/videoplayback", Invidious::Routes::VideoPlayback, :options_video_playback
|
# /profile?user=linustechtips
|
||||||
Invidious::Routing.options "/videoplayback/*", Invidious::Routes::VideoPlayback, :options_video_playback
|
get "/profile/#{path}", Routes::Channels, :profile
|
||||||
|
end
|
||||||
Invidious::Routing.get "/latest_version", Invidious::Routes::VideoPlayback, :latest_version
|
end
|
||||||
|
|
||||||
|
def register_watch_routes
|
||||||
|
get "/watch", Routes::Watch, :handle
|
||||||
|
post "/watch_ajax", Routes::Watch, :mark_watched
|
||||||
|
get "/watch/:id", Routes::Watch, :redirect
|
||||||
|
get "/shorts/:id", Routes::Watch, :redirect
|
||||||
|
get "/clip/:clip", Routes::Watch, :clip
|
||||||
|
get "/w/:id", Routes::Watch, :redirect
|
||||||
|
get "/v/:id", Routes::Watch, :redirect
|
||||||
|
get "/e/:id", Routes::Watch, :redirect
|
||||||
|
|
||||||
|
post "/download", Routes::Watch, :download
|
||||||
|
|
||||||
|
get "/embed/", Routes::Embed, :redirect
|
||||||
|
get "/embed/:id", Routes::Embed, :show
|
||||||
|
end
|
||||||
|
|
||||||
|
def register_yt_playlist_routes
|
||||||
|
get "/playlist", Routes::Playlists, :show
|
||||||
|
get "/mix", Routes::Playlists, :mix
|
||||||
|
get "/watch_videos", Routes::Playlists, :watch_videos
|
||||||
|
end
|
||||||
|
|
||||||
|
def register_search_routes
|
||||||
|
get "/opensearch.xml", Routes::Search, :opensearch
|
||||||
|
get "/results", Routes::Search, :results
|
||||||
|
get "/search", Routes::Search, :search
|
||||||
|
get "/hashtag/:hashtag", Routes::Search, :hashtag
|
||||||
|
end
|
||||||
|
|
||||||
|
# -------------------
|
||||||
|
# Media proxy routes
|
||||||
|
# -------------------
|
||||||
|
|
||||||
|
def register_api_manifest_routes
|
||||||
|
get "/api/manifest/dash/id/:id", Routes::API::Manifest, :get_dash_video_id
|
||||||
|
|
||||||
|
get "/api/manifest/dash/id/videoplayback", Routes::API::Manifest, :get_dash_video_playback
|
||||||
|
get "/api/manifest/dash/id/videoplayback/*", Routes::API::Manifest, :get_dash_video_playback_greedy
|
||||||
|
|
||||||
|
options "/api/manifest/dash/id/videoplayback", Routes::API::Manifest, :options_dash_video_playback
|
||||||
|
options "/api/manifest/dash/id/videoplayback/*", Routes::API::Manifest, :options_dash_video_playback
|
||||||
|
|
||||||
|
get "/api/manifest/hls_playlist/*", Routes::API::Manifest, :get_hls_playlist
|
||||||
|
get "/api/manifest/hls_variant/*", Routes::API::Manifest, :get_hls_variant
|
||||||
|
end
|
||||||
|
|
||||||
|
def register_video_playback_routes
|
||||||
|
get "/videoplayback", Routes::VideoPlayback, :get_video_playback
|
||||||
|
get "/videoplayback/*", Routes::VideoPlayback, :get_video_playback_greedy
|
||||||
|
|
||||||
|
options "/videoplayback", Routes::VideoPlayback, :options_video_playback
|
||||||
|
options "/videoplayback/*", Routes::VideoPlayback, :options_video_playback
|
||||||
|
|
||||||
|
get "/latest_version", Routes::VideoPlayback, :latest_version
|
||||||
|
end
|
||||||
|
|
||||||
|
def register_image_routes
|
||||||
|
get "/ggpht/*", Routes::Images, :ggpht
|
||||||
|
options "/sb/:authority/:id/:storyboard/:index", Routes::Images, :options_storyboard
|
||||||
|
get "/sb/:authority/:id/:storyboard/:index", Routes::Images, :get_storyboard
|
||||||
|
get "/s_p/:id/:name", Routes::Images, :s_p_image
|
||||||
|
get "/yts/img/:name", Routes::Images, :yts_image
|
||||||
|
get "/vi/:id/:name", Routes::Images, :thumbnails
|
||||||
|
end
|
||||||
|
|
||||||
|
# -------------------
|
||||||
|
# API routes
|
||||||
|
# -------------------
|
||||||
|
|
||||||
|
def register_api_v1_routes
|
||||||
|
{% begin %}
|
||||||
|
{{namespace = Routes::API::V1}}
|
||||||
|
|
||||||
|
# Videos
|
||||||
|
get "/api/v1/videos/:id", {{namespace}}::Videos, :videos
|
||||||
|
get "/api/v1/storyboards/:id", {{namespace}}::Videos, :storyboards
|
||||||
|
get "/api/v1/captions/:id", {{namespace}}::Videos, :captions
|
||||||
|
get "/api/v1/annotations/:id", {{namespace}}::Videos, :annotations
|
||||||
|
get "/api/v1/comments/:id", {{namespace}}::Videos, :comments
|
||||||
|
|
||||||
|
# Feeds
|
||||||
|
get "/api/v1/trending", {{namespace}}::Feeds, :trending
|
||||||
|
get "/api/v1/popular", {{namespace}}::Feeds, :popular
|
||||||
|
|
||||||
|
# Channels
|
||||||
|
get "/api/v1/channels/:ucid", {{namespace}}::Channels, :home
|
||||||
|
{% for route in {"videos", "latest", "playlists", "community", "search"} %}
|
||||||
|
get "/api/v1/channels/#{{{route}}}/:ucid", {{namespace}}::Channels, :{{route}}
|
||||||
|
get "/api/v1/channels/:ucid/#{{{route}}}", {{namespace}}::Channels, :{{route}}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
# 301 redirects to new /api/v1/channels/community/:ucid and /:ucid/community
|
||||||
|
get "/api/v1/channels/comments/:ucid", {{namespace}}::Channels, :channel_comments_redirect
|
||||||
|
get "/api/v1/channels/:ucid/comments", {{namespace}}::Channels, :channel_comments_redirect
|
||||||
|
|
||||||
|
# Search
|
||||||
|
get "/api/v1/search", {{namespace}}::Search, :search
|
||||||
|
get "/api/v1/search/suggestions", {{namespace}}::Search, :search_suggestions
|
||||||
|
|
||||||
|
# Authenticated
|
||||||
|
|
||||||
|
# The notification APIs cannot be extracted yet! They require the *local* notifications constant defined in invidious.cr
|
||||||
|
#
|
||||||
|
# Invidious::Routing.get "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications
|
||||||
|
# Invidious::Routing.post "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications
|
||||||
|
|
||||||
|
get "/api/v1/auth/preferences", {{namespace}}::Authenticated, :get_preferences
|
||||||
|
post "/api/v1/auth/preferences", {{namespace}}::Authenticated, :set_preferences
|
||||||
|
|
||||||
|
get "/api/v1/auth/feed", {{namespace}}::Authenticated, :feed
|
||||||
|
|
||||||
|
get "/api/v1/auth/subscriptions", {{namespace}}::Authenticated, :get_subscriptions
|
||||||
|
post "/api/v1/auth/subscriptions/:ucid", {{namespace}}::Authenticated, :subscribe_channel
|
||||||
|
delete "/api/v1/auth/subscriptions/:ucid", {{namespace}}::Authenticated, :unsubscribe_channel
|
||||||
|
|
||||||
|
get "/api/v1/auth/playlists", {{namespace}}::Authenticated, :list_playlists
|
||||||
|
post "/api/v1/auth/playlists", {{namespace}}::Authenticated, :create_playlist
|
||||||
|
patch "/api/v1/auth/playlists/:plid",{{namespace}}:: Authenticated, :update_playlist_attribute
|
||||||
|
delete "/api/v1/auth/playlists/:plid", {{namespace}}::Authenticated, :delete_playlist
|
||||||
|
post "/api/v1/auth/playlists/:plid/videos", {{namespace}}::Authenticated, :insert_video_into_playlist
|
||||||
|
delete "/api/v1/auth/playlists/:plid/videos/:index", {{namespace}}::Authenticated, :delete_video_in_playlist
|
||||||
|
|
||||||
|
get "/api/v1/auth/tokens", {{namespace}}::Authenticated, :get_tokens
|
||||||
|
post "/api/v1/auth/tokens/register", {{namespace}}::Authenticated, :register_token
|
||||||
|
post "/api/v1/auth/tokens/unregister", {{namespace}}::Authenticated, :unregister_token
|
||||||
|
|
||||||
|
get "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications
|
||||||
|
post "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
get "/api/v1/stats", {{namespace}}::Misc, :stats
|
||||||
|
get "/api/v1/playlists/:plid", {{namespace}}::Misc, :get_playlist
|
||||||
|
get "/api/v1/auth/playlists/:plid", {{namespace}}::Misc, :get_playlist
|
||||||
|
get "/api/v1/mixes/:rdid", {{namespace}}::Misc, :mixes
|
||||||
|
{% end %}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user