1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
require "http"
require "yaml"
require "digest/sha1"
require "option_parser"
require "colorize"
# Taken from https://crystal-lang.org/api/1.1.1/OptionParser.html
minified = false
OptionParser.parse 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("-h", "--help", "Show this help") do
puts parser
exit
end
parser.invalid_option do |flag|
STDERR.puts "ERROR: #{flag} is not a valid option."
STDERR.puts parser
exit(1)
end
end
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
# 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
path = "assets/videojs/#{dep}"
# Check for missing dependencies
if !Dir.exists?(path)
Dir.mkdir(path)
update_versions_yaml(required_dependencies, minified, dep)
dependencies_to_install << dep
else
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
`rm -rf #{path}/*`
dependencies_to_install << dep
update_versions_yaml(required_dependencies, minified, dep)
end
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).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)
if Digest::SHA1.hexdigest(data) != 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.
args = Process.parse_arguments("-zxvf '#{download_path}/package.tgz' -C '#{download_path}'")
process = Process.new("tar", args: args)
process.wait.success? # => true
# 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?("#{tmp_dir_path}/#{dep}/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
channel.send(dep_name)
end
end
if dependencies_to_install.empty?
puts "#{"Player".colorize(:blue)} #{"dependencies".colorize(:green)} are satisfied"
else
puts "#{"Resolving".colorize(:green)} #{"player".colorize(:blue)} dependencies"
dependencies_to_install.size.times do
result = channel.receive
puts "#{"Fetched".colorize(:green)} #{result.colorize(:blue)}"
end
end
# Cleanup
`rm -rf #{tmp_dir_path}`
|