Metadata-Version: 2.4
Name: fast-dlp
Version: 0.6.1
Summary: Fast YouTube downloader: up to 4K at full line speed, built-in merging without ffmpeg, plus Spotify, Yandex Music, SoundCloud, TikTok and more
Author: valkdz
License-Expression: MIT
Project-URL: Homepage, https://github.com/valkdzx/fast-dlp
Project-URL: Source, https://github.com/valkdzx/fast-dlp
Project-URL: Issues, https://github.com/valkdzx/fast-dlp/issues
Keywords: youtube,downloader,youtube-dl,yt-dlp,spotify,soundcloud,tiktok,video,music,mp3
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: End Users/Desktop
Classifier: Natural Language :: Russian
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

<p align="center">
  <img src="https://raw.githubusercontent.com/valkdzx/fast-dlp/main/assets/logo.svg" width="128" alt="fast-dlp">
</p>

<h1 align="center">fast-dlp</h1>

<p align="center">
  Быстрая альтернатива yt-dlp. Чистый Python, без зависимостей.<br>
  YouTube · Spotify · Яндекс Музыка · SoundCloud · TikTok · VK · Pinterest · Pornhub · Steam Workshop
</p>

- **Скорость:** 4K-видео на 345 МиБ скачивается за 7.3 с (~47 МиБ/с).
- **Без ffmpeg:** видео и звук склеиваются встроенным муксером меньше чем за секунду.
- **Музыка:** теги, обложки, тексты песен и ReplayGain пишутся сами.
- **Поиск:** один запрос сразу по всем источникам.
- **Экран выбора:** треки отмечаются в терминале, качаются параллельно.

---

## Установка

Нужен Python 3.9+.

```
pip install fast-dlp
```

Обновление: `pip install -U fast-dlp`. Чтобы команда была изолирована от остальных
пакетов, можно поставить через pipx: `pipx install fast-dlp`.

Из исходников:

```
git clone https://github.com/valkdzx/fast-dlp
cd fast-dlp
pip install -e .
```

Без установки: `py -m fast_dlp ...` или `fast-dlp.cmd ...` из папки проекта.

Необязательные программы:

- **ffmpeg** — только для MP3 и смешанных контейнеров;
- **Deno, Node.js или Bun** — только для возрастных видео через `login`.

## Примеры

```
fast-dlp https://youtu.be/VIDEO_ID                  # лучшее видео + звук
fast-dlp https://youtu.be/VIDEO_ID -q 1080          # не выше 1080p
fast-dlp https://youtu.be/VIDEO_ID -x               # только звук
fast-dlp https://youtu.be/VIDEO_ID --audio-format mp3
fast-dlp https://youtu.be/VIDEO_ID -F               # список форматов
fast-dlp https://youtu.be/VIDEO_ID -f 137+140       # конкретные itag
fast-dlp https://youtu.be/VIDEO_ID -g               # вывести прямые ссылки
fast-dlp https://open.spotify.com/track/TRACK_ID    # трек Spotify
fast-dlp https://open.spotify.com/album/ALBUM_ID    # альбом, экран выбора
fast-dlp "https://www.youtube.com/playlist?list=PLAYLIST_ID"
fast-dlp исполнитель - название                     # поиск по всем источникам
fast-dlp URL -N 32 -P D:\Video -o "{author} - {title}.{ext}"
fast-dlp web                                        # сайт-загрузчик на http://127.0.0.1:8000
```

---

## Сайт-загрузчик

```
fast-dlp web                         # http://127.0.0.1:8000
fast-dlp web --host 0.0.0.0 --port 8080   # доступ из локальной сети
```

Чёрная страница: вставляете ссылку на видео, выбираете параметры, нажимаете «Скачать».

Параметры:

