Compare commits

...
2 Commits
Author SHA1 Message Date
agw 902ab6244e fix(podcast): Messenger på iPhone og vandret scroll
CD / deploy (push) Successful in 16s
CI / check (push) Successful in 23s
Messenger på iPhone blokerer også x-safari-https://, så "Åbn i Safari" gjorde
ingenting. I stedet vises to veje, der virker: menuen ⋯ → "Åbn i ekstern
browser", eller "Kopiér adressen" og tilføj feedet i Apple Podcasts
(Bibliotek → ⋯ → Følg en serie via URL). Kopieringen falder tilbage til
execCommand/prompt, hvis clipboard-API'et er spærret. Android beholder
intent://-knappen.

Siden kunne skubbes til siden på iPhone. Afspiller og felter må nu aldrig
blive bredere end kortet, felter har 16 px skrift (ellers zoomer iOS ind),
lange ord brydes, og html/body skjuler vandret overløb.

Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
2026-10-06 08:03:19 +02:00
agw 6d32fd2b71 fix(podcast): lille omslag i link-forhåndsvisningen
CD / deploy (push) Successful in 15s
CI / check (push) Successful in 23s
Med det store omslag (3000 px) viste Messenger et kvadratisk billede i hele
beskedens bredde. Forhåndsvisningen bruger nu cover-lille.jpg (300 px), når
den findes, så den vises som en miniature ved teksten.

Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
2026-10-06 07:54:54 +02:00
4 changed files with 60 additions and 13 deletions
+20 -4
View File
@@ -457,9 +457,14 @@ async def podcast_faelles_side(request: Request, key: str):
base = str(request.base_url).rstrip("/")
udsendelser = _podcast_kontekst(request, rod, med_kladder=False)
s = udsendelser[0] if udsendelser else {"titel": "Podcast", "slug": "", "har_cover": False}
# Et lille omslag (300 px), så Messenger viser en miniature ved teksten i
# stedet for et billede i hele beskedens bredde.
lille = bool(s.get("slug")) and podcast.cover(s["slug"], lille=True) is not None
og = {"titel": s["titel"], "url": f"{base}{rod}/",
"beskrivelse": "Familiens podcast. Lyt i browseren, eller abonnér i din podcast-app.",
"billede": f"{base}{rod}/{s['slug']}/cover.jpg" if s.get("har_cover") else ""}
"billede": (f"{base}{rod}/{s['slug']}/cover-lille.jpg" if lille else
f"{base}{rod}/{s['slug']}/cover.jpg" if s.get("har_cover") else ""),
"str": 300 if lille else 3000}
headers = {"X-Robots-Tag": "noindex, nofollow, noarchive", "Referrer-Policy": "no-referrer",
"Cache-Control": "private, no-store"}
if forhaandsvisning:
@@ -468,13 +473,16 @@ async def podcast_faelles_side(request: Request, key: str):
return templates.TemplateResponse(request, "podcast_og.html", {"request": request, "og": og}, headers=headers)
indbygget = None
if _INDBYGGET.search(ua):
# Messenger på iPhone blokerer også x-safari-https://, så dér er der ingen
# knap ud af appen: vejledning til menuen + kopiér feedet til Apple Podcasts.
# Android-udgaven af Facebooks browser følger intent://-links.
sti = f"{request.url.hostname}{rod}/"
if re.search(r"iPhone|iPad|iOS", ua):
indbygget = {"knap": "Åbn i Safari", "href": f"x-safari-https://{sti}"}
indbygget = {"platform": "ios"}
elif "Android" in ua:
indbygget = {"knap": "Åbn i Chrome", "href": f"intent://{sti}#Intent;scheme=https;end"}
indbygget = {"platform": "android", "href": f"intent://{sti}#Intent;scheme=https;end"}
else:
indbygget = {"knap": "", "href": ""}
indbygget = {"platform": "andet"}
return templates.TemplateResponse(request, "podcast.html", {
"request": request, "user": None, "is_admin": False, "rod": rod, "delt": True, "og": og,
"indbygget": indbygget, "udsendelser": udsendelser, "saved": ""}, headers=headers)
@@ -491,6 +499,14 @@ async def podcast_faelles_feed(request: Request, key: str, name: str):
headers={"Cache-Control": "private, max-age=300", "X-Robots-Tag": "noindex, nofollow"})
@app.get("/podcast/f/{key}/{name}/cover-lille.jpg")
async def podcast_faelles_cover_lille(request: Request, key: str, name: str):
p = podcast.cover(name, lille=True)
if podcast.blokeret(_klient_ip(request)) or not podcast.faelles_slaa_op(key) or not p:
_ikke_fundet(request)
return FileResponse(p, media_type="image/jpeg", headers={"Cache-Control": "private, max-age=86400"})
@app.get("/podcast/f/{key}/{name}/cover.jpg")
async def podcast_faelles_cover(request: Request, key: str, name: str):
p = podcast.cover(name)
+3 -2
View File
@@ -115,8 +115,9 @@ def saet_kladde(name: str, slug: str, kladde: bool) -> bool:
return True
def cover(name: str) -> Path | None:
p = ROOT / name / "cover.jpg"
def cover(name: str, lille: bool = False) -> Path | None:
"""Omslaget; lille = 300 px-udgaven til link-forhåndsvisninger (cover-lille.jpg)."""
p = ROOT / name / ("cover-lille.jpg" if lille else "cover.jpg")
return p if SHOW.match(name or "") and p.exists() else None
+35 -5
View File
@@ -8,6 +8,17 @@
<meta property="og:url" content="{{ og.url }}">
{% if og.billede %}<meta property="og:image" content="{{ og.billede }}">{% endif %}{% endif %}{% endif %}{% endblock %}
{% block body %}
<style>
/* Ingen vandret scroll på iPhone: afspiller og felter må aldrig blive bredere end
kortet, og felter skal have mindst 16 px skrift, ellers zoomer iOS ind. */
html, body { overflow-x: hidden; }
.pod-side { max-width: 100%; overflow-wrap: anywhere; }
.pod-side img, .pod-side audio, .pod-side input, .pod-side .wt-card { max-width: 100%; box-sizing: border-box; }
.pod-side audio { display: block; width: 100%; min-width: 0; }
.pod-side input { font-size: 16px !important; min-width: 0; }
.pod-side .wt-btn { white-space: normal; }
</style>
<div class="pod-side">
{% if saved %}<div class="wt-flash">{{ saved }}</div>{% endif %}
{% if not udsendelser %}
<h1>Podcast</h1>
@@ -30,11 +41,15 @@
<h2 style="font-size:1.05rem;margin:0 0 .5rem">Abonnér i din podcast-app</h2>
{% if indbygget %}
<div class="wt-flash" style="margin:.4rem 0 .9rem">
Du har åbnet siden inde i Messenger, og den kan ikke åbne podcast-apps.
{% if indbygget.href %}Tryk her for at åbne siden i din almindelige browser, og abonnér derfra:
<div style="margin-top:.6rem"><a class="wt-btn wt-btn--primary" href="{{ indbygget.href }}">{{ indbygget.knap }}</a></div>
{% else %}Åbn siden i din almindelige browser (menuen ⋯ øverst), og abonnér derfra.{% endif %}
<div class="wt-muted" style="font-size:.85rem;margin-top:.5rem">Virker knappen ikke: tryk på ⋯ øverst og vælg "Åbn i ekstern browser". Du kan også bare lytte herunder.</div>
<p style="margin:0 0 .5rem">Du har åbnet siden inde i Messenger, og den kan ikke åbne podcast-apps. Du kan stadig lytte herunder. Vil du abonnere:</p>
{% if indbygget.platform == "android" %}
<p style="margin:.4rem 0"><a class="wt-btn wt-btn--primary" href="{{ indbygget.href }}">Åbn i Chrome</a></p>
<p style="margin:.4rem 0">Virker det ikke: tryk på ⋮ øverst til højre og vælg "Åbn i ekstern browser".</p>
{% else %}
<p style="margin:.4rem 0"><strong>1.</strong> Tryk på ⋯ øverst til højre og vælg "Åbn i ekstern browser" (eller "Åbn i Safari"). Så virker knapperne herunder.</p>
<p style="margin:.6rem 0 .3rem"><strong>2.</strong> Eller kopiér adressen og tilføj den selv i Apple Podcasts: <em>Bibliotek → ⋯ øverst → Følg en serie via URL</em>.</p>
<button class="wt-btn wt-btn--primary" type="button" data-kopier-tekst="{{ udsendelser[0].feed if udsendelser else '' }}">Kopiér adressen</button>
{% endif %}
</div>
{% endif %}
<p class="wt-muted" style="margin:.2rem 0 .8rem">{% if delt %}Tryk på din app, så kommer nye episoder af sig selv. Linket er familiens fælles, så del det kun inden for familien.{% else %}Tryk på din app. Nye episoder kommer så af sig selv. Adressen er din egen, så del den ikke; familien får hver sin.{% endif %}</p>
@@ -82,6 +97,20 @@
</form>
{% endif %}
<script>
function kopierTekst(t, knap) {
var ok = function () { knap.textContent = 'Kopieret'; };
var fallback = function () {
var ta = document.createElement('textarea'); ta.value = t; ta.setAttribute('readonly', '');
ta.style.position = 'fixed'; ta.style.top = '0'; ta.style.opacity = '0'; ta.style.fontSize = '16px';
document.body.appendChild(ta); ta.focus(); ta.setSelectionRange(0, t.length);
try { document.execCommand('copy'); ok(); } catch (e) { prompt('Kopiér adressen:', t); }
document.body.removeChild(ta);
};
if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(t).then(ok, fallback); else fallback();
}
document.querySelectorAll('[data-kopier-tekst]').forEach(function (b) {
b.addEventListener('click', function () { kopierTekst(b.dataset.kopierTekst, b); });
});
document.querySelectorAll('[data-kopier]').forEach(function (b) {
b.addEventListener('click', function () {
var i = document.getElementById(b.dataset.kopier);
@@ -92,4 +121,5 @@ document.querySelectorAll('[data-kopier]').forEach(function (b) {
});
});
</script>
</div>
{% endblock %}
+2 -2
View File
@@ -11,8 +11,8 @@
<meta property="og:url" content="{{ og.url }}">
<meta property="og:locale" content="da_DK">
{% if og.billede %}<meta property="og:image" content="{{ og.billede }}">
<meta property="og:image:width" content="3000">
<meta property="og:image:height" content="3000">
<meta property="og:image:width" content="{{ og.str }}">
<meta property="og:image:height" content="{{ og.str }}">
<meta name="twitter:card" content="summary">{% endif %}
</head>
<body><h1>{{ og.titel }}</h1><p>{{ og.beskrivelse }}</p></body>