Feature: Watch Together - History, Titel-Fetch, Next-Button
Server: - Video-History Tracking (max 50 Einträge pro Raum) - History wird bei Skip und Play-Video gespeichert - Server-seitiger Titel-Fetch via noembed.com als Fallback Client: - Aufklappbare History-Sektion im Queue-Panel - "Weiter" Button mit Text-Label statt nur Icon - YouTube-Thumbnails in der Warteschlange - History in RoomState integriert Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a1a1f31c8e
commit
6c57419959
3 changed files with 169 additions and 6 deletions
|
|
@ -12,6 +12,13 @@ interface QueueItem {
|
|||
addedBy: string;
|
||||
}
|
||||
|
||||
interface HistoryItem {
|
||||
url: string;
|
||||
title: string;
|
||||
addedBy: string;
|
||||
playedAt: string; // ISO timestamp
|
||||
}
|
||||
|
||||
interface RoomMember {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
@ -29,6 +36,7 @@ interface Room {
|
|||
currentTime: number;
|
||||
lastSyncAt: number;
|
||||
queue: QueueItem[];
|
||||
history: HistoryItem[];
|
||||
}
|
||||
|
||||
interface WtClient {
|
||||
|
|
@ -78,6 +86,7 @@ function getPlaybackState(room: Room): Record<string, any> {
|
|||
playing: room.playing,
|
||||
currentTime,
|
||||
updatedAt: Date.now(),
|
||||
history: room.history,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +101,7 @@ function serializeRoom(room: Room): Record<string, any> {
|
|||
playing: room.playing,
|
||||
currentTime: room.currentTime + (room.playing ? (Date.now() - room.lastSyncAt) / 1000 : 0),
|
||||
queue: room.queue,
|
||||
history: room.history,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -176,7 +186,7 @@ function handleDisconnect(client: WtClient): void {
|
|||
|
||||
// ── WebSocket Message Handler ──
|
||||
|
||||
function handleMessage(client: WtClient, msg: any): void {
|
||||
async function handleMessage(client: WtClient, msg: any): Promise<void> {
|
||||
switch (msg.type) {
|
||||
case 'create_room': {
|
||||
if (client.roomId) {
|
||||
|
|
@ -203,6 +213,7 @@ function handleMessage(client: WtClient, msg: any): void {
|
|||
currentTime: 0,
|
||||
lastSyncAt: Date.now(),
|
||||
queue: [],
|
||||
history: [],
|
||||
};
|
||||
|
||||
rooms.set(roomId, room);
|
||||
|
|
@ -260,7 +271,17 @@ function handleMessage(client: WtClient, msg: any): void {
|
|||
sendTo(client, { type: 'error', code: 'INVALID_URL', message: 'URL darf nicht leer sein.' });
|
||||
return;
|
||||
}
|
||||
const title = String(msg.title || url).slice(0, 128);
|
||||
let title = String(msg.title || '').trim();
|
||||
if (!title || title === url) {
|
||||
try {
|
||||
const resp = await fetch(`https://noembed.com/embed?url=${encodeURIComponent(url)}`);
|
||||
if (resp.ok) {
|
||||
const data = await resp.json() as Record<string, any>;
|
||||
if (data.title) title = String(data.title).slice(0, 128);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
if (!title) title = url;
|
||||
|
||||
room.queue.push({ url, title, addedBy: client.name });
|
||||
sendToRoom(room.id, { type: 'queue_updated', queue: room.queue });
|
||||
|
|
@ -325,6 +346,17 @@ function handleMessage(client: WtClient, msg: any): void {
|
|||
return;
|
||||
}
|
||||
|
||||
// Track current video in history before replacing
|
||||
if (room.currentVideo) {
|
||||
room.history.push({
|
||||
url: room.currentVideo.url,
|
||||
title: room.currentVideo.title,
|
||||
addedBy: '',
|
||||
playedAt: new Date().toISOString(),
|
||||
});
|
||||
if (room.history.length > 50) room.history = room.history.slice(-50);
|
||||
}
|
||||
|
||||
const index = msg.index != null ? Number(msg.index) : undefined;
|
||||
if (index !== undefined) {
|
||||
if (index < 0 || index >= room.queue.length) {
|
||||
|
|
@ -405,6 +437,17 @@ function handleMessage(client: WtClient, msg: any): void {
|
|||
sendTo(client, { type: 'error', code: 'NOT_HOST', message: 'Nur der Host kann die Wiedergabe steuern.' });
|
||||
return;
|
||||
}
|
||||
// Track current video in history before skipping
|
||||
if (room.currentVideo) {
|
||||
room.history.push({
|
||||
url: room.currentVideo.url,
|
||||
title: room.currentVideo.title,
|
||||
addedBy: '',
|
||||
playedAt: new Date().toISOString(),
|
||||
});
|
||||
// Keep history max 50 items
|
||||
if (room.history.length > 50) room.history = room.history.slice(-50);
|
||||
}
|
||||
if (room.queue.length > 0) {
|
||||
const item = room.queue.shift()!;
|
||||
room.currentVideo = { url: item.url, title: item.title };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue