| client | ||
| data | ||
| server | ||
| tools | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| docker-entrypoint.sh | ||
| Dockerfile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
VerseLink
Outil web local pour découper / composer des vidéos Chunithm depuis un album Immich, avec prévisualisation temps réel, marqueurs de scores Kamaitachi, rotation et rendu FFmpeg vertical 9:16.
Stack
- Backend : Node.js + Express (workspaces npm)
- Frontend : React + Vite + react-router-dom
- Sources : Immich (album vidéos), Kamaitachi (scores, API V3)
- Rendu : FFmpeg (
h264_nvenc) + sharp (génération overlay score / rank) - Données : Fichiers JSON locaux (
data/) + catalogue songs Chunithm (data/gameSongData/chunithm.json)
Structure du projet
VerseLink/
├── client/ # Frontend React (Vite)
│ ├── src/
│ │ ├── api.js # Client API
│ │ ├── App.jsx # Route config
│ │ ├── pages/
│ │ │ ├── VideosPage.jsx # Liste des vidéos de l'album
│ │ │ ├── EditorPage.jsx # Éditeur vidéo
│ │ │ └── ExportsPage.jsx # File d'export
│ │ └── components/
│ │ ├── VideoEditor.jsx # Éditeur principal (trim, métadonnées, export)
│ │ ├── VideoList.jsx # Grille des vidéos
│ │ ├── MusicSelectionModal.jsx # Sélecteur de musique Chunithm
│ │ ├── Timeline.jsx # Timeline avec marqueurs de scores
│ │ ├── LiveExportPreview.jsx # Prévisualisation temps réel du rendu 9:16
│ │ ├── Menu.jsx # Navigation latérale + statut file
│ │ └── ExportQueue.jsx # Mini file d'attente (compact)
│ └── vite.config.js # Proxy /api → backend
├── server/ # Backend Express
│ └── src/
│ ├── config.js # Configuration .env
│ ├── index.js # Server entry point
│ ├── routes/
│ │ ├── album.js # GET /api/album, GET /api/config
│ │ ├── assets.js # GET /api/assets/:id, PUT state, vidéo/thumbnail proxy, kamai-suggestions
│ │ ├── render.js # POST /api/render, /api/render/preview-frame, /api/render/queue/…
│ │ ├── localdata.js # GET /api/local-data
│ │ └── data.js # GET /api/data (songs), GET /api/covers
│ └── services/
│ ├── immich.js # Client API Immich
│ ├── kamaitachi.js # Client API Kamaitachi V3
│ ├── ffmpeg.js # Rendu FFmpeg (filter complex, overlay, téléchargement)
│ ├── renderQueue.js # File d'attente persistante (queue.json)
│ ├── timings.js # Fenêtre d'enregistrement + parsing timestamps
│ └── store.js # Store JSON local (timings.json)
├── tools/ # Génération d'assets Chunithm
│ ├── generateRank.cjs # Génération badges rank (S, S+, SS, …)
│ ├── generateScore.cjs # Génération chiffres score arc-en-ciel
│ ├── templates/ # Template spritesheet CHUNITHM
│ └── output/ # Images rank pré-générées
├── data/
│ ├── gameSongData/chunithm.json # Catalogue songs Chunithm (feat. sheets, difficultés, jackets)
│ ├── covers/ # Jackets mis en cache
│ └── mask/ # Masques PNG pour overlay
└── .env.example # Template de configuration
Setup
cp .env.example .env
# Édite .env avec tes infos Immich / Kamaitachi
npm install
npm run dev
- UI : http://127.0.0.1:5173
- API : http://127.0.0.1:3847
Docker 🐳
Le projet peut tourner en conteneur (serveur + client buildé + ffmpeg inclus).
# Lancer avec Docker Compose
cp .env.example .env # optionnel : les réglages passent surtout par les Settings de l'app
sudo docker compose up --build -d
- UI + API : http://localhost:3847
- Les données sont persistées dans le volume
verselink-data(catalogue, masques, exports, settings) - Le codec par défaut est
h264_nvenc(GPU NVIDIA). Sans GPU, passe enlibx264(logiciel) dans les Settings de l'app.
Détails de l'image :
| Élément | Détail |
|---|---|
| Build | Multi-stage : client React buildé (Vite) puis runtime Node 24 |
| ffmpeg | Inclus dans l'image + police CJK (wqy-zenhei) pour les overlays |
| Données seed | Catalogue songs + masques copiés automatiquement au premier démarrage si le volume est vide |
| Port | 3847 |
| Entrypoint | Initialise les dossiers runtime (output, raw, cache, covers) |
Workspaces npm
Le projet utilise npm workspaces. Les scripts principaux :
| Commande | Description |
|---|---|
npm run dev |
Lance server + client en parallèle |
npm run dev:server |
Server seulement (avec --watch) |
npm run dev:client |
Client Vite seulement |
npm run build |
Build client (production) |
npm run start |
Lance server seul (production, sert le client build) |
Permissions Immich (API key)
album.readasset.readasset.viewasset.download
Configuration (.env)
PORT=3847 # Port API (défaut)
HOST=0.0.0.0 # Bind host
# --- Immich ---
IMMICH_URL=https://immich.example.com
IMMICH_API_KEY=ta_cle_api
IMMICH_ALBUM_ID=id_album_immich
IMMICH_ALBUM_NAME=ChuniRaw # Nom d'affichage (optionnel)
# --- Kamaitachi (optionnel) ---
KAMAITACHI_URL=https://kamai.tachi.ac
KAMAITACHI_API_KEY=ta_cle_kamai
KAMAITACHI_USER=ton_pseudo
KAMAITACHI_GAME=chunithm # V3: chunithm (pas chunithm/Single)
KAMAITACHI_PLAYTYPE= # Laisser vide pour Single (mono-playtype)
# --- Dossiers locaux ---
WORK_DIR=./data
OUTPUT_DIR=./data/output
# --- Police pour FFmpeg (optionnel) ---
FFMPEG_FONT_PATH= # Chemin vers .ttf/.ttc
# Défauts : DejaVuSans-Bold.ttf, wqy-zenhei.ttc (CJK)
# --- Jackets Chunithm (optionnel) ---
CHUNI_SONG_COVER_URL= # URL base pour les covers (ex: https://assets.example.com/covers/)
Fonctionnalités détaillées
1. Album Immich
- Liste toutes les vidéos d'un album Immich
- Grille avec vignettes, durée, statut téléchargement
- Badges fenêtre d'enregistrement et suggestions Kamaitachi
- Rafraîchissement manuel
2. Fenêtre d'enregistrement
VerseLink déduit automatiquement la fenêtre temporelle de chaque vidéo :
- Début : extrait du nom de fichier
VID_YYYYMMDD_HHMMSS(heure locale téléphone) - Fin : métadonnée Immich
localDateTime(mêmes chiffres locaux) - Fuseau horaire : configurable (défaut
Europe/Paris)
Les scores Kamaitachi dont timeAchieved tombe dans cette fenêtre sont associés à la vidéo.
3. Intégration Kamaitachi (API V3)
Deux niveaux de matching :
- /scores/recent → les 100 derniers scores. Si la vidéo est récente, match direct.
- /sessions/recent + session details → si la vidéo est plus vieille que les 100 scores, recherche dans les sessions.
Les suggestions s'affichent dans l'éditeur sous forme de chips cliquables : appliquer une suggestion remplit automatiquement titre, artiste, difficulté, score, grade et positionne les marqueurs de coupe.
4. Éditeur vidéo
- Lecteur vidéo avec timeline et marqueurs
- Découpe : réglage début/fin (curseurs + champs numériques)
- Rotation : 90° / 180° / 270°
- Sélecteur de musique : modal avec recherche par titre, artiste, version, difficulté. Utilise le catalogue
data/gameSongData/chunithm.json(feat. covers, sheets, internal levels). - Métadonnées : titre, artiste, difficulté, score, grade, internal level
- Prévisualisation temps réel : rendu FFmpeg 9:16 du frame courant avec tous les overlays (jacket, score, rank, textes)
5. Rendu FFmpeg
Le rendu vertical produit une vidéo 1080×1920 avec :
- Fond flou (video + jacket en arrière-plan)
- Vidéo source redimensionnée centrée (1200px de large)
- Jacket : recadrée avec masques (PNG alpha), bordure arrondie
- Score : chiffres arc-en-ciel Chunithm (via
generateScore.cjs) - Rank : badge rank Chunithm (S, S+, SS, … via
generateRank.cjs) - Texte : titre, artiste, difficulté (avec couleur par difficulté) via drawtext
- Cache des images (score, jacket) pour éviter les régénérations
Codec vidéo : h264_nvenc (NVENC), audio : aac.
6. File d'attente d'export
- Ajout à la file avec toutes les métadonnées
- File persistante (
data/queue.json) : les jobs sont re-chargés au redémarrage - Statuts : queued → running (download → encoding) → done / error
- Progression : téléchargement (vitesse, ETA, taille) + encodage (pourcentage)
- Téléchargement du fichier final depuis l'interface
- Rafraîchissement automatique toutes les 3 secondes
7. Outils de génération
tools/generateRank.cjs: génère les badges rank Chunithm (S, S+, SS, SS+, SSS, SSS+) à partir d'une spritesheet templatetools/generateScore.cjs: génère les chiffres score arc-en-ciel (dernière ligne du template Chunithm) pour n'importe quel nombre- Utilisés automatiquement par le serveur lors du rendu
8. Overlays manuels / fallback
Si pas de Kamaitachi configuré ou pas de match :
- Saisie manuelle des scores dans le GUI
- Sauvegarde dans
data/timings.json - Optionnel : segments JSON dans la description Immich :
[{"start":12.5,"end":98.3,"label":"Song"}] - Parsing de patterns
start-enddans la description
API Routes
| Méthode | Route | Description |
|---|---|---|
GET |
/api/config |
Configuration exposée au client |
GET |
/api/album |
Liste des vidéos de l'album |
GET |
/api/assets/:id |
Détail d'un asset vidéo |
PUT |
/api/assets/:id/state |
Sauvegarde état éditeur (scores, cut, rotation, notes) |
GET |
/api/assets/:id/thumbnail |
Proxy vignette Immich |
GET |
/api/assets/:id/video |
Proxy vidéo Immich (range support) |
GET |
/api/assets/:id/download |
Téléchargement local vidéo |
GET |
/api/assets/:id/kamai-suggestions |
Suggestions scores Kamaitachi |
GET |
/api/render/preview-frame |
Frame PNG 9:16 (GET, avec params query) |
POST |
/api/render/preview-frame |
Frame PNG 9:16 (POST, avec body) |
POST |
/api/render/render |
Rendu vidéo complet |
POST |
/api/render/queue/add |
Ajout à la file d'attente |
GET |
/api/render/queue/status |
Statut de la file d'attente |
GET |
/api/render/download/:name |
Téléchargement fichier rendu |
GET |
/api/local-data |
Données locales (data.json racine) |
GET |
/api/data |
Catalogue songs (data.json) |
GET |
/api/covers |
Liste covers locales |
GET |
/api/health |
Healthcheck |
Kamaitachi — configuration
Les variables d'environnement dans .env :
KAMAITACHI_URL=https://kamai.tachi.ac
KAMAITACHI_API_KEY=ta_cle
KAMAITACHI_USER=ton_pseudo
KAMAITACHI_GAME=chunithm
KAMAITACHI_PLAYTYPE=
Obtenir la clé API Kamaitachi
- Connecte-toi sur kamai.tachi.ac
- Va dans Integrations / Developer → crée un API Client
- Utilise le Client File Flow :
https://kamai.tachi.ac/client-file-flow/TON_CLIENT_ID - Kamaitachi te fournit la clé
- Mets ton pseudo exact (sensible à la casse) dans
KAMAITACHI_USER
API V3 : le game path est /chunithm (plus /chunithm/Single). Les jeux multi-playtype deviennent /iidx-sp, etc.
Data
data/gameSongData/chunithm.json
Catalogue des chansons Chunithm utilisé pour le sélecteur de musique. Structure :
- Songs avec title, artist, category, version, imageName (jacket)
- Sheets par difficulté (basic → ultima) avec level, internalLevel, note counts
Fichiers locaux (WORK_DIR)
| Fichier / Dossier | Rôle |
|---|---|
data/timings.json |
Store des états éditeur (scores, cuts, rotation) |
data/queue.json |
File d'attente persistante |
data/raw/ |
Vidéos téléchargées depuis Immich |
data/output/ |
Vidéos rendues |
data/cache/ |
Images mises en cache (scores, placeholders) |
data/covers/ |
Jackets téléchargés |
data/mask/ |
Masques PNG pour overlay jacket/box |
FFmpeg
Requis avec support h264_nvenc pour l'encodage GPU. L'overlay utilise drawtext avec police wqy-zenhei pour le support CJK.
Sur Arch / CachyOS : pacman -S ffmpeg wqy-zenhei
Roadmap
- Liste des vidéos de l'album Immich
- Lecteur + timeline avec marqueurs scores
- Cut début/fin manuel
- Rotation 90°/180°/270°
- Sauvegarde locale des timings
- Rendu vertical FFmpeg (overlay jacket, score, rank, textes)
- File d'attente persistante
- Prévisualisation temps réel du rendu
- Intégration Kamaitachi (matching scores → vidéos)
- Catalogue songs Chunithm + sélecteur musique
- Génération assets rank/score (CHUNITHM style)
- Preview layout avec masques customisables
- Push YouTube / Shorts / TikTok / Insta
- Accès distant (Tailscale / Cloudflare Tunnel)