Silent timeline removed when hitting export
This commit is contained in:
@@ -10,6 +10,7 @@ type WsMsg =
|
||||
| { type: 'segments'; segments: { start: number; end: number }[]; duration: number }
|
||||
| { type: 'progress'; percent: number }
|
||||
| { type: 'done'; message: string }
|
||||
| { type: 'timeline'; segments: { start: number; end: number }[]; duration: number }
|
||||
| { type: 'error'; message: string }
|
||||
|
||||
type Phase = 'idle' | 'uploading' | 'analyzing' | 'ready' | 'exporting' | 'done'
|
||||
@@ -66,6 +67,13 @@ export default function App() {
|
||||
return { ...prev, progress: msg.percent }
|
||||
case 'done':
|
||||
return { ...prev, phase: 'done', outputFile: msg.message, progress: 100 }
|
||||
case 'timeline':
|
||||
return {
|
||||
...prev,
|
||||
phase: 'ready',
|
||||
duration: msg.duration,
|
||||
segments: msg.segments.map((s) => ({ ...s, kept: true })),
|
||||
}
|
||||
case 'error':
|
||||
return { ...prev, phase: 'ready', error: msg.message }
|
||||
default:
|
||||
@@ -86,7 +94,7 @@ export default function App() {
|
||||
minSilence: number,
|
||||
padding: number,
|
||||
) => {
|
||||
setState((prev) => ({ ...prev, phase: 'analyzing', segments: [], error: null }))
|
||||
setState((prev) => ({ ...prev, phase: 'analyzing', segments: [], outputFile: null, error: null }))
|
||||
try {
|
||||
const res = await fetch('/analyze', {
|
||||
method: 'POST',
|
||||
@@ -136,7 +144,7 @@ export default function App() {
|
||||
const res = await fetch('/export', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ filename: state.filename, segments: kept }),
|
||||
body: JSON.stringify({ filename: state.filename, segments: kept, duration: state.duration }),
|
||||
})
|
||||
if (!res.ok) throw new Error(await res.text())
|
||||
} catch (e) {
|
||||
@@ -254,7 +262,7 @@ export default function App() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{state.phase === 'done' && state.outputFile && (
|
||||
{state.outputFile && (
|
||||
<div className="done-panel">
|
||||
<span className="done-check">✓</span>
|
||||
<p>Export complete!</p>
|
||||
|
||||
@@ -14,6 +14,13 @@ import (
|
||||
type exportRequest struct {
|
||||
Filename string `json:"filename"`
|
||||
Segments []transcode.Segment `json:"segments"`
|
||||
Duration float64 `json:"duration"`
|
||||
}
|
||||
|
||||
type updateTimeLine struct {
|
||||
Type string `json:"type"`
|
||||
Segments []transcode.Segment `json:"segments"`
|
||||
Duration float64 `json:"duration"`
|
||||
}
|
||||
|
||||
func ExportHandler(st *store.Store, hub *ws.Hub) http.HandlerFunc {
|
||||
@@ -28,6 +35,7 @@ func ExportHandler(st *store.Store, hub *ws.Hub) http.HandlerFunc {
|
||||
http.Error(w, "bad JSON: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Segments) == 0 {
|
||||
http.Error(w, "no segments", http.StatusBadRequest)
|
||||
return
|
||||
@@ -50,15 +58,30 @@ func ExportHandler(st *store.Store, hub *ws.Hub) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
msg, _ := json.Marshal(map[string]string{
|
||||
doneMsg, _ := json.Marshal(map[string]string{
|
||||
"type": "done",
|
||||
"message": outputKey,
|
||||
})
|
||||
hub.Broadcast(msg)
|
||||
hub.Broadcast(doneMsg)
|
||||
|
||||
// Build normalized segments for the exported video: timestamps start at 0
|
||||
// with no gaps, so the timeline reflects the exported file's layout.
|
||||
var normalized []transcode.Segment
|
||||
cursor := 0.0
|
||||
for _, seg := range req.Segments {
|
||||
dur := seg.End - seg.Start
|
||||
normalized = append(normalized, transcode.Segment{Start: cursor, End: cursor + dur})
|
||||
cursor += dur
|
||||
}
|
||||
timelineMsg, _ := json.Marshal(updateTimeLine{
|
||||
Type: "timeline",
|
||||
Segments: normalized,
|
||||
Duration: cursor,
|
||||
})
|
||||
hub.Broadcast(timelineMsg)
|
||||
}()
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
|
||||
log.Println("Export handler works")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@ func ZoomHandler(hub *ws.Hub) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
h := transcode.HandlerZoom{Center: req.Center}
|
||||
result := h.TimelineZoom(req.Zoom, req.Duration)
|
||||
result := transcode.TimelineZoom(req.Center, req.Zoom, req.Duration)
|
||||
|
||||
data, _ := json.Marshal(result)
|
||||
hub.Broadcast(data)
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package transcode
|
||||
|
||||
// HandlerZoom holds the current scroll center for timeline zoom calculations.
|
||||
type HandlerZoom struct {
|
||||
Center float64 // center of the visible window in seconds
|
||||
}
|
||||
|
||||
// ZoomResult is the result of a TimelineZoom calculation.
|
||||
type ZoomResult struct {
|
||||
Type string `json:"type"`
|
||||
@@ -14,13 +9,13 @@ type ZoomResult struct {
|
||||
}
|
||||
|
||||
// TimelineZoom computes the visible time window for the given zoomPercentage
|
||||
// (1–100, where 100 = full duration visible) centered on h.Center.
|
||||
func (h *HandlerZoom) TimelineZoom(zoomPercentage, duration float64) *ZoomResult {
|
||||
// (1–100, where 100 = full duration visible) centered on zoomCenter.
|
||||
func TimelineZoom(zoomCenter, zoomPercentage, duration float64) *ZoomResult {
|
||||
visibleDuration := duration * (zoomPercentage / 100)
|
||||
half := visibleDuration / 2
|
||||
|
||||
viewStart := h.Center - half
|
||||
viewEnd := h.Center + half
|
||||
viewStart := zoomCenter - half
|
||||
viewEnd := zoomCenter + half
|
||||
|
||||
// Clamp to [0, duration], shifting the window rather than just truncating
|
||||
// so the visible span stays the same size when near the edges.
|
||||
|
||||
Reference in New Issue
Block a user