- что скачать: видео со звуком, только звук или только видео;
- формат: MP4 (H.264 / AV1 + AAC) или WebM (VP9 + Opus); для звука M4A, Opus или MP3;
- качество от 144p до 4K, с размером каждого варианта;
- кодек и битрейт MP3;
- отрывок по времени;
- теги, обложка, ReplayGain;
- шаблон имени файла;
- число соединений с YouTube.

**Сервер ничего не сохраняет.** Файл передаётся потоком (HTTP streaming):

1. Сначала скачиваются только заголовки и оглавления форматов (`sidx` / `Cues`). По ним
   заранее рассчитывается весь файл: новый заголовок, порядок фрагментов и точный размер,
   поэтому браузер сразу видит `Content-Length` и показывает прогресс.
2. Фрагменты качаются с googlevideo параллельно, в ограниченном окне памяти (~80 МиБ на
   загрузку). На лету правятся номера дорожек и времена, и всё сразу пишется в ответ.
   Результат побайтно совпадает с файлом, который склеил бы встроенный муксер.
3. MP3 кодируется ffmpeg прямо из потока и передаётся частями (`Transfer-Encoding: chunked`).

**Подтверждение доставки.** После последнего байта сервер закрывает свою сторону соединения
и ждёт, пока браузер закроет свою. Браузер закрывает соединение только после того, как
прочитал всё, поэтому файл помечается «Доставлено» только при настоящем подтверждении.

Другие исходы:

- сброс соединения означает «Прервано», и ту же ссылку можно открыть снова;
- если браузер молчит 60 с, статус будет «Отправлено».

Ссылка на скачивание одноразовая и живёт час. Страница следит за статусом через
`/api/status`.

| Запрос | Что делает |
|---|---|
| `GET /api/info?url=` | название, длительность, все форматы с размерами |
| `POST /api/jobs` | параметры → токен, имя файла, точный размер |
| `GET /dl/<токен>` | сам файл, потоком |
| `GET /api/status?t=a,b` | состояние загрузок: передано байт, скорость, статус |

Замер: 4K-видео на 232 МиБ доходит до браузера за 3.8 с (~60 МиБ/с).

## Почему быстро

- **Один запрос к API.** Нет загрузки watch-страницы и base.js: InnerTube-клиент `VISIONOS`
  отдаёт прямые ссылки (до 4K, без подписи и без `n`-параметра) примерно за 0.25 с.
- **Параллельная загрузка.** Файл качается в 16 потоков, у каждого постоянное
  HTTPS-соединение.
- **Куски под скорость сети.** Каждое соединение замеряет свою скорость и берёт следующий
  кусок примерно на 2 секунды загрузки:
  - на 60 Мбит/с это ~1 МиБ;
  - на гигабите 8 МиБ, это потолок: googlevideo режет скорость на диапазонах больше ~10 МБ;
  - к концу файла куски дробятся, чтобы все соединения закончили вместе.
- **Сразу в готовый файл.** Видео и звук YouTube качаются не в `.part`-файлы с последующей
  склейкой. По оглавлениям форматов (`sidx` / `Cues`) итоговый файл рассчитывается заранее,
  и каждый кусок пишется прямо на своё место. После загрузки правятся только заголовки
  фрагментов (несколько КиБ). Каждый байт скачивается и пишется на диск один раз.
- **Без записи нулей.** Windows при увеличении файла физически пишет нули: 3 ГБ нулей
  перед загрузкой выглядят как «фоновая закачка». Файл создаётся разреженным (NTFS
  sparse), и его размер задаётся мгновенно.
- **MP3 без промежуточного файла.** Звук идёт из сети прямо в ffmpeg.
- **Без перезапуска.** Протухшую ссылку (HTTP 403) обновляет новый запрос к `/player`, и
  загрузка продолжается с того же места.

## IPv4 и IPv6

