Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c1e6c1e
Add 1440p/2160p download options; add Audit.md and knowledge.md
Sucotasch Aug 11, 2026
dfea818
Apply audit fixes F1-F8; show only available qualities; warn on large…
Sucotasch Aug 11, 2026
88904f6
Add download-in-parts (15-min parts, toggle) and adaptive memory warn…
Sucotasch Aug 11, 2026
3f55299
Fix high-res capture: verify served resolution, re-apply quality, the…
Sucotasch Aug 11, 2026
fa6d57e
Force high-res via native quality menu (remove theater toggle); fix m…
Sucotasch Aug 11, 2026
d39ecd4
Fix frozen-frame capture: drive hops/completion off per-track buffere…
Sucotasch Aug 11, 2026
c382377
Fix high-res quality: never call setPlaybackQualityRange after a succ…
Sucotasch Aug 11, 2026
cd26f9e
Document known external conflict: quality-forcing extensions (YouTube…
Sucotasch Aug 11, 2026
441017a
Clean diagnostics behind DEBUG flag; extract pure helpers to lib/form…
Sucotasch Aug 11, 2026
e4a197f
Document: slow captures on one repeatedly-downloaded video are YouTub…
Sucotasch Aug 11, 2026
b00f3d6
Knowledge: confirm slow-capture case fully recovered after a pause (Y…
Sucotasch Aug 11, 2026
f164d1b
Bump to 1.5.0; MP3 at 320 kbps (was 192k); note bitrate in READMEs
Sucotasch Aug 11, 2026
62b20a6
Knowledge: exact cuts at 2160p are slow by design (4x pixels, single-…
Sucotasch Aug 11, 2026
368e3c6
Make MP3 bitrate selectable in the menu (192k default = original, 320…
Sucotasch Aug 11, 2026
5870446
Keep local working docs/tooling out of the repo (PR hygiene): .agents…
Sucotasch Aug 11, 2026
adc7d83
Stability round: all qualities via native menu (SABR ignores the JS A…
Sucotasch Aug 14, 2026
2918d3d
Strip invisible format/control chars in safeName ('Invalid filename' …
Sucotasch Aug 17, 2026
4af094d
Merge author's v1.4.3 subtitles fix (in-site navigation) into the PR …
Sucotasch Aug 17, 2026
fa70a14
Merge origin/main (author's v1.4.3) so the PR head contains it and Gi…
Sucotasch Aug 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,14 @@ package-lock.json
.DS_Store
Thumbs.db
*~

# Local diagnostics (user-saved console copies)
log.txt
Log.txt
Error.txt

# Local working docs / tooling — not part of the extension (kept out of PRs)
.agents/
ReviewPrompt.txt
knowledge.md
Audit.md
21 changes: 17 additions & 4 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ No `yt-dlp`, no third‑party sites or servers: everything runs locally in your

## Features

- **Video** — 720p / 1080p as `.mp4` (video + audio).
- **Audio** — `.mp3` (audio track only).
- **Video** — 720p / 1080p / 1440p / 2160p as `.mp4` (video + audio; 1440p and 2160p are
shown only when the video actually supports them).
- **Audio** — `.mp3` (audio track only; bitrate 192/320 kbps, selectable).
- **Clip selection** — "start — end" fields in the menu (default `0:00:00` … full length).
**Only the selected range is fetched**, not the whole video: e.g. 10 seconds out of an
hour-long video download in a couple of seconds.
Expand All @@ -26,6 +27,10 @@ No `yt-dlp`, no third‑party sites or servers: everything runs locally in your
whatever it displays is what gets saved.
- **Video format** — "Fast" (VP9 in mp4, no re‑encoding, seconds) or "H.264" (re‑encode for
compatibility with older players, slow).
- **Download in parts** — long videos can be saved in parts (~15 minutes per file): the
"По частям" toggle in the menu, or an adaptive warning that offers splitting when the
estimated capture needs too much memory (resolution, duration and available RAM are
taken into account).
- **Auto‑disables Autoplay** — the extension turns off YouTube's "Autoplay next" so the next
video won't start on its own.

Expand All @@ -50,10 +55,13 @@ No `yt-dlp`, no third‑party sites or servers: everything runs locally in your
1. Click the ▽ button in the player to open the menu.
2. Optionally set the **start** and **end** of a clip (defaults to the whole video).
3. Choose what to download:
- **Video** → `1080p` or `720p`;
- **Video** → `2160p`, `1440p`, `1080p` or `720p` (whichever are available);
- **Audio** → `MP3`;
- **Subtitles** → `.txt`.
4. For video you can switch the **Format**: "Fast" (default) or "H.264".
4. For video you can switch the **Format**: "Fast" (default) or "H.264", and enable
**"По частям"** — long videos are then saved in ~15-minute parts (files named
"… (part 1 of N)"). When a capture is estimated to need too much memory, the extension
itself offers downloading by parts.
5. Progress is shown in a toast; the finished file is saved via the browser's normal download.

## How it works
Expand All @@ -78,6 +86,11 @@ extension hooks in where the data has already been decrypted and split into trac

- Capture works by seeking through the buffer, so for very long videos it takes time
proportional to the length.
- 1440p and 2160p are offered only when the video supports them. Files at those resolutions
are huge: capture and muxing need lots of memory and time, and long 4K videos may hit the
capture limits — prefer downloading fragments for 4K.
- For long videos use **"По частям"**: each part is captured separately, so neither the
capture time limit nor memory accumulation is hit.
- "H.264" and `.mp3` re‑encode via `ffmpeg.wasm` (single‑threaded), which is noticeably slower
than the fast remux — up to a few minutes on long videos.
- In "Fast" mode the `.mp4` contains VP9/Opus codecs — it plays in Chrome, VLC and modern
Expand Down
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@

## Возможности

- **Видео** — 720p / 1080p в `.mp4` (видео + звук).
- **Аудио** — `.mp3` (только звуковая дорожка).
- **Видео** — 720p / 1080p / 1440p / 2160p в `.mp4` (видео + звук; 1440p и 2160p
показываются, только если доступны у ролика).
- **Аудио** — `.mp3` (только звуковая дорожка; битрейт 192/320 kbps на выбор).
- **Выбор фрагмента** — поля «начало — конец» в меню (по умолчанию `0:00:00` … полная длина
ролика). Загружается **только выбранный отрезок**, а не всё видео целиком: например,
10 секунд из середины часового ролика скачиваются за пару секунд.
Expand All @@ -28,6 +29,10 @@
сохраняется тот, который показан.
- **Формат видео** — «Быстро» (VP9 в mp4 без перекодирования, секунды) или «H.264»
(перекодирование для совместимости со старыми плеерами, медленно).
- **Скачивание по частям** — длинные ролики можно сохранять частями (~15 минут на файл):
переключатель «По частям» в меню, либо адаптивное предупреждение, которое само
предложит разбиение, если оценка захвата требует слишком много памяти (учитываются
разрешение, длительность и доступная RAM).
- **Авто-отключение автовоспроизведения** — расширение само выключает «Автовоспроизведение»
YouTube, чтобы следующий ролик не запускался сам.

Expand All @@ -53,10 +58,13 @@
1. Нажмите кнопку ▽ в плеере — откроется меню.
2. При необходимости задайте **начало** и **конец** фрагмента (по умолчанию — весь ролик).
3. Выберите, что скачать:
- **Видео** → `1080p` или `720p`;
- **Видео** → `2160p`, `1440p`, `1080p` или `720p` (доступные варианты);
- **Аудио** → `MP3`;
- **Субтитры** → `.txt`.
4. Для видео можно переключить **Формат**: «Быстро» (по умолчанию) или «H.264».
4. Для видео можно переключить **Формат**: «Быстро» (по умолчанию) или «H.264», а также
включить **«По частям»** — тогда длинный ролик сохранится частями по ~15 минут
(файлы «… (part 1 of N)»). Если оценка захвата требует много памяти, расширение само
предложит скачать по частям.
5. Прогресс отображается во всплывающем окне; готовый файл сохраняется через стандартную
загрузку браузера.

Expand All @@ -82,6 +90,11 @@ YouTube в вебе раздаёт HD не одним файлом, а по пр

- Захват идёт через перемотку буфера, поэтому для очень длинных роликов занимает время,
пропорциональное длине.
- Разрешения 1440p/2160p доступны, только если ролик их поддерживает. Файлы таких
разрешений очень велики: захват и муксинг требуют много памяти и времени, а длинные
4K-ролики могут упираться в лимиты захвата — для 4K лучше скачивать фрагменты.
- Для длинных роликов используйте **«По частям»**: каждая часть захватывается отдельно,
поэтому не упирается ни в лимит времени захвата, ни в накопление памяти.
- Режим «H.264» и `.mp3` перекодируют средствами `ffmpeg.wasm` (однопоточный) — это заметно
медленнее быстрой склейки, вплоть до нескольких минут на длинных видео.
- В режиме «Быстро» файл `.mp4` содержит кодеки VP9/Opus — он открывается в Chrome, VLC и
Expand Down
49 changes: 46 additions & 3 deletions extension/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,54 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
return true; // async
}

if (msg.t === 'ytdl-mem') {
// Adaptive capture-size warning needs real available RAM. chrome.system.* is not
// available in content scripts, so the UI asks here. Bytes → MB.
try {
chrome.system.memory.getInfo((info) => {
sendResponse({
ok: true,
capacity: Math.round(info.capacity / 1048576),
free: Math.round(info.availableCapacity / 1048576),
});
});
return true; // async
} catch (e) {
sendResponse({ ok: false, error: String(e) });
}
}

if (msg.t === 'ytdl-save') {
// Offscreen finished muxing and handed us a blob URL to save.
// Offscreen finished muxing and handed us a blob URL to save. downloads.download
// resolves when the download STARTS; the blob must stay alive until the browser
// process finishes reading it, so we revoke it on completion (or on failure, or a
// generous fallback) instead of on a fixed timer.
const isBlob = /^blob:/.test(msg.url);
chrome.downloads.download({ url: msg.url, filename: msg.filename, saveAs: false })
.then((id) => sendResponse({ ok: true, id }))
.catch((e) => sendResponse({ ok: false, error: String(e) }));
.then((id) => {
if (isBlob) {
let done = false;
function revoke() {
if (done) return;
done = true;
chrome.downloads.onChanged.removeListener(onChanged);
chrome.runtime.sendMessage({ t: 'ytdl-revoke', url: msg.url }).catch(() => {});
}
function onChanged(delta) {
if (delta.id !== id) return;
if (delta.state && (delta.state.current === 'complete' || delta.state.current === 'interrupted')) {
revoke();
}
}
chrome.downloads.onChanged.addListener(onChanged);
setTimeout(revoke, 10 * 60 * 1000); // belt-and-braces if onChanged never fires
}
sendResponse({ ok: true, id });
})
.catch((e) => {
if (isBlob) chrome.runtime.sendMessage({ t: 'ytdl-revoke', url: msg.url }).catch(() => {});
sendResponse({ ok: false, error: String(e) });
});
return true; // async
}
});
Loading