invidious/scripts/fetch-sabr-dependencies.cr

131 lines
4.1 KiB
Crystal

require "http"
require "yaml"
require "digest/sha1"
require "option_parser"
require "colorize"
# Script to fetch SABR (Server ABR) dependencies
# These are pre-built bundles for client-side SABR streaming support
SABR_DEPENDENCIES = {
"shaka-player" => {
"version" => "5.1.10",
"files" => [
{"url" => "https://cdn.jsdelivr.net/npm/shaka-player@5.1.10/dist/shaka-player.ui.js", "dest" => "shaka-player.ui.js"},
{"url" => "https://cdn.jsdelivr.net/npm/shaka-player@5.1.10/dist/controls.css", "dest" => "controls.css"},
]
},
# googlevideo + bgutils-js are bundled locally from npm via scripts/bundle-sabr-libs.js
# (no esm.sh fetch). youtubei.js is also bundled locally from npm, but we keep a
# pre-built jsDelivr copy as the esbuild entry for reproducibility.
"youtubei.js" => {
"version" => "17.0.1",
"files" => [
{"url" => "https://cdn.jsdelivr.net/npm/youtubei.js@17.0.1/bundle/browser.js", "dest" => "youtubei.bundle.min.js"},
]
},
}
def update_versions_yaml(dep_name : String, version : String)
File.open("assets/js/sabr/#{dep_name}/versions.yml", "w") do |io|
YAML.build(io) do |builder|
builder.mapping do
builder.scalar "version"
builder.scalar version
end
end
end
end
# Create the main sabr directory if it doesn't exist
sabr_dir = "assets/js/sabr"
Dir.mkdir_p(sabr_dir) unless Dir.exists?(sabr_dir)
dependencies_to_install = [] of String
SABR_DEPENDENCIES.each do |dep_name, dep_info|
path = "#{sabr_dir}/#{dep_name}"
version = dep_info["version"].as(String)
if !Dir.exists?(path)
Dir.mkdir_p(path)
dependencies_to_install << dep_name
else
if File.exists?("#{path}/versions.yml")
config = File.open("#{path}/versions.yml") do |file|
YAML.parse(file).as_h
end
if config["version"].as_s != version
# Clean old files
Dir.glob("#{path}/*.js").each { |f| File.delete(f) }
Dir.glob("#{path}/*.css").each { |f| File.delete(f) }
dependencies_to_install << dep_name
end
else
dependencies_to_install << dep_name
end
end
end
channel = Channel(String | Exception).new
dependencies_to_install.each do |dep_name|
spawn do
dep_info = SABR_DEPENDENCIES[dep_name]
version = dep_info["version"].as(String)
files = dep_info["files"].as(Array(Hash(String, String)))
dest_path = "#{sabr_dir}/#{dep_name}"
files.each do |file_info|
url = file_info["url"]
dest_file = file_info["dest"]
HTTP::Client.get(url) do |response|
if response.status_code == 200
File.write("#{dest_path}/#{dest_file}", response.body_io.gets_to_end)
else
raise Exception.new("Failed to fetch #{url}: HTTP #{response.status_code}")
end
end
end
update_versions_yaml(dep_name, version)
channel.send(dep_name)
rescue ex
channel.send(ex)
end
end
if dependencies_to_install.empty?
puts "#{"SABR".colorize(:blue)} #{"dependencies".colorize(:green)} are satisfied"
else
puts "#{"Resolving".colorize(:green)} #{"SABR".colorize(:blue)} dependencies"
dependencies_to_install.size.times do
result = channel.receive
if result.is_a? Exception
raise result
end
puts "#{"Fetched".colorize(:green)} #{result.colorize(:blue)}"
end
end
# Post-process: Remove Google Fonts from Shaka controls.css
shaka_css_path = "#{sabr_dir}/shaka-player/controls.css"
if File.exists?(shaka_css_path)
css_content = File.read(shaka_css_path)
# Remove @font-face rule that loads from fonts.gstatic.com
css_content = css_content.gsub(/@font-face\{[^}]*fonts\.gstatic\.com[^}]*\}/, "")
# Replace Roboto font-family with system fonts
css_content = css_content.gsub(/font-family:\s*Roboto[^;]*;/, "font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;")
File.write(shaka_css_path, css_content)
puts "#{"Patched".colorize(:green)} Shaka CSS to use system fonts"
end
# Note: googlevideo and bgutils-js are now bundled locally from npm via
# scripts/bundle-sabr-libs.js (esbuild, platform: 'browser', with
# process.env.NODE_ENV defined to 'production'). No esm.sh fetch or
# post-patching is required.