From 47237d21db91b574270dea211cd0a13e5b7b0e47 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Sat, 24 Jan 2026 19:47:37 -0300 Subject: [PATCH] Rework companion switcher --- src/invidious.cr | 12 +- src/invidious/config.cr | 2 +- src/invidious/helpers/backend_info.cr | 110 -------- src/invidious/helpers/companion_status.cr | 138 ++++++++++ src/invidious/jobs/backend_checker.cr | 14 - src/invidious/jobs/companion_checker.cr | 17 ++ src/invidious/routes/api/manifest.cr | 2 +- src/invidious/routes/before_all.cr | 299 +++++++++++++++------- src/invidious/routes/video_playback.cr | 2 +- src/invidious/user/cookies.cr | 5 +- src/invidious/views/components/player.ecr | 14 +- src/invidious/views/template.ecr | 49 ++-- src/invidious/yt_backend/youtube_api.cr | 22 +- 13 files changed, 423 insertions(+), 263 deletions(-) delete mode 100644 src/invidious/helpers/backend_info.cr create mode 100644 src/invidious/helpers/companion_status.cr delete mode 100644 src/invidious/jobs/backend_checker.cr create mode 100644 src/invidious/jobs/companion_checker.cr diff --git a/src/invidious.cr b/src/invidious.cr index cb276eda..ec7cef15 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -202,10 +202,16 @@ 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 +COMPANION_STATUS = begin + CompanionStatus.new if CONFIG.invidious_companion.present? +rescue + nil +end + +if companion_status = COMPANION_STATUS + Invidious::Jobs.register Invidious::Jobs::CompanionChecker.new(companion_status) 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") + LOGGER.info("jobs: Disabling CompanionChecker job. invidious-companion and their respective external video playback proxies (if set on invidious-companion) will not be checked") end Invidious::Jobs.start_all diff --git a/src/invidious/config.cr b/src/invidious/config.cr index 0bf0ca43..b477b19c 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -209,7 +209,7 @@ class Config property invidious_companion_key : String = "" # Invidious companion prefix for numbered domains - property invidious_companion_prefix : String = "" + property invidious_companion_prefix : String? = nil # Saved cookies in "name1=value1; name2=value2..." format @[YAML::Field(converter: Preferences::StringToCookies)] diff --git a/src/invidious/helpers/backend_info.cr b/src/invidious/helpers/backend_info.cr deleted file mode 100644 index ed60cc80..00000000 --- a/src/invidious/helpers/backend_info.cr +++ /dev/null @@ -1,110 +0,0 @@ -module BackendInfo - extend self - - enum Status - Dead = 0 - Blocked = 1 - Working = 2 - end - - struct CompanionData - include JSON::Serializable - property blocked : Bool = false - @[JSON::Field(key: "blockedCount")] - property blocked_count : Int64 = 0 - 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(CONFIG.check_backends_path) - if response.status_code == 200 - if response.content_type == "application/json" - body = response.body - status_json = CompanionData.from_json(body) - if status_json.blocked - updated_ends, updated_status = self.set_status(index, updated_ends, updated_status, Status::Blocked, true) - else - updated_ends, updated_status = self.set_status(index, updated_ends, updated_status, Status::Working, true) - end - else - updated_ends, updated_status = self.set_status(index, updated_ends, updated_status, Status::Working, true) - end - self.generate_csp([companion.public_url, companion.i2p_public_url], index) - else - _, updated_status = self.set_status(index, updated_ends, updated_status, Status::Dead, false) - end - rescue - _, updated_status = self.set_status(index, updated_ends, updated_status, Status::Dead, false) - 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(URI), index : Int32? = nil) - @@csp_mutex.synchronize do - @@csp[index] = "" - companion_url.each do |url| - fixed_url = "#{url.scheme}://#{url.host}#{url.port ? ":#{url.port}" : ""}" - @@csp[index] += " #{fixed_url}" - end - end - end - - private def set_status(index, updated_ends, updated_status, status, push = false) - @@check_mutex.synchronize do - updated_status[index] = status.to_i - updated_ends.push(index) if push - end - return {updated_ends, updated_status} - 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/helpers/companion_status.cr b/src/invidious/helpers/companion_status.cr new file mode 100644 index 00000000..2f92cac6 --- /dev/null +++ b/src/invidious/helpers/companion_status.cr @@ -0,0 +1,138 @@ +require "wait_group" + +class CompanionStatus + enum Status + # Color in the backend switcher: Red + Down = 0 + # Color in the backend switcher: Yellow + Blocked = 1 + # Color in the backend switcher: Green + Working = 2 + end + + struct CompanionHealthData + include JSON::Serializable + + property blocked : Bool = false + @[JSON::Field(key: "blockedCount")] + property blocked_count : Int64 = 0 + end + + class CompanionInfo + property companion : Config::CompanionConfig + property status : Status + property csp : String + + def initialize(companion) + @companion = companion + @status = Status::Down + @csp = "" + end + end + + class WorkingCompanions + property all : Array(Int32) + property community : Array(Int32) + + def initialize + @all = Array(Int32).new + @community = Array(Int32).new + end + end + + getter companions : Array(CompanionInfo) + getter working_companions : WorkingCompanions + # Reusable TLS Context for HTTP Client + # https://github.com/crystal-lang/crystal/issues/15419 + @tlscontext : OpenSSL::SSL::Context::Client + + def initialize + @companions = Array(CompanionInfo).new(CONFIG.invidious_companion.size) do |index| + CompanionInfo.new(CONFIG.invidious_companion[index]) + end + @working_companions = WorkingCompanions.new + @tlscontext = OpenSSL::SSL::Context::Client.new + end + + def check_companions + wg = WaitGroup.new(@companions.size) + + @companions.each_with_index do |companion, index| + c = companion.companion + spawn do + begin + self.healthcheck(c, index) + if @companions[index].status == Status::Working + LOGGER.trace("Companion checker: generating CSP for #{c.private_url}") + self.generate_csp( + [c.public_url, + c.i2p_public_url], index) + end + rescue + @companions[index].status == Status::Down + ensure + wg.done + end + end + end + + wg.wait + self.generate_working_companions + end + + private def generate_csp(companion_urls : Array(URI), index : Int32) + local_csp = "" + + companion_urls.each do |url| + host = url.host + next if !host.presence + scheme = url.scheme + port = url.port ? ":#{url.port}" : "" + + local_csp += "#{scheme}://#{host}#{port} " + end + + @companions[index].csp = local_csp + end + + private def generate_working_companions + # Aux variable to temporarily store the alive companions + # If we were to empty the `@working_companions`, some requests in the + # timespan of the `@info` iteration to find the working companions could be + # displayed as there was not working companions + local_working_companions = WorkingCompanions.new + + @companions.each_with_index do |companion, index| + if companion.status == Status::Working + local_working_companions.community << index + if !companion.companion.community + local_working_companions.all << index + end + end + end + + @working_companions = local_working_companions + end + + private def healthcheck(companion : Config::CompanionConfig, index : Int32) + client = HTTP::Client.new(companion.private_url, tls: @tlscontext) + client.connect_timeout = 10.seconds + + response = client.get(CONFIG.check_backends_path) + if response.status_code == 200 + if response.content_type == "application/json" + body = response.body + status_json = CompanionHealthData.from_json(body) + if status_json.blocked + @companions[index].status = Status::Blocked + else + @companions[index].status = Status::Working + end + else + @companions[index].status = Status::Working + end + else + @companions[index].status = Status::Down + end + end +end diff --git a/src/invidious/jobs/backend_checker.cr b/src/invidious/jobs/backend_checker.cr deleted file mode 100644 index fdfa0a45..00000000 --- a/src/invidious/jobs/backend_checker.cr +++ /dev/null @@ -1,14 +0,0 @@ -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/jobs/companion_checker.cr b/src/invidious/jobs/companion_checker.cr new file mode 100644 index 00000000..dc8e604a --- /dev/null +++ b/src/invidious/jobs/companion_checker.cr @@ -0,0 +1,17 @@ +class Invidious::Jobs::CompanionChecker < Invidious::Jobs::BaseJob + @companion_status : CompanionStatus + + def initialize(companion_status) + @companion_status = companion_status + end + + def begin + loop do + LOGGER.info("Companion checker: Starting") + @companion_status.check_companions + LOGGER.info("Companion 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/api/manifest.cr b/src/invidious/routes/api/manifest.cr index f06f5eea..7d8f40fe 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -9,7 +9,7 @@ module Invidious::Routes::API::Manifest region = env.params.query["region"]? if CONFIG.invidious_companion.present? - companion_public_url = env.get("companion_public_url").as(String) + companion_public_url = env.get("companion_companion_public_url").as(String) return env.redirect "#{companion_public_url}/api/manifest/dash/id/#{id}?#{env.params.query}" end diff --git a/src/invidious/routes/before_all.cr b/src/invidious/routes/before_all.cr index e5aa9482..2082eb72 100644 --- a/src/invidious/routes/before_all.cr +++ b/src/invidious/routes/before_all.cr @@ -1,12 +1,7 @@ module Invidious::Routes::BeforeAll - private COMPANION_PREFIXES = [] of String + extend self - CONFIG.invidious_companion.each_with_index do |_, i| - prefix = CONFIG.invidious_companion_prefix + "#{i + 1}" - COMPANION_PREFIXES << prefix - end - - def self.handle(env) + def handle(env) preferences = Preferences.from_json("{}") host = env.request.headers["Host"] @@ -28,75 +23,6 @@ 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 } - current_companion_d = host.split(":")[0].split(".")[0] - - if index = COMPANION_PREFIXES.index(current_companion_d) - env.set "using_domain", true - env.set "current_companion", index - env.set "companion_public_url", CONFIG.invidious_companion[index].public_url.to_s - else - 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 - if !working_ends.empty? - current_companion = working_ends.sample - else - current_companion = rand(CONFIG.invidious_companion.size) - end - end - - if current_companion < 0 - 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(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 - - if host.split(".").last == "i2p" - env.set "using_i2p", true - env.set "companion_public_url", CONFIG.invidious_companion[current_companion].i2p_public_url.to_s - else - env.set "using_i2p", false - env.set "companion_public_url", CONFIG.invidious_companion[current_companion].public_url.to_s - end - end - - extra_media_csp, extra_connect_csp = BackendInfo.get_csp(env.get("current_companion").as(Int32)) - end - end - # Only allow the pages at /embed/* to be embedded if env.request.resource.starts_with?("/embed") frame_ancestors = "'self' file: http: https:" @@ -107,22 +33,6 @@ module Invidious::Routes::BeforeAll scheme = env.request.headers["X-Forwarded-Proto"]? || ("https" if CONFIG.https_only) || "http" env.set "scheme", scheme - # TODO: Remove style-src's 'unsafe-inline', requires to remove all - # inline styles (, style=" [..] ") - env.response.headers["Content-Security-Policy"] = { - "default-src 'none'", - "script-src 'self'", - "style-src 'self' 'unsafe-inline'", - "img-src 'self' data: " + "#{scheme}://#{env.request.headers["Host"]?}", - "font-src 'self' data:", - "connect-src 'self'" + extra_connect_csp, - "manifest-src 'self'", - "media-src 'self' blob:" + extra_media_csp, - "child-src 'self' blob:", - "frame-src 'self'", - "frame-ancestors " + frame_ancestors, - }.join("; ") if CONFIG.csp - env.response.headers["Referrer-Policy"] = "same-origin" # Ask the chrom*-based browsers to disable FLoC @@ -183,6 +93,32 @@ module Invidious::Routes::BeforeAll preferences.locale = locale env.set "preferences", preferences + companion_csp = "" + if companion_status = COMPANION_STATUS + companion_csp = Invidious::Routes::BeforeAll::Companion.process_companion( + env, + host, + companion_status, + preferences + ) + end + + # TODO: Remove style-src's 'unsafe-inline', requires to remove all + # inline styles (, style=" [..] ") + env.response.headers["Content-Security-Policy"] = { + "default-src 'none'", + "script-src 'self'", + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data: " + "#{scheme}://#{env.request.headers["Host"]?}", + "font-src 'self' data:", + "connect-src 'self' " + companion_csp, + "manifest-src 'self'", + "media-src 'self' blob: " + companion_csp, + "child-src 'self' blob:", + "frame-src 'self'", + "frame-ancestors " + frame_ancestors, + }.join("; ") if CONFIG.csp + # Allow media resources to be loaded from google servers # TODO: check if *.youtube.com can be removed # @@ -212,3 +148,182 @@ module Invidious::Routes::BeforeAll env.set "current_page", URI.encode_www_form(current_page) end end + +# +# Invidious companion processing +# +module Invidious::Routes::BeforeAll::Companion + extend self + private COMPANION_PREFIXES = [] of String + + if c_prefix = CONFIG.invidious_companion_prefix + CONFIG.invidious_companion.each_with_index do |_, i| + prefix = c_prefix + "#{i + 1}" + COMPANION_PREFIXES << prefix + end + end + + def process_companion( + env : HTTP::Server::Context, + host : String, + companion_status : CompanionStatus, + preferences : Preferences, + ) + cookie_name = CONFIG.server_id_cookie_name + c_size = CONFIG.invidious_companion.size + current_companion = 0 + + # When accessing via domain we assume the user explicitely wants to access + # that domain. + if CONFIG.invidious_companion_prefix.presence && (index = self.using_invidious_domain?(host)) + env.set "companion_using_domain", true + env.set "companion_companion_public_url", CONFIG.invidious_companion[index].public_url.to_s + current_companion = index + else + # Set cookie if there is no cookie + if !env.request.cookies.has_key?(cookie_name) + current_companion = self.find_available_companion(env, host, nil, companion_status, preferences) + if current_companion + self.set_cookie(env, host, current_companion) + else + return "" + end + else + begin + current_companion = get_cookie(env) + current_companion = self.find_available_companion(env, host, current_companion, companion_status, preferences) + rescue + current_companion = rand(c_size) + self.set_cookie(env, host, current_companion) + end + end + + if current_companion.nil? + return "" + end + + # Set I2P public URL when it's being accessed via I2P. + # I2P is not like Tor, therefore I2P users can't connect to "clearnet" sites + # like it would work in Tor. + if host.split(".").last == "i2p" + env.set "companion_using_i2p", true + env.set "companion_companion_public_url", CONFIG.invidious_companion[current_companion].i2p_public_url.to_s + else + env.set "companion_using_i2p", false + env.set "companion_companion_public_url", CONFIG.invidious_companion[current_companion].public_url.to_s + end + end + + env.set "current_companion", current_companion + companion_csp = companion_status.companions[current_companion].csp + return companion_csp + end + + private def set_cookie( + env : HTTP::Server::Context, + host : String, + current_companion : Int32, + ) + cookie_name = CONFIG.server_id_cookie_name + env.response.cookies[cookie_name] = Invidious::User::Cookies.server_id(host, current_companion) + end + + private def get_cookie(env : HTTP::Server::Context) + cookie_name = CONFIG.server_id_cookie_name + return env.request.cookies[cookie_name].value.try &.to_i + end + + private def find_available_companion( + env : HTTP::Server::Context, + host : String, + current_companion : Int32?, + companion_status : CompanionStatus, + preferences : Preferences, + ) + companions = companion_status.companions + working_companions = companion_status.working_companions + c_size = companions.size + + if !preferences.show_community_backends + working_companions = working_companions.all + else + working_companions = working_companions.community + end + + if current_companion.nil? + available_companion = self.get_available_companion(c_size, working_companions) + if available_companion + current_companion = available_companion + return current_companion + else + return nil + end + end + + current_companion = self.wrap_current_companion(env, host, current_companion, c_size, working_companions) + if current_companion.nil? + return nil + end + + status = companions[current_companion].status + if status != CompanionStatus::Status::Working + alive_companion = self.get_available_companion(c_size, working_companions) + if alive_companion + current_companion = alive_companion + env.set "companion_switched", true + self.set_cookie(env, host, current_companion) + end + end + + return current_companion + end + + private def using_invidious_domain?(host : String) + current_companion_domain = host.split(":")[0].split(".")[0] + if index = COMPANION_PREFIXES.index(current_companion_domain) + return index + else + return nil + end + end + + # Checks if the current_companion does not match any companion + private def wrap_current_companion( + env : HTTP::Server::Context, + host : String, + current_companion : Int32, + invidious_companion_size : Int32, + working_companions : Array(Int32), + ) + if (current_companion < 0) || current_companion >= invidious_companion_size + current_companion = self.get_available_companion(invidious_companion_size, working_companions) + if current_companion + self.set_cookie(env, host, current_companion) + else + current_companion = rand(invidious_companion_size) + end + end + + return current_companion + end + + private def check_community(env, current_companion, companions) + companion = companions[current_companion].companion + + if companion.community + end + end + + private def get_available_companion( + invidious_companion_size : Int32, + working_companions : Array(Int32), + ) + if !working_companions.empty? + # Choose a random working companion + current_companion = working_companions.sample + return current_companion + else + return nil + end + end +end diff --git a/src/invidious/routes/video_playback.cr b/src/invidious/routes/video_playback.cr index 76d929a2..91013276 100644 --- a/src/invidious/routes/video_playback.cr +++ b/src/invidious/routes/video_playback.cr @@ -268,7 +268,7 @@ 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? - companion_public_url = env.get("companion_public_url").as(String) + companion_public_url = env.get("companion_companion_public_url").as(String) return env.redirect "#{companion_public_url}/latest_version?#{env.params.query}" end diff --git a/src/invidious/user/cookies.cr b/src/invidious/user/cookies.cr index eee92085..605ead3a 100644 --- a/src/invidious/user/cookies.cr +++ b/src/invidious/user/cookies.cr @@ -56,10 +56,7 @@ struct Invidious::User # 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 + def server_id(domain : String?, server_id : Int32) : HTTP::Cookie # 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. diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index 32258836..0378836f 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -25,8 +25,8 @@ audio_streams.each_with_index do |fmt, i| src_url = "/latest_version?id=#{video.id}&itag=#{fmt["itag"]}" src_url += "&local=true" if params.local - companion_public_url = env.get("companion_public_url").as(String) - src_url = companion_public_url + src_url + + companion_public_url = env.get("companion_companion_public_url").as(String) + src_url = companion_public_url + src_url + "&check=#{invidious_companion_check_id}" if (invidious_companion) bitrate = fmt["bitrate"] @@ -42,7 +42,7 @@ <% else %> <% if params.quality == "dash" src_url = "/api/manifest/dash/id/" + video.id + "?local=true&unique_res=1" - companion_public_url = env.get("companion_public_url").as(String) + companion_public_url = env.get("companion_companion_public_url").as(String) src_url = companion_public_url + src_url + "&check=#{invidious_companion_check_id}" if (invidious_companion) %> @@ -55,7 +55,7 @@ fmt_stream.each_with_index do |fmt, i| src_url = "/latest_version?id=#{video.id}&itag=#{fmt["itag"]}" src_url += "&local=true" if params.local - companion_public_url = env.get("companion_public_url").as(String) + companion_public_url = env.get("companion_companion_public_url").as(String) src_url = companion_public_url + src_url + "&check=#{invidious_companion_check_id}" if (invidious_companion) @@ -73,7 +73,8 @@ <% preferred_captions.each do |caption| api_captions_url = "/api/v1/captions/" - api_captions_url = invidious_companion.public_url.to_s + api_captions_url if (invidious_companion) + companion_public_url = env.get("companion_companion_public_url").as(String) + api_captions_url = companion_public_url + api_captions_url if (invidious_companion) api_captions_check_id = "&check=#{invidious_companion_check_id}" %> @@ -81,7 +82,8 @@ <% captions.each do |caption| api_captions_url = "/api/v1/captions/" - api_captions_url = invidious_companion.public_url.to_s + api_captions_url if (invidious_companion) + companion_public_url = env.get("companion_companion_public_url").as(String) + api_captions_url = companion_public_url + api_captions_url if (invidious_companion) api_captions_check_id = "&check=#{invidious_companion_check_id}" %> diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr index 1e17462a..2364d06e 100644 --- a/src/invidious/views/template.ecr +++ b/src/invidious/views/template.ecr @@ -108,28 +108,30 @@ <% - if CONFIG.invidious_companion.present? + if companion_status = COMPANION_STATUS current_backend = env.get?("current_companion").try &.as(Int32) - domain = env.get?("using_domain") + domain = env.get?("companion_using_domain") scheme = env.get("scheme") - status = BackendInfo.get_status companion_switched = env.get?("companion_switched") - using_i2p = env.get?("using_i2p") + using_i2p = env.get?("companion_using_i2p") + companions = companion_status.companions %>
<%= translate(locale, "companion_switch_backend") %> <% if domain %> - <% CONFIG.invidious_companion.each_with_index do | companion, index | %> - <% next if companion.community && !preferences.show_community_backends %> - <% next if companion.i2p_public_url.host.nil? && using_i2p %> - <% host_backend = env.request.headers["Host"].sub(/([^.]+)(\d+)/, "\\1#{index+1}") %> - <% is_current_backend_host = host_backend == env.request.headers["Host"] %> + <% CONFIG.invidious_companion.each_with_index do | companion, index | + next if companion.community && !preferences.show_community_backends + next if companion.i2p_public_url.host.nil? && using_i2p + host_backend = env.request.headers["Host"].sub(/([^.]+)(\d+)/, "\\1#{index+1}") + is_current_backend_host = host_backend == env.request.headers["Host"] + backend_name_prefix = CONFIG.backend_name_prefix + (index + 1).to_s + %> display: inline-block;"> - <%= HTML.escape(CONFIG.backend_name_prefix + (index + 1).to_s) %> <%= HTML.escape(companion.note) %> + <%= HTML.escape(backend_name_prefix) %> <%= HTML.escape(companion.note) %> <% if !(index == CONFIG.invidious_companion.size-1) %> @@ -137,16 +139,19 @@ <% end %> <% end %> <% else %> - <% current_page = env.get("current_page") %> - <% CONFIG.invidious_companion.each_with_index do | companion, index | %> - <% next if companion.community && !preferences.show_community_backends %> - <% next if companion.i2p_public_url.host.nil? && using_i2p %> + <% + current_page = env.get("current_page") + CONFIG.invidious_companion.each_with_index do | companion, index | + next if companion.community && !preferences.show_community_backends + next if companion.i2p_public_url.host.nil? && using_i2p + backend_name_prefix = CONFIG.backend_name_prefix + (index + 1).to_s + %> display: inline-block;"> - <%= HTML.escape(CONFIG.backend_name_prefix + (index + 1).to_s) %> <%= HTML.escape(companion.note) %> + <%= HTML.escape(backend_name_prefix) %> <%= HTML.escape(companion.note) %> <% if !(index == CONFIG.invidious_companion.size-1) %> @@ -342,7 +347,7 @@ <% if CONFIG.invidious_companion.present? - companion_public_url = env.get("companion_public_url").as(String) + companion_public_url = env.get("companion_companion_public_url").as(String) %>
You are currently using Backend: <%= current_backend ? companion_public_url : "Unable to get backend, this is bug, please report it!" %>
<% end %> diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index aab7a979..47d30961 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -652,19 +652,23 @@ module YoutubeAPI # Send the POST request begin - if env.nil? - working_ends = BackendInfo.get_working_ends - current_companion = working_ends.sample - else - current_companion = env.get("current_companion").as(Int32) + if companion_status = COMPANION_STATUS + if env.nil? + working_ends = companion_status.working_companions.community + current_companion = working_ends.sample + else + current_companion = env.get("current_companion").as(Int32) + end end response_body = Hash(String, JSON::Any).new - COMPANION_POOL[current_companion].client do |wrapper| - companion_base_url = wrapper.companion.private_url.path + if current_companion + COMPANION_POOL[current_companion].client do |wrapper| + companion_base_url = wrapper.companion.private_url.path - wrapper.client.post("#{companion_base_url}#{endpoint}", headers: headers, body: data.to_json) do |response| - response_body = JSON.parse(response.body_io).as_h + wrapper.client.post("#{companion_base_url}#{endpoint}", headers: headers, body: data.to_json) do |response| + response_body = JSON.parse(response.body_io).as_h + end end end