first commit
This commit is contained in:
195
README.md
Normal file
195
README.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# proxytmdb
|
||||
|
||||
Proxy/cache local de l'API TMDB enrichi des notes IMDb, avec recherche
|
||||
intelligente par titre/année/épisode. 100% Node.js (Fastify + worker_threads).
|
||||
|
||||
Conçu pour servir une base TMDB complète (~1.7M entrées movies + tv) sans
|
||||
appel API à chaque requête : tout est mis en cache sur disque par un cron
|
||||
quotidien et indexé en mémoire.
|
||||
|
||||
## Stack
|
||||
|
||||
- **Node.js 20+** (ESM, fetch natif, worker_threads, Intl.NumberFormat)
|
||||
- **Fastify 5** + `@fastify/secure-session` + `@fastify/static`
|
||||
- Aucune base de données — système de fichiers `<type>/<floor(id/1000)>/<id>.json`
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install
|
||||
cp .env.example .env
|
||||
# Edite .env : TMDB_API_KEY, PROXYTMDB_PASSWORD, SESSION_SECRET
|
||||
```
|
||||
|
||||
## Démarrage du serveur
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
Le `.env` est chargé automatiquement (Node 20+ `--env-file-if-exists`).
|
||||
|
||||
### Variables d'environnement
|
||||
|
||||
Obligatoires (sans valeur par défaut) :
|
||||
|
||||
| Variable | Rôle |
|
||||
|------------------------|------------------------------------------|
|
||||
| `TMDB_API_KEY` | Clé API TMDB |
|
||||
| `PROXYTMDB_PASSWORD` | Mot de passe page d'index |
|
||||
| `SESSION_SECRET` | Clé de signature des sessions (32 chars) |
|
||||
|
||||
Optionnelles (voir [.env.example](.env.example) pour la liste complète) :
|
||||
`PORT`, `HOST`, `PAGE_TITLE`, URLs externes, paramètres de recherche
|
||||
(`TITLE_TOLERANCE`, `LEV_*`, `YEAR_TOLERANCE`...).
|
||||
|
||||
## Cron (mise à jour des données)
|
||||
|
||||
Pipeline complet :
|
||||
|
||||
```bash
|
||||
npm run cron
|
||||
```
|
||||
|
||||
Étapes individuelles disponibles :
|
||||
|
||||
```bash
|
||||
npm run cron:imdb # title.ratings.tsv depuis datasets.imdbws.com
|
||||
npm run cron:tmdb # sync incrementale TMDB (movie + tv)
|
||||
npm run cron:justwatch # watch providers
|
||||
npm run cron:tmdb2imdb # mappings bidirectionnels TMDb <-> IMDb
|
||||
npm run cron:search # construction des chunks de recherche
|
||||
npm run cron:ambiguity # detection des doublons (CSV)
|
||||
```
|
||||
|
||||
Crontab système recommandée :
|
||||
|
||||
```
|
||||
13 13 * * * /home/matt/_WEB/proxytmdb/cron/run.sh > /home/matt/_WEB/proxytmdb/lastcron.txt 2>&1
|
||||
```
|
||||
|
||||
Le wrapper [cron/run.sh](cron/run.sh) charge nvm puis lance Node sur la
|
||||
version `nvm alias default` — pratique si tu mets à jour Node via nvm, le
|
||||
cron suivra automatiquement.
|
||||
|
||||
Si tu n'utilises pas nvm, remplace par :
|
||||
|
||||
```
|
||||
13 13 * * * cd /home/matt/_WEB/proxytmdb && /usr/bin/node --env-file-if-exists=.env cron/runAll.js > lastcron.txt 2>&1
|
||||
```
|
||||
|
||||
## Endpoints HTTP
|
||||
|
||||
### `GET /api?t=movie&q=<tmdb_id>`
|
||||
Retourne le JSON TMDB complet (cache local) avec les notes IMDb fusionnées
|
||||
(`note_imdb`, `vote_imdb`).
|
||||
|
||||
### `GET /api?t=tv&q=<tmdb_id>`
|
||||
Idem pour les séries.
|
||||
|
||||
### `GET /api?t=search&q=<requête>`
|
||||
Recherche par titre/année/épisode. Parse la requête (extraction année,
|
||||
extraction épisode `SxxExx`/`partN`/`NxN`/`Exxx`), choisit movie ou tv selon
|
||||
la présence d'un épisode, calcule les distances Levenshtein UTF-8 sur les
|
||||
titres FR/EN/VO, retourne les meilleurs résultats triés par score puis
|
||||
popularité puis écart d'année.
|
||||
|
||||
Réponse JSON :
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"title": "Inception",
|
||||
"english_title": "Inception",
|
||||
"original_title": "Inception",
|
||||
"years": 2010,
|
||||
"poster": "https://image.tmdb.org/t/p/w200/...",
|
||||
"genres": "Action Science-Fiction Aventure ",
|
||||
"countries": "GB US ",
|
||||
"runtime": "2 h 28 min",
|
||||
"imdb_id": "tt1375666",
|
||||
"imdb_url": "https://www.imdb.com/title/tt1375666",
|
||||
"note_imdb": 8.8,
|
||||
"vote_imdb": 2809792,
|
||||
"tmdb_id": 27205,
|
||||
"tmdb_url": "https://www.themoviedb.org/movie/27205",
|
||||
"note_tmdb": 8.4,
|
||||
"vote_tmdb": 39044,
|
||||
"budget": "$160,000,000",
|
||||
"revenue": "$839,030,630",
|
||||
"tagline": "...",
|
||||
"overview": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
L'ancien chemin `/api.php` est aussi exposé pour compatibilité.
|
||||
|
||||
### `GET /search?query=<requête>`
|
||||
Mêmes données que `/api?t=search` mais rendues en HTML (vignettes posters,
|
||||
panneau d'info, liens IMDb/TMDb cliquables). Compat : `/search.php`.
|
||||
|
||||
### `GET /`
|
||||
Page d'index protégée par mot de passe + listing des fichiers du projet.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
proxytmdb/
|
||||
├── server.js # Bootstrap Fastify
|
||||
├── config.js # Constantes, ports, chemins
|
||||
├── lib/
|
||||
│ ├── paths.js # Layout disque <type>/<floor(id/1000)>/<id>.json
|
||||
│ ├── mbLevenshtein.js # Levenshtein UTF-8 par codepoint
|
||||
│ ├── titleFilter.js # Translit ligatures + filtre Latin/chiffres
|
||||
│ ├── queryParser.js # Extraction annee/episode/titre
|
||||
│ ├── imdbRatings.js # Index IMDb en memoire (Map)
|
||||
│ ├── http.js # fetch + retry + concurrence limitee
|
||||
│ ├── format.js # Devises et runtime
|
||||
│ ├── searchEngine.js # Pool de 8 workers persistants
|
||||
│ └── searchWorker.js # Worker thread (1 chunk de la base)
|
||||
├── routes/
|
||||
│ ├── index.js # Login + listing protege
|
||||
│ ├── api.js # Endpoints JSON
|
||||
│ └── search.js # Vue HTML
|
||||
├── cron/
|
||||
│ ├── runAll.js # Pipeline complet
|
||||
│ ├── imdbRatings.js # title.ratings.tsv.gz
|
||||
│ ├── tmdbExports.js # Exports quotidiens TMDB
|
||||
│ ├── tmdbSync.js # Sync incrementale via /changes
|
||||
│ ├── justwatchSync.js # /watch/providers
|
||||
│ ├── tmdb2imdb.js # Mappings bidirectionnels
|
||||
│ ├── buildSearch.js # Chunks de recherche
|
||||
│ └── ambiguity.js # Detection des doublons
|
||||
├── test/
|
||||
│ └── helpers.test.js # Tests unitaires
|
||||
├── tmdbintegral/ # Donnees (gitignore)
|
||||
└── imdbratings.tsv # Donnees IMDb (gitignore)
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
13 tests unitaires sur les helpers (Levenshtein UTF-8, filtre titres, parser
|
||||
de requête, paths). La parité contre le PHP original a été validée pendant
|
||||
le portage sur 5 requêtes différentes (films, séries, ASCII, UTF-8, avec et
|
||||
sans année) — top-N IDs identiques dans le même ordre.
|
||||
|
||||
## Notes
|
||||
|
||||
- Les fichiers `searchmovie<i>.json` et `searchtv<i>.json` sont des **chunks
|
||||
de recherche** générés par le cron. Le serveur en charge un par worker au
|
||||
démarrage (mémoire résidente, ~120 Mo total).
|
||||
- L'index IMDb (`imdbratings.tsv`, ~30 Mo, 1.66M lignes) est chargé une fois
|
||||
en `Map` au démarrage et rechargé automatiquement quand son `mtime` change
|
||||
(typiquement après le cron quotidien).
|
||||
- Le layout disque `<type>/<floor(id/1000)>/<id>.json` est conservé à
|
||||
l'identique de l'ancienne version PHP : on peut basculer sans regénérer
|
||||
les ~1700 dossiers x 1000 fichiers existants.
|
||||
- Les 8 workers de recherche sont persistants (vs PHP qui forkait 8
|
||||
processus à chaque requête). Gain mesuré : ~30% sur le temps de réponse.
|
||||
Reference in New Issue
Block a user