- Nuevo composable useStreams.ts para gestionar streams de go2rtc - Componente StreamPlayer.vue para reproduccion (iframe/video/img) - Componente StreamViewer.vue con dropdowns de seleccion - Integrado en app.vue despues del card de grabacion - Soporta WebRTC, MSE, MP4, HLS y MJPEG
167 lines
3.9 KiB
TypeScript
167 lines
3.9 KiB
TypeScript
/**
|
|
* Composable para gestionar streams de video desde go2rtc
|
|
* API: https://streams.nucleoriofrio.com/api/streams
|
|
*/
|
|
|
|
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<string[]>('streams_list', () => [])
|
|
const selectedStream = useState<string | null>('streams_selected', () => null)
|
|
const selectedType = useState<StreamType>('streams_type', () => 'mse')
|
|
const isLoading = useState<boolean>('streams_loading', () => false)
|
|
const error = useState<string | null>('streams_error', () => null)
|
|
|
|
/**
|
|
* Obtiene la lista de streams disponibles desde la API
|
|
*/
|
|
const fetchStreams = async (): Promise<void> => {
|
|
isLoading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
const response = await $fetch<Record<string, unknown>>(`${BASE_URL}/api/streams`, {
|
|
credentials: 'include'
|
|
})
|
|
|
|
// 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
|
|
}
|
|
}
|