11 KiB
4K Support Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Add 2160p (4K) and 1440p (2K) as selectable download quality tiers, and update marketing copy that currently caps the advertised resolution at 1080p.
Architecture: The download pipeline already resolves quality generically — the probe (src/lib/ytdlp-probe.ts) filters a fixed QUALITIES list against the source video's max height, and the yt-dlp arg builder (src/lib/ytdlp.ts) turns any quality string into a height<=?N format selector. No pipeline logic changes; only the QUALITIES list, its test coverage, and static marketing copy change.
Tech Stack: TypeScript, Jest, next-intl (messages/*.json).
Global Constraints
- Spec:
docs/superpowers/specs/2026-08-11-4k-support-design.md - Add exactly two new tiers:
2160pand1440p(no other resolutions). - No changes to
src/lib/ytdlp.ts,worker/processor.ts, or the probe's filtering logic — both are already generic over the quality string. - Marketing copy change is a literal
1080p→4Kswap; wording around it stays otherwise identical. - No changes to
src/lib/downloader-platforms.ts— it carries no resolution claim.
Task 1: Add 2160p/1440p to the quality list and probe test coverage
Files:
- Modify:
src/lib/ytdlp-options.ts:2 - Modify:
src/lib/__tests__/ytdlp-probe.test.ts(add a new test in theparseProbeOutputdescribe block, after the existing "detects a video with multiple qualities" test at line 40)
Interfaces:
-
Consumes: nothing new.
-
Produces:
QUALITIES(fromsrc/lib/ytdlp-options.ts) now includes'2160p'and'1440p', ordered highest-to-lowest:['best', '2160p', '1440p', '1080p', '720p', '480p', '360p'].ytdlp-probe.tsandSubmitForm.tsxboth importQUALITIESby name (unchanged) and iterate it in array order — later tasks and existing consumers see the two new entries automatically. -
Step 1: Write the failing test
Add this test to src/lib/__tests__/ytdlp-probe.test.ts, right after the it('detects a video with multiple qualities and subtitles', ...) block (after line 40):
it('includes 4K and 2K when the source video offers them', () => {
const result = parseProbeOutput(json({
formats: [
{ height: 1080, vcodec: 'avc1' },
{ height: 1440, vcodec: 'avc1' },
{ height: 2160, vcodec: 'avc1' },
],
}))
expect(result.availableQualities).toEqual(['best', '2160p', '1440p', '1080p'])
})
- Step 2: Run test to verify it fails
Run: npm test -- ytdlp-probe.test.ts
Expected: FAIL — availableQualities is ['best', '1080p'] because QUALITIES does not yet contain '2160p'/'1440p'.
- Step 3: Write minimal implementation
In src/lib/ytdlp-options.ts, replace line 2:
export const QUALITIES = ['best', '2160p', '1440p', '1080p', '720p', '480p', '360p'] as const
- Step 4: Run test to verify it passes
Run: npm test -- ytdlp-probe.test.ts
Expected: PASS — all tests in the file pass, including the existing 1080p-source case ('caps available qualities to the max height found' and 'detects a video with multiple qualities and subtitles'), which stay unaffected since 2160p/1440p are correctly filtered out when maxHeight is below them.
- Step 5: Commit
git add src/lib/ytdlp-options.ts src/lib/__tests__/ytdlp-probe.test.ts
git commit -m "feat: add 4K and 2K quality tiers"
Task 2: Cover the 2160p format selector in the yt-dlp arg builder tests
Files:
- Modify:
src/lib/__tests__/ytdlp.test.ts(add a new test after the existingit('adds a height filter to -f when quality is not "best"', ...)block at line 41)
Interfaces:
-
Consumes:
buildYtdlpArgsfromsrc/lib/ytdlp.ts(unchanged signature:(params: YtdlpParams) => string[]),basefixture object defined at the top of the test file. -
Produces: nothing new — this task only adds test coverage confirming
buildYtdlpArgsalready handlesquality: '2160p'correctly (via its existing genericquality.replace('p', '')logic). -
Step 1: Write the failing-if-broken test
Add this test to src/lib/__tests__/ytdlp.test.ts, right after the it('adds a height filter to -f when quality is not "best"', ...) block (after line 41):
it('adds a height filter to -f for 4K quality', () => {
const args = buildYtdlpArgs({ ...base, quality: '2160p' })
const idx = args.indexOf('-f')
expect(idx).toBeGreaterThan(-1)
expect(args[idx + 1]).toContain('height<=?2160')
})
- Step 2: Run test to verify it passes
Run: npm test -- ytdlp.test.ts
Expected: PASS immediately — buildYtdlpArgs already builds the filter generically from the quality string, so no source change is needed. This step confirms that generic behavior explicitly with a regression test.
- Step 3: Commit
git add src/lib/__tests__/ytdlp.test.ts
git commit -m "test: cover 4K format selector in buildYtdlpArgs"
Task 3: Update marketing copy in all 4 locales to advertise 4K
Files:
- Modify:
messages/en.json(lines 52, 60, 79, 117, 166) - Modify:
messages/fr.json(lines 52, 60, 79, 117, 166) - Modify:
messages/es.json(lines 52, 60, 79, 117, 166) - Modify:
messages/it.json(lines 52, 60, 79, 117, 166)
Interfaces:
- Consumes: nothing — these are static i18n strings rendered by
HowItWorksSection.tsx,FaqAccordion.tsx, and the platform FAQ pages (via next-intl), none of which need changes since only string content changes, not keys. - Produces: nothing consumed by later tasks.
Each file has exactly 5 occurrences of the literal substring 1080p, all of which should become 4K, at these keys:
-
home.formatsDesc -
howItWorks.step2Desc -
faq.a4 -
downloaderPages.youtube.faqA2 -
downloaderPages.vimeo.faqA2 -
Step 1: Update
messages/en.json
Current (lines 52, 60, 79, 117, 166):
"formatsDesc": "mp4, mp3, webm, mkv -- up to 1080p"
"step2Desc": "Pick MP4, MP3, WebM or MKV and the quality you want, up to 1080p",
"a4": "Video in MP4, WebM or MKV, or audio-only in MP3, up to 1080p depending on the source video.",
"faqA2": "Up to 1080p, depending on the resolutions available for that specific video."
"faqA2": "It depends on what the uploader made available, up to 1080p in most cases."
Replace every 1080p with 4K (5 occurrences in this file):
"formatsDesc": "mp4, mp3, webm, mkv -- up to 4K"
"step2Desc": "Pick MP4, MP3, WebM or MKV and the quality you want, up to 4K",
"a4": "Video in MP4, WebM or MKV, or audio-only in MP3, up to 4K depending on the source video.",
"faqA2": "Up to 4K, depending on the resolutions available for that specific video."
"faqA2": "It depends on what the uploader made available, up to 4K in most cases."
- Step 2: Update
messages/fr.json
Current (lines 52, 60, 79, 117, 166):
"formatsDesc": "mp4, mp3, webm, mkv — jusqu'en 1080p"
"step2Desc": "Sélectionnez MP4, MP3, WebM ou MKV et la qualité souhaitée, jusqu'en 1080p",
"a4": "Vidéo en MP4, WebM ou MKV, ou audio seul en MP3, jusqu'en 1080p selon la vidéo source.",
"faqA2": "Jusqu'à 1080p, selon les résolutions disponibles pour cette vidéo en particulier."
"faqA2": "Cela dépend de ce que l'auteur a mis à disposition, jusqu'à 1080p dans la plupart des cas."
Replace every 1080p with 4K (5 occurrences in this file):
"formatsDesc": "mp4, mp3, webm, mkv — jusqu'en 4K"
"step2Desc": "Sélectionnez MP4, MP3, WebM ou MKV et la qualité souhaitée, jusqu'en 4K",
"a4": "Vidéo en MP4, WebM ou MKV, ou audio seul en MP3, jusqu'en 4K selon la vidéo source.",
"faqA2": "Jusqu'à 4K, selon les résolutions disponibles pour cette vidéo en particulier."
"faqA2": "Cela dépend de ce que l'auteur a mis à disposition, jusqu'à 4K dans la plupart des cas."
- Step 3: Update
messages/es.json
Current (lines 52, 60, 79, 117, 166):
"formatsDesc": "mp4, mp3, webm, mkv — hasta 1080p"
"step2Desc": "Selecciona MP4, MP3, WebM o MKV y la calidad deseada, hasta 1080p",
"a4": "Vídeo en MP4, WebM o MKV, o solo audio en MP3, hasta 1080p según el vídeo de origen.",
"faqA2": "Hasta 1080p, según las resoluciones disponibles para ese vídeo en concreto."
"faqA2": "Depende de lo que haya puesto a disposición quien lo subió, hasta 1080p en la mayoría de los casos."
Replace every 1080p with 4K (5 occurrences in this file):
"formatsDesc": "mp4, mp3, webm, mkv — hasta 4K"
"step2Desc": "Selecciona MP4, MP3, WebM o MKV y la calidad deseada, hasta 4K",
"a4": "Vídeo en MP4, WebM o MKV, o solo audio en MP3, hasta 4K según el vídeo de origen.",
"faqA2": "Hasta 4K, según las resoluciones disponibles para ese vídeo en concreto."
"faqA2": "Depende de lo que haya puesto a disposición quien lo subió, hasta 4K en la mayoría de los casos."
- Step 4: Update
messages/it.json
Current (lines 52, 60, 79, 117, 166):
"formatsDesc": "mp4, mp3, webm, mkv — fino a 1080p"
"step2Desc": "Seleziona MP4, MP3, WebM o MKV e la qualità desiderata, fino a 1080p",
"a4": "Video in MP4, WebM o MKV, oppure solo audio in MP3, fino a 1080p a seconda del video originale.",
"faqA2": "Fino a 1080p, in base alle risoluzioni disponibili per quel video specifico."
"faqA2": "Dipende da cosa ha reso disponibile chi ha caricato il video, fino a 1080p nella maggior parte dei casi."
Replace every 1080p with 4K (5 occurrences in this file):
"formatsDesc": "mp4, mp3, webm, mkv — fino a 4K"
"step2Desc": "Seleziona MP4, MP3, WebM o MKV e la qualità desiderata, fino a 4K",
"a4": "Video in MP4, WebM o MKV, oppure solo audio in MP3, fino a 4K a seconda del video originale.",
"faqA2": "Fino a 4K, in base alle risoluzioni disponibili per quel video specifico."
"faqA2": "Dipende da cosa ha reso disponibile chi ha caricato il video, fino a 4K nella maggior parte dei casi."
- Step 5: Verify no stray
1080premains in any locale file
Run: grep -rn "1080" messages/
Expected: no output (empty result).
- Step 6: Run the full test suite
Run: npm test
Expected: PASS — locale copy changes don't affect any test, and Tasks 1–2's new tests still pass.
- Step 7: Commit
git add messages/en.json messages/fr.json messages/es.json messages/it.json
git commit -m "docs: advertise 4K support in marketing copy across all locales"