Skip to main content

CLI tool to download YouTube videos at maximum quality for video editing

Project description

vidgrab

PT-BR | EN

Python License Version Lint CodeQL Coverage Last commit

CLI para baixar vídeos do YouTube na maior qualidade técnica disponível — streams de vídeo e áudio separados (DASH), mesclados via FFmpeg sem nenhum reencode. Feito para quem usa vídeo como material bruto em edição.


PT-BR


Como funciona

A maioria das ferramentas de download aplica reencode para juntar vídeo e áudio — o que degrada a qualidade e desperdiça tempo. O vidgrab faz diferente:

YouTube  →  stream de vídeo (H.264 / VP9 / AV1)  ─┐
         →  stream de áudio (AAC / Opus)           ─┴→  FFmpeg mux  →  arquivo final

Os dois streams são baixados separadamente no formato DASH (maior qualidade disponível) e mesclados em modo cópia — sem recodificação, sem perda de qualidade.


Funcionalidades

Funcionalidade Detalhe
Downloads paralelos Até 8 simultâneos via --workers
🔁 Retry inteligente Backoff exponencial em rate-limits (até 5 tentativas)
🔍 Dry run Veja título, resolução e tamanho antes de baixar
Resume automático Downloads interrompidos são retomados de onde pararam
📋 Batch download Arquivo .txt com uma URL por linha
🎬 Playlists Expande e baixa todos os vídeos de uma playlist
📁 Skip inteligente Detecta arquivo existente pelo ID e pula automaticamente
📄 Metadados JSON Sidecar .json com título, canal, data, tags e mais
🔒 Conteúdo restrito Suporte a cookies (Netscape) para vídeos com age-gate
⚙️ Config file Defaults pessoais em ~/.config/vidgrab/config.toml
🏷 Nomes previsíveis {data}-{slug}-{video_id}.{ext} em todo download
⚠️ Aviso de licença Alerta quando o vídeo não é Creative Commons

Dependências externas

Ferramenta Para quê Como instalar
Python 3.11+ Runtime python.org
ffmpeg Mesclar streams de vídeo e áudio Veja abaixo
yt-dlp Engine de download Instalado automaticamente via Poetry
Deno (opcional) Acesso a todos os formatos do YouTube, incluindo 4K/HDR Veja abaixo
Instalando o ffmpeg

Windows

winget install ffmpeg

macOS

brew install ffmpeg

Linux (Debian/Ubuntu)

sudo apt install ffmpeg

Ou baixe o executável em https://ffmpeg.org/download.html e adicione ao PATH.

Instalando o Deno (recomendado para 4K/HDR)

Sem o Deno, o yt-dlp usa um método alternativo que pode não enxergar todos os formatos disponíveis. Com o Deno instalado, a extração é completa.

Windows

winget install DenoLand.Deno

macOS

brew install deno

Linux

curl -fsSL https://deno.land/install.sh | sh

Instalação

Via pip (recomendado):

pip install vidgrab

Via pipx (isolado):

pipx install vidgrab

From source (desenvolvimento):

git clone https://github.com/gsjonio/video_grabber.git
cd video_grabber
poetry install
poetry run vidgrab --help

Automated installers:

  • Linux/macOS: bash scripts/install.sh
  • Windows: scripts/install.bat

These scripts check for Python 3.11+ and ffmpeg, then install via pip.


Docker

Se preferir evitar instalar Python e ffmpeg localmente, use o Docker:

# Build da imagem (from root directory)
docker build -t vidgrab .

# Usar para baixar vídeos
docker run -v /seu/diretorio:/data vidgrab https://youtu.be/dQw4w9WgXcQ

# Com opções adicionais
docker run -v /seu/diretorio:/data vidgrab https://youtu.be/dQw4w9WgXcQ --max-height 1080 --workers 4

Nota: O Docker image contém Python 3.11 + ffmpeg + vidgrab pre-instalado.


Uso

# Vídeo único — qualidade máxima
vidgrab https://youtu.be/dQw4w9WgXcQ

# Inspecionar antes de baixar (dry run)
vidgrab https://youtu.be/dQw4w9WgXcQ --dry-run

# Limitar a 1080p
vidgrab https://youtu.be/dQw4w9WgXcQ --max-height 1080

# Salvar em diretório específico
vidgrab https://youtu.be/dQw4w9WgXcQ --output ~/Videos/raw

# Baixar playlist inteira
vidgrab "https://youtube.com/playlist?list=PLxxxx" --playlist

