Fix a lot of stupid git rebase errors

This commit is contained in:
Sommerwiesel
2025-06-18 11:31:14 +02:00
parent 66d0951fb3
commit 07b9c36020
10 changed files with 1654 additions and 15 deletions
-15
View File
@@ -9,21 +9,6 @@ Based upon patches from:
https://github.com/yewtudotbe/invidious-custom
https://git.nadeko.net/Fijxu/invidious
<<<<<<< HEAD
# IMPORTANT
The instructions below are outdated.
YouTube is currently quadrupling down on trying to block Invidious instances, so there are almost daily changes to my server setup.
I haven't found the time yet to provide new/better instructions, so the instructions below should be taken with some grain of salt.
=======
# Requirements - you need to set this up before running my fork
1. HAProxy 2.x either run on the host or added to the docker-compose.yaml with my config under `etc/haproxy/haproxy.cfg`
2. PostgreSQL 16 server either run on the host or added to the docker-compose.yaml
3. (optional bvut highly recommended) PGBouncer to pool the connections to PostgreSQL. You'll otherwise run into lots of db pool errors under high loads
4. Nginx with my configs under etc/nginx
5. Valid ssl certificate for your domain. I recommend using dehydrated: https://github.com/dehydrated-io/dehydrated
6. Docker compose V2
>>>>>>> 9de2789 (Change to multiple backends)
# Build instructions
Do this in a separate dir (for example /srv/invidious) under non-root permissions
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
docker network create --subnet=192.42.6.0/24 --ipv6 --opt com.docker.network.bridge.name=inv_proxy invidious_proxy
+67
View File
@@ -0,0 +1,67 @@
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
maxconn 4000
daemon
defaults
log global
mode http
option httplog
option dontlognull
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
#errorfile 502 /etc/haproxy/errors/502-invidious.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
timeout connect 10s
timeout check 10s
timeout client 15s
timeout server 15s
timeout http-request 30s
frontend statistics
bind :::8404 v4v6
mode http
http-request use-service prometheus-exporter if { path /metrics }
stats enable
stats uri /stats
stats refresh 30s
no log
frontend invidious
bind 192.42.6.1:8118
mode http
default_backend invidious_proxy
backend invidious_proxy
mode http
cookie COMPANION_ID insert indirect nocache httponly secure
balance leastconn
#balance hdr(X-Forwarded-For)
hash-type consistent
retry-on all-retryable-errors
# Ipv6
server proxy_1 192.42.6.11:8282 cookie 0
server proxy_2 192.42.6.12:8282 cookie 1
server proxy_3 192.42.6.13:8282 cookie 2
server proxy_4 192.42.6.14:8282 cookie 3
server proxy_5 192.42.6.15:8282 cookie 4
server proxy_6 192.42.6.16:8282 cookie 5
server proxy_7 192.42.6.17:8282 cookie 6
server proxy_8 192.42.6.18:8282 cookie 7
# IPv4
#server proxy_1 192.42.6.200:8282 cookie 10
#server proxy_2 192.42.6.201:8282 cookie 11
+5
View File
@@ -0,0 +1,5 @@
location = /favicon.ico {
allow all;
log_not_found off;
access_log off;
}
+7
View File
@@ -0,0 +1,7 @@
location = /robots.txt {
add_header Content-Type text/plain;
root /srv/nginx/public;
allow all;
log_not_found off;
access_log off;
}
@@ -0,0 +1,577 @@
From aecf481a4e569f20e08294734162412a3ad9c83a Mon Sep 17 00:00:00 2001
From: Fijxu <fijxu@nadeko.net>
Date: Fri, 6 Jun 2025 12:27:08 -0400
Subject: [PATCH] feat: Support multiple companion backends
Stripped down version of what inv.nadeko.net Invidious instance does to
balance and allow the user to choose a backend.
---
locales/en-US.json | 3 +-
src/invidious.cr | 14 +++-
src/invidious/config.cr | 8 ++
src/invidious/helpers/backend_info.cr | 90 +++++++++++++++++++++
src/invidious/jobs/backend_checker.cr | 14 ++++
src/invidious/routes/backend_switcher.cr | 16 ++++
src/invidious/routes/before_all.cr | 53 +++++++++++-
src/invidious/routes/watch.cr | 2 +-
src/invidious/routing.cr | 1 +
src/invidious/user/cookies.cr | 26 ++++++
src/invidious/videos.cr | 10 +--
src/invidious/videos/parser.cr | 8 +-
src/invidious/views/template.ecr | 31 +++++++
src/invidious/yt_backend/connection_pool.cr | 9 ++-
src/invidious/yt_backend/youtube_api.cr | 12 ++-
15 files changed, 274 insertions(+), 23 deletions(-)
create mode 100644 src/invidious/helpers/backend_info.cr
create mode 100644 src/invidious/jobs/backend_checker.cr
create mode 100644 src/invidious/routes/backend_switcher.cr
diff --git a/locales/en-US.json b/locales/en-US.json
index 3f42a509..0b752d93 100644
--- a/locales/en-US.json
+++ b/locales/en-US.json
@@ -502,5 +502,6 @@
"carousel_go_to": "Go to slide `x`",
"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",
+ "backend_unavailable": "The backend you selected is unavailable. You have been redirected to another one"
}
diff --git a/src/invidious.cr b/src/invidious.cr
index 69f8a26c..c3071be1 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -96,9 +96,11 @@ YT_POOL = YoutubeConnectionPool.new(YT_URL, capacity: CONFIG.pool_size)
GGPHT_POOL = YoutubeConnectionPool.new(URI.parse("https://yt3.ggpht.com"), capacity: CONFIG.pool_size)
-COMPANION_POOL = CompanionConnectionPool.new(
- capacity: CONFIG.pool_size
-)
+COMPANION_POOL = [] of CompanionConnectionPool
+
+CONFIG.invidious_companion.each do |companion|
+ COMPANION_POOL << CompanionConnectionPool.new(companion, capacity: CONFIG.pool_size)
+end
# CLI
Kemal.config.extra_options do |parser|
@@ -203,6 +205,12 @@ Invidious::Jobs.register Invidious::Jobs::ClearExpiredItemsJob.new
Invidious::Jobs.register Invidious::Jobs::InstanceListRefreshJob.new
+if CONFIG.invidious_companion.present?
+ Invidious::Jobs.register Invidious::Jobs::CheckBackend.new
+else
+ LOGGER.info("jobs: Disabling CheckBackend job. invidious-companion and their respective external video playback proxies (if set on invidious-companion) will not be checked")
+end
+
Invidious::Jobs.start_all
def popular_videos
diff --git a/src/invidious/config.cr b/src/invidious/config.cr
index 4d69854c..4879b7f0 100644
--- a/src/invidious/config.cr
+++ b/src/invidious/config.cr
@@ -82,6 +82,8 @@ class Config
@[YAML::Field(converter: Preferences::URIConverter)]
property public_url : URI = URI.parse("")
+
+ property note : String = ""
end
# Number of threads to use for crawling videos from channels (for updating subscriptions)
@@ -183,6 +185,12 @@ class Config
# Playlist length limit
property playlist_length_limit : Int32 = 500
+ # Fijxu companion modifications
+
+ property server_id_cookie_name : String = "COMPANION_ID"
+ property check_backends_interval : Int32 = 30
+ property backend_name_prefix : String = "Backend"
+
def disabled?(option)
case disabled = CONFIG.disable_proxy
when Bool
diff --git a/src/invidious/helpers/backend_info.cr b/src/invidious/helpers/backend_info.cr
new file mode 100644
index 00000000..f576245e
--- /dev/null
+++ b/src/invidious/helpers/backend_info.cr
@@ -0,0 +1,90 @@
+module BackendInfo
+ extend self
+
+ enum Status
+ Dead = 0
+ Working = 1
+ end
+
+ @@status : Array(Int32) = Array.new(CONFIG.invidious_companion.size, Status::Dead.to_i)
+ @@csp : Array(String) = Array.new(CONFIG.invidious_companion.size, "")
+ @@working_ends : Array(Int32) = Array(Int32).new(0)
+ @@csp_mutex : Mutex = Mutex.new
+ @@check_mutex : Mutex = Mutex.new
+
+ def check_backends
+ check_companion()
+ LOGGER.debug("Invidious companion: New working_ends \"#{@@working_ends}\"")
+ LOGGER.debug("Invidious companion: New status \"#{@@status}\"")
+ end
+
+ private def check_companion
+ # Create Channels the size of CONFIG.invidious_companion
+ comp_size = CONFIG.invidious_companion.size
+ channels = Channel(Nil).new(comp_size)
+ updated_ends = Array(Int32).new(0)
+ updated_status = Array(Int32).new(CONFIG.invidious_companion.size, 0)
+
+ LOGGER.debug("Invidious companion: comp_size \"#{comp_size}\"")
+ CONFIG.invidious_companion.each_with_index do |companion, index|
+ spawn do
+ begin
+ client = HTTP::Client.new(companion.private_url)
+ client.connect_timeout = 10.seconds
+ response = client.get("/healthz")
+ if response.status_code == 200
+ @@check_mutex.synchronize do
+ updated_status[index] = Status::Working.to_i
+ updated_ends.push(index)
+ end
+ generate_csp([companion.public_url.to_s], index)
+ else
+ @@check_mutex.synchronize do
+ updated_status[index] = Status::Dead.to_i
+ end
+ end
+ rescue
+ @@check_mutex.synchronize do
+ updated_status[index] = Status::Dead.to_i
+ end
+ ensure
+ LOGGER.trace("Invidious companion: Done Index: \"#{index}\"")
+ channels.send(nil)
+ end
+ end
+ end
+ # Wait until we receive a signal from them all
+ LOGGER.debug("Invidious companion: Updating working_ends")
+ comp_size.times { channels.receive }
+ @@working_ends = updated_ends.sort!
+ @@status = updated_status
+ end
+
+ private def generate_csp(companion_url : Array(String), index : Int32? = nil)
+ @@csp_mutex.synchronize do
+ @@csp[index] = ""
+ companion_url.each do |url|
+ @@csp[index] += " #{url}"
+ end
+ end
+ end
+
+ def get_status
+ # Shouldn't need to lock since we never edit this array, only change the pointer.
+ return @@status
+ end
+
+ def get_working_ends
+ # Shouldn't need to lock since we never edit this array, only change the pointer.
+ return @@working_ends
+ end
+
+ def get_csp(index : Int32)
+ # A little mutex to prevent sending a partial CSP header
+ # Not sure if this is necessary. But if the @@csp[index] is being assigned
+ # at the same time when it's being accessed, a data race will appear
+ @@csp_mutex.synchronize do
+ return @@csp[index], @@csp[index]
+ end
+ end
+end
diff --git a/src/invidious/jobs/backend_checker.cr b/src/invidious/jobs/backend_checker.cr
new file mode 100644
index 00000000..fdfa0a45
--- /dev/null
+++ b/src/invidious/jobs/backend_checker.cr
@@ -0,0 +1,14 @@
+class Invidious::Jobs::CheckBackend < Invidious::Jobs::BaseJob
+ def initialize
+ end
+
+ def begin
+ loop do
+ LOGGER.info("Backend Checker: Starting")
+ BackendInfo.check_backends
+ LOGGER.info("Backend Checker: Done, sleeping for #{CONFIG.check_backends_interval} seconds")
+ sleep CONFIG.check_backends_interval.seconds
+ Fiber.yield
+ end
+ end
+end
diff --git a/src/invidious/routes/backend_switcher.cr b/src/invidious/routes/backend_switcher.cr
new file mode 100644
index 00000000..e4702729
--- /dev/null
+++ b/src/invidious/routes/backend_switcher.cr
@@ -0,0 +1,16 @@
+{% skip_file if flag?(:api_only) %}
+
+module Invidious::Routes::BackendSwitcher
+ def self.switch(env)
+ referer = get_referer(env, unroll: false)
+ backend_id = env.params.query["backend_id"]?.try &.to_i
+
+ if backend_id.nil?
+ return error_template(400, "Backend ID is required")
+ end
+
+ env.response.cookies[CONFIG.server_id_cookie_name] = Invidious::User::Cookies.server_id(env.request.headers["Host"], backend_id)
+
+ env.redirect referer
+ end
+end
diff --git a/src/invidious/routes/before_all.cr b/src/invidious/routes/before_all.cr
index b5269668..6fb2eef4 100644
--- a/src/invidious/routes/before_all.cr
+++ b/src/invidious/routes/before_all.cr
@@ -1,6 +1,9 @@
module Invidious::Routes::BeforeAll
def self.handle(env)
preferences = Preferences.from_json("{}")
+ host = env.request.headers["Host"]
+ scheme = env.request.headers["X-Forwarded-Proto"]? || ("https" if CONFIG.https_only) || "http"
+ env.set "scheme", scheme
begin
if prefs_cookie = env.request.cookies["PREFS"]?
@@ -20,6 +23,50 @@ module Invidious::Routes::BeforeAll
env.response.headers["X-XSS-Protection"] = "1; mode=block"
env.response.headers["X-Content-Type-Options"] = "nosniff"
+ extra_media_csp = ""
+ extra_connect_csp = ""
+
+ if CONFIG.invidious_companion.present?
+ if !{
+ "/sb/",
+ "/vi/",
+ "/s_p/",
+ "/yts/",
+ "/ggpht/",
+ }.any? { |r| env.request.resource.starts_with? r }
+ if !env.request.cookies[CONFIG.server_id_cookie_name]?
+ env.response.cookies[CONFIG.server_id_cookie_name] = Invidious::User::Cookies.server_id(host)
+ end
+
+ begin
+ current_companion = env.request.cookies[CONFIG.server_id_cookie_name].value.try &.to_i
+ rescue
+ working_ends = BackendInfo.get_working_ends
+ current_companion = working_ends.sample
+ end
+
+ if current_companion > CONFIG.invidious_companion.size
+ current_companion = current_companion % CONFIG.invidious_companion.size
+ env.response.cookies[CONFIG.server_id_cookie_name] = Invidious::User::Cookies.server_id(host, current_companion)
+ end
+
+ companion_status = BackendInfo.get_status
+
+ if companion_status[current_companion] != BackendInfo::Status::Working.to_i
+ current_companion = 0 if current_companion == companion_status.size - 1
+ alive_companion = companion_status.index(BackendInfo::Status::Working.to_i, offset: current_companion)
+ if alive_companion
+ env.set "companion_switched", true
+ current_companion = alive_companion
+ env.response.cookies[CONFIG.server_id_cookie_name] = Invidious::User::Cookies.server_id(host, current_companion)
+ end
+ end
+
+ env.set "current_companion", current_companion
+ extra_media_csp, extra_connect_csp = BackendInfo.get_csp(current_companion)
+ end
+ end
+
# Only allow the pages at /embed/* to be embedded
if env.request.resource.starts_with?("/embed")
frame_ancestors = "'self' file: http: https:"
@@ -33,11 +80,11 @@ module Invidious::Routes::BeforeAll
"default-src 'none'",
"script-src 'self'",
"style-src 'self' 'unsafe-inline'",
- "img-src 'self' data:",
+ "img-src 'self' data: " + "#{scheme}://#{env.request.headers["Host"]?}",
"font-src 'self' data:",
- "connect-src 'self'",
+ "connect-src 'self'" + extra_connect_csp,
"manifest-src 'self'",
- "media-src 'self' blob:",
+ "media-src 'self' blob:" + extra_media_csp,
"child-src 'self' blob:",
"frame-src 'self'",
"frame-ancestors " + frame_ancestors,
diff --git a/src/invidious/routes/watch.cr b/src/invidious/routes/watch.cr
index e777b3f1..7adb98ea 100644
--- a/src/invidious/routes/watch.cr
+++ b/src/invidious/routes/watch.cr
@@ -52,7 +52,7 @@ module Invidious::Routes::Watch
env.params.query.delete_all("listen")
begin
- video = get_video(id, region: params.region)
+ video = get_video(id, region: params.region, env: env)
rescue ex : NotFoundException
LOGGER.error("get_video not found: #{id} : #{ex.message}")
return error_template(404, ex)
diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr
index 46b71f1f..769a7f71 100644
--- a/src/invidious/routing.cr
+++ b/src/invidious/routing.cr
@@ -21,6 +21,7 @@ module Invidious::Routing
get "/privacy", Routes::Misc, :privacy
get "/licenses", Routes::Misc, :licenses
get "/redirect", Routes::Misc, :cross_instance_redirect
+ get "/switchbackend", Routes::BackendSwitcher, :switch
self.register_channel_routes
self.register_watch_routes
diff --git a/src/invidious/user/cookies.cr b/src/invidious/user/cookies.cr
index 654efc15..affcfdca 100644
--- a/src/invidious/user/cookies.cr
+++ b/src/invidious/user/cookies.cr
@@ -35,5 +35,31 @@ struct Invidious::User
samesite: HTTP::Cookie::SameSite::Lax
)
end
+
+ # Backend (CONFIG.server_id_cookie_name) cookie
+ # Parameter "domain" comes from the global config
+ def server_id(domain : String?, server_id : Int32? = nil) : HTTP::Cookie
+ if server_id.nil?
+ server_id = rand(CONFIG.invidious_companion.size)
+ end
+ # Strip the port from the domain if it's being accessed from another port
+ # Browsers will reject the cookie if it contains the port number. This is
+ # because `example.com:3000` is not the same as `example.com` on a cookie.
+ domain = domain.split(":")[0]
+ # Not secure if it's being accessed from I2P
+ # Browsers expect the domain to include https. On I2P there is no HTTPS
+ if domain.not_nil!.split(".").last == "i2p"
+ @@secure = false
+ end
+ return HTTP::Cookie.new(
+ name: CONFIG.server_id_cookie_name,
+ domain: domain,
+ path: "/",
+ value: server_id.to_s,
+ secure: @@secure,
+ http_only: true,
+ samesite: HTTP::Cookie::SameSite::Lax
+ )
+ end
end
end
diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr
index 348a0a66..404f5fed 100644
--- a/src/invidious/videos.cr
+++ b/src/invidious/videos.cr
@@ -294,7 +294,7 @@ struct Video
predicate_bool upcoming, isUpcoming
end
-def get_video(id, refresh = true, region = nil, force_refresh = false)
+def get_video(id, refresh = true, region = nil, force_refresh = false, env : HTTP::Server::Context | Nil = nil)
if (video = Invidious::Database::Videos.select(id)) && !region
# If record was last updated over 10 minutes ago, or video has since premiered,
# refresh (expire param in response lasts for 6 hours)
@@ -304,7 +304,7 @@ def get_video(id, refresh = true, region = nil, force_refresh = false)
force_refresh ||
video.schema_version != Video::SCHEMA_VERSION # cache control
begin
- video = fetch_video(id, region)
+ video = fetch_video(id, region, env)
Invidious::Database::Videos.update(video)
rescue ex
Invidious::Database::Videos.delete(id)
@@ -312,7 +312,7 @@ def get_video(id, refresh = true, region = nil, force_refresh = false)
end
end
else
- video = fetch_video(id, region)
+ video = fetch_video(id, region, env)
Invidious::Database::Videos.insert(video) if !region
end
@@ -320,10 +320,10 @@ def get_video(id, refresh = true, region = nil, force_refresh = false)
rescue DB::Error
# Avoid common `DB::PoolRetryAttemptsExceeded` error and friends
# Note: All DB errors inherit from `DB::Error`
- return fetch_video(id, region)
+ return fetch_video(id, region, env)
end
-def fetch_video(id, region)
+def fetch_video(id, region, env)
info = extract_video_info(video_id: id)
if reason = info["reason"]?
diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr
index feb58440..c4f946e8 100644
--- a/src/invidious/videos/parser.cr
+++ b/src/invidious/videos/parser.cr
@@ -58,7 +58,7 @@ def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)?
}
end
-def extract_video_info(video_id : String)
+def extract_video_info(video_id : String, env : HTTP::Server::Context | Nil = nil)
# Init client config for the API
client_config = YoutubeAPI::ClientConfig.new
@@ -116,7 +116,7 @@ def extract_video_info(video_id : String)
players_fallback.each do |player_fallback|
client_config.client_type = player_fallback
- next if !(player_fallback_response = try_fetch_streaming_data(video_id, client_config))
+ next if !(player_fallback_response = try_fetch_streaming_data(video_id, client_config, env))
if player_fallback_response.dig?("streamingData", "adaptiveFormats", 0, "url")
streaming_data = player_response["streamingData"].as_h
@@ -159,9 +159,9 @@ def extract_video_info(video_id : String)
return params
end
-def try_fetch_streaming_data(id : String, client_config : YoutubeAPI::ClientConfig) : Hash(String, JSON::Any)?
+def try_fetch_streaming_data(id : String, client_config : YoutubeAPI::ClientConfig, env : HTTP::Server::Context | Nil = nil) : Hash(String, JSON::Any)?
LOGGER.debug("try_fetch_streaming_data: [#{id}] Using #{client_config.client_type} client.")
- response = YoutubeAPI.player(video_id: id, params: "2AMB", client_config: client_config)
+ response = YoutubeAPI.player(video_id: id, params: "2AMB", client_config: client_config, env: env)
playability_status = response["playabilityStatus"]["status"]
LOGGER.debug("try_fetch_streaming_data: [#{id}] Got playabilityStatus == #{playability_status}.")
diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr
index 9904b4fc..ad6c1c0e 100644
--- a/src/invidious/views/template.ecr
+++ b/src/invidious/views/template.ecr
@@ -104,6 +104,37 @@
</div>
</div>
+ <%
+ if CONFIG.invidious_companion.present?
+ current_backend = env.get?("current_companion").try &.as(Int32)
+ scheme = env.get("scheme")
+ companion_switched = env.get?("companion_switched")
+ status = BackendInfo.get_status
+ %>
+ <div class="h-box" style="margin-bottom: 10px; font-size:0.85em; text-align: center;">
+ <b>Backend</b><br />
+ <% current_page = env.get("current_page") %>
+ <% CONFIG.invidious_companion.each_with_index do | companion, index | %>
+ <a href="/switchbackend?backend_id=<%= index.to_s %>&referer=<%= current_page %>" style="<%= current_backend == index ? "text-decoration-line: underline; font-weight:bold;" : "" %> display: inline-block;">
+ <%= HTML.escape(CONFIG.backend_name_prefix + (index + 1).to_s) %> <%= HTML.escape(companion.note) %>
+ <span style="font-size: 1rem; color:
+ <% if status[index] == BackendInfo::Status::Dead.to_i %> #fd4848; <% end %>
+ <% if status[index] == BackendInfo::Status::Working.to_i %> #42ae3c; <% end %>
+ ">•</span>
+ </a>
+ <% if !(index == CONFIG.invidious_companion.size-1) %>
+ <span> | </span>
+ <% end %>
+ <% end %>
+ </div>
+ <% end %>
+
+ <% if companion_switched %>
+ <div class="h-box">
+ <p><%= translate(locale, "backend_unavailable") %></p>
+ </div>
+ <% end %>
+
<% if CONFIG.banner %>
<div class="h-box">
<h3><%= CONFIG.banner %></h3>
diff --git a/src/invidious/yt_backend/connection_pool.cr b/src/invidious/yt_backend/connection_pool.cr
index 0daed46c..4c7227c4 100644
--- a/src/invidious/yt_backend/connection_pool.cr
+++ b/src/invidious/yt_backend/connection_pool.cr
@@ -49,7 +49,7 @@ end
struct CompanionConnectionPool
property pool : DB::Pool(HTTP::Client)
- def initialize(capacity = 5, timeout = 5.0)
+ def initialize(companion, capacity = 5, timeout = 10.0)
options = DB::Pool::Options.new(
initial_pool_size: 0,
max_pool_size: capacity,
@@ -58,7 +58,6 @@ struct CompanionConnectionPool
)
@pool = DB::Pool(HTTP::Client).new(options) do
- companion = CONFIG.invidious_companion.sample
next make_client(companion.private_url, use_http_proxy: false)
end
end
@@ -71,8 +70,10 @@ struct CompanionConnectionPool
rescue ex
conn.close
- companion = CONFIG.invidious_companion.sample
- conn = make_client(companion.private_url, use_http_proxy: false)
+ scheme = "http"
+ same_companion = "#{scheme}://#{conn.host}:#{conn.port}"
+
+ conn = make_client(URI.parse(same_companion), use_http_proxy: false)
response = yield conn
ensure
diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr
index b40092a1..1b37c4c9 100644
--- a/src/invidious/yt_backend/youtube_api.cr
+++ b/src/invidious/yt_backend/youtube_api.cr
@@ -465,6 +465,7 @@ module YoutubeAPI
*, # Force the following parameters to be passed by name
params : String,
client_config : ClientConfig | Nil = nil,
+ env : HTTP::Server::Context | Nil = nil,
)
# Playback context, separate because it can be different between clients
playback_ctx = {
@@ -501,7 +502,7 @@ module YoutubeAPI
end
if CONFIG.invidious_companion.present?
- return self._post_invidious_companion("/youtubei/v1/player", data)
+ return self._post_invidious_companion("/youtubei/v1/player", data, env)
else
return self._post_json("/youtubei/v1/player", data, client_config)
end
@@ -682,6 +683,7 @@ module YoutubeAPI
def _post_invidious_companion(
endpoint : String,
data : Hash,
+ env : HTTP::Server::Context | Nil,
) : Hash(String, JSON::Any)
headers = HTTP::Headers{
"Content-Type" => "application/json; charset=UTF-8",
@@ -695,7 +697,13 @@ module YoutubeAPI
# Send the POST request
begin
- response = COMPANION_POOL.client &.post(endpoint, headers: headers, body: data.to_json)
+ if env.nil?
+ working_ends = BackendInfo.get_working_ends
+ current_companion = working_ends.sample
+ else
+ current_companion = env.get("current_companion").as(Int32)
+ end
+ response = COMPANION_POOL[current_companion].client &.post(endpoint, headers: headers, body: data.to_json)
body = response.body
if (response.status_code != 200)
raise Exception.new(
--
2.49.0
+37
View File
@@ -0,0 +1,37 @@
From 236a2c70d3974f5a1fc55a5bc8047ff24d94bce2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=89milien=20Devos?= <github@emiliendevos.be>
Date: Tue, 16 Aug 2022 06:51:52 +0000
Subject: [PATCH] more csp
---
src/invidious/routes/before_all.cr | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/invidious/routes/before_all.cr b/src/invidious/routes/before_all.cr
index 8e2a253f..5c7cc959 100644
--- a/src/invidious/routes/before_all.cr
+++ b/src/invidious/routes/before_all.cr
@@ -23,9 +23,9 @@ module Invidious::Routes::BeforeAll
# Allow media resources to be loaded from google servers
# TODO: check if *.youtube.com can be removed
if CONFIG.disabled?("local") || !preferences.local
- extra_media_csp = " https://*.googlevideo.com:443 https://*.youtube.com:443"
+ extra_media_csp = " https://*.googlevideo.com:443 https://*.youtube.com:443 https://*.invidious.nerdvpn.de:443"
else
- extra_media_csp = ""
+ extra_media_csp = " https://*.invidious.nerdvpn.de:443"
end
# Only allow the pages at /embed/* to be embedded
@@ -43,7 +43,7 @@ module Invidious::Routes::BeforeAll
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data:",
"font-src 'self' data:",
- "connect-src 'self'",
+ "connect-src 'self' https://*.invidious.nerdvpn.de:443",
"manifest-src 'self'",
"media-src 'self' blob:" + extra_media_csp,
"child-src 'self' blob:",
--
2.36.1
@@ -0,0 +1,151 @@
From 7c4c24f829b4eeb8ca035b4a9d77eef53ca8aefa Mon Sep 17 00:00:00 2001
From: Emilien Devos <contact@emiliendevos.be>
Date: Sat, 10 Jun 2023 22:21:38 +0200
Subject: [PATCH 1/1] use redis for video cache
---
shard.lock | 8 ++++++++
shard.yml | 2 ++
src/invidious.cr | 8 +++++++-
src/invidious/config.cr | 3 +++
src/invidious/database/videos.cr | 16 +++++++++++++---
src/invidious/videos.cr | 2 +-
6 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/shard.lock b/shard.lock
index 397bd8bc..8c599d1b 100644
--- a/shard.lock
+++ b/shard.lock
@@ -36,6 +36,10 @@ shards:
git: https://github.com/will/crystal-pg.git
version: 0.28.0
+ pool:
+ git: https://github.com/ysbaddaden/pool.git
+ version: 0.2.4
+
protodec:
git: https://github.com/iv-org/protodec.git
version: 0.1.5
@@ -44,6 +48,10 @@ shards:
git: https://github.com/luislavena/radix.git
version: 0.4.1
+ redis:
+ git: https://github.com/stefanwille/crystal-redis.git
+ version: 2.9.1
+
spectator:
git: https://github.com/icy-arctic-fox/spectator.git
version: 0.10.6
diff --git a/shard.yml b/shard.yml
index 367f7c73..c46e1c42 100644
--- a/shard.yml
+++ b/shard.yml
@@ -31,6 +31,8 @@ dependencies:
http_proxy:
github: mamantoha/http_proxy
version: ~> 0.10.3
+ redis:
+ github: stefanwille/crystal-redis
development_dependencies:
spectator:
diff --git a/src/invidious.cr b/src/invidious.cr
index 3804197e..e2d40597 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -31,6 +31,7 @@ require "xml"
require "yaml"
require "compress/zip"
require "protodec/utils"
+require "redis"
require "./invidious/database/*"
require "./invidious/database/migrations/*"
@@ -60,7 +61,12 @@ alias IV = Invidious
CONFIG = Config.load
HMAC_KEY = CONFIG.hmac_key
-PG_DB = DB.open CONFIG.database_url
+PG_DB = DB.open CONFIG.database_url
+REDIS_DB = Redis::PooledClient.new(unixsocket: CONFIG.redis_socket || nil, url: CONFIG.redis_url || nil, database: CONFIG.redis_db || 0, password: CONFIG.redis_auth || nil)
+
+if REDIS_DB.ping
+ puts "Connected to redis"
+end
ARCHIVE_URL = URI.parse("https://archive.org")
PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com")
REDDIT_URL = URI.parse("https://www.reddit.com")
diff --git a/src/invidious/config.cr b/src/invidious/config.cr
index c4ddcdb3..5b6c576c 100644
--- a/src/invidious/config.cr
+++ b/src/invidious/config.cr
@@ -77,6 +77,11 @@ class Config
# Used for crawling channels: threads should check all videos uploaded by a channel
property full_refresh : Bool = false
+ property redis_url : String?
+ property redis_socket : String?
+ property redis_auth : String?
+ property redis_db : Int32?
+
# Jobs config structure. See jobs.cr and jobs/base_job.cr
property jobs = Invidious::Jobs::JobsConfig.new
diff --git a/src/invidious/database/videos.cr b/src/invidious/database/videos.cr
index 695f5b33..776ef5b1 100644
--- a/src/invidious/database/videos.cr
+++ b/src/invidious/database/videos.cr
@@ -10,7 +10,8 @@ module Invidious::Database::Videos
ON CONFLICT (id) DO NOTHING
SQL
- PG_DB.exec(request, video.id, video.info.to_json, video.updated)
+ REDIS_DB.set(video.id, video.info.to_json, ex: 3600)
+ REDIS_DB.set(video.id + ":time", video.updated, ex: 3600)
end
def delete(id)
@@ -19,7 +20,8 @@ module Invidious::Database::Videos
WHERE id = $1
SQL
- PG_DB.exec(request, id)
+ REDIS_DB.del(id)
+ REDIS_DB.del(id + ":time")
end
def delete_expired
@@ -47,6 +49,14 @@ module Invidious::Database::Videos
WHERE id = $1
SQL
- return PG_DB.query_one?(request, id, as: Video)
+ if ((info = REDIS_DB.get(id)) && (time = REDIS_DB.get(id + ":time")))
+ return Video.new({
+ id: id,
+ info: JSON.parse(info).as_h,
+ updated: Time.parse(time, "%Y-%m-%d %H:%M:%S %z", Time::Location::UTC),
+ })
+ else
+ return nil
+ end
end
end
diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr
index 6d0cf9ba..dde35291 100644
--- a/src/invidious/videos.cr
+++ b/src/invidious/videos.cr
@@ -400,7 +400,7 @@ def get_video(id, refresh = true, region = nil, force_refresh = false)
video.schema_version != Video::SCHEMA_VERSION # cache control
begin
video = fetch_video(id, region, env)
- Invidious::Database::Videos.update(video)
+ Invidious::Database::Videos.insert(video)
rescue ex
Invidious::Database::Videos.delete(id)
raise ex
--
2.46.0
+15
View File
@@ -0,0 +1,15 @@
diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr
index 8f5aa61d..f97a8732 100644
--- a/src/invidious/yt_backend/youtube_api.cr
+++ b/src/invidious/yt_backend/youtube_api.cr
@@ -638,9 +638,7 @@ module YoutubeAPI
body = YT_POOL.client() do |client|
client.post(url, headers: headers, body: data.to_json) do |response|
if response.status_code != 200
- raise InfoException.new("Error: non 200 status code. Youtube API returned \
- status code #{response.status_code}. See <a href=\"https://docs.invidious.io/youtube-errors-explained/\"> \
- https://docs.invidious.io/youtube-errors-explained/</a> for troubleshooting.")
+ raise InfoException.new("<h2>Error loading video</h2><p style='font-size: 0.8em; margin: -20px 0 20px 0;'>Received error from YouTube: #{response.status_code}</p>")
end
self._decompress(response.body_io, response.headers["Content-Encoding"]?)
end
+793
View File
@@ -0,0 +1,793 @@
diff --git a/locales/ar.json b/locales/ar.json
index 94103c29..427ddaf9 100644
--- a/locales/ar.json
+++ b/locales/ar.json
@@ -436,6 +436,7 @@
"Vietnamese (auto-generated)": "فيتنامي (تم إنشاؤه تلقائيًا)",
"crash_page_report_issue": "إذا لم يساعد أي مما سبق، يرجى فتح <a href=\"`x`\"> مشكلة جديدة على GitHub </a> (ويفضل أن يكون باللغة الإنجليزية) وتضمين النص التالي في رسالتك (لا تترجم هذا النص):",
"crash_page_read_the_faq": "قراءة <a href=\"`x`\"> الأسئلة المتكررة (الأسئلة الشائعة) </a>",
+ "preferences_hide_shorts_label": "إخفاء YouTube Shorts: ",
"preferences_watch_history_label": "تمكين سجل المشاهدة: ",
"English (United Kingdom)": "الإنجليزية (المملكة المتحدة)",
"Cantonese (Hong Kong)": "الكانتونية (هونغ كونغ)",
diff --git a/locales/bg.json b/locales/bg.json
index 5c99d98f..acf5a6f7 100644
--- a/locales/bg.json
+++ b/locales/bg.json
@@ -95,6 +95,7 @@
"search_filters_sort_option_rating": "Рейтинг",
"Manage subscriptions": "Управление на абонаментите",
"preferences_quality_dash_option_720p": "720p",
+ "preferences_hide_shorts_label": "Скрий YouTube Shorts: ",
"preferences_watch_history_label": "Активирай историята на гледане: ",
"user_saved_playlists": "`x` запази плейлисти",
"preferences_extend_desc_label": "Автоматично разшири описанието на видеото ",
diff --git a/locales/bn.json b/locales/bn.json
index 501a1ca3..e236bcc8 100644
--- a/locales/bn.json
+++ b/locales/bn.json
@@ -61,6 +61,7 @@
"generic_videos_count_plural": "{{count}}টি ভিডিও",
"generic_subscribers_count": "{{count}}জন অনুসরণকারী",
"generic_subscribers_count_plural": "{{count}}জন অনুসরণকারী",
+ "preferences_hide_shorts_label": "YouTube Shorts লুকান: ",
"preferences_watch_history_label": "দেখার ইতিহাস চালু করো: ",
"preferences_quality_option_dash": "ড্যাশ (সময়োপযোগী মান)",
"preferences_quality_dash_option_auto": "স্বয়ংক্রিয়",
diff --git a/locales/bn_BD.json b/locales/bn_BD.json
index a82b0da7..78c664fa 100644
--- a/locales/bn_BD.json
+++ b/locales/bn_BD.json
@@ -45,6 +45,7 @@
"Register": "নিবন্ধন",
"E-mail": "ই-মেইল",
"Preferences": "পছন্দসমূহ",
+ "preferences_hide_shorts_label": "YouTube Shorts লুকান: ",
"preferences_category_player": "প্লেয়ারের পছন্দসমূহ",
"preferences_video_loop_label": "সর্বদা লুপ: ",
"preferences_autoplay_label": "স্বয়ংক্রিয় চালু: ",
diff --git a/locales/ca.json b/locales/ca.json
index 474d6a3c..2c442c81 100644
--- a/locales/ca.json
+++ b/locales/ca.json
@@ -443,6 +443,7 @@
"Trending": "Tendència",
"Updated `x` ago": "Actualitzat fa `x`",
"Haitian Creole": "Crioll Haitià",
+ "preferences_hide_shorts_label": "Amaga YouTube Shorts: ",
"preferences_watch_history_label": "Habilita historial de reproduccions: ",
"generic_count_hours": "{{count}} hora",
"generic_count_hours_plural": "{{count}} hores",
diff --git a/locales/cs.json b/locales/cs.json
index 41f3db5c..08bb3f8a 100644
--- a/locales/cs.json
+++ b/locales/cs.json
@@ -338,6 +338,7 @@
"generic_subscribers_count_0": "{{count}} odběratel",
"generic_subscribers_count_1": "{{count}} odběratelé",
"generic_subscribers_count_2": "{{count}} odběratelů",
+ "preferences_hide_shorts_label": "Skrýt YouTube Shorts: ",
"preferences_watch_history_label": "Povolit historii sledování: ",
"preferences_quality_dash_option_240p": "240p",
"preferences_region_label": "Země obsahu: ",
diff --git a/locales/cy.json b/locales/cy.json
index eb391572..55bcc3d8 100644
--- a/locales/cy.json
+++ b/locales/cy.json
@@ -151,6 +151,7 @@
"preferences_category_player": "Hoffterau'r chwaraeydd",
"preferences_autoplay_label": "Chwarae'n awtomatig: ",
"preferences_local_label": "Llwytho fideos drwy ddirprwy weinydd: ",
+ "preferences_hide_shorts_label": "Cuddio YouTube Shorts: ",
"preferences_watch_history_label": "Galluogi hanes gwylio: ",
"preferences_speed_label": "Cyflymder rhagosodedig: ",
"preferences_quality_label": "Ansawdd fideos: ",
diff --git a/locales/da.json b/locales/da.json
index 9cbb446a..86e3445d 100644
--- a/locales/da.json
+++ b/locales/da.json
@@ -418,6 +418,7 @@
"crash_page_refresh": "forsøgte at <a href=\"`x`\">opdatere siden</a>",
"generic_playlists_count": "{{count}} spilleliste",
"generic_playlists_count_plural": "{{count}} spillelister",
+ "preferences_hide_shorts_label": "Skjul YouTube Shorts: ",
"preferences_watch_history_label": "Aktiver afspilningshistorik: ",
"tokens_count": "{{count}} token",
"tokens_count_plural": "{{count}} tokens",
diff --git a/locales/de.json b/locales/de.json
index e51d40b9..70ab245f 100644
--- a/locales/de.json
+++ b/locales/de.json
@@ -432,6 +432,7 @@
"Chinese (Hong Kong)": "Chinesisch (Hong Kong)",
"generic_playlists_count": "{{count}} Wiedergabeliste",
"generic_playlists_count_plural": "{{count}} Wiedergabelisten",
+ "preferences_hide_shorts_label": "YouTube-Shorts ausblenden: ",
"preferences_watch_history_label": "Wiedergabeverlauf aktivieren: ",
"English (United Kingdom)": "Englisch (Vereinigtes Königreich)",
"English (United States)": "Englisch (Vereinigte Staaten)",
diff --git a/locales/el.json b/locales/el.json
index e5a89a44..47ff3239 100644
--- a/locales/el.json
+++ b/locales/el.json
@@ -440,6 +440,7 @@
"videoinfo_youTube_embed_link": "Ενσωμάτωση",
"videoinfo_invidious_embed_link": "Σύνδεσμος Ενσωμάτωσης",
"search_filters_type_option_show": "Μπάρα προόδου διαβάσματος",
+ "preferences_hide_shorts_label": "Απόκρυψη YouTube Shorts: ",
"preferences_watch_history_label": "Ενεργοποίηση ιστορικού παρακολούθησης: ",
"search_filters_title": "Φίλτρο",
"search_message_no_results": "Δε βρέθηκαν αποτελέσματα.",
diff --git a/locales/en-US.json b/locales/en-US.json
index 0b752d93..401ab9f2 100644
--- a/locales/en-US.json
+++ b/locales/en-US.json
@@ -76,6 +76,7 @@
"preferences_continue_autoplay_label": "Autoplay next video: ",
"preferences_listen_label": "Listen by default: ",
"preferences_local_label": "Proxy videos: ",
+ "preferences_hide_shorts_label": "Hide YouTube-Shorts: ",
"preferences_watch_history_label": "Enable watch history: ",
"preferences_speed_label": "Default speed: ",
"preferences_quality_label": "Preferred video quality: ",
diff --git a/locales/eo.json b/locales/eo.json
index 7276c890..d5f3b7d4 100644
--- a/locales/eo.json
+++ b/locales/eo.json
@@ -425,6 +425,7 @@
"comments_points_count": "{{count}} poento",
"comments_points_count_plural": "{{count}} poentoj",
"Cantonese (Hong Kong)": "Kantona (Honkongo)",
+ "preferences_hide_shorts_label": "Kaŝi YouTube Shorts: ",
"preferences_watch_history_label": "Ebligi vidohistorion: ",
"preferences_quality_option_small": "Eta",
"generic_playlists_count": "{{count}} ludlisto",
diff --git a/locales/es.json b/locales/es.json
index 46217943..1ce51dd6 100644
--- a/locales/es.json
+++ b/locales/es.json
@@ -465,6 +465,7 @@
"Korean (auto-generated)": "Coreano (generados automáticamente)",
"Spanish (Mexico)": "Español (México)",
"Spanish (auto-generated)": "Español (generados automáticamente)",
+ "preferences_hide_shorts_label": "Ocultar YouTube Shorts: ",
"preferences_watch_history_label": "Habilitar historial de reproducciones: ",
"search_message_no_results": "No se han encontrado resultados.",
"search_message_change_filters_or_query": "Pruebe ampliar la consulta de búsqueda y/o a cambiar los filtros.",
diff --git a/locales/et.json b/locales/et.json
index 7f652810..f2a41b92 100644
--- a/locales/et.json
+++ b/locales/et.json
@@ -46,6 +46,7 @@
"Register": "Registreeru",
"E-mail": "E-post",
"Preferences": "Eelistused",
+ "preferences_hide_shorts_label": "Peida YouTube Shorts: ",
"preferences_category_player": "Mängija eelistused",
"preferences_continue_autoplay_label": "Mängi järgmine video automaatselt: ",
"preferences_quality_label": "Eelistatud videokvaliteet: ",
diff --git a/locales/eu.json b/locales/eu.json
index fbca537b..11e5972b 100644
--- a/locales/eu.json
+++ b/locales/eu.json
@@ -119,6 +119,7 @@
"Belarusian": "Bielorrusiara",
"Burmese": "Burmesera",
"Chinese (Simplified)": "Txinera (sinplifikatua)",
+ "preferences_hide_shorts_label": "Ezkutatu YouTube Shorts: ",
"preferences_watch_history_label": "Baimendu historia ikusi ",
"generic_videos_count": "{{count}}bideo",
"generic_videos_count_plural": "{{count}}bideoak",
diff --git a/locales/fa.json b/locales/fa.json
index 2326370d..4662c771 100644
--- a/locales/fa.json
+++ b/locales/fa.json
@@ -422,6 +422,7 @@
"search_filters_title": "پالایه",
"Chinese (Hong Kong)": "چینی (هنگ‌کنگ)",
"Dutch (auto-generated)": "هلندی (تولید خودکار)",
+ "preferences_hide_shorts_label": "مخفی کردن YouTube Shorts: ",
"preferences_watch_history_label": "فعال‌سازی تاریخچه‌ی پخش ",
"Indonesian (auto-generated)": "اندونزیایی (تولید خودکار)",
"English (United States)": "انگلیسی (ایالات متحده)",
diff --git a/locales/fi.json b/locales/fi.json
index 13fef6de..804aa4c1 100644
--- a/locales/fi.json
+++ b/locales/fi.json
@@ -432,6 +432,7 @@
"German (auto-generated)": "saksa (automaattisesti luotu)",
"Portuguese (auto-generated)": "portugali (automaattisesti luotu)",
"Russian (auto-generated)": "Venäjä (automaattisesti luotu)",
+ "preferences_hide_shorts_label": "Piilota YouTube Shorts: ",
"preferences_watch_history_label": "Ota katseluhistoria käyttöön: ",
"English (United Kingdom)": "englanti (Iso-Britannia)",
"English (United States)": "englanti (Yhdysvallat)",
diff --git a/locales/fr.json b/locales/fr.json
index 49aa09df..3076623a 100644
--- a/locales/fr.json
+++ b/locales/fr.json
@@ -477,6 +477,7 @@
"Vietnamese (auto-generated)": "Vietnamien (auto-généré)",
"Russian (auto-generated)": "Russe (auto-généré)",
"Spanish (Spain)": "Espagnol (Espagne)",
+ "preferences_hide_shorts_label": "Masquer YouTube Shorts: ",
"preferences_watch_history_label": "Activer l'historique de visionnage : ",
"search_filters_title": "Filtres",
"search_message_change_filters_or_query": "Essayez d'élargir votre recherche et/ou de changer les filtres.",
diff --git a/locales/he.json b/locales/he.json
index 6fee93b2..62c19b02 100644
--- a/locales/he.json
+++ b/locales/he.json
@@ -45,6 +45,7 @@
"Register": "הרשמה",
"E-mail": "דוא״ל",
"Preferences": "העדפות",
+ "preferences_hide_shorts_label": "הסתר YouTube Shorts: ",
"preferences_category_player": "העדפות הנגן",
"preferences_autoplay_label": "ניגון אוטומטי: ",
"preferences_continue_label": "ניגון הסרטון הבא כברירת מחדל: ",
diff --git a/locales/hi.json b/locales/hi.json
index 0a1c09dd..63afc443 100644
--- a/locales/hi.json
+++ b/locales/hi.json
@@ -90,6 +90,7 @@
"preferences_continue_autoplay_label": "अगला वीडियो अपने आप चलाएँ: ",
"preferences_listen_label": "डिफ़ॉल्ट से सुनें: ",
"preferences_local_label": "प्रॉक्सी वीडियो: ",
+ "preferences_hide_shorts_label": "YouTube Shorts छुपाएं: ",
"preferences_watch_history_label": "देखने का इतिहास सक्षम करें: ",
"preferences_speed_label": "वीडियो चलाने की डिफ़ॉल्ट रफ़्तार: ",
"preferences_quality_label": "वीडियो की प्राथमिक क्वालिटी: ",
diff --git a/locales/hr.json b/locales/hr.json
index 6adbcdc3..7b1d701d 100644
--- a/locales/hr.json
+++ b/locales/hr.json
@@ -467,6 +467,7 @@
"Korean (auto-generated)": "Korejski (automatski generirano)",
"Portuguese (auto-generated)": "Portugalski (automatski generirano)",
"Spanish (auto-generated)": "Španjolski (automatski generirano)",
+ "preferences_hide_shorts_label": "Sakrij YouTube Shorts: ",
"preferences_watch_history_label": "Aktiviraj povijest gledanja: ",
"search_filters_title": "Filtri",
"search_filters_date_option_none": "Bilo koji datum",
diff --git a/locales/hu-HU.json b/locales/hu-HU.json
index 8fbdd82f..2571c9d9 100644
--- a/locales/hu-HU.json
+++ b/locales/hu-HU.json
@@ -452,6 +452,7 @@
"French (auto-generated)": "francia (automatikusan generált)",
"Vietnamese (auto-generated)": "vietnámi (automatikusan generált)",
"search_filters_title": "Szűrők",
+ "preferences_hide_shorts_label": "YouTube Shorts elrejtése: ",
"preferences_watch_history_label": "Megnézett videók naplózása: ",
"search_message_no_results": "Nincs találat.",
"search_message_change_filters_or_query": "Próbálj meg bővebben rákeresni vagy a szűrőkön állítani.",
diff --git a/locales/ia.json b/locales/ia.json
index 236ec4b4..84c29150 100644
--- a/locales/ia.json
+++ b/locales/ia.json
@@ -25,6 +25,7 @@
"youtube": "YouTube",
"LIVE": "IN DIRECTO",
"reddit": "Reddit",
+ "preferences_hide_shorts_label": "Celar YouTube Shorts: ",
"preferences_category_player": "Preferentias de reproductor",
"Preferences": "Preferentias",
"preferences_quality_dash_option_auto": "Automatic",
diff --git a/locales/id.json b/locales/id.json
index 4c6e8548..6f9870de 100644
--- a/locales/id.json
+++ b/locales/id.json
@@ -408,6 +408,7 @@
"crash_page_you_found_a_bug": "Sepertinya kamu telah menemukan masalah di invidious!",
"crash_page_before_reporting": "Sebelum melaporkan masalah, pastikan anda memiliki:",
"English (United States)": "Inggris (US)",
+ "preferences_hide_shorts_label": "Sembunyikan YouTube Shorts: ",
"preferences_watch_history_label": "Aktifkan riwayat tontonan: ",
"English (United Kingdom)": "Inggris (UK)",
"search_filters_title": "Saring",
diff --git a/locales/is.json b/locales/is.json
index 28cacf31..264cb5f5 100644
--- a/locales/is.json
+++ b/locales/is.json
@@ -311,6 +311,7 @@
"Playlists": "Spilunarlistar",
"channel_tab_community_label": "Samfélag",
"Current version: ": "Núverandi útgáfa: ",
+ "preferences_hide_shorts_label": "Fela YouTube Shorts: ",
"preferences_watch_history_label": "Virkja áhorfsferil: ",
"Chinese (China)": "Kínverska (Kína)",
"Turkish (auto-generated)": "Tyrkneska (sjálfvirkt útbúið)",
diff --git a/locales/it.json b/locales/it.json
index c7143ef6..15c2b85e 100644
--- a/locales/it.json
+++ b/locales/it.json
@@ -447,6 +447,7 @@
"Popular enabled: ": "Popolare attivato: ",
"English (United Kingdom)": "Inglese (Regno Unito)",
"Portuguese (Brazil)": "Portoghese (Brasile)",
+ "preferences_hide_shorts_label": "Nascondi YouTube Shorts: ",
"preferences_watch_history_label": "Attiva cronologia di riproduzione: ",
"French (auto-generated)": "Francese (generati automaticamente)",
"search_message_use_another_instance": "Puoi anche <a href=\"`x`\">cercare in un'altra istanza</a>.",
diff --git a/locales/ja.json b/locales/ja.json
index c4b82486..37c98713 100644
--- a/locales/ja.json
+++ b/locales/ja.json
@@ -439,6 +439,7 @@
"user_saved_playlists": "`x`個の保存済みの再生リスト",
"crash_page_you_found_a_bug": "Invidious のバグのようです!",
"crash_page_refresh": "<a href=\"`x`\">ページを更新</a>を試す",
+ "preferences_hide_shorts_label": "YouTube Shorts を非表示にする: ",
"preferences_watch_history_label": "再生履歴を有効化 ",
"search_filters_date_option_none": "すべて",
"search_filters_type_option_all": "すべての種類",
diff --git a/locales/ko.json b/locales/ko.json
index 0224955f..d5a38de7 100644
--- a/locales/ko.json
+++ b/locales/ko.json
@@ -407,6 +407,7 @@
"Spanish (auto-generated)": "스페인어 (자동 생성됨)",
"preferences_quality_dash_option_1080p": "1080p",
"preferences_quality_dash_option_worst": "최저",
+ "preferences_hide_shorts_label": "YouTube Shorts 숨기기: ",
"preferences_watch_history_label": "시청 기록 저장: ",
"invidious": "인비디어스",
"preferences_quality_option_small": "낮음",
diff --git a/locales/lmo.json b/locales/lmo.json
index 9d2fe2a8..cce7774a 100644
--- a/locales/lmo.json
+++ b/locales/lmo.json
@@ -183,6 +183,7 @@
"preferences_continue_autoplay_label": "Fa partì en automatico el video seguént: ",
"preferences_listen_label": "Modalità de sól audio preimpostà: ",
"preferences_local_label": "Proxy par i video: ",
+ "preferences_hide_shorts_label": "Scund YouTube Shorts: ",
"preferences_watch_history_label": "Ativà la istoria de reproduzion: ",
"preferences_speed_label": "Velocità preimpostà: ",
"preferences_volume_label": "Volume del reprodutòr: ",
diff --git a/locales/lt.json b/locales/lt.json
index 740be7b6..7a9c3455 100644
--- a/locales/lt.json
+++ b/locales/lt.json
@@ -373,6 +373,7 @@
"generic_subscriptions_count_0": "{{count}} prenumerata",
"generic_subscriptions_count_1": "{{count}} prenumeratos",
"generic_subscriptions_count_2": "{{count}} prenumeratų",
+ "preferences_hide_shorts_label": "Slėpti YouTube Shorts: ",
"preferences_watch_history_label": "Įgalinti žiūrėjimo istoriją: ",
"preferences_quality_dash_option_1080p": "1080p",
"invidious": "Invidious",
diff --git a/locales/lv.json b/locales/lv.json
index a867c8f3..b53707a4 100644
--- a/locales/lv.json
+++ b/locales/lv.json
@@ -27,6 +27,7 @@
"Import YouTube subscriptions": "Ievietot YouTube CSV vai OPML abonementus",
"E-mail": "E-pasts",
"Preferences": "Iestatījumi",
+ "preferences_hide_shorts_label": "Paslēpt YouTube Shorts: ",
"preferences_category_player": "Atskaņotāja iestatījumi",
"preferences_quality_option_hd720": "HD - 720p",
"preferences_quality_option_medium": "Vidēja",
diff --git a/locales/nb-NO.json b/locales/nb-NO.json
index 38402fed..a81744e6 100644
--- a/locales/nb-NO.json
+++ b/locales/nb-NO.json
@@ -436,6 +436,7 @@
"Spanish (Spain)": "Spansk (Spania)",
"Spanish (auto-generated)": "Spansk (laget automatisk)",
"Vietnamese (auto-generated)": "Vietnamesisk (laget automatisk)",
+ "preferences_hide_shorts_label": "Skjul YouTube Shorts: ",
"preferences_watch_history_label": "Aktiver seerhistorikk: ",
"Chinese": "Kinesisk",
"Chinese (China)": "Kinesisk (Kina)",
diff --git a/locales/nl.json b/locales/nl.json
index e9ce7674..507bcb7c 100644
--- a/locales/nl.json
+++ b/locales/nl.json
@@ -409,6 +409,7 @@
"generic_subscriptions_count_plural": "{{count}} abonnementen",
"subscriptions_unseen_notifs_count": "{{count}} ongeziene melding",
"subscriptions_unseen_notifs_count_plural": "{{count}} ongeziene meldingen",
+ "preferences_hide_shorts_label": "Verberg YouTube Shorts: ",
"preferences_watch_history_label": "Kijkgeschiedenis inschakelen: ",
"crash_page_switch_instance": "geprobeerd hebt om <a href=\"`x`\">een andere instantie te gebruiken</a>",
"Portuguese (auto-generated)": "Portugees (automatisch gegenereerd)",
diff --git a/locales/or.json b/locales/or.json
index 948610f1..ba7e9d56 100644
--- a/locales/or.json
+++ b/locales/or.json
@@ -16,6 +16,7 @@
"last": "ଗତ",
"New password": "ନୂଆ ପାସ୍‌ୱର୍ଡ଼",
"preferences_quality_dash_option_1440p": "୧୪୪୦ପି",
+ "preferences_hide_shorts_label": "YouTube Shorts ଲୁଚାନ୍ତୁ: ",
"preferences_quality_dash_option_360p": "୩୬୦ପି",
"preferences_quality_option_medium": "ମଧ୍ୟମ",
"preferences_quality_dash_option_1080p": "୧୦୮୦ପି",
diff --git a/locales/pl.json b/locales/pl.json
index d78b7a95..d1dcc62c 100644
--- a/locales/pl.json
+++ b/locales/pl.json
@@ -472,6 +472,7 @@
"error_video_not_in_playlist": "Żądany film nie istnieje na tej playliście. <a href=\"`x`\">Kliknij tutaj, aby przejść do strony głównej playlisty.</a>",
"Popular enabled: ": "Popularne włączone: ",
"search_message_no_results": "Nie znaleziono wyników.",
+ "preferences_hide_shorts_label": "Ukryj YouTube Shorts: ",
"preferences_watch_history_label": "Włącz historię oglądania: ",
"search_filters_apply_button": "Zastosuj wybrane filtry",
"search_message_change_filters_or_query": "Spróbuj poszerzyć zapytanie wyszukiwania i/lub zmienić filtry.",
diff --git a/locales/pt-BR.json b/locales/pt-BR.json
index 1eb3c989..77b4ee4a 100644
--- a/locales/pt-BR.json
+++ b/locales/pt-BR.json
@@ -445,6 +445,7 @@
"Video unavailable": "Vídeo indisponível",
"videoinfo_started_streaming_x_ago": "Iniciou a transmissão a `x`",
"search_filters_title": "Filtro",
+ "preferences_hide_shorts_label": "Ocultar YouTube Shorts: ",
"preferences_watch_history_label": "Ativar histórico de exibição: ",
"search_message_no_results": "Nenhum resultado encontrado.",
"search_message_change_filters_or_query": "Tente ampliar sua consulta de pesquisa e/ou alterar os filtros.",
diff --git a/locales/pt-PT.json b/locales/pt-PT.json
index 449bde77..3538bb1a 100644
--- a/locales/pt-PT.json
+++ b/locales/pt-PT.json
@@ -402,6 +402,7 @@
"preferences_region_label": "País do conteúdo: ",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_720p": "720p",
+ "preferences_hide_shorts_label": "Ocultar YouTube Shorts: ",
"preferences_watch_history_label": "Ativar histórico de reprodução: ",
"preferences_quality_dash_option_best": "Melhor",
"preferences_quality_dash_option_worst": "Pior",
diff --git a/locales/pt.json b/locales/pt.json
index 6438e15b..397aa006 100644
--- a/locales/pt.json
+++ b/locales/pt.json
@@ -471,6 +471,7 @@
"search_filters_apply_button": "Aplicar filtros selecionados",
"Spanish (auto-generated)": "Espanhol (gerado automaticamente)",
"Spanish (Mexico)": "Espanhol (México)",
+ "preferences_hide_shorts_label": "Ocultar YouTube Shorts: ",
"preferences_watch_history_label": "Ativar histórico de reprodução: ",
"Chinese (China)": "Chinês (China)",
"Russian (auto-generated)": "Russo (gerado automaticamente)",
diff --git a/locales/ro.json b/locales/ro.json
index ccbeef63..6c39b1f8 100644
--- a/locales/ro.json
+++ b/locales/ro.json
@@ -329,6 +329,7 @@
"subscriptions_unseen_notifs_count_2": "{{count}} de notificări neverificate",
"crash_page_refresh": "încercat să <a href=\"`x`\">reîmprospătați pagina</a>",
"crash_page_switch_instance": "am încercat să <a href=\"`x`\">folosim o altă instanță</a>",
+ "preferences_hide_shorts_label": "Ascunde YouTube Shorts: ",
"preferences_watch_history_label": "Activează istoricul: ",
"invidious": "Invidious",
"preferences_vr_mode_label": "Videoclipuri interactive de 360 de grade (necesită WebGL): ",
diff --git a/locales/ru.json b/locales/ru.json
index 906f00fc..a42a9d82 100644
--- a/locales/ru.json
+++ b/locales/ru.json
@@ -468,6 +468,7 @@
"Video unavailable": "Видео недоступно",
"preferences_save_player_pos_label": "Запоминать позицию: ",
"preferences_region_label": "Страна источник ",
+ "preferences_hide_shorts_label": "Скрыть YouTube Shorts: ",
"preferences_watch_history_label": "Включить историю просмотров: ",
"search_filters_title": "Фильтр",
"search_filters_duration_option_none": "Любой длины",
diff --git a/locales/si.json b/locales/si.json
index 4637cbd2..f9103ea5 100644
--- a/locales/si.json
+++ b/locales/si.json
@@ -40,6 +40,7 @@
"preferences_continue_label": "මීලඟට වාදනය කරන්න: ",
"preferences_continue_autoplay_label": "මීළඟ වීඩියෝව ස්වයංක්‍රීයව ධාවනය කරන්න: ",
"preferences_local_label": "Proxy වීඩියෝ: ",
+ "preferences_hide_shorts_label": "YouTube Shorts සඟවන්න: ",
"preferences_watch_history_label": "නැරඹුම් ඉතිහාසය සබල කරන්න: ",
"preferences_speed_label": "පෙරනිමි වේගය: ",
"preferences_quality_option_dash": "DASH (අනුවර්තිත ගුණත්වය)",
diff --git a/locales/sk.json b/locales/sk.json
index 8add0f57..e90a0852 100644
--- a/locales/sk.json
+++ b/locales/sk.json
@@ -108,6 +108,7 @@
"preferences_quality_dash_label": "Preferovaná video kvalita DASH: ",
"preferences_quality_option_dash": "DASH (adaptívna kvalita)",
"preferences_quality_option_small": "Malá",
+ "preferences_hide_shorts_label": "Skryť YouTube Shorts: ",
"preferences_watch_history_label": "Zapnúť históriu pozerania: ",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_1080p": "1080p",
diff --git a/locales/sl.json b/locales/sl.json
index c36ad522..11465ae4 100644
--- a/locales/sl.json
+++ b/locales/sl.json
@@ -272,6 +272,7 @@
"An alternative front-end to YouTube": "Alternativni vmesnik za YouTube",
"preferences_category_player": "Nastavitve predvajalnika",
"preferences_continue_label": "Privzeto predvajaj naslednjega: ",
+ "preferences_hide_shorts_label": "Skrij YouTube Shorts: ",
"preferences_watch_history_label": "Omogoči zgodovino ogledov: ",
"preferences_quality_option_medium": "srednja",
"preferences_quality_option_dash": "DASH (prilagodljiva kakovost)",
diff --git a/locales/sq.json b/locales/sq.json
index cdf4b605..6de0e267 100644
--- a/locales/sq.json
+++ b/locales/sq.json
@@ -444,6 +444,7 @@
"error_video_not_in_playlist": "Videoja e kërkuar sekziston në këtë luajlistë. <a href=\"`x`\">Klikoni këtu për faqen hyrëse të luajlistës.</a>",
"search_message_use_another_instance": "Mundeni edhe të <a href=\"`x`\">kërkoni në një instancë tjetër</a>.",
"search_filters_date_label": "Datë ngarkimi",
+ "preferences_hide_shorts_label": "Fshih YouTube Shorts: ",
"preferences_watch_history_label": "Aktivizo historik parjesh: ",
"Top enabled: ": "Me kryesueset të aktivizuara: ",
"preferences_video_loop_label": "Përsërite gjithmonë: ",
diff --git a/locales/sr.json b/locales/sr.json
index c6614ba5..4c9f98da 100644
--- a/locales/sr.json
+++ b/locales/sr.json
@@ -394,6 +394,7 @@
"generic_count_minutes_1": "{{count}} minuta",
"generic_count_minutes_2": "{{count}} minuta",
"preferences_quality_dash_option_720p": "720p",
+ "preferences_hide_shorts_label": "Сакриј YouTube Shorts: ",
"preferences_watch_history_label": "Omogući istoriju gledanja: ",
"user_saved_playlists": "Sačuvanih plejlista: `x`",
"Spanish (Spain)": "Španski (Španija)",
diff --git a/locales/sr_Cyrl.json b/locales/sr_Cyrl.json
index e6ab0f35..1d52b578 100644
--- a/locales/sr_Cyrl.json
+++ b/locales/sr_Cyrl.json
@@ -394,6 +394,7 @@
"generic_count_minutes_1": "{{count}} минута",
"generic_count_minutes_2": "{{count}} минута",
"preferences_quality_dash_option_720p": "720p",
+ "preferences_hide_shorts_label": "Сакриј YouTube Shorts: ",
"preferences_watch_history_label": "Омогући историју гледања: ",
"user_saved_playlists": "Сачуваних плејлиста: `x`",
"Spanish (Spain)": "Шпански (Шпанија)",
diff --git a/locales/sv-SE.json b/locales/sv-SE.json
index 8f050d98..86d6996d 100644
--- a/locales/sv-SE.json
+++ b/locales/sv-SE.json
@@ -384,6 +384,7 @@
"generic_count_minutes": "{{count}}minut",
"generic_count_minutes_plural": "{{count}}minuter",
"preferences_quality_dash_option_720p": "720p",
+ "preferences_hide_shorts_label": "Dölj YouTube Shorts: ",
"preferences_watch_history_label": "Aktivera visningshistorik: ",
"user_saved_playlists": "`x` sparade spellistor",
"Spanish (Spain)": "Spanska (Spanien)",
diff --git a/locales/ta.json b/locales/ta.json
index 89e58668..2c0ca849 100644
--- a/locales/ta.json
+++ b/locales/ta.json
@@ -54,6 +54,7 @@
"preferences_autoplay_label": "தன்னியக்க: ",
"preferences_continue_label": "இயல்பாக அடுத்து விளையாடுங்கள்: ",
"preferences_local_label": "பதிலாள் வீடியோக்கள்: ",
+ "preferences_hide_shorts_label": "YouTube Shorts மறைக்கவும் : ",
"preferences_watch_history_label": "கண்காணிப்பு வரலாற்றை இயக்கு: ",
"preferences_speed_label": "இயல்புநிலை வேகம்: ",
"preferences_quality_label": "விருப்பமான வீடியோ தரம்: ",
diff --git a/locales/tr.json b/locales/tr.json
index cf3f8987..8fbfa848 100644
--- a/locales/tr.json
+++ b/locales/tr.json
@@ -451,6 +451,7 @@
"Portuguese (auto-generated)": "Portekizce (Otomatik Oluşturuldu)",
"Spanish (Spain)": "İspanyolca (İspanya)",
"Vietnamese (auto-generated)": "Vietnamca (Otomatik Oluşturuldu)",
+ "preferences_hide_shorts_label": "YouTube Shorts gizle: ",
"preferences_watch_history_label": "İzleme Geçmişini Etkinleştir: ",
"search_message_use_another_instance": "Ayrıca <a href=\"`x`\">başka bir örnekte arayabilirsiniz</a>.",
"search_filters_type_option_all": "Herhangi Bir Tür",
diff --git a/locales/uk.json b/locales/uk.json
index b99923e2..b1b8da03 100644
--- a/locales/uk.json
+++ b/locales/uk.json
@@ -362,6 +362,7 @@
"user_created_playlists": "Створено списків відтворення: `x`",
"user_saved_playlists": "Збережено списків відтворення: `x`",
"Video unavailable": "Відео недоступне",
+ "preferences_hide_shorts_label": "Сховати YouTube Shorts: ",
"preferences_watch_history_label": "Історія переглядів: ",
"preferences_quality_dash_label": "Бажана DASH-якість відео: ",
"preferences_quality_dash_option_144p": "144p",
diff --git a/locales/vi.json b/locales/vi.json
index 9c4a5a15..0d8a7eec 100644
--- a/locales/vi.json
+++ b/locales/vi.json
@@ -343,6 +343,7 @@
"Song: ": "Ca khúc: ",
"Premieres in `x`": "Trình chiếu ở `x`",
"preferences_quality_dash_option_worst": "Tệ nhất",
+ "preferences_hide_shorts_label": "Ẩn YouTube Shorts: ",
"preferences_watch_history_label": "Bật lịch sử video đã xem ",
"preferences_quality_option_hd720": "HD720",
"unsubscribe": "hủy đăng kí",
diff --git a/locales/zh-CN.json b/locales/zh-CN.json
index f3bc660b..f4d51449 100644
--- a/locales/zh-CN.json
+++ b/locales/zh-CN.json
@@ -435,6 +435,7 @@
"French (auto-generated)": "法语 (自动生成)",
"Turkish (auto-generated)": "土耳其语 (自动生成)",
"Spanish (Spain)": "西班牙语 (西班牙)",
+ "preferences_hide_shorts_label": "隐藏 YouTube Shorts: ",
"preferences_watch_history_label": "启用观看历史: ",
"search_message_use_another_instance": "你也可以 <a href=\"`x`\">在另一实例上搜索</a>。",
"search_filters_title": "过滤器",
diff --git a/locales/zh-TW.json b/locales/zh-TW.json
index 77805349..23852f40 100644
--- a/locales/zh-TW.json
+++ b/locales/zh-TW.json
@@ -435,6 +435,7 @@
"Portuguese (Brazil)": "葡萄牙語(巴西)",
"Japanese (auto-generated)": "日語(自動產生)",
"Portuguese (auto-generated)": "葡萄牙語(自動產生)",
+ "preferences_hide_shorts_label": "隱藏 YouTube Shorts: ",
"preferences_watch_history_label": "啟用觀看紀錄: ",
"search_message_change_filters_or_query": "嘗試擴大您的查詢字詞與/或變更過濾條件。",
"search_filters_apply_button": "套用選定的過濾條件",
diff --git a/src/invidious/config.cr b/src/invidious/config.cr
index 6a36c335..c889a776 100644
--- a/src/invidious/config.cr
+++ b/src/invidious/config.cr
@@ -31,6 +31,7 @@ struct ConfigPreferences
property listen : Bool = false
property local : Bool = false
property locale : String = "en-US"
+ property hide_shorts : Bool = false
property watch_history : Bool = true
property max_results : Int32 = 40
property notifications_only : Bool = false
diff --git a/src/invidious/routes/preferences.cr b/src/invidious/routes/preferences.cr
index 39ca77c0..74c78dcc 100644
--- a/src/invidious/routes/preferences.cr
+++ b/src/invidious/routes/preferences.cr
@@ -140,6 +140,10 @@ module Invidious::Routes::PreferencesRoute
unseen_only ||= "off"
unseen_only = unseen_only == "on"
+ hide_shorts = env.params.body["hide_shorts"]?.try &.as(String)
+ hide_shorts ||= "off"
+ hide_shorts = hide_shorts == "on"
+
notifications_only = env.params.body["notifications_only"]?.try &.as(String)
notifications_only ||= "off"
notifications_only = notifications_only == "on"
@@ -174,6 +178,7 @@ module Invidious::Routes::PreferencesRoute
speed: speed,
thin_mode: thin_mode,
unseen_only: unseen_only,
+ hide_shorts: hide_shorts,
video_loop: video_loop,
volume: volume,
extend_desc: extend_desc,
diff --git a/src/invidious/user/preferences.cr b/src/invidious/user/preferences.cr
index 0a8525f3..76bb00de 100644
--- a/src/invidious/user/preferences.cr
+++ b/src/invidious/user/preferences.cr
@@ -52,6 +52,7 @@ struct Preferences
property speed : Float32 = CONFIG.default_user_preferences.speed
property thin_mode : Bool = CONFIG.default_user_preferences.thin_mode
property unseen_only : Bool = CONFIG.default_user_preferences.unseen_only
+ property hide_shorts : Bool = CONFIG.default_user_preferences.hide_shorts
property video_loop : Bool = CONFIG.default_user_preferences.video_loop
property extend_desc : Bool = CONFIG.default_user_preferences.extend_desc
property volume : Int32 = CONFIG.default_user_preferences.volume
diff --git a/src/invidious/users.cr b/src/invidious/users.cr
index 0b2d1ef5..529228ac 100644
--- a/src/invidious/users.cr
+++ b/src/invidious/users.cr
@@ -54,37 +54,69 @@ def get_subscription_feed(user, max_results = 40, page = 1)
# "SELECT cv.* FROM channel_videos cv JOIN users ON cv.ucid = any(users.subscriptions) WHERE users.email = $1 AND published > now() - interval '1 month' ORDER BY published DESC"
# "SELECT DISTINCT ON (cv.ucid) cv.* FROM channel_videos cv JOIN users ON cv.ucid = any(users.subscriptions) WHERE users.email = ? AND NOT cv.id = any(users.watched) AND published > now() - interval '1 month' ORDER BY ucid, published DESC"
- videos = PG_DB.query_all("SELECT DISTINCT ON (cv.ucid) cv.* " \
- "FROM channel_videos cv " \
- "JOIN users ON cv.ucid = any(users.subscriptions) " \
- "WHERE users.email = $1 AND NOT cv.id = any(users.watched) AND published > now() - interval '1 month' " \
- "ORDER BY ucid, published DESC", user.email, as: ChannelVideo)
+ if user.preferences.hide_shorts
+ videos = PG_DB.query_all("SELECT DISTINCT ON (cv.ucid) cv.* " \
+ "FROM channel_videos cv " \
+ "JOIN users ON cv.ucid = any(users.subscriptions) " \
+ "WHERE users.email = $1 AND NOT cv.id = any(users.watched) AND published > now() - interval '1 month' AND cv.length_seconds > 180 " \
+ "ORDER BY ucid, published DESC", user.email, as: ChannelVideo)
+ else
+ videos = PG_DB.query_all("SELECT DISTINCT ON (cv.ucid) cv.* " \
+ "FROM channel_videos cv " \
+ "JOIN users ON cv.ucid = any(users.subscriptions) " \
+ "WHERE users.email = $1 AND NOT cv.id = any(users.watched) AND published > now() - interval '1 month' " \
+ "ORDER BY ucid, published DESC", user.email, as: ChannelVideo)
+ end
else
# Show latest video from each channel
- videos = PG_DB.query_all("SELECT DISTINCT ON (cv.ucid) cv.* " \
- "FROM channel_videos cv " \
- "JOIN users ON cv.ucid = any(users.subscriptions) " \
- "WHERE users.email = $1 AND published > now() - interval '1 month' " \
- "ORDER BY ucid, published DESC", user.email, as: ChannelVideo)
+ if user.preferences.hide_shorts
+ videos = PG_DB.query_all("SELECT DISTINCT ON (cv.ucid) cv.* " \
+ "FROM channel_videos cv " \
+ "JOIN users ON cv.ucid = any(users.subscriptions) " \
+ "WHERE users.email = $1 AND published > now() - interval '1 month' AND cv.length_seconds > 180 " \
+ "ORDER BY ucid, published DESC", user.email, as: ChannelVideo)
+ else
+ videos = PG_DB.query_all("SELECT DISTINCT ON (cv.ucid) cv.* " \
+ "FROM channel_videos cv " \
+ "JOIN users ON cv.ucid = any(users.subscriptions) " \
+ "WHERE users.email = $1 AND published > now() - interval '1 month' " \
+ "ORDER BY ucid, published DESC", user.email, as: ChannelVideo)
+ end
end
videos.sort_by!(&.published).reverse!
else
if user.preferences.unseen_only
# Only show unwatched
- videos = PG_DB.query_all("SELECT cv.* " \
- "FROM channel_videos cv " \
- "JOIN users ON cv.ucid = any(users.subscriptions) " \
- "WHERE users.email = $1 AND NOT cv.id = any(users.watched) AND published > now() - interval '1 month' " \
- "ORDER BY published DESC LIMIT $2 OFFSET $3", user.email, limit, offset, as: ChannelVideo)
+ if user.preferences.hide_shorts
+ videos = PG_DB.query_all("SELECT cv.* " \
+ "FROM channel_videos cv " \
+ "JOIN users ON cv.ucid = any(users.subscriptions) " \
+ "WHERE users.email = $1 AND NOT cv.id = any(users.watched) AND published > now() - interval '1 month' AND cv.length_seconds > 180 " \
+ "ORDER BY published DESC LIMIT $2 OFFSET $3", user.email, limit, offset, as: ChannelVideo)
+ else
+ videos = PG_DB.query_all("SELECT cv.* " \
+ "FROM channel_videos cv " \
+ "JOIN users ON cv.ucid = any(users.subscriptions) " \
+ "WHERE users.email = $1 AND NOT cv.id = any(users.watched) AND published > now() - interval '1 month' " \
+ "ORDER BY published DESC LIMIT $2 OFFSET $3", user.email, limit, offset, as: ChannelVideo)
+ end
else
# Sort subscriptions as normal
- videos = PG_DB.query_all("SELECT cv.* " \
- "FROM channel_videos cv " \
- "JOIN users ON cv.ucid = any(users.subscriptions) " \
- "WHERE users.email = $1 AND published > now() - interval '1 month' " \
- "ORDER BY published DESC LIMIT $2 OFFSET $3", user.email, limit, offset, as: ChannelVideo)
+ if user.preferences.hide_shorts
+ videos = PG_DB.query_all("SELECT cv.* " \
+ "FROM channel_videos cv " \
+ "JOIN users ON cv.ucid = any(users.subscriptions) " \
+ "WHERE users.email = $1 AND published > now() - interval '1 month' AND cv.length_seconds > 180 " \
+ "ORDER BY published DESC LIMIT $2 OFFSET $3", user.email, limit, offset, as: ChannelVideo)
+ else
+ videos = PG_DB.query_all("SELECT cv.* " \
+ "FROM channel_videos cv " \
+ "JOIN users ON cv.ucid = any(users.subscriptions) " \
+ "WHERE users.email = $1 AND published > now() - interval '1 month' " \
+ "ORDER BY published DESC LIMIT $2 OFFSET $3", user.email, limit, offset, as: ChannelVideo)
+ end
end
end
diff --git a/src/invidious/views/user/preferences.ecr b/src/invidious/views/user/preferences.ecr
index cf8b5593..83847bea 100644
--- a/src/invidious/views/user/preferences.ecr
+++ b/src/invidious/views/user/preferences.ecr
@@ -216,6 +216,11 @@
<input name="watch_history" id="watch_history" type="checkbox" <% if preferences.watch_history %>checked<% end %>>
</div>
+ <div class="pure-control-group">
+ <label for="hide_shorts"><%= translate(locale, "preferences_hide_shorts_label") %></label>
+ <input name="hide_shorts" id="hide_shorts" type="checkbox" <% if preferences.hide_shorts %>checked<% end %>>
+ </div>
+
<div class="pure-control-group">
<label for="annotations_subscribed"><%= translate(locale, "preferences_annotations_subscribed_label") %></label>
<input name="annotations_subscribed" id="annotations_subscribed" type="checkbox" <% if preferences.annotations_subscribed %>checked<% end %>>