diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index 7b90019..0ec9849 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -7,7 +7,11 @@
"Bash(python check_srt.py:*)",
"Bash(python fix_srt.py:*)",
"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": [],
"ask": []
diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
index 39cc0a2..5ae8320 100644
--- a/DOCUMENTATION.md
+++ b/DOCUMENTATION.md
@@ -501,13 +501,14 @@ https://cdn.jsdelivr.net/npm/hls.js@1.5.13
- Media hataları → HLS instance yeniden başlatma
- Fatal hataları → Error state'e düşme
-#### 3. RTMP/FLV Streaming
+#### 3. RTMP/FLV/IPTV Streaming
**Desteklenen Protokoller:**
- RTMP (rtmp://)
- RTMPS (rtmps://)
- RTMPT (rtmpt://)
- HTTP-FLV (.flv veya flv? query)
+- MPEG-TS (.ts) - IPTV streams
**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)
+
+// IPTV (MPEG-TS)
+
```
**HTTP-FLV Proxy Örneği (Node.js):**
diff --git a/README.md b/README.md
index b43beac..25bee64 100644
--- a/README.md
+++ b/README.md
@@ -73,6 +73,7 @@ A feature-rich, modern video player library built with React, TypeScript, and Vi
### 🚀 Advanced Features
- **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
- **Subtitles** - WebVTT and SRT support
- **Multiple Audio Tracks** - Switch between different audio streams
@@ -146,6 +147,15 @@ function App() {
/>
```
+### IPTV Streaming
+
+```tsx
+
+```
+
### Custom Theme
```tsx
@@ -259,7 +269,7 @@ video-player/
| 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 |
| `autoplay` | `boolean` | `false` | Auto-play video on load |
| `loop` | `boolean` | `false` | Loop video playback |
diff --git a/examples/App.tsx b/examples/App.tsx
index c73f389..ddf542e 100644
--- a/examples/App.tsx
+++ b/examples/App.tsx
@@ -58,7 +58,7 @@ function App() {
setVideoUrl(e.target.value)}
disabled={useDemo}
diff --git a/src/test/mocks/flv.mock.ts b/src/test/mocks/flv.mock.ts
new file mode 100644
index 0000000..64aa925
--- /dev/null
+++ b/src/test/mocks/flv.mock.ts
@@ -0,0 +1,9 @@
+// Mock for flv.js library used in tests
+export default {
+ isSupported: () => true,
+ getFeatureList: () => ({
+ mseSupported: true,
+ networkStreamIOSupported: true,
+ httpsSupported: true,
+ }),
+}
diff --git a/src/test/mocks/hls.mock.ts b/src/test/mocks/hls.mock.ts
new file mode 100644
index 0000000..388b035
--- /dev/null
+++ b/src/test/mocks/hls.mock.ts
@@ -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) {}
+}
diff --git a/src/utils/videoProtocol.test.ts b/src/utils/videoProtocol.test.ts
new file mode 100644
index 0000000..4124761
--- /dev/null
+++ b/src/utils/videoProtocol.test.ts
@@ -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)
+ })
+ })
+})
diff --git a/src/utils/videoProtocol.ts b/src/utils/videoProtocol.ts
index 7977f71..4353af1 100644
--- a/src/utils/videoProtocol.ts
+++ b/src/utils/videoProtocol.ts
@@ -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',
diff --git a/vitest.config.ts b/vitest.config.ts
index 373e6fc..ed11c6d 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -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,
+ },
+ },
});