Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
e613e3c
feat: optional `trending_enabled` and `search_enabled` params added
Apr 30, 2024
5fa2935
fix(feeds.cr): http status code
NorkzYT May 3, 2024
3abf216
fix(search.cr): http status code
May 3, 2024
e0f20b0
style(feeds.cr): fix code formatting
May 3, 2024
da422fa
feat(invidious): specific pages are disabled with the use of an array
Jun 30, 2024
ba7e504
chore(search.cr): add newline at end of file
Jun 30, 2024
bfe5159
chore(lint): format with crystal tool
Jul 1, 2024
2ec34f3
Update config/config.example.yml
NorkzYT Jul 1, 2024
b0cbd29
feat(en-US.json): add translation key and value
Jul 1, 2024
fdec904
Update src/invidious/views/user/preferences.ecr
NorkzYT Jul 1, 2024
209360e
Merge branch 'master' into optional-disable-api-features
NorkzYT Sep 3, 2024
b4f8a67
Merge branch 'iv-org:master' into optional-disable-api-features
NorkzYT Nov 11, 2024
a077509
Merge branch 'master' into optional-disable-api-features
NorkzYT May 21, 2025
aeb2b10
feat: test
May 21, 2025
ce0a9fa
feat: test2
May 22, 2025
171bac9
Update locales/en-US.json
NorkzYT May 24, 2025
e1d134f
Update locales/en-US.json
NorkzYT May 24, 2025
9fbce52
📝 (locales/en-US.json): remove redundant translation strings
NorkzYT May 24, 2025
3eeec86
🌐 (feeds.cr, search.cr): update translation keys for disabled feeds a…
NorkzYT May 24, 2025
e238624
feat(config.cr): introduce PagesEnabled struct for managing feature t…
NorkzYT Jun 7, 2025
116a5db
Merge branch 'iv-org:master' into optional-disable-api-features
NorkzYT Jun 7, 2025
ba65e4f
Config: Use from_yaml constructor for PagesEnabled
syeopite Aug 25, 2025
d496b6e
Use `PagesEnabled` struct when setting pages_enabled
syeopite Aug 25, 2025
f978c2b
Fix config precedence with popular_enabled
syeopite Aug 25, 2025
245ffc8
Mark attributes set over env var as present if needed
syeopite Aug 25, 2025
20e4e52
Add tests for `popular_enabled` deprecation logic
syeopite Aug 25, 2025
24d0724
chore: disable trending by default
NorkzYT Oct 28, 2025
dee1bd6
chore: missing hash key issue
NorkzYT Oct 28, 2025
ef59312
latest from master
NorkzYT Nov 16, 2025
03634db
Merge branch 'master' into optional-disable-api-features
NorkzYT Nov 16, 2025
8c922d0
revert docker compose file
NorkzYT Nov 16, 2025
2933c7e
Merge branch 'master' into optional-disable-api-features
NorkzYT Feb 28, 2026
bd6dd9c
feat: add search_page_disabled locale + preferences page filtering + …
NorkzYT Mar 1, 2026
ea36b2b
fix(preferences): unchecked pages_enabled checkboxes now persist corr…
NorkzYT Mar 22, 2026
562389b
fix(pages_enabled): hide search UI and block all search routes when d…
NorkzYT Mar 22, 2026
0eb941f
feat(search): subscription-only search mode when YouTube search is di…
NorkzYT Mar 22, 2026
58ced28
fix(playlists): prevent duplicate videos in the same playlist
NorkzYT Mar 23, 2026
8cd5f0f
fix(search_homepage): center subscription hint text under search bar
NorkzYT Mar 23, 2026
7ca2bbd
fix(before_all): halt response after rendering disabled page error
NorkzYT Mar 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions config/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,13 @@ https_only: false
# -----------------------------

##
## Enable/Disable the "Popular" tab on the main page.
## Enable/Disable specific pages on the main page.
##
## Accepted values: true, false
## Default: true
#pages_enabled:
# trending: true
# popular: true
# search: true
##
#popular_enabled: true

##
## Enable/Disable statstics (available at /api/v1/stats).
Expand Down
4 changes: 3 additions & 1 deletion locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -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: "
}
2 changes: 1 addition & 1 deletion src/invidious.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 5 additions & 1 deletion src/invidious/config.cr
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +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 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
Expand Down Expand Up @@ -152,6 +152,10 @@ 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"
Expand Down
9 changes: 7 additions & 2 deletions src/invidious/routes/api/v1/feeds.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ 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"]?

Expand All @@ -29,9 +34,9 @@ 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, 400, error_message
haltf env, 403, error_message
end

JSON.build do |json|
Expand Down
5 changes: 5 additions & 0 deletions src/invidious/routes/api/v1/search.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ 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
Expand Down
27 changes: 16 additions & 11 deletions src/invidious/routes/feeds.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand All @@ -45,19 +45,24 @@ module Invidious::Routes::Feeds
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
begin
trending, plid = fetch_trending(trending_type, region, locale)
rescue ex
return error_template(500, ex)
end

templated "feeds/trending"
templated "feeds/trending"
else
message = translate(locale, "The Trending feed has been disabled by the administrator.")
templated "message"
end
end

def self.subscriptions(env)
Expand Down
9 changes: 6 additions & 3 deletions src/invidious/routes/preferences.cr
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +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"
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"
Expand Down
67 changes: 36 additions & 31 deletions src/invidious/routes/search.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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.page_enabled?("search")
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
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

templated "search"
else
message = translate(locale, "Search has been disabled by the administrator.")
templated "message"
end
end

Expand Down
11 changes: 10 additions & 1 deletion src/invidious/views/user/preferences.ecr
Original file line number Diff line number Diff line change
Expand Up @@ -284,9 +284,18 @@

<div class="pure-control-group">
<label for="popular_enabled"><%= translate(locale, "Popular enabled: ") %></label>
<input name="popular_enabled" id="popular_enabled" type="checkbox" <% if CONFIG.popular_enabled %>checked<% end %>>
<input name="popular_enabled" id="popular_enabled" type="checkbox" <% if CONFIG.page_enabled?("popular") %>checked<% end %>>
</div>

<div class="pure-control-group">
<label for="trending_enabled"><%= translate(locale, "preferences_trending_enabled_label") %></label>
<input name="trending_enabled" id="trending_enabled" type="checkbox" <% if CONFIG.page_enabled?("trending") %>checked<% end %>>
</div>

<div class="pure-control-group">
<label for="search_enabled"><%= translate(locale, "preferences_search_enabled_label") %></label>
<input name="search_enabled" id="search_enabled" type="checkbox" <% if CONFIG.page_enabled?("search") %>checked<% end %>>
</div>

<div class="pure-control-group">
<label for="captcha_enabled"><%= translate(locale, "CAPTCHA enabled: ") %></label>
Expand Down