SigHelper: Make signature server optional and configurable

This commit is contained in:
Samantaz Fox 2024-08-07 23:12:27 +02:00
parent ec1bb5db87
commit 7798faf234
No known key found for this signature in database
GPG key ID: F42821059186176E
6 changed files with 39 additions and 27 deletions

View file

@ -153,6 +153,15 @@ Invidious::Database.check_integrity(CONFIG)
{% puts "\nDone checking player dependencies, now compiling Invidious...\n" %} {% puts "\nDone checking player dependencies, now compiling Invidious...\n" %}
{% end %} {% end %}
# Misc
DECRYPT_FUNCTION =
if sig_helper_address = CONFIG.signature_server.presence
IV::DecryptFunction.new(sig_helper_address)
else
nil
end
# Start jobs # Start jobs
if CONFIG.channel_threads > 0 if CONFIG.channel_threads > 0

View file

@ -118,6 +118,10 @@ class Config
# Connect to YouTube over 'ipv6', 'ipv4'. Will sometimes resolve fix issues with rate-limiting (see https://github.com/ytdl-org/youtube-dl/issues/21729) # Connect to YouTube over 'ipv6', 'ipv4'. Will sometimes resolve fix issues with rate-limiting (see https://github.com/ytdl-org/youtube-dl/issues/21729)
@[YAML::Field(converter: Preferences::FamilyConverter)] @[YAML::Field(converter: Preferences::FamilyConverter)]
property force_resolve : Socket::Family = Socket::Family::UNSPEC property force_resolve : Socket::Family = Socket::Family::UNSPEC
# External signature solver server socket (either a path to a UNIX domain socket or "<IP>:<Port>")
property signature_server : String? = nil
# Port to listen for connections (overridden by command line argument) # Port to listen for connections (overridden by command line argument)
property port : Int32 = 3000 property port : Int32 = 3000
# Host to bind (overridden by command line argument) # Host to bind (overridden by command line argument)

View file

@ -72,8 +72,12 @@ module Invidious::SigHelper
# High-level functions # High-level functions
# ---------------------- # ----------------------
module Client class Client
extend self @mux : Multiplexor
def initialize(uri_or_path)
@mux = Multiplexor.new(uri_or_path)
end
# Forces the server to re-fetch the YouTube player, and extract the necessary # Forces the server to re-fetch the YouTube player, and extract the necessary
# components from it (nsig function code, sig function code, signature timestamp). # components from it (nsig function code, sig function code, signature timestamp).
@ -148,7 +152,7 @@ module Invidious::SigHelper
end end
private def send_request(request : Request, &) private def send_request(request : Request, &)
channel = Multiplexor::INSTANCE.send(request) channel = @mux.send(request)
slice = channel.receive slice = channel.receive
return yield slice return yield slice
rescue ex rescue ex
@ -172,10 +176,8 @@ module Invidious::SigHelper
@conn : Connection @conn : Connection
INSTANCE = new("") def initialize(uri_or_path)
@conn = Connection.new(uri_or_path)
def initialize(url : String)
@conn = Connection.new(url)
listen listen
end end
@ -275,13 +277,14 @@ module Invidious::SigHelper
{% end %} {% end %}
def initialize(host_or_path : String) def initialize(host_or_path : String)
if host_or_path.empty?
host_or_path = "/tmp/inv_sig_helper.sock"
end
case host_or_path case host_or_path
when .starts_with?('/') when .starts_with?('/')
# Make sure that the file exists
if File.exists?(host_or_path)
@socket = UNIXSocket.new(host_or_path) @socket = UNIXSocket.new(host_or_path)
else
raise Exception.new("SigHelper: '#{host_or_path}' no such file")
end
when .starts_with?("tcp://") when .starts_with?("tcp://")
uri = URI.parse(host_or_path) uri = URI.parse(host_or_path)
@socket = TCPSocket.new(uri.host.not_nil!, uri.port.not_nil!) @socket = TCPSocket.new(uri.host.not_nil!, uri.port.not_nil!)

View file

