Metadata-Version: 2.4
Name: subs-down-n-sync
Version: 1.4.0
Summary: CLI to download and sync subtitles for video files using semantic embeddings + DTW
Author-email: Airton Soares <airtonnsoares@gmail.com>
License-Expression: GPL-3.0-or-later
Project-URL: Homepage, https://pypi.org/project/subs-down-n-sync/
Project-URL: Repository, https://github.com/airton-soares/subs_down_n_sync
Project-URL: Bug Tracker, https://github.com/airton-soares/subs_down_n_sync/issues
Keywords: subtitles,subtitle-sync,opensubtitles,nlp,cli,video,sentence-transformers,dtw
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Environment :: Console
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Utilities
Classifier: Natural Language :: Portuguese (Brazilian)
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: subliminal>=2.2
Requires-Dist: sentence-transformers>=3.0
Requires-Dist: scipy>=1.13
Requires-Dist: setuptools<81
Requires-Dist: rich>=13
Requires-Dist: charset-normalizer>=3.0
Requires-Dist: cryptography>=42
Requires-Dist: faster-whisper>=1.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-mock>=3.12; extra == "dev"
Requires-Dist: pytest-cov>=6.0; extra == "dev"
Requires-Dist: ruff>=0.9; extra == "dev"
Dynamic: license-file

# subs_down_n_sync

