tracksmith 0.3.0 → 0.3.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.
@@ -264,12 +264,11 @@ function resolveAudioTrackId(tracks, requested) {
264
264
  if (audio.length === 0) throw new CliError("The audio input contains no audio tracks.");
265
265
  throw new CliError(`The audio input contains ${audio.length} audio tracks; pass --track <id> (see: tracksmith inspect <file>).`);
266
266
  }
267
- function buildMuxArgs(a) {
267
+ function buildMuxArgs(a, platform = process.platform) {
268
268
  const trackOrder = [...a.videoTrackIds.map((trackId) => `0:${trackId}`), ...a.groups.map((group, index) => `${index + 1}:${group.trackId}`)].join(",");
269
269
  const args = [
270
270
  "mkvmerge",
271
- "--command-line-charset",
272
- "UTF-8",
271
+ ...platform === "win32" ? [] : ["--command-line-charset", "UTF-8"],
273
272
  "-o",
274
273
  toolPath(a.output),
275
274
  "--track-order",
@@ -1 +1 @@
1
- {"version":3,"file":"tracksmith.js","names":[],"sources":["../src/types.ts","../src/output.ts","../src/paths.ts","../src/probe.ts","../src/commands/edit.ts","../src/commands/extract.ts","../src/commands/inspect.ts","../src/commands/mux.ts","../src/commands/test-clip.ts","../src/cli.ts","../src/prompt.ts","../src/tools.ts","../src/runner.ts","../src/tracksmith.ts"],"sourcesContent":["export interface RunResult {\n exitCode: number;\n stdout: string;\n stderr: string;\n}\n\nexport interface RunOptions {\n stream?: 'stdout' | 'stderr';\n}\n\nexport interface Runner {\n run(argv: readonly string[], options?: RunOptions): Promise<RunResult>;\n}\n\nexport interface Track {\n id: number;\n type: string;\n codec: string;\n language?: string;\n name?: string;\n channels?: number;\n isDefault: boolean;\n isForced: boolean;\n}\n\nexport class CliError extends Error {\n constructor(\n message: string,\n readonly exitCode: number = 1\n ) {\n super(message);\n this.name = 'CliError';\n }\n}\n\nexport interface CommandDeps {\n runner: Runner;\n /** stdin is interactive — gates confirmation prompts. */\n isTTY: boolean;\n /** stderr is interactive — gates live tool progress output. */\n stderrIsTTY: boolean;\n confirm: (message: string) => Promise<boolean>;\n exists?: (path: string) => boolean;\n}\n","import { existsSync, statSync } from 'node:fs';\nimport { basename, dirname, extname, join } from 'node:path';\n\nimport { CliError } from './types.ts';\n\nexport function stemPath(file: string): string {\n return join(dirname(file), basename(file, extname(file)));\n}\n\nexport function defaultExtractOutput(source: string, trackId: number): string {\n return `${stemPath(source)}.track${trackId}.mka`;\n}\n\nexport function defaultTestClipOutput(video: string): string {\n return `${stemPath(video)}.sync-test.mkv`;\n}\n\nexport function requireInputFile(\n path: string,\n exists: (path: string) => boolean = existsSync\n): void {\n if (!exists(path)) throw new CliError(`Input file not found: \"${path}\".`);\n}\n\nexport interface OverwriteContext {\n force: boolean;\n isTTY: boolean;\n confirm: (message: string) => Promise<boolean>;\n exists?: (path: string) => boolean;\n inputs?: readonly string[];\n}\n\nfunction sameFile(left: string, right: string): boolean {\n try {\n const leftStat = statSync(left, { bigint: true });\n const rightStat = statSync(right, { bigint: true });\n return leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;\n } catch {\n return false;\n }\n}\n\n/** Returns the input path the output aliases on disk (same dev/inode), if any. */\nexport function findAliasedInput(output: string, inputs: readonly string[]): string | undefined {\n return inputs.find((input) => sameFile(output, input));\n}\n\nexport async function ensureWritable(path: string, ctx: OverwriteContext): Promise<void> {\n const aliasedInput = findAliasedInput(path, ctx.inputs ?? []);\n if (aliasedInput !== undefined) {\n throw new CliError(\n `Output \"${path}\" is the same file as input \"${aliasedInput}\". Choose a different output path.`\n );\n }\n\n const exists = ctx.exists ?? existsSync;\n if (!exists(path)) return;\n if (ctx.force) return;\n if (!ctx.isTTY) {\n throw new CliError(\n `Output \"${path}\" already exists. Pass --force to overwrite (cannot prompt in a non-interactive session).`\n );\n }\n const approved = await ctx.confirm(`Output \"${path}\" already exists. Overwrite?`);\n if (!approved) throw new CliError('Aborted: existing output left untouched.');\n}\n","import { isAbsolute, resolve } from 'node:path';\n\nconst TOOL_OPERATOR_PREFIXES = '-+@=()[]';\n\nexport function toolPath(path: string): string {\n if (isAbsolute(path) || !TOOL_OPERATOR_PREFIXES.includes(path[0] ?? '')) return path;\n return resolve(path);\n}\n","import type { Runner, Track } from './types.ts';\n\nimport * as z from 'zod/mini';\n\nimport { toolPath } from './paths.ts';\nimport { CliError } from './types.ts';\n\nconst mkvmergePropertiesSchema = z.object({\n language: z.optional(z.string()),\n language_ietf: z.optional(z.string()),\n track_name: z.optional(z.string()),\n audio_channels: z.optional(z.number()),\n default_track: z.optional(z.boolean()),\n forced_track: z.optional(z.boolean())\n});\n\nconst mkvmergeTrackSchema = z.object({\n id: z.int(),\n type: z.string(),\n codec: z.string(),\n properties: z.optional(mkvmergePropertiesSchema)\n});\n\nconst mkvmergeContainerSchema = z.object({\n properties: z.optional(z.object({ title: z.optional(z.string()) }))\n});\n\nconst mkvmergeOutputSchema = z.object({\n // Container metadata is ancillary: an unexpected shape must not fail track\n // parsing, so it is validated separately and dropped on mismatch.\n container: z.optional(z.unknown()),\n tracks: z.array(mkvmergeTrackSchema)\n});\n\nfunction containerTitle(container: unknown): string | undefined {\n const result = z.safeParse(mkvmergeContainerSchema, container);\n return result.success ? result.data.properties?.title : undefined;\n}\n\nexport interface MkvFile {\n title?: string;\n tracks: Track[];\n}\n\nexport function parseMkvmergeFile(json: string): MkvFile {\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch {\n throw new CliError('Could not parse mkvmerge -J output as JSON.');\n }\n const result = z.safeParse(mkvmergeOutputSchema, parsed);\n if (!result.success) throw new CliError('mkvmerge -J output has an unexpected structure.');\n\n return {\n title: containerTitle(result.data.container),\n tracks: result.data.tracks.map((track) => ({\n id: track.id,\n type: track.type,\n codec: track.codec,\n language: track.properties?.language_ietf || track.properties?.language || undefined,\n name: track.properties?.track_name,\n channels: track.properties?.audio_channels,\n isDefault: track.properties?.default_track ?? false,\n isForced: track.properties?.forced_track ?? false\n }))\n };\n}\n\nexport function parseMkvmergeJson(json: string): Track[] {\n return parseMkvmergeFile(json).tracks;\n}\n\nexport async function probeFile(runner: Runner, file: string): Promise<MkvFile> {\n const result = await runner.run(['mkvmerge', '-J', toolPath(file)]);\n if (result.exitCode >= 2) {\n throw new CliError(\n `mkvmerge could not read \"${file}\" (exit ${result.exitCode}):\\n${result.stderr || result.stdout}`\n );\n }\n return parseMkvmergeFile(result.stdout);\n}\n\nexport async function probeTracks(runner: Runner, file: string): Promise<Track[]> {\n return (await probeFile(runner, file)).tracks;\n}\n\nfunction validAudioIds(tracks: Track[]): string {\n const ids = tracks.filter((track) => track.type === 'audio').map((track) => track.id);\n return `Valid audio track IDs: ${ids.length > 0 ? ids.join(', ') : 'none'}`;\n}\n\nexport function requireAudioTrack(tracks: Track[], id: number): Track {\n const track = tracks.find((candidate) => candidate.id === id);\n if (!track) throw new CliError(`Track ${id} does not exist. ${validAudioIds(tracks)}`);\n if (track.type !== 'audio')\n throw new CliError(`Track ${id} is ${track.type}, not audio. ${validAudioIds(tracks)}`);\n return track;\n}\n\nexport function audioRelativeIndex(tracks: Track[], id: number): number {\n const index = tracks\n .filter((track) => track.type === 'audio')\n .findIndex((track) => track.id === id);\n if (index === -1)\n throw new CliError(`Track ${id} is not an audio track. ${validAudioIds(tracks)}`);\n return index;\n}\n\nexport function requireTrack(tracks: Track[], id: number): Track {\n const track = tracks.find((candidate) => candidate.id === id);\n if (!track) {\n const ids = tracks.map((candidate) => candidate.id);\n throw new CliError(\n `Track ${id} does not exist. Valid track IDs: ${ids.length > 0 ? ids.join(', ') : 'none'}`\n );\n }\n return track;\n}\n","import type { CommandDeps } from '../types.ts';\n\nimport { requireInputFile } from '../output.ts';\nimport { toolPath } from '../paths.ts';\nimport { probeTracks, requireTrack } from '../probe.ts';\nimport { CliError } from '../types.ts';\n\nexport type EditOpts =\n | { kind: 'title'; file: string; title: string }\n | {\n kind: 'track';\n file: string;\n track: number;\n name?: string;\n language?: string;\n isDefault?: boolean;\n isForced?: boolean;\n };\n\ntype TrackEditOpts = Extract<EditOpts, { kind: 'track' }>;\n\nfunction hasTrackEdit(opts: TrackEditOpts): boolean {\n return (\n opts.name !== undefined ||\n opts.language !== undefined ||\n opts.isDefault !== undefined ||\n opts.isForced !== undefined\n );\n}\n\nexport function buildEditArgs(opts: EditOpts): string[] {\n // Node and Bun always pass argv as UTF-8 bytes; without this flag mkvpropedit\n // decodes them with the system locale and silently mangles non-ASCII values\n // (e.g. drops a Japanese title under LC_ALL=C) while still exiting 0.\n const args = ['mkvpropedit', '--command-line-charset', 'UTF-8', toolPath(opts.file)];\n switch (opts.kind) {\n case 'title': {\n args.push('--edit', 'info');\n if (opts.title === '') args.push('--delete', 'title');\n else args.push('--set', `title=${opts.title}`);\n return args;\n }\n case 'track': {\n // mkvpropedit's track:n selector is 1-based over the same track order\n // mkvmerge --identify reports, so the inspect ID maps to id + 1.\n args.push('--edit', `track:${opts.track + 1}`);\n if (opts.name === '') args.push('--delete', 'name');\n else if (opts.name !== undefined) args.push('--set', `name=${opts.name}`);\n if (opts.language !== undefined) args.push('--set', `language=${opts.language}`);\n if (opts.isDefault !== undefined)\n args.push('--set', `flag-default=${opts.isDefault ? '1' : '0'}`);\n if (opts.isForced !== undefined)\n args.push('--set', `flag-forced=${opts.isForced ? '1' : '0'}`);\n return args;\n }\n default:\n return opts satisfies never;\n }\n}\n\n/** Returns mkvpropedit's diagnostic output when it exited 1 (modified with warnings). */\nexport async function editCommand(opts: EditOpts, deps: CommandDeps): Promise<string | undefined> {\n requireInputFile(opts.file, deps.exists);\n if (opts.kind === 'track') {\n if (!hasTrackEdit(opts)) {\n throw new CliError(\n 'Nothing to edit: pass --name, --language, --default/--no-default, or --forced/--no-forced.'\n );\n }\n requireTrack(await probeTracks(deps.runner, opts.file), opts.track);\n }\n const result = await deps.runner.run(buildEditArgs(opts));\n if (result.exitCode >= 2) {\n throw new CliError(\n `mkvpropedit failed (exit ${result.exitCode}):\\n${result.stderr || result.stdout}`\n );\n }\n if (result.exitCode === 1) {\n const output = [result.stdout, result.stderr]\n .map((stream) => stream.trim())\n .filter(Boolean)\n .join('\\n');\n return `mkvpropedit finished with warnings — check the file in a player:\\n${output}`;\n }\n return undefined;\n}\n","import type { CommandDeps } from '../types.ts';\n\nimport { defaultExtractOutput, ensureWritable, requireInputFile } from '../output.ts';\nimport { toolPath } from '../paths.ts';\nimport { probeTracks, requireAudioTrack } from '../probe.ts';\nimport { CliError } from '../types.ts';\n\nexport function buildExtractArgs(opts: { file: string; track: number; output: string }): string[] {\n return [\n 'mkvmerge',\n '-o',\n toolPath(opts.output),\n '--audio-tracks',\n String(opts.track),\n '--no-video',\n '--no-subtitles',\n '--no-buttons',\n '--no-chapters',\n '--no-attachments',\n '--no-global-tags',\n toolPath(opts.file)\n ];\n}\n\nexport async function extractCommand(\n opts: { file: string; track: number; output?: string; force: boolean },\n deps: CommandDeps\n): Promise<string> {\n requireInputFile(opts.file, deps.exists);\n const tracks = await probeTracks(deps.runner, opts.file);\n requireAudioTrack(tracks, opts.track);\n const output = opts.output ?? defaultExtractOutput(opts.file, opts.track);\n await ensureWritable(output, {\n force: opts.force,\n isTTY: deps.isTTY,\n confirm: deps.confirm,\n exists: deps.exists,\n inputs: [opts.file]\n });\n const result = await deps.runner.run(\n buildExtractArgs({ file: opts.file, track: opts.track, output }),\n deps.stderrIsTTY ? { stream: 'stdout' } : undefined\n );\n if (result.exitCode >= 2) {\n throw new CliError(\n `mkvmerge failed (exit ${result.exitCode}):\\n${result.stderr || result.stdout}`\n );\n }\n return output;\n}\n","import type { CommandDeps, Track } from '../types.ts';\n\nimport { requireInputFile } from '../output.ts';\nimport { probeFile } from '../probe.ts';\n\nexport function formatTable(header: readonly string[], rows: readonly string[][]): string {\n const all = [header, ...rows];\n const widths = header.map((_, column) => Math.max(...all.map((row) => row[column]!.length)));\n return all\n .map((row) =>\n row\n .map((cell, column) => cell.padEnd(widths[column]!))\n .join(' ')\n .trimEnd()\n )\n .join('\\n');\n}\n\nexport function formatTrackTable(tracks: Track[]): string {\n return formatTable(\n ['ID', 'TYPE', 'CODEC', 'LANG', 'CH', 'DEFAULT', 'FORCED', 'NAME'],\n tracks.map((track) => [\n String(track.id),\n track.type,\n track.codec,\n track.language ?? '-',\n track.channels !== undefined ? String(track.channels) : '-',\n track.isDefault ? 'yes' : '-',\n track.isForced ? 'yes' : '-',\n track.name ?? '-'\n ])\n );\n}\n\nexport async function inspectCommand(file: string, deps: CommandDeps): Promise<string> {\n requireInputFile(file, deps.exists);\n const { title, tracks } = await probeFile(deps.runner, file);\n const table = formatTrackTable(tracks);\n return title === undefined ? table : `Title: ${title}\\n\\n${table}`;\n}\n","import { existsSync } from 'node:fs';\n\nimport type { CommandDeps, Track } from '../types.ts';\n\nimport { findAliasedInput, requireInputFile } from '../output.ts';\nimport { toolPath } from '../paths.ts';\nimport { probeTracks, requireAudioTrack } from '../probe.ts';\nimport { CliError } from '../types.ts';\nimport { formatTable } from './inspect.ts';\n\n/** One `--audio` block from the CLI: a source file plus its per-track options. */\nexport interface MuxTrackRequest {\n audio: string;\n track?: number;\n delayMs: number;\n language?: string;\n name?: string;\n makeDefault: boolean;\n}\n\nexport interface MuxOptions {\n video: string;\n tracks: readonly MuxTrackRequest[];\n output: string;\n force: boolean;\n dryRun: boolean;\n}\n\n/** A request whose track id has been resolved against the probed source. */\nexport interface ResolvedMuxTrack {\n audio: string;\n trackId: number;\n delayMs: number;\n language?: string;\n trackName?: string;\n makeDefault: boolean;\n}\n\nexport type MuxResult = { kind: 'dry-run'; plan: string } | { kind: 'written'; output: string };\n\n/** Labels one `--audio` group by position and path, e.g. `--audio #2 (\"donor.mka\")`. */\nexport function describeAudioGroup(index: number, audio: string): string {\n return `--audio #${index + 1} (\"${audio}\")`;\n}\n\nexport function resolveAudioTrackId(tracks: Track[], requested: number | undefined): number {\n if (requested !== undefined) return requireAudioTrack(tracks, requested).id;\n const audio = tracks.filter((track) => track.type === 'audio');\n if (audio.length === 1) return audio[0]!.id;\n if (audio.length === 0) throw new CliError('The audio input contains no audio tracks.');\n throw new CliError(\n `The audio input contains ${audio.length} audio tracks; pass --track <id> (see: tracksmith inspect <file>).`\n );\n}\n\nexport function buildMuxArgs(a: {\n video: string;\n videoTrackIds: readonly number[];\n clearDefaultAudioIds: readonly number[];\n groups: readonly ResolvedMuxTrack[];\n output: string;\n}): string[] {\n const trackOrder = [\n ...a.videoTrackIds.map((trackId) => `0:${trackId}`),\n ...a.groups.map((group, index) => `${index + 1}:${group.trackId}`)\n ].join(',');\n const args = [\n 'mkvmerge',\n '--command-line-charset',\n 'UTF-8',\n '-o',\n toolPath(a.output),\n '--track-order',\n trackOrder\n ];\n for (const id of a.clearDefaultAudioIds) args.push('--default-track-flag', `${id}:no`);\n args.push(toolPath(a.video));\n for (const group of a.groups) {\n const id = String(group.trackId);\n args.push(\n '--audio-tracks',\n id,\n '--no-video',\n '--no-subtitles',\n '--no-buttons',\n '--no-chapters',\n '--no-attachments',\n '--no-global-tags'\n );\n if (group.delayMs !== 0) args.push('--sync', `${id}:${group.delayMs}`);\n if (group.language !== undefined) args.push('--language', `${id}:${group.language}`);\n if (group.trackName !== undefined) args.push('--track-name', `${id}:${group.trackName}`);\n args.push('--default-track-flag', `${id}:${group.makeDefault ? 'yes' : 'no'}`);\n args.push(toolPath(group.audio));\n }\n return args;\n}\n\nfunction buildPlan(a: {\n video: string;\n videoTracks: readonly Track[];\n groups: readonly ResolvedMuxTrack[];\n sourceTracks: ReadonlyMap<string, Track[]>;\n clearDefaultAudioIds: readonly number[];\n output: string;\n}): string {\n const cleared = new Set(a.clearDefaultAudioIds);\n const existingRows = a.videoTracks.map((track) => [\n String(track.id),\n track.type,\n track.codec,\n track.language ?? '-',\n track.channels !== undefined ? String(track.channels) : '-',\n track.isDefault && !cleared.has(track.id) ? 'yes' : '-',\n '-',\n track.name ?? '-',\n a.video\n ]);\n const newRows = a.groups.map((group, index) => {\n const donor = a.sourceTracks.get(group.audio)?.find((track) => track.id === group.trackId);\n return [\n String(a.videoTracks.length + index),\n 'audio',\n donor?.codec ?? '-',\n group.language ?? donor?.language ?? '-',\n donor?.channels !== undefined ? String(donor.channels) : '-',\n group.makeDefault ? 'yes' : '-',\n group.delayMs !== 0 ? `${group.delayMs}ms` : '-',\n group.trackName ?? donor?.name ?? '-',\n group.audio\n ];\n });\n const table = formatTable(\n ['ID', 'TYPE', 'CODEC', 'LANG', 'CH', 'DEFAULT', 'DELAY', 'NAME', 'SOURCE'],\n [...existingRows, ...newRows]\n );\n return `Planned tracks for \"${a.output}\":\\n${table}`;\n}\n\nexport async function muxCommand(opts: MuxOptions, deps: CommandDeps): Promise<MuxResult> {\n requireInputFile(opts.video, deps.exists);\n const uniqueAudio = [...new Set(opts.tracks.map((track) => track.audio))];\n for (const audio of uniqueAudio) requireInputFile(audio, deps.exists);\n\n const videoTracks = await probeTracks(deps.runner, opts.video);\n const sourceTracks = new Map(\n await Promise.all(\n uniqueAudio.map(async (audio): Promise<[string, Track[]]> => [\n audio,\n await probeTracks(deps.runner, audio)\n ])\n )\n );\n\n const groups: ResolvedMuxTrack[] = opts.tracks.map((track, index) => {\n let trackId: number;\n try {\n trackId = resolveAudioTrackId(sourceTracks.get(track.audio)!, track.track);\n } catch (error) {\n if (!(error instanceof CliError)) throw error;\n throw new CliError(`${describeAudioGroup(index, track.audio)}: ${error.message}`);\n }\n return {\n audio: track.audio,\n trackId,\n delayMs: track.delayMs,\n language: track.language,\n trackName: track.name,\n makeDefault: track.makeDefault\n };\n });\n if (groups.filter((group) => group.makeDefault).length > 1) {\n throw new CliError('--default can be set on at most one --audio input.');\n }\n\n const aliasedInput = findAliasedInput(opts.output, [opts.video, ...uniqueAudio]);\n if (aliasedInput !== undefined) {\n throw new CliError(\n `Output \"${opts.output}\" is the same file as input \"${aliasedInput}\". Choose a different output path.`\n );\n }\n\n const clearDefaultAudioIds = groups.some((group) => group.makeDefault)\n ? videoTracks\n .filter((track) => track.type === 'audio' && track.isDefault)\n .map((track) => track.id)\n : [];\n const plan = buildPlan({\n video: opts.video,\n videoTracks,\n groups,\n sourceTracks,\n clearDefaultAudioIds,\n output: opts.output\n });\n if (opts.dryRun) return { kind: 'dry-run', plan };\n\n const exists = deps.exists ?? existsSync;\n const outputExists = exists(opts.output);\n if (!opts.force) {\n if (deps.isTTY) {\n const overwrite = outputExists\n ? `\\nOutput \"${opts.output}\" already exists and will be overwritten.`\n : '';\n const approved = await deps.confirm(`${plan}${overwrite}\\nWrite \"${opts.output}\"?`);\n if (!approved) throw new CliError('Aborted: nothing written.');\n } else if (outputExists) {\n throw new CliError(\n `Output \"${opts.output}\" already exists. Pass --force to overwrite (cannot prompt in a non-interactive session).`\n );\n }\n }\n\n const result = await deps.runner.run(\n buildMuxArgs({\n video: opts.video,\n videoTrackIds: videoTracks.map((track) => track.id),\n clearDefaultAudioIds,\n groups,\n output: opts.output\n }),\n deps.stderrIsTTY ? { stream: 'stdout' } : undefined\n );\n if (result.exitCode >= 2) {\n throw new CliError(\n `mkvmerge failed (exit ${result.exitCode}):\\n${result.stderr || result.stdout}`\n );\n }\n return { kind: 'written', output: opts.output };\n}\n","import type { CommandDeps } from '../types.ts';\n\nimport { defaultTestClipOutput, ensureWritable, requireInputFile } from '../output.ts';\nimport { toolPath } from '../paths.ts';\nimport { audioRelativeIndex, probeTracks, requireAudioTrack } from '../probe.ts';\nimport { CliError } from '../types.ts';\n\nexport function parseTimeToSeconds(value: string): number {\n if (/^\\d+(\\.\\d+)?$/.test(value)) return Number(value);\n const match = /^(\\d+):([0-5]?\\d):([0-5]?\\d(?:\\.\\d+)?)$/.exec(value);\n if (!match)\n throw new CliError(`Invalid time \"${value}\". Use seconds (90 or 90.5) or HH:MM:SS[.ms].`);\n return Number(match[1]) * 3600 + Number(match[2]) * 60 + Number(match[3]);\n}\n\nexport function buildTestClipArgs(a: {\n video: string;\n audio: string;\n audioStreamIndex: number;\n startSeconds: number;\n durationSeconds: number;\n delayMs: number;\n output: string;\n}): string[] {\n // -y is safe: our own overwrite policy (ensureWritable) has already run.\n return [\n 'ffmpeg',\n '-hide_banner',\n '-nostdin',\n '-ss',\n String(a.startSeconds),\n '-i',\n toolPath(a.video),\n '-itsoffset',\n String(a.delayMs / 1000),\n '-ss',\n String(a.startSeconds),\n '-i',\n toolPath(a.audio),\n '-map',\n '0:v:0',\n '-map',\n `1:a:${a.audioStreamIndex}`,\n '-t',\n String(a.durationSeconds),\n '-c:v',\n 'libx264',\n '-preset',\n 'veryfast',\n '-crf',\n '18',\n '-c:a',\n 'copy',\n '-sn',\n '-abort_on',\n 'empty_output_stream',\n '-y',\n toolPath(a.output)\n ];\n}\n\nexport async function testClipCommand(\n opts: {\n video: string;\n audio: string;\n track: number;\n start: string;\n duration: string;\n delayMs: number;\n output?: string;\n force: boolean;\n },\n deps: CommandDeps\n): Promise<string> {\n requireInputFile(opts.video, deps.exists);\n requireInputFile(opts.audio, deps.exists);\n const startSeconds = parseTimeToSeconds(opts.start);\n const durationSeconds = parseTimeToSeconds(opts.duration);\n if (durationSeconds <= 0) throw new CliError('--duration must be greater than zero.');\n const donorTracks = await probeTracks(deps.runner, opts.audio);\n requireAudioTrack(donorTracks, opts.track);\n const output = opts.output ?? defaultTestClipOutput(opts.video);\n await ensureWritable(output, {\n force: opts.force,\n isTTY: deps.isTTY,\n confirm: deps.confirm,\n exists: deps.exists,\n inputs: [opts.video, opts.audio]\n });\n const result = await deps.runner.run(\n buildTestClipArgs({\n video: opts.video,\n audio: opts.audio,\n audioStreamIndex: audioRelativeIndex(donorTracks, opts.track),\n startSeconds,\n durationSeconds,\n delayMs: opts.delayMs,\n output\n }),\n deps.stderrIsTTY ? { stream: 'stderr' } : undefined\n );\n if (result.exitCode !== 0) {\n throw new CliError(\n `ffmpeg failed (exit ${result.exitCode}):\\n${result.stderr || result.stdout}`\n );\n }\n return output;\n}\n","import { parseArgs } from 'node:util';\n\nimport type { CommandDeps } from './types.ts';\n\nimport { editCommand } from './commands/edit.ts';\nimport { extractCommand } from './commands/extract.ts';\nimport { inspectCommand } from './commands/inspect.ts';\nimport { describeAudioGroup, muxCommand } from './commands/mux.ts';\nimport { testClipCommand } from './commands/test-clip.ts';\nimport { CliError } from './types.ts';\n\nconst USAGE = `tracksmith — inspect, edit, extract, sync-test, and mux Matroska audio tracks\n\nUsage:\n tracksmith inspect <file>\n tracksmith extract <file> --track <id> [--output <file>] [--force]\n tracksmith edit <file> --track <id> [--name <text>] [--language <lang>]\n [--default | --no-default] [--forced | --no-forced]\n tracksmith edit <file> --title <text>\n tracksmith test-clip --video <target> --audio <donor> --track <id> --start <time>\n [--duration 60] [--delay-ms 0] [--output <file>] [--force]\n tracksmith mux --video <target> --output <file> [--dry-run] [--force]\n --audio <mka-or-mkv> [--track <id>] [--delay-ms 0] [--language eng] [--name <title>] [--default]\n [--audio <next> ...]\n\nNotes:\n Repeat --audio to add several tracks; per-track flags apply to the most recent --audio.\n --default may be set on at most one --audio; it also clears the target's existing default audio flags.\n --dry-run prints the planned track layout without writing.\n --track is always the MKVToolNix track ID shown by \"tracksmith inspect\".\n edit changes metadata in place without remuxing. --name \"\" and --title \"\" clear the value\n (in Windows PowerShell use --name= and --title=; empty \"\" arguments get dropped).\n --title sets the file title and cannot be combined with --track edits.\n Positive --delay-ms delays audio; negative advances it (e.g. --delay-ms -250).\n Times accept seconds (90, 90.5) or HH:MM:SS[.ms].\n Check sync near the beginning, middle, and end; a changing offset cannot be fixed by one delay.\n Requires ffmpeg and MKVToolNix (mkvmerge, mkvpropedit) on PATH.`;\n\nfunction parseIntStrict(value: string | undefined, flag: string): number {\n if (value === undefined) throw new CliError(`${flag} is required.`);\n if (!/^-?\\d+$/.test(value)) throw new CliError(`${flag} must be an integer, got \"${value}\".`);\n return Number(value);\n}\n\nfunction parseTrack(value: string | undefined): number {\n const track = parseIntStrict(value, '--track');\n if (track < 0) throw new CliError(`--track must be >= 0, got ${track}.`);\n return track;\n}\n\nfunction requireFilePositional(\n positionals: string[],\n command: 'inspect' | 'extract' | 'edit'\n): string {\n if (positionals.length === 0) throw new CliError(`${command} requires a file argument.`);\n if (positionals.length > 1) throw new CliError(`${command} accepts exactly one file argument.`);\n return positionals[0]!;\n}\n\n// Wraps a parseArgs call so parse failures become CliErrors. Takes a thunk\n// (rather than the options object) so parseArgs keeps its precise generic\n// inference over each command's inline options literal.\nfunction parseOrCliError<T>(parseFn: () => T): T {\n try {\n return parseFn();\n } catch (error) {\n throw new CliError(error instanceof Error ? error.message : String(error));\n }\n}\n\n// parseArgs treats a leading-dash value token (\"-250\") as an option, so fold\n// `--delay-ms -250` into the equals form it does accept.\nfunction normalizeDelayMs(args: string[]): string[] {\n const out: string[] = [];\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i]!;\n const next = args[i + 1];\n if (arg === '--delay-ms' && next !== undefined && /^-\\d+$/.test(next)) {\n out.push(`--delay-ms=${next}`);\n i += 1;\n } else {\n out.push(arg);\n }\n }\n return out;\n}\n\nexport async function runCli(\n argv: string[],\n deps: CommandDeps,\n stdout: (line: string) => void = console.log\n): Promise<number> {\n const [command, ...rawRest] = argv;\n const rest = normalizeDelayMs(rawRest);\n try {\n switch (command) {\n case undefined: {\n stdout(USAGE);\n return 1;\n }\n case '--help':\n case '-h': {\n stdout(USAGE);\n return 0;\n }\n case 'inspect': {\n const { positionals } = parseOrCliError(() =>\n parseArgs({ args: rest, options: {}, allowPositionals: true, strict: true })\n );\n const file = requireFilePositional(positionals, 'inspect');\n stdout(await inspectCommand(file, deps));\n return 0;\n }\n case 'extract': {\n const { values, positionals } = parseOrCliError(() =>\n parseArgs({\n args: rest,\n options: {\n track: { type: 'string' },\n output: { type: 'string' },\n force: { type: 'boolean', default: false }\n },\n allowPositionals: true,\n strict: true\n })\n );\n const file = requireFilePositional(positionals, 'extract');\n const output = await extractCommand(\n {\n file,\n track: parseTrack(values.track),\n output: values.output,\n force: values.force ?? false\n },\n deps\n );\n stdout(`Wrote ${output}`);\n return 0;\n }\n case 'test-clip': {\n const { values } = parseOrCliError(() =>\n parseArgs({\n args: rest,\n options: {\n video: { type: 'string' },\n audio: { type: 'string' },\n track: { type: 'string' },\n start: { type: 'string' },\n duration: { type: 'string', default: '60' },\n 'delay-ms': { type: 'string', default: '0' },\n output: { type: 'string' },\n force: { type: 'boolean', default: false }\n },\n strict: true\n })\n );\n if (!values.video || !values.audio)\n throw new CliError('test-clip requires --video and --audio.');\n if (!values.start) throw new CliError('test-clip requires --start.');\n const output = await testClipCommand(\n {\n video: values.video,\n audio: values.audio,\n track: parseTrack(values.track),\n start: values.start,\n duration: values.duration ?? '60',\n delayMs: parseIntStrict(values['delay-ms'] ?? '0', '--delay-ms'),\n output: values.output,\n force: values.force ?? false\n },\n deps\n );\n stdout(`Wrote ${output}`);\n return 0;\n }\n case 'mux': {\n const { values, tokens } = parseOrCliError(() =>\n parseArgs({\n args: rest,\n options: {\n video: { type: 'string' },\n output: { type: 'string' },\n force: { type: 'boolean', default: false },\n 'dry-run': { type: 'boolean', default: false },\n audio: { type: 'string', multiple: true },\n track: { type: 'string', multiple: true },\n 'delay-ms': { type: 'string', multiple: true },\n language: { type: 'string', multiple: true },\n name: { type: 'string', multiple: true },\n default: { type: 'boolean', multiple: true }\n },\n strict: true,\n tokens: true\n })\n );\n const groups: {\n audio: string;\n track?: string;\n delayMs?: string;\n language?: string;\n name?: string;\n makeDefault?: true;\n }[] = [];\n for (const token of tokens) {\n if (token.kind !== 'option') continue;\n if (token.name === 'audio') {\n groups.push({ audio: token.value });\n continue;\n }\n if (\n token.name !== 'track' &&\n token.name !== 'delay-ms' &&\n token.name !== 'language' &&\n token.name !== 'name' &&\n token.name !== 'default'\n ) {\n continue;\n }\n const group = groups.at(-1);\n if (group === undefined) {\n throw new CliError(`--${token.name} must come after an --audio input.`);\n }\n switch (token.name) {\n case 'track':\n if (group.track !== undefined)\n throw new CliError(\n `Duplicate --track for ${describeAudioGroup(groups.length - 1, group.audio)}.`\n );\n group.track = token.value;\n break;\n case 'delay-ms':\n if (group.delayMs !== undefined)\n throw new CliError(\n `Duplicate --delay-ms for ${describeAudioGroup(groups.length - 1, group.audio)}.`\n );\n group.delayMs = token.value;\n break;\n case 'language':\n if (group.language !== undefined)\n throw new CliError(\n `Duplicate --language for ${describeAudioGroup(groups.length - 1, group.audio)}.`\n );\n group.language = token.value;\n break;\n case 'name':\n if (group.name !== undefined)\n throw new CliError(\n `Duplicate --name for ${describeAudioGroup(groups.length - 1, group.audio)}.`\n );\n group.name = token.value;\n break;\n case 'default':\n if (group.makeDefault !== undefined)\n throw new CliError(\n `Duplicate --default for ${describeAudioGroup(groups.length - 1, group.audio)}.`\n );\n group.makeDefault = true;\n break;\n }\n }\n if (!values.video || groups.length === 0)\n throw new CliError('mux requires --video and --audio.');\n if (!values.output)\n throw new CliError('mux requires --output (no default output name for the final file).');\n const result = await muxCommand(\n {\n video: values.video,\n tracks: groups.map((group) => ({\n audio: group.audio,\n track: group.track === undefined ? undefined : parseTrack(group.track),\n delayMs: parseIntStrict(group.delayMs ?? '0', '--delay-ms'),\n language: group.language ?? 'eng',\n name: group.name,\n makeDefault: group.makeDefault ?? false\n })),\n output: values.output,\n force: values.force ?? false,\n dryRun: values['dry-run'] ?? false\n },\n deps\n );\n if (result.kind === 'dry-run') stdout(result.plan);\n else stdout(`Wrote ${result.output}`);\n return 0;\n }\n case 'edit': {\n const { values, positionals } = parseOrCliError(() =>\n parseArgs({\n args: rest,\n options: {\n track: { type: 'string' },\n name: { type: 'string' },\n language: { type: 'string' },\n default: { type: 'boolean' },\n 'no-default': { type: 'boolean' },\n forced: { type: 'boolean' },\n 'no-forced': { type: 'boolean' },\n title: { type: 'string' }\n },\n allowPositionals: true,\n strict: true\n })\n );\n const file = requireFilePositional(positionals, 'edit');\n const hasTrackFlags =\n values.track !== undefined ||\n values.name !== undefined ||\n values.language !== undefined ||\n values.default !== undefined ||\n values['no-default'] !== undefined ||\n values.forced !== undefined ||\n values['no-forced'] !== undefined;\n if (values.title !== undefined && hasTrackFlags)\n throw new CliError(\n '--title cannot be combined with --track edits. Run them as separate invocations.'\n );\n if (values.default !== undefined && values['no-default'] !== undefined)\n throw new CliError('Pass either --default or --no-default, not both.');\n if (values.forced !== undefined && values['no-forced'] !== undefined)\n throw new CliError('Pass either --forced or --no-forced, not both.');\n if (values.language === '')\n throw new CliError('--language cannot be empty. Use --language und for undetermined.');\n let warning: string | undefined;\n if (values.title !== undefined) {\n warning = await editCommand({ kind: 'title', file, title: values.title }, deps);\n } else {\n if (values.track === undefined)\n throw new CliError('edit requires --track (or --title for the file title).');\n warning = await editCommand(\n {\n kind: 'track',\n file,\n track: parseTrack(values.track),\n name: values.name,\n language: values.language,\n isDefault: values.default ?? (values['no-default'] !== undefined ? false : undefined),\n isForced: values.forced ?? (values['no-forced'] !== undefined ? false : undefined)\n },\n deps\n );\n }\n if (warning !== undefined) console.error(`tracksmith: ${warning}`);\n stdout(`Edited ${file}`);\n return 0;\n }\n default:\n throw new CliError(`Unknown command \"${command}\".\\n\\n${USAGE}`);\n }\n } catch (error) {\n if (error instanceof CliError) {\n console.error(`tracksmith: ${error.message}`);\n return error.exitCode;\n }\n throw error;\n }\n}\n","import { once } from 'node:events';\nimport { createInterface } from 'node:readline/promises';\n\nimport type { Readable, Writable } from 'node:stream';\n\nexport interface ConfirmPromptOptions {\n input?: Readable;\n output?: Writable;\n}\n\nexport function createConfirmPrompt(\n options: ConfirmPromptOptions = {}\n): (message: string) => Promise<boolean> {\n const input = options.input ?? process.stdin;\n const output = options.output ?? process.stdout;\n\n return async (message: string): Promise<boolean> => {\n const readline = createInterface({ input, output });\n\n try {\n const closed = once(readline, 'close').then(() => ({ kind: 'eof' }) as const);\n const answered = readline.question(`${message} [y/N] `).then(\n (value) => ({ kind: 'answer', value }) as const,\n () => ({ kind: 'eof' }) as const\n );\n const result = await Promise.race([answered, closed]);\n\n return result.kind === 'answer' && /^y(?:es)?$/i.test(result.value.trim());\n } finally {\n readline.close();\n }\n };\n}\n","export const TOOLS = ['ffmpeg', 'mkvmerge', 'mkvpropedit'] as const;\n\nexport type ToolName = (typeof TOOLS)[number];\n\nexport function isToolName(value: string): value is ToolName {\n return TOOLS.some((tool) => tool === value);\n}\n\nconst MKVTOOLNIX_HINT =\n 'Windows: winget install MoritzBunkus.MKVToolNix or scoop install mkvtoolnix | macOS: brew install mkvtoolnix | Linux: sudo apt install mkvtoolnix or sudo pacman -S mkvtoolnix-cli';\n\nconst INSTALL_HINTS = {\n ffmpeg:\n 'Windows: winget install Gyan.FFmpeg or scoop install ffmpeg | macOS: brew install ffmpeg | Linux: sudo apt install ffmpeg or sudo pacman -S ffmpeg',\n mkvmerge: MKVTOOLNIX_HINT,\n mkvpropedit: MKVTOOLNIX_HINT\n} satisfies Record<ToolName, string>;\n\nexport function installHint(tool: ToolName): string {\n return INSTALL_HINTS[tool];\n}\n","import { spawn } from 'node:child_process';\n\nimport type { RunOptions, Runner, RunResult } from './types.ts';\n\nimport { installHint, isToolName } from './tools.ts';\nimport { CliError } from './types.ts';\n\nexport function processStartError(program: string, error: unknown): CliError {\n if (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n error.code === 'ENOENT' &&\n isToolName(program)\n ) {\n return new CliError(\n `${program} is required but was not found on PATH. ${installHint(program)}`\n );\n }\n\n const message = error instanceof Error ? error.message : String(error);\n return new CliError(`Failed to start ${program}: ${message}`);\n}\n\nconst writeToCliStderr = (chunk: string): void => {\n globalThis.process.stderr.write(chunk);\n};\n\nexport class ProcessRunner implements Runner {\n private readonly sink: (chunk: string) => void;\n\n constructor(options: { sink?: (chunk: string) => void } = {}) {\n this.sink = options.sink ?? writeToCliStderr;\n }\n\n async run(argv: readonly string[], options?: RunOptions): Promise<RunResult> {\n const [executable, ...args] = argv;\n if (executable === undefined) {\n throw new CliError('Could not start process: missing executable');\n }\n\n return await new Promise((resolve, reject) => {\n const process = spawn(executable, args, {\n stdio: ['ignore', 'pipe', 'pipe']\n });\n let stdout = '';\n let stderr = '';\n\n process.stdout.setEncoding('utf8');\n process.stderr.setEncoding('utf8');\n process.stdout.on('data', (chunk: string) => {\n stdout += chunk;\n if (options?.stream === 'stdout') this.sink(chunk);\n });\n process.stderr.on('data', (chunk: string) => {\n stderr += chunk;\n if (options?.stream === 'stderr') this.sink(chunk);\n });\n process.once('error', (error) => {\n reject(processStartError(executable, error));\n });\n // Windows loses the signal: a killed child reports a plain numeric exit\n // code (usually 1) with signal null, so this detection is POSIX-only and\n // Windows cannot distinguish a killed tool from a warning exit.\n process.once('close', (exitCode, signal) => {\n if (signal !== null) {\n const output = [stdout, stderr]\n .map((stream) => stream.trim())\n .filter(Boolean)\n .join('\\n');\n const diagnostics = output === '' ? '' : `\\nOutput before termination:\\n${output}`;\n reject(new CliError(`${executable} was terminated by signal ${signal}.${diagnostics}`));\n return;\n }\n resolve({ exitCode: exitCode ?? 1, stdout, stderr });\n });\n });\n }\n}\n","#!/usr/bin/env node\n\nimport { runCli } from './cli.ts';\nimport { createConfirmPrompt } from './prompt.ts';\nimport { ProcessRunner } from './runner.ts';\n\nprocess.exitCode = await runCli(process.argv.slice(2), {\n runner: new ProcessRunner(),\n isTTY: process.stdin.isTTY ?? false,\n stderrIsTTY: process.stderr.isTTY ?? false,\n confirm: createConfirmPrompt({ output: process.stderr })\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"tracksmith.js","names":[],"sources":["../src/types.ts","../src/output.ts","../src/paths.ts","../src/probe.ts","../src/commands/edit.ts","../src/commands/extract.ts","../src/commands/inspect.ts","../src/commands/mux.ts","../src/commands/test-clip.ts","../src/cli.ts","../src/prompt.ts","../src/tools.ts","../src/runner.ts","../src/tracksmith.ts"],"sourcesContent":["export interface RunResult {\n exitCode: number;\n stdout: string;\n stderr: string;\n}\n\nexport interface RunOptions {\n stream?: 'stdout' | 'stderr';\n}\n\nexport interface Runner {\n run(argv: readonly string[], options?: RunOptions): Promise<RunResult>;\n}\n\nexport interface Track {\n id: number;\n type: string;\n codec: string;\n language?: string;\n name?: string;\n channels?: number;\n isDefault: boolean;\n isForced: boolean;\n}\n\nexport class CliError extends Error {\n constructor(\n message: string,\n readonly exitCode: number = 1\n ) {\n super(message);\n this.name = 'CliError';\n }\n}\n\nexport interface CommandDeps {\n runner: Runner;\n /** stdin is interactive — gates confirmation prompts. */\n isTTY: boolean;\n /** stderr is interactive — gates live tool progress output. */\n stderrIsTTY: boolean;\n confirm: (message: string) => Promise<boolean>;\n exists?: (path: string) => boolean;\n}\n","import { existsSync, statSync } from 'node:fs';\nimport { basename, dirname, extname, join } from 'node:path';\n\nimport { CliError } from './types.ts';\n\nexport function stemPath(file: string): string {\n return join(dirname(file), basename(file, extname(file)));\n}\n\nexport function defaultExtractOutput(source: string, trackId: number): string {\n return `${stemPath(source)}.track${trackId}.mka`;\n}\n\nexport function defaultTestClipOutput(video: string): string {\n return `${stemPath(video)}.sync-test.mkv`;\n}\n\nexport function requireInputFile(\n path: string,\n exists: (path: string) => boolean = existsSync\n): void {\n if (!exists(path)) throw new CliError(`Input file not found: \"${path}\".`);\n}\n\nexport interface OverwriteContext {\n force: boolean;\n isTTY: boolean;\n confirm: (message: string) => Promise<boolean>;\n exists?: (path: string) => boolean;\n inputs?: readonly string[];\n}\n\nfunction sameFile(left: string, right: string): boolean {\n try {\n const leftStat = statSync(left, { bigint: true });\n const rightStat = statSync(right, { bigint: true });\n return leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;\n } catch {\n return false;\n }\n}\n\n/** Returns the input path the output aliases on disk (same dev/inode), if any. */\nexport function findAliasedInput(output: string, inputs: readonly string[]): string | undefined {\n return inputs.find((input) => sameFile(output, input));\n}\n\nexport async function ensureWritable(path: string, ctx: OverwriteContext): Promise<void> {\n const aliasedInput = findAliasedInput(path, ctx.inputs ?? []);\n if (aliasedInput !== undefined) {\n throw new CliError(\n `Output \"${path}\" is the same file as input \"${aliasedInput}\". Choose a different output path.`\n );\n }\n\n const exists = ctx.exists ?? existsSync;\n if (!exists(path)) return;\n if (ctx.force) return;\n if (!ctx.isTTY) {\n throw new CliError(\n `Output \"${path}\" already exists. Pass --force to overwrite (cannot prompt in a non-interactive session).`\n );\n }\n const approved = await ctx.confirm(`Output \"${path}\" already exists. Overwrite?`);\n if (!approved) throw new CliError('Aborted: existing output left untouched.');\n}\n","import { isAbsolute, resolve } from 'node:path';\n\nconst TOOL_OPERATOR_PREFIXES = '-+@=()[]';\n\nexport function toolPath(path: string): string {\n if (isAbsolute(path) || !TOOL_OPERATOR_PREFIXES.includes(path[0] ?? '')) return path;\n return resolve(path);\n}\n","import type { Runner, Track } from './types.ts';\n\nimport * as z from 'zod/mini';\n\nimport { toolPath } from './paths.ts';\nimport { CliError } from './types.ts';\n\nconst mkvmergePropertiesSchema = z.object({\n language: z.optional(z.string()),\n language_ietf: z.optional(z.string()),\n track_name: z.optional(z.string()),\n audio_channels: z.optional(z.number()),\n default_track: z.optional(z.boolean()),\n forced_track: z.optional(z.boolean())\n});\n\nconst mkvmergeTrackSchema = z.object({\n id: z.int(),\n type: z.string(),\n codec: z.string(),\n properties: z.optional(mkvmergePropertiesSchema)\n});\n\nconst mkvmergeContainerSchema = z.object({\n properties: z.optional(z.object({ title: z.optional(z.string()) }))\n});\n\nconst mkvmergeOutputSchema = z.object({\n // Container metadata is ancillary: an unexpected shape must not fail track\n // parsing, so it is validated separately and dropped on mismatch.\n container: z.optional(z.unknown()),\n tracks: z.array(mkvmergeTrackSchema)\n});\n\nfunction containerTitle(container: unknown): string | undefined {\n const result = z.safeParse(mkvmergeContainerSchema, container);\n return result.success ? result.data.properties?.title : undefined;\n}\n\nexport interface MkvFile {\n title?: string;\n tracks: Track[];\n}\n\nexport function parseMkvmergeFile(json: string): MkvFile {\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch {\n throw new CliError('Could not parse mkvmerge -J output as JSON.');\n }\n const result = z.safeParse(mkvmergeOutputSchema, parsed);\n if (!result.success) throw new CliError('mkvmerge -J output has an unexpected structure.');\n\n return {\n title: containerTitle(result.data.container),\n tracks: result.data.tracks.map((track) => ({\n id: track.id,\n type: track.type,\n codec: track.codec,\n language: track.properties?.language_ietf || track.properties?.language || undefined,\n name: track.properties?.track_name,\n channels: track.properties?.audio_channels,\n isDefault: track.properties?.default_track ?? false,\n isForced: track.properties?.forced_track ?? false\n }))\n };\n}\n\nexport function parseMkvmergeJson(json: string): Track[] {\n return parseMkvmergeFile(json).tracks;\n}\n\nexport async function probeFile(runner: Runner, file: string): Promise<MkvFile> {\n const result = await runner.run(['mkvmerge', '-J', toolPath(file)]);\n if (result.exitCode >= 2) {\n throw new CliError(\n `mkvmerge could not read \"${file}\" (exit ${result.exitCode}):\\n${result.stderr || result.stdout}`\n );\n }\n return parseMkvmergeFile(result.stdout);\n}\n\nexport async function probeTracks(runner: Runner, file: string): Promise<Track[]> {\n return (await probeFile(runner, file)).tracks;\n}\n\nfunction validAudioIds(tracks: Track[]): string {\n const ids = tracks.filter((track) => track.type === 'audio').map((track) => track.id);\n return `Valid audio track IDs: ${ids.length > 0 ? ids.join(', ') : 'none'}`;\n}\n\nexport function requireAudioTrack(tracks: Track[], id: number): Track {\n const track = tracks.find((candidate) => candidate.id === id);\n if (!track) throw new CliError(`Track ${id} does not exist. ${validAudioIds(tracks)}`);\n if (track.type !== 'audio')\n throw new CliError(`Track ${id} is ${track.type}, not audio. ${validAudioIds(tracks)}`);\n return track;\n}\n\nexport function audioRelativeIndex(tracks: Track[], id: number): number {\n const index = tracks\n .filter((track) => track.type === 'audio')\n .findIndex((track) => track.id === id);\n if (index === -1)\n throw new CliError(`Track ${id} is not an audio track. ${validAudioIds(tracks)}`);\n return index;\n}\n\nexport function requireTrack(tracks: Track[], id: number): Track {\n const track = tracks.find((candidate) => candidate.id === id);\n if (!track) {\n const ids = tracks.map((candidate) => candidate.id);\n throw new CliError(\n `Track ${id} does not exist. Valid track IDs: ${ids.length > 0 ? ids.join(', ') : 'none'}`\n );\n }\n return track;\n}\n","import type { CommandDeps } from '../types.ts';\n\nimport { requireInputFile } from '../output.ts';\nimport { toolPath } from '../paths.ts';\nimport { probeTracks, requireTrack } from '../probe.ts';\nimport { CliError } from '../types.ts';\n\nexport type EditOpts =\n | { kind: 'title'; file: string; title: string }\n | {\n kind: 'track';\n file: string;\n track: number;\n name?: string;\n language?: string;\n isDefault?: boolean;\n isForced?: boolean;\n };\n\ntype TrackEditOpts = Extract<EditOpts, { kind: 'track' }>;\n\nfunction hasTrackEdit(opts: TrackEditOpts): boolean {\n return (\n opts.name !== undefined ||\n opts.language !== undefined ||\n opts.isDefault !== undefined ||\n opts.isForced !== undefined\n );\n}\n\nexport function buildEditArgs(opts: EditOpts): string[] {\n // Node and Bun always pass argv as UTF-8 bytes; without this flag mkvpropedit\n // decodes them with the system locale and silently mangles non-ASCII values\n // (e.g. drops a Japanese title under LC_ALL=C) while still exiting 0.\n const args = ['mkvpropedit', '--command-line-charset', 'UTF-8', toolPath(opts.file)];\n switch (opts.kind) {\n case 'title': {\n args.push('--edit', 'info');\n if (opts.title === '') args.push('--delete', 'title');\n else args.push('--set', `title=${opts.title}`);\n return args;\n }\n case 'track': {\n // mkvpropedit's track:n selector is 1-based over the same track order\n // mkvmerge --identify reports, so the inspect ID maps to id + 1.\n args.push('--edit', `track:${opts.track + 1}`);\n if (opts.name === '') args.push('--delete', 'name');\n else if (opts.name !== undefined) args.push('--set', `name=${opts.name}`);\n if (opts.language !== undefined) args.push('--set', `language=${opts.language}`);\n if (opts.isDefault !== undefined)\n args.push('--set', `flag-default=${opts.isDefault ? '1' : '0'}`);\n if (opts.isForced !== undefined)\n args.push('--set', `flag-forced=${opts.isForced ? '1' : '0'}`);\n return args;\n }\n default:\n return opts satisfies never;\n }\n}\n\n/** Returns mkvpropedit's diagnostic output when it exited 1 (modified with warnings). */\nexport async function editCommand(opts: EditOpts, deps: CommandDeps): Promise<string | undefined> {\n requireInputFile(opts.file, deps.exists);\n if (opts.kind === 'track') {\n if (!hasTrackEdit(opts)) {\n throw new CliError(\n 'Nothing to edit: pass --name, --language, --default/--no-default, or --forced/--no-forced.'\n );\n }\n requireTrack(await probeTracks(deps.runner, opts.file), opts.track);\n }\n const result = await deps.runner.run(buildEditArgs(opts));\n if (result.exitCode >= 2) {\n throw new CliError(\n `mkvpropedit failed (exit ${result.exitCode}):\\n${result.stderr || result.stdout}`\n );\n }\n if (result.exitCode === 1) {\n const output = [result.stdout, result.stderr]\n .map((stream) => stream.trim())\n .filter(Boolean)\n .join('\\n');\n return `mkvpropedit finished with warnings — check the file in a player:\\n${output}`;\n }\n return undefined;\n}\n","import type { CommandDeps } from '../types.ts';\n\nimport { defaultExtractOutput, ensureWritable, requireInputFile } from '../output.ts';\nimport { toolPath } from '../paths.ts';\nimport { probeTracks, requireAudioTrack } from '../probe.ts';\nimport { CliError } from '../types.ts';\n\nexport function buildExtractArgs(opts: { file: string; track: number; output: string }): string[] {\n return [\n 'mkvmerge',\n '-o',\n toolPath(opts.output),\n '--audio-tracks',\n String(opts.track),\n '--no-video',\n '--no-subtitles',\n '--no-buttons',\n '--no-chapters',\n '--no-attachments',\n '--no-global-tags',\n toolPath(opts.file)\n ];\n}\n\nexport async function extractCommand(\n opts: { file: string; track: number; output?: string; force: boolean },\n deps: CommandDeps\n): Promise<string> {\n requireInputFile(opts.file, deps.exists);\n const tracks = await probeTracks(deps.runner, opts.file);\n requireAudioTrack(tracks, opts.track);\n const output = opts.output ?? defaultExtractOutput(opts.file, opts.track);\n await ensureWritable(output, {\n force: opts.force,\n isTTY: deps.isTTY,\n confirm: deps.confirm,\n exists: deps.exists,\n inputs: [opts.file]\n });\n const result = await deps.runner.run(\n buildExtractArgs({ file: opts.file, track: opts.track, output }),\n deps.stderrIsTTY ? { stream: 'stdout' } : undefined\n );\n if (result.exitCode >= 2) {\n throw new CliError(\n `mkvmerge failed (exit ${result.exitCode}):\\n${result.stderr || result.stdout}`\n );\n }\n return output;\n}\n","import type { CommandDeps, Track } from '../types.ts';\n\nimport { requireInputFile } from '../output.ts';\nimport { probeFile } from '../probe.ts';\n\nexport function formatTable(header: readonly string[], rows: readonly string[][]): string {\n const all = [header, ...rows];\n const widths = header.map((_, column) => Math.max(...all.map((row) => row[column]!.length)));\n return all\n .map((row) =>\n row\n .map((cell, column) => cell.padEnd(widths[column]!))\n .join(' ')\n .trimEnd()\n )\n .join('\\n');\n}\n\nexport function formatTrackTable(tracks: Track[]): string {\n return formatTable(\n ['ID', 'TYPE', 'CODEC', 'LANG', 'CH', 'DEFAULT', 'FORCED', 'NAME'],\n tracks.map((track) => [\n String(track.id),\n track.type,\n track.codec,\n track.language ?? '-',\n track.channels !== undefined ? String(track.channels) : '-',\n track.isDefault ? 'yes' : '-',\n track.isForced ? 'yes' : '-',\n track.name ?? '-'\n ])\n );\n}\n\nexport async function inspectCommand(file: string, deps: CommandDeps): Promise<string> {\n requireInputFile(file, deps.exists);\n const { title, tracks } = await probeFile(deps.runner, file);\n const table = formatTrackTable(tracks);\n return title === undefined ? table : `Title: ${title}\\n\\n${table}`;\n}\n","import { existsSync } from 'node:fs';\n\nimport type { CommandDeps, Track } from '../types.ts';\n\nimport { findAliasedInput, requireInputFile } from '../output.ts';\nimport { toolPath } from '../paths.ts';\nimport { probeTracks, requireAudioTrack } from '../probe.ts';\nimport { CliError } from '../types.ts';\nimport { formatTable } from './inspect.ts';\n\n/** One `--audio` block from the CLI: a source file plus its per-track options. */\nexport interface MuxTrackRequest {\n audio: string;\n track?: number;\n delayMs: number;\n language?: string;\n name?: string;\n makeDefault: boolean;\n}\n\nexport interface MuxOptions {\n video: string;\n tracks: readonly MuxTrackRequest[];\n output: string;\n force: boolean;\n dryRun: boolean;\n}\n\n/** A request whose track id has been resolved against the probed source. */\nexport interface ResolvedMuxTrack {\n audio: string;\n trackId: number;\n delayMs: number;\n language?: string;\n trackName?: string;\n makeDefault: boolean;\n}\n\nexport type MuxResult = { kind: 'dry-run'; plan: string } | { kind: 'written'; output: string };\n\n/** Labels one `--audio` group by position and path, e.g. `--audio #2 (\"donor.mka\")`. */\nexport function describeAudioGroup(index: number, audio: string): string {\n return `--audio #${index + 1} (\"${audio}\")`;\n}\n\nexport function resolveAudioTrackId(tracks: Track[], requested: number | undefined): number {\n if (requested !== undefined) return requireAudioTrack(tracks, requested).id;\n const audio = tracks.filter((track) => track.type === 'audio');\n if (audio.length === 1) return audio[0]!.id;\n if (audio.length === 0) throw new CliError('The audio input contains no audio tracks.');\n throw new CliError(\n `The audio input contains ${audio.length} audio tracks; pass --track <id> (see: tracksmith inspect <file>).`\n );\n}\n\nexport function buildMuxArgs(\n a: {\n video: string;\n videoTrackIds: readonly number[];\n clearDefaultAudioIds: readonly number[];\n groups: readonly ResolvedMuxTrack[];\n output: string;\n },\n platform: NodeJS.Platform = process.platform\n): string[] {\n const trackOrder = [\n ...a.videoTrackIds.map((trackId) => `0:${trackId}`),\n ...a.groups.map((group, index) => `${index + 1}:${group.trackId}`)\n ].join(',');\n // On POSIX mkvmerge decodes --track-name using the locale, so a non-UTF-8\n // locale silently mangles non-ASCII names unless we pin the charset. The\n // Windows build receives Unicode arguments natively and rejects this flag\n // (verified with mkvmerge v100: \"The file '--command-line-charset' could not\n // be opened\"), while mkvpropedit accepts it on both.\n const charset = platform === 'win32' ? [] : ['--command-line-charset', 'UTF-8'];\n const args = ['mkvmerge', ...charset, '-o', toolPath(a.output), '--track-order', trackOrder];\n for (const id of a.clearDefaultAudioIds) args.push('--default-track-flag', `${id}:no`);\n args.push(toolPath(a.video));\n for (const group of a.groups) {\n const id = String(group.trackId);\n args.push(\n '--audio-tracks',\n id,\n '--no-video',\n '--no-subtitles',\n '--no-buttons',\n '--no-chapters',\n '--no-attachments',\n '--no-global-tags'\n );\n if (group.delayMs !== 0) args.push('--sync', `${id}:${group.delayMs}`);\n if (group.language !== undefined) args.push('--language', `${id}:${group.language}`);\n if (group.trackName !== undefined) args.push('--track-name', `${id}:${group.trackName}`);\n args.push('--default-track-flag', `${id}:${group.makeDefault ? 'yes' : 'no'}`);\n args.push(toolPath(group.audio));\n }\n return args;\n}\n\nfunction buildPlan(a: {\n video: string;\n videoTracks: readonly Track[];\n groups: readonly ResolvedMuxTrack[];\n sourceTracks: ReadonlyMap<string, Track[]>;\n clearDefaultAudioIds: readonly number[];\n output: string;\n}): string {\n const cleared = new Set(a.clearDefaultAudioIds);\n const existingRows = a.videoTracks.map((track) => [\n String(track.id),\n track.type,\n track.codec,\n track.language ?? '-',\n track.channels !== undefined ? String(track.channels) : '-',\n track.isDefault && !cleared.has(track.id) ? 'yes' : '-',\n '-',\n track.name ?? '-',\n a.video\n ]);\n const newRows = a.groups.map((group, index) => {\n const donor = a.sourceTracks.get(group.audio)?.find((track) => track.id === group.trackId);\n return [\n String(a.videoTracks.length + index),\n 'audio',\n donor?.codec ?? '-',\n group.language ?? donor?.language ?? '-',\n donor?.channels !== undefined ? String(donor.channels) : '-',\n group.makeDefault ? 'yes' : '-',\n group.delayMs !== 0 ? `${group.delayMs}ms` : '-',\n group.trackName ?? donor?.name ?? '-',\n group.audio\n ];\n });\n const table = formatTable(\n ['ID', 'TYPE', 'CODEC', 'LANG', 'CH', 'DEFAULT', 'DELAY', 'NAME', 'SOURCE'],\n [...existingRows, ...newRows]\n );\n return `Planned tracks for \"${a.output}\":\\n${table}`;\n}\n\nexport async function muxCommand(opts: MuxOptions, deps: CommandDeps): Promise<MuxResult> {\n requireInputFile(opts.video, deps.exists);\n const uniqueAudio = [...new Set(opts.tracks.map((track) => track.audio))];\n for (const audio of uniqueAudio) requireInputFile(audio, deps.exists);\n\n const videoTracks = await probeTracks(deps.runner, opts.video);\n const sourceTracks = new Map(\n await Promise.all(\n uniqueAudio.map(async (audio): Promise<[string, Track[]]> => [\n audio,\n await probeTracks(deps.runner, audio)\n ])\n )\n );\n\n const groups: ResolvedMuxTrack[] = opts.tracks.map((track, index) => {\n let trackId: number;\n try {\n trackId = resolveAudioTrackId(sourceTracks.get(track.audio)!, track.track);\n } catch (error) {\n if (!(error instanceof CliError)) throw error;\n throw new CliError(`${describeAudioGroup(index, track.audio)}: ${error.message}`);\n }\n return {\n audio: track.audio,\n trackId,\n delayMs: track.delayMs,\n language: track.language,\n trackName: track.name,\n makeDefault: track.makeDefault\n };\n });\n if (groups.filter((group) => group.makeDefault).length > 1) {\n throw new CliError('--default can be set on at most one --audio input.');\n }\n\n const aliasedInput = findAliasedInput(opts.output, [opts.video, ...uniqueAudio]);\n if (aliasedInput !== undefined) {\n throw new CliError(\n `Output \"${opts.output}\" is the same file as input \"${aliasedInput}\". Choose a different output path.`\n );\n }\n\n const clearDefaultAudioIds = groups.some((group) => group.makeDefault)\n ? videoTracks\n .filter((track) => track.type === 'audio' && track.isDefault)\n .map((track) => track.id)\n : [];\n const plan = buildPlan({\n video: opts.video,\n videoTracks,\n groups,\n sourceTracks,\n clearDefaultAudioIds,\n output: opts.output\n });\n if (opts.dryRun) return { kind: 'dry-run', plan };\n\n const exists = deps.exists ?? existsSync;\n const outputExists = exists(opts.output);\n if (!opts.force) {\n if (deps.isTTY) {\n const overwrite = outputExists\n ? `\\nOutput \"${opts.output}\" already exists and will be overwritten.`\n : '';\n const approved = await deps.confirm(`${plan}${overwrite}\\nWrite \"${opts.output}\"?`);\n if (!approved) throw new CliError('Aborted: nothing written.');\n } else if (outputExists) {\n throw new CliError(\n `Output \"${opts.output}\" already exists. Pass --force to overwrite (cannot prompt in a non-interactive session).`\n );\n }\n }\n\n const result = await deps.runner.run(\n buildMuxArgs({\n video: opts.video,\n videoTrackIds: videoTracks.map((track) => track.id),\n clearDefaultAudioIds,\n groups,\n output: opts.output\n }),\n deps.stderrIsTTY ? { stream: 'stdout' } : undefined\n );\n if (result.exitCode >= 2) {\n throw new CliError(\n `mkvmerge failed (exit ${result.exitCode}):\\n${result.stderr || result.stdout}`\n );\n }\n return { kind: 'written', output: opts.output };\n}\n","import type { CommandDeps } from '../types.ts';\n\nimport { defaultTestClipOutput, ensureWritable, requireInputFile } from '../output.ts';\nimport { toolPath } from '../paths.ts';\nimport { audioRelativeIndex, probeTracks, requireAudioTrack } from '../probe.ts';\nimport { CliError } from '../types.ts';\n\nexport function parseTimeToSeconds(value: string): number {\n if (/^\\d+(\\.\\d+)?$/.test(value)) return Number(value);\n const match = /^(\\d+):([0-5]?\\d):([0-5]?\\d(?:\\.\\d+)?)$/.exec(value);\n if (!match)\n throw new CliError(`Invalid time \"${value}\". Use seconds (90 or 90.5) or HH:MM:SS[.ms].`);\n return Number(match[1]) * 3600 + Number(match[2]) * 60 + Number(match[3]);\n}\n\nexport function buildTestClipArgs(a: {\n video: string;\n audio: string;\n audioStreamIndex: number;\n startSeconds: number;\n durationSeconds: number;\n delayMs: number;\n output: string;\n}): string[] {\n // -y is safe: our own overwrite policy (ensureWritable) has already run.\n return [\n 'ffmpeg',\n '-hide_banner',\n '-nostdin',\n '-ss',\n String(a.startSeconds),\n '-i',\n toolPath(a.video),\n '-itsoffset',\n String(a.delayMs / 1000),\n '-ss',\n String(a.startSeconds),\n '-i',\n toolPath(a.audio),\n '-map',\n '0:v:0',\n '-map',\n `1:a:${a.audioStreamIndex}`,\n '-t',\n String(a.durationSeconds),\n '-c:v',\n 'libx264',\n '-preset',\n 'veryfast',\n '-crf',\n '18',\n '-c:a',\n 'copy',\n '-sn',\n '-abort_on',\n 'empty_output_stream',\n '-y',\n toolPath(a.output)\n ];\n}\n\nexport async function testClipCommand(\n opts: {\n video: string;\n audio: string;\n track: number;\n start: string;\n duration: string;\n delayMs: number;\n output?: string;\n force: boolean;\n },\n deps: CommandDeps\n): Promise<string> {\n requireInputFile(opts.video, deps.exists);\n requireInputFile(opts.audio, deps.exists);\n const startSeconds = parseTimeToSeconds(opts.start);\n const durationSeconds = parseTimeToSeconds(opts.duration);\n if (durationSeconds <= 0) throw new CliError('--duration must be greater than zero.');\n const donorTracks = await probeTracks(deps.runner, opts.audio);\n requireAudioTrack(donorTracks, opts.track);\n const output = opts.output ?? defaultTestClipOutput(opts.video);\n await ensureWritable(output, {\n force: opts.force,\n isTTY: deps.isTTY,\n confirm: deps.confirm,\n exists: deps.exists,\n inputs: [opts.video, opts.audio]\n });\n const result = await deps.runner.run(\n buildTestClipArgs({\n video: opts.video,\n audio: opts.audio,\n audioStreamIndex: audioRelativeIndex(donorTracks, opts.track),\n startSeconds,\n durationSeconds,\n delayMs: opts.delayMs,\n output\n }),\n deps.stderrIsTTY ? { stream: 'stderr' } : undefined\n );\n if (result.exitCode !== 0) {\n throw new CliError(\n `ffmpeg failed (exit ${result.exitCode}):\\n${result.stderr || result.stdout}`\n );\n }\n return output;\n}\n","import { parseArgs } from 'node:util';\n\nimport type { CommandDeps } from './types.ts';\n\nimport { editCommand } from './commands/edit.ts';\nimport { extractCommand } from './commands/extract.ts';\nimport { inspectCommand } from './commands/inspect.ts';\nimport { describeAudioGroup, muxCommand } from './commands/mux.ts';\nimport { testClipCommand } from './commands/test-clip.ts';\nimport { CliError } from './types.ts';\n\nconst USAGE = `tracksmith — inspect, edit, extract, sync-test, and mux Matroska audio tracks\n\nUsage:\n tracksmith inspect <file>\n tracksmith extract <file> --track <id> [--output <file>] [--force]\n tracksmith edit <file> --track <id> [--name <text>] [--language <lang>]\n [--default | --no-default] [--forced | --no-forced]\n tracksmith edit <file> --title <text>\n tracksmith test-clip --video <target> --audio <donor> --track <id> --start <time>\n [--duration 60] [--delay-ms 0] [--output <file>] [--force]\n tracksmith mux --video <target> --output <file> [--dry-run] [--force]\n --audio <mka-or-mkv> [--track <id>] [--delay-ms 0] [--language eng] [--name <title>] [--default]\n [--audio <next> ...]\n\nNotes:\n Repeat --audio to add several tracks; per-track flags apply to the most recent --audio.\n --default may be set on at most one --audio; it also clears the target's existing default audio flags.\n --dry-run prints the planned track layout without writing.\n --track is always the MKVToolNix track ID shown by \"tracksmith inspect\".\n edit changes metadata in place without remuxing. --name \"\" and --title \"\" clear the value\n (in Windows PowerShell use --name= and --title=; empty \"\" arguments get dropped).\n --title sets the file title and cannot be combined with --track edits.\n Positive --delay-ms delays audio; negative advances it (e.g. --delay-ms -250).\n Times accept seconds (90, 90.5) or HH:MM:SS[.ms].\n Check sync near the beginning, middle, and end; a changing offset cannot be fixed by one delay.\n Requires ffmpeg and MKVToolNix (mkvmerge, mkvpropedit) on PATH.`;\n\nfunction parseIntStrict(value: string | undefined, flag: string): number {\n if (value === undefined) throw new CliError(`${flag} is required.`);\n if (!/^-?\\d+$/.test(value)) throw new CliError(`${flag} must be an integer, got \"${value}\".`);\n return Number(value);\n}\n\nfunction parseTrack(value: string | undefined): number {\n const track = parseIntStrict(value, '--track');\n if (track < 0) throw new CliError(`--track must be >= 0, got ${track}.`);\n return track;\n}\n\nfunction requireFilePositional(\n positionals: string[],\n command: 'inspect' | 'extract' | 'edit'\n): string {\n if (positionals.length === 0) throw new CliError(`${command} requires a file argument.`);\n if (positionals.length > 1) throw new CliError(`${command} accepts exactly one file argument.`);\n return positionals[0]!;\n}\n\n// Wraps a parseArgs call so parse failures become CliErrors. Takes a thunk\n// (rather than the options object) so parseArgs keeps its precise generic\n// inference over each command's inline options literal.\nfunction parseOrCliError<T>(parseFn: () => T): T {\n try {\n return parseFn();\n } catch (error) {\n throw new CliError(error instanceof Error ? error.message : String(error));\n }\n}\n\n// parseArgs treats a leading-dash value token (\"-250\") as an option, so fold\n// `--delay-ms -250` into the equals form it does accept.\nfunction normalizeDelayMs(args: string[]): string[] {\n const out: string[] = [];\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i]!;\n const next = args[i + 1];\n if (arg === '--delay-ms' && next !== undefined && /^-\\d+$/.test(next)) {\n out.push(`--delay-ms=${next}`);\n i += 1;\n } else {\n out.push(arg);\n }\n }\n return out;\n}\n\nexport async function runCli(\n argv: string[],\n deps: CommandDeps,\n stdout: (line: string) => void = console.log\n): Promise<number> {\n const [command, ...rawRest] = argv;\n const rest = normalizeDelayMs(rawRest);\n try {\n switch (command) {\n case undefined: {\n stdout(USAGE);\n return 1;\n }\n case '--help':\n case '-h': {\n stdout(USAGE);\n return 0;\n }\n case 'inspect': {\n const { positionals } = parseOrCliError(() =>\n parseArgs({ args: rest, options: {}, allowPositionals: true, strict: true })\n );\n const file = requireFilePositional(positionals, 'inspect');\n stdout(await inspectCommand(file, deps));\n return 0;\n }\n case 'extract': {\n const { values, positionals } = parseOrCliError(() =>\n parseArgs({\n args: rest,\n options: {\n track: { type: 'string' },\n output: { type: 'string' },\n force: { type: 'boolean', default: false }\n },\n allowPositionals: true,\n strict: true\n })\n );\n const file = requireFilePositional(positionals, 'extract');\n const output = await extractCommand(\n {\n file,\n track: parseTrack(values.track),\n output: values.output,\n force: values.force ?? false\n },\n deps\n );\n stdout(`Wrote ${output}`);\n return 0;\n }\n case 'test-clip': {\n const { values } = parseOrCliError(() =>\n parseArgs({\n args: rest,\n options: {\n video: { type: 'string' },\n audio: { type: 'string' },\n track: { type: 'string' },\n start: { type: 'string' },\n duration: { type: 'string', default: '60' },\n 'delay-ms': { type: 'string', default: '0' },\n output: { type: 'string' },\n force: { type: 'boolean', default: false }\n },\n strict: true\n })\n );\n if (!values.video || !values.audio)\n throw new CliError('test-clip requires --video and --audio.');\n if (!values.start) throw new CliError('test-clip requires --start.');\n const output = await testClipCommand(\n {\n video: values.video,\n audio: values.audio,\n track: parseTrack(values.track),\n start: values.start,\n duration: values.duration ?? '60',\n delayMs: parseIntStrict(values['delay-ms'] ?? '0', '--delay-ms'),\n output: values.output,\n force: values.force ?? false\n },\n deps\n );\n stdout(`Wrote ${output}`);\n return 0;\n }\n case 'mux': {\n const { values, tokens } = parseOrCliError(() =>\n parseArgs({\n args: rest,\n options: {\n video: { type: 'string' },\n output: { type: 'string' },\n force: { type: 'boolean', default: false },\n 'dry-run': { type: 'boolean', default: false },\n audio: { type: 'string', multiple: true },\n track: { type: 'string', multiple: true },\n 'delay-ms': { type: 'string', multiple: true },\n language: { type: 'string', multiple: true },\n name: { type: 'string', multiple: true },\n default: { type: 'boolean', multiple: true }\n },\n strict: true,\n tokens: true\n })\n );\n const groups: {\n audio: string;\n track?: string;\n delayMs?: string;\n language?: string;\n name?: string;\n makeDefault?: true;\n }[] = [];\n for (const token of tokens) {\n if (token.kind !== 'option') continue;\n if (token.name === 'audio') {\n groups.push({ audio: token.value });\n continue;\n }\n if (\n token.name !== 'track' &&\n token.name !== 'delay-ms' &&\n token.name !== 'language' &&\n token.name !== 'name' &&\n token.name !== 'default'\n ) {\n continue;\n }\n const group = groups.at(-1);\n if (group === undefined) {\n throw new CliError(`--${token.name} must come after an --audio input.`);\n }\n switch (token.name) {\n case 'track':\n if (group.track !== undefined)\n throw new CliError(\n `Duplicate --track for ${describeAudioGroup(groups.length - 1, group.audio)}.`\n );\n group.track = token.value;\n break;\n case 'delay-ms':\n if (group.delayMs !== undefined)\n throw new CliError(\n `Duplicate --delay-ms for ${describeAudioGroup(groups.length - 1, group.audio)}.`\n );\n group.delayMs = token.value;\n break;\n case 'language':\n if (group.language !== undefined)\n throw new CliError(\n `Duplicate --language for ${describeAudioGroup(groups.length - 1, group.audio)}.`\n );\n group.language = token.value;\n break;\n case 'name':\n if (group.name !== undefined)\n throw new CliError(\n `Duplicate --name for ${describeAudioGroup(groups.length - 1, group.audio)}.`\n );\n group.name = token.value;\n break;\n case 'default':\n if (group.makeDefault !== undefined)\n throw new CliError(\n `Duplicate --default for ${describeAudioGroup(groups.length - 1, group.audio)}.`\n );\n group.makeDefault = true;\n break;\n }\n }\n if (!values.video || groups.length === 0)\n throw new CliError('mux requires --video and --audio.');\n if (!values.output)\n throw new CliError('mux requires --output (no default output name for the final file).');\n const result = await muxCommand(\n {\n video: values.video,\n tracks: groups.map((group) => ({\n audio: group.audio,\n track: group.track === undefined ? undefined : parseTrack(group.track),\n delayMs: parseIntStrict(group.delayMs ?? '0', '--delay-ms'),\n language: group.language ?? 'eng',\n name: group.name,\n makeDefault: group.makeDefault ?? false\n })),\n output: values.output,\n force: values.force ?? false,\n dryRun: values['dry-run'] ?? false\n },\n deps\n );\n if (result.kind === 'dry-run') stdout(result.plan);\n else stdout(`Wrote ${result.output}`);\n return 0;\n }\n case 'edit': {\n const { values, positionals } = parseOrCliError(() =>\n parseArgs({\n args: rest,\n options: {\n track: { type: 'string' },\n name: { type: 'string' },\n language: { type: 'string' },\n default: { type: 'boolean' },\n 'no-default': { type: 'boolean' },\n forced: { type: 'boolean' },\n 'no-forced': { type: 'boolean' },\n title: { type: 'string' }\n },\n allowPositionals: true,\n strict: true\n })\n );\n const file = requireFilePositional(positionals, 'edit');\n const hasTrackFlags =\n values.track !== undefined ||\n values.name !== undefined ||\n values.language !== undefined ||\n values.default !== undefined ||\n values['no-default'] !== undefined ||\n values.forced !== undefined ||\n values['no-forced'] !== undefined;\n if (values.title !== undefined && hasTrackFlags)\n throw new CliError(\n '--title cannot be combined with --track edits. Run them as separate invocations.'\n );\n if (values.default !== undefined && values['no-default'] !== undefined)\n throw new CliError('Pass either --default or --no-default, not both.');\n if (values.forced !== undefined && values['no-forced'] !== undefined)\n throw new CliError('Pass either --forced or --no-forced, not both.');\n if (values.language === '')\n throw new CliError('--language cannot be empty. Use --language und for undetermined.');\n let warning: string | undefined;\n if (values.title !== undefined) {\n warning = await editCommand({ kind: 'title', file, title: values.title }, deps);\n } else {\n if (values.track === undefined)\n throw new CliError('edit requires --track (or --title for the file title).');\n warning = await editCommand(\n {\n kind: 'track',\n file,\n track: parseTrack(values.track),\n name: values.name,\n language: values.language,\n isDefault: values.default ?? (values['no-default'] !== undefined ? false : undefined),\n isForced: values.forced ?? (values['no-forced'] !== undefined ? false : undefined)\n },\n deps\n );\n }\n if (warning !== undefined) console.error(`tracksmith: ${warning}`);\n stdout(`Edited ${file}`);\n return 0;\n }\n default:\n throw new CliError(`Unknown command \"${command}\".\\n\\n${USAGE}`);\n }\n } catch (error) {\n if (error instanceof CliError) {\n console.error(`tracksmith: ${error.message}`);\n return error.exitCode;\n }\n throw error;\n }\n}\n","import { once } from 'node:events';\nimport { createInterface } from 'node:readline/promises';\n\nimport type { Readable, Writable } from 'node:stream';\n\nexport interface ConfirmPromptOptions {\n input?: Readable;\n output?: Writable;\n}\n\nexport function createConfirmPrompt(\n options: ConfirmPromptOptions = {}\n): (message: string) => Promise<boolean> {\n const input = options.input ?? process.stdin;\n const output = options.output ?? process.stdout;\n\n return async (message: string): Promise<boolean> => {\n const readline = createInterface({ input, output });\n\n try {\n const closed = once(readline, 'close').then(() => ({ kind: 'eof' }) as const);\n const answered = readline.question(`${message} [y/N] `).then(\n (value) => ({ kind: 'answer', value }) as const,\n () => ({ kind: 'eof' }) as const\n );\n const result = await Promise.race([answered, closed]);\n\n return result.kind === 'answer' && /^y(?:es)?$/i.test(result.value.trim());\n } finally {\n readline.close();\n }\n };\n}\n","export const TOOLS = ['ffmpeg', 'mkvmerge', 'mkvpropedit'] as const;\n\nexport type ToolName = (typeof TOOLS)[number];\n\nexport function isToolName(value: string): value is ToolName {\n return TOOLS.some((tool) => tool === value);\n}\n\nconst MKVTOOLNIX_HINT =\n 'Windows: winget install MoritzBunkus.MKVToolNix or scoop install mkvtoolnix | macOS: brew install mkvtoolnix | Linux: sudo apt install mkvtoolnix or sudo pacman -S mkvtoolnix-cli';\n\nconst INSTALL_HINTS = {\n ffmpeg:\n 'Windows: winget install Gyan.FFmpeg or scoop install ffmpeg | macOS: brew install ffmpeg | Linux: sudo apt install ffmpeg or sudo pacman -S ffmpeg',\n mkvmerge: MKVTOOLNIX_HINT,\n mkvpropedit: MKVTOOLNIX_HINT\n} satisfies Record<ToolName, string>;\n\nexport function installHint(tool: ToolName): string {\n return INSTALL_HINTS[tool];\n}\n","import { spawn } from 'node:child_process';\n\nimport type { RunOptions, Runner, RunResult } from './types.ts';\n\nimport { installHint, isToolName } from './tools.ts';\nimport { CliError } from './types.ts';\n\nexport function processStartError(program: string, error: unknown): CliError {\n if (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n error.code === 'ENOENT' &&\n isToolName(program)\n ) {\n return new CliError(\n `${program} is required but was not found on PATH. ${installHint(program)}`\n );\n }\n\n const message = error instanceof Error ? error.message : String(error);\n return new CliError(`Failed to start ${program}: ${message}`);\n}\n\nconst writeToCliStderr = (chunk: string): void => {\n globalThis.process.stderr.write(chunk);\n};\n\nexport class ProcessRunner implements Runner {\n private readonly sink: (chunk: string) => void;\n\n constructor(options: { sink?: (chunk: string) => void } = {}) {\n this.sink = options.sink ?? writeToCliStderr;\n }\n\n async run(argv: readonly string[], options?: RunOptions): Promise<RunResult> {\n const [executable, ...args] = argv;\n if (executable === undefined) {\n throw new CliError('Could not start process: missing executable');\n }\n\n return await new Promise((resolve, reject) => {\n const process = spawn(executable, args, {\n stdio: ['ignore', 'pipe', 'pipe']\n });\n let stdout = '';\n let stderr = '';\n\n process.stdout.setEncoding('utf8');\n process.stderr.setEncoding('utf8');\n process.stdout.on('data', (chunk: string) => {\n stdout += chunk;\n if (options?.stream === 'stdout') this.sink(chunk);\n });\n process.stderr.on('data', (chunk: string) => {\n stderr += chunk;\n if (options?.stream === 'stderr') this.sink(chunk);\n });\n process.once('error', (error) => {\n reject(processStartError(executable, error));\n });\n // Windows loses the signal: a killed child reports a plain numeric exit\n // code (usually 1) with signal null, so this detection is POSIX-only and\n // Windows cannot distinguish a killed tool from a warning exit.\n process.once('close', (exitCode, signal) => {\n if (signal !== null) {\n const output = [stdout, stderr]\n .map((stream) => stream.trim())\n .filter(Boolean)\n .join('\\n');\n const diagnostics = output === '' ? '' : `\\nOutput before termination:\\n${output}`;\n reject(new CliError(`${executable} was terminated by signal ${signal}.${diagnostics}`));\n return;\n }\n resolve({ exitCode: exitCode ?? 1, stdout, stderr });\n });\n });\n }\n}\n","#!/usr/bin/env node\n\nimport { runCli } from './cli.ts';\nimport { createConfirmPrompt } from './prompt.ts';\nimport { ProcessRunner } from './runner.ts';\n\nprocess.exitCode = await runCli(process.argv.slice(2), {\n runner: new ProcessRunner(),\n isTTY: process.stdin.isTTY ?? false,\n stderrIsTTY: process.stderr.isTTY ?? false,\n confirm: createConfirmPrompt({ output: process.stderr })\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracksmith",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "CLI for inspecting, extracting, syncing, and muxing Matroska audio tracks.",
5
5
  "type": "module",
6
6
  "bin": {