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:
hibna
2025-11-04 05:24:21 +03:00
parent e3b2d396e1
commit becc9efc7f
9 changed files with 204 additions and 4 deletions
+5 -1
View File
@@ -7,7 +7,11 @@
"Bash(python check_srt.py:*)", "Bash(python check_srt.py:*)",
"Bash(python fix_srt.py:*)", "Bash(python fix_srt.py:*)",
"Bash(python:*)", "Bash(python:*)",
"Bash(pnpm run build:lib:*)" "Bash(pnpm run build:lib:*)",
"Bash(npm run build:lib:*)",
"Bash(npm publish)",
"Bash(git push:*)",
"Bash(npm test:*)"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []
+8 -1
View File
@@ -501,13 +501,14 @@ https://cdn.jsdelivr.net/npm/hls.js@1.5.13
- Media hataları → HLS instance yeniden başlatma - Media hataları → HLS instance yeniden başlatma
- Fatal hataları → Error state'e düşme - Fatal hataları → Error state'e düşme
#### 3. RTMP/FLV Streaming #### 3. RTMP/FLV/IPTV Streaming
**Desteklenen Protokoller:** **Desteklenen Protokoller:**
- RTMP (rtmp://) - RTMP (rtmp://)
- RTMPS (rtmps://) - RTMPS (rtmps://)
- RTMPT (rtmpt://) - RTMPT (rtmpt://)
- HTTP-FLV (.flv veya flv? query) - HTTP-FLV (.flv veya flv? query)
- MPEG-TS (.ts) - IPTV streams
**Kütüphane:** flv.js (opsiyonel, lazy-loaded) **Kütüphane:** flv.js (opsiyonel, lazy-loaded)
@@ -537,6 +538,12 @@ https://cdn.jsdelivr.net/npm/flv.js@1.6.2
// RTMP (HTTP-FLV proxy gerektirir) // RTMP (HTTP-FLV proxy gerektirir)
<VideoPlayer src="rtmp://example.com/live/stream" /> <VideoPlayer src="rtmp://example.com/live/stream" />
// IPTV (MPEG-TS)
<VideoPlayer
src="http://favoritv65.xyz:8080/live/username/password/98925.ts"
poster="http://example.com/channel-logo.png"
/>
``` ```
**HTTP-FLV Proxy Örneği (Node.js):** **HTTP-FLV Proxy Örneği (Node.js):**
+11 -1
View File
@@ -73,6 +73,7 @@ A feature-rich, modern video player library built with React, TypeScript, and Vi
### 🚀 Advanced Features ### 🚀 Advanced Features
- **HLS Streaming** - Automatic HLS.js integration for .m3u8 files - **HLS Streaming** - Automatic HLS.js integration for .m3u8 files
- **IPTV Support** - MPEG-TS (.ts) streams for IPTV services
- **HTTP Range Request** - Progressive download for large MP4 files - **HTTP Range Request** - Progressive download for large MP4 files
- **Subtitles** - WebVTT and SRT support - **Subtitles** - WebVTT and SRT support
- **Multiple Audio Tracks** - Switch between different audio streams - **Multiple Audio Tracks** - Switch between different audio streams
@@ -146,6 +147,15 @@ function App() {
/> />
``` ```
### IPTV Streaming
```tsx
<VideoPlayer
src="http://server.com:8080/live/username/password/12345.ts"
poster="http://example.com/channel-logo.png"
/>
```
### Custom Theme ### Custom Theme
```tsx ```tsx
@@ -259,7 +269,7 @@ video-player/
| Prop | Type | Default | Description | | Prop | Type | Default | Description |
|------|------|---------|-------------| |------|------|---------|-------------|
| `src` | `string` | **required** | Video source URL (MP4, WebM, HLS) | | `src` | `string` | **required** | Video source URL (MP4, WebM, HLS, IPTV .ts) |
| `poster` | `string` | - | Poster image URL | | `poster` | `string` | - | Poster image URL |
| `autoplay` | `boolean` | `false` | Auto-play video on load | | `autoplay` | `boolean` | `false` | Auto-play video on load |
| `loop` | `boolean` | `false` | Loop video playback | | `loop` | `boolean` | `false` | Loop video playback |
+1 -1
View File
@@ -58,7 +58,7 @@ function App() {
<div className="url-input"> <div className="url-input">
<input <input
type="text" type="text"
placeholder="Enter video URL (MP4, HLS)" placeholder="Enter video URL (MP4, HLS, IPTV .ts)"
value={videoUrl} value={videoUrl}
onChange={(e) => setVideoUrl(e.target.value)} onChange={(e) => setVideoUrl(e.target.value)}
disabled={useDemo} disabled={useDemo}
+9
View File
@@ -0,0 +1,9 @@
// Mock for flv.js library used in tests
export default {
isSupported: () => true,
getFeatureList: () => ({
mseSupported: true,
networkStreamIOSupported: true,
httpsSupported: true,
}),
}
+36
View File
@@ -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) {}
}
+118
View File
@@ -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)
})
})
})
+10
View File
@@ -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.) // Native HTML5 video formats (MP4, WebM, OGG, etc.)
return { return {
protocol: 'native', protocol: 'native',
+6
View File
@@ -20,4 +20,10 @@ export default defineConfig({
], ],
}, },
}, },
resolve: {
alias: {
'flv.js': new URL('./src/test/mocks/flv.mock.ts', import.meta.url).pathname,
'hls.js': new URL('./src/test/mocks/hls.mock.ts', import.meta.url).pathname,
},
},
}); });