mirror of
https://git.nadeko.net/Fijxu/invidious.git
synced 2025-06-28 01:48:26 +00:00
feat: do all the backend balancing on the invidious side
This will make invidious easier to maintain and escalate without the need of an overcomplicated reverse proxy configuration and multiple invidious instances with each one with a different configuration (in this case, invidious companion)
This commit is contained in:
parent
ddf6802d76
commit
d47aa3dd6a
@ -1,45 +1,49 @@
|
|||||||
module BackendInfo
|
module BackendInfo
|
||||||
extend self
|
extend self
|
||||||
@@exvpp_url : String = ""
|
@@exvpp_url : Array(String) = Array.new(CONFIG.invidious_companion.size, "")
|
||||||
@@status : Int32 = 0
|
@@status : Array(Int32) = Array.new(CONFIG.invidious_companion.size, 0)
|
||||||
|
|
||||||
def check_backends
|
def check_backends
|
||||||
check_companion()
|
check_companion()
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_companion
|
def check_companion
|
||||||
begin
|
CONFIG.invidious_companion.each_with_index do |companion, index|
|
||||||
response = HTTP::Client.get "#{CONFIG.invidious_companion.sample.private_url}/healthz"
|
spawn do
|
||||||
if response.status_code == 200
|
begin
|
||||||
check_videoplayback_proxy()
|
response = HTTP::Client.get "#{companion.private_url}/healthz"
|
||||||
else
|
if response.status_code == 200
|
||||||
@@status = 0
|
check_videoplayback_proxy(companion, index)
|
||||||
|
else
|
||||||
|
@@status[index] = 0
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
@@status[index] = 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
rescue
|
|
||||||
@@status = 0
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_videoplayback_proxy
|
private def check_videoplayback_proxy(companion : Config::CompanionConfig, index : Int32)
|
||||||
begin
|
begin
|
||||||
info = HTTP::Client.get "#{CONFIG.invidious_companion.sample.private_url}/info"
|
info = HTTP::Client.get "#{companion.private_url}/info"
|
||||||
exvpp_url = JSON.parse(info.body)["external_videoplayback_proxy"]?.try &.to_s
|
exvpp_url = JSON.parse(info.body)["external_videoplayback_proxy"]?.try &.to_s
|
||||||
exvpp_url = "" if exvpp_url.nil?
|
exvpp_url = "" if exvpp_url.nil?
|
||||||
@@exvpp_url = exvpp_url
|
@@exvpp_url[index] = exvpp_url
|
||||||
if exvpp_url.empty?
|
if exvpp_url.empty?
|
||||||
@@status = 2
|
@@status[index] = 2
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
exvpp_health = HTTP::Client.get "#{exvpp_url}/health"
|
exvpp_health = HTTP::Client.get "#{exvpp_url}/health"
|
||||||
if exvpp_health.status_code == 200
|
if exvpp_health.status_code == 200
|
||||||
@@status = 2
|
@@status[index] = 2
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
@@status = 1
|
@@status[index] = 1
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
@@status = 1
|
@@status[index] = 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
|
@ -9,7 +9,8 @@ module Invidious::Routes::API::Manifest
|
|||||||
region = env.params.query["region"]?
|
region = env.params.query["region"]?
|
||||||
|
|
||||||
if CONFIG.invidious_companion.present?
|
if CONFIG.invidious_companion.present?
|
||||||
invidious_companion = CONFIG.invidious_companion.sample
|
current_companion = env.get("current_companion").as(Int32)
|
||||||
|
invidious_companion = CONFIG.invidious_companion[current_companion]
|
||||||
return env.redirect "#{invidious_companion.public_url}/api/manifest/dash/id/#{id}?#{env.params.query}"
|
return env.redirect "#{invidious_companion.public_url}/api/manifest/dash/id/#{id}?#{env.params.query}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
16
src/invidious/routes/backend_switcher.cr
Normal file
16
src/invidious/routes/backend_switcher.cr
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{% skip_file if flag?(:api_only) %}
|
||||||
|
|
||||||
|
module Invidious::Routes::BackendSwitcher
|
||||||
|
def self.switch(env)
|
||||||
|
referer = get_referer(env)
|
||||||
|
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
|
@ -24,12 +24,33 @@ module Invidious::Routes::BeforeAll
|
|||||||
extra_connect_csp = ""
|
extra_connect_csp = ""
|
||||||
|
|
||||||
if CONFIG.invidious_companion.present?
|
if CONFIG.invidious_companion.present?
|
||||||
extra_media_csp = " #{CONFIG.invidious_companion.sample.public_url}"
|
if env.request.cookies[CONFIG.server_id_cookie_name]?.nil?
|
||||||
extra_connect_csp = " #{CONFIG.invidious_companion.sample.public_url}"
|
env.response.cookies[CONFIG.server_id_cookie_name] = Invidious::User::Cookies.server_id(env.request.headers["Host"])
|
||||||
exvpp_url = BackendInfo.get_exvpp
|
end
|
||||||
if !exvpp_url.empty?
|
|
||||||
extra_media_csp += " #{exvpp_url}"
|
begin
|
||||||
extra_connect_csp += " #{exvpp_url}"
|
current_companion = env.request.cookies[CONFIG.server_id_cookie_name].value.try &.to_i
|
||||||
|
rescue
|
||||||
|
current_companion = rand(CONFIG.invidious_companion.size)
|
||||||
|
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(env.request.headers["Host"], current_companion)
|
||||||
|
end
|
||||||
|
|
||||||
|
env.set "current_companion", current_companion
|
||||||
|
|
||||||
|
CONFIG.invidious_companion.each do |companion|
|
||||||
|
extra_media_csp += " #{companion.public_url}"
|
||||||
|
extra_connect_csp += " #{companion.public_url}"
|
||||||
|
end
|
||||||
|
exvpp_urls = BackendInfo.get_exvpp
|
||||||
|
exvpp_urls.each do |exvpp_url|
|
||||||
|
if !exvpp_url.empty?
|
||||||
|
extra_media_csp += " #{exvpp_url}"
|
||||||
|
extra_connect_csp += " #{exvpp_url}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -267,7 +267,8 @@ module Invidious::Routes::VideoPlayback
|
|||||||
# so we have a mechanism here to redirect to the latest version
|
# so we have a mechanism here to redirect to the latest version
|
||||||
def self.latest_version(env)
|
def self.latest_version(env)
|
||||||
if CONFIG.invidious_companion.present?
|
if CONFIG.invidious_companion.present?
|
||||||
invidious_companion = CONFIG.invidious_companion.sample
|
current_companion = env.get("current_companion").as(Int32)
|
||||||
|
invidious_companion = CONFIG.invidious_companion[current_companion]
|
||||||
return env.redirect "#{invidious_companion.public_url}/latest_version?#{env.params.query}"
|
return env.redirect "#{invidious_companion.public_url}/latest_version?#{env.params.query}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ module Invidious::Routes::Watch
|
|||||||
env.params.query.delete_all("listen")
|
env.params.query.delete_all("listen")
|
||||||
|
|
||||||
begin
|
begin
|
||||||
video = get_video(id, region: params.region)
|
video = get_video(id, region: params.region, env: env)
|
||||||
rescue ex : NotFoundException
|
rescue ex : NotFoundException
|
||||||
LOGGER.error("get_video not found: #{id} : #{ex.message}")
|
LOGGER.error("get_video not found: #{id} : #{ex.message}")
|
||||||
return error_template(404, ex)
|
return error_template(404, ex)
|
||||||
@ -214,7 +214,8 @@ module Invidious::Routes::Watch
|
|||||||
end
|
end
|
||||||
|
|
||||||
if CONFIG.invidious_companion.present?
|
if CONFIG.invidious_companion.present?
|
||||||
invidious_companion = CONFIG.invidious_companion.sample
|
current_companion = env.get("current_companion").as(Int32)
|
||||||
|
invidious_companion = CONFIG.invidious_companion[current_companion]
|
||||||
env.response.headers["Content-Security-Policy"] =
|
env.response.headers["Content-Security-Policy"] =
|
||||||
env.response.headers["Content-Security-Policy"]
|
env.response.headers["Content-Security-Policy"]
|
||||||
.gsub("media-src", "media-src #{invidious_companion.public_url}")
|
.gsub("media-src", "media-src #{invidious_companion.public_url}")
|
||||||
@ -350,8 +351,9 @@ module Invidious::Routes::Watch
|
|||||||
env.params.query["local"] = "true"
|
env.params.query["local"] = "true"
|
||||||
|
|
||||||
if (CONFIG.invidious_companion.present?)
|
if (CONFIG.invidious_companion.present?)
|
||||||
video = get_video(video_id)
|
video = get_video(video_id, env: env)
|
||||||
invidious_companion = CONFIG.invidious_companion.sample
|
current_companion = env.get("current_companion").as(Int32)
|
||||||
|
invidious_companion = CONFIG.invidious_companion[current_companion]
|
||||||
return env.redirect "#{invidious_companion.public_url}/latest_version?#{env.params.query}"
|
return env.redirect "#{invidious_companion.public_url}/latest_version?#{env.params.query}"
|
||||||
else
|
else
|
||||||
return Invidious::Routes::VideoPlayback.latest_version(env)
|
return Invidious::Routes::VideoPlayback.latest_version(env)
|
||||||
|
@ -21,6 +21,7 @@ module Invidious::Routing
|
|||||||
get "/privacy", Routes::Misc, :privacy
|
get "/privacy", Routes::Misc, :privacy
|
||||||
get "/licenses", Routes::Misc, :licenses
|
get "/licenses", Routes::Misc, :licenses
|
||||||
get "/redirect", Routes::Misc, :cross_instance_redirect
|
get "/redirect", Routes::Misc, :cross_instance_redirect
|
||||||
|
get "/switchbackend", Routes::BackendSwitcher, :switch
|
||||||
|
|
||||||
self.register_channel_routes
|
self.register_channel_routes
|
||||||
self.register_watch_routes
|
self.register_watch_routes
|
||||||
|
@ -45,5 +45,29 @@ struct Invidious::User
|
|||||||
samesite: HTTP::Cookie::SameSite::Lax
|
samesite: HTTP::Cookie::SameSite::Lax
|
||||||
)
|
)
|
||||||
end
|
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
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
@ -298,7 +298,7 @@ struct Video
|
|||||||
predicate_bool upcoming, isUpcoming
|
predicate_bool upcoming, isUpcoming
|
||||||
end
|
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 (video = Invidious::Database::Videos.select(id)) && !region
|
||||||
# If record was last updated over 10 minutes ago, or video has since premiered,
|
# If record was last updated over 10 minutes ago, or video has since premiered,
|
||||||
# refresh (expire param in response lasts for 6 hours)
|
# refresh (expire param in response lasts for 6 hours)
|
||||||
@ -308,7 +308,7 @@ def get_video(id, refresh = true, region = nil, force_refresh = false)
|
|||||||
force_refresh ||
|
force_refresh ||
|
||||||
video.schema_version != Video::SCHEMA_VERSION # cache control
|
video.schema_version != Video::SCHEMA_VERSION # cache control
|
||||||
begin
|
begin
|
||||||
video = fetch_video(id, region)
|
video = fetch_video(id, region, env)
|
||||||
Invidious::Database::Videos.insert(video)
|
Invidious::Database::Videos.insert(video)
|
||||||
rescue ex
|
rescue ex
|
||||||
Invidious::Database::Videos.delete(id)
|
Invidious::Database::Videos.delete(id)
|
||||||
@ -316,7 +316,7 @@ def get_video(id, refresh = true, region = nil, force_refresh = false)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
video = fetch_video(id, region)
|
video = fetch_video(id, region, env)
|
||||||
Invidious::Database::Videos.insert(video) if !region
|
Invidious::Database::Videos.insert(video) if !region
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -324,11 +324,11 @@ def get_video(id, refresh = true, region = nil, force_refresh = false)
|
|||||||
rescue DB::Error
|
rescue DB::Error
|
||||||
# Avoid common `DB::PoolRetryAttemptsExceeded` error and friends
|
# Avoid common `DB::PoolRetryAttemptsExceeded` error and friends
|
||||||
# Note: All DB errors inherit from `DB::Error`
|
# Note: All DB errors inherit from `DB::Error`
|
||||||
return fetch_video(id, region)
|
return fetch_video(id, region, env)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_video(id, region)
|
def fetch_video(id, region, env)
|
||||||
info = extract_video_info(video_id: id)
|
info = extract_video_info(video_id: id, env: env)
|
||||||
|
|
||||||
if reason = info["reason"]?
|
if reason = info["reason"]?
|
||||||
if reason == "Video unavailable"
|
if reason == "Video unavailable"
|
||||||
|
@ -58,12 +58,12 @@ def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)?
|
|||||||
}
|
}
|
||||||
end
|
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
|
# Init client config for the API
|
||||||
client_config = YoutubeAPI::ClientConfig.new
|
client_config = YoutubeAPI::ClientConfig.new
|
||||||
|
|
||||||
# Fetch data from the player endpoint
|
# Fetch data from the player endpoint
|
||||||
player_response = YoutubeAPI.player(video_id: video_id, params: "2AMB", client_config: client_config)
|
player_response = YoutubeAPI.player(env: env, video_id: video_id, params: "2AMB", client_config: client_config)
|
||||||
|
|
||||||
playability_status = player_response.dig?("playabilityStatus", "status").try &.as_s
|
playability_status = player_response.dig?("playabilityStatus", "status").try &.as_s
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ def extract_video_info(video_id : String)
|
|||||||
# following issue for an explanation about decrypted URLs:
|
# following issue for an explanation about decrypted URLs:
|
||||||
# https://github.com/TeamNewPipe/NewPipeExtractor/issues/562
|
# https://github.com/TeamNewPipe/NewPipeExtractor/issues/562
|
||||||
client_config.client_type = YoutubeAPI::ClientType::AndroidTestSuite
|
client_config.client_type = YoutubeAPI::ClientType::AndroidTestSuite
|
||||||
new_player_response = try_fetch_streaming_data(video_id, client_config)
|
new_player_response = try_fetch_streaming_data(video_id, client_config, env)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Replace player response and reset reason
|
# Replace player response and reset reason
|
||||||
@ -154,9 +154,9 @@ def extract_video_info(video_id : String)
|
|||||||
return params
|
return params
|
||||||
end
|
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.")
|
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"]
|
playability_status = response["playabilityStatus"]["status"]
|
||||||
LOGGER.debug("try_fetch_streaming_data: [#{id}] Got playabilityStatus == #{playability_status}.")
|
LOGGER.debug("try_fetch_streaming_data: [#{id}] Got playabilityStatus == #{playability_status}.")
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
<%
|
<%
|
||||||
locale = env.get("preferences").as(Preferences).locale
|
locale = env.get("preferences").as(Preferences).locale
|
||||||
dark_mode = env.get("preferences").as(Preferences).dark_mode
|
dark_mode = env.get("preferences").as(Preferences).dark_mode
|
||||||
current_backend = env.request.cookies[CONFIG.server_id_cookie_name]?.try &.value || env.request.headers["Host"]
|
|
||||||
current_external_videoplayback_proxy = Invidious::HttpServer::Utils.get_external_proxy()
|
current_external_videoplayback_proxy = Invidious::HttpServer::Utils.get_external_proxy()
|
||||||
status = BackendInfo.get_status
|
|
||||||
%>
|
%>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="<%= locale %>">
|
<html lang="<%= locale %>">
|
||||||
@ -37,15 +35,6 @@
|
|||||||
<div class="pure-u-1 pure-u-md-4-24">
|
<div class="pure-u-1 pure-u-md-4-24">
|
||||||
<a href="/" class="index-link pure-menu-heading">
|
<a href="/" class="index-link pure-menu-heading">
|
||||||
Invidious
|
Invidious
|
||||||
<% if status == 0 %>
|
|
||||||
<span style="color: #fd4848;">•</span>
|
|
||||||
<% end %>
|
|
||||||
<% if status == 1 %>
|
|
||||||
<span style="color: #d06925;">•</span>
|
|
||||||
<% end %>
|
|
||||||
<% if status == 2 %>
|
|
||||||
<span style="color: #42ae3c;">•</span>
|
|
||||||
<% end %>
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-u-md-12-24 searchbar">
|
<div class="pure-u-1 pure-u-md-12-24 searchbar">
|
||||||
@ -118,31 +107,35 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if !CONFIG.backends.empty? %>
|
<%
|
||||||
<% if !CONFIG.backend_domains.includes?(env.request.headers["Host"]) %>
|
if CONFIG.invidious_companion.present?
|
||||||
|
current_backend = env.get("current_companion").as(Int32)
|
||||||
|
status = BackendInfo.get_status
|
||||||
|
%>
|
||||||
<div class="h-box" style="margin-bottom: 10px;">
|
<div class="h-box" style="margin-bottom: 10px;">
|
||||||
<b>Switch Backend:</b>
|
<b>Switch Backend:</b>
|
||||||
<% CONFIG.backends.each do | backend | %>
|
<% CONFIG.invidious_companion.each_with_index do | backend, index | %>
|
||||||
<% backend = backend.split(CONFIG.backends_delimiter) %>
|
<% if current_backend == index %>
|
||||||
<% if current_backend == backend[0] %>
|
<a href="/switchbackend?backend_id=<%= index.to_s %>" style="text-decoration-line: underline; display: inline-block;">
|
||||||
<a href="/switchbackend?backend_id=<%= backend[0] %>" style="text-decoration-line: underline; display: inline-block;">
|
Backend<%= HTML.escape((index+1).to_s) %>
|
||||||
Backend<%= HTML.escape(backend[0]) %>
|
<span style="color:
|
||||||
<% if backend.size == 2 %>
|
<% if status[index] == 0 %> #fd4848; <% end %>
|
||||||
<%= HTML.escape(backend[1]) %>
|
<% if status[index] == 1 %> #d06925; <% end %>
|
||||||
<% end %>
|
<% if status[index] == 2 %> #42ae3c; <% end %>
|
||||||
|
">•</span>
|
||||||
|
<% else %>
|
||||||
|
<a href="/switchbackend?backend_id=<%= index.to_s %>" style="display: inline-block;">
|
||||||
|
Backend<%= HTML.escape((index+1).to_s) %>
|
||||||
|
<span style="color:
|
||||||
|
<% if status[index] == 0 %> #fd4848; <% end %>
|
||||||
|
<% if status[index] == 1 %> #d06925; <% end %>
|
||||||
|
<% if status[index] == 2 %> #42ae3c; <% end %>
|
||||||
|
">•</span>
|
||||||
|
<% end %>
|
||||||
</a> <span> | </span>
|
</a> <span> | </span>
|
||||||
<% else %>
|
<% end %>
|
||||||
<a href="/switchbackend?backend_id=<%= backend[0] %>" style="display: inline-block;">
|
|
||||||
Backend<%= HTML.escape(backend[0]) %>
|
|
||||||
<% if backend.size == 2 %>
|
|
||||||
<%= HTML.escape(backend[1]) %>
|
|
||||||
<% end %>
|
|
||||||
</a> <span> | </span>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<% if CONFIG.banner %>
|
<% if CONFIG.banner %>
|
||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
@ -152,202 +145,207 @@
|
|||||||
|
|
||||||
<%= content %>
|
<%= content %>
|
||||||
|
|
||||||
<% if buffer_footer %>
|
<% if buffer_footer %>
|
||||||
<div id="footer_buffer"></div>
|
<div id="footer_buffer"></div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/js/handlers.js?v=<%= ASSET_COMMIT %>"></script>
|
<script src="/js/handlers.js?v=<%= ASSET_COMMIT %>"></script>
|
||||||
<script src="/js/themes.js?v=<%= ASSET_COMMIT %>"></script>
|
<script src="/js/themes.js?v=<%= ASSET_COMMIT %>"></script>
|
||||||
<% if env.get? "user" %>
|
<% if env.get? "user" %>
|
||||||
<script src="/js/sse.js?v=<%= ASSET_COMMIT %>"></script>
|
<script src="/js/sse.js?v=<%= ASSET_COMMIT %>"></script>
|
||||||
<script id="notification_data" type="application/json">
|
<script id="notification_data" type="application/json">
|
||||||
<%=
|
<%=
|
||||||
{
|
{
|
||||||
"upload_text" => HTML.escape(translate(locale, "`x` uploaded a video")),
|
"upload_text" => HTML.escape(translate(locale, "`x` uploaded a video")),
|
||||||
"live_upload_text" => HTML.escape(translate(locale, "`x` is live"))
|
"live_upload_text" => HTML.escape(translate(locale, "`x` is live"))
|
||||||
}.to_pretty_json
|
}.to_pretty_json
|
||||||
%>
|
%>
|
||||||
</script>
|
</script>
|
||||||
<% if CONFIG.enable_user_notifications %>
|
<% if CONFIG.enable_user_notifications %>
|
||||||
<script src="/js/notifications.js?v=<%= ASSET_COMMIT %>"></script>
|
<script src="/js/notifications.js?v=<%= ASSET_COMMIT %>"></script>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<footer class="pure-g">
|
<footer class="pure-g">
|
||||||
<div class="pure-u-1 pure-u-md-2-24"></div>
|
<div class="pure-u-1 pure-u-md-2-24"></div>
|
||||||
<div class="h-box pure-u-1 pure-u-md-20-24" id="footer-content-container">
|
<div class="h-box pure-u-1 pure-u-md-20-24" id="footer-content-container">
|
||||||
<div class="pure-u-1 footer-content">
|
<div class="pure-u-1 footer-content">
|
||||||
<div class="footer-section pure-u-1-4" id="footer-custom-text">
|
<div class="footer-section pure-u-1-4" id="footer-custom-text">
|
||||||
<b>Invidious</b>
|
<b>Invidious</b>
|
||||||
<% if CONFIG.footer %>
|
<% if CONFIG.footer %>
|
||||||
<p><%=CONFIG.footer%></p>
|
<p><%=CONFIG.footer%></p>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p><%=translate(locale, "default_invidious_footer_text")%></p>
|
<p><%=translate(locale, "default_invidious_footer_text")%></p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-section">
|
<div class="footer-section">
|
||||||
<b class="footer-section-header"><%= translate(locale, "footer_navigation_section_header")%></b>
|
<b class="footer-section-header"><%= translate(locale, "footer_navigation_section_header")%></b>
|
||||||
<ul class="pure-menu-list footer-section-list">
|
<ul class="pure-menu-list footer-section-list">
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="/" title="<%= translate(locale, "footer_home_link")%>">
|
<a href="/" title="<%= translate(locale, "footer_home_link")%>">
|
||||||
<%= translate(locale, "footer_home_link") %>
|
<%= translate(locale, "footer_home_link") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="/feed/popular" title="<%= translate(locale, "Popular")%>">
|
<a href="/feed/popular" title="<%= translate(locale, "Popular")%>">
|
||||||
<%= translate(locale, "Popular") %>
|
<%= translate(locale, "Popular") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="/feed/trending" title="<%= translate(locale, "Trending")%>" style="">
|
<a href="/feed/trending" title="<%= translate(locale, "Trending")%>" style="">
|
||||||
<%= translate(locale, "Trending") %>
|
<%= translate(locale, "Trending") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="/search" title="<%= translate(locale, "Search")%>">
|
<a href="/search" title="<%= translate(locale, "Search")%>">
|
||||||
<%= translate(locale, "Search") %>
|
<%= translate(locale, "Search") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-section">
|
<div class="footer-section">
|
||||||
<b class="footer-section-header">Invidious</b>
|
<b class="footer-section-header">Invidious</b>
|
||||||
<ul class="pure-menu-list footer-section-list">
|
<ul class="pure-menu-list footer-section-list">
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="https://invidious.io" title="<%= translate(locale, "footer_project_homepage_link")%>">
|
<a href="https://invidious.io" title="<%= translate(locale, "footer_project_homepage_link")%>">
|
||||||
<%= translate(locale, "footer_project_homepage_link") %>
|
<%= translate(locale, "footer_project_homepage_link") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="https://github.com/iv-org/invidious" title="<%= translate(locale, "footer_source_code_link")%>">
|
<a href="https://github.com/iv-org/invidious" title="<%= translate(locale, "footer_source_code_link")%>">
|
||||||
<%= translate(locale, "footer_source_code_link") %>
|
<%= translate(locale, "footer_source_code_link") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="https://github.com/iv-org/invidious/issues" title="<%= translate(locale, "footer_issue_tracker_link")%>" style="">
|
<a href="https://github.com/iv-org/invidious/issues" title="<%= translate(locale, "footer_issue_tracker_link")%>" style="">
|
||||||
<%= translate(locale, "footer_issue_tracker_link") %>
|
<%= translate(locale, "footer_issue_tracker_link") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="https://instances.invidious.io" title="<%= translate(locale, "footer_public_instances_link")%>">
|
<a href="https://instances.invidious.io" title="<%= translate(locale, "footer_public_instances_link")%>">
|
||||||
<%= translate(locale, "footer_public_instances_link") %>
|
<%= translate(locale, "footer_public_instances_link") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="https://invidious.io/donate" title="<%= translate(locale, "footer_donate_link")%>">
|
<a href="https://invidious.io/donate" title="<%= translate(locale, "footer_donate_link")%>">
|
||||||
<%= translate(locale, "footer_donate_link") %>
|
<%= translate(locale, "footer_donate_link") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="https://matrix.to/#/#invidious:matrix.org" title="<%= translate(locale, "footer_matrix_link")%>">
|
<a href="https://matrix.to/#/#invidious:matrix.org" title="<%= translate(locale, "footer_matrix_link")%>">
|
||||||
<%= translate(locale, "footer_matrix_link") %>
|
<%= translate(locale, "footer_matrix_link") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<% if CONFIG.instance_maintainer_email || CONFIG.modified_source_code_url || CONFIG.footer_instance_tos_link || CONFIG.footer_instance_privacy_policy_link %>
|
<% if CONFIG.instance_maintainer_email || CONFIG.modified_source_code_url || CONFIG.footer_instance_tos_link || CONFIG.footer_instance_privacy_policy_link %>
|
||||||
<div class="footer-section">
|
<div class="footer-section">
|
||||||
<b class="footer-section-header">
|
<b class="footer-section-header">
|
||||||
<% if CONFIG.modified_source_code_url %>
|
<% if CONFIG.modified_source_code_url %>
|
||||||
<%= translate(locale, "footer_instance_section_header_modified_source")%>
|
<%= translate(locale, "footer_instance_section_header_modified_source")%>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= translate(locale, "footer_instance_section_header")%>
|
<%= translate(locale, "footer_instance_section_header")%>
|
||||||
<% end %>
|
<% end %>
|
||||||
</b>
|
</b>
|
||||||
<ul class="pure-menu-list footer-section-list">
|
<ul class="pure-menu-list footer-section-list">
|
||||||
<% if CONFIG.instance_maintainer_email %>
|
<% if CONFIG.instance_maintainer_email %>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="<%=HTML.escape("mailto:#{CONFIG.instance_maintainer_email.not_nil!}")%>" title="<%= translate(locale, "footer_contact_link")%>">
|
<a href="<%=HTML.escape("mailto:#{CONFIG.instance_maintainer_email.not_nil!}")%>" title="<%= translate(locale, "footer_contact_link")%>">
|
||||||
<%= translate(locale, "footer_contact_link") %>
|
<%= translate(locale, "footer_contact_link") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if CONFIG.modified_source_code_url %>
|
<% if CONFIG.modified_source_code_url %>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="<%=HTML.escape(CONFIG.modified_source_code_url.not_nil!)%>" title="<%= translate(locale, "footer_instance_section_modified_source_code")%>">
|
<a href="<%=HTML.escape(CONFIG.modified_source_code_url.not_nil!)%>" title="<%= translate(locale, "footer_instance_section_modified_source_code")%>">
|
||||||
<%= translate(locale, "footer_instance_section_modified_source_code") %>
|
<%= translate(locale, "footer_instance_section_modified_source_code") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if CONFIG.footer_instance_tos_link %>
|
<% if CONFIG.footer_instance_tos_link %>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="<%=HTML.escape(CONFIG.footer_instance_tos_link.not_nil!)%>" title="<%= translate(locale, "footer_instance_section_tos")%>">
|
<a href="<%=HTML.escape(CONFIG.footer_instance_tos_link.not_nil!)%>" title="<%= translate(locale, "footer_instance_section_tos")%>">
|
||||||
<%= translate(locale, "footer_instance_section_tos") %>
|
<%= translate(locale, "footer_instance_section_tos") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if CONFIG.footer_instance_privacy_policy_link %>
|
<% if CONFIG.footer_instance_privacy_policy_link %>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="<%=HTML.escape(CONFIG.footer_instance_privacy_policy_link.not_nil!)%>" title="<%= translate(locale, "footer_instance_section_privacy_policy")%>">
|
<a href="<%=HTML.escape(CONFIG.footer_instance_privacy_policy_link.not_nil!)%>" title="<%= translate(locale, "footer_instance_section_privacy_policy")%>">
|
||||||
<%= translate(locale, "footer_instance_section_privacy_policy") %>
|
<%= translate(locale, "footer_instance_section_privacy_policy") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if CONFIG.footer_instance_donate_link %>
|
<% if CONFIG.footer_instance_donate_link %>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="<%=HTML.escape(CONFIG.footer_instance_donate_link.not_nil!)%>" title="<%= translate(locale, "footer_instance_section_donate")%>">
|
<a href="<%=HTML.escape(CONFIG.footer_instance_donate_link.not_nil!)%>" title="<%= translate(locale, "footer_instance_section_donate")%>">
|
||||||
<%= translate(locale, "footer_instance_section_donate") %>
|
<%= translate(locale, "footer_instance_section_donate") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% CONFIG.footer_instance_section_custom_fields.each do | field | %>
|
<% CONFIG.footer_instance_section_custom_fields.each do | field | %>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="<%=HTML.escape(field[1])%>" title="<%= HTML.escape(field[0]) %>">
|
<a href="<%=HTML.escape(field[1])%>" title="<%= HTML.escape(field[0]) %>">
|
||||||
<%= HTML.escape(field[0]) %>
|
<%= HTML.escape(field[0]) %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="footer-section">
|
<div class="footer-section">
|
||||||
<b class="footer-section-header"><%= translate(locale, "footer_support_section_header")%></b>
|
<b class="footer-section-header"><%= translate(locale, "footer_support_section_header")%></b>
|
||||||
<ul class="pure-menu-list footer-section-list">
|
<ul class="pure-menu-list footer-section-list">
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="https://github.com/iv-org/invidious/issues/new" title="<%= translate(locale, "footer_report_bug_link")%>">
|
<a href="https://github.com/iv-org/invidious/issues/new" title="<%= translate(locale, "footer_report_bug_link")%>">
|
||||||
<%= translate(locale, "footer_report_bug_link") %>
|
<%= translate(locale, "footer_report_bug_link") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="pure-menu-item footer-section-item">
|
<li class="pure-menu-item footer-section-item">
|
||||||
<a href="#" title="<%= translate(locale, "footer_faq_link")%>" style="">
|
<a href="#" title="<%= translate(locale, "footer_faq_link")%>" style="">
|
||||||
<%= translate(locale, "footer_faq_link") %>
|
<%= translate(locale, "footer_faq_link") %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div class="footer-footer">
|
<div class="footer-footer">
|
||||||
<div class="box">You are currently using Backend: <%= current_backend %></div>
|
<%
|
||||||
<% if !current_external_videoplayback_proxy.empty? %>
|
if CONFIG.invidious_companion.present?
|
||||||
<div class="box">External Videoplayback Proxy: <%= current_external_videoplayback_proxy %></div>
|
current_backend = env.get("current_companion").as(Int32)
|
||||||
<% end %>
|
%>
|
||||||
<span class="left">
|
<div class="box">You are currently using Backend: <%= CONFIG.invidious_companion[current_backend].public_url %></div>
|
||||||
<% if CONFIG.modified_source_code_url %>
|
<% end %>
|
||||||
<%= translate(locale, "footer_current_version_modified") %>
|
<% if !current_external_videoplayback_proxy.empty? %>
|
||||||
<% else %>
|
<div class="box">External Videoplayback Proxy: <%= current_external_videoplayback_proxy %></div>
|
||||||
<%= translate(locale, "Current version: ") %>
|
<% end %>
|
||||||
<% end %>
|
<span class="left">
|
||||||
|
<% if CONFIG.modified_source_code_url %>
|
||||||
|
<%= translate(locale, "footer_current_version_modified") %>
|
||||||
|
<% else %>
|
||||||
|
<%= translate(locale, "Current version: ") %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<%= CURRENT_VERSION %>-<%= CURRENT_COMMIT %> @ <%= CURRENT_BRANCH %>
|
<%= CURRENT_VERSION %>-<%= CURRENT_COMMIT %> @ <%= CURRENT_BRANCH %>
|
||||||
</span>
|
</span>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<a href="/privacy" title="<%= translate(locale, "footer_privacy_policy_link")%>"><%= translate(locale, "footer_privacy_policy_link") %></a>
|
<a href="/privacy" title="<%= translate(locale, "footer_privacy_policy_link")%>"><%= translate(locale, "footer_privacy_policy_link") %></a>
|
||||||
<span> | </span>
|
<span> | </span>
|
||||||
<a href="/licenses" title="<%= translate(locale, "footer_licences_link")%>"><%= translate(locale, "footer_licences_link") %></a>
|
<a href="/licenses" title="<%= translate(locale, "footer_licences_link")%>"><%= translate(locale, "footer_licences_link") %></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-u-md-2-24"></div>
|
<div class="pure-u-1 pure-u-md-2-24"></div>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -63,7 +63,7 @@ struct CompanionConnectionPool
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def client(&)
|
def client(env : HTTP::Server::Context | Nil, &)
|
||||||
conn = pool.checkout
|
conn = pool.checkout
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@ -71,7 +71,13 @@ struct CompanionConnectionPool
|
|||||||
rescue ex
|
rescue ex
|
||||||
conn.close
|
conn.close
|
||||||
|
|
||||||
companion = CONFIG.invidious_companion.sample
|
if env.nil?
|
||||||
|
companion = CONFIG.invidious_companion.sample
|
||||||
|
else
|
||||||
|
current_companion = env.get("current_companion").as(Int32)
|
||||||
|
companion = CONFIG.invidious_companion[current_companion]
|
||||||
|
end
|
||||||
|
|
||||||
conn = make_client(companion.private_url, use_http_proxy: false)
|
conn = make_client(companion.private_url, use_http_proxy: false)
|
||||||
|
|
||||||
response = yield conn
|
response = yield conn
|
||||||
|
@ -456,6 +456,7 @@ module YoutubeAPI
|
|||||||
*, # Force the following parameters to be passed by name
|
*, # Force the following parameters to be passed by name
|
||||||
params : String,
|
params : String,
|
||||||
client_config : ClientConfig | Nil = nil,
|
client_config : ClientConfig | Nil = nil,
|
||||||
|
env : HTTP::Server::Context | Nil = nil,
|
||||||
)
|
)
|
||||||
# Playback context, separate because it can be different between clients
|
# Playback context, separate because it can be different between clients
|
||||||
playback_ctx = {
|
playback_ctx = {
|
||||||
@ -492,7 +493,7 @@ module YoutubeAPI
|
|||||||
end
|
end
|
||||||
|
|
||||||
if CONFIG.invidious_companion.present?
|
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
|
else
|
||||||
return self._post_json("/youtubei/v1/player", data, client_config)
|
return self._post_json("/youtubei/v1/player", data, client_config)
|
||||||
end
|
end
|
||||||
@ -673,6 +674,7 @@ module YoutubeAPI
|
|||||||
def _post_invidious_companion(
|
def _post_invidious_companion(
|
||||||
endpoint : String,
|
endpoint : String,
|
||||||
data : Hash,
|
data : Hash,
|
||||||
|
env : HTTP::Server::Context | Nil,
|
||||||
) : Hash(String, JSON::Any)
|
) : Hash(String, JSON::Any)
|
||||||
headers = HTTP::Headers{
|
headers = HTTP::Headers{
|
||||||
"Content-Type" => "application/json; charset=UTF-8",
|
"Content-Type" => "application/json; charset=UTF-8",
|
||||||
@ -686,7 +688,7 @@ module YoutubeAPI
|
|||||||
# Send the POST request
|
# Send the POST request
|
||||||
|
|
||||||
begin
|
begin
|
||||||
response = COMPANION_POOL.client &.post(endpoint, headers: headers, body: data.to_json)
|
response = COMPANION_POOL.client(env, &.post(endpoint, headers: headers, body: data.to_json))
|
||||||
body = response.body
|
body = response.body
|
||||||
if (response.status_code != 200)
|
if (response.status_code != 200)
|
||||||
raise Exception.new(
|
raise Exception.new(
|
||||||
|
Loading…
Reference in New Issue
Block a user