Files
Torrent-Indicator/demo.html
2026-03-08 01:33:56 +01:00

388 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Torrent Indicator — Démo</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body {
font-family: system-ui, sans-serif;
background: #f6f8fa;
color: #24292f;
margin: 0;
padding: 32px 16px;
}
.page { max-width: 760px; margin: 0 auto; }
h1 { font-size: 1.5rem; margin: 0 0 4px; }
.subtitle { color: #57606a; margin: 0 0 40px; }
section { margin-bottom: 48px; }
h2 {
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: .06em;
color: #57606a;
border-bottom: 1px solid #d0d7de;
padding-bottom: 6px;
margin: 0 0 16px;
}
.description { color: #57606a; font-size: 14px; margin: 0 0 16px; }
.widgets-row { display: flex; flex-wrap: wrap; gap: 16px; }
pre {
background: #161b22;
color: #e6edf3;
padding: 14px 16px;
border-radius: 8px;
font-size: 12px;
overflow-x: auto;
margin: 12px 0 0;
line-height: 1.6;
}
/* Formulaire interactif */
.try-form {
display: flex;
flex-direction: column;
gap: 10px;
}
.try-form label { font-size: 13px; font-weight: 600; }
.try-form input {
font-family: monospace;
font-size: 13px;
padding: 8px 10px;
border: 1px solid #d0d7de;
border-radius: 6px;
background: #fff;
width: 100%;
}
.try-form input:focus { outline: 2px solid #0969da; border-color: transparent; }
.try-controls { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.try-form button {
padding: 8px 16px;
background: #0969da;
color: #fff;
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
}
.try-form button:hover { background: #0757b8; }
.layout-toggle { display: flex; gap: 4px; }
.layout-toggle label {
font-size: 12px;
font-weight: normal;
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
padding: 6px 10px;
border: 1px solid #d0d7de;
border-radius: 6px;
background: #fff;
}
.layout-toggle input[type=radio] { accent-color: #0969da; }
#try-result { margin-top: 16px; }
</style>
<!-- ============================================================
CSS + JS du widget — copier depuis ghost-inject.html
Adapter API_URL selon votre déploiement.
============================================================ -->
<style id="ti-styles">
.ti-widget{display:inline-flex;flex-direction:column;font-family:system-ui,sans-serif;font-size:13px;border:1px solid #d0d7de;border-radius:8px;overflow:hidden;min-width:180px;max-width:260px;background:#fff;box-shadow:0 1px 4px rgba(0,0,0,.08);color:#24292f}
.ti-header{display:flex;align-items:center;gap:6px;padding:8px 12px;background:#f6f8fa;border-bottom:1px solid #d0d7de;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:#57606a}
.ti-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:#8c8c8c;transition:background .3s}
.ti-body{padding:10px 12px;display:flex;flex-direction:column;gap:6px}
.ti-row{display:flex;justify-content:space-between;align-items:center}
.ti-label{color:#57606a}
.ti-value{font-weight:600;font-variant-numeric:tabular-nums}
.ti-badge{display:inline-block;padding:1px 7px;border-radius:12px;font-size:11px;font-weight:600;letter-spacing:.02em}
.ti-health-dead{background:#ffeef0;color:#cf222e}
.ti-health-poor{background:#fff3cd;color:#9a6700}
.ti-health-good,.ti-health-excellent{background:#dafbe1;color:#116329}
.ti-dot-dead{background:#cf222e}
.ti-dot-poor{background:#d4a900}
.ti-dot-good,.ti-dot-excellent{background:#2da44e}
.ti-pop-low{background:#f0f0f0;color:#57606a}
.ti-pop-moderate{background:#ddf4ff;color:#0550ae}
.ti-pop-popular{background:#dbedff;color:#0550ae}
.ti-pop-viral{background:#fff0f0;color:#a40000}
.ti-footer{padding:5px 12px 7px;font-size:10px;color:#8c959f;border-top:1px solid #f0f0f0;text-align:right}
.ti-loading,.ti-error{padding:14px 12px;text-align:center;color:#57606a;font-size:12px}
.ti-error{color:#cf222e}
.ti-widget--wide{max-width:680px;width:100%;margin:0 auto;min-width:0}
.ti-widget--wide .ti-body{flex-direction:row;padding:0;gap:0}
.ti-stat{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;flex:1;padding:14px 8px;border-right:1px solid #f0f0f0}
.ti-stat:last-child{border-right:none}
.ti-stat-label{font-size:10px;color:#8c959f;text-transform:uppercase;letter-spacing:.05em;font-weight:600}
.ti-stat-value{font-size:22px;font-weight:700;font-variant-numeric:tabular-nums;line-height:1}
@media(max-width:480px){
.ti-widget--wide .ti-body{flex-wrap:wrap}
.ti-stat{flex:1 1 50%;border-bottom:1px solid #f0f0f0}
.ti-stat:nth-child(2n){border-right:none}
.ti-stat:nth-last-child(-n+2){border-bottom:none}
}
</style>
</head>
<body>
<div class="page">
<h1>Torrent Indicator</h1>
<p class="subtitle">Exemples d'intégration sur une page HTML standard (sans Ghost).</p>
<!-- ============================================================
Cas 1 — Layout compact, via info hash
============================================================ -->
<section>
<h2>1 · Layout compact — via info hash</h2>
<p class="description">
Usage minimal : un <code>div</code> avec <code>data-hash</code>.
Idéal en sidebar ou en ligne dans un article.
</p>
<div class="widgets-row">
<div class="torrent-indicator"
data-hash="3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0"
data-label="Ubuntu 24.04 LTS"></div>
<div class="torrent-indicator"
data-hash="9bb80f655e2a0490b1ed7b19b63a7b2acacffe0e"
data-label="Debian 12 netinst"></div>
</div>
<pre>&lt;div class="torrent-indicator"
data-hash="3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0"
data-label="Ubuntu 24.04 LTS"&gt;&lt;/div&gt;</pre>
</section>
<!-- ============================================================
Cas 2 — Layout large, via info hash
============================================================ -->
<section>
<h2>2 · Layout large — centré, pleine largeur</h2>
<p class="description">
Ajouter <code>data-layout="wide"</code> pour un affichage horizontal
qui occupe toute la largeur disponible. Adaptatif sur mobile (grille 2×2).
</p>
<div class="torrent-indicator"
data-hash="3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0"
data-label="Ubuntu 24.04 LTS"
data-layout="wide"></div>
<pre>&lt;div class="torrent-indicator"
data-hash="3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0"
data-label="Ubuntu 24.04 LTS"
data-layout="wide"&gt;&lt;/div&gt;</pre>
</section>
<!-- ============================================================
Cas 3 — Via lien magnet
============================================================ -->
<section>
<h2>3 · Via lien magnet</h2>
<p class="description">
<code>data-magnet</code> accepte un lien magnet complet.
Le hash est extrait automatiquement (hex 40 chars ou base32).
</p>
<div class="torrent-indicator"
data-magnet="magnet:?xt=urn:btih:3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0&dn=ubuntu-24.04-desktop-amd64.iso"
data-label="Ubuntu via magnet"
data-layout="wide"></div>
<pre>&lt;div class="torrent-indicator"
data-magnet="magnet:?xt=urn:btih:3b245504...&amp;dn=ubuntu..."
data-label="Ubuntu via magnet"
data-layout="wide"&gt;&lt;/div&gt;</pre>
</section>
<!-- ============================================================
Cas 4 — Plusieurs widgets sur la même page
============================================================ -->
<section>
<h2>4 · Plusieurs widgets sur la même page</h2>
<p class="description">
Les deux layouts coexistent. Le script s'initialise une seule fois
et cible tous les <code>.torrent-indicator</code> présents.
</p>
<div class="torrent-indicator"
data-hash="3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0"
data-label="Ubuntu 24.04"
data-layout="wide"></div>
<br>
<div class="widgets-row">
<div class="torrent-indicator"
data-hash="9bb80f655e2a0490b1ed7b19b63a7b2acacffe0e"
data-label="Debian 12"></div>
<div class="torrent-indicator"
data-hash="3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0"
data-label="Ubuntu 24.04"></div>
</div>
</section>
<!-- ============================================================
Cas 5 — Formulaire interactif
============================================================ -->
<section>
<h2>5 · Essayer avec votre propre torrent</h2>
<p class="description">
Entrez un info hash (40 caractères hex) ou un lien magnet.
</p>
<div class="try-form">
<label for="try-input">Hash ou lien magnet</label>
<input id="try-input"
type="text"
placeholder="abc123... ou magnet:?xt=urn:btih:..."
value="3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0">
<div class="try-controls">
<button onclick="tryWidget()">Afficher</button>
<div class="layout-toggle">
<label><input type="radio" name="try-layout" value="" checked> Compact</label>
<label><input type="radio" name="try-layout" value="wide"> Large</label>
</div>
</div>
</div>
<div id="try-result"></div>
<pre>// Créer un widget dynamiquement via l'API JS
var el = document.createElement('div');
el.className = 'torrent-indicator';
el.setAttribute('data-hash', 'abc123...');
el.setAttribute('data-layout', 'wide'); // optionnel
document.body.appendChild(el);
TorrentIndicator.init(el);</pre>
</section>
</div><!-- /page -->
<!-- ============================================================
Script du widget — à placer juste avant </body>
Adapter API_URL selon votre déploiement.
============================================================ -->
<script>
(function () {
'use strict';
// ← Adapter selon votre déploiement
var API_URL = 'https://toscr.team4kw.fr';
var L = {
header:'État du torrent', seeders:'Seeders', leechers:'Leechers',
health:'Santé', popularity:'Popularité', updated:'Mis à jour',
health_dead:'Mort', health_poor:'Faible', health_good:'Bon', health_excellent:'Excellent',
pop_low:'Faible', pop_moderate:'Modérée', pop_popular:'Populaire', pop_viral:'Virale',
loading:'Chargement…', error:'Données indisponibles', no_data:'Aucun tracker n\'a répondu', stale:'données en cache',
};
function esc(s){ return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
function fmt(n){ return Number(n).toLocaleString('fr-FR'); }
function ts(){ var d=new Date(),p=function(n){return n<10?'0'+n:''+n;}; return p(d.getHours())+':'+p(d.getMinutes())+':'+p(d.getSeconds()); }
function row(label,val){ return '<div class="ti-row"><span class="ti-label">'+esc(label)+'</span>'+val+'</div>'; }
function footer(data) {
return data.stale
? L.updated+' à '+ts()+' · <em>'+L.stale+'</em>'
: L.updated+' à '+ts();
}
function stat(label, valueHtml) {
return '<div class="ti-stat"><span class="ti-stat-label">'+esc(label)+'</span>'+valueHtml+'</div>';
}
function render(el, data) {
var lbl = el.getAttribute('data-label') || L.header;
var wide = el.getAttribute('data-layout') === 'wide';
if (data.error) {
var cls = wide ? 'ti-widget ti-widget--wide' : 'ti-widget';
el.innerHTML='<div class="'+cls+'"><div class="ti-header"><span class="ti-dot"></span>'+esc(lbl)+'</div><div class="ti-error">'+esc(data.error)+'</div></div>';
return;
}
var h=data.health||'dead', p=data.popularity||'low';
if (wide) {
el.innerHTML='<div class="ti-widget ti-widget--wide">'
+'<div class="ti-header"><span class="ti-dot ti-dot-'+h+'"></span>'+esc(lbl)+'</div>'
+'<div class="ti-body">'
+stat(L.seeders, '<span class="ti-stat-value" style="color:#2da44e">'+fmt(data.seeders)+'</span>')
+stat(L.leechers, '<span class="ti-stat-value" style="color:#cf222e">'+fmt(data.leechers)+'</span>')
+stat(L.health, '<span class="ti-badge ti-health-'+h+'">'+esc(L['health_'+h]||h)+'</span>')
+stat(L.popularity,'<span class="ti-badge ti-pop-'+p+'">'+esc(L['pop_'+p]||p)+'</span>')
+'</div>'
+'<div class="ti-footer">'+footer(data)+'</div>'
+'</div>';
} else {
el.innerHTML='<div class="ti-widget">'
+'<div class="ti-header"><span class="ti-dot ti-dot-'+h+'"></span>'+esc(lbl)+'</div>'
+'<div class="ti-body">'
+row(L.seeders, '<span class="ti-value" style="color:#2da44e">'+fmt(data.seeders)+'</span>')
+row(L.leechers, '<span class="ti-value" style="color:#cf222e">'+fmt(data.leechers)+'</span>')
+row(L.health, '<span class="ti-badge ti-health-'+h+'">'+esc(L['health_'+h]||h)+'</span>')
+row(L.popularity,'<span class="ti-badge ti-pop-'+p+'">'+esc(L['pop_'+p]||p)+'</span>')
+'</div>'
+'<div class="ti-footer">'+footer(data)+'</div>'
+'</div>';
}
}
function buildUrl(el) {
var hash=el.getAttribute('data-hash'), magnet=el.getAttribute('data-magnet');
if (hash) return API_URL+'?hash='+encodeURIComponent(hash.trim());
if (magnet) return API_URL+'?magnet='+encodeURIComponent(magnet.trim());
return null;
}
function load(el) {
var url=buildUrl(el);
if (!url) { render(el,{error:'Attribut data-hash ou data-magnet manquant.'}); return; }
var lbl=el.getAttribute('data-label')||L.header;
var wide=el.getAttribute('data-layout')==='wide';
var cls=wide?'ti-widget ti-widget--wide':'ti-widget';
el.innerHTML='<div class="'+cls+'"><div class="ti-header"><span class="ti-dot"></span>'+esc(lbl)+'</div><div class="ti-loading">'+L.loading+'</div></div>';
var xhr=new XMLHttpRequest();
xhr.open('GET',url,true); xhr.timeout=15000;
xhr.onload=function(){
if(xhr.status>=200&&xhr.status<300){
try{ var d=JSON.parse(xhr.responseText); if(d.sources===0&&!d.stale) d.error=L.no_data; render(el,d); }
catch(e){ render(el,{error:L.error}); }
} else { render(el,{error:L.error+' ('+xhr.status+')'}); }
};
xhr.onerror=xhr.ontimeout=function(){ render(el,{error:L.error}); };
xhr.send();
}
function init() {
var els=document.querySelectorAll('.torrent-indicator');
for(var i=0;i<els.length;i++) load(els[i]);
}
document.readyState==='loading'
? document.addEventListener('DOMContentLoaded',init)
: init();
window.TorrentIndicator={ refreshAll:init, init:load };
})();
</script>
<script>
// Formulaire interactif (cas 5)
function tryWidget() {
var input = document.getElementById('try-input').value.trim();
var layout = document.querySelector('input[name="try-layout"]:checked').value;
var result = document.getElementById('try-result');
if (!input) return;
var el = document.createElement('div');
el.className = 'torrent-indicator';
el.setAttribute('data-label', 'Résultat');
var isMagnet = input.startsWith('magnet:');
el.setAttribute(isMagnet ? 'data-magnet' : 'data-hash', input);
if (layout) el.setAttribute('data-layout', layout);
result.innerHTML = '';
result.appendChild(el);
TorrentIndicator.init(el);
}
</script>
</body>
</html>