From 66becbf46f7414a16bc4bde7430eeb3d5a8d0f8c Mon Sep 17 00:00:00 2001 From: syeopite Date: Thu, 12 Aug 2021 11:46:03 -0700 Subject: [PATCH] Restructure API route organisation --- src/invidious/routes/API/v1/feeds.cr | 116 ------------------ .../routes/{API/v1 => api_v1}/channels.cr | 35 +----- src/invidious/routes/api_v1/feeds.cr | 46 +++++++ .../routes/{API/v1 => api_v1}/misc.cr | 4 +- .../routes/{API/v1 => api_v1}/routes.cr | 22 ++-- src/invidious/routes/api_v1/search.cr | 24 ++++ src/invidious/routes/api_v1/video_playback.cr | 2 + .../routes/{API/v1 => api_v1}/widgets.cr | 80 +++++++++++- 8 files changed, 166 insertions(+), 163 deletions(-) delete mode 100644 src/invidious/routes/API/v1/feeds.cr rename src/invidious/routes/{API/v1 => api_v1}/channels.cr (91%) create mode 100644 src/invidious/routes/api_v1/feeds.cr rename src/invidious/routes/{API/v1 => api_v1}/misc.cr (80%) rename src/invidious/routes/{API/v1 => api_v1}/routes.cr (77%) create mode 100644 src/invidious/routes/api_v1/search.cr create mode 100644 src/invidious/routes/api_v1/video_playback.cr rename src/invidious/routes/{API/v1 => api_v1}/widgets.cr (82%) diff --git a/src/invidious/routes/API/v1/feeds.cr b/src/invidious/routes/API/v1/feeds.cr deleted file mode 100644 index 513c76db..00000000 --- a/src/invidious/routes/API/v1/feeds.cr +++ /dev/null @@ -1,116 +0,0 @@ -class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute - def comments(env) - locale = LOCALES[env.get("preferences").as(Preferences).locale]? - region = env.params.query["region"]? - - env.response.content_type = "application/json" - - id = env.params.url["id"] - - source = env.params.query["source"]? - source ||= "youtube" - - thin_mode = env.params.query["thin_mode"]? - thin_mode = thin_mode == "true" - - format = env.params.query["format"]? - format ||= "json" - - action = env.params.query["action"]? - action ||= "action_get_comments" - - continuation = env.params.query["continuation"]? - sort_by = env.params.query["sort_by"]?.try &.downcase - - if source == "youtube" - sort_by ||= "top" - - begin - comments = fetch_youtube_comments(id, PG_DB, continuation, format, locale, thin_mode, region, sort_by: sort_by, action: action) - rescue ex - return error_json(500, ex) - end - - return comments - elsif source == "reddit" - sort_by ||= "confidence" - - begin - comments, reddit_thread = fetch_reddit_comments(id, sort_by: sort_by) - content_html = template_reddit_comments(comments, locale) - - content_html = fill_links(content_html, "https", "www.reddit.com") - content_html = replace_links(content_html) - rescue ex - comments = nil - reddit_thread = nil - content_html = "" - end - - if !reddit_thread || !comments - env.response.status_code = 404 - return - end - - if format == "json" - reddit_thread = JSON.parse(reddit_thread.to_json).as_h - reddit_thread["comments"] = JSON.parse(comments.to_json) - - return reddit_thread.to_json - else - response = { - "title" => reddit_thread.title, - "permalink" => reddit_thread.permalink, - "contentHtml" => content_html, - } - - return response.to_json - end - end - end - - def trending(env) - locale = LOCALES[env.get("preferences").as(Preferences).locale]? - - env.response.content_type = "application/json" - - region = env.params.query["region"]? - trending_type = env.params.query["type"]? - - begin - trending, plid = fetch_trending(trending_type, region, locale) - rescue ex - return error_json(500, ex) - end - - videos = JSON.build do |json| - json.array do - trending.each do |video| - video.to_json(locale, json) - end - end - end - - videos - end - - def popular(env) - locale = LOCALES[env.get("preferences").as(Preferences).locale]? - - env.response.content_type = "application/json" - - if !CONFIG.popular_enabled - error_message = {"error" => "Administrator has disabled this endpoint."}.to_json - env.response.status_code = 400 - return error_message - end - - JSON.build do |json| - json.array do - popular_videos.each do |video| - video.to_json(locale, json) - end - end - end - end -end diff --git a/src/invidious/routes/API/v1/channels.cr b/src/invidious/routes/api_v1/channels.cr similarity index 91% rename from src/invidious/routes/API/v1/channels.cr rename to src/invidious/routes/api_v1/channels.cr index 149b1067..03ebebfb 100644 --- a/src/invidious/routes/API/v1/channels.cr +++ b/src/invidious/routes/api_v1/channels.cr @@ -1,5 +1,5 @@ -class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute - def home(env) +module Invidious::Routes::APIv1 + def self.home(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -124,7 +124,7 @@ class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute end end - def latest(env) + def self.latest(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -146,7 +146,7 @@ class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute end end - def videos(env) + def self.videos(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -182,7 +182,7 @@ class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute end end - def playlists(env) + def self.playlists(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -219,7 +219,7 @@ class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute end end - def community(env) + def self.community(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -241,27 +241,4 @@ class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute return error_json(500, ex) end end - - def channel_search(env) - locale = LOCALES[env.get("preferences").as(Preferences).locale]? - - env.response.content_type = "application/json" - - ucid = env.params.url["ucid"] - - query = env.params.query["q"]? - query ||= "" - - page = env.params.query["page"]?.try &.to_i? - page ||= 1 - - count, search_results = channel_search(query, page, ucid) - JSON.build do |json| - json.array do - search_results.each do |item| - item.to_json(locale, json) - end - end - end - end end diff --git a/src/invidious/routes/api_v1/feeds.cr b/src/invidious/routes/api_v1/feeds.cr new file mode 100644 index 00000000..c24266c6 --- /dev/null +++ b/src/invidious/routes/api_v1/feeds.cr @@ -0,0 +1,46 @@ +module Invidious::Routes::APIv1 + def self.trending(env) + locale = LOCALES[env.get("preferences").as(Preferences).locale]? + + env.response.content_type = "application/json" + + region = env.params.query["region"]? + trending_type = env.params.query["type"]? + + begin + trending, plid = fetch_trending(trending_type, region, locale) + rescue ex + return error_json(500, ex) + end + + videos = JSON.build do |json| + json.array do + trending.each do |video| + video.to_json(locale, json) + end + end + end + + videos + end + + def self.popular(env) + locale = LOCALES[env.get("preferences").as(Preferences).locale]? + + env.response.content_type = "application/json" + + if !CONFIG.popular_enabled + error_message = {"error" => "Administrator has disabled this endpoint."}.to_json + env.response.status_code = 400 + return error_message + end + + JSON.build do |json| + json.array do + popular_videos.each do |video| + video.to_json(locale, json) + end + end + end + end +end diff --git a/src/invidious/routes/API/v1/misc.cr b/src/invidious/routes/api_v1/misc.cr similarity index 80% rename from src/invidious/routes/API/v1/misc.cr rename to src/invidious/routes/api_v1/misc.cr index 02aa50c2..4bf8b8b0 100644 --- a/src/invidious/routes/API/v1/misc.cr +++ b/src/invidious/routes/api_v1/misc.cr @@ -1,6 +1,6 @@ -class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute +module Invidious::Routes::APIv1 # Stats API endpoint for Invidious - def stats(env) + def self.stats(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" diff --git a/src/invidious/routes/API/v1/routes.cr b/src/invidious/routes/api_v1/routes.cr similarity index 77% rename from src/invidious/routes/API/v1/routes.cr rename to src/invidious/routes/api_v1/routes.cr index 76dd138e..ec3d9dff 100644 --- a/src/invidious/routes/API/v1/routes.cr +++ b/src/invidious/routes/api_v1/routes.cr @@ -1,18 +1,18 @@ # 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") - Invidious::Routing.get "#{{{base_url}}}/stats", Invidious::Routes::V1Api, :stats + Invidious::Routing.get "#{{{base_url}}}/stats", Invidious::Routes::APIv1, :stats - Invidious::Routing.get "#{{{base_url}}}/storyboards/:id", Invidious::Routes::V1Api, :storyboards - Invidious::Routing.get "#{{{base_url}}}/captions/:id", Invidious::Routes::V1Api, :captions - Invidious::Routing.get "#{{{base_url}}}/annotations/:id", Invidious::Routes::V1Api, :annotations - Invidious::Routing.get "#{{{base_url}}}/search/suggestions/:id", Invidious::Routes::V1Api, :search_suggestions + Invidious::Routing.get "#{{{base_url}}}/storyboards/:id", Invidious::Routes::APIv1, :storyboards + Invidious::Routing.get "#{{{base_url}}}/captions/:id", Invidious::Routes::APIv1, :captions + Invidious::Routing.get "#{{{base_url}}}/annotations/:id", Invidious::Routes::APIv1, :annotations + Invidious::Routing.get "#{{{base_url}}}/search/suggestions/:id", Invidious::Routes::APIv1, :search_suggestions - Invidious::Routing.get "#{{{base_url}}}/comments/:id", Invidious::Routes::V1Api, :comments - Invidious::Routing.get "#{{{base_url}}}/trending", Invidious::Routes::V1Api, :trending - Invidious::Routing.get "#{{{base_url}}}/popular", Invidious::Routes::V1Api, :popular + Invidious::Routing.get "#{{{base_url}}}/comments/:id", Invidious::Routes::APIv1, :comments + Invidious::Routing.get "#{{{base_url}}}/trending", Invidious::Routes::APIv1, :trending + Invidious::Routing.get "#{{{base_url}}}/popular", Invidious::Routes::APIv1, :popular - Invidious::Routing.get "#{{{base_url}}}/channels/:ucid", Invidious::Routes::V1Api, :home + Invidious::Routing.get "#{{{base_url}}}/channels/:ucid", Invidious::Routes::APIv1, :home {% for route in { {"home", "home"}, @@ -23,8 +23,8 @@ macro define_v1_api_routes(base_url = "/api/v1") {"search", "channel_search"}, } %} - Invidious::Routing.get "#{{{base_url}}}/channels/#{{{route[0]}}}/:ucid", Invidious::Routes::V1Api, :{{route[1]}} - Invidious::Routing.get "#{{{base_url}}}/channels/:ucid/#{{{route[0]}}}", Invidious::Routes::V1Api, :{{route[1]}} + Invidious::Routing.get "#{{{base_url}}}/channels/#{{{route[0]}}}/:ucid", Invidious::Routes::APIv1, :{{route[1]}} + Invidious::Routing.get "#{{{base_url}}}/channels/:ucid/#{{{route[0]}}}", Invidious::Routes::APIv1, :{{route[1]}} {% end %} end diff --git a/src/invidious/routes/api_v1/search.cr b/src/invidious/routes/api_v1/search.cr new file mode 100644 index 00000000..61fdadd8 --- /dev/null +++ b/src/invidious/routes/api_v1/search.cr @@ -0,0 +1,24 @@ +module Invidious::Routes::APIv1 + def self.channel_search(env) + locale = LOCALES[env.get("preferences").as(Preferences).locale]? + + env.response.content_type = "application/json" + + ucid = env.params.url["ucid"] + + query = env.params.query["q"]? + query ||= "" + + page = env.params.query["page"]?.try &.to_i? + page ||= 1 + + count, search_results = channel_search(query, page, ucid) + JSON.build do |json| + json.array do + search_results.each do |item| + item.to_json(locale, json) + end + end + end + end +end diff --git a/src/invidious/routes/api_v1/video_playback.cr b/src/invidious/routes/api_v1/video_playback.cr new file mode 100644 index 00000000..16942b22 --- /dev/null +++ b/src/invidious/routes/api_v1/video_playback.cr @@ -0,0 +1,2 @@ +module Invidious::Routes::APIv1 +end diff --git a/src/invidious/routes/API/v1/widgets.cr b/src/invidious/routes/api_v1/widgets.cr similarity index 82% rename from src/invidious/routes/API/v1/widgets.cr rename to src/invidious/routes/api_v1/widgets.cr index d1a1213b..0b1cf67e 100644 --- a/src/invidious/routes/API/v1/widgets.cr +++ b/src/invidious/routes/api_v1/widgets.cr @@ -1,10 +1,10 @@ -class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute +module Invidious::Routes::APIv1 # Fetches YouTube storyboards # # Which are sprites containing x * y preview # thumbnails for individual scenes in a video. # See https://support.jwplayer.com/articles/how-to-add-preview-thumbnails - def storyboards(env) + def self.storyboards(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -80,7 +80,7 @@ class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute end end - def captions(env) + def self.captions(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -206,7 +206,7 @@ class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute webvtt end - def annotations(env) + def self.annotations(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "text/xml" @@ -280,7 +280,7 @@ class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute end end - def search_suggestions(env) + def self.search_suggestions(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? region = env.params.query["region"]? @@ -313,4 +313,74 @@ class Invidious::Routes::V1Api < Invidious::Routes::BaseRoute return error_json(500, ex) end end + + def self.comments(env) + locale = LOCALES[env.get("preferences").as(Preferences).locale]? + region = env.params.query["region"]? + + env.response.content_type = "application/json" + + id = env.params.url["id"] + + source = env.params.query["source"]? + source ||= "youtube" + + thin_mode = env.params.query["thin_mode"]? + thin_mode = thin_mode == "true" + + format = env.params.query["format"]? + format ||= "json" + + action = env.params.query["action"]? + action ||= "action_get_comments" + + continuation = env.params.query["continuation"]? + sort_by = env.params.query["sort_by"]?.try &.downcase + + if source == "youtube" + sort_by ||= "top" + + begin + comments = fetch_youtube_comments(id, continuation, format, locale, thin_mode, region, sort_by: sort_by) + rescue ex + return error_json(500, ex) + end + + return comments + elsif source == "reddit" + sort_by ||= "confidence" + + begin + comments, reddit_thread = fetch_reddit_comments(id, sort_by: sort_by) + content_html = template_reddit_comments(comments, locale) + + content_html = fill_links(content_html, "https", "www.reddit.com") + content_html = replace_links(content_html) + rescue ex + comments = nil + reddit_thread = nil + content_html = "" + end + + if !reddit_thread || !comments + env.response.status_code = 404 + return + end + + if format == "json" + reddit_thread = JSON.parse(reddit_thread.to_json).as_h + reddit_thread["comments"] = JSON.parse(comments.to_json) + + return reddit_thread.to_json + else + response = { + "title" => reddit_thread.title, + "permalink" => reddit_thread.permalink, + "contentHtml" => content_html, + } + + return response.to_json + end + end + end end