# Múltiplas URLs de um arquivo .txt com 5 workers
vidgrab --batch urls.txt --workers 5

# Forçar re-download mesmo se o arquivo já existir
vidgrab https://youtu.be/dQw4w9WgXcQ --force

# Conteúdo com restrição de idade
vidgrab https://youtu.be/dQw4w9WgXcQ --cookies ~/cookies.txt

# Salvar metadados em JSON
vidgrab https://youtu.be/dQw4w9WgXcQ --write-json

Arquivo --batch

Uma URL por linha. Linhas com # são ignoradas.

# Meus vídeos
https://youtu.be/dQw4w9WgXcQ
https://youtu.be/VIDEO_ID_2

Config file

Salve seus defaults pessoais em ~/.config/vidgrab/config.toml para não precisar repetir as flags:

output     = "~/Videos/raw"
workers    = 5
max_height = 1080

Flags passadas na linha de comando sempre têm prioridade sobre o config file.


Nomeação dos arquivos

{data_upload}-{slug-do-titulo}-{video_id}.{ext}

Exemplo: 20240315-never-gonna-give-you-up-dQw4w9WgXcQ.mp4

Com --write-json, um sidecar .json é criado ao lado do vídeo:

{
  "video_id": "dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up",
  "channel": "Rick Astley",
  "upload_date": "2009-10-25",
  "duration_seconds": 212,
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "description": "...",
  "tags": ["pop", "80s"]
}

Container e qualidade

Streams disponíveis Container de saída
H.264 + AAC mp4 (sem reencode)
VP9 / AV1 + Opus mkv (sem reencode)

O objetivo é nunca recodificar — apenas mesclar os streams.


Referência de opções

Opção Atalho Descrição
[URLS]... Uma ou mais URLs do YouTube
--batch FILE -b Arquivo .txt com uma URL por linha
--output DIR -o Diretório de saída (padrão: ~/Downloads)
--max-height INT Limitar resolução vertical (ex.: 1080)
--playlist Tratar URLs como playlists
--force -f Re-download mesmo se o arquivo já existe
--cookies FILE Arquivo de cookies (formato Netscape)
--write-json Salvar metadados em .json ao lado do vídeo
--workers INT -w Downloads paralelos (padrão: 3, máx: 8)
--dry-run Mostrar o que seria baixado, sem baixar
--quiet -q Suprimir toda saída exceto erros (útil para scripts)
--version -V Exibir versão
--install-completion Instalar autocomplete no shell atual
--help Exibir ajuda

Qualidade de código

O projeto usa uma stack completa de qualidade, integrada ao CI e aos pre-commit hooks:

Ferramenta Função
Ruff Linter + formatador (9 categorias de regras)
Pylint Análise estática avançada (10/10)
Mypy (strict) Type checking completo — dict[str, Any] em todas as fronteiras com yt-dlp
pytest + pytest-cov 120 testes unitários + integração, 89% cobertura, reportada ao Codecov
pre-commit Ruff, Pylint, Mypy e Markdownlint rodando antes de cada commit
GitHub Actions Lint, testes, CodeQL, release automática e publish no PyPI por tag
Dependabot Atualizações semanais de dependências Python e Actions
git-cliff Changelog automático bilíngue (PT-BR / EN) na release
Estrutura do projeto
vidgrab/
├── cli.py          # Interface Typer — parsing de argumentos e orquestração
├── downloader.py   # Lógica core — parallelismo, retry, classificação de erros
├── models.py       # VideoMetadata e DownloadResult como dataclasses tipadas
├── exceptions.py   # Hierarquia de exceções (geo-block, age-gate, unavailable…)
└── config.py       # Loader de ~/.config/vidgrab/config.toml via tomllib

tests/
├── test_exceptions.py      # 21 testes para todas as exceções
├── test_downloader_helpers.py  # 17 testes para helpers do downloader
├── test_downloader_download.py # 12 testes para orquestração de downloads
└── test_cli_handler.py     # 21 testes para CLI e UI

Contribuição

Contribuições são bem-vindas! Para desenvolver:

# Clone e instale
git clone https://github.com/gsjonio/video_grabber.git
cd video_grabber
poetry install

# Rode os testes
poetry run pytest

# Execute linters antes de commitar
poetry run pre-commit run --all-files

# Ou deixe pré-commit rodar automaticamente
pre-commit install

Release automático: Tags com formato v*.*.* acionam automaticamente:

  • ✅ Build e testes
  • ✅ Publish no PyPI
  • ✅ GitHub Release com changelog

