update
This commit is contained in:
@@ -44,7 +44,9 @@ PLAYER_LATENZ = 4
|
|||||||
# Das Fenster um die Marke: Standard und Grenzen
|
# Das Fenster um die Marke: Standard und Grenzen
|
||||||
VORHER = 30
|
VORHER = 30
|
||||||
DAUER = 35
|
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
|
MIN_DAUER = 3
|
||||||
MAX_DAUER = 180
|
MAX_DAUER = 180
|
||||||
MAX_TITEL = 80
|
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:
|
if titel is not None:
|
||||||
felder["titel"] = titel.strip()[:MAX_TITEL] or None
|
felder["titel"] = titel.strip()[:MAX_TITEL] or None
|
||||||
if vorher is not 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:
|
if dauer is not None:
|
||||||
felder["dauer"] = max(MIN_DAUER, min(MAX_DAUER, float(dauer)))
|
felder["dauer"] = max(MIN_DAUER, min(MAX_DAUER, float(dauer)))
|
||||||
fenster_neu = any(
|
fenster_neu = any(
|
||||||
@@ -173,7 +175,7 @@ def _datei_weg(name):
|
|||||||
def fenster(row, vorher=None, dauer=None):
|
def fenster(row, vorher=None, dauer=None):
|
||||||
"""Start (RFC3339) und Dauer des Ausschnitts um die Marke."""
|
"""Start (RFC3339) und Dauer des Ausschnitts um die Marke."""
|
||||||
marke = datetime.fromisoformat(row["marke_at"])
|
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)))
|
d = row["dauer"] if dauer is None else max(MIN_DAUER, min(MAX_DAUER, float(dauer)))
|
||||||
return _iso(marke - timedelta(seconds=v)), d
|
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):
|
def test_fenster_wird_begrenzt_und_macht_schnitt_ungueltig(client, monkeypatch):
|
||||||
m = marke(client)
|
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")
|
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
|
# Schneiden: der Playback-Server wird nachgestellt
|
||||||
class Antwort:
|
class Antwort:
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
// Der Zeitstrahl der Aufnahme rund um die Marke. Ein Balken zeigt das
|
// 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
|
// Fenster des Clips; an seinen Enden zieht man Anfang und Ende, in der
|
||||||
// Mitte schiebt man das ganze Fenster. Zeiten in Sekunden relativ zur
|
// Mitte schiebt man das ganze Fenster. Daneben zieht man den Strahl
|
||||||
// Marke: 0 ist der Moment, an dem C gedrückt wurde.
|
// 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'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// Stücke der Aufnahme: [{ von, bis }] relativ zur Marke
|
// Stücke der Aufnahme: [{ von, bis }] relativ zur Marke
|
||||||
segmente: { type: Array, default: () => [] },
|
segmente: { type: Array, default: () => [] },
|
||||||
maxVorher: { type: Number, default: 600 },
|
maxVorher: { type: Number, default: 4 * 3600 },
|
||||||
minDauer: { type: Number, default: 3 },
|
minDauer: { type: Number, default: 3 },
|
||||||
maxDauer: { type: Number, default: 180 },
|
maxDauer: { type: Number, default: 180 },
|
||||||
// Uhrzeit der Marke, für die Beschriftung
|
// Uhrzeit der Marke, für die Beschriftung
|
||||||
@@ -36,15 +37,28 @@ function prozent(t) {
|
|||||||
const start = computed(() => -vorher.value)
|
const start = computed(() => -vorher.value)
|
||||||
const ende = computed(() => -vorher.value + dauer.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(() => {
|
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 {
|
return {
|
||||||
von: Math.min(...props.segmente.map((s) => s.von)),
|
von: Math.min(...props.segmente.map((s) => s.von)),
|
||||||
bis: Math.max(...props.segmente.map((s) => s.bis)),
|
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) {
|
function zoomen(schritt) {
|
||||||
zoom.value = Math.max(0, Math.min(ZOOMS.length - 1, zoom.value + schritt))
|
zoom.value = Math.max(0, Math.min(ZOOMS.length - 1, zoom.value + schritt))
|
||||||
mitte.value = (start.value + ende.value) / 2
|
mitte.value = (start.value + ende.value) / 2
|
||||||
@@ -72,19 +86,30 @@ let ausgang = null
|
|||||||
|
|
||||||
function anfassen(e, was) {
|
function anfassen(e, was) {
|
||||||
griff = 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)
|
e.currentTarget.setPointerCapture(e.pointerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ziehen(e) {
|
function ziehen(e) {
|
||||||
if (!griff || !strahl.value) return
|
if (!griff || !strahl.value) return
|
||||||
const dt = ((e.clientX - ausgang.x) / strahl.value.clientWidth) * spanne.value
|
const dt = ((e.clientX - ausgang.x) / strahl.value.clientWidth) * spanne.value
|
||||||
const g = grenzen.value
|
const g = ausgang.stueck
|
||||||
if (griff === 'links') {
|
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
|
// Der Anfang wandert, das Ende bleibt stehen
|
||||||
const altesEnde = -ausgang.vorher + ausgang.dauer
|
const altesEnde = -ausgang.vorher + ausgang.dauer
|
||||||
let neuerStart = -ausgang.vorher + dt
|
let neuerStart = -ausgang.vorher + dt
|
||||||
neuerStart = Math.max(g.von, -props.maxVorher, neuerStart)
|
neuerStart = Math.max(g.von, neuerStart)
|
||||||
neuerStart = Math.min(
|
neuerStart = Math.min(
|
||||||
altesEnde - props.minDauer,
|
altesEnde - props.minDauer,
|
||||||
Math.max(altesEnde - props.maxDauer, neuerStart),
|
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))
|
const d = Math.max(props.minDauer, Math.min(props.maxDauer, neuesEnde + ausgang.vorher))
|
||||||
dauer.value = Math.round(d)
|
dauer.value = Math.round(d)
|
||||||
} else {
|
} else {
|
||||||
// Das ganze Fenster verschieben
|
// Das ganze Fenster verschieben, aber nicht aus dem Stück hinaus
|
||||||
let neuerStart = -ausgang.vorher + dt
|
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)
|
vorher.value = Math.round(-neuerStart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loslassen(e) {
|
function loslassen(e) {
|
||||||
if (!griff) return
|
if (!griff) return
|
||||||
|
const war = griff
|
||||||
griff = null
|
griff = null
|
||||||
e.currentTarget.releasePointerCapture?.(e.pointerId)
|
e.currentTarget.releasePointerCapture?.(e.pointerId)
|
||||||
emit('ende')
|
// Nur ein verändertes Fenster braucht eine neue Vorschau
|
||||||
|
if (war !== 'bahn') emit('ende')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -134,6 +161,7 @@ function loslassen(e) {
|
|||||||
<div
|
<div
|
||||||
ref="strahl"
|
ref="strahl"
|
||||||
class="strahl"
|
class="strahl"
|
||||||
|
@pointerdown="anfassen($event, 'bahn')"
|
||||||
@pointermove="ziehen"
|
@pointermove="ziehen"
|
||||||
@pointerup="loslassen"
|
@pointerup="loslassen"
|
||||||
@pointercancel="loslassen"
|
@pointercancel="loslassen"
|
||||||
@@ -153,7 +181,7 @@ function loslassen(e) {
|
|||||||
<span
|
<span
|
||||||
class="bereich"
|
class="bereich"
|
||||||
:style="{ left: prozent(start) + '%', width: prozent(ende) - prozent(start) + '%' }"
|
: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 links" @pointerdown.stop="anfassen($event, 'links')"></span>
|
||||||
<span class="griff rechts" @pointerdown.stop="anfassen($event, 'rechts')"></span>
|
<span class="griff rechts" @pointerdown.stop="anfassen($event, 'rechts')"></span>
|
||||||
@@ -206,6 +234,7 @@ function loslassen(e) {
|
|||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
cursor: grab;
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user