feat(api): delete marker endpoint
This commit is contained in:
16
bruno/VoD-System/Markers/Delete Marker.bru
Normal file
16
bruno/VoD-System/Markers/Delete Marker.bru
Normal 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
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
getStreamHistory,
|
||||
getMarkers,
|
||||
createMarker,
|
||||
deleteMarker,
|
||||
saveSectionByMarkers,
|
||||
clipSection,
|
||||
getJob,
|
||||
@@ -120,7 +121,7 @@ const StreamControl = () => {
|
||||
|
||||
const refreshMarkers = () => {
|
||||
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())))
|
||||
.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 idStr = String(id);
|
||||
setMarkerSaveError(null);
|
||||
@@ -289,28 +301,36 @@ const StreamControl = () => {
|
||||
{markerError && <p className="text-sm text-red-500">{markerError}</p>}
|
||||
|
||||
{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) => {
|
||||
const isStart = startMarkerId === String(m.id);
|
||||
const isEnd = endMarkerId === String(m.id);
|
||||
return (
|
||||
<button
|
||||
key={m.id}
|
||||
onClick={() => handleChipClick(m.id)}
|
||||
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",
|
||||
isStart && "border-primary bg-primary/5 ring-1 ring-primary",
|
||||
isEnd && "border-accent bg-accent/5 ring-1 ring-accent",
|
||||
!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-[11px] text-gray-400">
|
||||
{formatLocalDate(m.timestamp, { hour: "2-digit", minute: "2-digit", second: "2-digit" })}
|
||||
{isStart && <span className="text-primary font-medium"> · start</span>}
|
||||
{isEnd && <span className="text-accent font-medium"> · end</span>}
|
||||
</span>
|
||||
</button>
|
||||
<div key={m.id} className="group relative flex-shrink-0">
|
||||
<button
|
||||
onClick={() => handleChipClick(m.id)}
|
||||
className={clsx(
|
||||
"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",
|
||||
isEnd && "border-accent bg-accent/5 ring-1 ring-accent",
|
||||
!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-[11px] text-gray-400">
|
||||
{formatLocalDate(m.timestamp, { hour: "2-digit", minute: "2-digit", second: "2-digit" })}
|
||||
{isStart && <span className="text-primary font-medium"> · start</span>}
|
||||
{isEnd && <span className="text-accent font-medium"> · end</span>}
|
||||
</span>
|
||||
</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>
|
||||
|
||||
@@ -263,6 +263,18 @@ const createMarker = async (message: string): Promise<Marker> => {
|
||||
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 response = await fetch(API_URL + '/api/v1/media/save', {
|
||||
method: 'POST',
|
||||
@@ -327,6 +339,7 @@ export {
|
||||
getStreamHistory,
|
||||
getMarkers,
|
||||
createMarker,
|
||||
deleteMarker,
|
||||
saveSection,
|
||||
saveSectionByMarkers,
|
||||
clipSection,
|
||||
|
||||
@@ -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) {
|
||||
return new MarkerResponse(
|
||||
marker.getId(),
|
||||
|
||||
@@ -72,6 +72,11 @@ public class MarkerService {
|
||||
return markerRepository.saveAndFlush(marker);
|
||||
}
|
||||
|
||||
public void deleteMarker(Long id) {
|
||||
Marker marker = getMarkerById(id);
|
||||
markerRepository.delete(marker);
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 360_000)
|
||||
@Transactional
|
||||
public void deleteOldMarkers() {
|
||||
|
||||
Reference in New Issue
Block a user