fix(images): add proxy fallback to direct source url rendering

This commit is contained in:
Oliver 2026-02-18 10:20:47 +01:00
parent 910ca72c81
commit fb3465fb10
4 changed files with 26 additions and 8 deletions

View file

@ -3,6 +3,7 @@ from __future__ import annotations
import json import json
from pathlib import Path from pathlib import Path
import re import re
from urllib.parse import urlparse
from urllib.parse import urlencode from urllib.parse import urlencode
from urllib.request import Request as UrlRequest, urlopen from urllib.request import Request as UrlRequest, urlopen
@ -124,6 +125,14 @@ def _is_probably_irrelevant_image(url: str) -> bool:
return any(re.search(pattern, lowered) for pattern in patterns) return any(re.search(pattern, lowered) for pattern in patterns)
def _is_http_image_url(url: str) -> bool:
try:
parsed = urlparse(url)
except Exception:
return False
return parsed.scheme in {"http", "https"} and bool(parsed.netloc)
def _build_image_entries(article: dict, extraction: dict, meta: dict) -> list[dict[str, object]]: def _build_image_entries(article: dict, extraction: dict, meta: dict) -> list[dict[str, object]]:
all_images = _read_article_images(article, extraction) all_images = _read_article_images(article, extraction)
image_review = meta.get("image_review") if isinstance(meta.get("image_review"), dict) else {} image_review = meta.get("image_review") if isinstance(meta.get("image_review"), dict) else {}
@ -371,15 +380,19 @@ def admin_article_image_decision(
@router.get("/admin/images/proxy") @router.get("/admin/images/proxy")
def admin_image_proxy(request: Request, url: str): def admin_image_proxy(request: Request, url: str):
user = _admin_user(request) if not _is_http_image_url(url):
if not user:
return Response(status_code=401)
if not (url.startswith("http://") or url.startswith("https://")):
return Response(status_code=400) return Response(status_code=400)
try: try:
req = UrlRequest(url=url, headers={"User-Agent": IMAGE_PROXY_USER_AGENT, "Referer": url}) referer = request.headers.get("referer", "")
req = UrlRequest(
url=url,
headers={
"User-Agent": IMAGE_PROXY_USER_AGENT,
"Accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8",
"Referer": referer or url,
},
)
with urlopen(req, timeout=10) as resp: with urlopen(req, timeout=10) as resp:
body = resp.read() body = resp.read()
content_type = resp.headers.get("Content-Type", "application/octet-stream") content_type = resp.headers.get("Content-Type", "application/octet-stream")

View file

@ -210,6 +210,11 @@ button.secondary {
background: #f8fafc; background: #f8fafc;
} }
.img-failed {
opacity: 0.3;
filter: grayscale(1);
}
.image-meta { .image-meta {
margin-top: 6px; margin-top: 6px;
display: flex; display: flex;

View file

@ -79,7 +79,7 @@
{% for image in article.image_entries %} {% for image in article.image_entries %}
<article class="image-card {{ 'image-selected' if image.is_selected else '' }} {{ 'image-excluded' if image.is_excluded else '' }}"> <article class="image-card {{ 'image-selected' if image.is_selected else '' }} {{ 'image-excluded' if image.is_excluded else '' }}">
<a href="{{ image.url }}" target="_blank" rel="noopener"> <a href="{{ image.url }}" target="_blank" rel="noopener">
<img src="{{ image.proxy_url }}" alt="Artikelbild" loading="lazy" /> <img src="{{ image.proxy_url }}" data-fallback-src="{{ image.url }}" alt="Artikelbild" loading="lazy" onerror="if(!this.dataset.fallbackUsed){this.dataset.fallbackUsed='1';this.src=this.dataset.fallbackSrc;}else{this.classList.add('img-failed');}" />
</a> </a>
<div class="image-meta"> <div class="image-meta">
{% if image.is_selected %}<span class="badge ok">Ausgewählt</span>{% endif %} {% if image.is_selected %}<span class="badge ok">Ausgewählt</span>{% endif %}

View file

@ -157,7 +157,7 @@
<div class="subtle">Legal: {{ "OK" if a.legal_checked else "offen" }}</div> <div class="subtle">Legal: {{ "OK" if a.legal_checked else "offen" }}</div>
{% if a.selected_image_url %} {% if a.selected_image_url %}
<div class="subtle">Hauptbild gesetzt</div> <div class="subtle">Hauptbild gesetzt</div>
<a href="{{ a.selected_image_url }}" target="_blank" rel="noopener"><img src="{{ a.selected_image_proxy_url }}" alt="Hauptbild" class="thumb" loading="lazy" /></a> <a href="{{ a.selected_image_url }}" target="_blank" rel="noopener"><img src="{{ a.selected_image_proxy_url }}" data-fallback-src="{{ a.selected_image_url }}" alt="Hauptbild" class="thumb" loading="lazy" onerror="if(!this.dataset.fallbackUsed){this.dataset.fallbackUsed='1';this.src=this.dataset.fallbackSrc;}else{this.classList.add('img-failed');}" /></a>
{% endif %} {% endif %}
{% if a.summary %} {% if a.summary %}
<div><strong>Summary:</strong> {{ a.summary }}</div> <div><strong>Summary:</strong> {{ a.summary }}</div>