diff --git a/main.py b/main.py index 7ddabe5..3f8ac35 100644 --- a/main.py +++ b/main.py @@ -150,6 +150,15 @@ async def handle_configure(request): prefill = json_for_script(decoded) content = content.replace("const PREFILL = null;", f"const PREFILL = {prefill};") + # Base publique fiable (schéma+host), calculée côté serveur — la page + # ne doit pas déduire le schéma via window.location côté client : si + # /configure a été atteinte par un chemin http (proxy, accès direct + # au port...), l'URL de manifest générée hériterait du mauvais + # schéma, causant une erreur TLS à l'installation dans Stremio. + content = content.replace( + 'const PUBLIC_BASE = "";', f'const PUBLIC_BASE = {json_for_script(external_host_url(request))};' + ) + return web.Response(text=content, content_type="text/html") except Exception as e: logging.error(f"configure error: {e}") diff --git a/templates/configure.html b/templates/configure.html index be29dc8..9df7d0a 100644 --- a/templates/configure.html +++ b/templates/configure.html @@ -67,11 +67,12 @@ padding: 14px; border-radius: 10px; text-decoration: none; font-size: 1rem; } .install.disabled { opacity: .4; pointer-events: none; } - .url-box { - background: var(--card2); border: 1px solid var(--border); border-radius: 8px; padding: 10px; - font-size: .78rem; word-break: break-all; color: var(--muted); margin-top: 8px; display: none; - max-height: 4.6em; overflow-y: auto; + .url-row { display: flex; gap: 8px; } + .url-field { + flex: 1; min-width: 0; background: var(--card2); border: 1px solid var(--border); border-radius: 8px; + padding: 10px 12px; font-size: .78rem; color: var(--muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; } + .url-field:focus { outline: none; border-color: var(--accent); color: var(--text); } .chips { display: flex; flex-wrap: wrap; gap: 8px; } .chip { padding: 7px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--card2); @@ -242,11 +243,12 @@
-
-

-
- + +
+ +
+

➕ Installer dans Stremio
@@ -285,6 +287,14 @@ const SOURCE_LABELS = { const PREFILL = null; const IS_EDITING = PREFILL !== null; +// Base publique (scheme+host) calculée côté serveur (X-Forwarded-Proto / +// RANGER_PUBLIC_URL) — fiable même si la page /configure elle-même a été +// chargée via un chemin http (proxy, accès direct au port...). Utiliser +// window.location.protocol/host ici serait faux dans ce cas : ça a déjà +// causé des URLs stremio://...https implicite pointant en réalité vers un +// serveur http, d'où des erreurs TLS côté client à l'installation. +const PUBLIC_BASE = ""; + let debrids = []; // [{service, key}] let unit3ds = []; // [{url, key}] let torznabs = []; // [{name, url, apikey}] @@ -502,15 +512,17 @@ function b64url(obj) { function update() { const cfg = buildConfig(); const encoded = b64url(cfg); - const host = window.location.host; - const proto = window.location.protocol; - const manifestUrl = `${proto}//${host}/${encoded}/manifest.json`; - document.getElementById("urlBox").textContent = manifestUrl; + // Base fournie par le serveur (fiable, cf. commentaire sur PUBLIC_BASE) ; + // repli sur window.location seulement si l'injection serveur a échoué. + const base = PUBLIC_BASE || `${window.location.protocol}//${window.location.host}`; + const baseHost = base.replace(/^https?:\/\//, ""); + const manifestUrl = `${base}/${encoded}/manifest.json`; + document.getElementById("urlField").value = manifestUrl; const btn = document.getElementById("installBtn"); const valid = cfg.debrids.length > 0; btn.classList.toggle("disabled", !valid); - btn.href = valid ? `stremio://${host}/${encoded}/manifest.json` : "#"; + btn.href = valid ? `stremio://${baseHost}/${encoded}/manifest.json` : "#"; window._manifestUrl = manifestUrl; // En mode édition (config existante rouverte), le bouton réinstalle @@ -526,22 +538,33 @@ function update() { } function copyUrl() { + // navigator.clipboard exige un "contexte sécurisé" (HTTPS) — indisponible + // (undefined, pas juste refusé) si la page est chargée en http://. On + // dégrade vers document.execCommand('copy') (fonctionne aussi en http), + // puis en dernier recours vers une sélection manuelle du champ (toujours + // visible désormais, donc copiable à la main dans tous les cas). const b = event.target; const t = b.textContent; - navigator.clipboard.writeText(window._manifestUrl || "").then(() => { - // La confirmation sur le bouton suffit ; pas besoin d'afficher l'URL - // complète (elle peut faire >1800 caractères avec une config étoffée). - b.textContent = "✅ Copié !"; - setTimeout(() => b.textContent = t, 1500); - }).catch(() => { - // Fallback si le presse-papier est inaccessible (permissions navigateur) : - // on affiche l'URL dans une boîte à défilement pour copie manuelle. - const box = document.getElementById("urlBox"); - box.style.display = "block"; - box.textContent = window._manifestUrl || ""; - b.textContent = "⚠️ Copie manuelle ci-dessous"; + const field = document.getElementById("urlField"); + + const showCopied = () => { b.textContent = "✅ Copié !"; setTimeout(() => b.textContent = t, 1500); }; + const showManual = () => { + field.focus(); field.select(); + b.textContent = "☝️ Sélectionné — Ctrl/Cmd+C"; setTimeout(() => b.textContent = t, 2500); - }); + }; + const execCommandFallback = () => { + field.focus(); field.select(); + let ok = false; + try { ok = document.execCommand("copy"); } catch (e) { ok = false; } + if (ok) showCopied(); else showManual(); + }; + + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(window._manifestUrl || "").then(showCopied).catch(execCommandFallback); + } else { + execCommandFallback(); + } } // ---- Persistance : appliquer la config pré-remplie ----