Fix M18: make audio quality labels format-agnostic ("Best", not "Best (MP3)")
The audio quality preset "Best (MP3)" named a format that's wrong whenever the audio format is opus/flac/wav. Renamed it to "Best" so the label never contradicts the chosen audioFormat (which is picked separately). Bitrate presets stay as bitrates and are already ignored for lossless formats (M21). - AUDIO_QUALITY_OPTIONS[0]: 'Best (MP3)' -> 'Best' (buildArgs already had a `case 'Best'` returning '0', so arg generation is unchanged). - Updated the main + renderer defaults and the preview/mock/test fixtures. - Added a one-line migration in getSettings() that rewrites a stored legacy 'Best (MP3)' to 'Best' so existing users' dropdowns match. typecheck + 242 tests + eslint green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -274,7 +274,7 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
|
|||||||
persisted data lives in main, so nothing is lost).*
|
persisted data lives in main, so nothing is lost).*
|
||||||
- [x] **M17 — `statusByUrl` collapses duplicate URLs.** LibraryView maps URL→status into one
|
- [x] **M17 — `statusByUrl` collapses duplicate URLs.** LibraryView maps URL→status into one
|
||||||
`Map`; with "Download anyway" duplicates, a row can reflect the wrong item's state.
|
`Map`; with "Download anyway" duplicates, a row can reflect the wrong item's state.
|
||||||
- [ ] **M18 — Audio "quality" presets hard-code MP3.** `QUALITY_OPTIONS.audio` labels ("Best
|
- [x] **M18 — Audio "quality" presets hard-code MP3.** `QUALITY_OPTIONS.audio` labels ("Best
|
||||||
(MP3)", "320 kbps") are shown even when `downloadOptions.audioFormat` is opus/flac/wav, so the
|
(MP3)", "320 kbps") are shown even when `downloadOptions.audioFormat` is opus/flac/wav, so the
|
||||||
label contradicts the actual output format; `--audio-quality` is also meaningless for lossless.
|
label contradicts the actual output format; `--audio-quality` is also meaningless for lossless.
|
||||||
- [x] **M19 — Sidebar-collapsed pref uses `localStorage`** while every other preference uses
|
- [x] **M19 — Sidebar-collapsed pref uses `localStorage`** while every other preference uses
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const DEFAULTS: Settings = {
|
|||||||
audioDir: '',
|
audioDir: '',
|
||||||
defaultKind: 'video',
|
defaultKind: 'video',
|
||||||
defaultVideoQuality: 'Best available',
|
defaultVideoQuality: 'Best available',
|
||||||
defaultAudioQuality: 'Best (MP3)',
|
defaultAudioQuality: 'Best',
|
||||||
maxConcurrent: 2,
|
maxConcurrent: 2,
|
||||||
filenameTemplate: '%(title)s.%(ext)s',
|
filenameTemplate: '%(title)s.%(ext)s',
|
||||||
// Follow the OS theme on first launch (SR1) so a dark-mode Windows user isn't
|
// Follow the OS theme on first launch (SR1) so a dark-mode Windows user isn't
|
||||||
@@ -263,6 +263,11 @@ export function getSettings(): Settings {
|
|||||||
if (!(ACCENT_COLORS as readonly string[]).includes(cur.accentColor)) {
|
if (!(ACCENT_COLORS as readonly string[]).includes(cur.accentColor)) {
|
||||||
s.set('accentColor', DEFAULTS.accentColor)
|
s.set('accentColor', DEFAULTS.accentColor)
|
||||||
}
|
}
|
||||||
|
// Migrate the legacy audio-quality label 'Best (MP3)' to the format-agnostic
|
||||||
|
// 'Best' so the dropdown matches and the label no longer names a format (M18).
|
||||||
|
if (cur.defaultAudioQuality === 'Best (MP3)') {
|
||||||
|
s.set('defaultAudioQuality', 'Best')
|
||||||
|
}
|
||||||
// Hand callers (and, via IPC, the renderer) plaintext credentials — they're
|
// Hand callers (and, via IPC, the renderer) plaintext credentials — they're
|
||||||
// only encrypted on disk (see withDecryptedSecrets / encryptSecret).
|
// only encrypted on disk (see withDecryptedSecrets / encryptSecret).
|
||||||
cachedSettings = withDecryptedSecrets(s.store)
|
cachedSettings = withDecryptedSecrets(s.store)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ if (import.meta.env.DEV && !window.api) {
|
|||||||
audioDir: 'C:\\Users\\you\\Documents\\Audio',
|
audioDir: 'C:\\Users\\you\\Documents\\Audio',
|
||||||
defaultKind: 'video',
|
defaultKind: 'video',
|
||||||
defaultVideoQuality: 'Best available',
|
defaultVideoQuality: 'Best available',
|
||||||
defaultAudioQuality: 'Best (MP3)',
|
defaultAudioQuality: 'Best',
|
||||||
maxConcurrent: 2,
|
maxConcurrent: 2,
|
||||||
filenameTemplate: '%(title)s.%(ext)s',
|
filenameTemplate: '%(title)s.%(ext)s',
|
||||||
theme: 'system',
|
theme: 'system',
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ const seed: DownloadItem[] = PREVIEW
|
|||||||
channel: 'ChillStudio',
|
channel: 'ChillStudio',
|
||||||
durationLabel: '1:00:21',
|
durationLabel: '1:00:21',
|
||||||
kind: 'audio',
|
kind: 'audio',
|
||||||
quality: 'Best (MP3)',
|
quality: 'Best',
|
||||||
status: 'queued',
|
status: 'queued',
|
||||||
progress: 0
|
progress: 0
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const seed: HistoryEntry[] = PREVIEW
|
|||||||
channel: 'ChillStudio',
|
channel: 'ChillStudio',
|
||||||
url: 'https://youtube.com/watch?v=aaa1111',
|
url: 'https://youtube.com/watch?v=aaa1111',
|
||||||
kind: 'audio',
|
kind: 'audio',
|
||||||
quality: 'Best (MP3)',
|
quality: 'Best',
|
||||||
sizeLabel: '142 MB',
|
sizeLabel: '142 MB',
|
||||||
filePath: 'C:\\Users\\you\\Downloads\\Deep Focus — Ambient Mix.mp3',
|
filePath: 'C:\\Users\\you\\Downloads\\Deep Focus — Ambient Mix.mp3',
|
||||||
completedAt: Date.now() - 1000 * 60 * 60 * 26
|
completedAt: Date.now() - 1000 * 60 * 60 * 26
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const FALLBACK: Settings = {
|
|||||||
audioDir: PREVIEW ? 'C:\\Users\\you\\Documents\\Audio' : '',
|
audioDir: PREVIEW ? 'C:\\Users\\you\\Documents\\Audio' : '',
|
||||||
defaultKind: 'video',
|
defaultKind: 'video',
|
||||||
defaultVideoQuality: 'Best available',
|
defaultVideoQuality: 'Best available',
|
||||||
defaultAudioQuality: 'Best (MP3)',
|
defaultAudioQuality: 'Best',
|
||||||
maxConcurrent: 2,
|
maxConcurrent: 2,
|
||||||
filenameTemplate: '%(title)s.%(ext)s',
|
filenameTemplate: '%(title)s.%(ext)s',
|
||||||
// Mirror main's DEFAULTS (SR1): follow the OS theme until real settings load,
|
// Mirror main's DEFAULTS (SR1): follow the OS theme until real settings load,
|
||||||
|
|||||||
+6
-3
@@ -128,7 +128,10 @@ export const VIDEO_QUALITY_OPTIONS = ['Best available', '1080p', '720p', '480p',
|
|||||||
export type VideoQuality = (typeof VIDEO_QUALITY_OPTIONS)[number]
|
export type VideoQuality = (typeof VIDEO_QUALITY_OPTIONS)[number]
|
||||||
|
|
||||||
/** Quality/format labels for the audio download selector. */
|
/** Quality/format labels for the audio download selector. */
|
||||||
export const AUDIO_QUALITY_OPTIONS = ['Best (MP3)', '320 kbps', '192 kbps', '128 kbps'] as const
|
// Format-agnostic: the audio container/codec is chosen separately (downloadOptions
|
||||||
|
// .audioFormat); these are just the target quality. Bitrate values are ignored for
|
||||||
|
// lossless formats (flac/wav) -- see buildArgs (M18/M21).
|
||||||
|
export const AUDIO_QUALITY_OPTIONS = ['Best', '320 kbps', '192 kbps', '128 kbps'] as const
|
||||||
export type AudioQuality = (typeof AUDIO_QUALITY_OPTIONS)[number]
|
export type AudioQuality = (typeof AUDIO_QUALITY_OPTIONS)[number]
|
||||||
|
|
||||||
/** UI color theme: an explicit mode, or 'system' to follow the OS preference. */
|
/** UI color theme: an explicit mode, or 'system' to follow the OS preference. */
|
||||||
@@ -409,7 +412,7 @@ export interface StartDownloadOptions {
|
|||||||
id: string
|
id: string
|
||||||
url: string
|
url: string
|
||||||
kind: MediaKind
|
kind: MediaKind
|
||||||
/** one of the UI quality labels (e.g. '1080p', 'Best (MP3)') */
|
/** one of the UI quality labels (e.g. '1080p', 'Best') */
|
||||||
quality: string
|
quality: string
|
||||||
/** absolute output directory; main falls back to the OS Downloads folder */
|
/** absolute output directory; main falls back to the OS Downloads folder */
|
||||||
outputDir?: string
|
outputDir?: string
|
||||||
@@ -614,7 +617,7 @@ export interface HistoryEntry {
|
|||||||
channel?: string
|
channel?: string
|
||||||
url: string
|
url: string
|
||||||
kind: MediaKind
|
kind: MediaKind
|
||||||
/** Quality preset ('720p', 'Best available', 'Best (MP3)', …) or the probed
|
/** Quality preset ('720p', 'Best available', 'Best', …) or the probed
|
||||||
* format's display label. Used for display and, when no formatId is present,
|
* format's display label. Used for display and, when no formatId is present,
|
||||||
* as the fallback quality selector on re-download (H5). */
|
* as the fallback quality selector on re-download (H5). */
|
||||||
quality: string
|
quality: string
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ function opts(overrides: Partial<StartDownloadOptions> = {}): StartDownloadOptio
|
|||||||
return { id: 't', url: URL, kind: 'video', quality: '1080p', ...overrides }
|
return { id: 't', url: URL, kind: 'video', quality: '1080p', ...overrides }
|
||||||
}
|
}
|
||||||
function audio(overrides: Partial<StartDownloadOptions> = {}): StartDownloadOptions {
|
function audio(overrides: Partial<StartDownloadOptions> = {}): StartDownloadOptions {
|
||||||
return opts({ kind: 'audio', quality: 'Best (MP3)', ...overrides })
|
return opts({ kind: 'audio', quality: 'Best', ...overrides })
|
||||||
}
|
}
|
||||||
function dlo(overrides: Partial<DownloadOptions> = {}): DownloadOptions {
|
function dlo(overrides: Partial<DownloadOptions> = {}): DownloadOptions {
|
||||||
return { ...DEFAULT_DOWNLOAD_OPTIONS, ...overrides }
|
return { ...DEFAULT_DOWNLOAD_OPTIONS, ...overrides }
|
||||||
@@ -165,9 +165,7 @@ describe('audio: -x --audio-format <fmt>', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('maps the quality label to an --audio-quality value', () => {
|
it('maps the quality label to an --audio-quality value', () => {
|
||||||
expect(hasSeq(build(audio({ quality: 'Best (MP3)' }), dlo()), '--audio-quality', '0')).toBe(
|
expect(hasSeq(build(audio({ quality: 'Best' }), dlo()), '--audio-quality', '0')).toBe(true)
|
||||||
true
|
|
||||||
)
|
|
||||||
expect(hasSeq(build(audio({ quality: '320 kbps' }), dlo()), '--audio-quality', '320K')).toBe(
|
expect(hasSeq(build(audio({ quality: '320 kbps' }), dlo()), '--audio-quality', '320K')).toBe(
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ describe.skipIf(!RUN)('real yt-dlp downloads (buildArgs end-to-end)', () => {
|
|||||||
id: 'crop',
|
id: 'crop',
|
||||||
url: 'https://www.youtube.com/watch?v=jNQXAC9IVRw', // "Me at the zoo", 19s
|
url: 'https://www.youtube.com/watch?v=jNQXAC9IVRw', // "Me at the zoo", 19s
|
||||||
kind: 'audio',
|
kind: 'audio',
|
||||||
quality: 'Best (MP3)'
|
quality: 'Best'
|
||||||
}
|
}
|
||||||
const options = {
|
const options = {
|
||||||
...DEFAULT_DOWNLOAD_OPTIONS,
|
...DEFAULT_DOWNLOAD_OPTIONS,
|
||||||
@@ -181,7 +181,7 @@ describe.skipIf(!RUN)('real yt-dlp downloads (buildArgs end-to-end)', () => {
|
|||||||
id: 'sb',
|
id: 'sb',
|
||||||
url,
|
url,
|
||||||
kind: 'audio',
|
kind: 'audio',
|
||||||
quality: 'Best (MP3)'
|
quality: 'Best'
|
||||||
}
|
}
|
||||||
const options = {
|
const options = {
|
||||||
...DEFAULT_DOWNLOAD_OPTIONS,
|
...DEFAULT_DOWNLOAD_OPTIONS,
|
||||||
|
|||||||
Reference in New Issue
Block a user