Veja docs/CONTRIBUTING.md para mais detalhes.


English

CLI to download YouTube videos at the highest technically available quality — separate DASH video and audio streams, muxed via FFmpeg with no re-encoding. Built for raw footage in video editing workflows.


How it works

Most download tools re-encode to merge video and audio — degrading quality and wasting time. vidgrab does it differently:

YouTube  →  video stream (H.264 / VP9 / AV1)  ─┐
         →  audio stream (AAC / Opus)           ─┴→  FFmpeg mux  →  final file

Both streams are downloaded separately in DASH format (highest quality available) and muxed in copy mode — no transcoding, no quality loss.


Features

Feature Detail
Parallel downloads Up to 8 simultaneous via --workers
🔁 Smart retry Exponential backoff on rate-limits (up to 5 attempts)
🔍 Dry run Preview title, resolution and size before downloading
Auto resume Interrupted downloads pick up where they left off
📋 Batch download .txt file with one URL per line
🎬 Playlists Expands and downloads every video in a playlist
📁 Smart skip Detects existing file by video ID and skips automatically
📄 JSON metadata Sidecar .json with title, channel, date, tags and more
🔒 Restricted content Cookie support (Netscape format) for age-gated videos
⚙️ Config file Personal defaults at ~/.config/vidgrab/config.toml
🏷 Predictable names {date}-{slug}-{video_id}.{ext} on every download
⚠️ License warning Alerts when a video is not under a Creative Commons license

External dependencies

Tool Purpose How to install
Python 3.11+ Runtime python.org
ffmpeg Merge video + audio streams See below
yt-dlp Download engine Installed automatically via Poetry
Deno (optional) Access all YouTube formats including 4K/HDR See below
Installing ffmpeg

Windows

winget install ffmpeg

macOS

brew install ffmpeg

Linux (Debian/Ubuntu)

sudo apt install ffmpeg

Or grab the binary from https://ffmpeg.org/download.html and add it to your PATH.

Installing Deno (recommended for 4K/HDR)

Without Deno, yt-dlp falls back to an alternative extraction method that may not expose all available formats. With Deno, extraction is complete.

Windows

winget install DenoLand.Deno

macOS

brew install deno

Linux

curl -fsSL https://deno.land/install.sh | sh

Installation

Via pip (recommended):

pip install vidgrab

Via pipx (isolated):

pipx install vidgrab

From source (development):

git clone https://github.com/gsjonio/video_grabber.git
cd video_grabber
poetry install
poetry run vidgrab --help

Automated installers:

  • Linux/macOS: bash scripts/install.sh
  • Windows: scripts/install.bat

These scripts check for Python 3.11+ and ffmpeg, then install via pip.


Docker

To avoid installing Python and ffmpeg locally, use Docker:

# Build the image
docker build -t vidgrab .

# Download videos
docker run -v /your/directory:/data vidgrab https://youtu.be/dQw4w9WgXcQ

# With additional options
docker run -v /your/directory:/data vidgrab https://youtu.be/dQw4w9WgXcQ --max-height 1080 --workers 4

Note: The Docker image comes with Python 3.11 + ffmpeg + vidgrab pre-installed.


Usage

# Single video — maximum quality
vidgrab https://youtu.be/dQw4w9WgXcQ

# Inspect before downloading (dry run)
vidgrab https://youtu.be/dQw4w9WgXcQ --dry-run

# Cap at 1080p
vidgrab https://youtu.be/dQw4w9WgXcQ --max-height 1080

# Save to a specific directory
vidgrab https://youtu.be/dQw4w9WgXcQ --output ~/Videos/raw

# Download an entire playlist
vidgrab "https://youtube.com/playlist?list=PLxxxx" --playlist

# Download from a .txt file with 5 parallel workers
vidgrab --batch urls.txt --workers 5

# Force re-download even if the file already exists
vidgrab https://youtu.be/dQw4w9WgXcQ --force

# Age-restricted content
vidgrab https://youtu.be/dQw4w9WgXcQ --cookies ~/cookies.txt

# Save metadata as JSON
vidgrab https://youtu.be/dQw4w9WgXcQ --write-json

--batch file format

One URL per line. Lines starting with # are ignored.

# My videos
https://youtu.be/dQw4w9WgXcQ
https://youtu.be/VIDEO_ID_2

Config file

Save your personal defaults at ~/.config/vidgrab/config.toml to avoid repeating flags:

output     = "~/Videos/raw"
workers    = 5
max_height = 1080

Flags passed on the command line always take precedence over the config file.


