feat(frontend): streaming controls page

This commit is contained in:
2026-07-02 19:27:23 +01:00
parent a051407c1e
commit b8b4d3b05c
5 changed files with 509 additions and 3 deletions

View File

@@ -1,4 +1,4 @@
import type { VideoMetadata, APIResponse, User, Clip, Vod, JobResponse } from "./types.ts";
import type { VideoMetadata, APIResponse, User, Clip, Vod, JobResponse, StreamStatus, StreamHistoryItem, Marker } from "./types.ts";
const API_URL = import.meta.env.VITE_API_URL;
@@ -219,6 +219,92 @@ const isThumbnailAvailable = async (thumbnailUrl: string): Promise<boolean> => {
return response.ok;
};
const getCurrentStream = async (): Promise<StreamStatus> => {
const response = await fetch(API_URL + '/api/v1/stream/current', { credentials: 'include' });
if (!response.ok) throw new Error(`Failed to fetch stream status: ${response.status}`);
const result: APIResponse = await response.json();
if (result.status === 'error') throw new Error(`Failed to fetch stream status: ${result.message}`);
return result.data;
};
const getStreamHistory = async (userId: number): Promise<StreamHistoryItem[]> => {
const response = await fetch(API_URL + `/api/v1/stream/history/${userId}`, { credentials: 'include' });
if (!response.ok) throw new Error(`Failed to fetch stream history: ${response.status}`);
const result: APIResponse = await response.json();
if (result.status === 'error') throw new Error(`Failed to fetch stream history: ${result.message}`);
return result.data;
};
const getMarkers = async (): Promise<Marker[]> => {
const response = await fetch(API_URL + '/api/v1/markers', { credentials: 'include' });
if (!response.ok) throw new Error(`Failed to fetch markers: ${response.status}`);
const result: APIResponse = await response.json();
if (result.status === 'error') throw new Error(`Failed to fetch markers: ${result.message}`);
return result.data;
};
const createMarker = async (message: string): Promise<Marker> => {
const response = await fetch(API_URL + '/api/v1/markers', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ message }),
});
const result: APIResponse = await response.json();
if (!response.ok || result.status === 'error') throw new Error(`Failed to add marker: ${result.message}`);
return result.data;
};
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',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ startTime, endTime, title, description }),
});
const result: APIResponse = await response.json();
if (!response.ok || result.status === 'error') throw new Error(`Failed to save section: ${result.message}`);
return result.data.uuid;
};
const saveSectionByMarkers = async (startMarkerId: number, endMarkerId: number, title?: string, description?: string): Promise<string> => {
const response = await fetch(API_URL + '/api/v1/media/save/markers', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ startMarkerId, endMarkerId, title, description }),
});
const result: APIResponse = await response.json();
if (!response.ok || result.status === 'error') throw new Error(`Failed to save section: ${result.message}`);
return result.data.uuid;
};
const clipSection = async (duration: number, title?: string, description?: string): Promise<string> => {
const response = await fetch(API_URL + '/api/v1/media/clip', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ duration, title, description }),
});
const result: APIResponse = await response.json();
if (!response.ok || result.status === 'error') throw new Error(`Failed to clip section: ${result.message}`);
return result.data.uuid;
};
export {
login,
logout,
@@ -237,4 +323,11 @@ export {
deleteVod,
getStreamStatus,
isThumbnailAvailable,
getCurrentStream,
getStreamHistory,
getMarkers,
createMarker,
saveSection,
saveSectionByMarkers,
clipSection,
};

View File

@@ -16,6 +16,7 @@ type APIResponse = {
}
type User = {
id: number,
name: string,
username: string,
email: string,
@@ -51,11 +52,34 @@ type JobResponse = {
createdAt: string,
};
type StreamStatus = {
isStreaming: boolean,
id: number | null,
}
type StreamHistoryItem = {
id: number,
startDate: string | null,
endDate: string | null,
lastSeen: string | null,
}
type Marker = {
id: number,
userId: number,
streamId: number,
message: string,
timestamp: string,
}
export type {
APIResponse,
VideoMetadata,
User,
Clip,
Vod,
JobResponse
JobResponse,
StreamStatus,
StreamHistoryItem,
Marker
}