fix: Snapshot-URL Eingabe nach CAPTCHA, direkter Hash-URL für PDF
This commit is contained in:
@@ -9,13 +9,15 @@ const C = {
|
||||
};
|
||||
|
||||
export default function PaywallKiller() {
|
||||
const [inputUrl, setInputUrl] = useState('');
|
||||
const [status, setStatus] = useState('idle');
|
||||
const [archiveUrl, setArchiveUrl] = useState('');
|
||||
const [captchaDone, setCaptchaDone] = useState(false);
|
||||
const [errMsg, setErrMsg] = useState('');
|
||||
const [pdfLoading, setPdfLoading] = useState(false);
|
||||
const inputRef = useRef(null);
|
||||
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);
|
||||
|
||||
function getTargetUrl() {
|
||||
const url = inputUrl.trim();
|
||||
@@ -26,6 +28,7 @@ export default function PaywallKiller() {
|
||||
if (!inputUrl.trim()) return;
|
||||
const aUrl = `https://archive.ph/newest/${getTargetUrl()}`;
|
||||
setArchiveUrl(aUrl);
|
||||
setSnapshotUrl('');
|
||||
setStatus('open_archive');
|
||||
setCaptchaDone(false);
|
||||
setErrMsg('');
|
||||
@@ -36,6 +39,8 @@ export default function PaywallKiller() {
|
||||
}
|
||||
|
||||
async function handlePdf() {
|
||||
const urlToUse = snapshotUrl.trim() || archiveUrl;
|
||||
if (!urlToUse) return;
|
||||
setPdfLoading(true);
|
||||
setErrMsg('');
|
||||
try {
|
||||
@@ -46,7 +51,7 @@ export default function PaywallKiller() {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
},
|
||||
body: JSON.stringify({ archiveUrl }),
|
||||
body: JSON.stringify({ archiveUrl: urlToUse }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const d = await res.json().catch(() => ({}));
|
||||
@@ -72,16 +77,18 @@ export default function PaywallKiller() {
|
||||
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' }}>
|
||||
|
||||
{/* Header */}
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 4 }}>
|
||||
<span style={{ fontSize: 20 }}>🔓</span>
|
||||
@@ -116,83 +123,92 @@ export default function PaywallKiller() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Schritt 1: archive.ph öffnen + CAPTCHA */}
|
||||
{status === 'open_archive' && (
|
||||
<div style={{ ...S.card, borderColor: `${C.accent}55`, marginBottom: 16 }}>
|
||||
<div style={{ ...S.head, marginBottom: 8 }}>
|
||||
<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>
|
||||
ARCHIVE.PH ÖFFNEN
|
||||
</div>
|
||||
|
||||
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', marginBottom: 14, lineHeight: 1.7 }}>
|
||||
Ö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.
|
||||
</div>
|
||||
|
||||
<button onClick={handleOpenArchive}
|
||||
style={{ ...S.btn(C.accent), width: '100%', marginBottom: 14, fontSize: 13 }}>
|
||||
🌐 archive.ph öffnen
|
||||
</button>
|
||||
|
||||
{/* Checkbox-Bestätigung */}
|
||||
<div
|
||||
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 }}>
|
||||
<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>}
|
||||
<>
|
||||
{/* Schritt 1 */}
|
||||
<div style={{ ...S.card, borderColor: `${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>
|
||||
<span style={{ color: captchaDone ? C.success : 'rgba(255,255,255,0.6)', fontFamily: 'monospace', fontSize: 12 }}>
|
||||
Artikel ist sichtbar – CAPTCHA gelöst
|
||||
</span>
|
||||
</div>
|
||||
</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>
|
||||
<button onClick={handleOpenArchive}
|
||||
style={{ ...S.btn(C.accent), width: '100%', fontSize: 13, marginBottom: 12 }}>
|
||||
🌐 archive.ph öffnen
|
||||
</button>
|
||||
|
||||
{/* Schritt 2: PDF erstellen */}
|
||||
{status === 'open_archive' && captchaDone && (
|
||||
<div style={{ ...S.card, borderColor: `${C.success}55`, marginBottom: 16 }}>
|
||||
<div style={{ ...S.head, marginBottom: 8 }}>
|
||||
<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>
|
||||
PDF ERSTELLEN
|
||||
{/* 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)}
|
||||
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>
|
||||
<span style={{ color: captchaDone ? C.success : 'rgba(255,255,255,0.6)', fontFamily: 'monospace', fontSize: 12 }}>
|
||||
Artikel ist vollständig sichtbar
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<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 (15–30 Sekunden).
|
||||
</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...' : '⬇ Als PDF speichern'}
|
||||
</button>
|
||||
{pdfLoading && (
|
||||
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', marginTop: 8, textAlign: 'center' }}>
|
||||
Seite wird gerendert, bitte warten...
|
||||
|
||||
{/* Schritt 2 – nur wenn Checkbox gesetzt */}
|
||||
{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>
|
||||
</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>
|
||||
<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 (15–30s)...' : '⬇ Als PDF speichern'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Fehler */}
|
||||
{errMsg && (
|
||||
<div style={{ ...S.card, borderColor: `${C.error}44`, marginBottom: 12 }}>
|
||||
<div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
|
||||
<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>
|
||||
)}
|
||||
|
||||
{/* Idle-Info */}
|
||||
{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', 'Bestätigen dass der Artikel sichtbar ist'],
|
||||
['3', 'Snapshot-URL aus Adressleiste kopieren und einfügen'],
|
||||
['4', 'PDF automatisch herunterladen'],
|
||||
].map(([num, text]) => (
|
||||
<div key={num} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', marginBottom: 8 }}>
|
||||
|
||||
Reference in New Issue
Block a user