fix: Snapshot-URL Eingabe nach CAPTCHA, direkter Hash-URL für PDF

This commit is contained in:
2026-06-12 15:12:10 +02:00
parent 88cd754fb5
commit d0dc2fc3d1

View File

@@ -12,10 +12,12 @@ export default function PaywallKiller() {
const [inputUrl, setInputUrl] = useState(''); const [inputUrl, setInputUrl] = useState('');
const [status, setStatus] = useState('idle'); const [status, setStatus] = useState('idle');
const [archiveUrl, setArchiveUrl] = useState(''); const [archiveUrl, setArchiveUrl] = useState('');
const [snapshotUrl, setSnapshotUrl] = useState('');
const [captchaDone, setCaptchaDone] = useState(false); const [captchaDone, setCaptchaDone] = useState(false);
const [errMsg, setErrMsg] = useState(''); const [errMsg, setErrMsg] = useState('');
const [pdfLoading, setPdfLoading] = useState(false); const [pdfLoading, setPdfLoading] = useState(false);
const inputRef = useRef(null); const inputRef = useRef(null);
const snapshotRef = useRef(null);
function getTargetUrl() { function getTargetUrl() {
const url = inputUrl.trim(); const url = inputUrl.trim();
@@ -26,6 +28,7 @@ export default function PaywallKiller() {
if (!inputUrl.trim()) return; if (!inputUrl.trim()) return;
const aUrl = `https://archive.ph/newest/${getTargetUrl()}`; const aUrl = `https://archive.ph/newest/${getTargetUrl()}`;
setArchiveUrl(aUrl); setArchiveUrl(aUrl);
setSnapshotUrl('');
setStatus('open_archive'); setStatus('open_archive');
setCaptchaDone(false); setCaptchaDone(false);
setErrMsg(''); setErrMsg('');
@@ -36,6 +39,8 @@ export default function PaywallKiller() {
} }
async function handlePdf() { async function handlePdf() {
const urlToUse = snapshotUrl.trim() || archiveUrl;
if (!urlToUse) return;
setPdfLoading(true); setPdfLoading(true);
setErrMsg(''); setErrMsg('');
try { try {
@@ -46,7 +51,7 @@ export default function PaywallKiller() {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}), ...(token ? { Authorization: `Bearer ${token}` } : {}),
}, },
body: JSON.stringify({ archiveUrl }), body: JSON.stringify({ archiveUrl: urlToUse }),
}); });
if (!res.ok) { if (!res.ok) {
const d = await res.json().catch(() => ({})); const d = await res.json().catch(() => ({}));
@@ -72,16 +77,18 @@ export default function PaywallKiller() {
setInputUrl(''); setInputUrl('');
setStatus('idle'); setStatus('idle');
setArchiveUrl(''); setArchiveUrl('');
setSnapshotUrl('');
setCaptchaDone(false); setCaptchaDone(false);
setErrMsg(''); setErrMsg('');
setPdfLoading(false); setPdfLoading(false);
setTimeout(() => inputRef.current?.focus(), 50); setTimeout(() => inputRef.current?.focus(), 50);
} }
const canPdf = captchaDone && (snapshotUrl.trim().startsWith('https://archive.') || archiveUrl);
return ( return (
<div style={{ padding: '20px 16px', maxWidth: 640, margin: '0 auto' }}> <div style={{ padding: '20px 16px', maxWidth: 640, margin: '0 auto' }}>
{/* Header */}
<div style={{ marginBottom: 24 }}> <div style={{ marginBottom: 24 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 4 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 4 }}>
<span style={{ fontSize: 20 }}>🔓</span> <span style={{ fontSize: 20 }}>🔓</span>
@@ -116,83 +123,92 @@ export default function PaywallKiller() {
</div> </div>
</div> </div>
{/* Schritt 1: archive.ph öffnen + CAPTCHA */}
{status === 'open_archive' && ( {status === 'open_archive' && (
<div style={{ ...S.card, borderColor: `${C.accent}55`, marginBottom: 16 }}> <>
<div style={{ ...S.head, marginBottom: 8 }}> {/* Schritt 1 */}
<span style={{ background: `${C.accent}20`, border: `1px solid ${C.accent}44`, borderRadius: '50%', width: 18, height: 18, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, marginRight: 8 }}>1</span> <div style={{ ...S.card, borderColor: `${C.accent}55`, marginBottom: 12 }}>
ARCHIVE.PH ÖFFNEN <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>
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', marginBottom: 12, lineHeight: 1.7 }}>
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', marginBottom: 14, lineHeight: 1.7 }}> Öffne archive.ph, löse das CAPTCHA, warte bis der Artikel vollständig sichtbar ist.{'\n'}
Öffne archive.ph im Browser. Falls ein CAPTCHA erscheint: abhaken und warten bis der Artikel vollständig sichtbar ist. Dann zurückkommen und Schritt 2 ausführen. 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> </div>
<button onClick={handleOpenArchive} <button onClick={handleOpenArchive}
style={{ ...S.btn(C.accent), width: '100%', marginBottom: 14, fontSize: 13 }}> style={{ ...S.btn(C.accent), width: '100%', fontSize: 13, marginBottom: 12 }}>
🌐 archive.ph öffnen 🌐 archive.ph öffnen
</button> </button>
{/* Checkbox-Bestätigung */} {/* 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 <div
onClick={() => setCaptchaDone(v => !v)} onClick={() => setCaptchaDone(v => !v)}
style={{ display: 'flex', alignItems: 'center', gap: 12, cursor: 'pointer', padding: '10px 12px', background: captchaDone ? `${C.success}15` : 'rgba(255,255,255,0.03)', border: `1px solid ${captchaDone ? C.success + '55' : 'rgba(255,255,255,0.1)'}`, borderRadius: 8 }}> 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={{ <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' }}>
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>} {captchaDone && <span style={{ color: '#000', fontSize: 14, fontWeight: 'bold', lineHeight: 1 }}></span>}
</div> </div>
<span style={{ color: captchaDone ? C.success : 'rgba(255,255,255,0.6)', fontFamily: 'monospace', fontSize: 12 }}> <span style={{ color: captchaDone ? C.success : 'rgba(255,255,255,0.6)', fontFamily: 'monospace', fontSize: 12 }}>
Artikel ist sichtbar CAPTCHA gelöst Artikel ist vollständig sichtbar
</span> </span>
</div> </div>
</div> </div>
)}
{/* Schritt 2: PDF erstellen */} {/* Schritt 2 nur wenn Checkbox gesetzt */}
{status === 'open_archive' && captchaDone && ( {captchaDone && (
<div style={{ ...S.card, borderColor: `${C.success}55`, marginBottom: 16 }}> <div style={{ ...S.card, borderColor: `${C.success}55`, marginBottom: 12 }}>
<div style={{ ...S.head, marginBottom: 8 }}> <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: 18, height: 18, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, marginRight: 8 }}>2</span> <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>
PDF ERSTELLEN <span style={{ color: C.success, fontFamily: 'monospace', fontSize: 12 }}>PDF ERSTELLEN</span>
</div> </div>
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', marginBottom: 12, lineHeight: 1.6 }}> <div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', marginBottom: 12, lineHeight: 1.6 }}>
Der Server lädt den Artikel über archive.ph und erstellt ein PDF (1530 Sekunden). {snapshotUrl.trim()
? `Nutzt Snapshot-URL: ${snapshotUrl.trim()}`
: 'Nutzt: ' + archiveUrl}
</div> </div>
<button onClick={handlePdf} disabled={pdfLoading} <button onClick={handlePdf} disabled={pdfLoading}
style={{ ...S.btn(C.success), width: '100%', fontSize: 13, opacity: pdfLoading ? 0.6 : 1, cursor: pdfLoading ? 'default' : 'pointer' }}> style={{ ...S.btn(C.success), width: '100%', fontSize: 13, opacity: pdfLoading ? 0.6 : 1, cursor: pdfLoading ? 'default' : 'pointer' }}>
{pdfLoading ? '⏳ PDF wird erstellt...' : '⬇ Als PDF speichern'} {pdfLoading ? '⏳ PDF wird erstellt (1530s)...' : '⬇ Als PDF speichern'}
</button> </button>
{pdfLoading && (
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', marginTop: 8, textAlign: 'center' }}>
Seite wird gerendert, bitte warten...
</div> </div>
)} )}
</div> </>
)} )}
{/* Fehler */}
{errMsg && ( {errMsg && (
<div style={{ ...S.card, borderColor: `${C.error}44`, marginBottom: 12 }}> <div style={{ ...S.card, borderColor: `${C.error}44`, marginBottom: 12 }}>
<div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}> <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
<span></span> <span></span>
<span style={{ color: C.error, fontFamily: 'monospace', fontSize: 12 }}>{errMsg}</span> <div>
<div style={{ color: C.error, fontFamily: 'monospace', fontSize: 12, marginBottom: errMsg.includes('CAPTCHA') ? 8 : 0 }}>{errMsg}</div>
{errMsg.includes('CAPTCHA') && (
<button onClick={handleOpenArchive}
style={{ ...S.btn(C.accent), fontSize: 11 }}>
🌐 archive.ph nochmal öffnen
</button>
)}
</div>
</div> </div>
</div> </div>
)} )}
{/* Idle-Info */}
{status === 'idle' && ( {status === 'idle' && (
<div style={{ ...S.card }}> <div style={{ ...S.card }}>
<div style={{ ...S.head, marginBottom: 10 }}>SO FUNKTIONIERT ES</div> <div style={{ ...S.head, marginBottom: 10 }}>SO FUNKTIONIERT ES</div>
{[ {[
['1', 'URL des gesperrten Artikels einfügen'], ['1', 'URL des gesperrten Artikels einfügen'],
['2', 'archive.ph im Browser öffnen, CAPTCHA einmalig lösen'], ['2', 'archive.ph im Browser öffnen, CAPTCHA einmalig lösen'],
['3', 'Bestätigen dass der Artikel sichtbar ist'], ['3', 'Snapshot-URL aus Adressleiste kopieren und einfügen'],
['4', 'PDF automatisch herunterladen'], ['4', 'PDF automatisch herunterladen'],
].map(([num, text]) => ( ].map(([num, text]) => (
<div key={num} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', marginBottom: 8 }}> <div key={num} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', marginBottom: 8 }}>