Complete Phase C (custom commands & yt-dlp updater) and Phase D (library, UX & notifications)
Phase C had been implemented in the working tree but never committed: named custom-command templates (CRUD + per-download override), command preview, and the in-app yt-dlp self-updater. Phase D adds: history search/kind-filter/multi-select/bulk-delete/ re-download; native OS notifications on completion/failure; a private/ incognito download mode that skips history; settings+template backup/ restore via JSON; and a persistent error log surfaced as a Diagnostics report in Settings. See ROADMAP.md for the full per-item breakdown. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+36
-1
@@ -103,6 +103,35 @@ export interface AccessOptions {
|
||||
// each at least 1MB so tiny files don't get split pointlessly.
|
||||
export const ARIA2C_ARGS = 'aria2c:-x 16 -s 16 -k 1M'
|
||||
|
||||
/**
|
||||
* Shell-like split of a raw "extra yt-dlp args" string (Phase C custom-command
|
||||
* templates) into argv tokens. Supports single- and double-quoted spans so a
|
||||
* flag value containing spaces (e.g. a --ppa recipe) can be typed as one
|
||||
* token; no escape-character or nested-quote support beyond that.
|
||||
*/
|
||||
export function parseExtraArgs(raw: string): string[] {
|
||||
const args: string[] = []
|
||||
const re = /"([^"]*)"|'([^']*)'|(\S+)/g
|
||||
let m: RegExpExecArray | null
|
||||
while ((m = re.exec(raw)) !== null) {
|
||||
args.push(m[1] ?? m[2] ?? m[3])
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// Wrap a single argv token for human-readable display only (Phase C command
|
||||
// preview) — never used to build the real argv that gets spawned.
|
||||
function quoteForDisplay(arg: string): string {
|
||||
if (arg === '') return '""'
|
||||
if (/[\s"]/.test(arg)) return `"${arg.replace(/"/g, '\\"')}"`
|
||||
return arg
|
||||
}
|
||||
|
||||
/** Render a binary + argv as a single copy-pasteable command line. */
|
||||
export function formatCommandLine(exe: string, args: string[]): string {
|
||||
return [exe, ...args].map(quoteForDisplay).join(' ')
|
||||
}
|
||||
|
||||
function accessArgs(a: AccessOptions): string[] {
|
||||
const args: string[] = []
|
||||
if (a.proxy.trim()) args.push('--proxy', a.proxy.trim())
|
||||
@@ -152,7 +181,8 @@ export function buildArgs(
|
||||
outputTemplate: string,
|
||||
o: DownloadOptions,
|
||||
binDir: string,
|
||||
access: AccessOptions
|
||||
access: AccessOptions,
|
||||
extraArgs: string[] = []
|
||||
): string[] {
|
||||
const args = [
|
||||
'--newline',
|
||||
@@ -194,6 +224,11 @@ export function buildArgs(
|
||||
if (o.embedThumbnail && o.videoContainer !== 'webm') args.push('--embed-thumbnail')
|
||||
}
|
||||
|
||||
// Custom-command extra args go last, immediately before the `--` URL
|
||||
// terminator, so a user-supplied flag can override anything chosen above
|
||||
// (yt-dlp takes the last occurrence of most options).
|
||||
if (extraArgs.length > 0) args.push(...extraArgs)
|
||||
|
||||
// `--` terminates option parsing so the URL can never be read as a flag.
|
||||
args.push('--', opts.url)
|
||||
return args
|
||||
|
||||
Reference in New Issue
Block a user