fix(dashcam): wrap action buttons in div, ffmpeg stream for MOV playback

- local-videos.js: action buttons go in a div.data-list-actions inside td
  so td stays a table cell (not flex); thumbnail td gets fixed width 9rem
- app.py: video route now pipes ffmpeg with -c copy -movflags
  frag_keyframe+empty_moov so browsers get a fragmented MP4 stream that
  starts playing immediately without needing the moov atom at the end

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-10 00:17:10 -04:00
co-authored by Copilot
parent 5d9b2f6556
commit 51322d0e00
2 changed files with 31 additions and 44 deletions
+11 -6
View File
@@ -98,6 +98,8 @@
// Thumbnail cell
const preview = document.createElement("td");
preview.style.width = "9rem";
preview.style.padding = "0.25rem";
const image = document.createElement("img");
image.className = "local-video-thumbnail";
image.alt = "";
@@ -132,16 +134,18 @@
row.appendChild(upload);
if (file.uploaded) uploadedCount++;
// Actions
const actions = document.createElement("td");
actions.className = "data-list-actions";
// Actions — wrap in div so td stays a table cell (not flex)
const actionsTd = document.createElement("td");
actionsTd.style.whiteSpace = "nowrap";
const actionsDiv = document.createElement("div");
actionsDiv.className = "data-list-actions";
const playButton = document.createElement("button");
playButton.type = "button";
playButton.className = "btn btn-sm btn-outline-primary btn-icon-mobile";
playButton.dataset.playPath = file.path;
playButton.innerHTML = '<i class="fas fa-play"></i><span class="btn-label"> Play</span>';
actions.appendChild(playButton);
actionsDiv.appendChild(playButton);
const deleteButton = document.createElement("button");
deleteButton.type = "button";
@@ -149,8 +153,9 @@
deleteButton.dataset.deleteLocalPath = file.path;
deleteButton.dataset.confirm = `Delete the local Pi copy of "${file.path}"?`;
deleteButton.innerHTML = '<i class="fas fa-trash"></i><span class="btn-label"> Del</span>';
actions.appendChild(deleteButton);
row.appendChild(actions);
actionsDiv.appendChild(deleteButton);
actionsTd.appendChild(actionsDiv);
row.appendChild(actionsTd);
body.appendChild(row);
});