![CI](https://github.com/airton-soares/subs_down_n_sync/actions/workflows/ci.yml/badge.svg)
[![PyPI version](https://img.shields.io/pypi/v/subs-down-n-sync)](https://pypi.org/project/subs-down-n-sync/)
[![Python versions](https://img.shields.io/pypi/pyversions/subs-down-n-sync)](https://pypi.org/project/subs-down-n-sync/)
[![License](https://img.shields.io/pypi/l/subs-down-n-sync)](LICENSE)

CLI Python para baixar e sincronizar legendas para arquivos de vídeo. Idioma padrão: **pt-BR**, configurável via flag `--lang` (qualquer tag BCP 47).

A sincronização usa embeddings semânticos multilíngues ([sentence-transformers](https://www.sbert.net/), modelo `paraphrase-multilingual-MiniLM-L12-v2`) combinados com DTW: baixa uma legenda de referência no idioma original (padrão: EN, configurável via `--ref-lang`) e alinha os cues da legenda alvo aos timestamps da referência por similaridade semântica. Legendas com match exato (hash ou release group) são usadas sem sincronização.

## Instalação

### Recomendado: pipx

[`pipx`](https://pipx.pypa.io/) instala ferramentas CLI em ambientes isolados e adiciona automaticamente ao `PATH`:

```bash
pipx install subs-down-n-sync
```

### Alternativa: pip

```bash
pip install subs-down-n-sync
```

Se aparecer o aviso `WARNING: The script subs-down-n-sync is installed in '...' which is not on PATH`, adicione o diretório ao `PATH`:

```bash
# Linux/macOS — adicione ao ~/.bashrc ou ~/.zshrc
export PATH="$HOME/.local/bin:$PATH"
```

```powershell
# Windows (PowerShell) — adicione ao seu $PROFILE
$env:PATH += ";$env:APPDATA\Python\Scripts"
```

Após editar o arquivo de perfil, reinicie o terminal ou rode `source ~/.bashrc` (Linux/macOS).

Instale também o `ffmpeg`:

```bash
sudo apt install ffmpeg    # Debian/Ubuntu
brew install ffmpeg        # macOS
winget install Gyan.FFmpeg # Windows
```

Configure as credenciais do OpenSubtitles:

```bash
export OPENSUBTITLES_USERNAME="seu_usuario"
export OPENSUBTITLES_PASSWORD="sua_senha"
```

`OPENSUBTITLES_USERNAME` e `OPENSUBTITLES_PASSWORD` são opcionais se as credenciais
já estiverem salvas em `~/.config/subs-down-n-sync/credentials.enc` (criptografado com AES-128).
No primeiro uso sem env vars, o CLI solicita interativamente e salva o arquivo.
Env vars têm prioridade sobre o arquivo salvo.

> Para desenvolvimento, veja [Setup](#setup).

## Setup

Linux/macOS:

```bash
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```

Windows (PowerShell):

```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e ".[dev]"
```

Windows (cmd.exe):

```cmd
python -m venv .venv
.venv\Scripts\activate.bat
pip install -e ".[dev]"
```

Instale também o `ffmpeg` no sistema:

```bash
sudo apt install ffmpeg    # Debian/Ubuntu
brew install ffmpeg        # macOS
```

```powershell
winget install Gyan.FFmpeg          # Windows (winget)
choco install ffmpeg                # Windows (Chocolatey)
scoop install ffmpeg                # Windows (Scoop)
```

Confirme que `ffmpeg` está no `PATH` rodando `ffmpeg -version` em novo terminal.

## Configuração (uma única vez)

Linux/macOS:

```bash
export OPENSUBTITLES_USERNAME="seu_usuario"
export OPENSUBTITLES_PASSWORD="sua_senha"
```

Windows (PowerShell, sessão atual):

```powershell
$env:OPENSUBTITLES_USERNAME = "seu_usuario"
$env:OPENSUBTITLES_PASSWORD = "sua_senha"
```

Windows (persistente, próximas sessões):

```powershell
setx OPENSUBTITLES_USERNAME "seu_usuario"
setx OPENSUBTITLES_PASSWORD "sua_senha"
```

`OPENSUBTITLES_USERNAME` e `OPENSUBTITLES_PASSWORD` são opcionais se as credenciais
já estiverem salvas em `~/.config/subs-down-n-sync/credentials.enc` (criptografado com AES-128).
No primeiro uso sem env vars, o CLI solicita interativamente e salva o arquivo.
Env vars têm prioridade sobre o arquivo salvo.

## Uso

```bash
# Default: pt-BR
subs-down-n-sync /caminho/para/filme.mkv

# Outro idioma (BCP 47: 'en', 'pt-BR', 'en-US', 'es', 'ja', ...)
subs-down-n-sync /caminho/para/filme.mkv --lang en
subs-down-n-sync /caminho/para/filme.mkv -l es

# Processar diretório inteiro (busca vídeos recursivamente)
subs-down-n-sync /caminho/para/pasta/
subs-down-n-sync /caminho/para/pasta/ --lang en
subs-down-n-sync /caminho/para/pasta/ --overwrite   # sobrescreve legendas existentes (baixa da API)
subs-down-n-sync /caminho/para/pasta/ --resync      # sincroniza legenda existente sem API
subs-down-n-sync /caminho/para/pasta/ --parallel    # processa até 2 vídeos simultâneos

# Audio sync com modelo Whisper maior (mais preciso, mais lento)
subs-down-n-sync /caminho/para/filme.mkv --whisper-model base

# Idioma original diferente de EN (ex.: conteúdo espanhol)
subs-down-n-sync /caminho/para/filme.mkv --ref-lang es

# Ou via módulo Python
python -m subs_down_n_sync /caminho/para/filme.mkv
```

Ao passar um diretório, vídeos que já têm legenda (`<video>.<lang>.srt`) são pulados por padrão. Use `--overwrite` / `-o` para baixar novamente da API. Use `--resync` / `-r` para sincronizar a legenda existente sem bater na API (útil quando a legenda já está correta mas fora de sincronia). Use `--parallel` / `-p` para processar até 2 vídeos em paralelo.

Saída: `/caminho/para/filme.<lang>.srt` (ex.: `filme.pt-BR.srt`, `filme.en.srt`). Isso permite manter legendas do mesmo vídeo em idiomas diferentes sem sobrescrever.

## Desenvolvimento

```bash
pip install -e ".[dev]"
pytest
```

Os testes unitários rodam com gate de cobertura de 90% (configurado em `pyproject.toml`). O CI falha se a cobertura cair abaixo disso.

Para rodar sem o gate (útil ao explorar com `-k` ou `--collect-only`):

```bash
pytest --no-cov
```

## Lint e formatação

O projeto usa [Ruff](https://docs.astral.sh/ruff/) para formatação e lint.

```bash
ruff format .           # aplica formatação
ruff format --check .   # verifica sem escrever (usado no CI)
ruff check .            # roda lint
ruff check --fix .      # aplica fixes automáticos
```

O CI falha se `ruff format --check` ou `ruff check` encontrarem problemas.

## Testes de integração

O projeto tem duas camadas de testes:

- **Testes unitários** (padrão, `pytest`) — rápidos, mockam `subliminal` e `sentence_transformers`. Não precisam de rede nem de binários externos além do Python.
- **Testes de integração** (`pytest -m integration`) — exercitam o pipeline real de alinhamento semântico (download do modelo `sentence-transformers` + DTW) sobre legendas reais. Requer acesso à internet no primeiro run para baixar o modelo (~120 MB), cacheado pelo Hugging Face em `~/.cache/huggingface/`.

Como rodar cada camada:

```bash
pytest                    # só unit (rápido)
pytest -m integration     # só integração (baixa modelo de embeddings, roda DTW real)
pytest -m ""              # tudo (unit + integração)
```
