File size: 5,960 Bytes
6ce4ca6
 
 
 
 
67a499d
 
 
6ce4ca6
 
 
67a499d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ce4ca6
 
 
 
 
 
 
67a499d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/**
 * Video Connection System using Svelte 5 Runes
 * Clean and simple video producer/consumer management
 */

import { video } from "@robothub/transport-server-client";
import type { video as videoTypes } from "@robothub/transport-server-client";
import { settings } from "$lib/runes/settings.svelte";

// Simple connection state using runes
export class VideoConnectionState {
	// Producer state
	producer = $state({
		connected: false,
		client: null as videoTypes.VideoProducer | null,
		roomId: null as string | null,
		stream: null as MediaStream | null
	});

	// Consumer state
	consumer = $state({
		connected: false,
		client: null as videoTypes.VideoConsumer | null,
		roomId: null as string | null,
		stream: null as MediaStream | null
	});

	// Room listing state
	rooms = $state<videoTypes.RoomInfo[]>([]);
	roomsLoading = $state(false);

	// Derived state
	get hasProducer() {
		return this.producer.connected;
	}

	get hasConsumer() {
		return this.consumer.connected;
	}

	get isStreaming() {
		return this.hasProducer && this.producer.stream !== null;
	}

	get canConnectConsumer() {
		return this.hasProducer && this.producer.roomId !== null;
	}
}

// Create global instance
export const videoConnection = new VideoConnectionState();

// External action functions
export const videoActions = {
	// Room management
	async listRooms(workspaceId: string): Promise<videoTypes.RoomInfo[]> {
		videoConnection.roomsLoading = true;
		try {
			const client = new video.VideoClientCore(settings.transportServerUrl);
			const rooms = await client.listRooms(workspaceId);
			videoConnection.rooms = rooms;
			return rooms;
		} catch (error) {
			console.error("Failed to list rooms:", error);
			videoConnection.rooms = [];
			return [];
		} finally {
			videoConnection.roomsLoading = false;
		}
	},

	async createRoom(workspaceId: string, roomId?: string): Promise<string | null> {
		try {
			const client = new video.VideoClientCore(settings.transportServerUrl);
			const result = await client.createRoom(workspaceId, roomId);
			if (result) {
				// Refresh room list
				await this.listRooms(workspaceId);
				return result.roomId;
			}
			return null;
		} catch (error) {
			console.error("Failed to create room:", error);
			return null;
		}
	},

	async deleteRoom(workspaceId: string, roomId: string): Promise<boolean> {
		try {
			const client = new video.VideoClientCore(settings.transportServerUrl);
			await client.deleteRoom(workspaceId, roomId);
			// Refresh room list
			await this.listRooms(workspaceId);
			return true;
		} catch (error) {
			console.error("Failed to delete room:", error);
			return false;
		}
	},

	// Producer actions (simplified - only remote/local camera)
	async connectProducer(
		workspaceId: string
	): Promise<{ success: boolean; error?: string; roomId?: string }> {
		try {
			const producer = new video.VideoProducer(settings.transportServerUrl);

			// Create or join room
			const roomData = await producer.createRoom(workspaceId);
			const connected = await producer.connect(roomData.workspaceId, roomData.roomId);

			if (!connected) {
				throw new Error("Failed to connect producer");
			}

			// Start camera stream
			const stream = await producer.startCamera({
				video: { width: 1280, height: 720 },
				audio: true
			});

			// Update state
			videoConnection.producer.connected = true;
			videoConnection.producer.client = producer;
			videoConnection.producer.roomId = roomData.roomId;
			videoConnection.producer.stream = stream;

			// Refresh room list
			await this.listRooms(workspaceId);

			return { success: true, roomId: roomData.roomId };
		} catch (error) {
			console.error("Failed to connect producer:", error);
			return { success: false, error: error instanceof Error ? error.message : String(error) };
		}
	},

	async disconnectProducer(): Promise<void> {
		if (videoConnection.producer.client) {
			videoConnection.producer.client.disconnect();
		}
		if (videoConnection.producer.stream) {
			videoConnection.producer.stream.getTracks().forEach((track) => track.stop());
		}

		// Reset state
		videoConnection.producer.connected = false;
		videoConnection.producer.client = null;
		videoConnection.producer.roomId = null;
		videoConnection.producer.stream = null;
	},

	// Consumer actions (simplified - only remote consumer)
	async connectConsumer(
		workspaceId: string,
		roomId: string
	): Promise<{ success: boolean; error?: string }> {
		try {
			const consumer = new video.VideoConsumer(settings.transportServerUrl);
			const connected = await consumer.connect(workspaceId, roomId);

			if (!connected) {
				throw new Error("Failed to connect consumer");
			}

			// Start receiving video
			await consumer.startReceiving();

			// Set up stream receiving
			consumer.on("streamReceived", (stream: MediaStream) => {
				videoConnection.consumer.stream = stream;
			});

			// Update state
			videoConnection.consumer.connected = true;
			videoConnection.consumer.client = consumer;
			videoConnection.consumer.roomId = roomId;

			return { success: true };
		} catch (error) {
			console.error("Failed to connect consumer:", error);
			return { success: false, error: error instanceof Error ? error.message : String(error) };
		}
	},

	async disconnectConsumer(): Promise<void> {
		if (videoConnection.consumer.client) {
			videoConnection.consumer.client.disconnect();
		}

		// Reset state
		videoConnection.consumer.connected = false;
		videoConnection.consumer.client = null;
		videoConnection.consumer.roomId = null;
		videoConnection.consumer.stream = null;
	},

	// Utility functions
	async refreshRooms(workspaceId: string): Promise<void> {
		await this.listRooms(workspaceId);
	},

	getAvailableRooms(): videoTypes.RoomInfo[] {
		return videoConnection.rooms.filter((room) => room.participants.producer !== null);
	},

	getRoomById(roomId: string): videoTypes.RoomInfo | undefined {
		return videoConnection.rooms.find((room) => room.id === roomId);
	}
};