feat(diagnostics): persist full stderr detail behind a "Show details" toggle
describeDownloadError condenses a postprocessing failure to one line for the queue/notification, discarding the rest of yt-dlp's stderr. The persisted error log now also keeps the fuller non-noise transcript (stderrDetail) as an optional `detail` field, and the Diagnostics card gets a collapsible "Show details" expander per row so a failure like "Postprocessing: Conversion failed!" can be inspected -- and copied into a bug report -- without re-running the download from a terminal.
This commit is contained in:
@@ -74,7 +74,8 @@ export function isValidErrorLogEntry(o: unknown): o is ErrorLogEntry {
|
||||
(e.kind === 'video' || e.kind === 'audio') &&
|
||||
typeof e.error === 'string' &&
|
||||
typeof e.occurredAt === 'number' &&
|
||||
isOptionalString(e.title)
|
||||
isOptionalString(e.title) &&
|
||||
isOptionalString(e.detail)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
FILEPATH_MARKER,
|
||||
DEST_MARKER
|
||||
} from './core/buildArgs'
|
||||
import { describeDownloadError } from './log'
|
||||
import { describeDownloadError, stderrDetail } from './log'
|
||||
import { addErrorLog } from './errorlog'
|
||||
import {
|
||||
STALL_TIMEOUT_MS,
|
||||
@@ -117,7 +117,12 @@ function notify(wc: WebContents, title: string, body: string, incognito = false)
|
||||
if (flashWin && !flashWin.isDestroyed() && !flashWin.isFocused()) flashWin.flashFrame(true)
|
||||
}
|
||||
|
||||
function logFailure(opts: StartDownloadOptions, title: string | undefined, error: string): void {
|
||||
function logFailure(
|
||||
opts: StartDownloadOptions,
|
||||
title: string | undefined,
|
||||
error: string,
|
||||
detail?: string
|
||||
): void {
|
||||
// L136: incognito downloads are never recorded — not even a failure entry, which
|
||||
// would persist the title + URL in the user-facing diagnostics log.
|
||||
if (opts.incognito) return
|
||||
@@ -127,6 +132,7 @@ function logFailure(opts: StartDownloadOptions, title: string | undefined, error
|
||||
url: opts.url,
|
||||
kind: opts.kind,
|
||||
error,
|
||||
detail,
|
||||
occurredAt: Date.now()
|
||||
})
|
||||
}
|
||||
@@ -532,7 +538,7 @@ function wireChildProcess(params: {
|
||||
} else {
|
||||
const msg = describeDownloadError(stderrTail) || `yt-dlp exited with code ${code}`
|
||||
send(wc, { type: 'error', id: opts.id, error: msg })
|
||||
logFailure(opts, getTitle(), msg)
|
||||
logFailure(opts, getTitle(), msg, stderrDetail(stderrTail))
|
||||
notify(
|
||||
wc,
|
||||
opts.incognito ? 'Download failed' : (getTitle() ?? 'Download failed'),
|
||||
|
||||
@@ -53,3 +53,20 @@ export function describeDownloadError(stderr: string): string {
|
||||
}
|
||||
return context.length > 0 ? `${headline} — ${context.join(' · ')}` : headline
|
||||
}
|
||||
|
||||
/**
|
||||
* Full non-noise stderr, for the persisted error log's "Show details" expander
|
||||
* (Diagnostics card) — everything describeDownloadError condenses to one line,
|
||||
* kept in full (progress ticks and delete-bookkeeping still stripped) so a bug
|
||||
* report carries the real yt-dlp/ffmpeg transcript instead of a paraphrase.
|
||||
* Returns undefined when there's nothing beyond a single line — the headline
|
||||
* the caller already shows — so a plain, self-explanatory error doesn't grow a
|
||||
* details toggle with nothing new in it.
|
||||
*/
|
||||
export function stderrDetail(stderr: string): string | undefined {
|
||||
const lines = stderr
|
||||
.split('\n')
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l && !CONTEXT_NOISE.test(l))
|
||||
return lines.length > 1 ? lines.join('\n') : undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user