- JavaScript 89.7%
- CSS 9.7%
- Dockerfile 0.4%
- HTML 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Une page dédiée listant toutes les lectures du serveur, groupées par jour, avec filtres spectateur / bibliothèque / type / période, recherche textuelle et export CSV. Les filtres que Plex sait appliquer (compte, bibliothèque, dates) lui sont délégués, ce qui garde la pagination côté serveur. Le type et la recherche n'existent qu'après enrichissement des métadonnées : ils imposent un balayage, borné à 4000 lectures et annoncé dans la réponse. Au passage, une correction : un filtre à opérateur s'écrit `viewedAt>` et non `viewedAt>=`. Le `=` fait partie de la requête, pas de la clé — encodé en %3D, Plex ignore le filtre en silence et répond 200 avec l'historique complet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
| server | ||
| web | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
PerPlex
Interface web pour gérer ton serveur Plex. Première fonction : trouver les doublons et faire le ménage.
Pensée pour le téléphone d'abord — c'est là qu'on se rend compte que le NAS est plein.
Démarrage
Une seule commande, puis tout se passe dans le navigateur :
docker compose up -d
Ouvre http://localhost:3001 (ou http://<ip-de-la-machine>:3001 depuis ton
téléphone), clique sur « Connecter mon serveur Plex », et suis l'assistant.
Comment marche la connexion
PerPlex n'a jamais besoin de ton mot de passe. L'app affiche un code à 4 caractères et tu l'autorises depuis un appareil déjà connecté à Plex — exactement comme quand tu relies une TV :
- Sur le même appareil : le bouton « Autoriser sur cet appareil » ouvre Plex, tu valides, c'est fini.
- Depuis un autre appareil : va sur plex.tv/link et saisis le code affiché.
PerPlex récupère alors la liste de tes serveurs, teste chaque adresse pour trouver
celle qui répond depuis le conteneur, et tu choisis lequel gérer. Le token est stocké
côté serveur dans le volume perplex-config (fichier en 0600) et n'est jamais envoyé
au navigateur.
Pour changer de serveur plus tard : clique sur le nom du serveur en haut à droite → Changer de serveur.
Si tu as déjà ton adresse et ton token sous la main, le lien « J'ai déjà mon adresse et mon token » en bas de l'assistant permet de les saisir directement.
Ce que ça fait
- Regroupe les fichiers qui correspondent au même film ou au même épisode, qu'ils soient rangés sous une seule fiche Plex (« versions multiples ») ou éclatés sur plusieurs fiches distinctes (« fiches séparées »).
- Le rapprochement se fait sur l'identifiant externe (IMDb / TMDb / TVDb) quand Plex l'a, sinon sur titre + année normalisés — accents, casse et ponctuation ignorés.
- Classe chaque version de la meilleure à la moins bonne. La meilleure est marquée à garder, les autres reçoivent une raison lisible.
- Te laisse cocher ce qui part, puis supprime réellement les fichiers via Plex.
Ménage
Au-delà des doublons, l'onglet Ménage cherche ce qui occupe de l'espace sans servir, et ce qui est mal rangé :
- Jamais regardés — aucune lecture depuis leur ajout, avec l'ancienneté et la place occupée. Les séries sont comptées comme un titre, pas comme 200 épisodes.
- Pas revus depuis X mois — vus au moins une fois, mais plus depuis longtemps. Un titre vu sans date enregistrée n'est jamais proposé : on ne suggère pas de supprimer ce qu'on est incapable de dater.
- Fiches mal appariées — sans identifiant externe, sans année, sans affiche, ou dont le titre ressemble encore à un nom de fichier. À corriger dans Plex plutôt qu'à supprimer.
- Fichiers disparus — référencés par Plex mais absents du disque. C'est ce qui remplit la corbeille, que l'on peut vider depuis là. La vérification est explicite : elle force Plex à contrôler chaque média et prend plusieurs minutes.
- Fichiers orphelins — présents sur le disque mais absents de la bibliothèque.
Ce dernier point demande que tes dossiers médias soient visibles depuis le conteneur, aux mêmes chemins que ceux déclarés dans Plex. Sans montage, PerPlex le dit franchement plutôt que de renvoyer une liste vide :
volumes:
- perplex-config:/config
- /media/monNAS/Films:/media/monNAS/Films:ro
Le montage en lecture seule (:ro) est volontaire : PerPlex ne supprime jamais un
fichier orphelin, rien ne garantit qu'il soit inutile.
Choisir ce qui compte
Le bouton « Quel fichier garder ? » ouvre les critères de classement. Ils sont appliqués de haut en bas : le premier qui départage deux fichiers décide, les suivants ne servent qu'en cas d'égalité. Chacun se réordonne et se désactive.
| Critère | Ce qu'il regarde |
|---|---|
| Résolution | Le palier (4K, 1080p…), jamais la hauteur en pixels — deux fichiers 4K de formats d'image différents n'ont pas le même nombre de lignes et sont pourtant à égalité |
| Débit vidéo | Le débit global, par paliers de tolérance |
| Débit par mégapixel | La compression réelle. Placé avant la résolution, un 1080p généreux peut battre une 4K trop compressée |
| Qualité audio | Nombre de canaux d'abord, puis le codec (TrueHD/DTS-HD > EAC3 > AC3) |
| Taille du fichier | Le plus gros gagne |
| Codec vidéo moderne | AV1 > HEVC > H.264, à débit égal |
| Résolution sauf si trop compressée | La meilleure définition gagne, sauf quand son débit par mégapixel tombe sous le seuil réglable : elle perd alors tout avantage de définition, et ce sont le débit, le son ou la taille qui départagent |
| Ancienneté | Garde le plus ancien : c'est lui qui porte l'historique de visionnage |
La tolérance sur le débit (15 % par défaut) définit en dessous de quel écart deux débits sont jugés équivalents. C'est ce qui permet à l'audio de trancher entre deux encodages quasi identiques, au lieu de laisser 1 Mb/s d'écart décider seul.
Par défaut : résolution → débit → audio → taille → ancienneté.
Mettre des doublons de côté
Une version longue et une version cinéma ne sont pas des doublons. Le bouton « Mettre de côté » sur un titre l'exclut définitivement des résultats — il ne pourra plus être supprimé, même par erreur. La liste se gère depuis « Mis de côté », et tout est réintégrable.
Deux règles automatiques, désactivées par défaut, écartent d'office :
- Cadrages différents — une ouverture IMAX (1.78:1) face à du scope (2.20:1) ne montre pas la même image.
- Durées différentes — director's cut, version longue, montage cinéma.
Même sans ces règles, PerPlex affiche un avertissement ⚠ sur les titres concernés, ainsi que lorsque le fichier conservé n'a pas la meilleure piste audio, ou qu'une définition supérieure cache un encodage bien plus compressé.
Garde-fous
La suppression est définitive : Plex efface le fichier du disque, il n'y a pas de corbeille.
- Un groupe ne peut jamais perdre tous ses fichiers. C'est vérifié côté serveur, pas seulement dans l'interface.
- La sélection est recoupée avec le dernier scan : si l'état a changé, la requête est refusée.
- Il faut taper
SUPPRIMERdans la boîte de confirmation. - Si la dernière version d'une fiche est supprimée, PerPlex supprime la fiche entière plutôt que de laisser une entrée orpheline dans ta bibliothèque.
- Suppressions envoyées une par une : Plex encaisse mal les requêtes concurrentes.
- Rien n'est coché d'office : c'est toi qui déclenches la sélection.
Côté Plex, il faut avoir activé Réglages › Bibliothèque › « Autoriser la suppression de médias ». Sans ça, PerPlex refuse l'opération avec un message explicite. L'état de ce réglage est visible dans la fiche du serveur.
Si aucun serveur n'est joignable
L'assistant liste tes serveurs mais les marque « injoignable » ? Le conteneur n'est pas sur le même réseau que Plex.
Le plus souvent, l'assistant trouve tout seul l'adresse LAN (192.168.x.x) et ça marche.
Sinon, décommente network_mode: host dans docker-compose.yml,
retire le bloc ports, et relance docker compose up -d.
Maintenance et notifications
L'onglet Maintenance expose les tâches du majordome de Plex — sauvegarde et optimisation de la base, nettoyage des paquets obsolètes, analyse des bibliothèques — et permet de déclencher celles qui ont du sens hors de la fenêtre de maintenance. Les activités en cours s'y suivent en direct.
Les notifications arrivent même quand PerPlex est fermé : c'est tout leur intérêt. Chaque navigateur s'inscrit séparément, et une surveillance de fond alerte quand un transcodage n'arrive plus à suivre la lecture, quand un serveur ne répond plus, et quand il revient. Sur iPhone elles exigent que PerPlex soit d'abord ajouté à l'écran d'accueil.
Les clés VAPID sont générées au premier démarrage et conservées : les régénérer invaliderait toutes les inscriptions existantes.
Installer comme application
PerPlex est une PWA : sur téléphone comme sur bureau, le navigateur propose de l'installer, et elle s'ouvre alors en plein écran avec sa propre icône. Un bandeau discret le propose au bon moment ; sinon, le menu du navigateur contient « Installer PerPlex » (ou « Ajouter à l'écran d'accueil » sur iOS).
Un service worker garde la coquille de l'application en cache : elle s'ouvre même hors ligne. Les réponses de l'API ne sont jamais mises en cache — un tableau de bord affichant les chiffres d'hier sans le dire serait pire qu'un écran d'erreur, et ces réponses dépendent de la session.
Quand une nouvelle version est déployée, un bandeau propose de recharger plutôt que de laisser une application installée sur une version périmée.
Sécurité
PerPlex peut supprimer des fichiers et couper des lectures. Au premier lancement, l'interface propose de la protéger — deux méthodes :
Compte Plex (recommandé). Tu autorises depuis un appareil déjà connecté à Plex, comme pour la liaison du serveur. L'autorisation est déléguée à Plex : est admis qui a accès au serveur connecté. Par défaut, seul le propriétaire entre ; un réglage ouvre l'accès aux comptes avec qui le serveur est partagé.
Mot de passe unique, haché en scrypt dans la base, huit caractères minimum. Aucune
récupération possible. Il peut aussi être imposé par la variable PERPLEX_PASSWORD :
l'interface ne permet alors plus de le modifier.
Dans les deux cas la session tient dans un cookie HttpOnly SameSite=Lax valable
30 jours, révocable, et huit tentatives ratées bloquent l'adresse cinq minutes. Toute
l'API est verrouillée, sauf les routes d'authentification elles-mêmes.
Tant qu'aucune méthode n'est choisie, PerPlex reste ouvert : verrouiller une instance existante enfermerait son propriétaire dehors. La fiche du serveur le signale.
Même protégé, garde-le de préférence sur ton réseau local ou derrière un VPN.
Configuration
Tout est facultatif — copie .env.example en .env seulement si tu veux
changer quelque chose.
| Variable | Défaut | Rôle |
|---|---|---|
PERPLEX_PORT |
3001 |
Port exposé sur la machine hôte |
SCAN_CACHE_TTL |
300 |
Durée de vie du cache de scan, en secondes |
PLEX_URL |
— | Impose un serveur sans passer par l'assistant |
PLEX_TOKEN |
— | Idem. Si les deux sont définis, l'interface ne permet plus de changer de serveur |
PLEX_INSECURE |
0 |
1 pour accepter un certificat TLS auto-signé |
PERPLEX_PASSWORD |
— | Impose un mot de passe ; l'interface ne peut plus le changer |
PERPLEX_CORS_ORIGIN |
— | Autorise une origine externe. Inutile en usage normal |
La connexion, les critères de classement et les doublons mis de côté vivent dans une base
SQLite (/config/perplex.db) portée par le volume perplex-config. Tout survit aux
redémarrages et aux mises à jour de l'image. Une ancienne configuration config.json est
reprise automatiquement au premier démarrage.
Publier l'image (Portainer, autre machine)
Par défaut l'image porte un nom local : elle se construit sur place et ne se
télécharge de nulle part. C'est pour ça que le bouton Pull and redeploy de Portainer
échoue avec pull access denied for perplex — il n'y a rien à tirer.
Deux options.
Laisser Portainer construire. Dans la stack, décoche Re-pull image avant Update the stack. Avec une stack de type Repository, pointe-la sur ce dépôt et coche Re-clone plutôt que Re-pull.
Publier dans un registre, ce qui rend le pull possible partout. Avec le registre de ta forge Forgejo :
# jeton : Paramètres › Applications › Générer un jeton (portée : write:package)
echo "$TOKEN" | docker login forge.lequen.fr -u jlequen --password-stdin
export PERPLEX_IMAGE=forge.lequen.fr/jlequen/perplex:latest
docker compose build
docker compose push
Sur la machine qui héberge Portainer, mets la même valeur dans .env, ajoute
PERPLEX_PULL_POLICY=always, et Pull and redeploy fonctionnera.
Structure
server/ API Express — parle à Plex, détecte les doublons, supprime
src/plextv.js liaison par code et découverte des serveurs (plex.tv)
src/setup.js routes de l'assistant de connexion
src/db.js base SQLite : réglages et exceptions
src/store.js accès à la configuration de connexion
src/preferences.js critères de classement et comparateur
src/plex.js client HTTP Plex (pagination, erreurs, suppression)
src/duplicates.js regroupement et classement des versions
src/index.js routes HTTP + garde-fous de suppression
web/ Interface React (Vite), mobile-first
Développement
Uniquement si tu as Node 20+ en local — ce n'est pas nécessaire pour utiliser l'app.
npm install
npm run dev # interface sur :5173, API sur :3001
La base atterrit alors dans data/perplex.db au lieu du volume Docker.
Limites connues
- Le scan porte sur les bibliothèques Films et Séries. La musique et les photos sont ignorées.
- Deux versions volontaires d'un même film apparaissent comme des doublons. Les règles d'écartement automatique et la liste des exceptions sont là pour ça, mais la décision reste la tienne.
- Aucun classement automatique ne remplace un coup d'œil : PerPlex compare des chiffres (résolution, débit, canaux), pas des images. Les avertissements ⚠ signalent les cas où ces chiffres ne suffisent pas.
- Une bibliothèque de plusieurs milliers de titres met quelques dizaines de secondes à
charger la première fois. Le résultat est ensuite mis en cache pendant
SCAN_CACHE_TTL.