pinokiod 7.2.1 → 7.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/kernel/bin/ffmpeg.js +693 -224
- package/package.json +1 -1
package/kernel/bin/ffmpeg.js
CHANGED
|
@@ -2,235 +2,718 @@ const crypto = require("crypto")
|
|
|
2
2
|
const fs = require("fs")
|
|
3
3
|
const path = require("path")
|
|
4
4
|
const { execFile } = require("child_process")
|
|
5
|
+
const ParcelWatcher = require("@parcel/watcher")
|
|
5
6
|
const semver = require("semver")
|
|
6
7
|
const { rimraf } = require("rimraf")
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const RELEASE_VERSION = "8.1"
|
|
10
|
-
const RELEASE_RANGE = ">=8.1
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
url: "https://www.gyan.dev/ffmpeg/builds/packages/ffmpeg-8.1-essentials_build.zip",
|
|
19
|
-
sha256: "8748283d821613d930b0e7be685aaa9df4ca6f0ad4d0c42fd02622b3623463c6",
|
|
20
|
-
binaries: {
|
|
21
|
-
ffmpeg: "ffmpeg.exe",
|
|
22
|
-
ffprobe: "ffprobe.exe"
|
|
23
|
-
}
|
|
24
|
-
}]
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
darwin: {
|
|
28
|
-
x64: {
|
|
29
|
-
version: RELEASE_VERSION,
|
|
30
|
-
source: "Martin Riedl macOS amd64 release",
|
|
31
|
-
archives: [{
|
|
32
|
-
url: "https://ffmpeg.martin-riedl.de/download/macos/amd64/1774556648_8.1/ffmpeg.zip",
|
|
33
|
-
sha256: "eaa8aa619f8eccc7f548a730097f5d299cbf2d418888421c137557344d821130",
|
|
34
|
-
binaries: {
|
|
35
|
-
ffmpeg: "ffmpeg"
|
|
36
|
-
}
|
|
37
|
-
}, {
|
|
38
|
-
url: "https://ffmpeg.martin-riedl.de/download/macos/amd64/1774556648_8.1/ffprobe.zip",
|
|
39
|
-
sha256: "221bd0716dc15daf5745c5503773e5c23264c10c5ea956aa17ef492bbc0b0ac7",
|
|
40
|
-
binaries: {
|
|
41
|
-
ffprobe: "ffprobe"
|
|
42
|
-
}
|
|
43
|
-
}]
|
|
44
|
-
},
|
|
45
|
-
arm64: {
|
|
46
|
-
version: RELEASE_VERSION,
|
|
47
|
-
source: "Martin Riedl macOS arm64 release",
|
|
48
|
-
archives: [{
|
|
49
|
-
url: "https://ffmpeg.martin-riedl.de/download/macos/arm64/1774549676_8.1/ffmpeg.zip",
|
|
50
|
-
sha256: "cc3a7e0cce36c5eca6c17eeb93830984c657637a8e710dc98f19c8051201fa3a",
|
|
51
|
-
binaries: {
|
|
52
|
-
ffmpeg: "ffmpeg"
|
|
53
|
-
}
|
|
54
|
-
}, {
|
|
55
|
-
url: "https://ffmpeg.martin-riedl.de/download/macos/arm64/1774549676_8.1/ffprobe.zip",
|
|
56
|
-
sha256: "fd2e6b7fad9c9aa2bec17c0d7211b5afcc00b4b5c9b63c120985e80c3c198af6",
|
|
57
|
-
binaries: {
|
|
58
|
-
ffprobe: "ffprobe"
|
|
59
|
-
}
|
|
60
|
-
}]
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
linux: {
|
|
64
|
-
x64: {
|
|
65
|
-
version: RELEASE_VERSION,
|
|
66
|
-
source: "Martin Riedl Linux amd64 release",
|
|
67
|
-
archives: [{
|
|
68
|
-
url: "https://ffmpeg.martin-riedl.de/download/linux/amd64/1774550169_8.1/ffmpeg.zip",
|
|
69
|
-
sha256: "49f9a3642387626f82fd70dd6a268807efe23e0560d6934a6531d6e3e668f18f",
|
|
70
|
-
binaries: {
|
|
71
|
-
ffmpeg: "ffmpeg"
|
|
72
|
-
}
|
|
73
|
-
}, {
|
|
74
|
-
url: "https://ffmpeg.martin-riedl.de/download/linux/amd64/1774550169_8.1/ffprobe.zip",
|
|
75
|
-
sha256: "422082501af33fabb3946d101d098e5105f44492e5a16357c3fac79421544b0e",
|
|
76
|
-
binaries: {
|
|
77
|
-
ffprobe: "ffprobe"
|
|
78
|
-
}
|
|
79
|
-
}]
|
|
80
|
-
},
|
|
81
|
-
arm64: {
|
|
82
|
-
version: RELEASE_VERSION,
|
|
83
|
-
source: "Martin Riedl Linux arm64 release",
|
|
84
|
-
archives: [{
|
|
85
|
-
url: "https://ffmpeg.martin-riedl.de/download/linux/arm64/1774548896_8.1/ffmpeg.zip",
|
|
86
|
-
sha256: "87000dd625a4f409a5baf71ac177c22210db04ea144e01241713ab196ed39689",
|
|
87
|
-
binaries: {
|
|
88
|
-
ffmpeg: "ffmpeg"
|
|
89
|
-
}
|
|
90
|
-
}, {
|
|
91
|
-
url: "https://ffmpeg.martin-riedl.de/download/linux/arm64/1774548896_8.1/ffprobe.zip",
|
|
92
|
-
sha256: "eb56a190dea6bdd08da2c1e63d7c7523817384eff4dff227f4b088e56205414b",
|
|
93
|
-
binaries: {
|
|
94
|
-
ffprobe: "ffprobe"
|
|
95
|
-
}
|
|
96
|
-
}]
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
8
|
+
const Util = require("../util")
|
|
9
|
+
|
|
10
|
+
const RELEASE_VERSION = "8.0.1"
|
|
11
|
+
const RELEASE_RANGE = ">=8.0.1 <8.1.0"
|
|
12
|
+
const CONDA_SPEC = `ffmpeg=${RELEASE_VERSION}`
|
|
13
|
+
const CONDA_CHANNEL_FLAGS = "--override-channels -c conda-forge"
|
|
14
|
+
|
|
15
|
+
const WINDOWS_GDK_PIXBUF_POST_LINK_NOOP = `@echo off
|
|
16
|
+
rem Pinokio intentionally skips gdk-pixbuf loader cache generation for FFmpeg installs.
|
|
17
|
+
exit /b 0
|
|
18
|
+
`
|
|
100
19
|
|
|
101
20
|
class Ffmpeg {
|
|
102
|
-
description = "Installs
|
|
21
|
+
description = "Installs FFmpeg for audio and video processing."
|
|
103
22
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
23
|
+
cmd() {
|
|
24
|
+
return CONDA_SPEC
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
env(kernel) {
|
|
28
|
+
const activeKernel = kernel || this.kernel
|
|
29
|
+
const env = {
|
|
30
|
+
FFMPEG_PATH: this.binaryPath("ffmpeg", activeKernel),
|
|
31
|
+
FFPROBE_PATH: this.binaryPath("ffprobe", activeKernel)
|
|
32
|
+
}
|
|
33
|
+
if (activeKernel.platform === "win32") {
|
|
34
|
+
env.PATH = [this.libraryDir(activeKernel)]
|
|
35
|
+
}
|
|
36
|
+
if (activeKernel.platform === "linux") {
|
|
37
|
+
env.LD_LIBRARY_PATH = [this.libraryDir(activeKernel)]
|
|
110
38
|
}
|
|
111
|
-
return
|
|
39
|
+
return env
|
|
112
40
|
}
|
|
113
41
|
|
|
114
|
-
|
|
115
|
-
return
|
|
42
|
+
ffmpegPrefix(kernel = this.kernel) {
|
|
43
|
+
return kernel.bin.path("ffmpeg-env")
|
|
116
44
|
}
|
|
117
45
|
|
|
118
|
-
|
|
119
|
-
return
|
|
46
|
+
ffmpegPkgsDir(kernel = this.kernel) {
|
|
47
|
+
return kernel.bin.path("ffmpeg-pkgs")
|
|
120
48
|
}
|
|
121
49
|
|
|
122
|
-
|
|
123
|
-
|
|
50
|
+
binaryPath(tool, kernel = this.kernel) {
|
|
51
|
+
const filename = kernel.platform === "win32" ? `${tool}.exe` : tool
|
|
52
|
+
if (kernel.platform === "win32") {
|
|
53
|
+
return path.resolve(this.ffmpegPrefix(kernel), "Library", "bin", filename)
|
|
54
|
+
}
|
|
55
|
+
return path.resolve(this.ffmpegPrefix(kernel), "bin", filename)
|
|
124
56
|
}
|
|
125
57
|
|
|
126
|
-
|
|
127
|
-
|
|
58
|
+
libraryDir(kernel = this.kernel) {
|
|
59
|
+
if (kernel.platform === "win32") {
|
|
60
|
+
return path.resolve(this.ffmpegPrefix(kernel), "Library", "bin")
|
|
61
|
+
}
|
|
62
|
+
return path.resolve(this.ffmpegPrefix(kernel), "lib")
|
|
128
63
|
}
|
|
129
64
|
|
|
130
|
-
|
|
131
|
-
return
|
|
65
|
+
legacyStandalonePaths() {
|
|
66
|
+
return [
|
|
67
|
+
this.kernel.bin.path("ffmpeg"),
|
|
68
|
+
this.kernel.bin.path("ffmpeg-tmp")
|
|
69
|
+
]
|
|
132
70
|
}
|
|
133
71
|
|
|
134
|
-
|
|
135
|
-
|
|
72
|
+
async start() {
|
|
73
|
+
if (this.kernel.platform !== "darwin") {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
if (!(await this.hasInstalledBinaryPaths())) {
|
|
77
|
+
await this.removeBaseActivationHooks()
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
await this.ensureBaseActivationHooks()
|
|
81
|
+
await this.syncMacUvLibraryShims()
|
|
82
|
+
await this.startMacUvLibraryWatcher()
|
|
136
83
|
}
|
|
137
84
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
85
|
+
async install(req, ondata) {
|
|
86
|
+
await this.cleanupLegacyStandalone(ondata)
|
|
87
|
+
if (this.kernel.platform === "win32") {
|
|
88
|
+
await this.installWindows(ondata)
|
|
89
|
+
} else {
|
|
90
|
+
await this.installStandard(ondata)
|
|
143
91
|
}
|
|
92
|
+
await this.selfTest(ondata)
|
|
93
|
+
await this.ensureBaseActivationHooks()
|
|
94
|
+
await this.syncMacUvLibraryShims(ondata)
|
|
144
95
|
}
|
|
145
96
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
97
|
+
async installStandard(ondata) {
|
|
98
|
+
await this.resetInstallPrefix()
|
|
99
|
+
await this.kernel.bin.exec({
|
|
100
|
+
env: {
|
|
101
|
+
CONDA_PKGS_DIRS: this.ffmpegPkgsDir()
|
|
102
|
+
},
|
|
103
|
+
message: [
|
|
104
|
+
"conda clean -y --all",
|
|
105
|
+
`conda create -y -p "${this.ffmpegPrefix()}" ${CONDA_CHANNEL_FLAGS} ${this.cmd()}`
|
|
106
|
+
]
|
|
107
|
+
}, ondata)
|
|
149
108
|
}
|
|
150
109
|
|
|
151
|
-
async
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const downloads = []
|
|
110
|
+
async installWindows(ondata) {
|
|
111
|
+
await this.resetInstallPrefix()
|
|
112
|
+
const env = {
|
|
113
|
+
CONDA_PKGS_DIRS: this.ffmpegPkgsDir()
|
|
114
|
+
}
|
|
157
115
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
116
|
+
await this.kernel.bin.exec({
|
|
117
|
+
env,
|
|
118
|
+
message: [
|
|
119
|
+
"conda clean -y --all",
|
|
120
|
+
`conda create -y --download-only -p "${this.ffmpegPrefix()}" ${CONDA_CHANNEL_FLAGS} ${this.cmd()}`
|
|
121
|
+
]
|
|
122
|
+
}, ondata)
|
|
161
123
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
124
|
+
await this.patchWindowsGdkPixbufPostLink(this.ffmpegPkgsDir(), ondata)
|
|
125
|
+
|
|
126
|
+
await this.kernel.bin.exec({
|
|
127
|
+
env,
|
|
128
|
+
message: `conda create -y --offline -p "${this.ffmpegPrefix()}" ${CONDA_CHANNEL_FLAGS} ${this.cmd()}`
|
|
129
|
+
}, ondata)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async resetInstallPrefix() {
|
|
133
|
+
await rimraf(this.ffmpegPrefix())
|
|
134
|
+
await rimraf(this.ffmpegPkgsDir())
|
|
135
|
+
await fs.promises.mkdir(this.ffmpegPkgsDir(), { recursive: true })
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async patchWindowsGdkPixbufPostLink(pkgsDir, ondata) {
|
|
139
|
+
const entries = await fs.promises.readdir(pkgsDir, { withFileTypes: true })
|
|
140
|
+
const packageDirs = entries
|
|
141
|
+
.filter((entry) => entry.isDirectory() && /^gdk-pixbuf-/.test(entry.name))
|
|
142
|
+
.map((entry) => path.resolve(pkgsDir, entry.name))
|
|
143
|
+
|
|
144
|
+
if (packageDirs.length === 0) {
|
|
145
|
+
throw new Error("Could not find downloaded gdk-pixbuf package in the Conda cache after --download-only")
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let patchedCount = 0
|
|
149
|
+
let metadataCount = 0
|
|
150
|
+
for (const packageDir of packageDirs) {
|
|
151
|
+
const scripts = [
|
|
152
|
+
{
|
|
153
|
+
relativePath: "Scripts/.gdk-pixbuf-post-link.bat",
|
|
154
|
+
metadataRequired: true
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
relativePath: "info/recipe/post-link.bat",
|
|
158
|
+
metadataRequired: false
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
for (const { relativePath, metadataRequired } of scripts) {
|
|
163
|
+
const script = path.resolve(packageDir, ...relativePath.split("/"))
|
|
164
|
+
try {
|
|
165
|
+
await fs.promises.access(script)
|
|
166
|
+
await fs.promises.writeFile(script, WINDOWS_GDK_PIXBUF_POST_LINK_NOOP)
|
|
167
|
+
patchedCount += 1
|
|
168
|
+
const updatedMetadata = await this.updateCondaPathsJson(packageDir, relativePath, WINDOWS_GDK_PIXBUF_POST_LINK_NOOP)
|
|
169
|
+
if (updatedMetadata) {
|
|
170
|
+
metadataCount += 1
|
|
171
|
+
} else if (metadataRequired) {
|
|
172
|
+
throw new Error(`Patched ${relativePath} in ${packageDir}, but did not find a matching info/paths.json entry`)
|
|
184
173
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
await fs.promises.chmod(destination, 0o755)
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (error && error.code !== "ENOENT") {
|
|
176
|
+
throw error
|
|
189
177
|
}
|
|
190
178
|
}
|
|
191
179
|
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (patchedCount === 0) {
|
|
183
|
+
throw new Error("Found gdk-pixbuf in the Conda cache, but did not find any post-link scripts to patch")
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (ondata) {
|
|
187
|
+
ondata({
|
|
188
|
+
raw: `patched ${patchedCount} gdk-pixbuf post-link script(s) in the Conda cache and refreshed ${metadataCount} paths.json entr${metadataCount === 1 ? "y" : "ies"}...\r\n`
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async updateCondaPathsJson(packageDir, relativePath, contents) {
|
|
194
|
+
const pathsJsonPath = path.resolve(packageDir, "info", "paths.json")
|
|
195
|
+
let pathsJson
|
|
192
196
|
|
|
193
|
-
|
|
194
|
-
await
|
|
195
|
-
await this.ensureLegacyCondaFfmpegRemoved(ondata)
|
|
196
|
-
ondata({ raw: `ffmpeg ${spec.version} installed from ${spec.source}\r\n` })
|
|
197
|
+
try {
|
|
198
|
+
pathsJson = JSON.parse(await fs.promises.readFile(pathsJsonPath, "utf8"))
|
|
197
199
|
} catch (error) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
} finally {
|
|
201
|
-
for (const download of downloads) {
|
|
202
|
-
await fs.promises.rm(download, { force: true }).catch(() => {})
|
|
200
|
+
if (error && error.code === "ENOENT") {
|
|
201
|
+
return false
|
|
203
202
|
}
|
|
204
|
-
|
|
203
|
+
throw error
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!pathsJson || !Array.isArray(pathsJson.paths)) {
|
|
207
|
+
return false
|
|
205
208
|
}
|
|
209
|
+
|
|
210
|
+
const normalizedPath = relativePath.replace(/\\/g, "/")
|
|
211
|
+
const entry = pathsJson.paths.find((item) => item && item._path === normalizedPath)
|
|
212
|
+
if (!entry) {
|
|
213
|
+
return false
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const buffer = Buffer.isBuffer(contents) ? contents : Buffer.from(String(contents), "utf8")
|
|
217
|
+
entry.sha256 = crypto.createHash("sha256").update(buffer).digest("hex")
|
|
218
|
+
entry.size_in_bytes = buffer.length
|
|
219
|
+
|
|
220
|
+
await fs.promises.writeFile(pathsJsonPath, `${JSON.stringify(pathsJson, null, 2)}\n`)
|
|
221
|
+
return true
|
|
206
222
|
}
|
|
207
223
|
|
|
208
|
-
async
|
|
224
|
+
async hasInstalledBinaryPaths() {
|
|
209
225
|
try {
|
|
210
226
|
await fs.promises.access(this.binaryPath("ffmpeg"))
|
|
211
227
|
await fs.promises.access(this.binaryPath("ffprobe"))
|
|
212
|
-
|
|
213
|
-
|
|
228
|
+
return true
|
|
229
|
+
} catch (error) {
|
|
230
|
+
return false
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async installed() {
|
|
235
|
+
try {
|
|
236
|
+
if (!(await this.hasInstalledBinaryPaths())) {
|
|
237
|
+
await this.removeBaseActivationHooks()
|
|
214
238
|
return false
|
|
215
239
|
}
|
|
240
|
+
|
|
241
|
+
await this.ensureBaseActivationHooks()
|
|
242
|
+
await this.syncMacUvLibraryShims()
|
|
243
|
+
await this.startMacUvLibraryWatcher()
|
|
216
244
|
await this.selfTest()
|
|
217
245
|
return true
|
|
218
246
|
} catch (error) {
|
|
219
|
-
console.log("
|
|
247
|
+
console.log("conda ffmpeg installed check failed", error && error.message ? error.message : error)
|
|
220
248
|
return false
|
|
221
249
|
}
|
|
222
250
|
}
|
|
223
251
|
|
|
224
252
|
async uninstall(req, ondata) {
|
|
225
|
-
|
|
226
|
-
await
|
|
227
|
-
await
|
|
228
|
-
|
|
253
|
+
await this.stopMacUvLibraryWatcher()
|
|
254
|
+
await this.removeMacUvLibraryShims(ondata)
|
|
255
|
+
await this.removeBaseActivationHooks()
|
|
256
|
+
const prefix = this.ffmpegPrefix()
|
|
257
|
+
const exists = await fs.promises.access(prefix).then(() => true).catch(() => false)
|
|
258
|
+
if (exists) {
|
|
259
|
+
try {
|
|
260
|
+
await this.kernel.bin.exec({
|
|
261
|
+
env: {
|
|
262
|
+
CONDA_PKGS_DIRS: this.ffmpegPkgsDir()
|
|
263
|
+
},
|
|
264
|
+
message: `conda remove -y -p "${prefix}" --all`
|
|
265
|
+
}, ondata)
|
|
266
|
+
} catch (error) {
|
|
267
|
+
await rimraf(prefix)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
await rimraf(prefix)
|
|
271
|
+
await rimraf(this.ffmpegPkgsDir())
|
|
272
|
+
await this.cleanupLegacyStandalone(ondata)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
activationDirs() {
|
|
276
|
+
return {
|
|
277
|
+
activate: this.kernel.bin.path("miniconda", "etc", "conda", "activate.d"),
|
|
278
|
+
deactivate: this.kernel.bin.path("miniconda", "etc", "conda", "deactivate.d")
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
activationHookFiles() {
|
|
283
|
+
const { activate, deactivate } = this.activationDirs()
|
|
284
|
+
const files = [
|
|
285
|
+
{
|
|
286
|
+
path: path.resolve(activate, "zz_pinokio_ffmpeg.sh"),
|
|
287
|
+
content: this.posixActivateSh(this.kernel.platform === "win32")
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
path: path.resolve(deactivate, "zz_pinokio_ffmpeg.sh"),
|
|
291
|
+
content: this.posixDeactivateSh(this.kernel.platform === "win32")
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
|
|
295
|
+
if (this.kernel.platform !== "win32") {
|
|
296
|
+
return files
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return files.concat([
|
|
300
|
+
{
|
|
301
|
+
path: path.resolve(activate, "zz_pinokio_ffmpeg.bat"),
|
|
302
|
+
content: this.windowsActivateBat()
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
path: path.resolve(deactivate, "zz_pinokio_ffmpeg.bat"),
|
|
306
|
+
content: this.windowsDeactivateBat()
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
path: path.resolve(activate, "zz_pinokio_ffmpeg.ps1"),
|
|
310
|
+
content: this.windowsActivatePs1()
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
path: path.resolve(deactivate, "zz_pinokio_ffmpeg.ps1"),
|
|
314
|
+
content: this.windowsDeactivatePs1()
|
|
315
|
+
}
|
|
316
|
+
])
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async ensureBaseActivationHooks() {
|
|
320
|
+
const dirs = this.activationDirs()
|
|
321
|
+
await fs.promises.mkdir(dirs.activate, { recursive: true }).catch(() => {})
|
|
322
|
+
await fs.promises.mkdir(dirs.deactivate, { recursive: true }).catch(() => {})
|
|
323
|
+
for (const file of this.activationHookFiles()) {
|
|
324
|
+
await fs.promises.writeFile(file.path, file.content)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async removeBaseActivationHooks() {
|
|
329
|
+
for (const file of this.activationHookFiles()) {
|
|
330
|
+
await fs.promises.rm(file.path, { force: true }).catch(() => {})
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
windowsActivateBat() {
|
|
335
|
+
const prefix = this.ffmpegPrefix()
|
|
336
|
+
const runtime = this.libraryDir()
|
|
337
|
+
return `@echo off
|
|
338
|
+
set "PINOKIO_FFMPEG_PREFIX=${prefix}"
|
|
339
|
+
set "PINOKIO_FFMPEG_RUNTIME=${runtime}"
|
|
340
|
+
set "FFMPEG_PATH=%PINOKIO_FFMPEG_RUNTIME%\\ffmpeg.exe"
|
|
341
|
+
set "FFPROBE_PATH=%PINOKIO_FFMPEG_RUNTIME%\\ffprobe.exe"
|
|
342
|
+
call :pinokio_ffmpeg_remove_from_path "%PINOKIO_FFMPEG_RUNTIME%"
|
|
343
|
+
set "PATH=%PINOKIO_FFMPEG_RUNTIME%;%PATH%"
|
|
344
|
+
goto :eof
|
|
345
|
+
|
|
346
|
+
:pinokio_ffmpeg_remove_from_path
|
|
347
|
+
setlocal EnableDelayedExpansion
|
|
348
|
+
set "_pinokio_target=%~1"
|
|
349
|
+
set "_pinokio_path=;%PATH%;"
|
|
350
|
+
set "_pinokio_path=!_pinokio_path:;%_pinokio_target%;=;!"
|
|
351
|
+
set "_pinokio_path=!_pinokio_path:;%_pinokio_target%\\;=;!"
|
|
352
|
+
if "!_pinokio_path:~0,1!"==";" set "_pinokio_path=!_pinokio_path:~1!"
|
|
353
|
+
if "!_pinokio_path:~-1!"==";" set "_pinokio_path=!_pinokio_path:~0,-1!"
|
|
354
|
+
endlocal & set "PATH=%_pinokio_path%"
|
|
355
|
+
exit /b 0
|
|
356
|
+
`
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
windowsDeactivateBat() {
|
|
360
|
+
return `@echo off
|
|
361
|
+
if defined PINOKIO_FFMPEG_RUNTIME call :pinokio_ffmpeg_remove_from_path "%PINOKIO_FFMPEG_RUNTIME%"
|
|
362
|
+
set "FFMPEG_PATH="
|
|
363
|
+
set "FFPROBE_PATH="
|
|
364
|
+
set "PINOKIO_FFMPEG_PREFIX="
|
|
365
|
+
set "PINOKIO_FFMPEG_RUNTIME="
|
|
366
|
+
goto :eof
|
|
367
|
+
|
|
368
|
+
:pinokio_ffmpeg_remove_from_path
|
|
369
|
+
setlocal EnableDelayedExpansion
|
|
370
|
+
set "_pinokio_target=%~1"
|
|
371
|
+
set "_pinokio_path=;%PATH%;"
|
|
372
|
+
set "_pinokio_path=!_pinokio_path:;%_pinokio_target%;=;!"
|
|
373
|
+
set "_pinokio_path=!_pinokio_path:;%_pinokio_target%\\;=;!"
|
|
374
|
+
if "!_pinokio_path:~0,1!"==";" set "_pinokio_path=!_pinokio_path:~1!"
|
|
375
|
+
if "!_pinokio_path:~-1!"==";" set "_pinokio_path=!_pinokio_path:~0,-1!"
|
|
376
|
+
endlocal & set "PATH=%_pinokio_path%"
|
|
377
|
+
exit /b 0
|
|
378
|
+
`
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
windowsActivatePs1() {
|
|
382
|
+
const prefix = this.ffmpegPrefix().replace(/\\/g, "\\\\")
|
|
383
|
+
const runtime = this.libraryDir().replace(/\\/g, "\\\\")
|
|
384
|
+
return `$Env:PINOKIO_FFMPEG_PREFIX = "${prefix}"
|
|
385
|
+
$Env:PINOKIO_FFMPEG_RUNTIME = "${runtime}"
|
|
386
|
+
$Env:FFMPEG_PATH = Join-Path $Env:PINOKIO_FFMPEG_RUNTIME "ffmpeg.exe"
|
|
387
|
+
$Env:FFPROBE_PATH = Join-Path $Env:PINOKIO_FFMPEG_RUNTIME "ffprobe.exe"
|
|
388
|
+
$pinokioParts = @()
|
|
389
|
+
if ($Env:Path) {
|
|
390
|
+
$pinokioParts = @($Env:Path -split ';' | Where-Object { $_ -and $_ -ne $Env:PINOKIO_FFMPEG_RUNTIME })
|
|
391
|
+
}
|
|
392
|
+
$Env:Path = (@($Env:PINOKIO_FFMPEG_RUNTIME) + $pinokioParts) -join ';'
|
|
393
|
+
`
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
windowsDeactivatePs1() {
|
|
397
|
+
return `if ($Env:PINOKIO_FFMPEG_RUNTIME) {
|
|
398
|
+
$pinokioParts = @()
|
|
399
|
+
if ($Env:Path) {
|
|
400
|
+
$pinokioParts = @($Env:Path -split ';' | Where-Object { $_ -and $_ -ne $Env:PINOKIO_FFMPEG_RUNTIME })
|
|
401
|
+
}
|
|
402
|
+
$Env:Path = $pinokioParts -join ';'
|
|
403
|
+
}
|
|
404
|
+
Remove-Item -Path Env:\\FFMPEG_PATH -ErrorAction SilentlyContinue
|
|
405
|
+
Remove-Item -Path Env:\\FFPROBE_PATH -ErrorAction SilentlyContinue
|
|
406
|
+
Remove-Item -Path Env:\\PINOKIO_FFMPEG_PREFIX -ErrorAction SilentlyContinue
|
|
407
|
+
Remove-Item -Path Env:\\PINOKIO_FFMPEG_RUNTIME -ErrorAction SilentlyContinue
|
|
408
|
+
`
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
posixActivateSh(forceWindowsPaths = false) {
|
|
412
|
+
const prefix = forceWindowsPaths ? Util.p2u(this.ffmpegPrefix()) : this.ffmpegPrefix()
|
|
413
|
+
const binDir = forceWindowsPaths ? Util.p2u(path.resolve(this.ffmpegPrefix(), "Library", "bin")) : path.resolve(this.ffmpegPrefix(), "bin")
|
|
414
|
+
const libDir = forceWindowsPaths ? "" : this.libraryDir()
|
|
415
|
+
return `pinokio_ffmpeg_prepend_path() {
|
|
416
|
+
local target="$1"
|
|
417
|
+
local current="\${2-}"
|
|
418
|
+
local result=""
|
|
419
|
+
local part
|
|
420
|
+
local old_ifs="$IFS"
|
|
421
|
+
IFS=':'
|
|
422
|
+
for part in $current; do
|
|
423
|
+
[ -n "$part" ] || continue
|
|
424
|
+
[ "$part" = "$target" ] && continue
|
|
425
|
+
if [ -n "$result" ]; then
|
|
426
|
+
result="$result:$part"
|
|
427
|
+
else
|
|
428
|
+
result="$part"
|
|
429
|
+
fi
|
|
430
|
+
done
|
|
431
|
+
IFS="$old_ifs"
|
|
432
|
+
if [ -n "$result" ]; then
|
|
433
|
+
printf '%s:%s' "$target" "$result"
|
|
434
|
+
else
|
|
435
|
+
printf '%s' "$target"
|
|
436
|
+
fi
|
|
437
|
+
}
|
|
438
|
+
pinokio_ffmpeg_remove_path() {
|
|
439
|
+
local target="$1"
|
|
440
|
+
local current="\${2-}"
|
|
441
|
+
local result=""
|
|
442
|
+
local part
|
|
443
|
+
local old_ifs="$IFS"
|
|
444
|
+
IFS=':'
|
|
445
|
+
for part in $current; do
|
|
446
|
+
[ -n "$part" ] || continue
|
|
447
|
+
[ "$part" = "$target" ] && continue
|
|
448
|
+
if [ -n "$result" ]; then
|
|
449
|
+
result="$result:$part"
|
|
450
|
+
else
|
|
451
|
+
result="$part"
|
|
452
|
+
fi
|
|
453
|
+
done
|
|
454
|
+
IFS="$old_ifs"
|
|
455
|
+
printf '%s' "$result"
|
|
456
|
+
}
|
|
457
|
+
export PINOKIO_FFMPEG_PREFIX="${prefix}"
|
|
458
|
+
export PINOKIO_FFMPEG_BIN="${binDir}"
|
|
459
|
+
export FFMPEG_PATH="$PINOKIO_FFMPEG_BIN/${forceWindowsPaths ? "ffmpeg.exe" : "ffmpeg"}"
|
|
460
|
+
export FFPROBE_PATH="$PINOKIO_FFMPEG_BIN/${forceWindowsPaths ? "ffprobe.exe" : "ffprobe"}"
|
|
461
|
+
export PATH="$(pinokio_ffmpeg_prepend_path "$PINOKIO_FFMPEG_BIN" "$PATH")"
|
|
462
|
+
${forceWindowsPaths ? "" : `if [ "$(uname -s)" = "Linux" ]; then
|
|
463
|
+
export LD_LIBRARY_PATH="$(pinokio_ffmpeg_prepend_path "${libDir}" "\${LD_LIBRARY_PATH-}")"
|
|
464
|
+
fi
|
|
465
|
+
`}
|
|
466
|
+
unset -f pinokio_ffmpeg_prepend_path
|
|
467
|
+
unset -f pinokio_ffmpeg_remove_path
|
|
468
|
+
`
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
posixDeactivateSh(forceWindowsPaths = false) {
|
|
472
|
+
return `pinokio_ffmpeg_remove_path() {
|
|
473
|
+
local target="$1"
|
|
474
|
+
local current="\${2-}"
|
|
475
|
+
local result=""
|
|
476
|
+
local part
|
|
477
|
+
local old_ifs="$IFS"
|
|
478
|
+
IFS=':'
|
|
479
|
+
for part in $current; do
|
|
480
|
+
[ -n "$part" ] || continue
|
|
481
|
+
[ "$part" = "$target" ] && continue
|
|
482
|
+
if [ -n "$result" ]; then
|
|
483
|
+
result="$result:$part"
|
|
484
|
+
else
|
|
485
|
+
result="$part"
|
|
486
|
+
fi
|
|
487
|
+
done
|
|
488
|
+
IFS="$old_ifs"
|
|
489
|
+
printf '%s' "$result"
|
|
490
|
+
}
|
|
491
|
+
if [ -n "\${PINOKIO_FFMPEG_BIN-}" ]; then
|
|
492
|
+
export PATH="$(pinokio_ffmpeg_remove_path "$PINOKIO_FFMPEG_BIN" "$PATH")"
|
|
493
|
+
fi
|
|
494
|
+
${forceWindowsPaths ? "" : `if [ "$(uname -s)" = "Linux" ] && [ -n "\${PINOKIO_FFMPEG_PREFIX-}" ]; then
|
|
495
|
+
export LD_LIBRARY_PATH="$(pinokio_ffmpeg_remove_path "${this.libraryDir()}" "\${LD_LIBRARY_PATH-}")"
|
|
496
|
+
fi
|
|
497
|
+
`}
|
|
498
|
+
unset FFMPEG_PATH
|
|
499
|
+
unset FFPROBE_PATH
|
|
500
|
+
unset PINOKIO_FFMPEG_PREFIX
|
|
501
|
+
unset PINOKIO_FFMPEG_BIN
|
|
502
|
+
unset -f pinokio_ffmpeg_remove_path
|
|
503
|
+
`
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
async cleanupLegacyStandalone(ondata) {
|
|
507
|
+
for (const target of this.legacyStandalonePaths()) {
|
|
508
|
+
const exists = await fs.promises.access(target).then(() => true).catch(() => false)
|
|
509
|
+
if (exists) {
|
|
510
|
+
if (ondata) {
|
|
511
|
+
ondata({ raw: `removing legacy standalone ffmpeg files from ${target}...\r\n` })
|
|
512
|
+
}
|
|
513
|
+
await rimraf(target)
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
uvPythonRoot(kernel = this.kernel) {
|
|
519
|
+
return kernel.path("cache", "XDG_DATA_HOME", "uv", "python")
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
async startMacUvLibraryWatcher() {
|
|
523
|
+
if (this.kernel.platform !== "darwin" || this.macUvLibraryWatcher) {
|
|
524
|
+
return
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const root = this.uvPythonRoot()
|
|
528
|
+
await fs.promises.mkdir(root, { recursive: true })
|
|
529
|
+
this.macUvLibraryWatcher = await ParcelWatcher.subscribe(root, (error, events) => {
|
|
530
|
+
if (error) {
|
|
531
|
+
console.warn("ffmpeg uv library watcher error", error && error.message ? error.message : error)
|
|
532
|
+
return
|
|
533
|
+
}
|
|
534
|
+
if (!events || events.length === 0) {
|
|
535
|
+
return
|
|
536
|
+
}
|
|
537
|
+
this.scheduleMacUvLibraryShimSync()
|
|
538
|
+
})
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
async stopMacUvLibraryWatcher() {
|
|
542
|
+
if (this.macUvLibraryShimSyncTimer) {
|
|
543
|
+
clearTimeout(this.macUvLibraryShimSyncTimer)
|
|
544
|
+
this.macUvLibraryShimSyncTimer = null
|
|
545
|
+
}
|
|
546
|
+
if (this.macUvLibraryWatcher) {
|
|
547
|
+
await this.macUvLibraryWatcher.unsubscribe()
|
|
548
|
+
this.macUvLibraryWatcher = null
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
scheduleMacUvLibraryShimSync() {
|
|
553
|
+
if (this.macUvLibraryShimSyncTimer) {
|
|
554
|
+
clearTimeout(this.macUvLibraryShimSyncTimer)
|
|
555
|
+
}
|
|
556
|
+
this.macUvLibraryShimSyncTimer = setTimeout(async () => {
|
|
557
|
+
this.macUvLibraryShimSyncTimer = null
|
|
558
|
+
try {
|
|
559
|
+
await this.syncMacUvLibraryShims()
|
|
560
|
+
} catch (error) {
|
|
561
|
+
console.warn("ffmpeg uv library shim sync error", error && error.message ? error.message : error)
|
|
562
|
+
}
|
|
563
|
+
}, 250)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
async uvLibraryDirs(kernel = this.kernel) {
|
|
567
|
+
if (kernel.platform !== "darwin") {
|
|
568
|
+
return []
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const root = this.uvPythonRoot(kernel)
|
|
572
|
+
const entries = await fs.promises.readdir(root, { withFileTypes: true }).catch(() => [])
|
|
573
|
+
const dirs = []
|
|
574
|
+
|
|
575
|
+
for (const entry of entries) {
|
|
576
|
+
if (!entry.isDirectory()) {
|
|
577
|
+
continue
|
|
578
|
+
}
|
|
579
|
+
const libDir = path.resolve(root, entry.name, "lib")
|
|
580
|
+
const exists = await fs.promises.access(libDir).then(() => true).catch(() => false)
|
|
581
|
+
if (exists) {
|
|
582
|
+
dirs.push(libDir)
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return dirs
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
async ffmpegLibraryFiles(kernel = this.kernel) {
|
|
590
|
+
if (kernel.platform !== "darwin") {
|
|
591
|
+
return []
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const dir = this.libraryDir(kernel)
|
|
595
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true }).catch(() => [])
|
|
596
|
+
return entries
|
|
597
|
+
.filter((entry) => entry.isFile() || entry.isSymbolicLink())
|
|
598
|
+
.map((entry) => entry.name)
|
|
599
|
+
.filter((name) => /^lib(?:av|sw)[^.]+(?:\.\d+)*\.dylib$/i.test(name))
|
|
600
|
+
.sort()
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
async syncMacUvLibraryShims(ondata) {
|
|
604
|
+
if (this.kernel.platform !== "darwin") {
|
|
605
|
+
return
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const [libraryDirs, libraryFiles] = await Promise.all([
|
|
609
|
+
this.uvLibraryDirs(),
|
|
610
|
+
this.ffmpegLibraryFiles()
|
|
611
|
+
])
|
|
612
|
+
|
|
613
|
+
if (libraryDirs.length === 0 || libraryFiles.length === 0) {
|
|
614
|
+
return
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
let createdCount = 0
|
|
618
|
+
let refreshedCount = 0
|
|
619
|
+
const sourceDir = this.libraryDir()
|
|
620
|
+
|
|
621
|
+
for (const libDir of libraryDirs) {
|
|
622
|
+
for (const filename of libraryFiles) {
|
|
623
|
+
const source = path.resolve(sourceDir, filename)
|
|
624
|
+
const target = path.resolve(libDir, filename)
|
|
625
|
+
const desiredLink = path.relative(libDir, source)
|
|
626
|
+
|
|
627
|
+
let stat
|
|
628
|
+
try {
|
|
629
|
+
stat = await fs.promises.lstat(target)
|
|
630
|
+
} catch (error) {
|
|
631
|
+
if (!error || error.code !== "ENOENT") {
|
|
632
|
+
throw error
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (!stat) {
|
|
637
|
+
await fs.promises.symlink(desiredLink, target)
|
|
638
|
+
createdCount += 1
|
|
639
|
+
continue
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (!stat.isSymbolicLink()) {
|
|
643
|
+
continue
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const currentLink = await fs.promises.readlink(target)
|
|
647
|
+
if (currentLink === desiredLink) {
|
|
648
|
+
continue
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
await fs.promises.unlink(target)
|
|
652
|
+
await fs.promises.symlink(desiredLink, target)
|
|
653
|
+
refreshedCount += 1
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (ondata && (createdCount > 0 || refreshedCount > 0)) {
|
|
658
|
+
ondata({
|
|
659
|
+
raw: `synced ${createdCount + refreshedCount} FFmpeg dylib shim(s) into uv Python runtime libraries...\r\n`
|
|
660
|
+
})
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
async removeMacUvLibraryShims(ondata) {
|
|
665
|
+
if (this.kernel.platform !== "darwin") {
|
|
666
|
+
return
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
const [libraryDirs, libraryFiles] = await Promise.all([
|
|
670
|
+
this.uvLibraryDirs(),
|
|
671
|
+
this.ffmpegLibraryFiles()
|
|
672
|
+
])
|
|
673
|
+
|
|
674
|
+
if (libraryDirs.length === 0 || libraryFiles.length === 0) {
|
|
675
|
+
return
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const sourceDir = this.libraryDir()
|
|
679
|
+
let removedCount = 0
|
|
680
|
+
|
|
681
|
+
for (const libDir of libraryDirs) {
|
|
682
|
+
for (const filename of libraryFiles) {
|
|
683
|
+
const target = path.resolve(libDir, filename)
|
|
684
|
+
|
|
685
|
+
let stat
|
|
686
|
+
try {
|
|
687
|
+
stat = await fs.promises.lstat(target)
|
|
688
|
+
} catch (error) {
|
|
689
|
+
if (!error || error.code !== "ENOENT") {
|
|
690
|
+
throw error
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (!stat || !stat.isSymbolicLink()) {
|
|
695
|
+
continue
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const currentLink = await fs.promises.readlink(target)
|
|
699
|
+
const resolved = path.resolve(libDir, currentLink)
|
|
700
|
+
if (path.dirname(resolved) !== sourceDir) {
|
|
701
|
+
continue
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
await fs.promises.unlink(target)
|
|
705
|
+
removedCount += 1
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if (ondata && removedCount > 0) {
|
|
710
|
+
ondata({ raw: `removed ${removedCount} FFmpeg dylib shim(s) from uv Python runtime libraries...\r\n` })
|
|
711
|
+
}
|
|
229
712
|
}
|
|
230
713
|
|
|
231
714
|
async selfTest(ondata) {
|
|
232
715
|
if (ondata) {
|
|
233
|
-
ondata({ raw: "verifying ffmpeg
|
|
716
|
+
ondata({ raw: "verifying ffmpeg installation...\r\n" })
|
|
234
717
|
}
|
|
235
718
|
|
|
236
719
|
const ffmpegVersionOutput = await this.execBinary(this.binaryPath("ffmpeg"), ["-version"])
|
|
@@ -250,66 +733,52 @@ class Ffmpeg {
|
|
|
250
733
|
throw new Error(`Unexpected ffprobe version: ${this.firstLine(ffprobeVersionOutput)}`)
|
|
251
734
|
}
|
|
252
735
|
|
|
736
|
+
await this.assertSharedLibraries()
|
|
253
737
|
return true
|
|
254
738
|
}
|
|
255
739
|
|
|
256
|
-
async
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
740
|
+
async assertSharedLibraries() {
|
|
741
|
+
const dir = this.libraryDir()
|
|
742
|
+
const entries = await fs.promises.readdir(dir).catch(() => {
|
|
743
|
+
throw new Error(`Missing FFmpeg library directory: ${dir}`)
|
|
744
|
+
})
|
|
261
745
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (this.hasLegacyCondaFfmpeg()) {
|
|
268
|
-
throw new Error("Legacy conda ffmpeg is still installed in the base environment")
|
|
746
|
+
const patterns = this.sharedLibraryPatterns()
|
|
747
|
+
for (const pattern of patterns) {
|
|
748
|
+
if (!entries.some((name) => pattern.test(name))) {
|
|
749
|
+
throw new Error(`Missing FFmpeg shared library matching ${pattern}`)
|
|
750
|
+
}
|
|
269
751
|
}
|
|
270
752
|
}
|
|
271
753
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
754
|
+
sharedLibraryPatterns() {
|
|
755
|
+
if (this.kernel.platform === "win32") {
|
|
756
|
+
return [
|
|
757
|
+
/^avcodec-\d+\.dll$/i,
|
|
758
|
+
/^avformat-\d+\.dll$/i,
|
|
759
|
+
/^avutil-\d+\.dll$/i,
|
|
760
|
+
/^swresample-\d+\.dll$/i,
|
|
761
|
+
/^swscale-\d+\.dll$/i
|
|
762
|
+
]
|
|
276
763
|
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
async sha256(filePath) {
|
|
280
|
-
const buffer = await fs.promises.readFile(filePath)
|
|
281
|
-
return crypto.createHash("sha256").update(buffer).digest("hex")
|
|
282
|
-
}
|
|
283
764
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
ffmpeg: this.binaryPath("ffmpeg"),
|
|
293
|
-
ffprobe: this.binaryPath("ffprobe")
|
|
294
|
-
}
|
|
765
|
+
if (this.kernel.platform === "darwin") {
|
|
766
|
+
return [
|
|
767
|
+
/^libavcodec(\.\d+)*\.dylib$/i,
|
|
768
|
+
/^libavformat(\.\d+)*\.dylib$/i,
|
|
769
|
+
/^libavutil(\.\d+)*\.dylib$/i,
|
|
770
|
+
/^libswresample(\.\d+)*\.dylib$/i,
|
|
771
|
+
/^libswscale(\.\d+)*\.dylib$/i
|
|
772
|
+
]
|
|
295
773
|
}
|
|
296
|
-
await fs.promises.writeFile(this.manifestPath(), JSON.stringify(manifest, null, 2))
|
|
297
|
-
}
|
|
298
774
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
return found
|
|
307
|
-
}
|
|
308
|
-
} else if (entry.name === targetName) {
|
|
309
|
-
return fullPath
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return null
|
|
775
|
+
return [
|
|
776
|
+
/^libavcodec\.so(\.\d+)*$/i,
|
|
777
|
+
/^libavformat\.so(\.\d+)*$/i,
|
|
778
|
+
/^libavutil\.so(\.\d+)*$/i,
|
|
779
|
+
/^libswresample\.so(\.\d+)*$/i,
|
|
780
|
+
/^libswscale\.so(\.\d+)*$/i
|
|
781
|
+
]
|
|
313
782
|
}
|
|
314
783
|
|
|
315
784
|
execBinary(file, args) {
|