Output filename pattern

{upload_date}-{title-slug}-{video_id}.{ext}

Example: 20240315-never-gonna-give-you-up-dQw4w9WgXcQ.mp4

With --write-json, a sidecar .json is saved next to each video:

{
  "video_id": "dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up",
  "channel": "Rick Astley",
  "upload_date": "2009-10-25",
  "duration_seconds": 212,
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "description": "...",
  "tags": ["pop", "80s"]
}

Container and quality

Available streams Output container
H.264 + AAC mp4 (no re-encode)
VP9 / AV1 + Opus mkv (no re-encode)

The goal is to never re-encode — only mux the streams.


Option reference

Option Short Description
[URLS]... One or more YouTube URLs
--batch FILE -b .txt file with one URL per line
--output DIR -o Output directory (default: ~/Downloads)
--max-height INT Cap vertical resolution (e.g. 1080)
--playlist Treat URLs as playlists
--force -f Re-download even if the file already exists
--cookies FILE Cookies file (Netscape format)
--write-json Save metadata as a .json sidecar next to the video
--workers INT -w Parallel downloads (default: 3, max: 8)
--dry-run Show what would be downloaded without downloading
--quiet -q Suppress all output except errors (useful for scripting)
--version -V Show version and exit
--install-completion Install shell autocomplete for the current shell
--help Show help

Code quality

The project uses a full quality stack, integrated into CI and pre-commit hooks:

Tool Role
Ruff Linter + formatter (9 rule categories)
Pylint Advanced static analysis (10/10)
Mypy (strict) Full type checking — dict[str, Any] at every yt-dlp boundary
pytest + pytest-cov 120 unit + integration tests, 89% coverage reported to Codecov
pre-commit Ruff, Pylint, Mypy and Markdownlint run before every commit
GitHub Actions Lint, tests, CodeQL, automatic release and PyPI publish on tag push
Dependabot Weekly updates for Python deps and Actions
git-cliff Automatic bilingual changelog (EN / PT-BR) on release
Project structure
vidgrab/
├── cli.py          # Typer interface — argument parsing and orchestration
├── downloader.py   # Core logic — parallelism, retry, typed error classification
├── models.py       # VideoMetadata and DownloadResult as typed dataclasses
├── exceptions.py   # Exception hierarchy (geo-block, age-gate, unavailable…)
└── config.py       # ~/.config/vidgrab/config.toml loader via tomllib

tests/
├── test_exceptions.py      # 21 tests for all exceptions
├── test_downloader_helpers.py  # 17 tests for downloader helpers
├── test_downloader_download.py # 12 tests for download orchestration
└── test_cli_handler.py     # 21 tests for CLI and UI

Contributing

Contributions are welcome! To develop:

# Clone and install
git clone https://github.com/gsjonio/video_grabber.git
cd video_grabber
poetry install

# Run tests
poetry run pytest

# Run linters before committing
poetry run pre-commit run --all-files

# Or let pre-commit run automatically
pre-commit install

Automatic releases: Tags matching v*.*.* automatically trigger:

  • ✅ Build and tests
  • ✅ PyPI publish
  • ✅ GitHub Release with changelog

See docs/CONTRIBUTING.md for more details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vidgrab-1.0.2.tar.gz (22.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vidgrab-1.0.2-py3-none-any.whl (19.0 kB view details)

Uploaded Python 3

File details

Details for the file vidgrab-1.0.2.tar.gz.

File metadata

  • Download URL: vidgrab-1.0.2.tar.gz
  • Upload date:
  • Size: 22.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.11.15 Linux/6.17.0-1018-azure

File hashes

Hashes for vidgrab-1.0.2.tar.gz
Algorithm Hash digest
SHA256 5a77e37aa93c3894592630137a3a3c19912c1df1c57e621d926f101495e21aff
MD5 064d02db16873f3a63abd8b69da6feb4
BLAKE2b-256 6d14ea77838a6d86e727748b0f346eaa268a987bb2bd890cc324cafc196adbda

See more details on using hashes here.

File details

Details for the file vidgrab-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: vidgrab-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 19.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.11.15 Linux/6.17.0-1018-azure

File hashes

Hashes for vidgrab-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 1044f53996ca216adf8947fa44a35c113c09bd1760a51322e8e5bd5f5e752c17
MD5 2d6f257455de17c412bf222c46be4a60
BLAKE2b-256 6e483fbae250e379aa889ad1a5c7306b8366ec9eb00d78f87c8ff88292149492

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page