Add IPTV (.ts) stream support and tests
Introduced detection and support for MPEG-TS (.ts) IPTV streams in videoProtocol, updated documentation and examples to reflect IPTV support, and added comprehensive tests for protocol detection. Mock implementations for flv.js and hls.js were added for testing, and vitest config now aliases these libraries to their mocks.
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
// Mock for flv.js library used in tests
|
||||
export default {
|
||||
isSupported: () => true,
|
||||
getFeatureList: () => ({
|
||||
mseSupported: true,
|
||||
networkStreamIOSupported: true,
|
||||
httpsSupported: true,
|
||||
}),
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Mock for hls.js library used in tests
|
||||
export default class Hls {
|
||||
static isSupported() {
|
||||
return true
|
||||
}
|
||||
|
||||
static get Events() {
|
||||
return {
|
||||
MEDIA_ATTACHED: 'hlsMediaAttached',
|
||||
MANIFEST_PARSED: 'hlsManifestParsed',
|
||||
ERROR: 'hlsError',
|
||||
}
|
||||
}
|
||||
|
||||
static get ErrorTypes() {
|
||||
return {
|
||||
NETWORK_ERROR: 'networkError',
|
||||
MEDIA_ERROR: 'mediaError',
|
||||
OTHER_ERROR: 'otherError',
|
||||
}
|
||||
}
|
||||
|
||||
static get ErrorDetails() {
|
||||
return {
|
||||
MANIFEST_LOAD_ERROR: 'manifestLoadError',
|
||||
LEVEL_LOAD_ERROR: 'levelLoadError',
|
||||
FRAG_LOAD_ERROR: 'fragLoadError',
|
||||
}
|
||||
}
|
||||
|
||||
loadSource(_src: string) {}
|
||||
attachMedia(_video: HTMLVideoElement) {}
|
||||
destroy() {}
|
||||
on(_event: string, _handler: Function) {}
|
||||
off(_event: string, _handler: Function) {}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { detectVideoProtocol, isHlsStream, isRtmpStream, isLiveStream } from './videoProtocol'
|
||||
|
||||
describe('videoProtocol', () => {
|
||||
describe('detectVideoProtocol', () => {
|
||||
it('should detect MPEG-TS IPTV streams', () => {
|
||||
const result = detectVideoProtocol('http://favoritv65.xyz:8080/live/Apollon45/HpjWrDa6gWWd/98925.ts')
|
||||
expect(result.protocol).toBe('hls')
|
||||
expect(result.isLive).toBe(true)
|
||||
expect(result.needsSpecialPlayer).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect .ts files with query parameters', () => {
|
||||
const result = detectVideoProtocol('http://example.com/stream/video.ts?token=abc123')
|
||||
expect(result.protocol).toBe('hls')
|
||||
expect(result.isLive).toBe(true)
|
||||
expect(result.needsSpecialPlayer).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect HLS streams', () => {
|
||||
const result = detectVideoProtocol('http://example.com/stream/playlist.m3u8')
|
||||
expect(result.protocol).toBe('hls')
|
||||
expect(result.needsSpecialPlayer).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect live HLS streams', () => {
|
||||
const result = detectVideoProtocol('http://example.com/live/stream/playlist.m3u8')
|
||||
expect(result.protocol).toBe('hls')
|
||||
expect(result.isLive).toBe(true)
|
||||
expect(result.needsSpecialPlayer).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect RTMP streams', () => {
|
||||
const result = detectVideoProtocol('rtmp://example.com/live/stream')
|
||||
expect(result.protocol).toBe('rtmp')
|
||||
expect(result.isLive).toBe(true)
|
||||
expect(result.needsSpecialPlayer).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect FLV streams', () => {
|
||||
const result = detectVideoProtocol('http://example.com/stream.flv')
|
||||
expect(result.protocol).toBe('rtmp')
|
||||
expect(result.needsSpecialPlayer).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect DASH streams', () => {
|
||||
const result = detectVideoProtocol('http://example.com/stream.mpd')
|
||||
expect(result.protocol).toBe('dash')
|
||||
expect(result.needsSpecialPlayer).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect native video formats', () => {
|
||||
const result = detectVideoProtocol('http://example.com/video.mp4')
|
||||
expect(result.protocol).toBe('native')
|
||||
expect(result.isLive).toBe(false)
|
||||
expect(result.needsSpecialPlayer).toBe(false)
|
||||
})
|
||||
|
||||
it('should handle empty string', () => {
|
||||
const result = detectVideoProtocol('')
|
||||
expect(result.protocol).toBe('native')
|
||||
expect(result.isLive).toBe(false)
|
||||
expect(result.needsSpecialPlayer).toBe(false)
|
||||
})
|
||||
|
||||
it('should not confuse TypeScript files with transport streams', () => {
|
||||
// .ts in path but not as extension shouldn't be detected
|
||||
const result = detectVideoProtocol('http://example.com/ts/video.mp4')
|
||||
expect(result.protocol).toBe('native')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isHlsStream', () => {
|
||||
it('should return true for HLS streams', () => {
|
||||
expect(isHlsStream('http://example.com/stream.m3u8')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return true for IPTV .ts streams', () => {
|
||||
expect(isHlsStream('http://favoritv65.xyz:8080/live/user/pass/98925.ts')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for non-HLS streams', () => {
|
||||
expect(isHlsStream('http://example.com/video.mp4')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isRtmpStream', () => {
|
||||
it('should return true for RTMP streams', () => {
|
||||
expect(isRtmpStream('rtmp://example.com/live/stream')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return true for FLV streams', () => {
|
||||
expect(isRtmpStream('http://example.com/stream.flv')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for non-RTMP streams', () => {
|
||||
expect(isRtmpStream('http://example.com/video.mp4')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isLiveStream', () => {
|
||||
it('should return true for IPTV streams', () => {
|
||||
expect(isLiveStream('http://favoritv65.xyz:8080/live/user/pass/98925.ts')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return true for live HLS streams', () => {
|
||||
expect(isLiveStream('http://example.com/live/stream.m3u8')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return true for RTMP streams', () => {
|
||||
expect(isLiveStream('rtmp://example.com/live/stream')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for MP4 files', () => {
|
||||
expect(isLiveStream('http://example.com/video.mp4')).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -71,6 +71,16 @@ export const detectVideoProtocol = (src: string): ProtocolDetectionResult => {
|
||||
}
|
||||
}
|
||||
|
||||
// MPEG-TS (IPTV) detection
|
||||
// Check for .ts extension (Transport Stream used in IPTV)
|
||||
if (lowerSrc.includes('.ts') || lowerSrc.match(/\.ts(\?|$)/)) {
|
||||
return {
|
||||
protocol: 'hls', // HLS player can handle MPEG-TS streams
|
||||
isLive: true, // IPTV streams are typically live
|
||||
needsSpecialPlayer: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Native HTML5 video formats (MP4, WebM, OGG, etc.)
|
||||
return {
|
||||
protocol: 'native',
|
||||
|
||||
Reference in New Issue
Block a user