From 392e44796950aa7763c4880963a4bbd101cf517e Mon Sep 17 00:00:00 2001 From: syeopite Date: Sun, 18 Feb 2024 12:49:36 -0800 Subject: [PATCH 01/13] VideoJS dep manager: Add ability to skip checksum --- scripts/fetch-player-dependencies.cr | 15 +++++++++++++-- src/invidious.cr | 23 ++++++++++++++++++----- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index 813e4ce4e..a11b472e8 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -4,11 +4,22 @@ require "digest/sha1" require "option_parser" require "colorize" +# Hacky solution to get separated arguments when called from invidious.cr +if ARGV.size == 1 + parser_args = [] of String + ARGV[0].split(",") { |str| parser_args << str.strip } +else + parser_args = ARGV +end + # Taken from https://crystal-lang.org/api/1.1.1/OptionParser.html minified = false -OptionParser.parse do |parser| +skip_checksum = false + +OptionParser.parse(parser_args) do |parser| parser.banner = "Usage: Fetch VideoJS dependencies [arguments]" parser.on("-m", "--minified", "Use minified versions of VideoJS dependencies (performance and bandwidth benefit)") { minified = true } + parser.on("--skip-checksum", "Skips the checksum validation of downloaded files") { skip_checksum = true } parser.on("-h", "--help", "Show this help") do puts parser @@ -89,7 +100,7 @@ dependencies_to_install.each do |dep| File.write("#{download_path}/package.tgz", data) # https://github.com/iv-org/invidious/pull/2397#issuecomment-922375908 - if `sha1sum #{download_path}/package.tgz`.split(" ")[0] != required_dependencies[dep]["shasum"] + if !skip_checksum && `sha1sum #{download_path}/package.tgz`.split(" ")[0] != required_dependencies[dep]["shasum"] raise Exception.new("Checksum for '#{dep}' failed") end end diff --git a/src/invidious.cr b/src/invidious.cr index c8cac80ec..a8c2a85b0 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -144,12 +144,25 @@ Invidious::Database.check_integrity(CONFIG) # Running the script by itself would show some colorful feedback while this doesn't. # Perhaps we should just move the script to runtime in order to get that feedback? - {% puts "\nChecking player dependencies, this may take more than 20 minutes... If it is stuck, check your internet connection.\n" %} - {% if flag?(:minified_player_dependencies) %} - {% puts run("../scripts/fetch-player-dependencies.cr", "--minified").stringify %} - {% else %} - {% puts run("../scripts/fetch-player-dependencies.cr").stringify %} + {% fetch_script_arguments = [] of StringLiteral %} + + {% + potential_arguments = { + {:minified_player_dependencies, "--minified"}, + {:skip_player_dependencies_checksum, "--skip-checksum"}, + } + %} + + {% for potential_argument in potential_arguments %} + {% flag_, script_argument = potential_argument %} + + {% if flag?(flag_) %} + {% fetch_script_arguments << script_argument.id %} + {% end %} {% end %} + + {% puts "\nChecking player dependencies, this may take more than 20 minutes... If it is stuck, check your internet connection.\n" %} + {% puts run("../scripts/fetch-player-dependencies.cr", fetch_script_arguments.splat).stringify %} {% puts "\nDone checking player dependencies, now compiling Invidious...\n" %} {% end %} From 9c0cb2a513b96b80bf33a49c25c019e3a6247ebf Mon Sep 17 00:00:00 2001 From: syeopite Date: Sun, 18 Feb 2024 14:15:44 -0800 Subject: [PATCH 02/13] Add check for missing dependency files --- scripts/fetch-player-dependencies.cr | 40 ++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index a11b472e8..1c2c07947 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -58,26 +58,44 @@ end # different or the requested dependency just isn't present, then it needs to be # installed. -# Since we can't know when videojs-youtube-annotations is updated, we'll just always fetch -# a new copy each time. dependencies_to_install = [] of String required_dependencies.keys.each do |dep| - dep = dep.to_s + dep = dep.as_s path = "assets/videojs/#{dep}" + # Check for missing dependencies + # + # Does the directory exist? + # Does the Javascript file exist? + # Does the CSS file exist? + # + # videojs-contrib-quality-levels.js is the only dependency that does not come with a CSS file so + # we skip the check there if !Dir.exists?(path) Dir.mkdir(path) - dependencies_to_install << dep - else - config = File.open("#{path}/versions.yml") do |file| - YAML.parse(file).as_h + next dependencies_to_install << dep + elsif !(File.exists?("#{path}/#{dep}.js") || File.exists?("#{path}/versions.yml")) + next dependencies_to_install << dep + elsif dep != "videojs-contrib-quality-levels" && !File.exists?("#{path}/#{dep}.css") + next dependencies_to_install << dep + end + + # Check if we need to update the dependency + + config = File.open("#{path}/versions.yml") do |file| + YAML.parse(file).as_h + end + + if config["version"].as_s != required_dependencies[dep]["version"].as_s || config["minified"].as_bool != minified + # Clear directory + {"*.js", "*.css"}.each do |file_types| + Dir.glob("#{path}/#{file_types}").each do |file_path| + File.delete(file_path) + end end - if config["version"].as_s != required_dependencies[dep]["version"].as_s || config["minified"].as_bool != minified - `rm -rf #{path}/*.js #{path}/*.css` - dependencies_to_install << dep - end + dependencies_to_install << dep end end From 3195fd06f29012e196fbc89daadb2ab1925f8957 Mon Sep 17 00:00:00 2001 From: syeopite Date: Sun, 18 Feb 2024 16:49:21 -0800 Subject: [PATCH 03/13] Rewrite fetch logic for player dependencies - Refactor into dedicated class - Simplify workarounds for dependencies with differing structure --- scripts/fetch-player-dependencies.cr | 184 ++++++++++-------- .../views/components/player_sources.ecr | 6 +- videojs-dependencies.yml | 20 +- 3 files changed, 128 insertions(+), 82 deletions(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index 1c2c07947..4188ce56a 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -4,6 +4,110 @@ require "digest/sha1" require "option_parser" require "colorize" +class Dependency + @dependency_config : Hash(YAML::Any, YAML::Any) + + def initialize( + required_dependencies : Hash(YAML::Any, YAML::Any), + @dependency : String, + @tmp_dir_path : String, + @minified : Bool, + @skip_checksum : Bool + ) + @dependency_config = required_dependencies[@dependency].as_h + + @download_path = "#{@tmp_dir_path}/#{@dependency}" + @destination_path = "assets/videojs/#{@dependency}" + end + + private def request + HTTP::Client.get("https://registry.npmjs.org/#{@dependency}/-/#{@dependency}-#{@dependency_config["version"]}.tgz") do |response| + Dir.mkdir(@download_path) + data = response.body_io.gets_to_end + File.write("#{@download_path}/package.tgz", data) + + # https://github.com/iv-org/invidious/pull/2397#issuecomment-922375908 + if !@skip_checksum && `sha1sum #{@download_path}/package.tgz`.split(" ")[0] != @dependency_config["shasum"] + raise Exception.new("Checksum for '#{@dependency}' failed") + end + end + end + + private def move_file(full_target_path, extension) + minified_target_path = sprintf(full_target_path, {"file_extension": ".min.#{extension}"}) + + if @minified && File.exists?(minified_target_path) + target_path = minified_target_path + else + target_path = sprintf(full_target_path, {"file_extension": ".#{extension}"}) + end + + if download_as = @dependency_config.dig?(YAML::Any.new("install_instructions"), YAML::Any.new("download_as")) + destination_path = "#{@destination_path}/#{sprintf(download_as.as_s, {"file_extension": ".#{extension}"})}" + else + destination_path = @destination_path + end + + # https://github.com/crystal-lang/crystal/issues/7777 + `mv #{target_path} #{destination_path}` + end + + private def fetch_path(is_css) + if is_css + instruction_path = "css_path" + else + instruction_path = "js_path" + end + + # https://github.com/crystal-lang/crystal/issues/14305 + if raw_target_path = @dependency_config.dig?(YAML::Any.new("install_instructions"), YAML::Any.new(instruction_path)) + return "#{@download_path}/package/#{raw_target_path}" + else + return "#{@download_path}/package/dist/#{@dependency}%{file_extension}" + end + end + + private def move_js_file + return self.move_file(self.fetch_path(is_css: false), "js") + end + + private def move_css_file + path = self.fetch_path(is_css: true) + + if File.exists?(sprintf(path, {"file_extension": ".css"})) + return move_file(path, "css") + end + end + + private def update_versions_yaml + File.open("#{@destination_path}/versions.yml", "w") do |io| + YAML.build(io) do |builder| + builder.mapping do + # Versions + builder.scalar "version" + builder.scalar "#{@dependency_config["version"]}" + + builder.scalar "minified" + builder.scalar @minified + end + end + end + end + + def fetch + self.request + + # Crystal's stdlib provides no way of extracting a tarball + `tar -vzxf '#{@download_path}/package.tgz' -C '#{@download_path}'` + raise "Extraction for #{@dependency} failed" if !$?.success? + + self.move_js_file + self.move_css_file + + self.update_versions_yaml + end +end + # Hacky solution to get separated arguments when called from invidious.cr if ARGV.size == 1 parser_args = [] of String @@ -37,21 +141,6 @@ required_dependencies = File.open("videojs-dependencies.yml") do |file| YAML.parse(file).as_h end -def update_versions_yaml(required_dependencies, minified, dep_name) - File.open("assets/videojs/#{dep_name}/versions.yml", "w") do |io| - YAML.build(io) do |builder| - builder.mapping do - # Versions - builder.scalar "version" - builder.scalar "#{required_dependencies[dep_name]["version"]}" - - builder.scalar "minified" - builder.scalar minified - end - end - end -end - # The first step is to check which dependencies we'll need to install. # If the version we have requested in `videojs-dependencies.yml` is the # same as what we've installed, we shouldn't do anything. Likewise, if it's @@ -103,72 +192,13 @@ end # But first we'll setup a temp directory to store the plugins tmp_dir_path = "#{Dir.tempdir}/invidious-videojs-dep-install" Dir.mkdir(tmp_dir_path) if !Dir.exists? tmp_dir_path - channel = Channel(String | Exception).new dependencies_to_install.each do |dep| spawn do - dep_name = dep - download_path = "#{tmp_dir_path}/#{dep}" - dest_path = "assets/videojs/#{dep}" - - HTTP::Client.get("https://registry.npmjs.org/#{dep}/-/#{dep}-#{required_dependencies[dep]["version"]}.tgz") do |response| - Dir.mkdir(download_path) - data = response.body_io.gets_to_end - File.write("#{download_path}/package.tgz", data) - - # https://github.com/iv-org/invidious/pull/2397#issuecomment-922375908 - if !skip_checksum && `sha1sum #{download_path}/package.tgz`.split(" ")[0] != required_dependencies[dep]["shasum"] - raise Exception.new("Checksum for '#{dep}' failed") - end - end - - # Unless we install an external dependency, crystal provides no way of extracting a tarball. - # Thus we'll go ahead and call a system command. - `tar -vzxf '#{download_path}/package.tgz' -C '#{download_path}'` - raise "Extraction for #{dep} failed" if !$?.success? - - # Would use File.rename in the following steps but for some reason it just doesn't work here. - # Video.js itself is structured slightly differently - dep = "video" if dep == "video.js" - - # This dep nests everything under an additional JS or CSS folder - if dep == "silvermine-videojs-quality-selector" - js_path = "js/" - - # It also stores their quality selector as `quality-selector.css` - `mv #{download_path}/package/dist/css/quality-selector.css #{dest_path}/quality-selector.css` - else - js_path = "" - end - - # Would use File.rename but for some reason it just doesn't work here. - if minified && File.exists?("#{download_path}/package/dist/#{js_path}#{dep}.min.js") - `mv #{download_path}/package/dist/#{js_path}#{dep}.min.js #{dest_path}/#{dep}.js` - else - `mv #{download_path}/package/dist/#{js_path}#{dep}.js #{dest_path}/#{dep}.js` - end - - # Fetch CSS which isn't guaranteed to exist - # - # Also, video JS changes structure here once again... - dep = "video-js" if dep == "video" - - # VideoJS marker uses a dot on the CSS files. - dep = "videojs.markers" if dep == "videojs-markers" - - if File.exists?("#{download_path}/package/dist/#{dep}.css") - if minified && File.exists?("#{download_path}/package/dist/#{dep}.min.css") - `mv #{download_path}/package/dist/#{dep}.min.css #{dest_path}/#{dep}.css` - else - `mv #{download_path}/package/dist/#{dep}.css #{dest_path}/#{dep}.css` - end - end - - # Update/create versions file for the dependency - update_versions_yaml(required_dependencies, minified, dep_name) - - channel.send(dep_name) + dependency = Dependency.new(required_dependencies, dep, tmp_dir_path, minified, skip_checksum) + dependency.fetch + channel.send(dep) rescue ex channel.send(ex) end diff --git a/src/invidious/views/components/player_sources.ecr b/src/invidious/views/components/player_sources.ecr index 9af3899c4..4d4543f40 100644 --- a/src/invidious/views/components/player_sources.ecr +++ b/src/invidious/views/components/player_sources.ecr @@ -1,12 +1,12 @@ - + - + - + diff --git a/videojs-dependencies.yml b/videojs-dependencies.yml index e9ccc9dde..ec70cdbb2 100644 --- a/videojs-dependencies.yml +++ b/videojs-dependencies.yml @@ -1,7 +1,14 @@ -# Due to a 'video append of' error (see #3011), we're stuck on 7.12.1. +#Due to a 'video append of' error (see #3011), we're stuck on 7.12.1. video.js: version: 7.12.1 shasum: 1d12eeb1f52e3679e8e4c987d9b9eb37e2247fa2 + + install_instructions: + js_path: "dist/video%{file_extension}" + css_path: "dist/video-js%{file_extension}" + + # Normalize names to simplify File.exists? check + download_as: "video.js%{file_extension}" videojs-contrib-quality-levels: version: 2.1.0 @@ -15,6 +22,10 @@ videojs-markers: version: 1.0.1 shasum: d7f8d804253fd587813271f8db308a22b9f7df34 + install_instructions: + css_path: "dist/videojs.markers%{file_extension}" + download_as: "videojs-markers%{file_extension}" + videojs-mobile-ui: version: 0.6.1 shasum: 0e146c4c481cbee0729cb5e162e558b455562cd0 @@ -38,11 +49,16 @@ videojs-vtt-thumbnails: # We're using iv-org's fork of videojs-quality-selector, # which isn't published on NPM, and doesn't have any # easy way of fetching the compiled variant. -# + # silvermine-videojs-quality-selector: # version: 1.1.2 # shasum: 94033ff9ee52ba6da1263b97c9a74d5b3dfdf711 +# install_instructions: +# js_path: "dist/js/silvermine-videojs-quality-selector%{file_extension}" +# css_path: "dist/css/quality-selector%{file_extension}" +# download_as: silvermine-videojs-quality-selector%{file_extension} + # Ditto. Although this extension contains the complied variant in its git repo, # it lacks any sort of versioning. As such, the script will ignore it. From 37340f227ddbdb87343397a8840b28656093d47d Mon Sep 17 00:00:00 2001 From: syeopite Date: Sun, 18 Feb 2024 16:56:26 -0800 Subject: [PATCH 04/13] Use native crystal sha1 digest Crystal now supports OpenSSL 3 Reverts 4e629ca858ee21acebe458342311afab3ecd4155 --- scripts/fetch-player-dependencies.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index 4188ce56a..5acdb2933 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -27,7 +27,7 @@ class Dependency File.write("#{@download_path}/package.tgz", data) # https://github.com/iv-org/invidious/pull/2397#issuecomment-922375908 - if !@skip_checksum && `sha1sum #{@download_path}/package.tgz`.split(" ")[0] != @dependency_config["shasum"] + if !@skip_checksum && Digest::SHA1.hexdigest(data) != @dependency_config["shasum"] raise Exception.new("Checksum for '#{@dependency}' failed") end end From facf727e66c0bff926858af8dac7176353c7a639 Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 19 Feb 2024 11:44:22 -0800 Subject: [PATCH 05/13] Copy dependency file instead of moving them This allows for using the native crystal File.copy function and the caching of downloaded tarballs. --- scripts/fetch-player-dependencies.cr | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index 5acdb2933..173e9fbc7 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -42,14 +42,15 @@ class Dependency target_path = sprintf(full_target_path, {"file_extension": ".#{extension}"}) end + target_path = Path[target_path] + if download_as = @dependency_config.dig?(YAML::Any.new("install_instructions"), YAML::Any.new("download_as")) destination_path = "#{@destination_path}/#{sprintf(download_as.as_s, {"file_extension": ".#{extension}"})}" else - destination_path = @destination_path + destination_path = Path[@destination_path].join(target_path.basename) end - # https://github.com/crystal-lang/crystal/issues/7777 - `mv #{target_path} #{destination_path}` + File.copy(target_path, destination_path) end private def fetch_path(is_css) From 5d0055361f53812f9e5e9539c63af77a27460c40 Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 19 Feb 2024 12:15:46 -0800 Subject: [PATCH 06/13] Use cached tarball when available --- scripts/fetch-player-dependencies.cr | 49 ++++++++++++++++++++-------- src/invidious.cr | 1 + 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index 173e9fbc7..18e8cf809 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -1,5 +1,6 @@ require "http" require "yaml" +require "file_utils" require "digest/sha1" require "option_parser" require "colorize" @@ -20,16 +21,36 @@ class Dependency @destination_path = "assets/videojs/#{@dependency}" end - private def request - HTTP::Client.get("https://registry.npmjs.org/#{@dependency}/-/#{@dependency}-#{@dependency_config["version"]}.tgz") do |response| - Dir.mkdir(@download_path) - data = response.body_io.gets_to_end - File.write("#{@download_path}/package.tgz", data) + private def validate_checksum(io) + if !@skip_checksum && Digest::SHA1.hexdigest(io) != @dependency_config["shasum"] + raise IO::Error.new("Checksum for '#{@dependency}' failed") + end + end - # https://github.com/iv-org/invidious/pull/2397#issuecomment-922375908 - if !@skip_checksum && Digest::SHA1.hexdigest(data) != @dependency_config["shasum"] - raise Exception.new("Checksum for '#{@dependency}' failed") + # Requests and downloads a specific dependency from NPM + # + # Validates a cached tarball if it already exists. + private def request + downloaded_package_path = "#{@download_path}/package.tgz" + + # Create a download directory for the dependency if it does not already exist + if Dir.exists?(@download_path) + # Validate checksum of existing cached tarball + # Fetches a new one when the checksum fails. + if File.exists?(downloaded_package_path) + begin + return self.validate_checksum(File.open(downloaded_package_path)) + rescue IO::Error + end end + else + Dir.mkdir(@download_path) + end + + HTTP::Client.get("https://registry.npmjs.org/#{@dependency}/-/#{@dependency}-#{@dependency_config["version"]}.tgz") do |response| + data = response.body_io.gets_to_end + File.write(downloaded_package_path, data) + self.validate_checksum(data) end end @@ -42,15 +63,13 @@ class Dependency target_path = sprintf(full_target_path, {"file_extension": ".#{extension}"}) end - target_path = Path[target_path] - if download_as = @dependency_config.dig?(YAML::Any.new("install_instructions"), YAML::Any.new("download_as")) destination_path = "#{@destination_path}/#{sprintf(download_as.as_s, {"file_extension": ".#{extension}"})}" else - destination_path = Path[@destination_path].join(target_path.basename) + destination_path = @destination_path end - File.copy(target_path, destination_path) + FileUtils.cp(target_path, destination_path) end private def fetch_path(is_css) @@ -120,11 +139,13 @@ end # Taken from https://crystal-lang.org/api/1.1.1/OptionParser.html minified = false skip_checksum = false +clear_cache = false OptionParser.parse(parser_args) do |parser| parser.banner = "Usage: Fetch VideoJS dependencies [arguments]" parser.on("-m", "--minified", "Use minified versions of VideoJS dependencies (performance and bandwidth benefit)") { minified = true } parser.on("--skip-checksum", "Skips the checksum validation of downloaded files") { skip_checksum = true } + parser.on("--clear-cache", "Clears the cache and re-downloads all dependency files") { clear_cache = true } parser.on("-h", "--help", "Show this help") do puts parser @@ -221,4 +242,6 @@ else end # Cleanup -`rm -rf #{tmp_dir_path}` +if clear_cache + FileUtils.rm_r("#{tmp_dir_path}") +end diff --git a/src/invidious.cr b/src/invidious.cr index a8c2a85b0..3e02dde33 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -150,6 +150,7 @@ Invidious::Database.check_integrity(CONFIG) potential_arguments = { {:minified_player_dependencies, "--minified"}, {:skip_player_dependencies_checksum, "--skip-checksum"}, + {:clear_player_dependencies_cache, "--clear-cache"}, } %} From 71806ae18bad70956b90992b2a1345665b82b765 Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 19 Feb 2024 14:18:56 -0800 Subject: [PATCH 07/13] VideoJS Dependency Manager: Refactor configuration --- scripts/fetch-player-dependencies.cr | 198 +++++++++++++++------------ videojs-dependencies.yml | 106 +++++++------- 2 files changed, 168 insertions(+), 136 deletions(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index 18e8cf809..2385dc36f 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -5,24 +5,108 @@ require "digest/sha1" require "option_parser" require "colorize" +# Represents an "install_instruction" section specified per dependency in `videojs-dependencies.yml` +# +# This is used to modify the download logic for dependencies that are packaged differently. +struct InstallInstruction + include YAML::Serializable + + property js_path : String? = nil + property css_path : String? = nil + property download_as : String? = nil +end + +# Object representing a dependency specified within `videojs-dependencies.yml` +class ConfigDependency + include YAML::Serializable + + property version : String + property shasum : String + + property install_instructions : InstallInstruction? = nil + + # Checks if the current dependency needs to be installed/updated + def fetch?(name : String) + path = "assets/videojs/#{name}" + + # Check for missing dependency files + # + # Does the directory exist? + # Does the Javascript file exist? + # Does the CSS file exist? + # + # videojs-contrib-quality-levels.js is the only dependency that does not come with a CSS file so + # we skip the check there + if !Dir.exists?(path) + Dir.mkdir(path) + return true + elsif !(File.exists?("#{path}/#{name}.js") || File.exists?("#{path}/versions.yml")) + return true + elsif name != "videojs-contrib-quality-levels" && !File.exists?("#{path}/#{name}.css") + return true + end + + # Check if we need to update the dependency + + versions = File.open("#{path}/versions.yml") do |file| + YAML.parse(file).as_h + end + + if versions["version"].as_s != self.version || versions["minified"].as_bool != CONFIG.minified + # Clear directory + {"*.js", "*.css"}.each do |file_types| + Dir.glob("#{path}/#{file_types}").each do |file_path| + File.delete(file_path) + end + end + + return true + end + + return false + end +end + +# Object representing the `videojs-dependencies.yml` file +class PlayerDependenciesConfig + include YAML::Serializable + + property version : String + property dependencies : Hash(YAML::Any, ConfigDependency) + + def get_dependencies_to_fetch + return self.dependencies.select { |name, config| config.fetch?(name.as_s) } + end +end + +# Runtime Dependency config for easy access to all the variables +class Config + property minified : Bool + property skip_checksum : Bool + property clear_cache : Bool + + property dependency_config : PlayerDependenciesConfig + + def initialize(path : String) + @minified = false + @skip_checksum = false + @clear_cache = false + + @dependency_config = PlayerDependenciesConfig.from_yaml(File.read(path)) + end +end + +# Object representing a player dependency class Dependency - @dependency_config : Hash(YAML::Any, YAML::Any) - - def initialize( - required_dependencies : Hash(YAML::Any, YAML::Any), - @dependency : String, - @tmp_dir_path : String, - @minified : Bool, - @skip_checksum : Bool - ) - @dependency_config = required_dependencies[@dependency].as_h + @config : ConfigDependency + def initialize(@config : ConfigDependency, @dependency : String, @tmp_dir_path : String) @download_path = "#{@tmp_dir_path}/#{@dependency}" @destination_path = "assets/videojs/#{@dependency}" end private def validate_checksum(io) - if !@skip_checksum && Digest::SHA1.hexdigest(io) != @dependency_config["shasum"] + if !CONFIG.skip_checksum && Digest::SHA1.hexdigest(io) != @config.shasum raise IO::Error.new("Checksum for '#{@dependency}' failed") end end @@ -47,7 +131,7 @@ class Dependency Dir.mkdir(@download_path) end - HTTP::Client.get("https://registry.npmjs.org/#{@dependency}/-/#{@dependency}-#{@dependency_config["version"]}.tgz") do |response| + HTTP::Client.get("https://registry.npmjs.org/#{@dependency}/-/#{@dependency}-#{@config.version}.tgz") do |response| data = response.body_io.gets_to_end File.write(downloaded_package_path, data) self.validate_checksum(data) @@ -57,14 +141,14 @@ class Dependency private def move_file(full_target_path, extension) minified_target_path = sprintf(full_target_path, {"file_extension": ".min.#{extension}"}) - if @minified && File.exists?(minified_target_path) + if CONFIG.minified && File.exists?(minified_target_path) target_path = minified_target_path else target_path = sprintf(full_target_path, {"file_extension": ".#{extension}"}) end - if download_as = @dependency_config.dig?(YAML::Any.new("install_instructions"), YAML::Any.new("download_as")) - destination_path = "#{@destination_path}/#{sprintf(download_as.as_s, {"file_extension": ".#{extension}"})}" + if download_as = @config.install_instructions.try &.download_as + destination_path = "#{@destination_path}/#{sprintf(download_as, {"file_extension": ".#{extension}"})}" else destination_path = @destination_path end @@ -74,13 +158,12 @@ class Dependency private def fetch_path(is_css) if is_css - instruction_path = "css_path" + raw_target_path = @config.install_instructions.try &.css_path else - instruction_path = "js_path" + raw_target_path = @config.install_instructions.try &.js_path end - # https://github.com/crystal-lang/crystal/issues/14305 - if raw_target_path = @dependency_config.dig?(YAML::Any.new("install_instructions"), YAML::Any.new(instruction_path)) + if raw_target_path return "#{@download_path}/package/#{raw_target_path}" else return "#{@download_path}/package/dist/#{@dependency}%{file_extension}" @@ -105,10 +188,10 @@ class Dependency builder.mapping do # Versions builder.scalar "version" - builder.scalar "#{@dependency_config["version"]}" + builder.scalar "#{@config.version}" builder.scalar "minified" - builder.scalar @minified + builder.scalar CONFIG.minified end end end @@ -128,6 +211,8 @@ class Dependency end end +CONFIG = Config.new("videojs-dependencies.yml") + # Hacky solution to get separated arguments when called from invidious.cr if ARGV.size == 1 parser_args = [] of String @@ -137,15 +222,11 @@ else end # Taken from https://crystal-lang.org/api/1.1.1/OptionParser.html -minified = false -skip_checksum = false -clear_cache = false - OptionParser.parse(parser_args) do |parser| parser.banner = "Usage: Fetch VideoJS dependencies [arguments]" - parser.on("-m", "--minified", "Use minified versions of VideoJS dependencies (performance and bandwidth benefit)") { minified = true } - parser.on("--skip-checksum", "Skips the checksum validation of downloaded files") { skip_checksum = true } - parser.on("--clear-cache", "Clears the cache and re-downloads all dependency files") { clear_cache = true } + parser.on("-m", "--minified", "Use minified versions of VideoJS dependencies (performance and bandwidth benefit)") { CONFIG.minified = true } + parser.on("--skip-checksum", "Skips the checksum validation of downloaded files") { CONFIG.skip_checksum = true } + parser.on("--clear-cache", "Clears the cache and re-downloads all dependency files") { CONFIG.clear_cache = true } parser.on("-h", "--help", "Show this help") do puts parser @@ -159,68 +240,17 @@ OptionParser.parse(parser_args) do |parser| end end -required_dependencies = File.open("videojs-dependencies.yml") do |file| - YAML.parse(file).as_h -end +dependencies_to_install = CONFIG.dependency_config.get_dependencies_to_fetch -# The first step is to check which dependencies we'll need to install. -# If the version we have requested in `videojs-dependencies.yml` is the -# same as what we've installed, we shouldn't do anything. Likewise, if it's -# different or the requested dependency just isn't present, then it needs to be -# installed. - -dependencies_to_install = [] of String - -required_dependencies.keys.each do |dep| - dep = dep.as_s - path = "assets/videojs/#{dep}" - - # Check for missing dependencies - # - # Does the directory exist? - # Does the Javascript file exist? - # Does the CSS file exist? - # - # videojs-contrib-quality-levels.js is the only dependency that does not come with a CSS file so - # we skip the check there - if !Dir.exists?(path) - Dir.mkdir(path) - next dependencies_to_install << dep - elsif !(File.exists?("#{path}/#{dep}.js") || File.exists?("#{path}/versions.yml")) - next dependencies_to_install << dep - elsif dep != "videojs-contrib-quality-levels" && !File.exists?("#{path}/#{dep}.css") - next dependencies_to_install << dep - end - - # Check if we need to update the dependency - - config = File.open("#{path}/versions.yml") do |file| - YAML.parse(file).as_h - end - - if config["version"].as_s != required_dependencies[dep]["version"].as_s || config["minified"].as_bool != minified - # Clear directory - {"*.js", "*.css"}.each do |file_types| - Dir.glob("#{path}/#{file_types}").each do |file_path| - File.delete(file_path) - end - end - - dependencies_to_install << dep - end -end - -# Now we begin the fun part of installing the dependencies. -# But first we'll setup a temp directory to store the plugins tmp_dir_path = "#{Dir.tempdir}/invidious-videojs-dep-install" Dir.mkdir(tmp_dir_path) if !Dir.exists? tmp_dir_path channel = Channel(String | Exception).new -dependencies_to_install.each do |dep| +dependencies_to_install.each do |dep_name, dependency_config| spawn do - dependency = Dependency.new(required_dependencies, dep, tmp_dir_path, minified, skip_checksum) + dependency = Dependency.new(dependency_config, dep_name.as_s, tmp_dir_path) dependency.fetch - channel.send(dep) + channel.send(dep_name.as_s) rescue ex channel.send(ex) end @@ -242,6 +272,6 @@ else end # Cleanup -if clear_cache +if CONFIG.clear_cache FileUtils.rm_r("#{tmp_dir_path}") end diff --git a/videojs-dependencies.yml b/videojs-dependencies.yml index ec70cdbb2..6b29b51ad 100644 --- a/videojs-dependencies.yml +++ b/videojs-dependencies.yml @@ -1,70 +1,72 @@ -#Due to a 'video append of' error (see #3011), we're stuck on 7.12.1. -video.js: - version: 7.12.1 - shasum: 1d12eeb1f52e3679e8e4c987d9b9eb37e2247fa2 - - install_instructions: - js_path: "dist/video%{file_extension}" - css_path: "dist/video-js%{file_extension}" +version: 1 +dependencies: + #Due to a 'video append of' error (see #3011), we're stuck on 7.12.1. + video.js: + version: 7.12.1 + shasum: 1d12eeb1f52e3679e8e4c987d9b9eb37e2247fa2 - # Normalize names to simplify File.exists? check - download_as: "video.js%{file_extension}" + install_instructions: + js_path: "dist/video%{file_extension}" + css_path: "dist/video-js%{file_extension}" -videojs-contrib-quality-levels: - version: 2.1.0 - shasum: 046e9e21ed01043f512b83a1916001d552457083 + # Normalize names to simplify File.exists? check + download_as: "video.js%{file_extension}" -videojs-http-source-selector: - version: 1.1.6 - shasum: 073aadbea0106ba6c98d6b611094dbf8554ffa1f + videojs-contrib-quality-levels: + version: 2.1.0 + shasum: 046e9e21ed01043f512b83a1916001d552457083 -videojs-markers: - version: 1.0.1 - shasum: d7f8d804253fd587813271f8db308a22b9f7df34 + videojs-http-source-selector: + version: 1.1.6 + shasum: 073aadbea0106ba6c98d6b611094dbf8554ffa1f - install_instructions: - css_path: "dist/videojs.markers%{file_extension}" - download_as: "videojs-markers%{file_extension}" + videojs-markers: + version: 1.0.1 + shasum: d7f8d804253fd587813271f8db308a22b9f7df34 -videojs-mobile-ui: - version: 0.6.1 - shasum: 0e146c4c481cbee0729cb5e162e558b455562cd0 + install_instructions: + css_path: "dist/videojs.markers%{file_extension}" + download_as: "videojs-markers%{file_extension}" -videojs-overlay: - version: 2.1.4 - shasum: 5a103b25374dbb753eb87960d8360c2e8f39cc05 + videojs-mobile-ui: + version: 0.6.1 + shasum: 0e146c4c481cbee0729cb5e162e558b455562cd0 -videojs-share: - version: 3.2.1 - shasum: 0a3024b981387b9d21c058c829760a72c14b8ceb + videojs-overlay: + version: 2.1.4 + shasum: 5a103b25374dbb753eb87960d8360c2e8f39cc05 -videojs-vr: - version: 1.8.0 - shasum: 7f2f07f760d8a329c615acd316e49da6ee8edd34 + videojs-share: + version: 3.2.1 + shasum: 0a3024b981387b9d21c058c829760a72c14b8ceb -videojs-vtt-thumbnails: - version: 0.0.13 - shasum: d1e7d47f4ed80bb52f5fc4f4bad4bfc871f5970f + videojs-vr: + version: 1.8.0 + shasum: 7f2f07f760d8a329c615acd316e49da6ee8edd34 -# We're using iv-org's fork of videojs-quality-selector, -# which isn't published on NPM, and doesn't have any -# easy way of fetching the compiled variant. + videojs-vtt-thumbnails: + version: 0.0.13 + shasum: d1e7d47f4ed80bb52f5fc4f4bad4bfc871f5970f -# silvermine-videojs-quality-selector: -# version: 1.1.2 -# shasum: 94033ff9ee52ba6da1263b97c9a74d5b3dfdf711 + # We're using iv-org's fork of videojs-quality-selector, + # which isn't published on NPM, and doesn't have any + # easy way of fetching the compiled variant. -# install_instructions: -# js_path: "dist/js/silvermine-videojs-quality-selector%{file_extension}" -# css_path: "dist/css/quality-selector%{file_extension}" -# download_as: silvermine-videojs-quality-selector%{file_extension} + # silvermine-videojs-quality-selector: + # version: 1.1.2 + # shasum: 94033ff9ee52ba6da1263b97c9a74d5b3dfdf711 + + # install_instructions: + # js_path: "dist/js/silvermine-videojs-quality-selector%{file_extension}" + # css_path: "dist/css/quality-selector%{file_extension}" + # download_as: silvermine-videojs-quality-selector%{file_extension} -# Ditto. Although this extension contains the complied variant in its git repo, -# it lacks any sort of versioning. As such, the script will ignore it. -# -# videojs-youtube-annotations: -# github: https://github.com/afrmtbl/videojs-youtube-annotations + # Ditto. Although this extension contains the complied variant in its git repo, + # it lacks any sort of versioning. As such, the script will ignore it. + # + # videojs-youtube-annotations: + # github: https://github.com/afrmtbl/videojs-youtube-annotations From ee5a396a0a9f60e2c2d5deb79929e31f6be8bf8c Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 19 Feb 2024 14:24:39 -0800 Subject: [PATCH 08/13] VideoJS Dep. Manager: Allow custom registry url --- scripts/fetch-player-dependencies.cr | 3 ++- videojs-dependencies.yml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index 2385dc36f..f1004b341 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -72,6 +72,7 @@ class PlayerDependenciesConfig include YAML::Serializable property version : String + property registry_url : String property dependencies : Hash(YAML::Any, ConfigDependency) def get_dependencies_to_fetch @@ -131,7 +132,7 @@ class Dependency Dir.mkdir(@download_path) end - HTTP::Client.get("https://registry.npmjs.org/#{@dependency}/-/#{@dependency}-#{@config.version}.tgz") do |response| + HTTP::Client.get("#{CONFIG.dependency_config.registry_url}}/#{@dependency}/-/#{@dependency}-#{@config.version}.tgz") do |response| data = response.body_io.gets_to_end File.write(downloaded_package_path, data) self.validate_checksum(data) diff --git a/videojs-dependencies.yml b/videojs-dependencies.yml index 6b29b51ad..2d9b67d3f 100644 --- a/videojs-dependencies.yml +++ b/videojs-dependencies.yml @@ -1,4 +1,5 @@ version: 1 +registry_url: "https://registry.npmjs.org" dependencies: #Due to a 'video append of' error (see #3011), we're stuck on 7.12.1. video.js: From e33c407855e1b5710d666420aa40d52270ce05fc Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 19 Feb 2024 14:30:33 -0800 Subject: [PATCH 09/13] VideoJS Dep. Manager: allow custom cache directory --- scripts/fetch-player-dependencies.cr | 24 +++++++++++++++++------- videojs-dependencies.yml | 2 ++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index f1004b341..6642ef704 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -73,6 +73,7 @@ class PlayerDependenciesConfig property version : String property registry_url : String + property cache_directory : String property dependencies : Hash(YAML::Any, ConfigDependency) def get_dependencies_to_fetch @@ -95,14 +96,24 @@ class Config @dependency_config = PlayerDependenciesConfig.from_yaml(File.read(path)) end + + # Less verbose way to access @dependency_config.registry_url + def registry_url + return @dependency_config.registry_url + end + + # Less verbose way to access @dependency_config.cache_directory + def cache_directory + return @dependency_config.cache_directory + end end # Object representing a player dependency class Dependency @config : ConfigDependency - def initialize(@config : ConfigDependency, @dependency : String, @tmp_dir_path : String) - @download_path = "#{@tmp_dir_path}/#{@dependency}" + def initialize(@config : ConfigDependency, @dependency : String) + @download_path = "#{CONFIG.cache_directory}/#{@dependency}" @destination_path = "assets/videojs/#{@dependency}" end @@ -132,7 +143,7 @@ class Dependency Dir.mkdir(@download_path) end - HTTP::Client.get("#{CONFIG.dependency_config.registry_url}}/#{@dependency}/-/#{@dependency}-#{@config.version}.tgz") do |response| + HTTP::Client.get("#{CONFIG.registry_url}/#{@dependency}/-/#{@dependency}-#{@config.version}.tgz") do |response| data = response.body_io.gets_to_end File.write(downloaded_package_path, data) self.validate_checksum(data) @@ -243,13 +254,12 @@ end dependencies_to_install = CONFIG.dependency_config.get_dependencies_to_fetch -tmp_dir_path = "#{Dir.tempdir}/invidious-videojs-dep-install" -Dir.mkdir(tmp_dir_path) if !Dir.exists? tmp_dir_path +Dir.mkdir(CONFIG.cache_directory) if !Dir.exists? CONFIG.cache_directory channel = Channel(String | Exception).new dependencies_to_install.each do |dep_name, dependency_config| spawn do - dependency = Dependency.new(dependency_config, dep_name.as_s, tmp_dir_path) + dependency = Dependency.new(dependency_config, dep_name.as_s) dependency.fetch channel.send(dep_name.as_s) rescue ex @@ -274,5 +284,5 @@ end # Cleanup if CONFIG.clear_cache - FileUtils.rm_r("#{tmp_dir_path}") + FileUtils.rm_r("#{CONFIG.cache_directory}") end diff --git a/videojs-dependencies.yml b/videojs-dependencies.yml index 2d9b67d3f..0ee84a0d4 100644 --- a/videojs-dependencies.yml +++ b/videojs-dependencies.yml @@ -1,5 +1,7 @@ version: 1 registry_url: "https://registry.npmjs.org" +cache_directory: "/tmp/invidious-videojs-dep-install" + dependencies: #Due to a 'video append of' error (see #3011), we're stuck on 7.12.1. video.js: From 55b92112e33b6210d19d743607a2dd97f8f68996 Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 19 Feb 2024 14:58:20 -0800 Subject: [PATCH 10/13] Add additional documentation --- scripts/fetch-player-dependencies.cr | 21 +++++++++++++++------ videojs-dependencies.yml | 2 ++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index 6642ef704..5eb40c7b6 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -126,7 +126,7 @@ class Dependency # Requests and downloads a specific dependency from NPM # # Validates a cached tarball if it already exists. - private def request + private def request_dependency downloaded_package_path = "#{@download_path}/package.tgz" # Create a download directory for the dependency if it does not already exist @@ -150,6 +150,7 @@ class Dependency end end + # Moves a VideoJS dependency file of the given extension from extracted tarball to Invidious directory private def move_file(full_target_path, extension) minified_target_path = sprintf(full_target_path, {"file_extension": ".min.#{extension}"}) @@ -168,6 +169,7 @@ class Dependency FileUtils.cp(target_path, destination_path) end + # Fetch path of where a VideoJS dependency is located in the extracted tarball private def fetch_path(is_css) if is_css raw_target_path = @config.install_instructions.try &.css_path @@ -182,10 +184,14 @@ class Dependency end end + # Wrapper around `#move_file` to move the dependency's JS file private def move_js_file return self.move_file(self.fetch_path(is_css: false), "js") end + # Wrapper around `#move_file` to move the dependency's CSS file + # + # Does nothing with the CSS file does not exist. private def move_css_file path = self.fetch_path(is_css: true) @@ -194,6 +200,7 @@ class Dependency end end + # Updates the dependency's versions.yml with the current fetched version and its minified status private def update_versions_yaml File.open("#{@destination_path}/versions.yml", "w") do |io| YAML.build(io) do |builder| @@ -209,8 +216,9 @@ class Dependency end end - def fetch - self.request + # Installs a VideoJS dependency into Invidious + def install + self.request_dependency # Crystal's stdlib provides no way of extracting a tarball `tar -vzxf '#{@download_path}/package.tgz' -C '#{@download_path}'` @@ -252,15 +260,16 @@ OptionParser.parse(parser_args) do |parser| end end -dependencies_to_install = CONFIG.dependency_config.get_dependencies_to_fetch - +# Create cache directory Dir.mkdir(CONFIG.cache_directory) if !Dir.exists? CONFIG.cache_directory + +dependencies_to_install = CONFIG.dependency_config.get_dependencies_to_fetch channel = Channel(String | Exception).new dependencies_to_install.each do |dep_name, dependency_config| spawn do dependency = Dependency.new(dependency_config, dep_name.as_s) - dependency.fetch + dependency.install channel.send(dep_name.as_s) rescue ex channel.send(ex) diff --git a/videojs-dependencies.yml b/videojs-dependencies.yml index 0ee84a0d4..53c7b1791 100644 --- a/videojs-dependencies.yml +++ b/videojs-dependencies.yml @@ -1,5 +1,7 @@ version: 1 registry_url: "https://registry.npmjs.org" + +# Dependencies are stored as /package.tgz cache_directory: "/tmp/invidious-videojs-dep-install" dependencies: From 8f129b85ad09c647fceb1e50ab3033b45207c660 Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 19 Feb 2024 15:01:27 -0800 Subject: [PATCH 11/13] Use cache directory relative to Invidious repo https://github.com/iv-org/invidious/pull/2397#discussion_r753732453 --- .gitignore | 1 + videojs-dependencies.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7a26e1a6d..686589ae2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /invidious /sentry /config/config.yml +/invidious-videojs-dep-install \ No newline at end of file diff --git a/videojs-dependencies.yml b/videojs-dependencies.yml index 53c7b1791..274a960aa 100644 --- a/videojs-dependencies.yml +++ b/videojs-dependencies.yml @@ -2,7 +2,7 @@ version: 1 registry_url: "https://registry.npmjs.org" # Dependencies are stored as /package.tgz -cache_directory: "/tmp/invidious-videojs-dep-install" +cache_directory: "./invidious-videojs-dep-install" dependencies: #Due to a 'video append of' error (see #3011), we're stuck on 7.12.1. From 34ae6f4aa2c9b7a5f90ccd97bee2649ed9c114e4 Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 19 Feb 2024 15:05:03 -0800 Subject: [PATCH 12/13] Move hook for videojs-contrib-quality-levels to config file --- scripts/fetch-player-dependencies.cr | 6 ++---- videojs-dependencies.yml | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index 5eb40c7b6..b10122bff 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -14,6 +14,7 @@ struct InstallInstruction property js_path : String? = nil property css_path : String? = nil property download_as : String? = nil + property no_styling : Bool = false end # Object representing a dependency specified within `videojs-dependencies.yml` @@ -34,15 +35,12 @@ class ConfigDependency # Does the directory exist? # Does the Javascript file exist? # Does the CSS file exist? - # - # videojs-contrib-quality-levels.js is the only dependency that does not come with a CSS file so - # we skip the check there if !Dir.exists?(path) Dir.mkdir(path) return true elsif !(File.exists?("#{path}/#{name}.js") || File.exists?("#{path}/versions.yml")) return true - elsif name != "videojs-contrib-quality-levels" && !File.exists?("#{path}/#{name}.css") + elsif !(self.install_instructions.try &.no_styling) && !File.exists?("#{path}/#{name}.css") return true end diff --git a/videojs-dependencies.yml b/videojs-dependencies.yml index 274a960aa..38f4cff38 100644 --- a/videojs-dependencies.yml +++ b/videojs-dependencies.yml @@ -21,6 +21,9 @@ dependencies: version: 2.1.0 shasum: 046e9e21ed01043f512b83a1916001d552457083 + install_instructions: + no_styling: true + videojs-http-source-selector: version: 1.1.6 shasum: 073aadbea0106ba6c98d6b611094dbf8554ffa1f From dfd94f4f2eaedc054f5f528738b2e362496c46d9 Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 19 Feb 2024 15:16:33 -0800 Subject: [PATCH 13/13] Improve error message for checksum failure --- scripts/fetch-player-dependencies.cr | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr index b10122bff..097d61a7f 100755 --- a/scripts/fetch-player-dependencies.cr +++ b/scripts/fetch-player-dependencies.cr @@ -116,8 +116,11 @@ class Dependency end private def validate_checksum(io) - if !CONFIG.skip_checksum && Digest::SHA1.hexdigest(io) != @config.shasum - raise IO::Error.new("Checksum for '#{@dependency}' failed") + return if CONFIG.skip_checksum + + digest = Digest::SHA1.hexdigest(io) + if digest != @config.shasum + raise IO::Error.new("Checksum for '#{@dependency}' failed. \"#{digest}\" does not match configured \"#{@config.shasum}\"") end end