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,
|
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,16 +301,16 @@ 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 (
|
||||||
|
<div key={m.id} className="group relative flex-shrink-0">
|
||||||
<button
|
<button
|
||||||
key={m.id}
|
|
||||||
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"
|
||||||
@@ -311,6 +323,14 @@ const StreamControl = () => {
|
|||||||
{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>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user