Browseren streamer 16 kHz PCM direkte til ElevenLabs' realtime-STT med et engangs-token fra serveren (/api/stt-token); teksten vokser under optagelsen og sendes med kladden, så serveren slipper for at skrive af. 'Spørgsmål og svar' bruger ét audio-element (iOS blokerer ellers det andet play()) og laver spørgsmålets lyd on demand for ældre klip. Admin kan rense favorit-klip med ElevenLabs Audio Isolation (<id>-ren.mp3), aldrig som standard. Claude-Session: https://claude.ai/code/session_01JmhJG6JnhXfzNiKv8f2vHu
60 lines
5.6 KiB
HTML
60 lines
5.6 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Admin — erindringer: {{ p.navn }}{% endblock %}
|
|
{% block body %}
|
|
<p><a href="/admin/erindringer" class="wt-muted">← Erindringer</a></p>
|
|
<h1>{{ p.navn }}</h1>
|
|
{% if saved %}<div class="wt-flash">{{ saved }}</div>{% endif %}
|
|
|
|
<div class="wt-card" style="padding:1rem 1.2rem;max-width:48rem;margin:0 0 1.2rem">
|
|
{% if p.bruger %}
|
|
<p style="margin:0 0 .6rem">Logger ind som <strong>{{ p.bruger }}</strong> og bruger <a href="/erindringer">/erindringer</a>.</p>
|
|
{% else %}
|
|
<p style="margin:0 0 .6rem"><strong>Gæstelink</strong>{% if p.lukket %} <span class="wt-tag muted">lukket</span>{% endif %}</p>
|
|
{% if not p.lukket %}
|
|
<div style="display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:.8rem">
|
|
<input class="wt-input" readonly id="g-link" value="{{ base }}/erindringer/g/{{ p.noegle }}/" 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('g-link');i.select();navigator.clipboard&&navigator.clipboard.writeText(i.value);this.textContent='Kopieret'">Kopiér</button>
|
|
</div>
|
|
{% endif %}
|
|
<form method="post" action="/admin/erindringer/person/{{ p.pid }}" style="display:flex;gap:.5rem;flex-wrap:wrap;margin:0 0 .8rem">
|
|
<button class="wt-btn" name="handling" value="ny-noegle" onclick="return confirm('Nyt link? Det gamle holder op med at virke.')">Nyt link</button>
|
|
{% if p.lukket %}<button class="wt-btn" name="handling" value="aabn">Åbn igen</button>{% else %}<button class="wt-btn wt-btn--danger" name="handling" value="luk">Luk linket</button>{% endif %}
|
|
</form>
|
|
{% endif %}
|
|
<form method="post" action="/admin/erindringer/person/{{ p.pid }}" style="margin:0">
|
|
<input type="hidden" name="handling" value="projekter">
|
|
<p style="margin:.2rem 0 .3rem">Emner:</p>
|
|
{% for pr in projekter %}
|
|
<label style="display:inline-block;margin:.2rem .8rem .2rem 0"><input type="checkbox" name="projekter" value="{{ pr.slug }}" {% if pr.slug in p.projekter %}checked{% endif %}{% if p.bruger and pr.slug == 'dagbog' %} disabled checked{% endif %}> {{ pr.titel }}</label>
|
|
{% endfor %}
|
|
<p style="margin:.6rem 0 0"><button class="wt-btn" type="submit">Gem emner</button>
|
|
<a class="wt-btn" href="/admin/erindringer/p/{{ p.pid }}/eksport.zip">Hent alt som zip</a></p>
|
|
</form>
|
|
</div>
|
|
|
|
<h2 style="font-size:1.15rem">{{ klip|length }} svar</h2>
|
|
<p class="wt-muted" style="margin:.2rem 0 .8rem;max-width:48rem">Markér de svar, der skal med i podcasten, som favorit. Kun favoritter kan renses med voice isolation (ElevenLabs Audio Isolation, cirka 1000 credits pr. minut); det laver en ekstra fil ved siden af originalen og bruges ikke til dagbog.</p>
|
|
{% for k in klip %}
|
|
<div class="wt-card" id="k-{{ k.id }}" style="padding:.9rem 1.2rem;margin:.6rem 0;max-width:48rem{% if k.favorit %};border-color:var(--color-accent){% endif %}">
|
|
<div class="wt-muted" style="font-size:.85rem">{{ k.tid[:16]|replace('T',' ') }} · {{ k.projekt_titel }} · {% if k.skrevet %}skrevet{% else %}{{ k.varighed // 60 }}:{{ '%02d' % (k.varighed % 60) }}{% endif %}{% if k.redigeret %} · tekst rettet{% endif %} · <span style="font-family:ui-monospace,monospace">{{ k.id }}</span></div>
|
|
<p style="margin:.4rem 0"><strong>{{ k.spoergsmaal }}</strong>{% if k.kilde %} <span class="wt-tag muted">{{ k.kilde }}</span>{% endif %}</p>
|
|
{% if k.spoergsmaal_talt and k.spoergsmaal_talt != k.spoergsmaal %}<p class="wt-muted" style="margin:0 0 .3rem;font-size:.85rem">Oplæst som: {{ k.spoergsmaal_talt }}</p>{% endif %}
|
|
{% if k.spoergsmaal_lyd %}<p style="margin:.2rem 0"><span class="wt-muted" style="font-size:.85rem">Spørgsmålet:</span> <audio controls preload="none" src="/admin/erindringer/p/{{ p.pid }}/klip/{{ k.id }}-sp.mp3" style="width:100%;max-width:100%"></audio></p>{% endif %}
|
|
{% if not k.skrevet %}<p style="margin:.2rem 0"><span class="wt-muted" style="font-size:.85rem">Svaret:</span> <audio controls preload="none" src="/admin/erindringer/p/{{ p.pid }}/klip/{{ k.id }}.mp3" style="width:100%;max-width:100%"></audio></p>
|
|
{% if k.renset %}<p style="margin:.2rem 0"><span class="wt-muted" style="font-size:.85rem">Svaret, renset (voice isolation):</span> <audio controls preload="none" src="/admin/erindringer/p/{{ p.pid }}/klip/{{ k.id }}-ren.mp3" style="width:100%;max-width:100%"></audio></p>{% endif %}{% endif %}
|
|
<form method="post" action="/admin/erindringer/p/{{ p.pid }}/klip/{{ k.id }}" style="margin:.5rem 0 0">
|
|
<textarea class="wt-input" name="tekst" rows="4" style="width:100%">{{ k.tekst }}</textarea>
|
|
<p style="margin:.5rem 0 0;display:flex;gap:.5rem;flex-wrap:wrap">
|
|
<button class="wt-btn" name="handling" value="ret">Gem tekst</button>
|
|
<button class="wt-btn{% if k.favorit %} wt-btn--primary{% endif %}" name="handling" value="favorit" onclick="this.form.favorit.value='{{ '0' if k.favorit else '1' }}'">{{ '★ Favorit' if k.favorit else '☆ Markér som favorit' }}</button>
|
|
<input type="hidden" name="favorit" value="1">
|
|
{% if not k.skrevet and k.favorit %}<button class="wt-btn" name="handling" value="isoler" title="ElevenLabs Audio Isolation fjerner baggrundsstøj; cirka 1000 credits pr. minut" onclick="return confirm('Rens lyden med voice isolation? Det koster cirka 1000 credits pr. minut.')">{{ 'Rens igen' if k.renset else 'Rens lyd (voice isolation)' }}</button>{% endif %}
|
|
<button class="wt-btn wt-btn--danger" name="handling" value="slet" onclick="return confirm('Slet svaret for altid?')">Slet</button>
|
|
</p>
|
|
</form>
|
|
</div>
|
|
{% else %}
|
|
<p class="wt-muted">Ingen svar endnu.</p>
|
|
{% endfor %}
|
|
{% endblock %}
|