fix: archive.is Suche via HTML-Parse, Button-Farben gefixt

This commit is contained in:
2026-06-12 01:42:42 +02:00
parent 38c27ac5bb
commit 902d5fd99e
2 changed files with 146 additions and 137 deletions

View File

@@ -1,16 +1,14 @@
import { useState, useRef } from 'react';
import { S, api } from '../lib.js';
// ── Farben ─────────────────────────────────────────────────────────────────
const C = {
accent: '#f59e0b',
success: '#4ade80',
error: '#f87171',
muted: 'rgba(255,255,255,0.4)',
dim: 'rgba(255,255,255,0.07)',
accent: '#f59e0b',
success: '#4ade80',
error: '#f87171',
muted: '#6b7280',
dim: '#1f2937',
};
// ── Hilfsfunktion: Domain aus URL extrahieren ──────────────────────────────
function getDomain(url) {
try { return new URL(url).hostname.replace(/^www\./, ''); }
catch { return url; }
@@ -18,14 +16,13 @@ function getDomain(url) {
export default function PaywallKiller() {
const [inputUrl, setInputUrl] = useState('');
const [status, setStatus] = useState('idle'); // idle | checking | found | notfound | pdf | error
const [status, setStatus] = useState('idle');
const [archiveUrl, setArchiveUrl] = useState('');
const [archiveType, setArchiveType] = useState('');
const [errMsg, setErrMsg] = useState('');
const [pdfLoading, setPdfLoading] = useState(false);
const inputRef = useRef(null);
// ── URL prüfen ────────────────────────────────────────────────────────────
async function handleCheck() {
const url = inputUrl.trim();
if (!url) return;
@@ -48,7 +45,6 @@ export default function PaywallKiller() {
}
}
// ── PDF herunterladen ─────────────────────────────────────────────────────
async function handlePdf() {
setPdfLoading(true);
try {
@@ -61,12 +57,10 @@ export default function PaywallKiller() {
},
body: JSON.stringify({ archiveUrl }),
});
if (!res.ok) {
const data = await res.json().catch(() => ({}));
throw new Error(data.error || 'PDF-Fehler');
}
const blob = await res.blob();
const objUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
@@ -84,7 +78,6 @@ export default function PaywallKiller() {
}
}
// ── Reset ─────────────────────────────────────────────────────────────────
function handleReset() {
setInputUrl('');
setStatus('idle');
@@ -95,7 +88,6 @@ export default function PaywallKiller() {
setTimeout(() => inputRef.current?.focus(), 50);
}
// ── Paste aus Clipboard ───────────────────────────────────────────────────
async function handlePaste() {
try {
const text = await navigator.clipboard.readText();
@@ -109,14 +101,15 @@ export default function PaywallKiller() {
const isFound = status === 'found';
const isNotFound = status === 'notfound';
const isError = status === 'error';
const hasResult = isFound || isNotFound || isError;
return (
<div style={{ padding: '20px 16px', maxWidth: 660, margin: '0 auto' }}>
<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: 22 }}>🔓</span>
<span style={{ fontSize: 20 }}>🔓</span>
<span style={{ color: C.accent, fontFamily: 'monospace', fontSize: 13, letterSpacing: 1 }}>
PAYWALL-KILLER
</span>
@@ -126,9 +119,11 @@ export default function PaywallKiller() {
</div>
</div>
{/* URL-Eingabe */}
{/* Eingabe */}
<div style={{ ...S.card, marginBottom: 16 }}>
<div style={{ ...S.head, marginBottom: 10 }}>ARTIKEL-URL</div>
{/* Input + Paste-Button */}
<div style={{ display: 'flex', gap: 8, marginBottom: 10 }}>
<input
ref={inputRef}
@@ -138,10 +133,25 @@ export default function PaywallKiller() {
placeholder="https://www.zeitung.de/artikel/..."
style={{ ...S.inp, flex: 1 }}
/>
<button onClick={handlePaste} style={{ ...S.btn(C.muted, true), padding: '5px 12px' }} title="Aus Zwischenablage einfügen">
<button
onClick={handlePaste}
title="Aus Zwischenablage einfügen"
style={{
background: 'rgba(255,255,255,0.06)',
border: '1px solid rgba(255,255,255,0.12)',
borderRadius: 6,
padding: '0 12px',
color: '#fff',
cursor: 'pointer',
fontSize: 14,
flexShrink: 0,
}}
>
📋
</button>
</div>
{/* Action-Buttons */}
<div style={{ display: 'flex', gap: 8 }}>
<button
onClick={handleCheck}
@@ -149,15 +159,28 @@ export default function PaywallKiller() {
style={{
...S.btn(C.accent),
flex: 1,
opacity: (isChecking || !inputUrl.trim()) ? 0.5 : 1,
opacity: (isChecking || !inputUrl.trim()) ? 0.45 : 1,
cursor: (isChecking || !inputUrl.trim()) ? 'default' : 'pointer',
}}
>
{isChecking ? '⏳ Suche Archiv...' : '🔍 Archiv suchen'}
</button>
{(isFound || isNotFound || isError) && (
<button onClick={handleReset} style={S.btn(C.muted, false)}>
Zurücksetzen
{hasResult && (
<button
onClick={handleReset}
style={{
background: 'rgba(255,255,255,0.06)',
border: '1px solid rgba(255,255,255,0.14)',
borderRadius: 6,
padding: '8px 14px',
color: 'rgba(255,255,255,0.6)',
cursor: 'pointer',
fontFamily: 'monospace',
fontSize: 12,
whiteSpace: 'nowrap',
}}
>
Reset
</button>
)}
</div>
@@ -165,26 +188,25 @@ export default function PaywallKiller() {
{/* Gefunden */}
{isFound && (
<div style={{ ...S.card, borderColor: `${C.success}44`, marginBottom: 16 }}>
<div style={{ ...S.card, borderColor: `${C.success}55`, marginBottom: 16 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
<span style={{ fontSize: 16 }}></span>
<span></span>
<span style={{ color: C.success, fontFamily: 'monospace', fontSize: 12 }}>
Archiv gefunden
<span style={{
marginLeft: 8,
background: `${C.accent}22`,
border: `1px solid ${C.accent}44`,
borderRadius: 4,
padding: '1px 6px',
fontSize: 10,
color: C.accent,
}}>
{archiveType === 'newest' ? 'neuestes' : 'ältestes'} Archiv
</span>
</span>
<span style={{
background: `${C.accent}20`,
border: `1px solid ${C.accent}55`,
borderRadius: 4,
padding: '1px 7px',
fontSize: 10,
color: C.accent,
fontFamily: 'monospace',
}}>
{archiveType === 'newest' ? 'neuester Snapshot' : 'ältester Snapshot'}
</span>
</div>
{/* Archive-URL anzeigen */}
<div style={{
background: 'rgba(255,255,255,0.03)',
border: '1px solid rgba(255,255,255,0.08)',
@@ -193,18 +215,18 @@ export default function PaywallKiller() {
marginBottom: 14,
wordBreak: 'break-all',
}}>
<a href={archiveUrl} target="_blank" rel="noreferrer" style={{ color: C.accent, fontSize: 11, fontFamily: 'monospace' }}>
<a href={archiveUrl} target="_blank" rel="noreferrer"
style={{ color: C.accent, fontSize: 11, fontFamily: 'monospace' }}>
{archiveUrl}
</a>
</div>
{/* Aktions-Buttons */}
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
<a
href={archiveUrl}
target="_blank"
rel="noreferrer"
style={{ ...S.btn(C.success), textDecoration: 'none', display: 'inline-block' }}
style={{ ...S.btn(C.success), textDecoration: 'none' }}
>
🌐 Im Browser öffnen
</a>
@@ -221,10 +243,9 @@ export default function PaywallKiller() {
</button>
</div>
{/* PDF-Hinweis */}
{pdfLoading && (
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', marginTop: 10 }}>
Seite wird geladen und als PDF gerendert. Das kann 1530 Sekunden dauern...
Seite wird gerendert, das dauert 1530 Sekunden...
</div>
)}
</div>
@@ -234,25 +255,23 @@ export default function PaywallKiller() {
{isNotFound && (
<div style={{ ...S.card, borderColor: `${C.error}44` }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
<span style={{ fontSize: 16 }}>📭</span>
<span>📭</span>
<span style={{ color: C.error, fontFamily: 'monospace', fontSize: 12 }}>
Kein Archiv gefunden
</span>
</div>
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', lineHeight: 1.6 }}>
Für <strong style={{ color: 'rgba(255,255,255,0.6)' }}>{getDomain(inputUrl)}</strong> existiert
kein Eintrag auf archive.is. Der Artikel wurde möglicherweise noch nie archiviert.
</div>
<div style={{ marginTop: 12 }}>
<a
href={`https://archive.is/${inputUrl.trim()}`}
target="_blank"
rel="noreferrer"
style={{ ...S.btn(C.muted), textDecoration: 'none', display: 'inline-block', fontSize: 11 }}
>
🔗 Manuell auf archive.is prüfen
</a>
<div style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', lineHeight: 1.6, marginBottom: 12 }}>
Für <strong style={{ color: 'rgba(255,255,255,0.55)' }}>{getDomain(inputUrl)}</strong> wurde
kein Snapshot auf archive.is gefunden.
</div>
<a
href={`https://archive.is/${inputUrl.trim()}`}
target="_blank"
rel="noreferrer"
style={{ ...S.btn(C.muted), textDecoration: 'none', display: 'inline-block', fontSize: 11 }}
>
🔗 Manuell auf archive.is prüfen
</a>
</div>
)}
@@ -260,7 +279,7 @@ export default function PaywallKiller() {
{isError && (
<div style={{ ...S.card, borderColor: `${C.error}44` }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<span style={{ fontSize: 16 }}></span>
<span></span>
<span style={{ color: C.error, fontFamily: 'monospace', fontSize: 12 }}>
{errMsg}
</span>
@@ -268,33 +287,27 @@ export default function PaywallKiller() {
</div>
)}
{/* Info-Sektion */}
{/* Idle-Info */}
{status === 'idle' && (
<div style={{ ...S.card, marginTop: 8 }}>
<div style={{ ...S.head, marginBottom: 10 }}>WIE ES FUNKTIONIERT</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
{[
['1', 'URL des gesperrten Artikels einfügen'],
['2', 'Archiv auf archive.is suchen (newest → oldest)'],
['3', 'Archivierte Version im Browser öffnen oder als PDF speichern'],
].map(([num, text]) => (
<div key={num} style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
<span style={{
background: `${C.accent}22`,
border: `1px solid ${C.accent}44`,
borderRadius: '50%',
width: 20, height: 20,
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 10, color: C.accent, fontFamily: 'monospace', flexShrink: 0,
}}>
{num}
</span>
<span style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace', lineHeight: 1.6 }}>
{text}
</span>
</div>
))}
</div>
<div style={{ ...S.card }}>
<div style={{ ...S.head, marginBottom: 10 }}>SO FUNKTIONIERT ES</div>
{[
['1', 'URL des gesperrten Artikels einfügen'],
['2', 'Archiv auf archive.is suchen'],
['3', 'Im Browser öffnen oder als PDF speichern'],
].map(([num, text]) => (
<div key={num} style={{ display: 'flex', gap: 10, alignItems: 'center', 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',
}}>{num}</span>
<span style={{ color: C.muted, fontSize: 11, fontFamily: 'monospace' }}>{text}</span>
</div>
))}
</div>
)}
</div>