From 66b45a8fe2dde8eafa88db8b3077dad7305c068e Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 23 Aug 2021 16:28:30 -0700 Subject: [PATCH] Bountiful changes - Use haltf in more locations - Fix wrong URL params - Rename API modules - Remove API routing file and move everything to general iv routing file --- src/invidious/routes/api/manifest.cr | 15 +--- src/invidious/routes/api/v1/authenticated.cr | 5 +- src/invidious/routes/api/v1/channels.cr | 15 +++- src/invidious/routes/api/v1/feeds.cr | 5 +- src/invidious/routes/api/v1/misc.cr | 2 +- src/invidious/routes/api/v1/routes.cr | 67 --------------- src/invidious/routes/api/v1/search.cr | 2 +- src/invidious/routes/api/v1/videos.cr | 31 +++---- src/invidious/routes/video_playback.cr | 10 --- src/invidious/routing.cr | 89 ++++++++++++++++++++ 10 files changed, 122 insertions(+), 119 deletions(-) delete mode 100644 src/invidious/routes/api/v1/routes.cr diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr index 31e1a123..93bee55c 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -1,4 +1,4 @@ -module Invidious::Routes::APIManifest +module Invidious::Routes::API::Manifest # /api/manifest/dash/id/:id def self.get_dash_video_id(env) env.response.headers.add("Access-Control-Allow-Origin", "*") @@ -222,16 +222,3 @@ module Invidious::Routes::APIManifest manifest end end - -macro define_api_manifest_routes - Invidious::Routing.get "/api/manifest/dash/id/:id", Invidious::Routes::APIManifest, :get_dash_video_id - - Invidious::Routing.get "/api/manifest/dash/id/videoplayback", Invidious::Routes::APIManifest, :get_dash_video_playback - Invidious::Routing.get "/api/manifest/dash/id/videoplayback/*", Invidious::Routes::APIManifest, :get_dash_video_playback_greedy - - Invidious::Routing.options "/api/manifest/dash/id/videoplayback", Invidious::Routes::APIManifest, :options_dash_video_playback - Invidious::Routing.options "/api/manifest/dash/id/videoplayback/*", Invidious::Routes::APIManifest, :options_dash_video_playback - - Invidious::Routing.get "/api/manifest/hls_playlist/*", Invidious::Routes::APIManifest, :get_hls_playlist - Invidious::Routing.get "/api/manifest/hls_variant/*", Invidious::Routes::APIManifest, :get_hls_variant -end diff --git a/src/invidious/routes/api/v1/authenticated.cr b/src/invidious/routes/api/v1/authenticated.cr index 4201f26d..b4e9e9c8 100644 --- a/src/invidious/routes/api/v1/authenticated.cr +++ b/src/invidious/routes/api/v1/authenticated.cr @@ -1,4 +1,7 @@ -module Invidious::Routes::APIv1::Authenticated +module Invidious::Routes::API::V1::Authenticated + # The notification APIs cannot be extracted yet! + # They require the *local* notifications constant defined in invidious.cr + # # def self.notifications(env) # env.response.content_type = "text/event-stream" diff --git a/src/invidious/routes/api/v1/channels.cr b/src/invidious/routes/api/v1/channels.cr index 3401232b..5caa656d 100644 --- a/src/invidious/routes/api/v1/channels.cr +++ b/src/invidious/routes/api/v1/channels.cr @@ -1,4 +1,4 @@ -module Invidious::Routes::APIv1::Channels +module Invidious::Routes::API::V1::Channels def self.home(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? @@ -241,7 +241,7 @@ module Invidious::Routes::APIv1::Channels end end - def self.channel_search(env) + def self.search(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -263,4 +263,15 @@ module Invidious::Routes::APIv1::Channels end end end + + # 301 redirect from /api/v1/channels/comments/:ucid + # and /api/v1/channels/:ucid/comments to new /api/v1/channels/:ucid/community and + # corresponding equivalent URL structure of the other one. + def self.channel_comments_redirect(env) + env.response.content_type = "application/json" + ucid = env.params.url["ucid"] + + env.response.headers["Location"] = "/api/v1/channels/#{ucid}/community?#{env.params.query}" + haltf env, status_code: 301 + end end diff --git a/src/invidious/routes/api/v1/feeds.cr b/src/invidious/routes/api/v1/feeds.cr index 0107b71d..bb8f661b 100644 --- a/src/invidious/routes/api/v1/feeds.cr +++ b/src/invidious/routes/api/v1/feeds.cr @@ -1,4 +1,4 @@ -module Invidious::Routes::APIv1::Feeds +module Invidious::Routes::API::V1::Feeds def self.trending(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? @@ -31,8 +31,7 @@ module Invidious::Routes::APIv1::Feeds if !CONFIG.popular_enabled error_message = {"error" => "Administrator has disabled this endpoint."}.to_json - env.response.status_code = 400 - return error_message + haltf env, 400, error_message end JSON.build do |json| diff --git a/src/invidious/routes/api/v1/misc.cr b/src/invidious/routes/api/v1/misc.cr index afb61fc1..cf95bd9b 100644 --- a/src/invidious/routes/api/v1/misc.cr +++ b/src/invidious/routes/api/v1/misc.cr @@ -1,4 +1,4 @@ -module Invidious::Routes::APIv1::Misc +module Invidious::Routes::API::V1::Misc # Stats API endpoint for Invidious def self.stats(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? diff --git a/src/invidious/routes/api/v1/routes.cr b/src/invidious/routes/api/v1/routes.cr deleted file mode 100644 index 9e3c03be..00000000 --- a/src/invidious/routes/api/v1/routes.cr +++ /dev/null @@ -1,67 +0,0 @@ -# There is far too many API routes to define in invidious.cr -# so we'll just do it here instead with a macro. -macro define_v1_api_routes(base_url = "/api/v1") - # Videos - Invidious::Routing.get "#{{{base_url}}}/videos/:id", Invidious::Routes::APIv1::Videos, :videos - Invidious::Routing.get "#{{{base_url}}}/storyboards/:id", Invidious::Routes::APIv1::Videos, :storyboards - Invidious::Routing.get "#{{{base_url}}}/captions/:id", Invidious::Routes::APIv1::Videos, :captions - Invidious::Routing.get "#{{{base_url}}}/annotations/:id", Invidious::Routes::APIv1::Videos, :annotations - Invidious::Routing.get "#{{{base_url}}}/comments/:id", Invidious::Routes::APIv1::Videos, :comments - - # Feeds - Invidious::Routing.get "#{{{base_url}}}/trending", Invidious::Routes::APIv1::Feeds, :trending - Invidious::Routing.get "#{{{base_url}}}/popular", Invidious::Routes::APIv1::Feeds, :popular - - # Channels - Invidious::Routing.get "#{{{base_url}}}/channels/:ucid", Invidious::Routes::APIv1::Channels, :home - {% for route in { - {"home", "home"}, - {"videos", "videos"}, - {"latest", "latest"}, - {"playlists", "playlists"}, - {"comments", "community"}, # Why is the route for the community API `comments`?, - {"search", "channel_search"}, - } %} - - Invidious::Routing.get "#{{{base_url}}}/channels/#{{{route[0]}}}/:ucid", Invidious::Routes::APIv1::Channels, :{{route[1]}} - Invidious::Routing.get "#{{{base_url}}}/channels/:ucid/#{{{route[0]}}}", Invidious::Routes::APIv1::Channels, :{{route[1]}} - {% end %} - - # Search - Invidious::Routing.get "#{{{base_url}}}/search", Invidious::Routes::APIv1::Search, :search - Invidious::Routing.get "#{{{base_url}}}/search/suggestions/:id", Invidious::Routes::APIv1::Search, :search_suggestions - - # Authenticated - # Invidious::Routing.get "#{{{base_url}}}/auth/notifications", Invidious::Routes::APIv1::Authenticated, :notifications - # Invidious::Routing.post "#{{{base_url}}}/auth/notifications", Invidious::Routes::APIv1::Authenticated, :notifications - - Invidious::Routing.get "#{{{base_url}}}/auth/preferences", Invidious::Routes::APIv1::Authenticated, :get_preferences - Invidious::Routing.post "#{{{base_url}}}/auth/preferences", Invidious::Routes::APIv1::Authenticated, :set_preferences - - Invidious::Routing.get "#{{{base_url}}}/auth/feed", Invidious::Routes::APIv1::Authenticated, :feed - - Invidious::Routing.get "#{{{base_url}}}/auth/subscriptions", Invidious::Routes::APIv1::Authenticated, :get_subscriptions - Invidious::Routing.post "#{{{base_url}}}/auth/subscriptions/:ucid", Invidious::Routes::APIv1::Authenticated, :subscribe_channel - Invidious::Routing.delete "#{{{base_url}}}/auth/subscriptions/:ucid", Invidious::Routes::APIv1::Authenticated, :unsubscribe_channel - - - Invidious::Routing.get "#{{{base_url}}}/auth/playlists", Invidious::Routes::APIv1::Authenticated, :list_playlists - Invidious::Routing.post "#{{{base_url}}}/auth/playlists", Invidious::Routes::APIv1::Authenticated, :create_playlist - Invidious::Routing.patch "#{{{base_url}}}/auth/playlists/:ucid", Invidious::Routes::APIv1::Authenticated, :update_playlist_attribute - Invidious::Routing.delete "#{{{base_url}}}/auth/playlists/:ucid", Invidious::Routes::APIv1::Authenticated, :delete_playlist - - - Invidious::Routing.post "#{{{base_url}}}/auth/playlists/:ucid/videos", Invidious::Routes::APIv1::Authenticated, :insert_video_into_playlist - Invidious::Routing.delete "#{{{base_url}}}/auth/playlists/:ucid/videos/:index", Invidious::Routes::APIv1::Authenticated, :delete_video_in_playlist - - Invidious::Routing.get "#{{{base_url}}}/auth/tokens", Invidious::Routes::APIv1::Authenticated, :get_tokens - Invidious::Routing.post "#{{{base_url}}}/auth/tokens/register", Invidious::Routes::APIv1::Authenticated, :register_token - Invidious::Routing.post "#{{{base_url}}}/auth/tokens/unregister", Invidious::Routes::APIv1::Authenticated, :unregister_token - - # Misc - Invidious::Routing.get "#{{{base_url}}}/stats", Invidious::Routes::APIv1::Misc, :stats - Invidious::Routing.get "#{{{base_url}}}/playlists/:plid", Invidious::Routes::APIv1::Misc, :get_playlist - Invidious::Routing.get "#{{{base_url}}}/auth/playlists/:plid", Invidious::Routes::APIv1::Misc, :get_playlist - Invidious::Routing.get "#{{{base_url}}}//mixes/:rdid", Invidious::Routes::APIv1::Misc, :mixes - -end diff --git a/src/invidious/routes/api/v1/search.cr b/src/invidious/routes/api/v1/search.cr index e4d5809f..f3a6fa06 100644 --- a/src/invidious/routes/api/v1/search.cr +++ b/src/invidious/routes/api/v1/search.cr @@ -1,4 +1,4 @@ -module Invidious::Routes::APIv1::Search +module Invidious::Routes::API::V1::Search def self.search(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? region = env.params.query["region"]? diff --git a/src/invidious/routes/api/v1/videos.cr b/src/invidious/routes/api/v1/videos.cr index 0eb2fca3..575e6fdf 100644 --- a/src/invidious/routes/api/v1/videos.cr +++ b/src/invidious/routes/api/v1/videos.cr @@ -1,4 +1,4 @@ -module Invidious::Routes::APIv1::Videos +module Invidious::Routes::API::V1::Videos def self.videos(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? @@ -41,8 +41,7 @@ module Invidious::Routes::APIv1::Videos env.response.headers["Location"] = env.request.resource.gsub(id, ex.video_id) return error_json(302, "Video is unavailable", {"videoId" => ex.video_id}) rescue ex - env.response.status_code = 500 - return + haltf env, 500 end captions = video.captions @@ -80,8 +79,7 @@ module Invidious::Routes::APIv1::Videos end if caption.empty? - env.response.status_code = 404 - return + haltf env, 404 else caption = caption[0] end @@ -164,8 +162,7 @@ module Invidious::Routes::APIv1::Videos env.response.headers["Location"] = env.request.resource.gsub(id, ex.video_id) return error_json(302, "Video is unavailable", {"videoId" => ex.video_id}) rescue ex - env.response.status_code = 500 - return + haltf env, 500 end storyboards = video.storyboards @@ -189,8 +186,7 @@ module Invidious::Routes::APIv1::Videos storyboard = storyboards.select { |storyboard| width == "#{storyboard[:width]}" || height == "#{storyboard[:height]}" } if storyboard.empty? - env.response.status_code = 404 - return + haltf env, 404 else storyboard = storyboard[0] end @@ -236,8 +232,7 @@ module Invidious::Routes::APIv1::Videos source ||= "archive" if !id.match(/[a-zA-Z0-9_-]{11}/) - env.response.status_code = 400 - return + haltf env, 400 end annotations = "" @@ -267,13 +262,11 @@ module Invidious::Routes::APIv1::Videos response = make_client(URI.parse(location.headers["Location"]), &.get(location.headers["Location"])) if response.body.empty? - env.response.status_code = 404 - return + haltf env, 404 end if response.status_code != 200 - env.response.status_code = response.status_code - return + haltf env, response.status_code end annotations = response.body @@ -284,8 +277,7 @@ module Invidious::Routes::APIv1::Videos response = YT_POOL.client &.get("/annotations_invideo?video_id=#{id}") if response.status_code != 200 - env.response.status_code = response.status_code - return + haltf env, response.status_code end annotations = response.body @@ -293,7 +285,7 @@ module Invidious::Routes::APIv1::Videos etag = sha256(annotations)[0, 16] if env.request.headers["If-None-Match"]?.try &.== etag - env.response.status_code = 304 + haltf env, 304 else env.response.headers["ETag"] = etag annotations @@ -349,8 +341,7 @@ module Invidious::Routes::APIv1::Videos end if !reddit_thread || !comments - env.response.status_code = 404 - return + haltf env, 404 end if format == "json" diff --git a/src/invidious/routes/video_playback.cr b/src/invidious/routes/video_playback.cr index 0fe2853d..acbf62b4 100644 --- a/src/invidious/routes/video_playback.cr +++ b/src/invidious/routes/video_playback.cr @@ -278,13 +278,3 @@ module Invidious::Routes::VideoPlayback return env.redirect url end end - -macro define_video_playback_routes - Invidious::Routing.get "/videoplayback", Invidious::Routes::VideoPlayback, :get_video_playback - Invidious::Routing.get "/videoplayback/*", Invidious::Routes::VideoPlayback, :get_video_playback_greedy - - Invidious::Routing.options "/videoplayback", Invidious::Routes::VideoPlayback, :options_video_playback - Invidious::Routing.options "/videoplayback/*", Invidious::Routes::VideoPlayback, :options_video_playback - - Invidious::Routing.get "/latest_version", Invidious::Routes::VideoPlayback, :latest_version -end diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 1fd3477d..62a51399 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -9,3 +9,92 @@ module Invidious::Routing {% end %} end + +macro define_v1_api_routes + {{namespace = Invidious::Routes::API::V1}} + # Videos + Invidious::Routing.get "/api/v1/videos/:id", {{namespace}}::Videos, :videos + Invidious::Routing.get "/api/v1/storyboards/:id", {{namespace}}::Videos, :storyboards + 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 + + # Feeds + Invidious::Routing.get "/api/v1/trending", {{namespace}}::Feeds, :trending + Invidious::Routing.get "/api/v1/popular", {{namespace}}::Feeds, :popular + + # Channels + Invidious::Routing.get "/api/v1/channels/:ucid", {{namespace}}::Channels, :home + {% for route in {"videos", "latest", "playlists", "community", "search"} %} + Invidious::Routing.get "/api/v1/channels/#{{{route}}}/:ucid", {{namespace}}::Channels, :{{route}} + Invidious::Routing.get "/api/v1/channels/:ucid/#{{{route}}}", {{namespace}}::Channels, :{{route}} + {% end %} + + # 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 + Invidious::Routing.get "/api/v1/channels/:ucid/comments", {{namespace}}::Channels, :channel_comments_redirect + + + # Search + Invidious::Routing.get "/api/v1/search", {{namespace}}::Search, :search + Invidious::Routing.get "/api/v1/search/suggestions/:id", {{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 + + Invidious::Routing.get "/api/v1/auth/preferences", {{namespace}}::Authenticated, :get_preferences + Invidious::Routing.post "/api/v1/auth/preferences", {{namespace}}::Authenticated, :set_preferences + + Invidious::Routing.get "/api/v1/auth/feed", {{namespace}}::Authenticated, :feed + + Invidious::Routing.get "/api/v1/auth/subscriptions", {{namespace}}::Authenticated, :get_subscriptions + Invidious::Routing.post "/api/v1/auth/subscriptions/:ucid", {{namespace}}::Authenticated, :subscribe_channel + Invidious::Routing.delete "/api/v1/auth/subscriptions/:ucid", {{namespace}}::Authenticated, :unsubscribe_channel + + + Invidious::Routing.get "/api/v1/auth/playlists", {{namespace}}::Authenticated, :list_playlists + Invidious::Routing.post "/api/v1/auth/playlists", {{namespace}}::Authenticated, :create_playlist + Invidious::Routing.patch "/api/v1/auth/playlists/:plid",{{namespace}}:: Authenticated, :update_playlist_attribute + Invidious::Routing.delete "/api/v1/auth/playlists/:plid", {{namespace}}::Authenticated, :delete_playlist + + + Invidious::Routing.post "/api/v1/auth/playlists/:plid/videos", {{namespace}}::Authenticated, :insert_video_into_playlist + Invidious::Routing.delete "/api/v1/auth/playlists/:plid/videos/:index", {{namespace}}::Authenticated, :delete_video_in_playlist + + Invidious::Routing.get "/api/v1/auth/tokens", {{namespace}}::Authenticated, :get_tokens + Invidious::Routing.post "/api/v1/auth/tokens/register", {{namespace}}::Authenticated, :register_token + Invidious::Routing.post "/api/v1/auth/tokens/unregister", {{namespace}}::Authenticated, :unregister_token + + # Misc + 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 + Invidious::Routing.get "/api/v1//mixes/:rdid", {{namespace}}::Misc, :mixes +end + +macro define_api_manifest_routes + Invidious::Routing.get "/api/manifest/dash/id/:id", Invidious::Routes::API::Manifest, :get_dash_video_id + + Invidious::Routing.get "/api/manifest/dash/id/videoplayback", Invidious::Routes::API::Manifest, :get_dash_video_playback + Invidious::Routing.get "/api/manifest/dash/id/videoplayback/*", Invidious::Routes::API::Manifest, :get_dash_video_playback_greedy + + Invidious::Routing.options "/api/manifest/dash/id/videoplayback", Invidious::Routes::API::Manifest, :options_dash_video_playback + Invidious::Routing.options "/api/manifest/dash/id/videoplayback/*", Invidious::Routes::API::Manifest, :options_dash_video_playback + + 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 +end + +macro define_video_playback_routes + Invidious::Routing.get "/videoplayback", Invidious::Routes::VideoPlayback, :get_video_playback + Invidious::Routing.get "/videoplayback/*", Invidious::Routes::VideoPlayback, :get_video_playback_greedy + + Invidious::Routing.options "/videoplayback", Invidious::Routes::VideoPlayback, :options_video_playback + Invidious::Routing.options "/videoplayback/*", Invidious::Routes::VideoPlayback, :options_video_playback + + Invidious::Routing.get "/latest_version", Invidious::Routes::VideoPlayback, :latest_version +end