diff --git a/Makefile b/Makefile index 4ad29fc..390b790 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: install dev stop sync build prod logs down push pull stream stream-stop mtx mtx-stop +.PHONY: install dev stop sync build prod logs down push pull stream stream-local stream-stop mtx mtx-stop install: cd backend && python3 -m venv .venv && .venv/bin/pip install -r requirements.txt @@ -48,13 +48,19 @@ down: docker compose down # ── Senden ─────────────────────────────────────────────────────────────────── -# Ohne Argument an den lokalen MediaMTX, sonst an den Server: -# make stream ZIEL=horror.marha.de -ZIEL ?= 127.0.0.1 +# Der Regelfall geht an den Server. Ein anderes Ziel bei Bedarf: +# make stream ZIEL=anderer.host +ZIEL ?= horror.marha.de -stream: mtx +# Räumt vorher auf: ein noch laufendes OBS belegt sonst die Sendestrecke, +# und der Tonweg wird sauber neu aufgebaut. +stream: stream-stop @scripts/stream.sh $(ZIEL) +# Zum Ausprobieren ohne Server, gegen das lokale MediaMTX +stream-local: mtx + @scripts/stream.sh 127.0.0.1 + stream-stop: -@pkill -f "obs --profile filmabend" 2>/dev/null -@scripts/ton.sh ab diff --git a/README.md b/README.md index aedde13..b1c9707 100644 --- a/README.md +++ b/README.md @@ -129,9 +129,9 @@ OBS ──RTMP──► MediaMTX ──LL-HLS──► FastAPI ──► Browser **Senden:** ```bash -make stream # an den lokalen MediaMTX -make stream ZIEL=horror.marha.de # an den Server -make stream-stop # OBS beenden +make stream # an den Server, beendet vorher ein laufendes OBS +make stream-local # an den lokalen MediaMTX, zum Ausprobieren +make stream-stop # OBS beenden und den Tonweg zurückstellen ``` Strg-C im Terminal beendet OBS nicht zuverlässig — deshalb `make stream-stop` diff --git a/frontend/src/App.vue b/frontend/src/App.vue index d0df7f1..766786c 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -224,6 +224,13 @@ const gesehenZahl = computed( // Wer den Abend leitet, darf drehen und die Runde leeren. Kein Login — // die Tastenkombination reicht, die Seite steht ohnehin nur Freunden offen. +// Ein Name für alles: Vorschläge und Wertungen. Ohne Namen läuft es +// anonym, das ist ausdrücklich erlaubt. +const NAME_SCHLUESSEL = 'filmabend_name' +const name = ref(localStorage.getItem(NAME_SCHLUESSEL) || '') + +watch(name, (wert) => localStorage.setItem(NAME_SCHLUESSEL, wert)) + const HOST_SCHLUESSEL = 'filmabend_host' const host = ref(localStorage.getItem(HOST_SCHLUESSEL) === '1') @@ -389,6 +396,13 @@ onUnmounted(() => window.removeEventListener('keydown', tastatur)) {{ tab.label }} {{ tab.count }} + Host @@ -405,6 +419,7 @@ onUnmounted(() => window.removeEventListener('keydown', tastatur)) @@ -466,6 +481,7 @@ onUnmounted(() => window.removeEventListener('keydown', tastatur)) @open="selected = $event" :mein-vorschlag="meinVorschlag" :host="host" + :name="name" :gesehen="gesehen.some((e) => e.movie_id === selected.id)" @suggested="nachVorschlag" @watched="ladeGesehen" @@ -512,9 +528,15 @@ onUnmounted(() => window.removeEventListener('keydown', tastatur)) border-bottom-color: var(--accent); } +.tabs .name { + width: 11rem; + margin: 0 0 0.35rem auto; + padding: 0.35rem 0.6rem; + font-size: 0.82rem; +} + .host { align-self: center; - margin-left: auto; padding: 0.15rem 0.5rem; color: var(--accent); font-size: 0.7rem; diff --git a/frontend/src/components/MovieDetail.vue b/frontend/src/components/MovieDetail.vue index e7270f0..1a250b2 100644 --- a/frontend/src/components/MovieDetail.vue +++ b/frontend/src/components/MovieDetail.vue @@ -7,33 +7,35 @@ const props = defineProps({ series: { type: Array, default: () => [] }, meinVorschlag: { type: Object, default: null }, host: { type: Boolean, default: false }, + name: { type: String, default: '' }, gesehen: { type: Boolean, default: false }, }) const emit = defineEmits(['close', 'open', 'suggested', 'watched']) -const vorschlagen = ref({ zustand: 'bereit', name: '' }) +const vorschlagen = ref({ zustand: 'bereit' }) const schonVorgeschlagen = computed( () => props.meinVorschlag?.movie_id === props.movie.id ) async function abschicken() { - vorschlagen.value = { ...vorschlagen.value, zustand: 'laeuft' } + vorschlagen.value = { zustand: 'laeuft' } try { const resp = await fetch('/api/suggestions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ movie_id: props.movie.id, - name: vorschlagen.value.name || null, + // Der Name steht oben rechts, einmal für alles + name: props.name || null, }), }) if (!resp.ok) throw new Error() - vorschlagen.value = { ...vorschlagen.value, zustand: 'fertig' } + vorschlagen.value = { zustand: 'fertig' } emit('suggested') } catch { - vorschlagen.value = { ...vorschlagen.value, zustand: 'fehler' } + vorschlagen.value = { zustand: 'fehler' } } } @@ -145,12 +147,6 @@ onUnmounted(() => { Du hast „{{ meinVorschlag.title }}“ vorgeschlagen. Ein neuer Vorschlag ersetzt ihn. - const fehler = ref('') const laden = ref(true) -// Der Name gilt für alle Bewertungen und überlebt das Neuladen -const NAME_SCHLUESSEL = 'filmabend_name' -const name = ref(localStorage.getItem(NAME_SCHLUESSEL) || '') - const STERNE = [1, 2, 3, 4, 5] // Beim Schweben füllen sich alle Sterne bis zum Zeiger, wie gewohnt @@ -45,14 +41,13 @@ async function laden_() { } async function bewerten(item, sterne) { - localStorage.setItem(NAME_SCHLUESSEL, name.value) // Sofort anzeigen, der Live-Kanal liefert gleich die richtigen Zahlen item.mine = sterne try { await fetch(`/api/watched/${item.id}/rating`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ stars: sterne, name: name.value || null }), + body: JSON.stringify({ stars: sterne, name: props.name || null }), }) } catch { fehler.value = 'Bewerten hat nicht geklappt.' @@ -114,16 +109,6 @@ onUnmounted(() => abbestellen?.()) - - - - {{ fehler }} Noch kein Film geschaut. @@ -223,19 +208,6 @@ onUnmounted(() => abbestellen?.())
{{ fehler }}
Noch kein Film geschaut. @@ -223,19 +208,6 @@ onUnmounted(() => abbestellen?.())