From e613e3c08c0b807e012ce08e41afa565d57ed39d Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Tue, 30 Apr 2024 16:34:40 -0400 Subject: [PATCH 01/22] feat: optional `trending_enabled` and `search_enabled` params added --- config/config.example.yml | 16 ++++++ src/invidious/config.cr | 2 + src/invidious/routes/api/v1/feeds.cr | 5 ++ src/invidious/routes/api/v1/search.cr | 5 ++ src/invidious/routes/feeds.cr | 25 +++++---- src/invidious/routes/preferences.cr | 8 +++ src/invidious/routes/search.cr | 67 +++++++++++++----------- src/invidious/views/user/preferences.ecr | 9 ++++ 8 files changed, 96 insertions(+), 41 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 38085a20b..9f0c252a5 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -210,6 +210,22 @@ https_only: false ## #popular_enabled: true +## +## Enable/Disable the "Trending" tab on the main page. +## +## Accepted values: true, false +## Default: true +## +#trending_enabled: true + +## +## Enable/Disable "Search" on the main page. +## +## Accepted values: true, false +## Default: true +## +#search_enabled: true + ## ## Enable/Disable statstics (available at /api/v1/stats). ## The following data is available: diff --git a/src/invidious/config.cr b/src/invidious/config.cr index 09c2168b8..f6146cd78 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -91,6 +91,8 @@ class Config # Subscribe to channels using PubSubHubbub (requires domain, hmac_key) property use_pubsub_feeds : Bool | Int32 = false property popular_enabled : Bool = true + property trending_enabled : Bool = true + property search_enabled : Bool = true property captcha_enabled : Bool = true property login_enabled : Bool = true property registration_enabled : Bool = true diff --git a/src/invidious/routes/api/v1/feeds.cr b/src/invidious/routes/api/v1/feeds.cr index 41865f34b..f6feab5ff 100644 --- a/src/invidious/routes/api/v1/feeds.cr +++ b/src/invidious/routes/api/v1/feeds.cr @@ -4,6 +4,11 @@ module Invidious::Routes::API::V1::Feeds env.response.content_type = "application/json" + if !CONFIG.trending_enabled + error_message = {"error" => "Administrator has disabled this endpoint."}.to_json + haltf env, 400, error_message + end + region = env.params.query["region"]? trending_type = env.params.query["type"]? diff --git a/src/invidious/routes/api/v1/search.cr b/src/invidious/routes/api/v1/search.cr index 2922b0601..526914b09 100644 --- a/src/invidious/routes/api/v1/search.cr +++ b/src/invidious/routes/api/v1/search.cr @@ -5,6 +5,11 @@ module Invidious::Routes::API::V1::Search env.response.content_type = "application/json" + if !CONFIG.search_enabled + error_message = {"error" => "Administrator has disabled this endpoint."}.to_json + haltf env, 400, error_message + end + query = Invidious::Search::Query.new(env.params.query, :regular, region) begin diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr index e20a7139e..22a23a7e5 100644 --- a/src/invidious/routes/feeds.cr +++ b/src/invidious/routes/feeds.cr @@ -44,20 +44,25 @@ module Invidious::Routes::Feeds def self.trending(env) locale = env.get("preferences").as(Preferences).locale + + if CONFIG.trending_enabled + trending_type = env.params.query["type"]? + trending_type ||= "Default" - trending_type = env.params.query["type"]? - trending_type ||= "Default" + region = env.params.query["region"]? + region ||= env.get("preferences").as(Preferences).region - region = env.params.query["region"]? - region ||= env.get("preferences").as(Preferences).region + begin + trending, plid = fetch_trending(trending_type, region, locale) + rescue ex + return error_template(500, ex) + end - begin - trending, plid = fetch_trending(trending_type, region, locale) - rescue ex - return error_template(500, ex) + templated "feeds/trending" + else + message = translate(locale, "The Trending feed has been disabled by the administrator.") + templated "message" end - - templated "feeds/trending" end def self.subscriptions(env) diff --git a/src/invidious/routes/preferences.cr b/src/invidious/routes/preferences.cr index 112535bd4..9aee0d8c9 100644 --- a/src/invidious/routes/preferences.cr +++ b/src/invidious/routes/preferences.cr @@ -198,6 +198,14 @@ module Invidious::Routes::PreferencesRoute popular_enabled ||= "off" CONFIG.popular_enabled = popular_enabled == "on" + trending_enabled = env.params.body["trending_enabled"]?.try &.as(String) + trending_enabled ||= "off" + CONFIG.trending_enabled = trending_enabled == "on" + + search_enabled = env.params.body["search_enabled"]?.try &.as(String) + search_enabled ||= "off" + CONFIG.search_enabled = search_enabled == "on" + captcha_enabled = env.params.body["captcha_enabled"]?.try &.as(String) captcha_enabled ||= "off" CONFIG.captcha_enabled = captcha_enabled == "on" diff --git a/src/invidious/routes/search.cr b/src/invidious/routes/search.cr index 5be335339..ae7d98336 100644 --- a/src/invidious/routes/search.cr +++ b/src/invidious/routes/search.cr @@ -40,41 +40,46 @@ module Invidious::Routes::Search prefs = env.get("preferences").as(Preferences) locale = prefs.locale - region = env.params.query["region"]? || prefs.region + if CONFIG.search_enabled + region = env.params.query["region"]? || prefs.region - query = Invidious::Search::Query.new(env.params.query, :regular, region) + query = Invidious::Search::Query.new(env.params.query, :regular, region) - if query.empty? - # Display the full page search box implemented in #1977 - env.set "search", "" - templated "search_homepage", navbar_search: false - else - user = env.get? "user" - - begin - items = query.process - rescue ex : ChannelSearchException - return error_template(404, "Unable to find channel with id of '#{HTML.escape(ex.channel)}'. Are you sure that's an actual channel id? It should look like 'UC4QobU6STFB0P71PMvOGN5A'.") - rescue ex - return error_template(500, ex) - end - - redirect_url = Invidious::Frontend::Misc.redirect_url(env) - - # Pagination - page_nav_html = Frontend::Pagination.nav_numeric(locale, - base_url: "/search?#{query.to_http_params}", - current_page: query.page, - show_next: (items.size >= 20) - ) - - if query.type == Invidious::Search::Query::Type::Channel - env.set "search", "channel:#{query.channel} #{query.text}" + if query.empty? + # Display the full page search box implemented in #1977 + env.set "search", "" + templated "search_homepage", navbar_search: false else - env.set "search", query.text - end + user = env.get? "user" - templated "search" + begin + items = query.process + rescue ex : ChannelSearchException + return error_template(404, "Unable to find channel with id of '#{HTML.escape(ex.channel)}'. Are you sure that's an actual channel id? It should look like 'UC4QobU6STFB0P71PMvOGN5A'.") + rescue ex + return error_template(500, ex) + end + + redirect_url = Invidious::Frontend::Misc.redirect_url(env) + + # Pagination + page_nav_html = Frontend::Pagination.nav_numeric(locale, + base_url: "/search?#{query.to_http_params}", + current_page: query.page, + show_next: (items.size >= 20) + ) + + if query.type == Invidious::Search::Query::Type::Channel + env.set "search", "channel:#{query.channel} #{query.text}" + else + env.set "search", query.text + end + + templated "search" + end + else + message = translate(locale, "Search has been disabled by the administrator.") + templated "message" end end diff --git a/src/invidious/views/user/preferences.ecr b/src/invidious/views/user/preferences.ecr index 55349c5a2..4cda84212 100644 --- a/src/invidious/views/user/preferences.ecr +++ b/src/invidious/views/user/preferences.ecr @@ -287,6 +287,15 @@ checked<% end %>> +
+ + checked<% end %>> +
+ +
+ + checked<% end %>> +
From 5fa293541b05aa9fc0be404e5e872cb991cbe423 Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Fri, 3 May 2024 10:36:43 -0400 Subject: [PATCH 02/22] fix(feeds.cr): http status code --- src/invidious/routes/api/v1/feeds.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/invidious/routes/api/v1/feeds.cr b/src/invidious/routes/api/v1/feeds.cr index f6feab5ff..3a4835260 100644 --- a/src/invidious/routes/api/v1/feeds.cr +++ b/src/invidious/routes/api/v1/feeds.cr @@ -6,7 +6,7 @@ module Invidious::Routes::API::V1::Feeds if !CONFIG.trending_enabled error_message = {"error" => "Administrator has disabled this endpoint."}.to_json - haltf env, 400, error_message + haltf env, 403, error_message end region = env.params.query["region"]? @@ -36,7 +36,7 @@ module Invidious::Routes::API::V1::Feeds if !CONFIG.popular_enabled error_message = {"error" => "Administrator has disabled this endpoint."}.to_json - haltf env, 400, error_message + haltf env, 403, error_message end JSON.build do |json| From 3abf21625cc88d92815814a6217d7a173eec1336 Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Fri, 3 May 2024 10:38:57 -0400 Subject: [PATCH 03/22] fix(search.cr): http status code --- src/invidious/routes/api/v1/search.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/routes/api/v1/search.cr b/src/invidious/routes/api/v1/search.cr index 526914b09..3c44a989d 100644 --- a/src/invidious/routes/api/v1/search.cr +++ b/src/invidious/routes/api/v1/search.cr @@ -7,7 +7,7 @@ module Invidious::Routes::API::V1::Search if !CONFIG.search_enabled error_message = {"error" => "Administrator has disabled this endpoint."}.to_json - haltf env, 400, error_message + haltf env, 403, error_message end query = Invidious::Search::Query.new(env.params.query, :regular, region) From e0f20b06418e1a93ae6c0e27e8e2871970c92803 Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Fri, 3 May 2024 14:00:28 -0400 Subject: [PATCH 04/22] style(feeds.cr): fix code formatting --- src/invidious/routes/feeds.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr index 22a23a7e5..693becb83 100644 --- a/src/invidious/routes/feeds.cr +++ b/src/invidious/routes/feeds.cr @@ -44,7 +44,7 @@ module Invidious::Routes::Feeds def self.trending(env) locale = env.get("preferences").as(Preferences).locale - + if CONFIG.trending_enabled trending_type = env.params.query["type"]? trending_type ||= "Default" From da422fae05da70740be881c12f82c955f9fb14b7 Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Sun, 30 Jun 2024 18:05:26 -0400 Subject: [PATCH 05/22] feat(invidious): specific pages are disabled with the use of an array --- config/config.example.yml | 27 ++++++------------------ src/invidious.cr | 2 +- src/invidious/config.cr | 9 +++++--- src/invidious/routes/api/v1/feeds.cr | 4 ++-- src/invidious/routes/api/v1/search.cr | 2 +- src/invidious/routes/feeds.cr | 4 ++-- src/invidious/routes/preferences.cr | 19 ++++++----------- src/invidious/routes/search.cr | 4 ++-- src/invidious/views/user/preferences.ecr | 6 +++--- 9 files changed, 30 insertions(+), 47 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 9f0c252a5..8749ec572 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -203,28 +203,13 @@ https_only: false # ----------------------------- ## -## Enable/Disable the "Popular" tab on the main page. +## Enable/Disable specific pages on the main page. +## Example: +## pages_enabled: +## trending: true +## popular: true +## search: true ## -## Accepted values: true, false -## Default: true -## -#popular_enabled: true - -## -## Enable/Disable the "Trending" tab on the main page. -## -## Accepted values: true, false -## Default: true -## -#trending_enabled: true - -## -## Enable/Disable "Search" on the main page. -## -## Accepted values: true, false -## Default: true -## -#search_enabled: true ## ## Enable/Disable statstics (available at /api/v1/stats). diff --git a/src/invidious.cr b/src/invidious.cr index e0bd01015..d0a02a3dd 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -176,7 +176,7 @@ if (CONFIG.use_pubsub_feeds.is_a?(Bool) && CONFIG.use_pubsub_feeds.as(Bool)) || Invidious::Jobs.register Invidious::Jobs::SubscribeToFeedsJob.new(PG_DB, HMAC_KEY) end -if CONFIG.popular_enabled +if CONFIG.page_enabled?("popular") Invidious::Jobs.register Invidious::Jobs::PullPopularVideosJob.new(PG_DB) end diff --git a/src/invidious/config.cr b/src/invidious/config.cr index f6146cd78..085aeef89 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -90,9 +90,7 @@ class Config property domain : String? # Subscribe to channels using PubSubHubbub (requires domain, hmac_key) property use_pubsub_feeds : Bool | Int32 = false - property popular_enabled : Bool = true - property trending_enabled : Bool = true - property search_enabled : Bool = true + property pages_enabled : Hash(String, Bool) = {"trending" => true, "popular" => true, "search" => true} property captcha_enabled : Bool = true property login_enabled : Bool = true property registration_enabled : Bool = true @@ -154,6 +152,11 @@ class Config end end + def page_enabled?(page : String) : Bool + @pages_enabled[page]? || false + end + + def self.load # Load config from file or YAML string env var env_config_file = "INVIDIOUS_CONFIG_FILE" diff --git a/src/invidious/routes/api/v1/feeds.cr b/src/invidious/routes/api/v1/feeds.cr index 3a4835260..bfec4fb6f 100644 --- a/src/invidious/routes/api/v1/feeds.cr +++ b/src/invidious/routes/api/v1/feeds.cr @@ -4,7 +4,7 @@ module Invidious::Routes::API::V1::Feeds env.response.content_type = "application/json" - if !CONFIG.trending_enabled + if !CONFIG.page_enabled?("trending") error_message = {"error" => "Administrator has disabled this endpoint."}.to_json haltf env, 403, error_message end @@ -34,7 +34,7 @@ module Invidious::Routes::API::V1::Feeds env.response.content_type = "application/json" - if !CONFIG.popular_enabled + if !CONFIG.page_enabled?("popular") error_message = {"error" => "Administrator has disabled this endpoint."}.to_json haltf env, 403, error_message end diff --git a/src/invidious/routes/api/v1/search.cr b/src/invidious/routes/api/v1/search.cr index 3c44a989d..48df3a2ba 100644 --- a/src/invidious/routes/api/v1/search.cr +++ b/src/invidious/routes/api/v1/search.cr @@ -5,7 +5,7 @@ module Invidious::Routes::API::V1::Search env.response.content_type = "application/json" - if !CONFIG.search_enabled + if !CONFIG.page_enabled?("search") error_message = {"error" => "Administrator has disabled this endpoint."}.to_json haltf env, 403, error_message end diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr index 693becb83..114b9edc7 100644 --- a/src/invidious/routes/feeds.cr +++ b/src/invidious/routes/feeds.cr @@ -34,7 +34,7 @@ module Invidious::Routes::Feeds def self.popular(env) locale = env.get("preferences").as(Preferences).locale - if CONFIG.popular_enabled + if CONFIG.page_enabled?("popular") templated "feeds/popular" else message = translate(locale, "The Popular feed has been disabled by the administrator.") @@ -45,7 +45,7 @@ module Invidious::Routes::Feeds def self.trending(env) locale = env.get("preferences").as(Preferences).locale - if CONFIG.trending_enabled + if CONFIG.page_enabled?("trending") trending_type = env.params.query["type"]? trending_type ||= "Default" diff --git a/src/invidious/routes/preferences.cr b/src/invidious/routes/preferences.cr index 9aee0d8c9..9106d4cf3 100644 --- a/src/invidious/routes/preferences.cr +++ b/src/invidious/routes/preferences.cr @@ -194,17 +194,12 @@ module Invidious::Routes::PreferencesRoute end CONFIG.default_user_preferences.feed_menu = admin_feed_menu - popular_enabled = env.params.body["popular_enabled"]?.try &.as(String) - popular_enabled ||= "off" - CONFIG.popular_enabled = popular_enabled == "on" - - trending_enabled = env.params.body["trending_enabled"]?.try &.as(String) - trending_enabled ||= "off" - CONFIG.trending_enabled = trending_enabled == "on" - - search_enabled = env.params.body["search_enabled"]?.try &.as(String) - search_enabled ||= "off" - CONFIG.search_enabled = search_enabled == "on" + pages_enabled = { + "popular" => (env.params.body["popular_enabled"]?.try &.as(String) || "off") == "on", + "trending" => (env.params.body["trending_enabled"]?.try &.as(String) || "off") == "on", + "search" => (env.params.body["search_enabled"]?.try &.as(String) || "off") == "on" + } + CONFIG.pages_enabled = pages_enabled captcha_enabled = env.params.body["captcha_enabled"]?.try &.as(String) captcha_enabled ||= "off" @@ -355,4 +350,4 @@ module Invidious::Routes::PreferencesRoute env.redirect referer end -end +end \ No newline at end of file diff --git a/src/invidious/routes/search.cr b/src/invidious/routes/search.cr index ae7d98336..f825ffc7b 100644 --- a/src/invidious/routes/search.cr +++ b/src/invidious/routes/search.cr @@ -40,7 +40,7 @@ module Invidious::Routes::Search prefs = env.get("preferences").as(Preferences) locale = prefs.locale - if CONFIG.search_enabled + if CONFIG.page_enabled?("search") region = env.params.query["region"]? || prefs.region query = Invidious::Search::Query.new(env.params.query, :regular, region) @@ -115,4 +115,4 @@ module Invidious::Routes::Search templated "hashtag" end -end +end \ No newline at end of file diff --git a/src/invidious/views/user/preferences.ecr b/src/invidious/views/user/preferences.ecr index 4cda84212..564eca9e8 100644 --- a/src/invidious/views/user/preferences.ecr +++ b/src/invidious/views/user/preferences.ecr @@ -284,17 +284,17 @@
- checked<% end %>> + checked<% end %>>
- checked<% end %>> + checked<% end %>>
- checked<% end %>> + checked<% end %>>
From ba7e504fd1eb6fe2932089a3de267a24cdc037fa Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Sun, 30 Jun 2024 18:11:56 -0400 Subject: [PATCH 06/22] chore(search.cr): add newline at end of file --- src/invidious/routes/search.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/routes/search.cr b/src/invidious/routes/search.cr index f825ffc7b..8c500e02a 100644 --- a/src/invidious/routes/search.cr +++ b/src/invidious/routes/search.cr @@ -115,4 +115,4 @@ module Invidious::Routes::Search templated "hashtag" end -end \ No newline at end of file +end From bfe51590b1bae0a8a86ad68c0b35c3ba63e4d390 Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Mon, 1 Jul 2024 14:48:55 -0400 Subject: [PATCH 07/22] chore(lint): format with crystal tool --- src/invidious/config.cr | 1 - src/invidious/routes/preferences.cr | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/invidious/config.cr b/src/invidious/config.cr index 085aeef89..4adadc48a 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -156,7 +156,6 @@ class Config @pages_enabled[page]? || false end - def self.load # Load config from file or YAML string env var env_config_file = "INVIDIOUS_CONFIG_FILE" diff --git a/src/invidious/routes/preferences.cr b/src/invidious/routes/preferences.cr index 9106d4cf3..1dfc472a4 100644 --- a/src/invidious/routes/preferences.cr +++ b/src/invidious/routes/preferences.cr @@ -195,9 +195,9 @@ module Invidious::Routes::PreferencesRoute CONFIG.default_user_preferences.feed_menu = admin_feed_menu pages_enabled = { - "popular" => (env.params.body["popular_enabled"]?.try &.as(String) || "off") == "on", + "popular" => (env.params.body["popular_enabled"]?.try &.as(String) || "off") == "on", "trending" => (env.params.body["trending_enabled"]?.try &.as(String) || "off") == "on", - "search" => (env.params.body["search_enabled"]?.try &.as(String) || "off") == "on" + "search" => (env.params.body["search_enabled"]?.try &.as(String) || "off") == "on", } CONFIG.pages_enabled = pages_enabled @@ -350,4 +350,4 @@ module Invidious::Routes::PreferencesRoute env.redirect referer end -end \ No newline at end of file +end From 2ec34f36f5e3bd1f5905b7d970281ee52d30db91 Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Mon, 1 Jul 2024 19:32:53 -0400 Subject: [PATCH 08/22] Update config/config.example.yml Co-authored-by: Samantaz Fox --- config/config.example.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 8749ec572..2b0e23232 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -204,11 +204,11 @@ https_only: false ## ## Enable/Disable specific pages on the main page. -## Example: -## pages_enabled: -## trending: true -## popular: true -## search: true +## +#pages_enabled: +# trending: true +# popular: true +# search: true ## ## From b0cbd29563d3d03d8e978dab24270f93a5724a5a Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Mon, 1 Jul 2024 19:36:14 -0400 Subject: [PATCH 09/22] feat(en-US.json): add translation key and value --- locales/en-US.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/locales/en-US.json b/locales/en-US.json index 3987f796e..faf98fff1 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -496,5 +496,7 @@ "toggle_theme": "Toggle Theme", "carousel_slide": "Slide {{current}} of {{total}}", "carousel_skip": "Skip the Carousel", - "carousel_go_to": "Go to slide `x`" + "carousel_go_to": "Go to slide `x`", + "preferences_trending_enabled_label": "Trending enabled: ", + "preferences_search_enabled_label": "Search enabled: " } From fdec9046b1352eb7877a24df3235a84fe7fdc3f2 Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Mon, 1 Jul 2024 19:36:33 -0400 Subject: [PATCH 10/22] Update src/invidious/views/user/preferences.ecr Co-authored-by: Samantaz Fox --- src/invidious/views/user/preferences.ecr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/invidious/views/user/preferences.ecr b/src/invidious/views/user/preferences.ecr index 564eca9e8..e1633cbce 100644 --- a/src/invidious/views/user/preferences.ecr +++ b/src/invidious/views/user/preferences.ecr @@ -288,12 +288,12 @@
- + checked<% end %>>
- + checked<% end %>>
From aeb2b1046d4f17c68a24cf019c92ba206e029c41 Mon Sep 17 00:00:00 2001 From: Norkz Date: Wed, 21 May 2025 19:44:35 -0400 Subject: [PATCH 11/22] feat: test --- locales/en-US.json | 6 +- src/invidious/config.cr | 35 +++++++--- src/invidious/routes/search.cr | 114 +++++++++++++-------------------- 3 files changed, 74 insertions(+), 81 deletions(-) diff --git a/locales/en-US.json b/locales/en-US.json index 2d0adc271..1aa2ab927 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -504,5 +504,7 @@ "preferences_search_enabled_label": "Search enabled: ", "timeline_parse_error_placeholder_heading": "Unable to parse item", "timeline_parse_error_placeholder_message": "Invidious encountered an error while trying to parse this item. For more information see below:", - "timeline_parse_error_show_technical_details": "Show technical details" -} + "timeline_parse_error_show_technical_details": "Show technical details", + "Search has been disabled by the administrator.": "Search has been disabled by the administrator.", + "This helps protect our community. Learn more": "This helps protect our community. Learn more" +} \ No newline at end of file diff --git a/src/invidious/config.cr b/src/invidious/config.cr index 9bf5be441..36cb40a3e 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -111,13 +111,29 @@ class Config # Used to tell Invidious it is behind a proxy, so links to resources should be https:// property https_only : Bool? + # HMAC signing key for CSRF tokens and verifying pubsub subscriptions property hmac_key : String = "" # Domain to be used for links to resources on the site where an absolute URL is required property domain : String? # Subscribe to channels using PubSubHubbub (requires domain, hmac_key) property use_pubsub_feeds : Bool | Int32 = false - property pages_enabled : Hash(String, Bool) = {"trending" => true, "popular" => true, "search" => true} + + # ————————————————————————————————————————————————————————————————————————————————————— + # DEPRECATED: use `pages_enabled["popular"]` instead. + @[Deprecated("`popular_enabled` will be removed in a future release; use pages_enabled[\"popular\"] instead")] + property popular_enabled : Bool = true + + # Global per-page feature toggles. + # Valid keys: "trending", "popular", "search" + # If someone sets both `popular_enabled` and `pages_enabled["popular"]`, the latter takes precedence. + property pages_enabled : Hash(String, Bool) = { + "trending" => true, + "popular" => true, + "search" => true, + } + # ————————————————————————————————————————————————————————————————————————————————————— + property captcha_enabled : Bool = true property login_enabled : Bool = true property registration_enabled : Bool = true @@ -188,18 +204,21 @@ class Config when Bool return disabled when Array - if disabled.includes? option - return true - else - return false - end + disabled.includes?(option) else - return false + false end end + # Centralized page toggle with legacy fallback for `popular_enabled` def page_enabled?(page : String) : Bool - @pages_enabled[page]? || false + if pages_enabled.has_key?(page) + pages_enabled[page] + elsif page == "popular" + popular_enabled + else + true + end end def self.load diff --git a/src/invidious/routes/search.cr b/src/invidious/routes/search.cr index cbae9183a..bd76775dc 100644 --- a/src/invidious/routes/search.cr +++ b/src/invidious/routes/search.cr @@ -40,79 +40,51 @@ module Invidious::Routes::Search prefs = env.get("preferences").as(Preferences) locale = prefs.locale - if CONFIG.page_enabled?("search") - region = env.params.query["region"]? || prefs.region - - query = Invidious::Search::Query.new(env.params.query, :regular, region) - - if query.empty? - # Display the full page search box implemented in #1977 - env.set "search", "" - templated "search_homepage", navbar_search: false - else - user = env.get? "user" - - # An URL was copy/pasted in the search box. - # Redirect the user to the appropriate page. - if query.url? - return env.redirect UrlSanitizer.process(query.text).to_s - end - - begin - if user - items = query.process(user.as(User)) - else - items = query.process - end - rescue ex : ChannelSearchException - return error_template(404, "Unable to find channel with id of '#{HTML.escape(ex.channel)}'. Are you sure that's an actual channel id? It should look like 'UC4QobU6STFB0P71PMvOGN5A'.") - rescue ex - return error_template(500, ex) - end - - redirect_url = Invidious::Frontend::Misc.redirect_url(env) - - # Pagination - page_nav_html = Frontend::Pagination.nav_numeric(locale, - base_url: "/search?#{query.to_http_params}", - current_page: query.page, - show_next: (items.size >= 20) - ) - - if query.type == Invidious::Search::Query::Type::Channel - env.set "search", "channel:#{query.channel} #{query.text}" - else - user = env.get? "user" - - begin - items = query.process - rescue ex : ChannelSearchException - return error_template(404, "Unable to find channel with id of '#{HTML.escape(ex.channel)}'. Are you sure that's an actual channel id? It should look like 'UC4QobU6STFB0P71PMvOGN5A'.") - rescue ex - return error_template(500, ex) - end - - redirect_url = Invidious::Frontend::Misc.redirect_url(env) - - # Pagination - page_nav_html = Frontend::Pagination.nav_numeric(locale, - base_url: "/search?#{query.to_http_params}", - current_page: query.page, - show_next: (items.size >= 20) - ) - - if query.type == Invidious::Search::Query::Type::Channel - env.set "search", "channel:#{query.channel} #{query.text}" - else - env.set "search", query.text - end - - templated "search" - end - else + # if search is disabled, show the “disabled” message immediately + unless CONFIG.page_enabled?("search") message = translate(locale, "Search has been disabled by the administrator.") - templated "message" + return templated "message" end + + # otherwise, do a normal search + region = env.params.query["region"]? || prefs.region + query = Invidious::Search::Query.new(env.params.query, :regular, region) + + # empty query → show homepage + if query.empty? + env.set "search", "" + return templated "search_homepage", navbar_search: false + end + + # non‐empty query → process it + user = env.get?("user") + if query.url? + return env.redirect UrlSanitizer.process(query.text).to_s + end + + begin + items = user ? query.process(user.as(User)) : query.process + rescue ex : ChannelSearchException + return error_template 404, "Unable to find channel with id “#{HTML.escape(ex.channel)}”…" + rescue ex + return error_template 500, ex + end + + # Pagination + page_nav_html = Frontend::Pagination.nav_numeric(locale, + base_url: "/search?#{query.to_http_params}", + current_page: query.page, + show_next: (items.size >= 20) + ) + + # If it's a channel search, prefix the box; otherwise just show the text + if query.type == Invidious::Search::Query::Type::Channel + env.set "search", "channel:#{query.channel} #{query.text}" + else + env.set "search", query.text + end + + templated "search" end def self.hashtag(env : HTTP::Server::Context) From ce0a9faaaef07ec1c286f8fb7f40712c0c1727b5 Mon Sep 17 00:00:00 2001 From: Norkz Date: Wed, 21 May 2025 21:21:45 -0400 Subject: [PATCH 12/22] feat: test2 --- locales/en-US.json | 1 + src/invidious/routes/search.cr | 2 ++ 2 files changed, 3 insertions(+) diff --git a/locales/en-US.json b/locales/en-US.json index 1aa2ab927..eb0dfe2d9 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -4,6 +4,7 @@ "Answer": "Answer", "Search for videos": "Search for videos", "The Popular feed has been disabled by the administrator.": "The Popular feed has been disabled by the administrator.", + "The Trending feed has been disabled by the administrator": "The Trending feed has been disabled by the administrator", "generic_channels_count": "{{count}} channel", "generic_channels_count_plural": "{{count}} channels", "generic_views_count": "{{count}} view", diff --git a/src/invidious/routes/search.cr b/src/invidious/routes/search.cr index bd76775dc..eb253d2c7 100644 --- a/src/invidious/routes/search.cr +++ b/src/invidious/routes/search.cr @@ -70,6 +70,8 @@ module Invidious::Routes::Search return error_template 500, ex end + redirect_url = Invidious::Frontend::Misc.redirect_url(env) + # Pagination page_nav_html = Frontend::Pagination.nav_numeric(locale, base_url: "/search?#{query.to_http_params}", From 171bac97d1742b99bdf24dfe2d37d8561e92325c Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Sat, 24 May 2025 10:01:50 -0400 Subject: [PATCH 13/22] Update locales/en-US.json Co-authored-by: Fijxu --- locales/en-US.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en-US.json b/locales/en-US.json index eb0dfe2d9..de9df4524 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -3,8 +3,8 @@ "Add to playlist: ": "Add to playlist: ", "Answer": "Answer", "Search for videos": "Search for videos", - "The Popular feed has been disabled by the administrator.": "The Popular feed has been disabled by the administrator.", - "The Trending feed has been disabled by the administrator": "The Trending feed has been disabled by the administrator", + "popular_page_disabled": "The Popular feed has been disabled by the administrator.", + "trending_page_disabled": "The Trending feed has been disabled by the administrator.", "generic_channels_count": "{{count}} channel", "generic_channels_count_plural": "{{count}} channels", "generic_views_count": "{{count}} view", From e1d134f6a06f9fa2f28c22766f625890da6efe59 Mon Sep 17 00:00:00 2001 From: Richard Lora Date: Sat, 24 May 2025 10:02:00 -0400 Subject: [PATCH 14/22] Update locales/en-US.json Co-authored-by: Fijxu --- locales/en-US.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en-US.json b/locales/en-US.json index de9df4524..e5d340cf3 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -506,6 +506,6 @@ "timeline_parse_error_placeholder_heading": "Unable to parse item", "timeline_parse_error_placeholder_message": "Invidious encountered an error while trying to parse this item. For more information see below:", "timeline_parse_error_show_technical_details": "Show technical details", - "Search has been disabled by the administrator.": "Search has been disabled by the administrator.", + "search_page_disabled": "Search has been disabled by the administrator.", "This helps protect our community. Learn more": "This helps protect our community. Learn more" } \ No newline at end of file From 9fbce527e7a3a1bb5256c0e900c516f78bf01443 Mon Sep 17 00:00:00 2001 From: NorkzYT Date: Sat, 24 May 2025 14:20:34 +0000 Subject: [PATCH 15/22] =?UTF-8?q?=F0=9F=93=9D=20(locales/en-US.json):=20re?= =?UTF-8?q?move=20redundant=20translation=20strings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en-US.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/locales/en-US.json b/locales/en-US.json index e5d340cf3..d5276edf3 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -506,6 +506,5 @@ "timeline_parse_error_placeholder_heading": "Unable to parse item", "timeline_parse_error_placeholder_message": "Invidious encountered an error while trying to parse this item. For more information see below:", "timeline_parse_error_show_technical_details": "Show technical details", - "search_page_disabled": "Search has been disabled by the administrator.", - "This helps protect our community. Learn more": "This helps protect our community. Learn more" + "search_page_disabled": "Search has been disabled by the administrator." } \ No newline at end of file From 3eeec8632d73c122ea7c1736d7bb4064318c8928 Mon Sep 17 00:00:00 2001 From: NorkzYT Date: Sat, 24 May 2025 14:50:58 +0000 Subject: [PATCH 16/22] =?UTF-8?q?=F0=9F=8C=90=20(feeds.cr,=20search.cr):?= =?UTF-8?q?=20update=20translation=20keys=20for=20disabled=20feeds=20and?= =?UTF-8?q?=20search=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/invidious/routes/feeds.cr | 4 ++-- src/invidious/routes/search.cr | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr index 672f62804..c1852f5e8 100644 --- a/src/invidious/routes/feeds.cr +++ b/src/invidious/routes/feeds.cr @@ -37,7 +37,7 @@ module Invidious::Routes::Feeds if CONFIG.page_enabled?("popular") templated "feeds/popular" else - message = translate(locale, "The Popular feed has been disabled by the administrator.") + message = translate(locale, "popular_page_disabled") templated "message" end end @@ -60,7 +60,7 @@ module Invidious::Routes::Feeds templated "feeds/trending" else - message = translate(locale, "The Trending feed has been disabled by the administrator.") + message = translate(locale, "trending_page_disabled") templated "message" end end diff --git a/src/invidious/routes/search.cr b/src/invidious/routes/search.cr index eb253d2c7..8a9a2d2b4 100644 --- a/src/invidious/routes/search.cr +++ b/src/invidious/routes/search.cr @@ -42,7 +42,7 @@ module Invidious::Routes::Search # if search is disabled, show the “disabled” message immediately unless CONFIG.page_enabled?("search") - message = translate(locale, "Search has been disabled by the administrator.") + message = translate(locale, "search_page_disabled") return templated "message" end From e238624e8fdb21d4fd8894677adcc1b8507f9757 Mon Sep 17 00:00:00 2001 From: NorkzYT Date: Sat, 7 Jun 2025 11:52:13 +0000 Subject: [PATCH 17/22] feat(config.cr): introduce PagesEnabled struct for managing feature toggles for pages refactor(routes): replace direct page_enabled checks with centralized logic in before_all.cr for cleaner endpoint management chore(routes): remove redundant page_enabled checks from individual routes to streamline code and improve maintainability --- src/invidious/config.cr | 43 ++++++++++++++++++++++----- src/invidious/routes/api/v1/feeds.cr | 10 ------- src/invidious/routes/api/v1/search.cr | 5 ---- src/invidious/routes/before_all.cr | 22 ++++++++++++++ src/invidious/routes/feeds.cr | 34 +++++++-------------- src/invidious/routes/preferences.cr | 6 ++-- src/invidious/routes/search.cr | 6 ---- 7 files changed, 71 insertions(+), 55 deletions(-) diff --git a/src/invidious/config.cr b/src/invidious/config.cr index 36cb40a3e..59380cb8e 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -71,6 +71,37 @@ struct HTTPProxyConfig property port : Int32 end +# Structure used for global per-page feature toggles +struct PagesEnabled + include YAML::Serializable + + property trending : Bool = true + property popular : Bool = true + property search : Bool = true + + def has_key?(key : String) : Bool + %w(trending popular search).includes?(key) + end + + def [](key : String) : Bool + case key + when "trending" then @trending + when "popular" then @popular + when "search" then @search + else raise KeyError.new("Unknown page '#{key}'") + end + end + + def []=(key : String, value : Bool) + case key + when "trending" then @trending = value + when "popular" then @popular = value + when "search" then @search = value + else raise KeyError.new("Unknown page '#{key}'") + end + end +end + class Config include YAML::Serializable @@ -127,11 +158,7 @@ class Config # Global per-page feature toggles. # Valid keys: "trending", "popular", "search" # If someone sets both `popular_enabled` and `pages_enabled["popular"]`, the latter takes precedence. - property pages_enabled : Hash(String, Bool) = { - "trending" => true, - "popular" => true, - "search" => true, - } + property pages_enabled : PagesEnabled = PagesEnabled.new # ————————————————————————————————————————————————————————————————————————————————————— property captcha_enabled : Bool = true @@ -212,10 +239,10 @@ class Config # Centralized page toggle with legacy fallback for `popular_enabled` def page_enabled?(page : String) : Bool - if pages_enabled.has_key?(page) - pages_enabled[page] + if @pages_enabled.has_key?(page) + @pages_enabled[page] elsif page == "popular" - popular_enabled + @popular_enabled else true end diff --git a/src/invidious/routes/api/v1/feeds.cr b/src/invidious/routes/api/v1/feeds.cr index bfec4fb6f..2e8328cb0 100644 --- a/src/invidious/routes/api/v1/feeds.cr +++ b/src/invidious/routes/api/v1/feeds.cr @@ -4,11 +4,6 @@ module Invidious::Routes::API::V1::Feeds env.response.content_type = "application/json" - if !CONFIG.page_enabled?("trending") - error_message = {"error" => "Administrator has disabled this endpoint."}.to_json - haltf env, 403, error_message - end - region = env.params.query["region"]? trending_type = env.params.query["type"]? @@ -34,11 +29,6 @@ module Invidious::Routes::API::V1::Feeds env.response.content_type = "application/json" - if !CONFIG.page_enabled?("popular") - error_message = {"error" => "Administrator has disabled this endpoint."}.to_json - haltf env, 403, error_message - end - JSON.build do |json| json.array do popular_videos.each do |video| diff --git a/src/invidious/routes/api/v1/search.cr b/src/invidious/routes/api/v1/search.cr index 3d978c17b..59a30745e 100644 --- a/src/invidious/routes/api/v1/search.cr +++ b/src/invidious/routes/api/v1/search.cr @@ -5,11 +5,6 @@ module Invidious::Routes::API::V1::Search env.response.content_type = "application/json" - if !CONFIG.page_enabled?("search") - error_message = {"error" => "Administrator has disabled this endpoint."}.to_json - haltf env, 403, error_message - end - query = Invidious::Search::Query.new(env.params.query, :regular, region) begin diff --git a/src/invidious/routes/before_all.cr b/src/invidious/routes/before_all.cr index b52696687..5f6a1daab 100644 --- a/src/invidious/routes/before_all.cr +++ b/src/invidious/routes/before_all.cr @@ -102,6 +102,28 @@ module Invidious::Routes::BeforeAll preferences.locale = locale env.set "preferences", preferences + path = env.request.path + page_key = case path + when "/feed/popular", "/api/v1/popular" + "popular" + when "/feed/trending", "/api/v1/trending" + "trending" + when "/search", "/api/v1/search" + "search" + else + nil + end + + if page_key && !CONFIG.page_enabled?(page_key) + if path.starts_with?("/api/") + error_message = {error: "Administrator has disabled this endpoint."}.to_json + haltf env, 403, error_message + else + message = "#{page_key}_page_disabled" + return error_template(403, message) + end + end + # Allow media resources to be loaded from google servers # TODO: check if *.youtube.com can be removed # diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr index c1852f5e8..de82e1fb9 100644 --- a/src/invidious/routes/feeds.cr +++ b/src/invidious/routes/feeds.cr @@ -33,36 +33,24 @@ module Invidious::Routes::Feeds def self.popular(env) locale = env.get("preferences").as(Preferences).locale - - if CONFIG.page_enabled?("popular") - templated "feeds/popular" - else - message = translate(locale, "popular_page_disabled") - templated "message" - end + templated "feeds/popular" end def self.trending(env) locale = env.get("preferences").as(Preferences).locale + trending_type = env.params.query["type"]? + trending_type ||= "Default" - if CONFIG.page_enabled?("trending") - trending_type = env.params.query["type"]? - trending_type ||= "Default" + region = env.params.query["region"]? + region ||= env.get("preferences").as(Preferences).region - region = env.params.query["region"]? - region ||= env.get("preferences").as(Preferences).region - - begin - trending, plid = fetch_trending(trending_type, region, locale) - rescue ex - return error_template(500, ex) - end - - templated "feeds/trending" - else - message = translate(locale, "trending_page_disabled") - templated "message" + begin + trending, plid = fetch_trending(trending_type, region, locale) + rescue ex + return error_template(500, ex) end + + templated "feeds/trending" end def self.subscriptions(env) diff --git a/src/invidious/routes/preferences.cr b/src/invidious/routes/preferences.cr index 34cea3878..3852f8a95 100644 --- a/src/invidious/routes/preferences.cr +++ b/src/invidious/routes/preferences.cr @@ -200,9 +200,9 @@ module Invidious::Routes::PreferencesRoute CONFIG.default_user_preferences.feed_menu = admin_feed_menu pages_enabled = { - "popular" => (env.params.body["popular_enabled"]?.try &.as(String) || "off") == "on", - "trending" => (env.params.body["trending_enabled"]?.try &.as(String) || "off") == "on", - "search" => (env.params.body["search_enabled"]?.try &.as(String) || "off") == "on", + popular: (env.params.body["popular_enabled"]?.try &.as(String) || "on") == "on", + trending: (env.params.body["trending_enabled"]?.try &.as(String) || "on") == "on", + search: (env.params.body["search_enabled"]?.try &.as(String) || "on") == "on", } CONFIG.pages_enabled = pages_enabled diff --git a/src/invidious/routes/search.cr b/src/invidious/routes/search.cr index 8a9a2d2b4..5b10887c0 100644 --- a/src/invidious/routes/search.cr +++ b/src/invidious/routes/search.cr @@ -40,12 +40,6 @@ module Invidious::Routes::Search prefs = env.get("preferences").as(Preferences) locale = prefs.locale - # if search is disabled, show the “disabled” message immediately - unless CONFIG.page_enabled?("search") - message = translate(locale, "search_page_disabled") - return templated "message" - end - # otherwise, do a normal search region = env.params.query["region"]? || prefs.region query = Invidious::Search::Query.new(env.params.query, :regular, region) From ba65e4ff25f2dd4aaa02eefb1dd1a3a92e997190 Mon Sep 17 00:00:00 2001 From: syeopite Date: Sun, 24 Aug 2025 23:06:12 -0700 Subject: [PATCH 18/22] Config: Use from_yaml constructor for PagesEnabled `PagesEnabled` doesn't define a blank constructor without any parameters meaning that `PagesEnabled.new` leads to a error due to lack of arguments --- src/invidious/config.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/config.cr b/src/invidious/config.cr index 59380cb8e..f9a3572af 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -158,7 +158,7 @@ class Config # Global per-page feature toggles. # Valid keys: "trending", "popular", "search" # If someone sets both `popular_enabled` and `pages_enabled["popular"]`, the latter takes precedence. - property pages_enabled : PagesEnabled = PagesEnabled.new + property pages_enabled : PagesEnabled = PagesEnabled.from_yaml("") # ————————————————————————————————————————————————————————————————————————————————————— property captcha_enabled : Bool = true From d496b6e34aaf03810853fcda081cbefd8f394bb5 Mon Sep 17 00:00:00 2001 From: syeopite Date: Sun, 24 Aug 2025 23:12:20 -0700 Subject: [PATCH 19/22] Use `PagesEnabled` struct when setting pages_enabled The config update logic for the admin panel was not updated to use the `PagesEnabled` struct introduced in commit `e238624` --- src/invidious/config.cr | 2 ++ src/invidious/routes/preferences.cr | 9 ++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/invidious/config.cr b/src/invidious/config.cr index f9a3572af..28b81b8de 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -79,6 +79,8 @@ struct PagesEnabled property popular : Bool = true property search : Bool = true + def initialize(@trending, @popular, @search); end + def has_key?(key : String) : Bool %w(trending popular search).includes?(key) end diff --git a/src/invidious/routes/preferences.cr b/src/invidious/routes/preferences.cr index 3852f8a95..d8317ae26 100644 --- a/src/invidious/routes/preferences.cr +++ b/src/invidious/routes/preferences.cr @@ -199,12 +199,11 @@ module Invidious::Routes::PreferencesRoute end CONFIG.default_user_preferences.feed_menu = admin_feed_menu - pages_enabled = { - popular: (env.params.body["popular_enabled"]?.try &.as(String) || "on") == "on", + CONFIG.pages_enabled = PagesEnabled.new( + popular: (env.params.body["popular_enabled"]?.try &.as(String) || "on") == "on", trending: (env.params.body["trending_enabled"]?.try &.as(String) || "on") == "on", - search: (env.params.body["search_enabled"]?.try &.as(String) || "on") == "on", - } - CONFIG.pages_enabled = pages_enabled + search: (env.params.body["search_enabled"]?.try &.as(String) || "on") == "on", + ) captcha_enabled = env.params.body["captcha_enabled"]?.try &.as(String) captcha_enabled ||= "off" From f978c2b228662ee1f6670da5dc4a88e68d2a0d7e Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 25 Aug 2025 00:58:13 -0700 Subject: [PATCH 20/22] Fix config precedence with popular_enabled The original approach cannot be used to properly handle the precedence of `popular_enabled` in relations to the new `pages_enabled` since it is impossible to determine whether they are unset due to the presence of default values. In addition the original precedence handling wasn't actually doing anything since PagesEnabled.has_key? will always return true if the key is valid and thus use page_enabled and its default values regardless of whether popular_enabled was set or not (which again is also impossible to know) To fix this two new instance variables are introduced in order to track whether these two attributes were present in the YAML config and the logic for handling them is instead reduced to only using `page_enabled` and copying `popular_enabled` to page_enabled["popular"] when `page_enabled` field is unset. --- src/invidious/config.cr | 74 ++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/src/invidious/config.cr b/src/invidious/config.cr index 28b81b8de..f218cb69f 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -72,34 +72,26 @@ struct HTTPProxyConfig end # Structure used for global per-page feature toggles -struct PagesEnabled +record PagesEnabled, + trending : Bool = true, + popular : Bool = true, + search : Bool = true do include YAML::Serializable - property trending : Bool = true - property popular : Bool = true - property search : Bool = true - - def initialize(@trending, @popular, @search); end - - def has_key?(key : String) : Bool - %w(trending popular search).includes?(key) + def [](key : String) : Bool + fetch(key) { raise KeyError.new("Unknown page '#{key}'") } end - def [](key : String) : Bool + def []?(key : String) : Bool + fetch(key) { nil } + end + + private def fetch(key : String, &) case key when "trending" then @trending when "popular" then @popular when "search" then @search - else raise KeyError.new("Unknown page '#{key}'") - end - end - - def []=(key : String, value : Bool) - case key - when "trending" then @trending = value - when "popular" then @popular = value - when "search" then @search = value - else raise KeyError.new("Unknown page '#{key}'") + else yield end end end @@ -153,14 +145,26 @@ class Config property use_pubsub_feeds : Bool | Int32 = false # ————————————————————————————————————————————————————————————————————————————————————— + + # A @{{key}}_present variable is required for both fields in order to handle the precedence for + # the deprecated `popular_enabled` in relations to `pages_enabled` + # DEPRECATED: use `pages_enabled["popular"]` instead. @[Deprecated("`popular_enabled` will be removed in a future release; use pages_enabled[\"popular\"] instead")] + @[YAML::Field(presence: true)] property popular_enabled : Bool = true + @[YAML::Field(ignore: true)] + property popular_enabled_present : Bool + # Global per-page feature toggles. # Valid keys: "trending", "popular", "search" # If someone sets both `popular_enabled` and `pages_enabled["popular"]`, the latter takes precedence. + @[YAML::Field(presence: true)] property pages_enabled : PagesEnabled = PagesEnabled.from_yaml("") + + @[YAML::Field(ignore: true)] + property pages_enabled_present : Bool # ————————————————————————————————————————————————————————————————————————————————————— property captcha_enabled : Bool = true @@ -241,13 +245,7 @@ class Config # Centralized page toggle with legacy fallback for `popular_enabled` def page_enabled?(page : String) : Bool - if @pages_enabled.has_key?(page) - @pages_enabled[page] - elsif page == "popular" - @popular_enabled - else - true - end + return @pages_enabled[page] end def self.load @@ -336,6 +334,8 @@ class Config exit(1) end + config.process_deprecation + # Build database_url from db.* if it's not set directly if config.database_url.to_s.empty? if db = config.db @@ -373,4 +373,24 @@ class Config return config end + + # Processes deprecated values + # + # Warns when they are set and handles any precedence issue that may arise when present alongside a successor attribute + # + # This method is public as to allow specs to test the behavior without going through #load + # + # :nodoc: + def process_deprecation(log_io : IO = STDOUT) + # Handle deprecated popular_enabled config and warn if it is set + if self.popular_enabled_present + log_io.puts "Warning: `popular_enabled` has been deprecated and replaced by the `pages_enabled` config" + log_io.puts "If both are set `pages_enabled` will take precedence over `popular_enabled`" + + # Only use popular_enabled value when pages_enabled is unset + if !self.pages_enabled_present + self.pages_enabled = self.pages_enabled.copy_with(popular: self.popular_enabled) + end + end + end end From 245ffc8396b8cb03b7fb163f3d9fafa4818dac5a Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 25 Aug 2025 01:35:40 -0700 Subject: [PATCH 21/22] Mark attributes set over env var as present if needed --- src/invidious/config.cr | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/invidious/config.cr b/src/invidious/config.cr index f218cb69f..848a8a950 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -285,6 +285,12 @@ class Config begin config.{{ivar.id}} = ivar_type.from_yaml(env_value) success = true + + # Update associated _present key if any + {% other_ivar = @type.instance_vars.find { |other_ivar| other_ivar.name == ivar.name + "_present" } %} + {% if other_ivar && (ann = other_ivar.annotation(YAML::Field)) && ann[:ignore] == true %} + config.{{other_ivar.name.id}} = true + {% end %} rescue # nop end From 20e4e52b8b35a9df996a6047483839c27f0fa607 Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 25 Aug 2025 01:36:05 -0700 Subject: [PATCH 22/22] Add tests for `popular_enabled` deprecation logic --- spec/invidious/config_spec.cr | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 spec/invidious/config_spec.cr diff --git a/spec/invidious/config_spec.cr b/spec/invidious/config_spec.cr new file mode 100644 index 000000000..1e9d33552 --- /dev/null +++ b/spec/invidious/config_spec.cr @@ -0,0 +1,50 @@ +require "../spec_helper" +require "../../src/invidious/jobs.cr" +require "../../src/invidious/jobs/*" +require "../../src/invidious/config.cr" +require "../../src/invidious/user/preferences.cr" + +# Allow this file to be executed independently of other specs +{% if !@type.has_constant?("CONFIG") %} + CONFIG = Config.from_yaml("") +{% end %} + +private def construct_config(yaml) + config = Config.from_yaml(yaml) + File.open(File::NULL, "w") { |io| config.process_deprecation(io) } + return config +end + +Spectator.describe Config do + context "page_enabled" do + it "Can disable pages" do + config = construct_config <<-YAML + pages_enabled: + popular: false + search: false + YAML + + expect(config.page_enabled?("trending")).to eq(true) + expect(config.page_enabled?("popular")).to eq(false) + expect(config.page_enabled?("search")).to eq(false) + end + + it "Takes precedence over popular_enabled" do + config = construct_config <<-YAML + popular_enabled: false + pages_enabled: + popular: true + YAML + + expect(config.page_enabled?("popular")).to eq(true) + end + end + + it "Deprecated popular_enabled still works" do + config = construct_config <<-YAML + popular_enabled: false + YAML + + expect(config.page_enabled?("popular")).to eq(false) + end +end