feat(api): delete marker endpoint

This commit is contained in:
2026-07-02 19:37:27 +01:00
parent b8b4d3b05c
commit fe735faba8
5 changed files with 82 additions and 19 deletions

View File

@@ -0,0 +1,16 @@
meta {
name: Delete Marker
type: http
seq: 4
}
delete {
url: {{base_url}}/markers/5
body: none
auth: inherit
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -9,6 +9,7 @@ import {
getStreamHistory, getStreamHistory,
getMarkers, getMarkers,
createMarker, createMarker,
deleteMarker,
saveSectionByMarkers, saveSectionByMarkers,
clipSection, clipSection,
getJob, getJob,
@@ -120,7 +121,7 @@ const StreamControl = () => {
const refreshMarkers = () => { const refreshMarkers = () => {
getMarkers() getMarkers()
.then((data) => setMarkers(data.filter((m) => m.streamId === streamStatus?.id) .then((data) => setMarkers(data
.sort((a, b) => stringToDate(a.timestamp).getTime() - stringToDate(b.timestamp).getTime()))) .sort((a, b) => stringToDate(a.timestamp).getTime() - stringToDate(b.timestamp).getTime())))
.catch(() => {}); .catch(() => {});
}; };
@@ -209,6 +210,17 @@ const StreamControl = () => {
} }
}; };
const handleDeleteMarker = async (id: number) => {
try {
await deleteMarker(id);
if (startMarkerId === String(id)) setStartMarkerId("");
if (endMarkerId === String(id)) setEndMarkerId("");
refreshMarkers();
} catch (err) {
setMarkerError(err instanceof Error ? err.message : "Failed to delete marker");
}
};
const handleChipClick = (id: number) => { const handleChipClick = (id: number) => {
const idStr = String(id); const idStr = String(id);
setMarkerSaveError(null); setMarkerSaveError(null);
@@ -289,28 +301,36 @@ const StreamControl = () => {
{markerError && <p className="text-sm text-red-500">{markerError}</p>} {markerError && <p className="text-sm text-red-500">{markerError}</p>}
{markers.length > 0 && ( {markers.length > 0 && (
<div className="flex gap-2 overflow-x-auto pb-0.5 -mx-1 px-1"> <div className="flex gap-2 overflow-x-auto pb-0.5 -mx-1 px-1 pt-1.5">
{markers.map((m) => { {markers.map((m) => {
const isStart = startMarkerId === String(m.id); const isStart = startMarkerId === String(m.id);
const isEnd = endMarkerId === String(m.id); const isEnd = endMarkerId === String(m.id);
return ( return (
<button <div key={m.id} className="group relative flex-shrink-0">
key={m.id} <button
onClick={() => handleChipClick(m.id)} onClick={() => handleChipClick(m.id)}
className={clsx( className={clsx(
"flex-shrink-0 flex flex-col items-start gap-0.5 rounded-lg border px-3 py-1.5 text-left transition-colors duration-150", "flex flex-col items-start gap-0.5 rounded-lg border px-3 py-1.5 text-left transition-colors duration-150",
isStart && "border-primary bg-primary/5 ring-1 ring-primary", isStart && "border-primary bg-primary/5 ring-1 ring-primary",
isEnd && "border-accent bg-accent/5 ring-1 ring-accent", isEnd && "border-accent bg-accent/5 ring-1 ring-accent",
!isStart && !isEnd && "border-gray-200 hover:border-gray-300 hover:bg-gray-50" !isStart && !isEnd && "border-gray-200 hover:border-gray-300 hover:bg-gray-50"
)} )}
> >
<span className="text-xs font-medium text-gray-800 max-w-40 truncate">{m.message}</span> <span className="text-xs font-medium text-gray-800 max-w-40 truncate">{m.message}</span>
<span className="text-[11px] text-gray-400"> <span className="text-[11px] text-gray-400">
{formatLocalDate(m.timestamp, { hour: "2-digit", minute: "2-digit", second: "2-digit" })} {formatLocalDate(m.timestamp, { hour: "2-digit", minute: "2-digit", second: "2-digit" })}
{isStart && <span className="text-primary font-medium"> · start</span>} {isStart && <span className="text-primary font-medium"> · start</span>}
{isEnd && <span className="text-accent font-medium"> · end</span>} {isEnd && <span className="text-accent font-medium"> · end</span>}
</span> </span>
</button> </button>
<button
onClick={(e) => { e.stopPropagation(); handleDeleteMarker(m.id); }}
title="Delete marker"
className="hidden group-hover:flex items-center justify-center absolute -top-1.5 -right-1.5 w-4 h-4 rounded-full bg-gray-400 hover:bg-red-500 text-white transition-colors duration-150"
>
<X size={10} />
</button>
</div>
); );
})} })}
</div> </div>

View File

@@ -263,6 +263,18 @@ const createMarker = async (message: string): Promise<Marker> => {
return result.data; return result.data;
}; };
const deleteMarker = async (id: number): Promise<void> => {
const response = await fetch(API_URL + `/api/v1/markers/${id}`, {
method: 'DELETE',
credentials: 'include',
});
if (!response.ok) throw new Error(`Failed to delete marker: ${response.status}`);
const result: APIResponse = await response.json();
if (result.status === 'error') throw new Error(`Failed to delete marker: ${result.message}`);
};
const saveSection = async (startTime: string, endTime: string, title?: string, description?: string): Promise<string> => { const saveSection = async (startTime: string, endTime: string, title?: string, description?: string): Promise<string> => {
const response = await fetch(API_URL + '/api/v1/media/save', { const response = await fetch(API_URL + '/api/v1/media/save', {
method: 'POST', method: 'POST',
@@ -327,6 +339,7 @@ export {
getStreamHistory, getStreamHistory,
getMarkers, getMarkers,
createMarker, createMarker,
deleteMarker,
saveSection, saveSection,
saveSectionByMarkers, saveSectionByMarkers,
clipSection, clipSection,

View File

@@ -51,6 +51,15 @@ public class MarkerController {
); );
} }
@DeleteMapping("/{id}")
public ResponseEntity<APIResponse<String>> deleteMarker(@PathVariable Long id) {
markerService.deleteMarker(id);
return ResponseEntity.ok(
new APIResponse<>(SUCCESS, "Marker deleted successfully", "Marker " + id + " deleted.")
);
}
private MarkerResponse convertToDTO(Marker marker) { private MarkerResponse convertToDTO(Marker marker) {
return new MarkerResponse( return new MarkerResponse(
marker.getId(), marker.getId(),

View File

@@ -72,6 +72,11 @@ public class MarkerService {
return markerRepository.saveAndFlush(marker); return markerRepository.saveAndFlush(marker);
} }
public void deleteMarker(Long id) {
Marker marker = getMarkerById(id);
markerRepository.delete(marker);
}
@Scheduled(fixedDelay = 360_000) @Scheduled(fixedDelay = 360_000)
@Transactional @Transactional
public void deleteOldMarkers() { public void deleteOldMarkers() {