Feat : Streamers page revamp
All checks were successful
release / build (push) Successful in 27s
release / verify-windows (push) Successful in 1m26s

This commit is contained in:
jeanotx32
2026-08-12 15:28:56 +02:00
parent d8846aac9a
commit 96b364bd66
15 changed files with 1625 additions and 131 deletions

View File

@@ -880,6 +880,8 @@ export interface WatchTarget {
/** Agent qui enregistrera ce streamer, s'il est assigné. */
agentId: string | null;
notify: boolean;
/** Épinglé en tête de liste. Purement un confort de tri, sans effet ailleurs. */
favorite: boolean;
/**
* Lancer l'enregistrement sans intervention dès que ce profil est en direct
* depuis assez longtemps. Exige un agent assigné, et un seul profil en
@@ -906,6 +908,70 @@ export interface WatchTarget {
lastLiveEndedAt: number | null;
}
// ---------------------------------------------------------------------------
// Historique de diffusion
// ---------------------------------------------------------------------------
/**
* Une diffusion publique observée, du passage en direct à sa fin.
*
* Distinct de `WatchTarget.lastLive*`, qui ne garde que la dernière : ici on
* conserve l'historique, c'est ce qui rend une frise possible.
*/
export interface StreamSession {
id: number;
targetId: string;
startedAt: number;
/** Nul tant que la diffusion est en cours. */
endedAt: number | null;
}
/**
* Un intervalle réellement capturé par une VM.
*
* Séparé des diffusions à dessein : un enregistrement démarre en général après
* le début du stream, peut s'arrêter avant sa fin, et une même diffusion peut
* en compter plusieurs. Les superposer sur la frise est justement ce qui montre
* ce qui a été gardé et ce qui a été manqué.
*/
export interface RecordingSpan {
id: number;
/** Nul si l'enregistrement n'a pas pu être rattaché à un profil suivi. */
targetId: string | null;
agentId: string | null;
startedAt: number;
/** Nul tant que la capture est en cours. */
endedAt: number | null;
}
export interface TimelineData {
sessions: StreamSession[];
spans: RecordingSpan[];
}
/**
* Le pseudo que cette VM est en train de capturer, ou `null`.
*
* `startTargetRecording()` cale la surveillance de l'agent sur le profil qu'il
* enregistre : ce pseudo est donc le seul lien fiable entre une capture en
* cours et le profil concerné. Sans lui, une VM occupée ferait passer *tous*
* les profils qui lui sont assignés pour « en enregistrement », alors qu'elle
* n'en capture qu'un seul à la fois.
*/
export function recordingUsername(agent: Pick<AgentView, 'status' | 'watch'>): string | null {
if (!agent.status.recording) return null;
return agent.watch.username || null;
}
/** Cette VM enregistre-t-elle bien *ce* profil, et non un autre qui lui est assigné ? */
export function isRecordingTarget(
agent: Pick<AgentView, 'status' | 'watch'> | null | undefined,
target: Pick<WatchTarget, 'username'>,
): boolean {
if (!agent) return false;
return recordingUsername(agent) === target.username;
}
export type ServerToDashboard =
| { type: 'snapshot'; agents: AgentView[]; logs: LogEntry[]; targets: WatchTarget[] }
| { type: 'agent'; agent: AgentView }