fix: kein Server-PDF, Browser-Druck-Anleitung stattdessen

This commit is contained in:
2026-06-12 15:46:07 +02:00
parent c916267200
commit 2e89669c9b

View File

@@ -12,12 +12,8 @@ export default function PaywallKiller() {
const [inputUrl, setInputUrl] = useState('');
const [status, setStatus] = useState('idle');
const [archiveUrl, setArchiveUrl] = useState('');
const [snapshotUrl, setSnapshotUrl] = useState('');
const [captchaDone, setCaptchaDone] = useState(false);
const [errMsg, setErrMsg] = useState('');
const [pdfLoading, setPdfLoading] = useState(false);
const inputRef = useRef(null);
const snapshotRef = useRef(null);
const inputRef = useRef(null);
function getTargetUrl() {
const url = inputUrl.trim();
@@ -28,60 +24,18 @@ export default function PaywallKiller() {
if (!inputUrl.trim()) return;
const aUrl = `https://archive.ph/newest/${getTargetUrl()}`;
setArchiveUrl(aUrl);
setSnapshotUrl('');
setStatus('open_archive');
setCaptchaDone(false);
setErrMsg('');
}
async function handlePdf() {
const urlToUse = snapshotUrl.trim() || archiveUrl;
if (!urlToUse) return;
setPdfLoading(true);
setErrMsg('');
try {
const token = localStorage.getItem('sk_token');
const res = await fetch('/api/tools/paywallkiller/pdf', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
body: JSON.stringify({ archiveUrl: urlToUse }),
});
if (!res.ok) {
const d = await res.json().catch(() => ({}));
throw new Error(d.error || 'PDF-Fehler');
}
const blob = await res.blob();
const objUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = objUrl;
a.download = 'artikel_' + Date.now() + '.pdf';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(() => URL.revokeObjectURL(objUrl), 5000);
} catch (err) {
setErrMsg(err.message || 'PDF fehlgeschlagen');
} finally {
setPdfLoading(false);
}
}
function handleReset() {
setInputUrl('');
setStatus('idle');
setArchiveUrl('');
setSnapshotUrl('');
setCaptchaDone(false);
setErrMsg('');
setPdfLoading(false);
setTimeout(() => inputRef.current?.focus(), 50);
}
const canPdf = captchaDone && (snapshotUrl.trim().startsWith('https://archive.') || archiveUrl);
return (
<div style={{ padding: '20px 16px', maxWidth: 640, margin: '0 auto' }}>
@@ -122,40 +76,25 @@ export default function PaywallKiller() {
{status === 'open_archive' && (
<>
{/* Schritt 1 */}
<div style={{ ...S.card, borderColor: `${C.accent}55`, marginBottom: 12 }}>
<div style={{ ...S.card, borderColor: captchaDone ? `${C.success}55` : `${C.accent}55`, marginBottom: 12 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
<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 }}>1</span>
<span style={{ color: C.accent, fontFamily: 'monospace', fontSize: 12 }}>ARCHIVE.PH ÖFFNEN & CAPTCHA LÖSEN</span>
</div>
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', marginBottom: 12, lineHeight: 1.7 }}>
Öffne archive.ph, löse das CAPTCHA, warte bis der Artikel vollständig sichtbar ist.{'\n'}
Kopiere dann die URL aus der Adressleiste (sieht aus wie{' '}
<span style={{ color: 'rgba(255,255,255,0.5)' }}>archive.ph/AbCdE</span>) und füge sie unten ein.
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', lineHeight: 1.7, marginBottom: 12 }}>
Öffne archive.ph und löse das CAPTCHA. Warte bis der vollständige Artikel sichtbar ist, dann zurückkommen und Schritt 2 ausführen.
</div>
<a
href={archiveUrl}
target="_blank"
rel="noreferrer"
style={{ ...S.btn(C.accent), display: 'block', textAlign: 'center', textDecoration: 'none', width: '100%', boxSizing: 'border-box', fontSize: 13, marginBottom: 12 }}>
<a href={archiveUrl} target="_blank" rel="noreferrer"
style={{ ...S.btn(C.accent), display: 'block', textAlign: 'center', textDecoration: 'none', boxSizing: 'border-box', fontSize: 13, marginBottom: 12 }}>
🌐 archive.ph öffnen
</a>
{/* Snapshot-URL Eingabe */}
<div style={{ ...S.head, marginBottom: 6, fontSize: 10 }}>SNAPSHOT-URL AUS ADRESSLEISTE (optional)</div>
<input
ref={snapshotRef}
value={snapshotUrl}
onChange={e => setSnapshotUrl(e.target.value)}
placeholder="https://archive.ph/AbCdE"
style={{ ...S.inp, width: '100%', boxSizing: 'border-box', marginBottom: 12, fontSize: 12 }}
/>
{/* Checkbox */}
<div
onClick={() => setCaptchaDone(v => !v)}
<div onClick={() => setCaptchaDone(v => !v)}
style={{ display: 'flex', alignItems: 'center', gap: 12, cursor: 'pointer', padding: '10px 12px', background: captchaDone ? `${C.success}12` : 'rgba(255,255,255,0.03)', border: `1px solid ${captchaDone ? C.success + '55' : 'rgba(255,255,255,0.1)'}`, borderRadius: 8 }}>
<div style={{ width: 22, height: 22, borderRadius: 5, flexShrink: 0, background: captchaDone ? C.success : 'transparent', border: `2px solid ${captchaDone ? C.success : 'rgba(255,255,255,0.3)'}`, display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'all 0.15s' }}>
{captchaDone && <span style={{ color: '#000', fontSize: 14, fontWeight: 'bold', lineHeight: 1 }}></span>}
<div style={{ width: 22, height: 22, borderRadius: 5, flexShrink: 0, background: captchaDone ? C.success : 'transparent', border: `2px solid ${captchaDone ? C.success : 'rgba(255,255,255,0.3)'}`, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
{captchaDone && <span style={{ color: '#000', fontSize: 14, fontWeight: 'bold' }}></span>}
</div>
<span style={{ color: captchaDone ? C.success : 'rgba(255,255,255,0.6)', fontFamily: 'monospace', fontSize: 12 }}>
Artikel ist vollständig sichtbar
@@ -163,52 +102,50 @@ export default function PaywallKiller() {
</div>
</div>
{/* Schritt 2 nur wenn Checkbox gesetzt */}
{/* Schritt 2 PDF über Browser-Druck */}
{captchaDone && (
<div style={{ ...S.card, borderColor: `${C.success}55`, marginBottom: 12 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 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 }}>2</span>
<span style={{ color: C.success, fontFamily: 'monospace', fontSize: 12 }}>PDF ERSTELLEN</span>
<span style={{ color: C.success, fontFamily: 'monospace', fontSize: 12 }}>ALS PDF SPEICHERN</span>
</div>
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', marginBottom: 12, lineHeight: 1.6 }}>
{snapshotUrl.trim()
? `Nutzt Snapshot-URL: ${snapshotUrl.trim()}`
: 'Nutzt: ' + archiveUrl}
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', lineHeight: 1.7, marginBottom: 14 }}>
Öffne den Artikel nochmal und nutze die <strong style={{ color: 'rgba(255,255,255,0.7)' }}>Drucken-Funktion</strong> deines Browsers um ihn als PDF zu speichern:
</div>
<button onClick={handlePdf} disabled={pdfLoading}
style={{ ...S.btn(C.success), width: '100%', fontSize: 13, opacity: pdfLoading ? 0.6 : 1, cursor: pdfLoading ? 'default' : 'pointer' }}>
{pdfLoading ? '⏳ PDF wird erstellt (1530s)...' : '⬇ Als PDF speichern'}
</button>
{/* Anleitung */}
{[
['📱', 'Android Chrome', 'Menü (⋮) → Teilen → Drucken → Als PDF speichern'],
['🍎', 'iOS Safari', 'Teilen → Als PDF sichern'],
['💻', 'Desktop', 'Strg+P / Cmd+P → Ziel: Als PDF speichern'],
].map(([icon, label, desc]) => (
<div key={label} style={{ display: 'flex', gap: 10, marginBottom: 8, padding: '8px 10px', background: 'rgba(255,255,255,0.03)', borderRadius: 6, border: '1px solid rgba(255,255,255,0.07)' }}>
<span style={{ fontSize: 16, flexShrink: 0 }}>{icon}</span>
<div>
<div style={{ color: 'rgba(255,255,255,0.7)', fontFamily: 'monospace', fontSize: 11, marginBottom: 2 }}>{label}</div>
<div style={{ color: C.muted, fontFamily: 'monospace', fontSize: 10, lineHeight: 1.5 }}>{desc}</div>
</div>
</div>
))}
<a href={archiveUrl} target="_blank" rel="noreferrer"
style={{ ...S.btn(C.success), display: 'block', textAlign: 'center', textDecoration: 'none', boxSizing: 'border-box', fontSize: 13, marginTop: 4 }}>
🖨 Artikel öffnen Als PDF drucken
</a>
</div>
)}
</>
)}
{errMsg && (
<div style={{ ...S.card, borderColor: `${C.error}44`, marginBottom: 12 }}>
<div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
<span></span>
<div>
<div style={{ color: C.error, fontFamily: 'monospace', fontSize: 12, marginBottom: errMsg.includes('CAPTCHA') ? 8 : 0 }}>{errMsg}</div>
{errMsg.includes('CAPTCHA') && (
<a href={archiveUrl} target="_blank" rel="noreferrer"
style={{ ...S.btn(C.accent), display: 'inline-block', textDecoration: 'none', fontSize: 11 }}>
🌐 archive.ph nochmal öffnen
</a>
)}
</div>
</div>
</div>
)}
{status === 'idle' && (
<div style={{ ...S.card }}>
<div style={{ ...S.head, marginBottom: 10 }}>SO FUNKTIONIERT ES</div>
{[
['1', 'URL des gesperrten Artikels einfügen'],
['2', 'archive.ph im Browser öffnen, CAPTCHA einmalig lösen'],
['3', 'Snapshot-URL aus Adressleiste kopieren und einfügen'],
['4', 'PDF automatisch herunterladen'],
['2', 'archive.ph öffnen, CAPTCHA einmalig lösen'],
['3', 'Bestätigen dass der Artikel sichtbar ist'],
['4', 'Über Drucken-Funktion als PDF speichern'],
].map(([num, text]) => (
<div key={num} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', marginBottom: 8 }}>
<span style={{ background: `${C.accent}20`, border: `1px solid ${C.accent}44`, borderRadius: '50%', width: 20, height: 20, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, color: C.accent, fontFamily: 'monospace', marginTop: 2 }}>{num}</span>