]*class=["'][^"']*THUMBS-BLOCK[^"']*["'][^>]*>[\s\S]*?<\/div>/gi, '')
- .replace(/
]*id=["']tools["'][^>]*>[\s\S]*?<\/div>/gi, '');
-
- // ── Links zurück auf Originale umschreiben ────────────────────────────
- // archive.ph schreibt Links um zu: https://archive.ph/TIMESTAMP/https://original.url
- // Wir extrahieren die Original-URL
- html = html.replace(
- /https?:\/\/archive\.(?:ph|is|today|fo|li|vn|md|gg)\/[a-zA-Z0-9]+\/(https?:\/\/[^"'\s>]+)/g,
- '$1'
- );
- // Auch relative Archive-Links
- html = html.replace(
- /href="\/[a-zA-Z0-9]{4,10}\/(https?:\/\/[^"]+)"/g,
- 'href="$1"'
- );
-
- // ── CSS für sauberes Drucken injizieren ───────────────────────────────
- const printCss = `
-
-`;
-
- // CSS vor einfügen
- if (html.includes('')) {
- html = html.replace('', printCss + '');
- } else {
- html = printCss + html;
- }
-
- // Base-Tag setzen damit relative URLs funktionieren
- const baseTag = `
`;
- if (html.includes('')) {
- html = html.replace('', '' + baseTag);
- }
-
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
- // X-Frame-Options entfernen damit wir es anzeigen können
- res.removeHeader('X-Frame-Options');
- res.send(html);
- } catch (err) {
- res.status(500).send(`Fehler: ${err.message}`);
- }
-});
-
-// ── GET /api/tools/paywallkiller/cdx?url=... ──────────────────────────────
-router.get('/cdx', authenticate, async (req, res) => {
- const { url } = req.query;
- if (!url) return res.status(400).json({ error: 'url fehlt' });
- let targetUrl = url.trim();
- if (!targetUrl.startsWith('http')) targetUrl = 'https://' + targetUrl;
- const cdxUrl = `https://web.archive.org/cdx/search/cdx?url=${encodeURIComponent(targetUrl)}&output=json&limit=1&fl=timestamp&filter=statuscode:200&fastLatest=true`;
- try {
- const r = await fetchHtml(cdxUrl);
- if (r.status !== 200) return res.status(502).json({ error: `CDX ${r.status}` });
- const data = JSON.parse(r.body);
- if (!Array.isArray(data) || data.length < 2 || !data[1]?.[0]) return res.json({ found: false });
- return res.json({ found: true, waybackUrl: `https://web.archive.org/web/${data[1][0]}/${targetUrl}` });
- } catch (err) {
- return res.status(500).json({ error: err.message });
- }
-});
+// Paywall-Killer hat keinen aktiven Backend-Endpoint mehr.
+// archive.ph blockt Server-seitige Requests (429).
+// Der komplette Flow läuft im Browser des Users.
module.exports = router;
diff --git a/frontend/src/tools/paywallkiller.jsx b/frontend/src/tools/paywallkiller.jsx
index c8816cf..79d15a3 100644
--- a/frontend/src/tools/paywallkiller.jsx
+++ b/frontend/src/tools/paywallkiller.jsx
@@ -9,13 +9,10 @@ const C = {
};
export default function PaywallKiller() {
- const [inputUrl, setInputUrl] = useState('');
- const [snapshotUrl, setSnapshotUrl] = useState('');
- const [status, setStatus] = useState('idle'); // idle | step1 | step2
- const [archiveUrl, setArchiveUrl] = useState('');
- const [captchaDone, setCaptchaDone] = useState(false);
- const inputRef = useRef(null);
- const snapshotRef = useRef(null);
+ const [inputUrl, setInputUrl] = useState('');
+ const [archiveUrl, setArchiveUrl] = useState('');
+ const [done, setDone] = useState(false);
+ const inputRef = useRef(null);
function getTargetUrl() {
const url = inputUrl.trim();
@@ -25,37 +22,16 @@ export default function PaywallKiller() {
function handleSearch() {
if (!inputUrl.trim()) return;
setArchiveUrl(`https://archive.ph/newest/${getTargetUrl()}`);
- setStatus('step1');
- setCaptchaDone(false);
- setSnapshotUrl('');
- }
-
- function handleCaptchaDone() {
- setCaptchaDone(true);
- setStatus('step2');
- setTimeout(() => snapshotRef.current?.focus(), 100);
- }
-
- function getRenderUrl() {
- const snap = snapshotUrl.trim();
- if (snap && snap.startsWith('https://archive.')) {
- const token = localStorage.getItem('sk_token') || '';
- return `/api/tools/paywallkiller/render?url=${encodeURIComponent(snap)}&token=${encodeURIComponent(token)}`;
- }
- return null;
+ setDone(false);
}
function handleReset() {
setInputUrl('');
- setSnapshotUrl('');
- setStatus('idle');
setArchiveUrl('');
- setCaptchaDone(false);
+ setDone(false);
setTimeout(() => inputRef.current?.focus(), 50);
}
- const renderUrl = getRenderUrl();
-
return (
@@ -64,114 +40,91 @@ export default function PaywallKiller() {
🔓
PAYWALL-KILLER
-
archive.ph · Bereinigt · Als PDF drucken
+
archive.ph · Als PDF speichern
ARTIKEL-URL
setInputUrl(e.target.value)}
- onKeyDown={e => e.key === 'Enter' && status === 'idle' && handleSearch()}
+ onKeyDown={e => e.key === 'Enter' && handleSearch()}
placeholder="URL einfügen (Long-Press → Einfügen)"
style={{ ...S.inp, width: '100%', boxSizing: 'border-box', marginBottom: 10 }}
/>
- {status === 'idle' ? (
-
- 🔍 Archiv suchen
-
- ) : (
+
+ 🔍 Archiv suchen
+
+ {archiveUrl && (
- ✕ Reset
+ ✕
)}
- {/* Schritt 1: archive.ph öffnen */}
- {(status === 'step1' || status === 'step2') && (
-
-
- 1
- ARCHIVE.PH ÖFFNEN
- {status === 'step2' && ✓ erledigt }
-
+ {/* Ergebnis */}
+ {archiveUrl && (
+
+
ARTIKEL ÖFFNEN & ALS PDF SPEICHERN
- {status === 'step1' && (
- <>
-
- Öffne archive.ph. Falls ein CAPTCHA erscheint: lösen und warten bis der Artikel vollständig sichtbar ist. Dann die
URL aus der Adressleiste kopieren – sie sieht dann so aus:
-
archive.ph/AbCdE oder
archive.ph/20260608.../https://...
+ {/* Schritt 1 */}
+
+
1
+
- )}
-
- {/* Schritt 2: Snapshot-URL einfügen + bereinigt öffnen */}
- {status === 'step2' && (
-
-
- 2
- SNAPSHOT-URL EINFÜGEN
+
-
- Kopiere die URL aus der Adressleiste von archive.ph und füge sie hier ein:
-
-
-
setSnapshotUrl(e.target.value)}
- placeholder="https://archive.ph/AbCdE"
- style={{ ...S.inp, width: '100%', boxSizing: 'border-box', marginBottom: 12, fontSize: 12 }}
- />
-
- {renderUrl ? (
- <>
-
- 🧹 Bereinigt öffnen (ohne Banner & Archiv-Links)
-
-
-
DANN ALS PDF DRUCKEN
-
- 📱 Android: Menü → Teilen → Drucken → Als PDF · Kopf-/Fußzeilen aus{'\n'}
- 🍎 iOS: Teilen → Als PDF sichern{'\n'}
- 💻 Desktop: Strg+P → Als PDF · Kopf-/Fußzeilen aus
+ {/* Schritt 2 */}
+
+
2
+
+
+ Wenn der Artikel sichtbar ist, als PDF speichern:
+
+
+ {[
+ ['📱', 'Android Chrome', 'Menü (⋮) → Teilen → Drucken → Als PDF · Kopf-/Fußzeilen: aus'],
+ ['🍎', 'iOS Safari', 'Teilen-Button → Als PDF sichern'],
+ ['💻', 'Desktop', 'Strg+P / Cmd+P → Als PDF · Kopf-/Fußzeilen: aus'],
+ ].map(([icon, label, desc]) => (
+
+
{icon} {label}
+
{desc}
+
+ ))}
+
+
+
+ ℹ️ Der archive.ph-Balken oben ist Teil der archivierten Seite und kann nicht automatisch entfernt werden. Im Druckdialog "Kopf-/Fußzeilen" deaktivieren hält das PDF sauber.
- >
- ) : (
-
- Snapshot-URL oben einfügen um fortzufahren...
- )}
+
)}
- {status === 'idle' && (
+ {/* Idle-Info */}
+ {!archiveUrl && (
SO FUNKTIONIERT ES
{[
['1', 'URL des gesperrten Artikels einfügen'],
- ['2', 'archive.ph öffnen, CAPTCHA einmalig lösen'],
- ['3', 'URL aus Adressleiste kopieren und hier einfügen'],
- ['4', 'Bereinigt öffnen (kein Banner, normale Links) → als PDF drucken'],
+ ['2', 'Artikel auf archive.ph öffnen'],
+ ['3', 'CAPTCHA einmalig lösen (danach dauerhaft gespeichert)'],
+ ['4', 'Über Drucken-Funktion als PDF speichern'],
].map(([num, text]) => (
{num}