/** * Composable para gestionar streams de video desde go2rtc * Usa proxy backend para evitar problemas de CORS/cookies entre subdominios * API Proxy: /api/streams/list * Streaming: https://streams.nucleoriofrio.com (iframe con sesion propia) */ export type StreamType = 'webrtc' | 'mse' | 'mp4' | 'hls' | 'mjpeg' export interface StreamTypeOption { label: string value: StreamType description: string useIframe: boolean } export const STREAM_TYPES: StreamTypeOption[] = [ { label: 'WebRTC', value: 'webrtc', description: 'Menor latencia', useIframe: true }, { label: 'MSE', value: 'mse', description: 'Media Source Extensions', useIframe: true }, { label: 'MP4', value: 'mp4', description: 'Streaming MP4', useIframe: false }, { label: 'HLS', value: 'hls', description: 'HTTP Live Streaming', useIframe: false }, { label: 'MJPEG', value: 'mjpeg', description: 'Motion JPEG', useIframe: false } ] export const useStreams = () => { const BASE_URL = 'https://streams.nucleoriofrio.com' // Estado reactivo const streams = useState('streams_list', () => []) const selectedStream = useState('streams_selected', () => null) const selectedType = useState('streams_type', () => 'mse') const isLoading = useState('streams_loading', () => false) const error = useState('streams_error', () => null) /** * Obtiene la lista de streams disponibles via proxy backend */ const fetchStreams = async (): Promise => { isLoading.value = true error.value = null try { // Usar proxy backend para evitar CORS/cookies issues const response = await $fetch>('/api/streams/list') // Extraer nombres de streams del objeto streams.value = Object.keys(response).sort() // Seleccionar el primero si no hay ninguno seleccionado if (streams.value.length > 0 && !selectedStream.value) { selectedStream.value = streams.value[0] } } catch (err: unknown) { const errorMessage = (err as Error)?.message || 'Error al cargar streams' error.value = errorMessage console.error('[Streams] Error fetching streams:', err) } finally { isLoading.value = false } } /** * Genera la URL del stream segun el tipo seleccionado */ const getStreamUrl = computed((): string | null => { if (!selectedStream.value) return null const streamName = encodeURIComponent(selectedStream.value) switch (selectedType.value) { case 'webrtc': return `${BASE_URL}/stream.html?src=${streamName}` case 'mse': return `${BASE_URL}/stream.html?src=${streamName}&mode=mse` case 'mp4': return `${BASE_URL}/api/stream.mp4?src=${streamName}` case 'hls': return `${BASE_URL}/api/stream.m3u8?src=${streamName}` case 'mjpeg': return `${BASE_URL}/api/stream.mjpeg?src=${streamName}` default: return null } }) /** * Determina si se debe usar iframe o video nativo */ const useIframe = computed((): boolean => { const typeConfig = STREAM_TYPES.find(t => t.value === selectedType.value) return typeConfig?.useIframe ?? true }) /** * Obtiene las opciones para el dropdown de tipos */ const streamTypeOptions = computed(() => { return STREAM_TYPES.map(type => ({ label: type.label, value: type.value })) }) /** * Obtiene las opciones para el dropdown de streams */ const streamOptions = computed(() => { return streams.value.map(name => ({ label: name.replace(/_/g, ' '), value: name })) }) /** * Limpia el error */ const clearError = () => { error.value = null } return { // Estado streams: readonly(streams), selectedStream, selectedType, isLoading: readonly(isLoading), error: readonly(error), // Computed getStreamUrl, useIframe, streamTypeOptions, streamOptions, // Metodos fetchStreams, clearError, // Constantes STREAM_TYPES } }