mirror of
https://git.nadeko.net/Fijxu/invidious.git
synced 2025-06-27 17:38:25 +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
|
||||
extend self
|
||||
@@exvpp_url : String = ""
|
||||
@@status : Int32 = 0
|
||||
@@exvpp_url : Array(String) = Array.new(CONFIG.invidious_companion.size, "")
|
||||
@@status : Array(Int32) = Array.new(CONFIG.invidious_companion.size, 0)
|
||||
|
||||
def check_backends
|
||||
check_companion()
|
||||
end
|
||||
|
||||
def check_companion
|
||||
begin
|
||||
response = HTTP::Client.get "#{CONFIG.invidious_companion.sample.private_url}/healthz"
|
||||
if response.status_code == 200
|
||||
check_videoplayback_proxy()
|
||||
else
|
||||
@@status = 0
|
||||
CONFIG.invidious_companion.each_with_index do |companion, index|
|
||||
spawn do
|
||||
begin
|
||||
response = HTTP::Client.get "#{companion.private_url}/healthz"
|
||||
if response.status_code == 200
|
||||
check_videoplayback_proxy(companion, index)
|
||||
else
|
||||
@@status[index] = 0
|
||||
end
|
||||
rescue
|
||||
@@status[index] = 0
|
||||
end
|
||||
end
|
||||
rescue
|
||||
@@status = 0
|
||||
end
|
||||
end
|
||||
|
||||
def check_videoplayback_proxy
|
||||
private def check_videoplayback_proxy(companion : Config::CompanionConfig, index : Int32)
|
||||
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 = "" if exvpp_url.nil?
|
||||
@@exvpp_url = exvpp_url
|
||||
@@exvpp_url[index] = exvpp_url
|
||||
if exvpp_url.empty?
|
||||
@@status = 2
|
||||
@@status[index] = 2
|
||||
return
|
||||
else
|
||||
begin
|
||||
exvpp_health = HTTP::Client.get "#{exvpp_url}/health"
|
||||
if exvpp_health.status_code == 200
|
||||
@@status = 2
|
||||
@@status[index] = 2
|
||||
return
|
||||
else
|
||||
@@status = 1
|
||||
@@status[index] = 1
|
||||
end
|
||||
rescue
|
||||
@@status = 1
|
||||
@@status[index] = 1
|
||||
end
|
||||
end
|
||||
rescue
|
||||
|
@ -9,7 +9,8 @@ module Invidious::Routes::API::Manifest
|
||||
region = env.params.query["region"]?
|
||||
|
||||
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}"
|
||||
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 = ""
|
||||
|
||||
if CONFIG.invidious_companion.present?
|
||||
extra_media_csp = " #{CONFIG.invidious_companion.sample.public_url}"
|
||||
extra_connect_csp = " #{CONFIG.invidious_companion.sample.public_url}"
|
||||
exvpp_url = BackendInfo.get_exvpp
|
||||
if !exvpp_url.empty?
|
||||
extra_media_csp += " #{exvpp_url}"
|
||||
extra_connect_csp += " #{exvpp_url}"
|
||||
if env.request.cookies[CONFIG.server_id_cookie_name]?.nil?
|
||||
env.response.cookies[CONFIG.server_id_cookie_name] = Invidious::User::Cookies.server_id(env.request.headers["Host"])
|
||||
end
|
||||
|
||||
begin
|
||||
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
|
||||
|
||||
|
@ -267,7 +267,8 @@ module Invidious::Routes::VideoPlayback
|
||||
# so we have a mechanism here to redirect to the latest version
|
||||
def self.latest_version(env)
|
||||
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}"
|
||||
end
|
||||
|
||||
|
@ -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)
|
||||
@ -214,7 +214,8 @@ module Invidious::Routes::Watch
|
||||
end
|
||||
|
||||
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"]
|
||||
.gsub("media-src", "media-src #{invidious_companion.public_url}")
|
||||
@ -350,8 +351,9 @@ module Invidious::Routes::Watch
|
||||
env.params.query["local"] = "true"
|
||||
|
||||
if (CONFIG.invidious_companion.present?)
|
||||
video = get_video(video_id)
|
||||
invidious_companion = CONFIG.invidious_companion.sample
|
||||
video = get_video(video_id, env: env)
|
||||
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}"
|
||||
else
|
||||
return Invidious::Routes::VideoPlayback.latest_version(env)
|
||||
|
@ -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
|
||||
|
@ -45,5 +45,29 @@ 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
|
||||
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
|
||||
|
@ -298,7 +298,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)
|
||||
@ -308,7 +308,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.insert(video)
|
||||
rescue ex
|
||||
Invidious::Database::Videos.delete(id)
|
||||
@ -316,7 +316,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
|
||||
|
||||
@ -324,11 +324,11 @@ 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)
|
||||
info = extract_video_info(video_id: id)
|
||||
def fetch_video(id, region, env)
|
||||
info = extract_video_info(video_id: id, env: env)
|
||||
|
||||
if reason = info["reason"]?
|
||||
if reason == "Video unavailable"
|
||||
|
@ -58,12 +58,12 @@ 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
|
||||
|
||||
# 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
|
||||
|
||||
@ -119,7 +119,7 @@ def extract_video_info(video_id : String)
|
||||
# following issue for an explanation about decrypted URLs:
|
||||
# https://github.com/TeamNewPipe/NewPipeExtractor/issues/562
|
||||
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
|
||||
|
||||
# Replace player response and reset reason
|
||||
@ -154,9 +154,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}.")
|
||||
|
@ -1,9 +1,7 @@
|
||||
<%
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
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()
|
||||
status = BackendInfo.get_status
|
||||
%>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<%= locale %>">
|
||||
@ -37,15 +35,6 @@
|
||||
<div class="pure-u-1 pure-u-md-4-24">
|
||||
<a href="/" class="index-link pure-menu-heading">
|
||||
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>
|
||||
</div>
|
||||
<div class="pure-u-1 pure-u-md-12-24 searchbar">
|
||||
@ -118,31 +107,35 @@
|
||||
</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;">
|
||||
<b>Switch Backend:</b>
|
||||
<% CONFIG.backends.each do | backend | %>
|
||||
<% backend = backend.split(CONFIG.backends_delimiter) %>
|
||||
<% if current_backend == backend[0] %>
|
||||
<a href="/switchbackend?backend_id=<%= backend[0] %>" style="text-decoration-line: underline; display: inline-block;">
|
||||
Backend<%= HTML.escape(backend[0]) %>
|
||||
<% if backend.size == 2 %>
|
||||
<%= HTML.escape(backend[1]) %>
|
||||
<% end %>
|
||||
<% CONFIG.invidious_companion.each_with_index do | backend, index | %>
|
||||
<% if current_backend == index %>
|
||||
<a href="/switchbackend?backend_id=<%= index.to_s %>" style="text-decoration-line: underline; 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>
|
||||
<% 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>
|
||||
<% else %>
|
||||
<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 %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if CONFIG.banner %>
|
||||
<div class="h-box">
|
||||
@ -152,202 +145,207 @@
|
||||
|
||||
<%= content %>
|
||||
|
||||
<% if buffer_footer %>
|
||||
<div id="footer_buffer"></div>
|
||||
<% end %>
|
||||
<% if buffer_footer %>
|
||||
<div id="footer_buffer"></div>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/handlers.js?v=<%= ASSET_COMMIT %>"></script>
|
||||
<script src="/js/themes.js?v=<%= ASSET_COMMIT %>"></script>
|
||||
<% if env.get? "user" %>
|
||||
<script src="/js/sse.js?v=<%= ASSET_COMMIT %>"></script>
|
||||
<script id="notification_data" type="application/json">
|
||||
<%=
|
||||
{
|
||||
"upload_text" => HTML.escape(translate(locale, "`x` uploaded a video")),
|
||||
"live_upload_text" => HTML.escape(translate(locale, "`x` is live"))
|
||||
}.to_pretty_json
|
||||
%>
|
||||
</script>
|
||||
<% if CONFIG.enable_user_notifications %>
|
||||
<script src="/js/notifications.js?v=<%= ASSET_COMMIT %>"></script>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/handlers.js?v=<%= ASSET_COMMIT %>"></script>
|
||||
<script src="/js/themes.js?v=<%= ASSET_COMMIT %>"></script>
|
||||
<% if env.get? "user" %>
|
||||
<script src="/js/sse.js?v=<%= ASSET_COMMIT %>"></script>
|
||||
<script id="notification_data" type="application/json">
|
||||
<%=
|
||||
{
|
||||
"upload_text" => HTML.escape(translate(locale, "`x` uploaded a video")),
|
||||
"live_upload_text" => HTML.escape(translate(locale, "`x` is live"))
|
||||
}.to_pretty_json
|
||||
%>
|
||||
</script>
|
||||
<% if CONFIG.enable_user_notifications %>
|
||||
<script src="/js/notifications.js?v=<%= ASSET_COMMIT %>"></script>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<footer class="pure-g">
|
||||
<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="pure-u-1 footer-content">
|
||||
<div class="footer-section pure-u-1-4" id="footer-custom-text">
|
||||
<b>Invidious</b>
|
||||
<% if CONFIG.footer %>
|
||||
<p><%=CONFIG.footer%></p>
|
||||
<% else %>
|
||||
<p><%=translate(locale, "default_invidious_footer_text")%></p>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<b class="footer-section-header"><%= translate(locale, "footer_navigation_section_header")%></b>
|
||||
<ul class="pure-menu-list footer-section-list">
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="/" title="<%= translate(locale, "footer_home_link")%>">
|
||||
<%= translate(locale, "footer_home_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="/feed/popular" title="<%= translate(locale, "Popular")%>">
|
||||
<%= translate(locale, "Popular") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="/feed/trending" title="<%= translate(locale, "Trending")%>" style="">
|
||||
<%= translate(locale, "Trending") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="/search" title="<%= translate(locale, "Search")%>">
|
||||
<%= translate(locale, "Search") %>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<b class="footer-section-header">Invidious</b>
|
||||
<ul class="pure-menu-list footer-section-list">
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="https://invidious.io" title="<%= translate(locale, "footer_project_homepage_link")%>">
|
||||
<%= translate(locale, "footer_project_homepage_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="https://github.com/iv-org/invidious" title="<%= translate(locale, "footer_source_code_link")%>">
|
||||
<%= translate(locale, "footer_source_code_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<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="">
|
||||
<%= translate(locale, "footer_issue_tracker_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="https://instances.invidious.io" title="<%= translate(locale, "footer_public_instances_link")%>">
|
||||
<%= translate(locale, "footer_public_instances_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="https://invidious.io/donate" title="<%= translate(locale, "footer_donate_link")%>">
|
||||
<%= translate(locale, "footer_donate_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="https://matrix.to/#/#invidious:matrix.org" title="<%= translate(locale, "footer_matrix_link")%>">
|
||||
<%= translate(locale, "footer_matrix_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<footer class="pure-g">
|
||||
<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="pure-u-1 footer-content">
|
||||
<div class="footer-section pure-u-1-4" id="footer-custom-text">
|
||||
<b>Invidious</b>
|
||||
<% if CONFIG.footer %>
|
||||
<p><%=CONFIG.footer%></p>
|
||||
<% else %>
|
||||
<p><%=translate(locale, "default_invidious_footer_text")%></p>
|
||||
<% end %>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<% 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">
|
||||
<b class="footer-section-header">
|
||||
<% if CONFIG.modified_source_code_url %>
|
||||
<%= translate(locale, "footer_instance_section_header_modified_source")%>
|
||||
<% else %>
|
||||
<%= translate(locale, "footer_instance_section_header")%>
|
||||
<% end %>
|
||||
</b>
|
||||
<ul class="pure-menu-list footer-section-list">
|
||||
<% if CONFIG.instance_maintainer_email %>
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_contact_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
<% if CONFIG.modified_source_code_url %>
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_instance_section_modified_source_code") %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
<% if CONFIG.footer_instance_tos_link %>
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_instance_section_tos") %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
<% if CONFIG.footer_instance_privacy_policy_link %>
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_instance_section_privacy_policy") %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<b class="footer-section-header"><%= translate(locale, "footer_navigation_section_header")%></b>
|
||||
<ul class="pure-menu-list footer-section-list">
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="/" title="<%= translate(locale, "footer_home_link")%>">
|
||||
<%= translate(locale, "footer_home_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="/feed/popular" title="<%= translate(locale, "Popular")%>">
|
||||
<%= translate(locale, "Popular") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="/feed/trending" title="<%= translate(locale, "Trending")%>" style="">
|
||||
<%= translate(locale, "Trending") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="/search" title="<%= translate(locale, "Search")%>">
|
||||
<%= translate(locale, "Search") %>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<b class="footer-section-header">Invidious</b>
|
||||
<ul class="pure-menu-list footer-section-list">
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="https://invidious.io" title="<%= translate(locale, "footer_project_homepage_link")%>">
|
||||
<%= translate(locale, "footer_project_homepage_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="https://github.com/iv-org/invidious" title="<%= translate(locale, "footer_source_code_link")%>">
|
||||
<%= translate(locale, "footer_source_code_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<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="">
|
||||
<%= translate(locale, "footer_issue_tracker_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="https://instances.invidious.io" title="<%= translate(locale, "footer_public_instances_link")%>">
|
||||
<%= translate(locale, "footer_public_instances_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="https://invidious.io/donate" title="<%= translate(locale, "footer_donate_link")%>">
|
||||
<%= translate(locale, "footer_donate_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="https://matrix.to/#/#invidious:matrix.org" title="<%= translate(locale, "footer_matrix_link")%>">
|
||||
<%= translate(locale, "footer_matrix_link") %>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<% if CONFIG.footer_instance_donate_link %>
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_instance_section_donate") %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% 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">
|
||||
<b class="footer-section-header">
|
||||
<% if CONFIG.modified_source_code_url %>
|
||||
<%= translate(locale, "footer_instance_section_header_modified_source")%>
|
||||
<% else %>
|
||||
<%= translate(locale, "footer_instance_section_header")%>
|
||||
<% end %>
|
||||
</b>
|
||||
<ul class="pure-menu-list footer-section-list">
|
||||
<% if CONFIG.instance_maintainer_email %>
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_contact_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
<% if CONFIG.modified_source_code_url %>
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_instance_section_modified_source_code") %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
<% if CONFIG.footer_instance_tos_link %>
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_instance_section_tos") %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
<% if CONFIG.footer_instance_privacy_policy_link %>
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_instance_section_privacy_policy") %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<% CONFIG.footer_instance_section_custom_fields.each do | field | %>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="<%=HTML.escape(field[1])%>" title="<%= HTML.escape(field[0]) %>">
|
||||
<%= HTML.escape(field[0]) %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
<% if CONFIG.footer_instance_donate_link %>
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_instance_section_donate") %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<div class="footer-section">
|
||||
<b class="footer-section-header"><%= translate(locale, "footer_support_section_header")%></b>
|
||||
<ul class="pure-menu-list footer-section-list">
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_report_bug_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="#" title="<%= translate(locale, "footer_faq_link")%>" style="">
|
||||
<%= translate(locale, "footer_faq_link") %>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="footer-footer">
|
||||
<div class="box">You are currently using Backend: <%= current_backend %></div>
|
||||
<% if !current_external_videoplayback_proxy.empty? %>
|
||||
<div class="box">External Videoplayback Proxy: <%= current_external_videoplayback_proxy %></div>
|
||||
<% 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 %>
|
||||
</span>
|
||||
<div class="right">
|
||||
<a href="/privacy" title="<%= translate(locale, "footer_privacy_policy_link")%>"><%= translate(locale, "footer_privacy_policy_link") %></a>
|
||||
<span> | </span>
|
||||
<a href="/licenses" title="<%= translate(locale, "footer_licences_link")%>"><%= translate(locale, "footer_licences_link") %></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-1 pure-u-md-2-24"></div>
|
||||
</footer>
|
||||
<% CONFIG.footer_instance_section_custom_fields.each do | field | %>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="<%=HTML.escape(field[1])%>" title="<%= HTML.escape(field[0]) %>">
|
||||
<%= HTML.escape(field[0]) %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="footer-section">
|
||||
<b class="footer-section-header"><%= translate(locale, "footer_support_section_header")%></b>
|
||||
<ul class="pure-menu-list footer-section-list">
|
||||
<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")%>">
|
||||
<%= translate(locale, "footer_report_bug_link") %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="pure-menu-item footer-section-item">
|
||||
<a href="#" title="<%= translate(locale, "footer_faq_link")%>" style="">
|
||||
<%= translate(locale, "footer_faq_link") %>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="footer-footer">
|
||||
<%
|
||||
if CONFIG.invidious_companion.present?
|
||||
current_backend = env.get("current_companion").as(Int32)
|
||||
%>
|
||||
<div class="box">You are currently using Backend: <%= CONFIG.invidious_companion[current_backend].public_url %></div>
|
||||
<% end %>
|
||||
<% if !current_external_videoplayback_proxy.empty? %>
|
||||
<div class="box">External Videoplayback Proxy: <%= current_external_videoplayback_proxy %></div>
|
||||
<% 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 %>
|
||||
</span>
|
||||
<div class="right">
|
||||
<a href="/privacy" title="<%= translate(locale, "footer_privacy_policy_link")%>"><%= translate(locale, "footer_privacy_policy_link") %></a>
|
||||
<span> | </span>
|
||||
<a href="/licenses" title="<%= translate(locale, "footer_licences_link")%>"><%= translate(locale, "footer_licences_link") %></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-1 pure-u-md-2-24"></div>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -63,7 +63,7 @@ struct CompanionConnectionPool
|
||||
end
|
||||
end
|
||||
|
||||
def client(&)
|
||||
def client(env : HTTP::Server::Context | Nil, &)
|
||||
conn = pool.checkout
|
||||
|
||||
begin
|
||||
@ -71,7 +71,13 @@ struct CompanionConnectionPool
|
||||
rescue ex
|
||||
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)
|
||||
|
||||
response = yield conn
|
||||
|
@ -456,6 +456,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 = {
|
||||
@ -492,7 +493,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
|
||||
@ -673,6 +674,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",
|
||||
@ -686,7 +688,7 @@ module YoutubeAPI
|
||||
# Send the POST request
|
||||
|
||||
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
|
||||
if (response.status_code != 200)
|
||||
raise Exception.new(
|
||||
|
Loading…
Reference in New Issue
Block a user