Et valgfrit fælles link, /podcast/f/<nøgle>/, til fx familiens Messenger- gruppe: samme afspillerside som den personlige, med abonnér-knapper og feed under samme sti, men uden login og uden admin-ting. Kladder vises aldrig der. I /admin/podcast laves linket, nøglen kan skiftes (det gamle dør med det samme), det kan slås fra, og hentningerne tælles. Siden er noindex (header og meta) og sender ingen referrer. Forkerte nøgler giver 404 ligesom ukendte stier, og en klient der rammer 20 forkerte nøgler på ti minutter, får 404 på alt under /podcast/<nøgle> i et stykke tid. Det gælder også de personlige nøgler. Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
99 lines
5.4 KiB
HTML
99 lines
5.4 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}Admin — podcast{% endblock %}
|
||
{% block body %}
|
||
<p><a href="/admin" class="wt-muted">← Admin</a></p>
|
||
<h1>Podcast</h1>
|
||
<p class="wt-lead">Kladder ses kun af administratorer, også i deres podcast-app. Udgiv en episode, når den er klar til familien. Lytternes adresser er personlige; trækker du en tilbage, holder den op med at virke, og de andre mærker intet. Personen får en ny adresse næste gang de åbner siden, så længe de er i gruppen Familie; skal nogen helt ud, så fjern dem fra gruppen i Authentik. Siden for familien er <a href="/podcast">/podcast</a>.</p>
|
||
{% if saved %}<div class="wt-flash">{{ saved }}</div>{% endif %}
|
||
|
||
{% for u in udsendelser %}
|
||
<h2 style="font-size:1.15rem;margin-top:1.6rem">{{ u.titel }}</h2>
|
||
<table class="wt-table">
|
||
<thead><tr><th>Nr</th><th>Titel</th><th>Varighed</th><th>Status</th><th></th></tr></thead>
|
||
<tbody>
|
||
{% for e in u.episoder %}
|
||
<tr>
|
||
<td>{{ e.nr }}</td>
|
||
<td>{{ e.titel }}<div class="wt-muted" style="font-size:.8rem">{{ e.slug }}.mp3 · {{ (e.bytes / 1048576) | round(1) }} MB</div></td>
|
||
<td>{{ (e.varighed // 60) }}:{{ '%02d' % (e.varighed % 60) }}</td>
|
||
<td>{% if e.kladde %}Kladde{% else %}Udgivet {{ (e.udgivet or '')[:10] }}{% endif %}</td>
|
||
<td>
|
||
<form method="post" action="/admin/podcast/{{ u.slug }}/{{ e.slug }}/udgiv" style="margin:0">
|
||
{% if e.kladde %}
|
||
<button class="wt-btn wt-btn--primary" type="submit">Udgiv</button>
|
||
{% else %}
|
||
<input type="hidden" name="kladde" value="1"><button class="wt-btn" type="submit">Gør til kladde</button>
|
||
{% endif %}
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="5" class="wt-muted">Ingen episoder. Udgiv med publish.py i skillen slaegtens-stemmer.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
{% endfor %}
|
||
|
||
<h2 style="font-size:1.15rem;margin-top:2rem">Fælles familielink</h2>
|
||
<p class="wt-muted" style="margin:.3rem 0 .8rem">Ét link uden login, fx til familiens Messenger-gruppe: afspiller og feed i ét. Kladder vises aldrig her. Skifter du nøglen, holder det gamle link op med at virke med det samme, også for dem der abonnerer via det.</p>
|
||
<div class="wt-card" style="padding:1rem 1.2rem;max-width:48rem">
|
||
{% if faelles.key %}
|
||
<p style="margin:0 0 .6rem"><strong>{{ 'Slået til' if faelles.aktiv else 'Slået fra' }}</strong>
|
||
· {{ faelles.hentninger or 0 }} hentninger{% if faelles.sidst %} · sidst <span data-ts="{{ faelles.sidst }}"></span>{% endif %}</p>
|
||
{% if faelles.aktiv %}
|
||
<div style="display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:.8rem">
|
||
<input class="wt-input" readonly id="faelles-link" value="{{ base }}/podcast/f/{{ faelles.key }}/"
|
||
style="flex:1 1 22rem;font-family:ui-monospace,monospace;font-size:.85rem" onclick="this.select()">
|
||
<button class="wt-btn" type="button" onclick="var i=document.getElementById('faelles-link');i.select();navigator.clipboard&&navigator.clipboard.writeText(i.value);this.textContent='Kopieret'">Kopiér</button>
|
||
</div>
|
||
{% endif %}
|
||
<form method="post" action="/admin/podcast/faelles" style="display:flex;gap:.5rem;flex-wrap:wrap;margin:0">
|
||
<button class="wt-btn" type="submit" name="handling" value="skift"
|
||
onclick="return confirm('Det gamle fælles link holder op med at virke. Fortsæt?')">Skift nøgle</button>
|
||
{% if faelles.aktiv %}
|
||
<button class="wt-btn" type="submit" name="handling" value="fra">Slå fra</button>
|
||
{% else %}
|
||
<button class="wt-btn wt-btn--primary" type="submit" name="handling" value="til">Slå til</button>
|
||
{% endif %}
|
||
</form>
|
||
{% else %}
|
||
<form method="post" action="/admin/podcast/faelles" style="margin:0">
|
||
<button class="wt-btn wt-btn--primary" type="submit" name="handling" value="skift">Lav fælles link</button>
|
||
</form>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<h2 style="font-size:1.15rem;margin-top:2rem">Lytternes adresser</h2>
|
||
<table class="wt-table">
|
||
<thead><tr><th>Bruger</th><th>Oprettet</th><th>Sidst hentet</th><th>Hentninger</th><th>App</th><th></th></tr></thead>
|
||
<tbody>
|
||
{% for k in noegler %}
|
||
<tr{% if k.tilbagekaldt %} style="opacity:.55"{% endif %}>
|
||
<td>{{ k.user }}{% if k.admin %} <span class="wt-muted">(admin)</span>{% endif %}</td>
|
||
<td data-ts="{{ k.oprettet }}"></td>
|
||
<td data-ts="{{ k.sidst or '' }}"></td>
|
||
<td title="{% for h, n in (k.hentet or {}).items() %}{{ h }}: {{ n }} {% endfor %}">{{ k.hentninger or 0 }}</td>
|
||
<td class="wt-muted" style="font-size:.8rem;max-width:16rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ k.sidst_ua or '' }}</td>
|
||
<td>
|
||
<form method="post" action="/admin/podcast/noegle/{{ k.key }}" style="margin:0">
|
||
{% if k.tilbagekaldt %}
|
||
<input type="hidden" name="aktiv" value="1"><button class="wt-btn" type="submit">Genaktivér</button>
|
||
{% else %}
|
||
<button class="wt-btn" type="submit" onclick="return confirm('Træk {{ k.user }}s adresse tilbage?')">Træk tilbage</button>
|
||
{% endif %}
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="6" class="wt-muted">Ingen endnu. En lytter får sin adresse første gang de åbner /podcast.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
<script>
|
||
document.querySelectorAll('[data-ts]').forEach(function (td) {
|
||
var t = parseInt(td.dataset.ts, 10);
|
||
td.textContent = t ? new Date(t * 1000).toLocaleString('da-DK', { dateStyle: 'short', timeStyle: 'short' }) : '–';
|
||
});
|
||
</script>
|
||
{% endblock %}
|