update
This commit is contained in:
@@ -44,7 +44,9 @@ PLAYER_LATENZ = 4
|
||||
# Das Fenster um die Marke: Standard und Grenzen
|
||||
VORHER = 30
|
||||
DAUER = 35
|
||||
MAX_VORHER = 600
|
||||
# Die Marke ist nur ein Anker — das Fenster darf überall auf dem
|
||||
# Zeitstrahl liegen, auch weit nach der Marke (vorher negativ)
|
||||
MAX_VORHER = 4 * 3600
|
||||
MIN_DAUER = 3
|
||||
MAX_DAUER = 180
|
||||
MAX_TITEL = 80
|
||||
@@ -138,7 +140,7 @@ def aendern(clip_id, session_id, host, titel=None, vorher=None, dauer=None):
|
||||
if titel is not None:
|
||||
felder["titel"] = titel.strip()[:MAX_TITEL] or None
|
||||
if vorher is not None:
|
||||
felder["vorher"] = max(0, min(MAX_VORHER, float(vorher)))
|
||||
felder["vorher"] = max(-MAX_VORHER, min(MAX_VORHER, float(vorher)))
|
||||
if dauer is not None:
|
||||
felder["dauer"] = max(MIN_DAUER, min(MAX_DAUER, float(dauer)))
|
||||
fenster_neu = any(
|
||||
@@ -173,7 +175,7 @@ def _datei_weg(name):
|
||||
def fenster(row, vorher=None, dauer=None):
|
||||
"""Start (RFC3339) und Dauer des Ausschnitts um die Marke."""
|
||||
marke = datetime.fromisoformat(row["marke_at"])
|
||||
v = row["vorher"] if vorher is None else max(0, min(MAX_VORHER, float(vorher)))
|
||||
v = row["vorher"] if vorher is None else max(-MAX_VORHER, min(MAX_VORHER, float(vorher)))
|
||||
d = row["dauer"] if dauer is None else max(MIN_DAUER, min(MAX_DAUER, float(dauer)))
|
||||
return _iso(marke - timedelta(seconds=v)), d
|
||||
|
||||
|
||||
@@ -128,8 +128,12 @@ def test_fremde_sehen_nichts_der_host_alles(client, client2, zuschauer, hostmodu
|
||||
|
||||
def test_fenster_wird_begrenzt_und_macht_schnitt_ungueltig(client, monkeypatch):
|
||||
m = marke(client)
|
||||
geaendert = client.patch(f"/api/clips/{m['id']}", json={"vorher": 5000, "dauer": 1, "titel": " Hallo "}).json()
|
||||
geaendert = client.patch(f"/api/clips/{m['id']}", json={"vorher": 99999, "dauer": 1, "titel": " Hallo "}).json()
|
||||
assert (geaendert["vorher"], geaendert["dauer"], geaendert["titel"]) == (clips.MAX_VORHER, clips.MIN_DAUER, "Hallo")
|
||||
# Das Fenster darf auch nach der Marke liegen — bis vier Stunden weit
|
||||
assert client.patch(f"/api/clips/{m['id']}", json={"vorher": -60}).json()["vorher"] == -60
|
||||
assert client.patch(f"/api/clips/{m['id']}", json={"vorher": -99999}).json()["vorher"] == -clips.MAX_VORHER
|
||||
client.patch(f"/api/clips/{m['id']}", json={"vorher": clips.MAX_VORHER, "dauer": 1})
|
||||
|
||||
# Schneiden: der Playback-Server wird nachgestellt
|
||||
class Antwort:
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<script setup>
|
||||
// Der Zeitstrahl der Aufnahme rund um die Marke. Ein Balken zeigt das
|
||||
// Fenster des Clips; an seinen Enden zieht man Anfang und Ende, in der
|
||||
// Mitte schiebt man das ganze Fenster. Zeiten in Sekunden relativ zur
|
||||
// Marke: 0 ist der Moment, an dem C gedrückt wurde.
|
||||
// Mitte schiebt man das ganze Fenster. Daneben zieht man den Strahl
|
||||
// selbst nach links und rechts. Zeiten in Sekunden relativ zur Marke:
|
||||
// 0 ist der Moment, an dem C gedrückt wurde.
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
// Stücke der Aufnahme: [{ von, bis }] relativ zur Marke
|
||||
segmente: { type: Array, default: () => [] },
|
||||
maxVorher: { type: Number, default: 600 },
|
||||
maxVorher: { type: Number, default: 4 * 3600 },
|
||||
minDauer: { type: Number, default: 3 },
|
||||
maxDauer: { type: Number, default: 180 },
|
||||
// Uhrzeit der Marke, für die Beschriftung
|
||||
@@ -36,15 +37,28 @@ function prozent(t) {
|
||||
const start = computed(() => -vorher.value)
|
||||
const ende = computed(() => -vorher.value + dauer.value)
|
||||
|
||||
// Wo die Aufnahme überhaupt Material hat
|
||||
// Wo die Aufnahme überhaupt Material hat — so weit lässt sich der Strahl schieben
|
||||
const grenzen = computed(() => {
|
||||
if (!props.segmente.length) return { von: -props.maxVorher, bis: props.maxDauer }
|
||||
if (!props.segmente.length) return { von: -props.maxVorher, bis: props.maxVorher }
|
||||
return {
|
||||
von: Math.min(...props.segmente.map((s) => s.von)),
|
||||
bis: Math.max(...props.segmente.map((s) => s.bis)),
|
||||
}
|
||||
})
|
||||
|
||||
// Das Stück der Aufnahme, in dem das Fenster liegt. Zwischen zwei
|
||||
// Stücken ist Loch — dort gibt es kein Bild, dorthin darf das Fenster
|
||||
// nicht. Liegt es in einem Loch, gilt das nähere Stück.
|
||||
function abstand(s, t) {
|
||||
return t < s.von ? s.von - t : t > s.bis ? t - s.bis : 0
|
||||
}
|
||||
|
||||
function stueckUm(t) {
|
||||
if (!props.segmente.length) return { von: -props.maxVorher, bis: props.maxVorher }
|
||||
const s = props.segmente.reduce((a, b) => (abstand(a, t) <= abstand(b, t) ? a : b))
|
||||
return { von: Math.max(s.von, -props.maxVorher), bis: Math.min(s.bis, props.maxVorher) }
|
||||
}
|
||||
|
||||
function zoomen(schritt) {
|
||||
zoom.value = Math.max(0, Math.min(ZOOMS.length - 1, zoom.value + schritt))
|
||||
mitte.value = (start.value + ende.value) / 2
|
||||
@@ -72,19 +86,30 @@ let ausgang = null
|
||||
|
||||
function anfassen(e, was) {
|
||||
griff = was
|
||||
ausgang = { x: e.clientX, vorher: vorher.value, dauer: dauer.value }
|
||||
ausgang = {
|
||||
x: e.clientX,
|
||||
vorher: vorher.value,
|
||||
dauer: dauer.value,
|
||||
mitte: mitte.value,
|
||||
// Das Stück, in dem das Fenster beim Anfassen liegt — darin bleibt es
|
||||
stueck: stueckUm(-vorher.value + dauer.value / 2),
|
||||
}
|
||||
e.currentTarget.setPointerCapture(e.pointerId)
|
||||
}
|
||||
|
||||
function ziehen(e) {
|
||||
if (!griff || !strahl.value) return
|
||||
const dt = ((e.clientX - ausgang.x) / strahl.value.clientWidth) * spanne.value
|
||||
const g = grenzen.value
|
||||
if (griff === 'links') {
|
||||
const g = ausgang.stueck
|
||||
if (griff === 'bahn') {
|
||||
// Den Strahl selbst schieben — die Zeiten laufen der Hand entgegen
|
||||
const r = grenzen.value
|
||||
mitte.value = Math.max(r.von, Math.min(r.bis, ausgang.mitte - dt))
|
||||
} else if (griff === 'links') {
|
||||
// Der Anfang wandert, das Ende bleibt stehen
|
||||
const altesEnde = -ausgang.vorher + ausgang.dauer
|
||||
let neuerStart = -ausgang.vorher + dt
|
||||
neuerStart = Math.max(g.von, -props.maxVorher, neuerStart)
|
||||
neuerStart = Math.max(g.von, neuerStart)
|
||||
neuerStart = Math.min(
|
||||
altesEnde - props.minDauer,
|
||||
Math.max(altesEnde - props.maxDauer, neuerStart),
|
||||
@@ -97,18 +122,20 @@ function ziehen(e) {
|
||||
const d = Math.max(props.minDauer, Math.min(props.maxDauer, neuesEnde + ausgang.vorher))
|
||||
dauer.value = Math.round(d)
|
||||
} else {
|
||||
// Das ganze Fenster verschieben
|
||||
// Das ganze Fenster verschieben, aber nicht aus dem Stück hinaus
|
||||
let neuerStart = -ausgang.vorher + dt
|
||||
neuerStart = Math.max(g.von, -props.maxVorher, Math.min(g.bis - ausgang.dauer, neuerStart))
|
||||
neuerStart = Math.max(g.von, Math.min(g.bis - ausgang.dauer, neuerStart))
|
||||
vorher.value = Math.round(-neuerStart)
|
||||
}
|
||||
}
|
||||
|
||||
function loslassen(e) {
|
||||
if (!griff) return
|
||||
const war = griff
|
||||
griff = null
|
||||
e.currentTarget.releasePointerCapture?.(e.pointerId)
|
||||
emit('ende')
|
||||
// Nur ein verändertes Fenster braucht eine neue Vorschau
|
||||
if (war !== 'bahn') emit('ende')
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -134,6 +161,7 @@ function loslassen(e) {
|
||||
<div
|
||||
ref="strahl"
|
||||
class="strahl"
|
||||
@pointerdown="anfassen($event, 'bahn')"
|
||||
@pointermove="ziehen"
|
||||
@pointerup="loslassen"
|
||||
@pointercancel="loslassen"
|
||||
@@ -153,7 +181,7 @@ function loslassen(e) {
|
||||
<span
|
||||
class="bereich"
|
||||
:style="{ left: prozent(start) + '%', width: prozent(ende) - prozent(start) + '%' }"
|
||||
@pointerdown="anfassen($event, 'mitte')"
|
||||
@pointerdown.stop="anfassen($event, 'mitte')"
|
||||
>
|
||||
<span class="griff links" @pointerdown.stop="anfassen($event, 'links')"></span>
|
||||
<span class="griff rechts" @pointerdown.stop="anfassen($event, 'rechts')"></span>
|
||||
@@ -206,6 +234,7 @@ function loslassen(e) {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
cursor: grab;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user