YouTube проверяет каждый адрес отдельно. У домашнего подключения общий IPv4-адрес, а у каждого
устройства свой IPv6, и Windows по умолчанию предпочитает IPv6. Если на IPv6-адрес YouTube
отвечает bot-check («Sign in to confirm you're not a bot»), fast-dlp сам переключает весь
процесс на IPv4 и запоминает это на 6 часов (`%LOCALAPPDATA%\fast-dlp\route.json`).
Переключается весь процесс сразу, потому что ссылки на поток привязаны к адресу, с которого
их запросили. Флаги `-4` / `-6` задают семейство адресов вручную.

## Возрастные видео

Если VISIONOS отвечает `LOGIN_REQUIRED`, fast-dlp переходит на клиент `TVHTML5` (Tizen,
версия `5.<дата>`) с OAuth-токеном аккаунта:

- client_id и client_secret берутся из бандла `youtube.com/tv` (кэш 6 ч);
- refresh token меняется на access token (кэш до истечения срока);
- `signatureCipher` и `n` расшифровываются через base.js: верхнеуровневые побочные эффекты
  вырезаются, а сборщик ссылок с `.set("alr","yes")` вызывается во внешнем JS-рантайме.

```
fast-dlp login                      # код устройства на google.com/device
fast-dlp URL --refresh-token TOKEN  # или готовый refresh token (сохраняется)
```

Токен можно передать и через переменную `FAST_DLP_REFRESH_TOKEN`.

## Spotify

```
fast-dlp https://open.spotify.com/track/TRACK_ID
fast-dlp https://open.spotify.com/playlist/PLAYLIST_ID -I 1-10,15
```

Звук берётся с YouTube, Spotify даёт только данные о треке. Ключи не нужны: данные (с ISRC)
приходят через бэкенд CDisc (`https://2281273.xyz/spotify?url=…&beta=true`, сырой JSON
Spotify). Свой адрес задаёт переменная `FAST_DLP_SPOTIFY_BACKEND`.

Можно использовать и свои ключи Web API: приложение создаётся на
https://developer.spotify.com/dashboard, ключи вводятся командой `fast-dlp spotify-setup`
или задаются переменными `FAST_DLP_SPOTIFY_ID` / `FAST_DLP_SPOTIFY_SECRET`. С ключами
Spotify участвует и в общем поиске.

Источники данных перебираются по порядку: свои ключи → бэкенд → публичная embed-страница
(без ISRC).

Как трек находится на YouTube Music:

1. По **ISRC**. Результат принимается, только если длительность отличается не больше чем
   на 5 с и совпадает название или артист.
2. Поиск «артист + название» с фильтром «песни»: оценивается сходство названия, артиста и
   разница в длительности (не больше 15 с).
3. То же с фильтром «видео»: клипы и перезаливы.

У треков плейлиста из бэкенда нет обложки и даты, поэтому они догружаются перед
скачиванием каждого трека. Редакционные плейлисты Spotify (`37i9…`) берутся с
embed-страницы.

## Другие источники

| Источник | Что умеет | Вход |
|---|---|---|
| **Яндекс Музыка** | трек, альбом, плейлист. С токеном файл берётся прямо с Яндекса (MP3 320 при Плюсе); без токена трек ищется на YouTube | `fast-dlp yandex-login`, необязательно |
| **VK Музыка** | трек, плейлист, альбом. HLS с AES-128 расшифровывается встроенным AES, MP3 достаётся из MPEG-TS без перекодирования | `fast-dlp vk-setup` |
| **SoundCloud** | трек, сет (плейлист/альбом), теги и обложка 500×500 | нет |
| **TikTok** | видео без водяного знака (h264, `--codec hevc` — h265), фото-посты, `-x` — оригинальный звук, короткие ссылки | нет |
| **Pinterest** | видео, истории, картинки в оригинале | нет |
| **Pornhub** | видео: лучший MP4 не выше `-q`, качается параллельно по диапазонам (сайт режет скорость на одно соединение); если MP4 нет, берётся HLS. Где страна требует настоящую проверку возраста, fast-dlp сообщает об этом и ничего не обходит | нет |
| **Steam Workshop** | предмет или коллекция. Старые предметы — по прямой ссылке, SteamPipe — через `steamcmd`, который скачивается сам. Для платных игр: `--steam-user ЛОГИН`, пароль и Steam Guard вводятся в steamcmd один раз | для платных игр |
| **Любой файл / .m3u8** | прямые ссылки — в N потоков по диапазонам; HLS — сегменты параллельно, с AES-128 | нет |

Токены лежат в `%APPDATA%\fast-dlp\` (`yandex.json`, `vk.json`); можно задать и через
`FAST_DLP_YANDEX_TOKEN` / `FAST_DLP_VK_TOKEN`.

AES написан на чистом Python и сверен с векторами FIPS-197 и NIST SP 800-38A. Один поток даёт
~1.6 МиБ/с, поэтому большие объёмы расшифровываются в пуле процессов на всех ядрах.

## Поиск сразу по всем источникам

```
fast-dlp исполнитель - название
```

Если в аргументах нет ни одной ссылки, они считаются поисковым запросом. YouTube Music,
Spotify, Яндекс Музыка, YouTube и SoundCloud опрашиваются параллельно, весь поиск занимает
около 1 с. Результаты открываются в экране выбора: у каждого видно источник и длительность,
отмечен лучший. С `-y` или без терминала сразу скачивается лучший результат.

## Выбор треков в терминале

Ссылка на альбом или плейлист открывает экран выбора. Язык экрана и таблицы загрузок берётся из системы: русский на русской Windows/Linux/macOS, английский на остальных. Задать вручную: `--lang ru` / `--lang en` или переменная `FAST_DLP_LANG`.

```
 Spotify · «Альбом» · 12   выбрано 11 из 12
 ─ Треки ──────────────────────────────────────────────
 ▸ [x]  1  Трек 1 — Исполнитель                    3:41
   [ ]  2  Трек 2 — Исполнитель                    4:05
 ─ Параметры ──────────────────────────────────────────
   Формат          ◀ mp3 ▶
   Качество MP3    ◀ 320k ▶
   Параллельно     ◀ 4 ▶
   Теги / Обложка  ◀ да ▶
   Уже скачанные   ◀ пропускать ▶
```

| Клавиша | Действие |
|---|---|
| ↑ ↓, Home/End, PgUp/PgDn | перемещение |
| Пробел | отметить / снять трек |
| ← → | на параметре: менять значение; на треках: листать страницами |
| A / N / I | все / ничего / инвертировать |
| Enter | скачать отмеченное |
| Esc / Q | отмена |

После Enter открывается живая таблица: у каждого трека свой статус (в очереди / качается /
готово / уже было / ошибка), внизу последние строки лога.

`-y` качает всё без экрана выбора, `--select` открывает его всегда. С `-I` или без
терминала экран не открывается.

## Плейлисты и альбомы

```
fast-dlp "https://www.youtube.com/playlist?list=PLAYLIST_ID"
fast-dlp https://open.spotify.com/album/ALBUM_ID
fast-dlp https://open.spotify.com/playlist/PLAYLIST_ID -I 1-10,15   # только эти номера
```

Плейлист YouTube читается через InnerTube `/browse` со всеми продолжениями (913 видео за
6.8 с). Файлы раскладываются в папку `{playlist}/` с номерами `{index}`. Элементы качаются
по 4 сразу (`-J`): альбом из 14 треков в MP3 — за 18 с вместо 52. Уже скачанные файлы
пропускаются ещё до запроса к YouTube, `-w` качает всё заново.

## Синхронизация плейлистов

```
fast-dlp sync https://open.spotify.com/playlist/PLAYLIST_ID
fast-dlp sync "https://www.youtube.com/playlist?list=PLAYLIST_ID" --sync-dir D:\Music\Mix -x
```

Новые треки докачиваются, убранные из плейлиста удаляются. Удаляются только файлы, которые
создал sync (они записаны в `.fast-dlp-sync.json`); `--sync-keep` не удаляет ничего. Рядом
пишется `<плейлист>.m3u8`. Повторный запуск без изменений занимает меньше секунды — удобно
для планировщика.

## Отрывок видео без ffmpeg

```
fast-dlp URL --section 3:40-5:10
fast-dlp URL --section 1:00:00-                     # с часа до конца
fast-dlp "https://youtu.be/VIDEO_ID?t=220" --section t   # с отметки t= в ссылке
```

По оглавлению формата (`sidx` в MP4, `Cues` в WebM) скачиваются только байты нужного
отрывка, а муксер переносит время к нулю. Отрывок 30 с из 4-минутного 1080p — 13 МБ вместо
80. Резка идёт по ключевым кадрам, как у `ffmpeg -c copy`, без перекодирования.

## Тексты песен и громкость

- **Тексты** берутся из LRCLIB, синхронизированные (LRC), если есть. Для музыки включено по
  умолчанию, для видео — флагом `--lyrics`. Вшиваются в MP3 (`USLT`), M4A (`©lyr`) и
  WebM/MKV. `--write-lyrics` сохраняет ещё и `.lrc`, `--no-lyrics` отключает.
- **ReplayGain** без анализа файла: YouTube сам сообщает громкость трека, из неё получается
  усиление ReplayGain 2.0 (опора −18 LUFS). `--no-replaygain` отключает.

## Субтитры, главы, эфиры (YouTube)

```
fast-dlp URL --embed-subs --sub-langs en,ru
fast-dlp URL --write-subs --write-auto-subs --sub-langs all --sub-format vtt
fast-dlp URL --embed-chapters --sponsorblock-mark all
fast-dlp URL --write-info-json --write-description --write-thumbnail --write-comments
fast-dlp LIVE_URL --live-duration 600              # записать эфир 10 минут
fast-dlp LIVE_URL --live-from-start                # с начала окна DVR
fast-dlp "https://www.youtube.com/@CHANNEL"        # канал
fast-dlp "ytsearch10:запрос" -x                    # поиск по YouTube
fast-dlp URL --download-archive done.txt           # не качать уже скачанное
fast-dlp URL --proxy http://user:pass@host:8080
```

- **Субтитры** — ручные и автоматические, встраиваются без ffmpeg (MP4 — `tx3g`, MKV — SRT).
- **Главы** — из маркеров плеера или таймкодов в описании; **SponsorBlock** размечает
  рекламу, интро и т. д.
- **Эфиры** записываются через HLS, пока трансляция не кончится или не нажат Ctrl+C.
- **Запасной HLS** до 4K включается сам, если прямые ссылки не работают (`--hls` —
  принудительно).
- **Побочные файлы:** `.info.json`, `.description`, `.jpg`, `.comments.json`.
- **Шаблон имени:** `{title} {id} {uploader} {channel} {upload_date} {year} {duration}
  {height} {resolution} {playlist} {playlist_index} {album} {artist} {track_number} {site}
  {ext}` и другие. Работает форматирование Python: `{playlist_index:03d}`. Неизвестное поле
  становится `NA`, `/` создаёт папки.

## Теги и обложки

Теги пишутся без ffmpeg:

- **MP4/M4A** — атомы iTunes (`©nam ©ART ©alb aART ©day trkn ©cmt covr`);
- **WebM** — Matroska `Tags`, обложка во вложении;
- **MP3** — ID3v2.3, обложка во фрейме APIC.

Обложка по умолчанию встраивается только в аудио; `--embed-thumbnail` — и в видео,
`--no-cover` / `--no-metadata` отключают.

## MP3

```
fast-dlp URL --audio-format mp3                 # 320k CBR
fast-dlp URL --audio-format mp3 --audio-quality V0
```

YouTube не отдаёт MP3, поэтому нужен ffmpeg (libmp3lame): трёхминутный трек кодируется за
~2 с. `--audio-format opus` / `m4a` берут поток без перекодирования.

## Склейка без ffmpeg

Обычно отдельной склейки нет: файл собирается прямо при загрузке (см. «Почему быстро»).
Встроенный муксер работает, когда нужны встроенные субтитры или главы, с `--keep-parts`
и для запасного пути HLS. Он только копирует данные, ничего не перекодирует:

- **WebM + WebM** (vp9/av01 + opus): кластеры чередуются по времени, Cues и SeekHead
  пишутся заново.
- **MP4 + M4A** (фрагментированные): один `moov` с двумя дорожками, `moof+mdat`
  чередуются по времени.

| Что | Размер | Время |
|---|---|---|
| 4K60 vp9 + opus → .webm | 1.3 ГБ | 0.90 с |
| 4K vp9 + opus → .webm | 345 МиБ | 0.14 с |
| 1080p60 avc1 + aac → .mp4 | 255 МиБ | 0.11 с |

Потоки побитово совпадают с исходными. ffmpeg нужен только для смешанных контейнеров
(`-f 398+251`) или с `--merger ffmpeg`.

## Тесты

```
py -m unittest discover -s tests -t .                              # 113 тестов без сети, ~4 с
set FAST_DLP_NETWORK=1 && py -m unittest discover -s tests -t .    # + живые проверки
```

## Устройство

| Файл | Что делает |
|---|---|
| `fast_dlp/innertube.py` | клиенты VISIONOS / TVHTML5, запрос `/player` |
| `fast_dlp/oauth.py` | вход по коду устройства, refresh |
| `fast_dlp/cipher.py` | подготовка base.js, решатель sig/n |
| `fast_dlp/extractor.py` | VISIONOS → (возрастное) → TV + OAuth |
| `fast_dlp/download.py` | многопоточная загрузка по диапазонам |
| `fast_dlp/formats.py` | выбор форматов, таблица `-F` |
| `fast_dlp/mux.py` | склейка WebM и fMP4, теги и обложки |
| `fast_dlp/mp3.py` | MP3 через ffmpeg, свой ID3v2.3 |
| `fast_dlp/playlist.py` | плейлисты, каналы, поиск YouTube |
| `fast_dlp/spotify.py` | Spotify: свои ключи / бэкенд CDisc / embed-страница |
| `fast_dlp/ytmusic.py` | поиск YouTube Music и сопоставление трека |
| `fast_dlp/sources/` | Яндекс, VK, SoundCloud, TikTok, Pinterest, Pornhub, Steam, любые файлы |
| `fast_dlp/hls.py` | HLS, AES-128, запись эфиров |
| `fast_dlp/ytmeta.py` | дата, главы, комментарии, субтитры, SponsorBlock |
| `fast_dlp/search.py` | поиск по всем источникам |
| `fast_dlp/section.py` | отрывок по времени |
| `fast_dlp/sync.py` | синхронизация с плейлистом |
| `fast_dlp/lyrics.py` | тексты LRCLIB, ReplayGain |
| `fast_dlp/postprocess.py` | шаблон имени, побочные файлы, архив |
| `fast_dlp/aes.py` | AES-128-CBC на чистом Python |
| `fast_dlp/ts.py` | разбор MPEG-TS |
| `fast_dlp/tui.py` | экран выбора и таблица загрузок |
| `fast_dlp/stream.py` | файл потоком: план по индексам, параллельные фрагменты, правка на лету |
| `fast_dlp/web/` | сайт: сервер, одноразовые ссылки, подтверждение доставки, страница |
| `fast_dlp/cli.py` | командная строка |

Кэш: `%LOCALAPPDATA%\fast-dlp`. Токены и настройки: `%APPDATA%\fast-dlp`.

## Лицензия

MIT, см. [LICENSE](LICENSE).
