Commit graph

17 commits

Author SHA1 Message Date
Valentin
51130931f4 Fix: URL de manifest fiable + champ toujours visible et copiable
Some checks failed
Build & Push Docker image / build (push) Has been cancelled
Un utilisateur a signalé "unable to parse TLS packet header" en installant
via le bouton, et un bouton "Copier" qui ne fait rien sur Brave/Android.

Cause racine commune : /configure construisait l'URL du manifest et le lien
stremio:// côté client via window.location.protocol/host — fiable
uniquement si la page /configure elle-même a été chargée par le même chemin
que celui utilisé ensuite pour installer l'addon. Si un utilisateur atteint
le VPS par un chemin différent (accès direct au port, proxy sans
X-Forwarded-Proto...), la page peut se charger en http alors que le domaine
public est en https (ou l'inverse) : le lien stremio:// généré promet un
schéma que le serveur ne tient pas à cette adresse, d'où l'erreur TLS.

- Le serveur injecte désormais PUBLIC_BASE (calculé via external_host_url,
  la même logique fiable X-Forwarded-Proto/RANGER_PUBLIC_URL qui sert déjà
  au manifest JSON) dans la page /configure, échappé comme PREFILL. Le JS
  ne déduit plus jamais le schéma/host depuis window.location.
- navigator.clipboard exige un contexte sécurisé (HTTPS) : sur http://, il
  est absent (pas juste refusé), ce qui expliquait le bouton "Copier"
  silencieusement inopérant. copyUrl() dégrade maintenant vers
  document.execCommand('copy') (fonctionne aussi en http), puis en dernier
  recours sélectionne le texte du champ pour une copie manuelle.
- Le champ URL du manifest est désormais TOUJOURS visible (readonly,
  sélectionnable au clic) au lieu d'être cascadé derrière un clic sur
  "Copier" — décorrèle la consultation/copie manuelle du bouton d'action,
  qui reste utilisable indépendamment pour l'installation en un clic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 11:17:24 +02:00
Valentin
6e6703d012 Fix: "0 résultats" intermittent — budget de recherche + cache vide trop long
Audit complet du dépôt suite à un signalement de résultats vides
intermittents qui se résolvaient tout seuls après plusieurs minutes.
Deux causes racines identifiées et corrigées :

1. run_search() attendait TOUS les trackers (asyncio.gather sans budget)
   avant de renvoyer quoi que ce soit. Chaque tracker a son propre timeout
   (10-20s), mais un seul tracker capricieux qui va au bout du sien retarde
   toute la réponse — combiné à la récupération des métadonnées (jusqu'à
   20s) et la vérification de disponibilité débrideur (jusqu'à 20s), la
   latence totale pouvait dépasser ce que Stremio tolère côté client,
   donnant un "0 résultat" trompeur qui se résout dès que le tracker
   redevient rapide. Ajout d'un budget global de 15s (asyncio.wait) : les
   trackers pas encore revenus sont abandonnés pour cette requête sans
   bloquer les autres, même principe que le budget déjà en place sur la
   vérification débrideur.

2. Un résultat de recherche VIDE (tracker qui répond 200 avec 0 item lors
   d'un accroc passager, pas une exception) était mis en cache pour le
   TTL_SEARCH complet (30 min par défaut) — verrouillant "aucun résultat"
   pour TOUT LE MONDE sur cette instance partagée jusqu'à expiration, même
   une fois le tracker redevenu normal. Nouveau TTL_SEARCH_EMPTY (3 min par
   défaut, RANGER_TTL_SEARCH_EMPTY) appliqué uniquement aux résultats vides.

Durcissement additionnel (trouvé en auditant les autres appels réseau) :
- Aucune instance aiohttp.ClientSession n'avait de timeout par défaut au
  niveau session ; plusieurs appels dans alldebrid.py/torbox.py/
  stremthru.py/realdebrid.py/debridlink.py (chemin /resolve, au clic sur
  lecture) n'avaient pas de timeout par appel non plus, héritant du défaut
  aiohttp de 300s. Ajout d'un timeout de session par défaut (20s) partout,
  qui n'écrase pas les timeouts par-appel déjà présents.
- Suppression de services/tmdb.py (code mort, jamais importé — remplacé
  depuis le début par core/metadata.py).

Le ping /health toutes les 30s dans les logs est le HEALTHCHECK Docker
normal (urllib.request, cf. Dockerfile), pas un souci.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 09:30:49 +02:00
Valentin
8e0ce975df UX: bouton "appliquer" en mode édition + copie manifeste compacte
Some checks are pending
Build & Push Docker image / build (push) Waiting to run
- /configure détecte quand une config existante est rouverte (PREFILL) et
  bascule le bouton principal sur "🔄 Appliquer les modifications" avec un
  texte d'aide, au lieu de garder "Installer dans Stremio" (confus quand on
  édite un lien déjà installé). Stremio remplace l'installation existante
  (même ID d'addon) au clic, aucun changement backend nécessaire.
- "Copier l'URL du manifest" n'affiche plus la boîte de texte avec l'URL
  complète (>1800 caractères avec une config étoffée, prenait toute la
  hauteur d'écran) : la confirmation sur le bouton suffit. La boîte ne
  s'affiche qu'en secours si le presse-papier est inaccessible, avec une
  hauteur plafonnée et défilement interne.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 15:33:56 +02:00
Valentin
956a79d726 Security: correctifs suite à un audit externe (P0 critiques + P1 rapides)
Some checks are pending
Build & Push Docker image / build (push) Waiting to run
Suite à un audit externe du dépôt, correction des vulnérabilités confirmées
après vérification manuelle du code :

P0 (critiques) :
- RealDebrid : suppression d'un serveur StremThru tiers codé en dur
  (stremthru.13377001.xyz) auquel la clé API RD était envoyée à l'insu de
  l'utilisateur dès lors qu'aucune StremThru propre n'était configurée.
  Remplacé par un vrai client API Real-Debrid officiel (api.real-debrid.com
  exclusivement).
- XSS réfléchie sur /configure : la config décodée était injectée brute
  (json.dumps) dans un <script> inline, une valeur contenant "</script>"
  cassait hors du tag. Échappement des caractères dangereux en séquences
  unicode avant injection.
- XSS stockée dans le dashboard admin : un ID Stremio non validé entrait
  dans les clés de cache puis était rendu via innerHTML sans échappement
  (dont un point d'injection via onclick jamais échappé du tout). Validation
  stricte de l'IMDB ID côté serveur (^tt\d{5,9}$) + reconstruction du
  dashboard sans innerHTML/onclick interpolés (DOM API + addEventListener).
- Fuite de cache inter-comptes : la clé de cache recherche et la clé de
  cache des liens résolus ne dépendaient pas des identifiants utilisés,
  seulement de l'IMDB/hash. Deux profils différents (ex: plusieurs membres
  d'une famille) partageant la même instance pouvaient recevoir les
  résultats ou liens streaming de l'autre. Namespacing par empreinte des
  credentials (core.cache.fingerprint) pour les trackers/débrideurs à clé ;
  cache resté partagé pour les trackers publics (pas de credential, pas de
  fuite).
- SSRF via Torznab/UNIT3D : les URLs d'indexeurs viennent de la config
  utilisateur, or Ranger est exposé publiquement (config auto-suffisante,
  pas besoin de connaître de clé). Ajout de core/netsafety.py qui résout et
  bloque les cibles loopback/link-local/réservées (dont le 169.254.169.254
  des métadonnées cloud) avant chaque requête ; RFC1918 reste autorisé
  (usage prévu : Jackett/Prowlarr auto-hébergé sur le LAN de l'opérateur).

Corrections additionnelles :
- Token admin : comparaison à temps constant (hmac.compare_digest) et
  suppression du fallback ?token= en query string (loggable par les CDN/proxy).
- Conteneur Docker : utilisateur non-root (UID/GID 1000 fixes).
- RANGER_PUBLIC_URL (optionnel) : URL publique fixe prioritaire sur les
  en-têtes X-Forwarded-Proto/Host, potentiellement usurpables si le port du
  conteneur est aussi joignable directement.
- Détection anime : le genre Animation (16) est désormais requis dans tous
  les cas — un "or orig_lang == 'ja'" classait n'importe quel drama
  live-action japonais comme anime.
- Numérotation absolue transmise jusqu'à /resolve (elle ne servait qu'au
  filtrage) : la sélection du bon fichier dans un pack anime fansub peut
  désormais matcher l'épisode absolu en plus de SxxExx.
- Budget de 20s sur la vérification de disponibilité multi-débrideurs : un
  backend lent/hors service ne bloque plus toute la réponse /stream.
- UNIT3D : suppression de download_torrent (code mort, jamais appelé) et
  réduction du logging de réponse brute (risque de fuite de tokens signés).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 11:51:33 +02:00
Valentin
79ef3401a3 Fix: contenu mixte HTTPS/HTTP bloquant le chargement du manifest sur Stremio
Derrière Cloudflare/un reverse proxy, request.scheme reflète la connexion
interne (souvent http) et non le schéma vu par le client (https). Le
manifest servi en https contenait un logo en http://, et les URLs de
résolution des streams auraient eu le même problème à la lecture.
L'app Stremio officielle (contrainte de contenu mixte, contrairement aux
proxys serveur type AIOStreams ou aux apps natives type Nuvio) restait
bloquée indéfiniment sur "Loading addon manifest".

Ajout de external_scheme()/external_host_url() qui respectent
X-Forwarded-Proto (posé par Cloudflare/Caddy/nginx), utilisés partout
à la place de request.scheme direct (logo, manifest, resolve URLs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 11:15:07 +02:00
Valentin
b2233be562 Feat: score qualité, cache liens résolus, mode d'affichage simplifié
- Score qualité global (résolution/source/HDR/codec/langue/seeders) +
  critère de tri "quality" (), sélectionnable et par défaut en 2e position.
  Tri par défaut élargi à tous les critères (dont priorité tracker).
- Cache court des liens débrideur résolus (TTL 15 min, clé service+hash+SE+ip)
  -> seek instantané, ~5x moins d'appels débrideur/StremThru. Table dédiée,
  visible et purgeable dans l'admin.
- Mode d'affichage : "detailed" (actuel) ou "simple" (famille) = 1 lien par
  résolution, meilleur choix auto (cache puis score qualité).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 01:05:38 +02:00
Valentin
f873050b36 UX: retrait du réglage P2P + défauts de filtres plus sensés
- Suppression de la case "Afficher les liens P2P" (source de confusion,
  peu utile face au débrideur). Le champ backend show_p2p reste mais figé
  à false ; l'install requiert désormais au moins un débrideur.
- Défauts filtres : max/résolution = 10 (au lieu de 0), résultats = 30,
  taille min/max = 0 (illimité), résolutions/codecs/langues = toutes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:25:58 +02:00
Valentin
7babe764bc UX: fusion des réglages de cache en un seul sélecteur
Les deux cases "Uniquement en cache" / "Afficher les non-cachés" étaient un
choix binaire confus. Remplacées par un sélecteur "Résultats débrideur" :
cache seul ou cache + non-cachés (ajout au débrideur au clic). Le backend
(cached_only / show_uncached) est inchangé.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:21:31 +02:00
Valentin
ce54b92bf9 Persistance config, labels trackers lisibles, format AIOStreams, logo R
- Persistance : /{config}/configure ré-injecte la config (PREFILL) et
  pré-remplit tout le formulaire (clés, débrideurs, trackers, filtres, tri).
- Priorité trackers : labels lisibles (ThePirateBay au lieu de "apibay", etc.)
  dans les listes de tri et de priorité des sources.
- Format stream compatible AIOStreams (comme Frenchio) : champs title +
  description + métadonnées top-level (size, seeders, quality, codec,
  release_type, language) en plus des tags [XX+].
- Logo : "R" violet sur fond sombre arrondi (SVG servi sur /logo.svg),
  référencé par le manifest (logo + icon), reflète la homepage web.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 20:13:11 +02:00
Valentin
ff379145cf Feat: panel admin (/admin) avec stats, explorateur de cache et force-refresh
- Protégé par RANGER_ADMIN_TOKEN (panel désactivé si non défini)
- Dashboard : uptime, taille DB, requêtes/streams servis, résolutions,
  hit-rate cache (recherche + dispo), répartition par débrideur/source
- Explorateur de cache : recherches + métadonnées, suppression unitaire
- Force-refresh par IMDB ID (purge recherches+méta -> re-fetch Stremio frais)
- Maintenance : cleanup expirés, flush sélectif ou total
- Métriques hit/miss instrumentées dans le cache

Fix: web.Response étant falsy, `if err:` ne bloquait pas l'auth admin
-> remplacé par `if err is not None:`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 19:48:07 +02:00
Valentin
bce7037e90 Feat: choix de StremThru par débrideur (case à cocher)
Chaque débrideur peut router via StremThru ou utiliser son API native,
indépendamment. Utile pour ne proxifier que les débrideurs IP-bloqués
(ex: AllDebrid) tout en gardant TorBox en direct. Défaut : via StremThru
(rétro-compatible avec les configs existantes).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 15:39:49 +02:00
Valentin
23f3620668 Fix: bug de closure dans l'orchestrateur de recherche
Les lambdas capturaient la variable svc par référence : toutes les
coroutines tapaient le dernier tracker construit (EZTV pour les séries,
Nyaa pour les animes) → résultats mono-source, erreur original_title
sur NyaaService, et rate-limit 429 massif sur Nyaa.
Chaque lambda binde désormais son service via argument par défaut.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 15:30:42 +02:00
Valentin
b711cc720f CI : build multi-arch (amd64 + arm64) pour VPS ARM
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 15:15:13 +02:00
Valentin
75f271ca0e Doc : image ghcr publique (plus de docker login requis)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 15:13:43 +02:00
Valentin
070e7d7d67 Déploiement VPS via image ghcr pré-buildée (docker-compose.ghcr.yml)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 15:05:39 +02:00
Valentin
6bad2f3805 CI : build & push de l'image Docker vers ghcr.io
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 15:03:09 +02:00
Valentin
709265ccb8 Ranger v1.0.0 — addon Stremio ultime multi-trackers/multi-débrideurs
Addon Stremio complet, films/séries/anime, contenu FR et international :
- Multi-débrideurs avec priorité (AllDebrid, Real-Debrid, TorBox, DebridLink)
- StremThru intégré (contournement blocage IP datacenter)
- Compat AIOStreams (tags [XX+], statut cache)
- TMDB + fallback Cinemeta, détection anime, numérotation absolue
- Trackers publics (YGG, TPB, EZTV, Nyaa) + privés (C411, Torr9, Tr4ker,
  NekoBT, ABN, UNIT3D) + Torznab générique (Jackett/Prowlarr)
- Filtres taille/résolution/codec/langue, tri multi-critères, déduplication
- Cache SQLite (dispo débrideur + recherches + métadonnées)
- Page /configure interactive, image Docker + docker-compose

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 14:56:22 +02:00