Files
mkv2avi/README.md
T
Alexandre 6c0c96c65d
Release / release (push) Failing after 6s
CI: workflow Gitea Actions de livraison par tag
- build + push image dans le registry Gitea sur tag vX.Y.Z
- création d'une release Gitea avec le compose de prod en asset
- doc CI/CD dans le README

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 03:01:36 +02:00

197 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🎬 MKV → AVI
Petite application web (Docker) pour parcourir un dossier média, **télécharger** un
fichier d'origine ou le **convertir en AVI Xvid** lisible par les vieux lecteurs DivX/DVD.
La conversion reprend le pipeline qui a fait ses preuves :
- vidéo **Xvid** (`mpeg4 -vtag xvid`, qualité `qscale 4`) ;
- audio **MP3 stéréo** (downmix 5.1 → 2.0) ;
- sélection auto de la **piste française** en évitant l'audiodescription ;
- **tone-mapping HDR → SDR** propre via `zscale` (les sources 4K HDR10/DV/HLG
sont détectées automatiquement) ;
- redimensionnement au choix (720 px « max compatibilité », 1280 px, ou original).
> ️ Le décodage est **logiciel** : dans un conteneur, l'accélération matérielle
> (VideoToolbox sur Mac, QSV/NVENC) n'est pas disponible. Comptez plusieurs minutes
> à quelques dizaines de minutes par film selon le CPU.
## Démarrage rapide
```bash
# Dossier contenant vos films (monté en lecture seule)
export MEDIA_DIR=/chemin/vers/mes/films
# Où écrire les AVI convertis (optionnel, défaut ./output)
export OUTPUT_DIR=$PWD/output
docker compose up -d --build
```
Puis ouvrir **http://localhost:8080**.
- Rechercher un fichier dans le champ de recherche.
- **⬇ Télécharger** : récupère le fichier d'origine.
- **🎬 Convertir en AVI** : choisir définition + piste audio → suivre la progression →
**⬇ Télécharger l'AVI** une fois terminé.
Les fichiers convertis restent aussi dans le dossier `OUTPUT_DIR`.
## Authentification Keycloak (OIDC)
L'app peut exiger une connexion Keycloak (flow **Authorization Code**, session par
cookie). Elle est **désactivée tant que `OIDC_ISSUER` est vide** — pratique en dev local.
Une fois configurée, **tout utilisateur authentifié** du realm a accès ; login/logout
et nom de l'utilisateur sont gérés dans l'app.
### 1. Créer le client dans Keycloak
- **Clients → Create client** : type `OpenID Connect`, Client ID `mkv2avi`.
- **Client authentication : On** (client confidentiel) → un *secret* est généré
(onglet **Credentials**).
- **Valid redirect URIs** : `https://mkv2avi.mondomaine.fr/auth/callback`
- **Valid post logout redirect URIs** : `https://mkv2avi.mondomaine.fr/`
- **Web origins** : `+` (ou l'URL exacte).
### 2. Renseigner les variables
```bash
cp .env.example .env
# puis éditer .env :
# OIDC_ISSUER=https://keycloak.mondomaine.fr/realms/mon-realm
# OIDC_CLIENT_ID=mkv2avi
# OIDC_CLIENT_SECRET=...(onglet Credentials)
# SESSION_SECRET=$(openssl rand -hex 32)
# SESSION_HTTPS_ONLY=true # si servi en HTTPS via Nginx Proxy Manager
docker compose up -d --build
```
> Derrière **Nginx Proxy Manager** (TLS) : uvicorn tourne avec `--proxy-headers`, donc
> l'URL de callback est déduite en `https`. En cas de souci, forcez-la avec
> `OIDC_REDIRECT_URI`. Activez `SESSION_HTTPS_ONLY=true` pour des cookies `Secure`.
### Stack tout-en-un (Keycloak inclus)
Pour tester/déployer **Keycloak + PostgreSQL + l'app** d'un coup, un compose dédié
avec un realm pré-importé (client `mkv2avi` + utilisateur de test) est fourni :
```bash
# 1. Rendre Keycloak résolvable sous le même nom côté navigateur ET côté conteneur
echo "127.0.0.1 keycloak.local" | sudo tee -a /etc/hosts
# 2. (optionnel) dossier de films à convertir
export MEDIA_DIR=/Volumes/data/films
# 3. Démarrer le stack
docker compose -f docker-compose.keycloak.yml up -d --build
```
| Service | URL | Identifiants |
|---------|-----|--------------|
| App MKV → AVI | http://localhost:8090 | `alex` / `alex` |
| Console Keycloak | http://localhost:8080 | `admin` / `admin` |
Le realm `mkv2avi` (`keycloak/realm-mkv2avi.json`) contient déjà le client confidentiel
(`secret : mkv2avi-secret-change-me`) et un utilisateur. **Change les secrets/mots de
passe avant tout usage réel** (variables `OIDC_CLIENT_SECRET`, `SESSION_SECRET`,
`KC_ADMIN_PASSWORD`, `KC_DB_PASSWORD`, et le `secret` dans le realm JSON).
> **Pourquoi `keycloak.local` ?** L'URL de Keycloak (l'*issuer*) doit être **identique**
> depuis le navigateur et depuis le conteneur de l'app. L'alias réseau Docker
> `keycloak.local` + la ligne `/etc/hosts` garantissent cette cohérence. En production
> derrière Nginx Proxy Manager, on remplace tout ça par le vrai domaine
> (`KC_HOSTNAME` + `OIDC_ISSUER` = `https://auth.mondomaine.fr/realms/mkv2avi`).
## Production derrière Nginx Proxy Manager
Fichier `docker-compose.prod.yml` : Keycloak en **mode production** (`start`), TLS
terminé par NPM. Domaines : app = `https://mkv2avi.maisongrondin.fr`,
Keycloak = `https://auth.mkv2avi.maisongrondin.fr`.
### 1. Secrets
```bash
cp .env.prod.example .env.prod
# générer : openssl rand -hex 32 (pour OIDC_CLIENT_SECRET et SESSION_SECRET)
# OIDC_CLIENT_SECRET doit être identique au "secret" du client dans
# keycloak/realm-mkv2avi.json (ou régénéré ensuite dans la console Keycloak).
```
### 2. Démarrage
```bash
docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --build
```
Les services publient sur l'hôte : Keycloak `:8080`, app `:8090`.
### 3. Deux Proxy Hosts dans NPM
| Domaine | Forward vers | Options |
|---------|--------------|---------|
| `auth.mkv2avi.maisongrondin.fr` | `http` `<IP_HOTE>` `8080` | SSL (Let's Encrypt) · Force SSL · Websockets |
| `mkv2avi.maisongrondin.fr` | `http` `<IP_HOTE>` `8090` | SSL · Force SSL · Websockets |
Pour l'app, ajoute cette **Advanced / Custom Nginx Configuration** (téléchargements de
gros fichiers sans coupure — même logique que ton fix Nextcloud) :
```nginx
proxy_buffering off;
proxy_request_buffering off;
client_max_body_size 0;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
```
### 4. Points de vigilance
- **DNS interne** : le conteneur `mkv2avi` doit résoudre `auth.mkv2avi.maisongrondin.fr`
(il valide les tokens via cette URL). Si ton réseau ne fait pas de *hairpin NAT*,
décommente le bloc `extra_hosts` du compose en pointant vers l'IP de NPM/hôte.
- **Keycloak** sert en HTTP interne (`KC_HTTP_ENABLED=true`) et fait confiance aux
en-têtes `X-Forwarded-*` de NPM (`KC_PROXY_HEADERS=xforwarded`) ; l'issuer reste
figé sur l'URL publique (`KC_HOSTNAME`).
- Change **tous** les mots de passe/secrets par défaut avant exposition.
## Déploiement sur TrueNAS SCALE
Utilisable comme *Custom App* (Docker) : montez le dataset des films sur `/media`
(en lecture seule) et un dataset de sortie sur `/output`, exposez le port `8000`.
## CI/CD (Gitea Actions)
Workflow `.gitea/workflows/release.yml`**livraison par tag** :
```bash
git tag v0.1.0
git push origin v0.1.0
```
À chaque tag `vX.Y.Z`, la pipeline :
1. build l'image Docker ;
2. la pousse dans le **registry Gitea**`git.maisongrondin.fr/alexandre/mkv2avi:X.Y.Z` (+ `:latest`) ;
3. crée une **release Gitea** avec `docker-compose.prod.yml` et `.env.prod.example` en pièces jointes.
L'authentification au registry utilise le **token automatique du runner** (`secrets.GITEA_TOKEN`),
aucun secret à configurer. Pour cibler un autre registry (Distribution), changez `REGISTRY`/`IMAGE`
en tête du workflow et ajoutez un `docker login` avec vos identifiants en secrets.
**Prérequis** : Actions activées sur le dépôt, un Act Runner avec accès Docker et le label
`ubuntu-latest`. Le déploiement se fait ensuite manuellement (voir *Production* ci-dessus) :
`docker compose --env-file .env.prod -f docker-compose.prod.yml up -d`.
## Variables d'environnement
| Variable | Défaut | Rôle |
|---------------|-----------|----------------------------------------------|
| `MEDIA_DIR` | `/media` | Dossier parcouru (monté en lecture seule) |
| `OUTPUT_DIR` | `/output` | Dossier des AVI convertis |
| `MAX_WORKERS` | `1` | Conversions simultanées (1 = en série) |
| `MAX_RESULTS` | `400` | Nb max de fichiers listés par recherche |
| `OIDC_ISSUER` | *(vide)* | URL du realm Keycloak (vide = auth off) |
| `OIDC_CLIENT_ID` | *(vide)* | Client ID Keycloak |
| `OIDC_CLIENT_SECRET` | *(vide)* | Secret du client confidentiel |
| `OIDC_REDIRECT_URI` | *(auto)* | Force l'URL de callback si besoin |
| `SESSION_SECRET` | *(à changer)* | Clé de signature des cookies de session |
| `SESSION_HTTPS_ONLY` | `false` | Cookies `Secure` (mettre `true` en HTTPS) |
## Développement local (sans Docker)
```bash
pip install -r requirements.txt
# ffmpeg avec le filtre zscale requis pour le HDR (sinon HDR non géré)
MEDIA_DIR=/chemin/films OUTPUT_DIR=./output \
uvicorn main:app --app-dir app --reload --port 8000
```