This commit is contained in:
team3
2026-09-30 01:34:44 +02:00
parent ae5b1d0b1d
commit 624df7e7ee
4 changed files with 50 additions and 10 deletions

View File

@@ -47,8 +47,10 @@ build:
cd frontend && npm run build
# ── Produktion (auf dem Server ausführen, im Projektverzeichnis) ──────────────
# --force-recreate: auch MediaMTX kommt neu hoch und liest die
# mediamtx.yml frisch — sonst bleibt eine Konfigänderung ohne Wirkung
prod:
docker compose up -d --build
docker compose up -d --build --force-recreate
@echo "horror läuft: https://horror.marha.de"
# Der Sync läuft im Container, ein backend/.venv gibt es dort nicht

View File

@@ -354,8 +354,12 @@ async def aufnahme_loeschen(
raise HTTPException(status_code=403, detail="Nur für den Host")
try:
anzahl = await clips.aufnahme_loeschen(start, dauer)
except httpx.HTTPStatusError as e:
raise HTTPException(
status_code=502, detail=f"MediaMTX lehnt ab ({e.response.status_code})."
)
except httpx.HTTPError:
raise HTTPException(status_code=502, detail="Löschen hat nicht geklappt.")
raise HTTPException(status_code=502, detail="Die MediaMTX-API ist nicht erreichbar.")
return {"segmente": anzahl}

View File

@@ -79,7 +79,6 @@ const loeschFrage = ref(null)
async function aufnahmenLaden() {
if (!props.host) return
aufnahmenFehler.value = ''
loeschFrage.value = null
try {
const resp = await fetch('/api/aufnahmen')
@@ -96,9 +95,14 @@ async function aufnahmeLoeschen(s) {
loeschFrage.value = s.start
return
}
aufnahmenFehler.value = ''
const params = new URLSearchParams({ start: s.start, dauer: s.dauer })
const resp = await fetch(`/api/aufnahmen?${params}`, { method: 'DELETE' })
if (!resp.ok) aufnahmenFehler.value = 'Löschen hat nicht geklappt.'
if (!resp.ok) {
// Die Meldung vom Server sagt, woran es lag — sie bleibt stehen
const daten = await resp.json().catch(() => ({}))
aufnahmenFehler.value = daten.detail || 'Löschen hat nicht geklappt.'
}
await aufnahmenLaden()
}

View File

@@ -25,9 +25,7 @@ const laden = ref(true)
// Ausgeblendete Filme bleiben für den Host sichtbar, damit er sie
// zurückholen kann. Die Liste kommt jüngster zuerst — so liegt er links.
const seiten = computed(() =>
props.host ? items.value : items.value.filter((e) => !e.hidden)
)
const seiten = computed(() => (props.host ? items.value : items.value.filter((e) => !e.hidden)))
const buch = ref(null)
const aktuell = ref(0)
@@ -236,7 +234,7 @@ async function laden_() {
watch(
() => props.ich?.id,
() => laden_()
() => laden_(),
)
onMounted(() => {
@@ -284,7 +282,20 @@ onUnmounted(() => {
/>
</div>
<p class="stand">{{ aktuell + 1 }} / {{ seiten.length }}</p>
<p class="stand">
<button type="button" :disabled="aktuell <= 0" aria-label="Zurück" @click="blaettern(-1)">
‹
</button>
<span>{{ aktuell + 1 }} / {{ seiten.length }}</span>
<button
type="button"
:disabled="aktuell >= seiten.length - 1"
aria-label="Weiter"
@click="blaettern(1)"
>
›
</button>
</p>
</template>
</div>
</template>
@@ -345,7 +356,10 @@ onUnmounted(() => {
position: absolute;
bottom: -1.7rem;
left: 50%;
padding: 0.15rem 0.6rem;
display: flex;
gap: 0.4rem;
align-items: center;
padding: 0.15rem 0.4rem;
color: var(--muted);
font-size: 0.75rem;
transform: translateX(-50%);
@@ -353,6 +367,22 @@ onUnmounted(() => {
border-radius: 10px;
}
.stand button {
padding: 0 0.3rem;
color: var(--muted);
font-size: 1.1rem;
line-height: 1;
}
.stand button:hover:not(:disabled) {
color: var(--text);
}
.stand button:disabled {
opacity: 0.3;
cursor: default;
}
.note {
padding: 2.5rem 0;
color: var(--muted);