Hver række under "Hentede videoer" på /admin/yt har nu en Slet-knap (med bekræftelse; nævner det, hvis videoen er favorit). Sletningen bruger samme sikkerhedsnet som oprydningen: kun filer portalen selv har hentet, og undertekster/miniature slettes med. Er det jobbets sidste fil, markeres jobbet som slettet, og Emby-biblioteket opdateres. Video-id'et bliver i abonnementets "seen", så en overvåget kanal ikke henter den igen. Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
188 lines
10 KiB
HTML
188 lines
10 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Admin — YouTube offline{% endblock %}
|
|
{% block body %}
|
|
<h1>YouTube offline</h1>
|
|
<p class="lead">Indsæt en YouTube-URL, så hentes videoen til Emby-biblioteket
|
|
<strong>{{ settings.library_name }}</strong> — klar til at se uden net, fx i et fly.
|
|
Når du har <strong>markeret den som set</strong> i Emby, slettes den efter
|
|
{{ settings.grace_days }} dage. <strong>Favoritter slettes aldrig.</strong>
|
|
Halvt sete videoer rører oprydningen ikke.</p>
|
|
{% if saved %}<div class="flash">{{ saved }}</div>{% endif %}
|
|
|
|
{% if unconfigured %}
|
|
<div class="err">Ikke konfigureret endnu. Kræver <code>EMBY_URL/EMBY_API_KEY</code> i
|
|
<code>/etc/portal.env</code>, <code>yt-dlp</code> installeret, og at download-mappen
|
|
<code>{{ download_dir }}</code> findes i containeren.</div>
|
|
{% else %}
|
|
|
|
{% if inv_error %}<div class="err">⚠ Emby: {{ inv_error }} — oprydningen står stille indtil den kan læse se-data (sikkerhed).</div>{% endif %}
|
|
|
|
{# ---- hent ny ---- #}
|
|
<form method="post" action="/admin/yt/add" style="margin-top:1.4rem;display:flex;gap:.6rem;flex-wrap:wrap;align-items:center">
|
|
<input name="url" placeholder="https://www.youtube.com/watch?v=…" required
|
|
style="flex:1 1 26rem;min-width:16rem">
|
|
<label class="sw" style="flex:0 0 auto"><input type="checkbox" name="playlist"> <span>Hele playlisten</span></label>
|
|
<button class="btn" type="submit">Hent</button>
|
|
</form>
|
|
|
|
{# ---- topkort ---- #}
|
|
<div class="grid" style="grid-template-columns:repeat(auto-fill,minmax(14rem,1fr));margin-top:1.4rem">
|
|
<div class="stat"><div class="muted" style="font-size:.8rem;text-transform:uppercase;letter-spacing:.08em">I biblioteket</div>
|
|
<div class="n">{{ ours|length }}</div>
|
|
<div class="muted" style="font-size:.85rem;margin-top:.4rem">hentet af portalen · {{ human(ours_size) }}</div></div>
|
|
<div class="stat"><div class="muted" style="font-size:.8rem;text-transform:uppercase;letter-spacing:.08em">På vej ud</div>
|
|
<div class="n">{{ leaving|length }}</div>
|
|
<div class="muted" style="font-size:.85rem;margin-top:.4rem">set · i karens</div></div>
|
|
<div class="stat"><div class="muted" style="font-size:.8rem;text-transform:uppercase;letter-spacing:.08em">I kø</div>
|
|
<div class="n">{{ queued }}</div>
|
|
<div class="muted" style="font-size:.85rem;margin-top:.4rem">{% if running %}1 i gang{% else %}ingen i gang{% endif %}</div></div>
|
|
<div class="stat"><div class="muted" style="font-size:.8rem;text-transform:uppercase;letter-spacing:.08em">Sletning</div>
|
|
<div class="n" style="font-size:1.4rem;margin-top:.4rem">{% if settings.enabled %}<span style="color:#3ecf8e">AKTIV</span>{% else %}<span style="color:#f5a623">fra</span>{% endif %}</div>
|
|
<div class="muted" style="font-size:.85rem;margin-top:.4rem">{{ settings.grace_days }} dages karens</div></div>
|
|
</div>
|
|
|
|
{# ---- indstillinger ---- #}
|
|
<details style="margin-top:1.6rem">
|
|
<summary style="cursor:pointer;font-weight:600">⚙️ Indstillinger</summary>
|
|
<form method="post" action="/admin/yt/settings" style="margin-top:1rem">
|
|
<div class="setgrid">
|
|
<label class="sw"><input type="checkbox" name="enabled" {% if settings.enabled %}checked{% endif %}> <span>Slet sete videoer automatisk</span></label>
|
|
<label class="sw"><input type="checkbox" name="sponsorblock" {% if settings.sponsorblock %}checked{% endif %}> <span>SponsorBlock — klip sponsorater ud</span></label>
|
|
<label class="sw"><input type="checkbox" name="subtitles" {% if settings.subtitles %}checked{% endif %}> <span>Undertekster hvis de findes</span></label>
|
|
<label class="sw"><input type="checkbox" name="metadata" {% if settings.metadata %}checked{% endif %}> <span>Metadata, miniature & kapitler</span></label>
|
|
</div>
|
|
<div class="setgrid" style="margin-top:.8rem">
|
|
<label class="fld"><span>Karens efter set (dage)</span><input type="number" name="grace_days" min="0" max="365" value="{{ settings.grace_days }}"></label>
|
|
<label class="fld"><span>Undertekst-sprog</span><input name="sub_langs" value="{{ settings.sub_langs }}"></label>
|
|
<label class="fld"><span>Emby-bibliotek</span><input name="library_name" value="{{ settings.library_name }}"></label>
|
|
<label class="fld"><span>Kvalitet (yt-dlp -f)</span><input name="quality" value="{{ settings.quality }}"></label>
|
|
</div>
|
|
<p style="margin-top:1rem;display:flex;gap:.6rem;flex-wrap:wrap">
|
|
<button class="btn" type="submit">Gem indstillinger</button>
|
|
<button class="btn secondary" type="submit" formaction="/admin/yt/run" formmethod="post">Kør oprydning nu</button>
|
|
</p>
|
|
</form>
|
|
</details>
|
|
|
|
{# ---- abonnementer ---- #}
|
|
<h2 style="margin-top:2rem">Overvågede playlister & kanaler</h2>
|
|
<p class="muted" style="margin-top:.3rem">
|
|
En <strong>playliste</strong> henter nye videoer efterhånden som de lægges i den —
|
|
det der allerede ligger i playlisten, når du opretter overvågningen, hentes ikke.
|
|
En <strong>kanal</strong> holder de <strong>X nyeste</strong> videoer liggende: kommer der en ny,
|
|
ryger den ældste ud. Favoritter tæller <em>ikke</em> med i X og slettes aldrig, så en
|
|
favoritering frigør en plads. Manuelle hentninger og playlister tæller heller ikke med.
|
|
</p>
|
|
|
|
<form method="post" action="/admin/yt/sub/add" style="margin-top:1rem;display:flex;gap:.6rem;flex-wrap:wrap;align-items:center">
|
|
<input name="url" placeholder="Playliste- eller kanal-URL" required style="flex:1 1 22rem;min-width:14rem">
|
|
<select name="kind" style="flex:0 0 auto">
|
|
<option value="">Gæt selv</option>
|
|
<option value="playlist">Playliste</option>
|
|
<option value="channel">Kanal</option>
|
|
</select>
|
|
<label class="fld" style="flex:0 0 9rem"><span>cycle X (kanal)</span>
|
|
<input type="number" name="cycle" min="1" max="100" placeholder="fx 3"></label>
|
|
<button class="btn" type="submit">Overvåg</button>
|
|
<button class="btn secondary" type="submit" formaction="/admin/yt/subs/check">Tjek alle nu</button>
|
|
</form>
|
|
|
|
{% if subs %}
|
|
<table class="wt-table" style="margin-top:1rem">
|
|
<thead><tr><th>Navn</th><th style="width:7rem">Type</th><th style="width:12rem">Cyklus</th>
|
|
<th style="width:10rem">Sidst tjekket</th><th style="width:12rem"></th></tr></thead>
|
|
<tbody>
|
|
{% for sb in subs %}
|
|
<tr>
|
|
<td>{{ sb.title }}<br><span class="muted" style="font-size:.8rem">{{ sb.url }}</span></td>
|
|
<td>{% if sb.kind == 'channel' %}Kanal{% else %}Playliste{% endif %}</td>
|
|
<td class="muted">
|
|
{% if sb.kind == 'channel' and sb.cycle %}
|
|
{{ sb.in_cycle }} af {{ sb.cycle }} i cyklus
|
|
{% if sb.favorites %}<br>+ {{ sb.favorites }} favorit{% if sb.favorites != 1 %}ter{% endif %} (fredet){% endif %}
|
|
{% else %}
|
|
{{ sb.on_disk }} hentet
|
|
{% endif %}
|
|
</td>
|
|
<td class="muted">{% if sb.last_check %}{{ ((now - sb.last_check) / 3600) | round(0) | int }} t siden{% else %}aldrig{% endif %}</td>
|
|
<td>
|
|
<form method="post" action="/admin/yt/sub/{{ sb.id }}/save" style="display:flex;gap:.4rem;align-items:center">
|
|
{% if sb.kind == 'channel' %}<input type="number" name="cycle" min="1" max="100" value="{{ sb.cycle }}" style="width:4.2rem">{% endif %}
|
|
<label class="sw" style="margin:0"><input type="checkbox" name="enabled" {% if sb.enabled %}checked{% endif %}> <span>aktiv</span></label>
|
|
<button class="btn" type="submit">Gem</button>
|
|
<button class="btn secondary" type="submit" formaction="/admin/yt/sub/{{ sb.id }}/del"
|
|
onclick="return confirm('Fjern overvågningen af {{ sb.title }}? Allerede hentede videoer bliver liggende.')">✕</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}
|
|
<p class="muted">Ingen overvågninger endnu.</p>
|
|
{% endif %}
|
|
|
|
{# ---- vores videoer ---- #}
|
|
<h2 style="margin-top:2rem">Hentede videoer</h2>
|
|
{% if not ours %}<p class="muted">Ingen endnu — indsæt en URL ovenfor.</p>{% else %}
|
|
<table class="wt-table" style="margin-top:.8rem">
|
|
<thead><tr><th>Titel</th><th style="width:7rem">Størrelse</th><th style="width:9rem">Status</th><th style="width:11rem">Slettes</th><th style="width:5.5rem"></th></tr></thead>
|
|
<tbody>
|
|
{% for v in ours %}
|
|
<tr>
|
|
<td>{{ v.name }}</td>
|
|
<td class="muted">{{ human(v.size) }}</td>
|
|
<td>
|
|
{% if v.favorite %}<span style="color:#3ecf8e">★ favorit — fredet</span>
|
|
{% elif v.played %}<span style="color:#f5a623">set</span>
|
|
{% else %}<span class="muted">ikke set</span>{% endif %}
|
|
</td>
|
|
<td class="muted">
|
|
{% if v.favorite %}aldrig
|
|
{% elif v.delete_after %}om {{ ((v.delete_after - now) / 86400) | round(0, 'ceil') | int }} dage
|
|
{% else %}—{% endif %}
|
|
</td>
|
|
<td>
|
|
<form method="post" action="/admin/yt/slet" style="margin:0"
|
|
onsubmit="return confirm({{ ('Slet „' ~ v.name ~ '“ nu?' ~ (' Den er markeret som favorit.' if v.favorite else '')) | tojson | forceescape }})">
|
|
<input type="hidden" name="path" value="{{ v.path }}">
|
|
<button class="wt-btn" type="submit">Slet</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% endif %}
|
|
|
|
{# ---- kø & historik ---- #}
|
|
{% if jobs %}
|
|
<h2 style="margin-top:2rem">Kø & historik</h2>
|
|
<table class="wt-table" style="margin-top:.8rem">
|
|
<thead><tr><th>URL / titel</th><th style="width:8rem">Status</th><th>Besked</th></tr></thead>
|
|
<tbody>
|
|
{% for j in jobs %}
|
|
<tr>
|
|
<td>{{ j.title or j.url }}</td>
|
|
<td>{% if j.status == 'done' %}<span style="color:#3ecf8e">hentet</span>
|
|
{% elif j.status == 'error' %}<span style="color:#f87171">fejl</span>
|
|
{% elif j.status == 'running' %}i gang…
|
|
{% elif j.status == 'deleted' %}<span class="muted">slettet</span>
|
|
{% else %}i kø{% endif %}</td>
|
|
<td class="muted">{{ j.error }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% endif %}
|
|
|
|
{% if log %}
|
|
<h2 style="margin-top:2rem">Log</h2>
|
|
<ul class="muted" style="margin-top:.6rem;line-height:1.7">
|
|
{% for l in log %}<li>{{ l.text }}</li>{% endfor %}
|
|
</ul>
|
|
{% endif %}
|
|
|
|
{% endif %}
|
|
{% endblock %}
|