@ -1,10 +1,11 @@
require "http/params" require "http/params"
require "./sig_helper" require "./sig_helper"
struct Invidious::DecryptFunction class Invidious::DecryptFunction
@last_update : Time = Time.utc - 42.days @last_update : Time = Time.utc - 42.days
def initialize def initialize(uri_or_path)
@client = SigHelper::Client.new(uri_or_path)
self.check_update self.check_update
end end
@ -16,19 +17,18 @@ struct Invidious::DecryptFunction
# Get the time when the player was updated, in the event where # Get the time when the player was updated, in the event where
# multiple invidious processes are run in parallel. # multiple invidious processes are run in parallel.
player_ts = Invidious::SigHelper::Client.get_player_timestamp player_time = Time.unix(@client.get_player_timestamp || 0)
player_time = Time.unix(player_ts || 0)
if (now - player_time) > 5.minutes if (now - player_time) > 5.minutes
LOGGER.debug("Signature: Player might be outdated, updating") LOGGER.debug("Signature: Player might be outdated, updating")
Invidious::SigHelper::Client.force_update @client.force_update
@last_update = Time.utc @last_update = Time.utc
end end
end end
def decrypt_nsig(n : String) : String? def decrypt_nsig(n : String) : String?
self.check_update self.check_update
return SigHelper::Client.decrypt_n_param(n) return @client.decrypt_n_param(n)
rescue ex rescue ex
LOGGER.debug(ex.message || "Signature: Unknown error") LOGGER.debug(ex.message || "Signature: Unknown error")
LOGGER.trace(ex.inspect_with_backtrace) LOGGER.trace(ex.inspect_with_backtrace)
@ -37,7 +37,7 @@ struct Invidious::DecryptFunction
def decrypt_signature(str : String) : String? def decrypt_signature(str : String) : String?
self.check_update self.check_update
return SigHelper::Client.decrypt_sig(str) return @client.decrypt_sig(str)
rescue ex rescue ex
LOGGER.debug(ex.message || "Signature: Unknown error") LOGGER.debug(ex.message || "Signature: Unknown error")
LOGGER.trace(ex.inspect_with_backtrace) LOGGER.trace(ex.inspect_with_backtrace)
@ -46,7 +46,7 @@ struct Invidious::DecryptFunction
def get_sts : UInt64? def get_sts : UInt64?
self.check_update self.check_update
return SigHelper::Client.get_signature_timestamp return @client.get_signature_timestamp
rescue ex rescue ex
LOGGER.debug(ex.message || "Signature: Unknown error") LOGGER.debug(ex.message || "Signature: Unknown error")
LOGGER.trace(ex.inspect_with_backtrace) LOGGER.trace(ex.inspect_with_backtrace)

View file

@ -1,5 +1,3 @@
private DECRYPT_FUNCTION = IV::DecryptFunction.new
enum VideoType enum VideoType
Video Video
Livestream Livestream
@ -108,14 +106,14 @@ struct Video
LOGGER.debug("Videos: Decoding '#{cfr}'") LOGGER.debug("Videos: Decoding '#{cfr}'")
unsig = DECRYPT_FUNCTION.decrypt_signature(cfr["s"]) unsig = DECRYPT_FUNCTION.try &.decrypt_signature(cfr["s"])
params[sp] = unsig if unsig params[sp] = unsig if unsig
else else
url = URI.parse(fmt["url"].as_s) url = URI.parse(fmt["url"].as_s)
params = url.query_params params = url.query_params
end end
n = DECRYPT_FUNCTION.decrypt_nsig(params["n"]) n = DECRYPT_FUNCTION.try &.decrypt_nsig(params["n"])
params["n"] = n if n params["n"] = n if n
params["host"] = url.host.not_nil! params["host"] = url.host.not_nil!

View file

@ -2,8 +2,6 @@
# This file contains youtube API wrappers # This file contains youtube API wrappers
# #
private STS_FETCHER = IV::DecryptFunction.new
module YoutubeAPI module YoutubeAPI
extend self extend self
@ -462,7 +460,7 @@ module YoutubeAPI
} of String => String | Int64 } of String => String | Int64
if {"WEB", "TVHTML5"}.any? { |s| client_config.name.starts_with? s } if {"WEB", "TVHTML5"}.any? { |s| client_config.name.starts_with? s }
if sts = STS_FETCHER.get_sts if sts = DECRYPT_FUNCTION.try &.get_sts
playback_ctx["signatureTimestamp"] = sts.to_i64 playback_ctx["signatureTimestamp"] = sts.to_i64
end end
end end