Fix L54/L61/L63: thumbnail error fallback, ipcMain.handle, audioQuality label
L54: DownloadBar preview image now tracks failed src in state (same pattern as
MediaThumb); on load error shows the kind icon fallback instead of broken img.
L61: Convert taskbarProgress from ipcMain.on/ipcRenderer.send to ipcMain.handle/
ipcRenderer.invoke so all IPC channels use the same request-response idiom.
App.tsx calls void to explicitly discard the resolved promise.
L63: audioQuality() now has an explicit 'Best' case alongside the bitrate strings;
unknown labels still fall back to '0' but the default branch is documented.
typecheck + 242 tests + eslint + prettier green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+3
-3
@@ -483,7 +483,7 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
|
|||||||
- [ ] **L52 — Installer handoff is a fixed `setTimeout(app.quit, 1500)`** (updater.ts) — a race on slow machines.
|
- [ ] **L52 — Installer handoff is a fixed `setTimeout(app.quit, 1500)`** (updater.ts) — a race on slow machines.
|
||||||
- [x] **L53 — `dialog.show*Dialog(win!, …)` non-null assertions** on a possibly-null window
|
- [x] **L53 — `dialog.show*Dialog(win!, …)` non-null assertions** on a possibly-null window
|
||||||
(chooseFolder, backup export/import) — can throw if the sender window is gone.
|
(chooseFolder, backup export/import) — can throw if the sender window is gone.
|
||||||
- [ ] **L54 — DownloadBar preview `<img>` has no `onError` fallback** (inconsistent with `MediaThumb`'s retry).
|
- [x] **L54 — DownloadBar preview `<img>` has no `onError` fallback** (inconsistent with `MediaThumb`'s retry).
|
||||||
- [x] **L55 — QueueItem meta line can show size twice** — a probed-format `quality` label already
|
- [x] **L55 — QueueItem meta line can show size twice** — a probed-format `quality` label already
|
||||||
contains the size, then `sizeLabel` is appended again.
|
contains the size, then `sizeLabel` is appended again.
|
||||||
- [ ] **L56 — SponsorBlock ON with zero categories silently no-ops** (`buildArgs` guards on
|
- [ ] **L56 — SponsorBlock ON with zero categories silently no-ops** (`buildArgs` guards on
|
||||||
@@ -494,10 +494,10 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
|
|||||||
- [x] **L59 — `THEME_BACKGROUND` (index.ts) duplicates `pageBackground` (theme.ts)** — two
|
- [x] **L59 — `THEME_BACKGROUND` (index.ts) duplicates `pageBackground` (theme.ts)** — two
|
||||||
hand-synced color constants (a "keep in sync" comment guards them).
|
hand-synced color constants (a "keep in sync" comment guards them).
|
||||||
- [x] **L60 — `'external-url'` channel name lacks the `category:` prefix** every other IPC channel uses.
|
- [x] **L60 — `'external-url'` channel name lacks the `category:` prefix** every other IPC channel uses.
|
||||||
- [ ] **L61 — `taskbarProgress` is the lone `ipcMain.on`** (fire-and-forget) amid all-`invoke` channels.
|
- [x] **L61 — `taskbarProgress` is the lone `ipcMain.on`** (fire-and-forget) amid all-`invoke` channels.
|
||||||
- [ ] **L62 — "Best available" synthetic format hardcodes `ext:'mp4'`/`hasAudio:true`** (probe.ts) —
|
- [ ] **L62 — "Best available" synthetic format hardcodes `ext:'mp4'`/`hasAudio:true`** (probe.ts) —
|
||||||
mislabels when the chosen container is mkv/webm.
|
mislabels when the chosen container is mkv/webm.
|
||||||
- [ ] **L63 — `audioQuality()` unknown label → silent `'0'` (best)** — the audio analog of H5.
|
- [x] **L63 — `audioQuality()` unknown label → silent `'0'` (best)** — the audio analog of H5.
|
||||||
- [ ] **L64 — `ARIA2C_ARGS` connection params hardcoded** (`-x16 -s16 -k1M`), no user control.
|
- [ ] **L64 — `ARIA2C_ARGS` connection params hardcoded** (`-x16 -s16 -k1M`), no user control.
|
||||||
- [ ] **L65 — Pause uses `taskkill /F`** like cancel — a forced kill risks an unflushed `.part` tail vs a graceful stop.
|
- [ ] **L65 — Pause uses `taskkill /F`** like cancel — a forced kill risks an unflushed `.part` tail vs a graceful stop.
|
||||||
- [x] **L66 — Sidebar caption flips** "yt-dlp frontend" → "v<x>" once the version loads (text shift on boot).
|
- [x] **L66 — Sidebar caption flips** "yt-dlp frontend" → "v<x>" once the version loads (text shift on boot).
|
||||||
|
|||||||
@@ -55,8 +55,11 @@ function audioQuality(quality: string): string {
|
|||||||
return '192K'
|
return '192K'
|
||||||
case '128 kbps':
|
case '128 kbps':
|
||||||
return '128K'
|
return '128K'
|
||||||
|
case 'Best':
|
||||||
|
return '0'
|
||||||
default:
|
default:
|
||||||
return '0' // Best
|
// Unrecognised label — fall back to best (0) so the download still works.
|
||||||
|
return '0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -427,8 +427,8 @@ function registerIpcHandlers(): void {
|
|||||||
ipcMain.handle(IpcChannels.scheduledSyncGet, () => getScheduledSync())
|
ipcMain.handle(IpcChannels.scheduledSyncGet, () => getScheduledSync())
|
||||||
ipcMain.handle(IpcChannels.scheduledSyncSet, (_e, enabled: boolean) => setScheduledSync(enabled))
|
ipcMain.handle(IpcChannels.scheduledSyncSet, (_e, enabled: boolean) => setScheduledSync(enabled))
|
||||||
|
|
||||||
// Reflect overall queue progress on the Windows taskbar (fire-and-forget).
|
// Reflect overall queue progress on the Windows taskbar.
|
||||||
ipcMain.on(IpcChannels.taskbarProgress, (_e, p: TaskbarProgress) => {
|
ipcMain.handle(IpcChannels.taskbarProgress, (_e, p: TaskbarProgress) => {
|
||||||
if (!mainWindow || mainWindow.isDestroyed()) return
|
if (!mainWindow || mainWindow.isDestroyed()) return
|
||||||
if (p.mode === 'none') {
|
if (p.mode === 'none') {
|
||||||
mainWindow.setProgressBar(-1)
|
mainWindow.setProgressBar(-1)
|
||||||
|
|||||||
@@ -223,9 +223,9 @@ const api = {
|
|||||||
return () => ipcRenderer.removeListener(IpcChannels.terminalOutput, listener)
|
return () => ipcRenderer.removeListener(IpcChannels.terminalOutput, listener)
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Reflect overall queue progress on the Windows taskbar (fire-and-forget). */
|
/** Reflect overall queue progress on the Windows taskbar. */
|
||||||
setTaskbarProgress: (p: TaskbarProgress): void =>
|
setTaskbarProgress: (p: TaskbarProgress): Promise<void> =>
|
||||||
ipcRenderer.send(IpcChannels.taskbarProgress, p),
|
ipcRenderer.invoke(IpcChannels.taskbarProgress, p),
|
||||||
|
|
||||||
/** Open a YouTube WebView to automatically extract a PO token (Phase P). */
|
/** Open a YouTube WebView to automatically extract a PO token (Phase P). */
|
||||||
mintPoToken: (): Promise<string | null> =>
|
mintPoToken: (): Promise<string | null> =>
|
||||||
|
|||||||
@@ -69,7 +69,11 @@ function App(): React.JSX.Element {
|
|||||||
lastFraction = s.progress
|
lastFraction = s.progress
|
||||||
lastMode = mode
|
lastMode = mode
|
||||||
lastBadge = s.downloading
|
lastBadge = s.downloading
|
||||||
window.api?.setTaskbarProgress?.({ fraction: s.progress, mode, badgeCount: s.downloading })
|
void window.api?.setTaskbarProgress?.({
|
||||||
|
fraction: s.progress,
|
||||||
|
mode,
|
||||||
|
badgeCount: s.downloading
|
||||||
|
})
|
||||||
}
|
}
|
||||||
push(useDownloads.getState().items)
|
push(useDownloads.getState().items)
|
||||||
return useDownloads.subscribe((st) => push(st.items))
|
return useDownloads.subscribe((st) => push(st.items))
|
||||||
|
|||||||
@@ -327,6 +327,7 @@ export function DownloadBar(): React.JSX.Element {
|
|||||||
|
|
||||||
// Probe state for the single-video format picker.
|
// Probe state for the single-video format picker.
|
||||||
const [info, setInfo] = useState<MediaInfo | null>(null)
|
const [info, setInfo] = useState<MediaInfo | null>(null)
|
||||||
|
const [thumbFailed, setThumbFailed] = useState<string | null>(null)
|
||||||
const [probing, setProbing] = useState(false)
|
const [probing, setProbing] = useState(false)
|
||||||
const [probeError, setProbeError] = useState<string | null>(null)
|
const [probeError, setProbeError] = useState<string | null>(null)
|
||||||
const [formatId, setFormatId] = useState<string>('')
|
const [formatId, setFormatId] = useState<string>('')
|
||||||
@@ -627,8 +628,13 @@ export function DownloadBar(): React.JSX.Element {
|
|||||||
|
|
||||||
{info && (
|
{info && (
|
||||||
<div className={styles.preview}>
|
<div className={styles.preview}>
|
||||||
{info.thumbnail ? (
|
{info.thumbnail && thumbFailed !== info.thumbnail ? (
|
||||||
<img className={styles.previewThumb} src={info.thumbnail} alt="" />
|
<img
|
||||||
|
className={styles.previewThumb}
|
||||||
|
src={info.thumbnail}
|
||||||
|
alt=""
|
||||||
|
onError={() => setThumbFailed(info.thumbnail ?? null)}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.previewThumb}>
|
<div className={styles.previewThumb}>
|
||||||
{kind === 'audio' ? (
|
{kind === 'audio' ? (
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ if (import.meta.env.DEV && !window.api) {
|
|||||||
runTerminal: async () => ({ ok: true }),
|
runTerminal: async () => ({ ok: true }),
|
||||||
cancelTerminal: async () => {},
|
cancelTerminal: async () => {},
|
||||||
onTerminalOutput: () => () => {},
|
onTerminalOutput: () => () => {},
|
||||||
setTaskbarProgress: () => {},
|
setTaskbarProgress: async () => {},
|
||||||
mintPoToken: async () => null
|
mintPoToken: async () => null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user