fix: kein Countdown, ehrlicher UX mit Retry-Button und klaren Hinweisen

This commit is contained in:
2026-06-12 17:10:29 +02:00
parent 115ad2eb87
commit f19deb1897

View File

@@ -8,27 +8,10 @@ const C = {
muted: '#6b7280',
};
// Wayback CDX prüft ob Snapshot existiert (CORS: *)
async function checkWayback(targetUrl) {
try {
const res = await fetch(
`https://web.archive.org/cdx/search/cdx?url=${encodeURIComponent(targetUrl)}&output=json&limit=1&fl=timestamp&filter=statuscode:200&fastLatest=true`
);
if (!res.ok) return null;
const data = await res.json();
if (Array.isArray(data) && data.length >= 2 && data[1]?.[0]) {
return `https://web.archive.org/web/${data[1][0]}/${targetUrl}`;
}
} catch {}
return null;
}
export default function PaywallKiller() {
const [inputUrl, setInputUrl] = useState('');
const [archiveUrl, setArchiveUrl] = useState('');
// status: idle | checking | archiving | ready | notfound
const [status, setStatus] = useState('idle');
const [statusMsg, setStatusMsg] = useState('');
const inputRef = useRef(null);
function getTargetUrl() {
@@ -36,32 +19,12 @@ export default function PaywallKiller() {
return url.startsWith('http') ? url : 'https://' + url;
}
async function handleSearch() {
function handleSearch() {
if (!inputUrl.trim()) return;
const targetUrl = getTargetUrl();
setStatus('checking');
setStatusMsg('Prüfe ob Archiv existiert...');
setArchiveUrl('');
// Schritt 1: Prüfen ob archive.ph schon einen Snapshot hat
// archive.ph /newest/ leitet weiter wenn gefunden, sonst Suchseite
// Wir nutzen CDX der Wayback Machine als schnelle Vorab-Prüfung
// und bauen immer die archive.ph URL auf
const newestUrl = `https://archive.ph/newest/${targetUrl}`;
// Parallel: Wayback prüfen (liefert uns Info ob überhaupt archiviert)
// Aber primär: archive.ph direkt versuchen
// Falls "No results" → automatisch archivieren via no-cors submit
setArchiveUrl(newestUrl);
// Schritt 2: archive.ph Submit (fire-and-forget, no-cors)
// Das triggert die Archivierung falls noch nicht vorhanden
setStatus('archiving');
setStatusMsg('Archiviere Artikel (falls noch nicht vorhanden)...');
// archive.ph Submit (no-cors, fire-and-forget) triggert Archivierung
try {
// archive.ph Submit via no-cors kein Response lesbar aber Request geht raus
const formData = new FormData();
formData.append('url', targetUrl);
formData.append('anyway', '1');
@@ -72,13 +35,7 @@ export default function PaywallKiller() {
}).catch(() => {});
} catch {}
// Schritt 3: 8s warten dann Artikel öffnen
// (archive.ph braucht ~5-15s zum Archivieren)
for (let i = 8; i > 0; i--) {
setStatusMsg(`Archivierung läuft... öffnet in ${i}s`);
await new Promise(r => setTimeout(r, 1000));
}
setArchiveUrl(`https://archive.ph/newest/${targetUrl}`);
setStatus('ready');
}
@@ -86,30 +43,28 @@ export default function PaywallKiller() {
setInputUrl('');
setArchiveUrl('');
setStatus('idle');
setStatusMsg('');
setTimeout(() => inputRef.current?.focus(), 50);
}
return (
<div style={{ padding: '20px 16px', maxWidth: 640, margin: '0 auto' }}>
{/* Header */}
<div style={{ marginBottom: 24 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 4 }}>
<span style={{ fontSize: 20 }}>🔓</span>
<span style={{ color: C.accent, fontFamily: 'monospace', fontSize: 13, letterSpacing: 1 }}>PAYWALL-KILLER</span>
</div>
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', marginBottom: 10 }}>archive.ph · Als PDF speichern</div>
{/* Getestet-Badges */}
<div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
<span style={{ color: 'rgba(255,255,255,0.25)', fontFamily: 'monospace', fontSize: 9, letterSpacing: 0.5 }}>GETESTET MIT</span>
{[
{ name: 'waz.de', favicon: 'https://www.google.com/s2/favicons?domain=waz.de&sz=16' },
{ name: 'bild.de', favicon: 'https://www.google.com/s2/favicons?domain=bild.de&sz=16' },
{ name: 'heise.de', favicon: 'https://www.google.com/s2/favicons?domain=heise.de&sz=16' },
].map(({ name, favicon }) => (
{ name: 'waz.de', domain: 'waz.de' },
{ name: 'bild.de', domain: 'bild.de' },
{ name: 'heise.de', domain: 'heise.de' },
].map(({ name, domain }) => (
<div key={name} style={{ display: 'flex', alignItems: 'center', gap: 4, padding: '2px 7px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.08)', borderRadius: 4 }}>
<img src={favicon} width={12} height={12} style={{ borderRadius: 2, opacity: 0.7 }} alt="" />
<img src={`https://www.google.com/s2/favicons?domain=${domain}&sz=16`} width={12} height={12} style={{ borderRadius: 2, opacity: 0.7 }} alt="" />
<span style={{ color: 'rgba(255,255,255,0.35)', fontFamily: 'monospace', fontSize: 9 }}>{name}</span>
</div>
))}
@@ -142,32 +97,17 @@ export default function PaywallKiller() {
</div>
</div>
{/* Lade-Status */}
{(status === 'checking' || status === 'archiving') && (
<div style={{ ...S.card, borderColor: `${C.accent}44`, marginBottom: 12 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<span style={{ fontSize: 16 }}></span>
<span style={{ color: C.accent, fontFamily: 'monospace', fontSize: 12 }}>{statusMsg}</span>
</div>
{status === 'archiving' && (
<div style={{ color: C.muted, fontSize: 10, fontFamily: 'monospace', marginTop: 8, lineHeight: 1.5 }}>
archive.ph archiviert den Artikel. Das dauert einen Moment. Anschließend wird er automatisch geöffnet.
</div>
)}
</div>
)}
{/* Ergebnis */}
{status === 'ready' && archiveUrl && (
{status === 'ready' && (
<div style={{ ...S.card, borderColor: `${C.accent}55`, marginBottom: 12 }}>
<div style={{ ...S.head, marginBottom: 12 }}>ARTIKEL ÖFFNEN & ALS PDF SPEICHERN</div>
{/* Schritt 1 */}
<div style={{ display: 'flex', gap: 10, marginBottom: 16 }}>
<div style={{ display: 'flex', gap: 10, marginBottom: 4 }}>
<span style={{ background: `${C.accent}20`, border: `1px solid ${C.accent}44`, borderRadius: '50%', width: 22, height: 22, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, color: C.accent, fontFamily: 'monospace', flexShrink: 0, marginTop: 1 }}>1</span>
<div style={{ flex: 1 }}>
<div style={{ color: 'rgba(255,255,255,0.7)', fontFamily: 'monospace', fontSize: 11, marginBottom: 8 }}>
Öffne archive.ph falls CAPTCHA erscheint einmalig lösen:
Öffne den Artikel auf archive.ph. Falls CAPTCHA erscheint: einmalig lösen. Falls "No results": auf <strong style={{ color: 'rgba(255,255,255,0.5)' }}>"archive this url"</strong> klicken, kurz warten, dann nochmal öffnen.
</div>
<a href={archiveUrl} target="_blank" rel="noreferrer"
style={{ ...S.btn(C.accent), display: 'block', textAlign: 'center', textDecoration: 'none', boxSizing: 'border-box', fontSize: 13 }}>
@@ -176,12 +116,23 @@ export default function PaywallKiller() {
</div>
</div>
{/* Nochmal öffnen nach Archivierung */}
<div style={{ margin: '10px 0 16px 32px', padding: '8px 12px', background: 'rgba(255,255,255,0.03)', borderRadius: 6, border: '1px solid rgba(255,255,255,0.07)' }}>
<div style={{ color: C.muted, fontSize: 10, fontFamily: 'monospace', marginBottom: 6 }}>
Nach "archive this url" ~1530s warten, dann:
</div>
<a href={archiveUrl} target="_blank" rel="noreferrer"
style={{ ...S.btn(C.muted), display: 'inline-block', textDecoration: 'none', fontSize: 11 }}>
🔄 Nochmal öffnen
</a>
</div>
{/* Schritt 2 */}
<div style={{ display: 'flex', gap: 10 }}>
<span style={{ background: `${C.success}20`, border: `1px solid ${C.success}44`, borderRadius: '50%', width: 22, height: 22, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, color: C.success, fontFamily: 'monospace', flexShrink: 0, marginTop: 1 }}>2</span>
<div style={{ flex: 1 }}>
<div style={{ color: 'rgba(255,255,255,0.7)', fontFamily: 'monospace', fontSize: 11, marginBottom: 8 }}>
Als PDF speichern:
Wenn der Artikel sichtbar ist, als PDF speichern:
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
{[
@@ -211,8 +162,8 @@ export default function PaywallKiller() {
<div style={{ ...S.head, marginBottom: 10 }}>SO FUNKTIONIERT ES</div>
{[
['1', 'URL des gesperrten Artikels einfügen'],
['2', 'Archiv wird automatisch gesucht und erstellt falls nötig'],
['3', 'Artikel auf archive.ph öffnen CAPTCHA einmalig lösen'],
['2', 'Artikel auf archive.ph öffnen'],
['3', 'Falls nicht archiviert: "archive this url" klicken, kurz warten, nochmal öffnen'],
['4', 'Über Drucken-Funktion als PDF speichern'],
].map(([num, text]) => (
<div key={num} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', marginBottom: 8 }}>