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 @@
@@ -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 ----