veodl 1.12.0 → 1.12.1
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/CHANGELOG.md +12 -0
- package/LICENSE +21 -21
- package/README.md +8 -6
- package/docs/AGENT_GUIDE.md +1 -0
- package/package.json +1 -5
- package/src/alias.js +258 -258
- package/src/backend-update.js +191 -191
- package/src/backend.js +572 -548
- package/src/cli.js +7 -3
- package/src/compatibility.js +38 -38
- package/src/config-clipboard.js +67 -67
- package/src/config-editor.js +188 -188
- package/src/config-errors.js +64 -64
- package/src/config-reset.js +39 -39
- package/src/disk-space.js +67 -67
- package/src/doctor.js +306 -302
- package/src/download-cache.js +31 -31
- package/src/downloader.js +37 -2
- package/src/execution.js +59 -59
- package/src/experimental-fragments.js +29 -29
- package/src/flush.js +66 -66
- package/src/history.js +173 -173
- package/src/inline-region.js +72 -72
- package/src/inspect-media.js +179 -179
- package/src/interactive.js +19 -19
- package/src/jobs.js +218 -218
- package/src/legacy-config-comments.js +106 -106
- package/src/media-assets.js +272 -0
- package/src/naming.js +53 -53
- package/src/open-file.js +15 -15
- package/src/option-suggestions.js +43 -43
- package/src/paths.js +26 -26
- package/src/playlist.js +31 -31
- package/src/run-archive.js +148 -148
- package/src/runs.js +8 -8
- package/src/source-discovery.js +85 -83
- package/src/stats.js +63 -63
- package/src/terminal-size.js +8 -8
- package/src/terminal-title.js +19 -19
- package/src/tool-setup.js +289 -151
- package/src/updater.js +229 -229
- package/src/utils.js +208 -208
- package/src/version.js +38 -38
package/src/backend-update.js
CHANGED
|
@@ -1,191 +1,191 @@
|
|
|
1
|
-
import { commandOutput } from './output.js';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { rm } from 'node:fs/promises';
|
|
4
|
-
import {
|
|
5
|
-
RELEASE, activeOverride, backendCacheDirectory, clearBackendOverride, installBackend,
|
|
6
|
-
isValidRelease, readBackendOverride, releaseUrl, selectAsset, writeBackendOverride,
|
|
7
|
-
} from './backend.js';
|
|
8
|
-
import { compareVersions } from './version.js';
|
|
9
|
-
import { readableError, cleanText } from './utils.js';
|
|
10
|
-
|
|
11
|
-
const API_LATEST = 'https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest';
|
|
12
|
-
const CHECKSUMS = 'SHA2-256SUMS';
|
|
13
|
-
|
|
14
|
-
export const BACKEND_HELP = `veo backend - manage the yt-dlp backend
|
|
15
|
-
|
|
16
|
-
Usage:
|
|
17
|
-
veo backend update Install the newest yt-dlp release
|
|
18
|
-
veo backend update --check Only report whether a newer release exists
|
|
19
|
-
veo backend reset Forget the installed release and use the pinned one
|
|
20
|
-
veo backend reset --keep-files
|
|
21
|
-
Remove the pointer but keep the downloaded files
|
|
22
|
-
veo backend --help Show this help
|
|
23
|
-
|
|
24
|
-
veo ships a pinned, hash-verified yt-dlp release. \`veo backend update\` is an
|
|
25
|
-
opt-in escape hatch for when a website changed and the pinned release is too old:
|
|
26
|
-
it installs the newest official release after verifying its SHA-256 against that
|
|
27
|
-
release's own SHA2-256SUMS file over HTTPS.
|
|
28
|
-
|
|
29
|
-
That checksum does not ship with veo, so this weakens the pinned-hash guarantee:
|
|
30
|
-
it then rests on HTTPS and GitHub alone. \`veo backend reset\` returns to the
|
|
31
|
-
release veo was built and tested against. VEO_YT_DLP_PATH always wins over both.
|
|
32
|
-
`;
|
|
33
|
-
|
|
34
|
-
export async function fetchLatestBackendRelease({ fetchImpl = fetch, timeoutMs = 10_000, signal } = {}) {
|
|
35
|
-
const response = await fetchImpl(API_LATEST, {
|
|
36
|
-
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs),
|
|
37
|
-
headers: { accept: 'application/vnd.github+json', 'user-agent': 'veo' },
|
|
38
|
-
});
|
|
39
|
-
if (!response.ok) {
|
|
40
|
-
await response.body?.cancel();
|
|
41
|
-
throw new Error(`Could not query the latest yt-dlp release: HTTP ${response.status}.`);
|
|
42
|
-
}
|
|
43
|
-
const data = await response.json();
|
|
44
|
-
const tag = data?.tag_name;
|
|
45
|
-
if (!isValidRelease(tag)) throw new Error(`The release host returned an unexpected version: ${cleanText(String(tag))}`);
|
|
46
|
-
return tag;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Parse an official SHA2-256SUMS file into a name -> hash map. */
|
|
50
|
-
export function parseChecksums(text) {
|
|
51
|
-
const hashes = new Map();
|
|
52
|
-
for (const line of String(text).split('\n')) {
|
|
53
|
-
const match = /^([a-f0-9]{64})\s+\*?(.+?)\s*$/i.exec(line.trim());
|
|
54
|
-
if (match) hashes.set(path.basename(match[2]), match[1].toLowerCase());
|
|
55
|
-
}
|
|
56
|
-
if (!hashes.size) throw new Error('The release checksum list could not be parsed.');
|
|
57
|
-
return hashes;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export async function fetchReleaseChecksum({ release, asset, fetchImpl = fetch, timeoutMs = 15_000, signal }) {
|
|
61
|
-
if (!isValidRelease(release)) throw new Error(`Invalid yt-dlp release: ${release}`);
|
|
62
|
-
const response = await fetchImpl(releaseUrl(release, CHECKSUMS), {
|
|
63
|
-
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs),
|
|
64
|
-
});
|
|
65
|
-
if (!response.ok) {
|
|
66
|
-
await response.body?.cancel();
|
|
67
|
-
throw new Error(`Could not download ${CHECKSUMS} for yt-dlp ${release}: HTTP ${response.status}.`);
|
|
68
|
-
}
|
|
69
|
-
const hash = parseChecksums(await response.text()).get(asset);
|
|
70
|
-
if (!hash) throw new Error(`yt-dlp ${release} does not publish a checksum for ${asset}.`);
|
|
71
|
-
return hash;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function muslDetected() {
|
|
75
|
-
return process.platform === 'linux' && !process.report.getReport().header.glibcVersionRuntime;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export async function checkBackend({
|
|
79
|
-
platform = process.platform,
|
|
80
|
-
arch = process.arch,
|
|
81
|
-
musl = muslDetected(),
|
|
82
|
-
signal,
|
|
83
|
-
stateFile,
|
|
84
|
-
fetchImpl = fetch,
|
|
85
|
-
installed,
|
|
86
|
-
latest,
|
|
87
|
-
} = {}) {
|
|
88
|
-
const asset = selectAsset(platform, arch, musl);
|
|
89
|
-
const active = installed !== undefined ? installed : await activeOverride(asset, { signal, stateFile });
|
|
90
|
-
return {
|
|
91
|
-
asset,
|
|
92
|
-
active,
|
|
93
|
-
current: active?.release ?? RELEASE,
|
|
94
|
-
latest: latest ?? await fetchLatestBackendRelease({ fetchImpl, signal }),
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export async function backendUpdateMain(args = [], {
|
|
99
|
-
stdout = process.stdout,
|
|
100
|
-
stderr = process.stderr,
|
|
101
|
-
env = process.env,
|
|
102
|
-
signal,
|
|
103
|
-
stateFile,
|
|
104
|
-
platform = process.platform,
|
|
105
|
-
arch = process.arch,
|
|
106
|
-
fetchImpl = fetch,
|
|
107
|
-
check = checkBackend,
|
|
108
|
-
install = installBackend,
|
|
109
|
-
reset = clearBackendOverride,
|
|
110
|
-
removeFiles = directory => rm(directory, { recursive: true, force: true }),
|
|
111
|
-
} = {}) {
|
|
112
|
-
[args, stdout, stderr] = commandOutput(args, stdout, stderr);
|
|
113
|
-
const [command, ...rest] = args;
|
|
114
|
-
if (command === undefined || command === '-h' || command === '--help' || rest.includes('-h') || rest.includes('--help')) {
|
|
115
|
-
stdout.write(BACKEND_HELP);
|
|
116
|
-
return 0;
|
|
117
|
-
}
|
|
118
|
-
if (command === 'reset') {
|
|
119
|
-
const unknown = rest.filter(token => token !== '--keep-files');
|
|
120
|
-
if (unknown.length) throw new Error(`Unknown option for veo backend reset: ${unknown.join(' ')}. Use: veo backend reset [--keep-files]`);
|
|
121
|
-
if (platform === 'android') {
|
|
122
|
-
stdout.write('Android/Termux uses system yt-dlp or VEO_YT_DLP_PATH. There is no managed backend to reset.\n');
|
|
123
|
-
return 0;
|
|
124
|
-
}
|
|
125
|
-
const state = await readBackendOverride(stateFile);
|
|
126
|
-
if (!await reset(stateFile)) {
|
|
127
|
-
stdout.write(`No installed backend to remove. veo uses the pinned release ${RELEASE}.\n`);
|
|
128
|
-
return 0;
|
|
129
|
-
}
|
|
130
|
-
if (state?.release && !rest.includes('--keep-files')) await removeFiles(backendCacheDirectory(state.release)).catch(() => {});
|
|
131
|
-
const removed = state?.release ? ` yt-dlp ${state.release}` : ' the installed backend';
|
|
132
|
-
stdout.write(`Removed${removed}. veo uses the pinned release ${RELEASE} again.\n`);
|
|
133
|
-
return 0;
|
|
134
|
-
}
|
|
135
|
-
if (command !== 'update') {
|
|
136
|
-
throw new Error(`Unknown backend command: ${cleanText(command)}. Use: veo backend update [--check] | veo backend reset`);
|
|
137
|
-
}
|
|
138
|
-
const checkOnly = rest.includes('--check');
|
|
139
|
-
const unknown = rest.filter(token => token !== '--check');
|
|
140
|
-
if (unknown.length) throw new Error(`Unknown option for veo backend update: ${unknown.join(' ')}. Use: veo backend update [--check]`);
|
|
141
|
-
if (platform === 'android') {
|
|
142
|
-
stdout.write('Android/Termux uses system yt-dlp or VEO_YT_DLP_PATH, not a pinned release. Update the Termux package with: pkg upgrade python-yt-dlp yt-dlp-ejs\n');
|
|
143
|
-
return 0;
|
|
144
|
-
}
|
|
145
|
-
if (env.VEO_YT_DLP_PATH) stderr.write('veo: VEO_YT_DLP_PATH is set and always takes precedence over the managed backend.\n');
|
|
146
|
-
|
|
147
|
-
let info;
|
|
148
|
-
try {
|
|
149
|
-
info = await check({ platform, arch, signal, stateFile, fetchImpl });
|
|
150
|
-
} catch (error) {
|
|
151
|
-
stderr.write(`veo: ${readableError(error)}\n`);
|
|
152
|
-
return 1;
|
|
153
|
-
}
|
|
154
|
-
const { asset, current, latest } = info;
|
|
155
|
-
if (!asset) {
|
|
156
|
-
stderr.write(`veo: no standalone yt-dlp is published for ${platform}/${arch}. Set VEO_YT_DLP_PATH to a trusted executable.\n`);
|
|
157
|
-
return 1;
|
|
158
|
-
}
|
|
159
|
-
if (compareVersions(latest, current) <= 0) {
|
|
160
|
-
stdout.write(`yt-dlp ${current} is up to date${current === RELEASE ? ' (pinned release, hash verified at build time)' : ' (installed release)'}.\n`);
|
|
161
|
-
return 0;
|
|
162
|
-
}
|
|
163
|
-
if (checkOnly) {
|
|
164
|
-
stdout.write(`Newer yt-dlp release available: ${latest} (using ${current}). Run: veo backend update\n`);
|
|
165
|
-
return 0;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
let sha256;
|
|
169
|
-
try {
|
|
170
|
-
sha256 = await fetchReleaseChecksum({ release: latest, asset, fetchImpl, signal });
|
|
171
|
-
} catch (error) {
|
|
172
|
-
stderr.write(`veo: ${readableError(error)}\nNothing was installed. The pinned release ${RELEASE} is still in use.\n`);
|
|
173
|
-
return 1;
|
|
174
|
-
}
|
|
175
|
-
stdout.write(`Installing yt-dlp ${latest} for ${platform}/${asset}…\n`);
|
|
176
|
-
try {
|
|
177
|
-
await install({ release: latest, asset, sha256, signal, status: message => stdout.write(`${message}\n`) });
|
|
178
|
-
} catch (error) {
|
|
179
|
-
stderr.write(`veo: ${readableError(error)}\nThe pinned release ${RELEASE} is still in use.\n`);
|
|
180
|
-
return 1;
|
|
181
|
-
}
|
|
182
|
-
try {
|
|
183
|
-
await writeBackendOverride({ release: latest, asset, sha256, platform, arch, installed: new Date().toISOString() }, stateFile);
|
|
184
|
-
} catch (error) {
|
|
185
|
-
stderr.write(`veo: ${readableError(error)}\nThe download succeeded, but veo could not record it; the pinned release stays in use.\n`);
|
|
186
|
-
return 1;
|
|
187
|
-
}
|
|
188
|
-
stdout.write(`yt-dlp ${latest} installed and will be used for the next download.\n`);
|
|
189
|
-
stderr.write(`veo: note: yt-dlp ${latest} was verified against the checksum published with that release over HTTPS, not against a hash shipped with veo. Run "veo backend reset" to return to the pinned release.\n`);
|
|
190
|
-
return 0;
|
|
191
|
-
}
|
|
1
|
+
import { commandOutput } from './output.js';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { rm } from 'node:fs/promises';
|
|
4
|
+
import {
|
|
5
|
+
RELEASE, activeOverride, backendCacheDirectory, clearBackendOverride, installBackend,
|
|
6
|
+
isValidRelease, readBackendOverride, releaseUrl, selectAsset, writeBackendOverride,
|
|
7
|
+
} from './backend.js';
|
|
8
|
+
import { compareVersions } from './version.js';
|
|
9
|
+
import { readableError, cleanText } from './utils.js';
|
|
10
|
+
|
|
11
|
+
const API_LATEST = 'https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest';
|
|
12
|
+
const CHECKSUMS = 'SHA2-256SUMS';
|
|
13
|
+
|
|
14
|
+
export const BACKEND_HELP = `veo backend - manage the yt-dlp backend
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
veo backend update Install the newest yt-dlp release
|
|
18
|
+
veo backend update --check Only report whether a newer release exists
|
|
19
|
+
veo backend reset Forget the installed release and use the pinned one
|
|
20
|
+
veo backend reset --keep-files
|
|
21
|
+
Remove the pointer but keep the downloaded files
|
|
22
|
+
veo backend --help Show this help
|
|
23
|
+
|
|
24
|
+
veo ships a pinned, hash-verified yt-dlp release. \`veo backend update\` is an
|
|
25
|
+
opt-in escape hatch for when a website changed and the pinned release is too old:
|
|
26
|
+
it installs the newest official release after verifying its SHA-256 against that
|
|
27
|
+
release's own SHA2-256SUMS file over HTTPS.
|
|
28
|
+
|
|
29
|
+
That checksum does not ship with veo, so this weakens the pinned-hash guarantee:
|
|
30
|
+
it then rests on HTTPS and GitHub alone. \`veo backend reset\` returns to the
|
|
31
|
+
release veo was built and tested against. VEO_YT_DLP_PATH always wins over both.
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
export async function fetchLatestBackendRelease({ fetchImpl = fetch, timeoutMs = 10_000, signal } = {}) {
|
|
35
|
+
const response = await fetchImpl(API_LATEST, {
|
|
36
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs),
|
|
37
|
+
headers: { accept: 'application/vnd.github+json', 'user-agent': 'veo' },
|
|
38
|
+
});
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
await response.body?.cancel();
|
|
41
|
+
throw new Error(`Could not query the latest yt-dlp release: HTTP ${response.status}.`);
|
|
42
|
+
}
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
const tag = data?.tag_name;
|
|
45
|
+
if (!isValidRelease(tag)) throw new Error(`The release host returned an unexpected version: ${cleanText(String(tag))}`);
|
|
46
|
+
return tag;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Parse an official SHA2-256SUMS file into a name -> hash map. */
|
|
50
|
+
export function parseChecksums(text) {
|
|
51
|
+
const hashes = new Map();
|
|
52
|
+
for (const line of String(text).split('\n')) {
|
|
53
|
+
const match = /^([a-f0-9]{64})\s+\*?(.+?)\s*$/i.exec(line.trim());
|
|
54
|
+
if (match) hashes.set(path.basename(match[2]), match[1].toLowerCase());
|
|
55
|
+
}
|
|
56
|
+
if (!hashes.size) throw new Error('The release checksum list could not be parsed.');
|
|
57
|
+
return hashes;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function fetchReleaseChecksum({ release, asset, fetchImpl = fetch, timeoutMs = 15_000, signal }) {
|
|
61
|
+
if (!isValidRelease(release)) throw new Error(`Invalid yt-dlp release: ${release}`);
|
|
62
|
+
const response = await fetchImpl(releaseUrl(release, CHECKSUMS), {
|
|
63
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs),
|
|
64
|
+
});
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
await response.body?.cancel();
|
|
67
|
+
throw new Error(`Could not download ${CHECKSUMS} for yt-dlp ${release}: HTTP ${response.status}.`);
|
|
68
|
+
}
|
|
69
|
+
const hash = parseChecksums(await response.text()).get(asset);
|
|
70
|
+
if (!hash) throw new Error(`yt-dlp ${release} does not publish a checksum for ${asset}.`);
|
|
71
|
+
return hash;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function muslDetected() {
|
|
75
|
+
return process.platform === 'linux' && !process.report.getReport().header.glibcVersionRuntime;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function checkBackend({
|
|
79
|
+
platform = process.platform,
|
|
80
|
+
arch = process.arch,
|
|
81
|
+
musl = muslDetected(),
|
|
82
|
+
signal,
|
|
83
|
+
stateFile,
|
|
84
|
+
fetchImpl = fetch,
|
|
85
|
+
installed,
|
|
86
|
+
latest,
|
|
87
|
+
} = {}) {
|
|
88
|
+
const asset = selectAsset(platform, arch, musl);
|
|
89
|
+
const active = installed !== undefined ? installed : await activeOverride(asset, { signal, stateFile });
|
|
90
|
+
return {
|
|
91
|
+
asset,
|
|
92
|
+
active,
|
|
93
|
+
current: active?.release ?? RELEASE,
|
|
94
|
+
latest: latest ?? await fetchLatestBackendRelease({ fetchImpl, signal }),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function backendUpdateMain(args = [], {
|
|
99
|
+
stdout = process.stdout,
|
|
100
|
+
stderr = process.stderr,
|
|
101
|
+
env = process.env,
|
|
102
|
+
signal,
|
|
103
|
+
stateFile,
|
|
104
|
+
platform = process.platform,
|
|
105
|
+
arch = process.arch,
|
|
106
|
+
fetchImpl = fetch,
|
|
107
|
+
check = checkBackend,
|
|
108
|
+
install = installBackend,
|
|
109
|
+
reset = clearBackendOverride,
|
|
110
|
+
removeFiles = directory => rm(directory, { recursive: true, force: true }),
|
|
111
|
+
} = {}) {
|
|
112
|
+
[args, stdout, stderr] = commandOutput(args, stdout, stderr);
|
|
113
|
+
const [command, ...rest] = args;
|
|
114
|
+
if (command === undefined || command === '-h' || command === '--help' || rest.includes('-h') || rest.includes('--help')) {
|
|
115
|
+
stdout.write(BACKEND_HELP);
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
if (command === 'reset') {
|
|
119
|
+
const unknown = rest.filter(token => token !== '--keep-files');
|
|
120
|
+
if (unknown.length) throw new Error(`Unknown option for veo backend reset: ${unknown.join(' ')}. Use: veo backend reset [--keep-files]`);
|
|
121
|
+
if (platform === 'android') {
|
|
122
|
+
stdout.write('Android/Termux uses system yt-dlp or VEO_YT_DLP_PATH. There is no managed backend to reset.\n');
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
const state = await readBackendOverride(stateFile);
|
|
126
|
+
if (!await reset(stateFile)) {
|
|
127
|
+
stdout.write(`No installed backend to remove. veo uses the pinned release ${RELEASE}.\n`);
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
if (state?.release && !rest.includes('--keep-files')) await removeFiles(backendCacheDirectory(state.release)).catch(() => {});
|
|
131
|
+
const removed = state?.release ? ` yt-dlp ${state.release}` : ' the installed backend';
|
|
132
|
+
stdout.write(`Removed${removed}. veo uses the pinned release ${RELEASE} again.\n`);
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
if (command !== 'update') {
|
|
136
|
+
throw new Error(`Unknown backend command: ${cleanText(command)}. Use: veo backend update [--check] | veo backend reset`);
|
|
137
|
+
}
|
|
138
|
+
const checkOnly = rest.includes('--check');
|
|
139
|
+
const unknown = rest.filter(token => token !== '--check');
|
|
140
|
+
if (unknown.length) throw new Error(`Unknown option for veo backend update: ${unknown.join(' ')}. Use: veo backend update [--check]`);
|
|
141
|
+
if (platform === 'android') {
|
|
142
|
+
stdout.write('Android/Termux uses system yt-dlp or VEO_YT_DLP_PATH, not a pinned release. Update the Termux package with: pkg upgrade python-yt-dlp yt-dlp-ejs\n');
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
if (env.VEO_YT_DLP_PATH) stderr.write('veo: VEO_YT_DLP_PATH is set and always takes precedence over the managed backend.\n');
|
|
146
|
+
|
|
147
|
+
let info;
|
|
148
|
+
try {
|
|
149
|
+
info = await check({ platform, arch, signal, stateFile, fetchImpl });
|
|
150
|
+
} catch (error) {
|
|
151
|
+
stderr.write(`veo: ${readableError(error)}\n`);
|
|
152
|
+
return 1;
|
|
153
|
+
}
|
|
154
|
+
const { asset, current, latest } = info;
|
|
155
|
+
if (!asset) {
|
|
156
|
+
stderr.write(`veo: no standalone yt-dlp is published for ${platform}/${arch}. Set VEO_YT_DLP_PATH to a trusted executable.\n`);
|
|
157
|
+
return 1;
|
|
158
|
+
}
|
|
159
|
+
if (compareVersions(latest, current) <= 0) {
|
|
160
|
+
stdout.write(`yt-dlp ${current} is up to date${current === RELEASE ? ' (pinned release, hash verified at build time)' : ' (installed release)'}.\n`);
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
if (checkOnly) {
|
|
164
|
+
stdout.write(`Newer yt-dlp release available: ${latest} (using ${current}). Run: veo backend update\n`);
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let sha256;
|
|
169
|
+
try {
|
|
170
|
+
sha256 = await fetchReleaseChecksum({ release: latest, asset, fetchImpl, signal });
|
|
171
|
+
} catch (error) {
|
|
172
|
+
stderr.write(`veo: ${readableError(error)}\nNothing was installed. The pinned release ${RELEASE} is still in use.\n`);
|
|
173
|
+
return 1;
|
|
174
|
+
}
|
|
175
|
+
stdout.write(`Installing yt-dlp ${latest} for ${platform}/${asset}…\n`);
|
|
176
|
+
try {
|
|
177
|
+
await install({ release: latest, asset, sha256, signal, status: message => stdout.write(`${message}\n`) });
|
|
178
|
+
} catch (error) {
|
|
179
|
+
stderr.write(`veo: ${readableError(error)}\nThe pinned release ${RELEASE} is still in use.\n`);
|
|
180
|
+
return 1;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
await writeBackendOverride({ release: latest, asset, sha256, platform, arch, installed: new Date().toISOString() }, stateFile);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
stderr.write(`veo: ${readableError(error)}\nThe download succeeded, but veo could not record it; the pinned release stays in use.\n`);
|
|
186
|
+
return 1;
|
|
187
|
+
}
|
|
188
|
+
stdout.write(`yt-dlp ${latest} installed and will be used for the next download.\n`);
|
|
189
|
+
stderr.write(`veo: note: yt-dlp ${latest} was verified against the checksum published with that release over HTTPS, not against a hash shipped with veo. Run "veo backend reset" to return to the pinned release.\n`);
|
|
190
|
+
return 0;
|
|
191
|
+
}
|