# 🎬 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` `` `8080` | SSL (Let's Encrypt) · Force SSL · Websockets | | `mkv2avi.maisongrondin.fr` | `http` `` `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 ```