diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 788b25d..f5439e0 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -1,2669 +1,699 @@ -# 🎬 @source/player - Tam Dökümantasyon +# @source/player Documentation -**Modern, zengin özellikli ve hafif React video oynatıcı kütüphanesi** +This document reflects the current codebase in this repository (`version 3.1.0`) and replaces older, drifted documentation. -[](https://www.npmjs.com/package/@source/player) -[](https://bundlephobia.com/package/@source/player) -[](https://www.typescriptlang.org/) -[](https://opensource.org/licenses/MIT) +## Table of contents ---- +1. [Overview](#1-overview) +2. [Architecture](#2-architecture) +3. [Installation and setup](#3-installation-and-setup) +4. [Quick usage](#4-quick-usage) +5. [Streaming and protocol handling](#5-streaming-and-protocol-handling) +6. [Subtitles and subtitle style editor](#6-subtitles-and-subtitle-style-editor) +7. [Customization and modular composition](#7-customization-and-modular-composition) +8. [Keyboard and touch interaction](#8-keyboard-and-touch-interaction) +9. [Internationalization](#9-internationalization) +10. [API reference](#10-api-reference) +11. [Public exports](#11-public-exports) +12. [Error handling and reliability](#12-error-handling-and-reliability) +13. [Browser behavior and known limitations](#13-browser-behavior-and-known-limitations) +14. [Testing and development](#14-testing-and-development) -## 📑 İçindekiler +## 1. Overview -- [Genel Bakış](#-genel-bakış) -- [Kurulum](#-kurulum) - - [NPM/PNPM ile Kurulum](#1-npmpnpm-ile-kurulum) - - [.npmrc Yapılandırması](#2-npmrc-yapılandırması) - - [Kütüphaneyi Yükleme](#3-kütüphaneyi-yükleme) -- [Hızlı Başlangıç](#-hızlı-başlangıç) -- [Mimari ve Yapı](#-mimari-ve-yapı) -- [Özellikler](#-özellikler) - - [Video Format Desteği](#video-format-desteği) - - [Altyazı Sistemi](#altyazı-sistemi) - - [Ses Parça Yönetimi](#ses-parça-yönetimi) - - [Kalite Seviyesi Kontrolü](#kalite-seviyesi-kontrolü) - - [Klavye Kısayolları](#klavye-kısayolları) - - [Dokunmatik Jestler](#dokunmatik-jestler) -- [API Referansı](#-api-referansı) - - [VideoPlayer Props](#videoplayer-props) - - [Exported Components](#exported-components) - - [Exported Hooks](#exported-hooks) - - [Exported Utilities](#exported-utilities) - - [Type Definitions](#type-definitions) -- [Kullanım Örnekleri](#-kullanım-örnekleri) -- [Gelişmiş Kullanım](#-gelişmiş-kullanım) -- [State Yönetimi](#-state-yönetimi) -- [Tema ve Özelleştirme](#-tema-ve-özelleştirme) -- [Uluslararasılaştırma (i18n)](#-uluslararasılaştırma-i18n) -- [Performans](#-performans) -- [Tarayıcı Uyumluluğu](#-tarayıcı-uyumluluğu) -- [Hata Yönetimi](#-hata-yönetimi) -- [Geliştirme](#-geliştirme) -- [Katkıda Bulunma](#-katkıda-bulunma) -- [Lisans](#-lisans) +`@source/player` is a React video player library with: ---- +- Protocol-aware playback (`native`, `hls`, `rtmp/flv`, `mpegts`) +- Built-in controls with settings menus (speed, quality, subtitles, subtitle styling, audio tracks) +- Modular extension points for custom controls and overlays +- Strong TypeScript API (props, handle types, context types, utility exports) +- Runtime loading strategy for optional stream engines (`hls.js`, `flv.js`, `mpegts.js`) -## 🌟 Genel Bakış +## 2. Architecture -`@source/player`, React uygulamaları için özel olarak tasarlanmış, modern bir video oynatıcı kütüphanesidir. HLS streaming, RTMP/FLV desteği, çoklu altyazı ve ses parçaları, kalite değiştirme ve daha fazlası gibi gelişmiş özellikleri içerir. +The player is split into focused modules. -### Neden @source/player? +### 2.1 High-level component graph -| Özellik | @source/player | video.js | react-player | plyr | -|---------|---------------------|----------|--------------|------| -| **Paket Boyutu (gzipped)** | **~15KB** ✅ | ~500KB ❌ | ~50KB ⚠️ | ~30KB ⚠️ | -| **Runtime Bağımlılıkları** | **0** ✅ | Çok ❌ | Az ⚠️ | Az ⚠️ | -| **React (Web)** | **Evet** ✅ | Wrapper ⚠️ | **Evet** ✅ | Wrapper ⚠️ | -| **TypeScript Native** | **Evet** ✅ | Types ⚠️ | Kısmi ⚠️ | Types ⚠️ | -| **HLS Desteği** | **Evet** ✅ | Evet ✅ | Evet ✅ | Hayır ❌ | -| **RTMP/FLV Desteği** | **Evet** ✅ | Hayır ❌ | Hayır ❌ | Hayır ❌ | -| **Kalite Değiştirme** | **Evet** ✅ | Evet ✅ | Sınırlı ⚠️ | Hayır ❌ | -| **Dokunmatik Jestler** | **15+** ✅ | Sınırlı ⚠️ | Hayır ❌ | Sınırlı ⚠️ | -| **Klavye Kısayolları** | **15+** ✅ | ~8 ⚠️ | Temel ⚠️ | ~10 ⚠️ | -| **i18n Desteği** | **Evet** ✅ | Evet ✅ | Hayır ❌ | Evet ✅ | +- `VideoPlayer` +- `PlayerErrorBoundary` +- `PlayerProvider` (context + state + actions + i18n) +- `VideoElement` (media element, protocol setup, media events, subtitle rendering) +- `ControlsLayer` (controls visibility, settings menu, keyboard/touch integration) +- `SettingsMenu` (lazy-loaded, menu subviews) -### Ana Avantajlar +### 2.2 Core responsibilities -- 📦 **%97 daha küçük** - video.js'e göre sadece 15KB (500KB yerine) -- ⚡ **Çok hızlı** - Sıfır runtime bağımlılık, daha hızlı yükleme -- 🎯 **React-first** - React için özel yapılmış, wrapper değil -- 🔧 **Tam TypeScript** - Box'tan çıktığı gibi tam tip güvenliği -- 🎨 **Kolay özelleştirme** - CSS değişkenleri ile tema desteği -- 📱 **Mobil hazır** - Kapsamlı dokunmatik jest desteği -- 🌍 **Uluslararasılaştırılmış** - İngilizce ve Türkçe yerleşik i18n -- ♿ **Erişilebilir** - ARIA etiketleri ve klavye navigasyonu -- 🎬 **Çoklu format** - MP4, WebM, HLS (.m3u8), RTMP/FLV desteği -- 🔒 **Güvenli** - CORS hatası tespiti ve yardımcı hata mesajları +- `VideoPlayer` +- Normalizes props and default values +- Resolves subtitle style editor config (`enabled` + `storageKey`) +- Wraps content with `PlayerErrorBoundary` and `PlayerProvider` +- Exposes imperative API via ref (`VideoPlayerHandle`) ---- +- `PlayerProvider` +- Owns central `videoState`, `uiState`, and `settings` +- Manages subtitle style draft/commit/persist lifecycle +- Merges built-in translations with custom translations +- Exposes playback and UI actions through context -## 📦 Kurulum +- `VideoElement` +- Validates URL and detects protocol +- Initializes and tears down stream engine instances +- Subscribes to media events and forwards callbacks +- Converts `.srt` subtitles to VTT Blob URLs +- Renders subtitles using a custom overlay (native rendering disabled) -Bu kütüphane, Gitea üzerinde barındırılan özel bir npm registry'de yayınlanmaktadır. Kurulum için aşağıdaki adımları takip edin. +- `ControlsLayer` +- Handles auto-hide controls behavior +- Loads `SettingsMenu` lazily +- Enables keyboard shortcuts and touch gestures hooks +- Renders center play, spinner, progress, volume, settings, PIP, fullscreen -### 1. NPM/PNPM ile Kurulum +## 3. Installation and setup -Öncelikle projenizde npm veya pnpm kurulu olduğundan emin olun: +This package is published to a private registry. -```bash -# npm kurulu mu kontrol et -npm --version +1. Add `.npmrc`: -# pnpm kurulu mu kontrol et (opsiyonel, hızlı alternatif) -pnpm --version - -# pnpm kurulu değilse -npm install -g pnpm -``` - -### 2. .npmrc Yapılandırması - -`@source` scope'lu paketlerin Gitea registry'den çekilmesi için projenizin kök dizininde bir `.npmrc` dosyası oluşturmanız gerekir. - -#### Adım 2.1: .npmrc Dosyası Oluşturma - -Projenizin **kök dizininde** (package.json dosyasının bulunduğu yerde) `.npmrc` adlı bir dosya oluşturun: - -```bash -# Linux/Mac -touch .npmrc - -# Windows (PowerShell) -New-Item -Path .npmrc -ItemType File -``` - -#### Adım 2.2: .npmrc İçeriği - -`.npmrc` dosyasını aşağıdaki içerikle doldurun: - -```ini -# @source scope'u için private registry'yi kullan -@source:registry=https://gits.hibna.com.tr/api/packages/hibna/npm/ - -# Authentication token -# Token'ınızı almak için: https://gits.hibna.com.tr/user/settings/applications -# "Generate New Token" butonuna tıklayın ve "read:package" yetkisini seçin -//gits.hibna.com.tr/api/packages/hibna/npm/:_authToken=YOUR_TOKEN_HERE -``` - -**ÖNEMLİ:** `YOUR_TOKEN_HERE` kısmını kendi Gitea access token'ınız ile değiştirin! - -#### Adım 2.3: Gitea Access Token Alma - -1. Gitea hesabınıza giriş yapın: https://gits.hibna.com.tr -2. Sağ üst köşeden profil ikonuna tıklayın → **Settings** -3. Sol menüden **Applications** seçeneğine tıklayın -4. **Manage Access Tokens** bölümünde **Generate New Token** butonuna tıklayın -5. Token için bir isim verin (örn: "video-player-npm") -6. **Select permissions** bölümünde şu yetkileri seçin: - - `read:package` - Paketleri okuma yetkisi - - (Yayınlayacaksanız) `write:package` - Paket yayınlama yetkisi -7. **Generate Token** butonuna tıklayın -8. Oluşturulan token'ı kopyalayın (bu token sadece bir kez gösterilir!) -9. Token'ı `.npmrc` dosyasındaki `YOUR_TOKEN_HERE` yerine yapıştırın - -#### Adım 2.4: .npmrc'yi .gitignore'a Ekleyin - -**ÇOK ÖNEMLİ:** Token'ınızı Git'e commit etmeyin! `.npmrc` dosyasını `.gitignore`'a ekleyin: - -```bash -# .gitignore dosyasına ekleyin -echo ".npmrc" >> .gitignore -``` - -`.gitignore` dosyanız şöyle görünmeli: - -``` -node_modules/ -dist/ -.npmrc # ← Bu satırı ekleyin -.env -``` - -#### Alternatif: Environment Variable Kullanımı (Önerilen - Daha Güvenli) - -Daha güvenli bir yöntem için token'ı environment variable olarak saklayabilirsiniz: - -**.npmrc dosyası:** ```ini @source:registry=https://gits.hibna.com.tr/api/packages/hibna/npm/ //gits.hibna.com.tr/api/packages/hibna/npm/:_authToken=${GITS_NPM_TOKEN} ``` -**Environment variable ayarlama:** +2. Install package: ```bash -# Linux/Mac (.bashrc veya .zshrc dosyasına ekleyin) -export GITS_NPM_TOKEN=your-actual-token-here - -# Windows (PowerShell) -$env:GITS_NPM_TOKEN="your-actual-token-here" - -# Windows (Kalıcı - Sistem Environment Variables) -# Sistem Özellikler → Gelişmiş → Ortam Değişkenleri → Yeni -# Değişken adı: GITS_NPM_TOKEN -# Değişken değeri: your-actual-token-here -``` - -**CI/CD için (GitHub Actions, GitLab CI, vb.):** - -Repository settings → Secrets → `GITS_NPM_TOKEN` adında bir secret oluşturun. - -### 3. Kütüphaneyi Yükleme - -`.npmrc` dosyası yapılandırıldıktan sonra, kütüphaneyi yükleyebilirsiniz: - -```bash -# npm ile npm install @source/player - -# veya pnpm ile (önerilen - daha hızlı) +# or pnpm add @source/player - -# veya yarn ile +# or yarn add @source/player ``` -### 4. Peer Dependencies +3. Ensure app already includes React and ReactDOM (`>=18`). -Kütüphane, React 18+ gerektirir. Eğer projenizde yoksa: +4. Import player styles: + +```tsx +import '@source/player/styles.css' +``` + +Optional local dependencies (instead of CDN fallback): ```bash -npm install react@^18.0.0 react-dom@^18.0.0 -# veya -pnpm add react@^18.0.0 react-dom@^18.0.0 +npm install hls.js flv.js mpegts.js ``` -### 5. Optional Dependencies (Otomatik) +## 4. Quick usage -HLS ve RTMP/FLV desteği için gerekli kütüphaneler, ihtiyaç duyulduğunda **otomatik olarak lazy-load** edilir: - -- `hls.js` - HLS streaming için (.m3u8 dosyaları) -- `flv.js` - RTMP/FLV streaming için - -Bu kütüphaneler manuel kuruluma gerek yoktur. Ancak bundle size'ı azaltmak için isterseniz kurabilirsiniz: - -```bash -npm install --save-optional hls.js flv.js -``` - -### Kurulum Doğrulama - -Kurulumun başarılı olduğunu doğrulamak için: - -```bash -npm list @source/player -# veya -pnpm list @source/player -``` - -Çıktı şöyle olmalı: -``` -your-project@1.0.0 -└─┬ @source/player@0.1.5 -``` - -### Sorun Giderme - -**Problem: "401 Unauthorized" hatası** -- `.npmrc` dosyasındaki token'ın doğru olduğundan emin olun -- Token'ın `read:package` yetkisine sahip olduğundan emin olun -- Token'ın süresi dolmamış olduğundan emin olun - -**Problem: "404 Not Found" hatası** -- Registry URL'inin doğru olduğundan emin olun -- `@source:registry=https://gits.hibna.com.tr/api/packages/hibna/npm/` -- İnternet bağlantınızı kontrol edin -- Gitea sunucusunun erişilebilir olduğundan emin olun - -**Problem: "Cannot find module '@source/player'"** -- `node_modules` klasörünü silin ve yeniden yükleyin: - ```bash - rm -rf node_modules package-lock.json - npm install - ``` - ---- - -## 🚀 Hızlı Başlangıç - -### Temel Kullanım +### 4.1 Minimal usage ```tsx import { VideoPlayer } from '@source/player' import '@source/player/styles.css' -function App() { - return ( - - ) +export function App() { + return } ``` -### HLS Streaming ile Kullanım - -```tsx - -``` - -### Event Handler'lar ile Kullanım +### 4.2 With subtitles + editor ```tsx console.log('Video oynatılmaya başladı')} - onPause={() => console.log('Video durakladı')} - onEnded={() => console.log('Video bitti')} - onTimeUpdate={(time) => console.log('Geçerli zaman:', time)} - onError={(error) => console.error('Video hatası:', error)} + subtitles={[ + { src: '/subs/en.vtt', lang: 'en', label: 'English', default: true }, + { src: '/subs/tr.srt', lang: 'tr', label: 'Turkish' }, + ]} + subtitleStyleEditor={{ enabled: true, storageKey: 'player-subtitle-style' }} /> ``` -### Tema Özelleştirmesi +### 4.3 With theme and modular controls ```tsx -``` - ---- - -## 🏗️ Mimari ve Yapı - -### Proje Yapısı - -``` -video-player/ -├── src/ -│ ├── components/ # React UI bileşenleri -│ │ ├── controls/ # Kontrol butonları (Play, Pause, vb.) -│ │ │ ├── PlayPauseButton.tsx -│ │ │ ├── ProgressBar.tsx -│ │ │ ├── VolumeControl.tsx -│ │ │ ├── TimeDisplay.tsx -│ │ │ ├── FullscreenButton.tsx -│ │ │ ├── PIPButton.tsx -│ │ │ ├── SettingsButton.tsx -│ │ │ └── CenterPlayButton.tsx -│ │ ├── menus/ # Ayar menüleri -│ │ │ └── SettingsMenu.tsx -│ │ ├── overlays/ # Overlay bileşenleri -│ │ │ └── LoadingSpinner.tsx -│ │ ├── VideoPlayer.tsx # Ana wrapper bileşen -│ │ ├── VideoElement.tsx # Video element handler -│ │ └── ControlsLayer.tsx # Kontrol katmanı -│ ├── contexts/ # React Context -│ │ └── PlayerContext.tsx -│ ├── hooks/ # Custom React hooks -│ │ ├── useKeyboardShortcuts.ts -│ │ └── useTouchGestures.ts -│ ├── utils/ # Utility fonksiyonlar -│ │ ├── hlsSetup.ts # HLS.js setup -│ │ ├── hlsLoader.ts # HLS.js lazy loading -│ │ ├── hlsControl.ts # HLS kalite/ses kontrolü -│ │ ├── rtmpSetup.ts # FLV.js setup -│ │ ├── rtmpLoader.ts # FLV.js lazy loading -│ │ ├── videoProtocol.ts # Protokol tespiti -│ │ ├── m3u8Parser.ts # M3U8 manifest parsing -│ │ ├── subtitles.ts # SRT/VTT altyazı işleme -│ │ ├── corsHelper.ts # CORS doğrulama -│ │ ├── time.ts # Zaman formatlama -│ │ └── polyfills.ts # Tarayıcı uyumluluk -│ ├── icons/ # SVG icon bileşenleri -│ │ └── index.tsx -│ ├── styles/ # CSS değişkenleri -│ │ └── variables.css -│ ├── i18n/ # Uluslararasılaştırma -│ │ └── index.ts # İngilizce & Türkçe -│ ├── types/ # TypeScript tanımları -│ │ └── index.ts -│ └── index.ts # Ana export dosyası -├── examples/ # Demo uygulaması -│ ├── App.tsx -│ └── main.tsx -├── dist/ # Build çıktısı -├── package.json -├── vite.config.ts # Geliştirme config -├── vite.config.lib.ts # Kütüphane build config -└── tsconfig.json -``` - -### Bileşen Hiyerarşisi - -``` -VideoPlayer (Main) -├── PlayerProvider (Context) -│ ├── VideoElement (Video handler) -│ │ └── element -│ ├── ControlsLayer (Controls container) -│ │ ├── CenterPlayButton (Overlay) -│ │ ├── PlayPauseButton -│ │ ├── ProgressBar -│ │ ├── VolumeControl -│ │ ├── TimeDisplay -│ │ ├── SettingsButton -│ │ ├── FullscreenButton -│ │ ├── PIPButton -│ │ └── SettingsMenu (Lazy-loaded) -│ │ ├── Main Menu -│ │ ├── Speed Menu -│ │ ├── Quality Menu -│ │ ├── Subtitle Menu -│ │ └── Audio Track Menu -│ └── LoadingSpinner (Overlay) -``` - -### State Management - -Kütüphane, **React Context API** kullanarak merkezi state yönetimi sağlar: - -- **VideoState**: Video durumu (playing, currentTime, duration, vb.) -- **UIState**: UI durumu (controls visibility, menu states, vb.) -- **PlayerSettings**: Player ayarları (quality, subtitle, audioTrack, vb.) - -Context yapısı, tüm child bileşenlerin state'e erişmesini ve güncellemesini sağlar. - ---- - -## ✨ Özellikler - -### Video Format Desteği - -#### 1. Native HTML5 Video (MP4, WebM, OGG) - -**Desteklenen Formatlar:** -- MP4 (H.264/H.265 codec) -- WebM (VP8/VP9 codec) -- OGG (Theora codec) - -**Nasıl Çalışır:** -- Doğrudan `` elementi ile `src` attribute kullanılır -- HTTP Range Request desteği ile progressive download -- Harici kütüphane gerektirmez -- Tüm modern tarayıcılarda native destek - -**Kullanım:** -```tsx - -``` - -#### 2. HLS Streaming (.m3u8) - -**Özellikler:** -- Adaptive bitrate streaming (ABR) -- Çoklu kalite seviyeleri -- Çoklu ses parçaları -- Altyazı parça çıkarma -- Otomatik kalite seçimi -- Live stream desteği - -**Kütüphane:** hls.js (opsiyonel, lazy-loaded) - -**Safari Desteği:** Safari'de native HLS desteği var, kütüphane yüklenmez - -**Loading Stratejisi:** -1. HLS source tespit edilir (.m3u8 uzantısı) -2. Safari ise → Native HLS kullanılır -3. Diğer tarayıcılarda: - - Önce NPM package'dan hls.js yüklenir - - Başarısız olursa CDN'den fallback (jsdelivr) - - MSE desteği kontrol edilir - -**CDN Fallback:** -``` -https://cdn.jsdelivr.net/npm/hls.js@1.5.13 -``` - -**Kullanım:** -```tsx - -``` - -**Manifest Örneği:** -```m3u8 -#EXTM3U -#EXT-X-VERSION:3 - -# Kalite seviyeleri -#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=1920x1080 -1080p.m3u8 -#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=1280x720 -720p.m3u8 -#EXT-X-STREAM-INF:BANDWIDTH=500000,RESOLUTION=854x480 -480p.m3u8 - -# Ses parçaları -#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="English",LANGUAGE="en",URI="audio_en.m3u8" -#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="Turkish",LANGUAGE="tr",URI="audio_tr.m3u8" -``` - -**Hata Yönetimi:** -- Network hataları → Otomatik yeniden bağlanma -- Media hataları → HLS instance yeniden başlatma -- Fatal hataları → Error state'e düşme - -#### 3. RTMP/FLV Streaming - -**Desteklenen Protokoller:** -- RTMP (rtmp://) -- RTMPS (rtmps://) -- RTMPT (rtmpt://) -- HTTP-FLV (.flv veya flv? query) - -**Kütüphane:** flv.js (opsiyonel, lazy-loaded) - -**Özellikler:** -- Live stream desteği -- Hata recovery -- İstatistik takibi -- Low latency - -**Loading Stratejisi:** -1. RTMP/FLV source tespit edilir -2. Önce NPM package'dan flv.js yüklenir -3. Başarısız olursa CDN'den fallback -4. MSE desteği kontrol edilir - -**CDN Fallback:** -``` -https://cdn.jsdelivr.net/npm/flv.js@1.6.2 -``` - -**ÖNEMLİ NOT:** Doğrudan RTMP URL'leri tarayıcıda çalışmaz. HTTP-FLV proxy gerektirir. - -**Kullanım:** -```tsx -// HTTP-FLV - - -// RTMP (HTTP-FLV proxy gerektirir) - -``` - -#### 4. IPTV (MPEG-TS) Streaming - -**Desteklenen Format:** -- MPEG-TS (.ts) - Direct transport stream files - -**Kütüphane:** Yok (Native HTML5 video element) - -**Özellikler:** -- Doğrudan HTTP üzerinden TS stream oynatma -- Canlı IPTV kanalları için ideal -- Ek kütüphane gerekmez - -**Tarayıcı Desteği:** -- ⚠️ **Sınırlı destek**: Tüm tarayıcılar MPEG-TS formatını desteklemez -- ✅ **Chrome/Edge**: Genellikle destekler -- ⚠️ **Firefox**: Sınırlı destek -- ⚠️ **Safari**: Sınırlı destek -- ℹ️ **Alternatif**: M3U8 playlist üzerinden IPTV kullanımı önerilir - -**Kullanım:** -```tsx -// IPTV (MPEG-TS) - Direct stream - { - console.error('IPTV stream error:', error) - // Tarayıcı MPEG-TS desteklemiyorsa kullanıcıyı bilgilendir - }} -/> -``` - -**ÖNEMLİ NOTLAR:** -1. **Tarayıcı Uyumluluğu**: Tüm tarayıcılar `.ts` dosyalarını doğrudan oynatamaz -2. **Alternatif Çözüm**: Mümkünse IPTV sağlayıcınızdan `.m3u8` (HLS) link isteyin -3. **CORS**: IPTV sunucusu CORS ayarlarını doğru yapılandırmalıdır -4. **Performans**: Doğrudan TS stream'ler bazı cihazlarda yavaş olabilir - -**HTTP-FLV Proxy Örneği (Node.js):** -```javascript -// RTMP stream'i HTTP-FLV'ye dönüştüren proxy -const ffmpeg = require('fluent-ffmpeg'); - -ffmpeg('rtmp://source.com/live/stream') - .outputOptions([ - '-c copy', - '-f flv' - ]) - .pipe(res); -``` - -#### 5. DASH Support (Planlanıyor) - -**Durum:** Protokol tespit ediliyor ancak henüz implement edilmemiş - -**.mpd URL'leri tespit edilir ve hata mesajı döner:** -``` -DASH streaming is detected but not yet supported -``` - -**Gelecek implementasyon** dash.js kütüphanesi ile planlanıyor. - -### Protocol Detection System - -**Dosya:** `src/utils/videoProtocol.ts` - -Video URL'inden otomatik protokol tespiti: - -```typescript -function detectVideoProtocol(url: string): { - protocol: VideoProtocol - isLive: boolean - needsSpecialPlayer: boolean -} -``` - -**Tespit Mantığı:** - -1. **HLS:** `.m3u8` uzantısı -2. **RTMP:** `rtmp://`, `rtmps://`, `rtmpt://`, `rtmpe://` protokolleri -3. **FLV:** `.flv` uzantısı veya URL'de `flv?` var -4. **DASH:** `.mpd` uzantısı -5. **Native:** Diğer her şey (MP4, WebM, vb.) - -**Live Stream Tespiti:** -- RTMP/FLV → Her zaman live -- HLS → Manifest'te `#EXT-X-PLAYLIST-TYPE:VOD` yoksa live -- Diğerleri → VOD - -**Kullanım:** -```typescript -import { detectVideoProtocol } from '@source/player' - -const info = detectVideoProtocol('https://example.com/stream.m3u8') -console.log(info.protocol) // 'hls' -console.log(info.isLive) // true/false -console.log(info.needsSpecialPlayer) // true -``` - -### Altyazı Sistemi - -**Dosyalar:** -- `src/utils/subtitles.ts` - Altyazı işleme -- `src/components/VideoElement.tsx` - Altyazı entegrasyonu - -#### Desteklenen Formatlar - -**1. WebVTT (.vtt):** -- Native tarayıcı desteği -- Doğrudan `` elementi ile kullanılır - -**2. SRT (.srt):** -- WebVTT'ye otomatik dönüştürme -- Blob URL ile tarayıcıya verilir - -#### Özellikler - -- ✅ Çoklu altyazı parçaları -- ✅ Dil seçimi -- ✅ Varsayılan altyazı desteği -- ✅ Yüklendiğinde otomatik etkinleştirme -- ✅ SRT → VTT dönüşümü -- ✅ HLS manifest'ten altyazı çıkarma -- ✅ Manuel ve HLS altyazı birleştirme - -#### SRT → VTT Dönüşümü - -**SRT Format:** -```srt -1 -00:00:01,000 --> 00:00:04,000 -Merhaba dünya - -2 -00:00:05,000 --> 00:00:08,000 -İkinci altyazı -``` - -**WebVTT Format (dönüştürülmüş):** -```vtt -WEBVTT - -00:00:01.000 --> 00:00:04.000 -Merhaba dünya - -00:00:05.000 --> 00:00:08.000 -İkinci altyazı -``` - -**Dönüşüm Fonksiyonu:** -```typescript -import { parseSRT, createSubtitleBlobURL } from '@source/player' - -const srtContent = '...' // SRT içeriği -const vttContent = parseSRT(srtContent) -const blobUrl = createSubtitleBlobURL(vttContent) -// blobUrl → blob:http://localhost:5173/abc123... -``` - -#### Implementation Akışı - -1. Altyazı dosyaları fetch edilir -2. SRT ise WebVTT'ye dönüştürülür -3. Blob URL'leri oluşturulur -4. `` elementleri dinamik olarak eklenir -5. Track mode Settings context üzerinden kontrol edilir -6. Component unmount'ta blob URL'leri temizlenir - -#### Kullanım - -**Temel Kullanım:** -```tsx - -``` - -**HLS ile Otomatik Altyazı (manifest'ten):** -```tsx - -// HLS manifest'te altyazılar varsa otomatik ekler -``` - -**Programmatic Control:** -```tsx -import { usePlayerContext } from '@source/player' - -function CustomSubtitleToggle() { - const { settings, setSubtitle } = usePlayerContext() - - return ( - setSubtitle(settings.subtitle ? null : mySubtitle)}> - {settings.subtitle ? 'Altyazıyı Kapat' : 'Altyazıyı Aç'} - - ) -} -``` - -### Ses Parça Yönetimi - -**Dosyalar:** -- `src/utils/hlsLoader.ts` - HLS ses parçası çıkarma -- `src/utils/hlsControl.ts` - Ses parçası kontrolü - -#### Özellikler - -- ✅ HLS manifest'ten ses parçası çıkarma -- ✅ Oynatma sırasında değiştirme -- ✅ Dil tespiti -- ✅ Varsayılan parça seçimi -- ✅ Otomatik seçim desteği - -#### HLS Manifest'ten Çıkarma - -**Manifest Örneği:** -```m3u8 -#EXTM3U -#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="English",DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="en",URI="audio_en.m3u8" -#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="Turkish",LANGUAGE="tr",URI="audio_tr.m3u8" -``` - -**Çıkarılan AudioTrack:** -```typescript -{ - name: "English", - language: "en", - url: "audio_en.m3u8", - groupId: "audio", - default: true, - autoselect: true -} -``` - -#### Kullanım - -**Programmatic Control:** -```tsx -import { usePlayerContext } from '@source/player' - -function AudioTrackSelector() { - const { settings, setAudioTrack } = usePlayerContext() - const [tracks] = useState([...]) // HLS'den alınan parçalar - - return ( - { - const track = tracks.find(t => t.name === e.target.value) - setAudioTrack(track || null) - }} - > - {tracks.map(track => ( - - {track.label} ({track.language}) - - ))} - - ) -} -``` - -### Kalite Seviyesi Kontrolü - -**Dosyalar:** -- `src/utils/hlsLoader.ts` - Kalite çıkarma -- `src/utils/hlsControl.ts` - Kalite kontrolü - -#### Özellikler - -- ✅ HLS'den çoklu kalite seviyeleri -- ✅ Manuel kalite seçimi -- ✅ Otomatik kalite (adaptive bitrate) -- ✅ Çözünürlük bazlı etiketleme (1080p, 720p, vb.) -- ✅ Bitrate bilgisi gösterimi -- ✅ Seçilen kalitenin kalıcılığı - -#### Kalite Tespiti - -**HLS Manifest:** -```m3u8 -#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080 -1080p.m3u8 -#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720 -720p.m3u8 -#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=854x480 -480p.m3u8 -``` - -**Çıkarılan VideoQuality:** -```typescript -[ - { - label: "1080p", - height: 1080, - width: 1920, - bitrate: 5000000, - levelIndex: 0, - url: "1080p.m3u8" - }, - { - label: "720p", - height: 720, - width: 1280, - bitrate: 2500000, - levelIndex: 1, - url: "720p.m3u8" - }, - // ... -] -``` - -#### Kalite Değiştirme - -**Otomatik Kalite (Adaptive Bitrate):** -```typescript -import { setHlsQualityLevel } from '@source/player' - -setHlsQualityLevel(hlsInstance, null) // Auto -``` - -**Manuel Kalite:** -```typescript -setHlsQualityLevel(hlsInstance, { - label: "1080p", - levelIndex: 0, - // ... -}) -``` - -#### Kullanım - -**Settings Menu'den:** -Kullanıcı Settings → Quality menüsünden kalite seçer. - -**Programmatic Control:** -```tsx -import { usePlayerContext } from '@source/player' - -function QualitySelector() { - const { settings, setQuality } = usePlayerContext() - const [qualities] = useState([...]) // HLS'den alınan - - return ( - { - if (e.target.value === 'auto') { - setQuality(null) // Auto - } else { - const quality = qualities.find(q => q.label === e.target.value) - setQuality(quality || null) - } - }} - > - Auto - {qualities.map(q => ( - - {q.label} ({(q.bitrate! / 1000000).toFixed(1)} Mbps) - - ))} - - ) -} -``` - -### Klavye Kısayolları - -**Dosya:** `src/hooks/useKeyboardShortcuts.ts` - -#### Tüm Kısayollar - -| Tuş | Aksiyon | Açıklama | -|-----|---------|----------| -| `Space` | Oynat/Duraklat | Videoyu oynatır veya duraklatır | -| `K` | Oynat/Duraklat | Space ile aynı | -| `←` (Sol Ok) | -5 saniye | 5 saniye geri sar | -| `→` (Sağ Ok) | +5 saniye | 5 saniye ileri sar | -| `J` | -10 saniye | 10 saniye geri sar | -| `L` | +10 saniye | 10 saniye ileri sar | -| `↑` (Yukarı Ok) | Ses +10% | Sesi %10 artır | -| `↓` (Aşağı Ok) | Ses -10% | Sesi %10 azalt | -| `M` | Sessiz/Sesli | Sesi aç/kapat | -| `F` | Tam ekran | Tam ekran moduna geç/çık | -| `P` | PIP | Picture-in-Picture aç/kapat | -| `0` | 0% | Videonun başına git | -| `1` | 10% | Videonun %10'una git | -| `2` | 20% | Videonun %20'sine git | -| `3` | 30% | Videonun %30'una git | -| `4` | 40% | Videonun %40'ına git | -| `5` | 50% | Videonun ortasına git | -| `6` | 60% | Videonun %60'ına git | -| `7` | 70% | Videonun %70'ine git | -| `8` | 80% | Videonun %80'ine git | -| `9` | 90% | Videonun %90'ına git | -| `Home` | Başa git | Videonun en başına git | -| `End` | Sona git | Videonun en sonuna git | - -#### Özellikler - -- ✅ Input/textarea alanlarında devre dışı -- ✅ Sadece aktif/focus olan player için çalışır (çoklu player uyumu) -- ✅ Default tarayıcı davranışını önler -- ✅ Enable/disable ile açılıp kapatılabilir -- ✅ Fullscreen'de de çalışır - -#### Kullanım - -**Otomatik (default):** -```tsx - -// Klavye kısayolları otomatik aktif -``` - -**Manuel kontrol:** -```tsx - -``` - -**Custom hook ile:** -```tsx -import { useKeyboardShortcuts } from '@source/player' - -function MyComponent() { - const { videoRef, containerRef } = usePlayerContext() - - useKeyboardShortcuts({ - videoRef, - containerRef, - enabled: true - }) - - // ... -} -``` - -### Dokunmatik Jestler - -**Dosya:** `src/hooks/useTouchGestures.ts` - -#### Tüm Jestler - -| Jest | Aksiyon | Açıklama | -|------|---------|----------| -| Tek dokunuş (tap) | Oynat/Duraklat | Videoyu oynatır/duraklatır | -| Çift dokunuş sol | -10 saniye | 10 saniye geri sar | -| Çift dokunuş sağ | +10 saniye | 10 saniye ileri sar | -| Yatay kaydırma (swipe) | Seek | Videoyu ileri/geri sar (max 30s) | -| Dikey kaydırma (swipe) | Ses | Sesi artır/azalt | - -#### Özellikler - -- ✅ Tap tespiti (timeout ile) -- ✅ Double-tap tespit (pozisyon bazlı - sol/sağ) -- ✅ Görsel geri bildirim animasyonları -- ✅ Swipe mesafesi hesaplama -- ✅ Threshold değerleri (minimum hareket mesafesi) -- ✅ Mobil ve tablet uyumlu - -#### Parametreler - -**Tap Timeout:** 300ms (çift dokunuş için bekleme süresi) -**Swipe Threshold:** 50px (minimum swipe mesafesi) -**Double Tap Zone:** Ekran genişliğinin 1/3'ü (sol/sağ bölge) - -#### Görsel Geri Bildirim - -**Double Tap Animasyonu:** -- Sol tarafa çift dokunuş → "« 10s" animasyonu -- Sağ tarafa çift dokunuş → "10s »" animasyonu -- 500ms sonra kaybolur - -**Seek Feedback:** -- Swipe sırasında progress bar güncellemesi -- Seek edilen zamanın gösterimi - -#### Kullanım - -**Otomatik (mobil cihazlarda):** -```tsx - -// Touch jestleri otomatik aktif (touch destekli cihazlarda) -``` - -**Custom hook ile:** -```tsx -import { useTouchGestures } from '@source/player' - -function MyComponent() { - const { videoRef, containerRef } = usePlayerContext() - - useTouchGestures({ - videoRef, - containerRef, - onGesture: (event) => { - console.log('Jest:', event.type, event.direction) - } - }) - - // ... -} -``` - ---- - -## 📚 API Referansı - -### VideoPlayer Props - -```typescript -interface VideoPlayerProps { - // Video kaynağı (zorunlu) - src: string - - // Poster/thumbnail resmi - poster?: string - - // Protokol override (default: auto) - protocol?: 'auto' | 'native' | 'hls' | 'rtmp' | 'dash' | 'mpegts' - - // Otomatik oynat - autoplay?: boolean - - // Döngü - loop?: boolean - - // Başlangıçta sessiz - muted?: boolean - - // Başlangıç ses seviyesi (0-1) - volume?: number - - // Oynatma hızı - playbackRate?: number - - // Başlangıç zamanı (saniye) - currentTime?: number - - // Video element attribute'ları - crossOrigin?: '' | 'anonymous' | 'use-credentials' - preload?: 'none' | 'metadata' | 'auto' - playsInline?: boolean - controlsList?: string - - // Kontrolleri göster (default: true) - controls?: boolean - - // Altyazı parçaları - subtitles?: SubtitleTrack[] - - // Tema özelleştirme - theme?: PlayerTheme - - // Dil (i18n için) - language?: string - - // Klavye kısayolları (default: true) - keyboardShortcuts?: boolean - - // Picture-in-Picture butonu (default: true) - pictureInPicture?: boolean - - // CSS class - className?: string - - // Inline styles - style?: CSSProperties - - // Event handlers - onPlay?: () => void - onPause?: () => void - onEnded?: () => void - onTimeUpdate?: (currentTime: number) => void - onVolumeChange?: (volume: number) => void - onError?: (error: Error) => void - onLoadedMetadata?: () => void - onSeeking?: () => void - onSeeked?: () => void -} -``` - -#### Prop Detayları - -**src** (zorunlu) -- Tip: `string` -- Video dosyasının URL'i -- Desteklenen formatlar: - - MP4: `https://example.com/video.mp4` - - WebM: `https://example.com/video.webm` - - HLS: `https://example.com/stream.m3u8` - - RTMP: `rtmp://example.com/live/stream` - - FLV: `https://example.com/stream.flv` - -**poster** -- Tip: `string | undefined` -- Video yüklenmeden önce gösterilecek resim -- Önerilen format: JPG, PNG, WebP -- Önerilen boyut: Video ile aynı aspect ratio - -**autoplay** -- Tip: `boolean | undefined` -- Default: `false` -- NOT: Çoğu tarayıcı, `muted: true` olmadan autoplay'e izin vermez - -**loop** -- Tip: `boolean | undefined` -- Default: `false` -- Video bittiğinde başa döner - -**muted** -- Tip: `boolean | undefined` -- Default: `false` -- Başlangıçta sessiz moda alır - -**controls** -- Tip: `boolean | undefined` -- Default: `true` -- `false` yaparsanız tüm kontroller gizlenir - -**subtitles** -- Tip: `SubtitleTrack[] | undefined` -- Altyazı parçaları dizisi -- WebVTT (.vtt) ve SRT (.srt) desteklenir - -**theme** -- Tip: `PlayerTheme | undefined` -- CSS variable'ları override eder -- Tüm alanlar opsiyonel - -**language** -- Tip: `string | undefined` -- Default: Tarayıcı dili -- Desteklenen: `'en'`, `'tr'` - -**keyboardShortcuts** -- Tip: `boolean | undefined` -- Default: `true` -- Klavye kısayollarını aktif/deaktif eder - -**pictureInPicture** -- Tip: `boolean | undefined` -- Default: `true` -- PIP butonunu gösterir/gizler -- Tarayıcı desteği yoksa otomatik gizlenir - -**onTimeUpdate** -- Tip: `(currentTime: number) => void | undefined` -- Video oynarken sürekli çağrılır (~250ms aralıklarla) -- Analytics ve progress tracking için kullanışlı - -**onError** -- Tip: `(error: Error) => void | undefined` -- Video yükleme veya oynatma hatalarında çağrılır -- CORS hataları için `isCORSError(error)` kullanabilirsiniz - -### Type Definitions - -#### SubtitleTrack - -```typescript -interface SubtitleTrack { - // Altyazı dosyasının URL'i (.vtt veya .srt) - src: string - - // Dil kodu (ISO 639-1) - lang: string - - // Kullanıcıya gösterilecek etiket - label: string - - // Varsayılan altyazı olarak işaretle - default?: boolean -} -``` - -**Örnek:** -```typescript -const subtitles: SubtitleTrack[] = [ - { - src: '/subtitles/english.vtt', - lang: 'en', - label: 'English', - default: true - }, - { - src: '/subtitles/turkish.srt', - lang: 'tr', - label: 'Türkçe' - } -] -``` - -#### AudioTrack - -```typescript -interface AudioTrack { - // Ses parçasının adı - name: string - - // Dil kodu - language: string - - // HLS manifest'teki URL - url: string - - // Group ID (HLS) - groupId: string - - // Varsayılan parça mı - default?: boolean - - // Otomatik seçilsin mi - autoselect?: boolean -} -``` - -**Örnek:** -```typescript -const audioTrack: AudioTrack = { - name: "English Stereo", - language: "en", - url: "audio_en.m3u8", - groupId: "audio", - default: true, - autoselect: true -} -``` - -#### VideoQuality - -```typescript -interface VideoQuality { - // Yükseklik (piksel) - height?: number - - // Kullanıcıya gösterilecek etiket (örn: "1080p") - label: string - - // Kalite stream URL'i (HLS) - url?: string - - // Genişlik (piksel) - width?: number - - // Bitrate (bits/second) - bitrate?: number - - // HLS.js level index - levelIndex?: number -} -``` - -**Örnek:** -```typescript -const quality: VideoQuality = { - label: "1080p", - height: 1080, - width: 1920, - bitrate: 5000000, - levelIndex: 0, - url: "1080p.m3u8" -} -``` - -#### PlayerTheme - -```typescript -interface PlayerTheme { - // Ana renk (progress bar, butonlar) - primaryColor?: string - - // Vurgu rengi (hover states) - accentColor?: string - - // Arka plan rengi - backgroundColor?: string - - // Metin rengi - textColor?: string -} -``` - -**Örnek:** -```typescript -const theme: PlayerTheme = { - primaryColor: '#ef4444', // Kırmızı - accentColor: '#dc2626', // Koyu kırmızı - backgroundColor: '#000000', // Siyah - textColor: '#ffffff' // Beyaz -} -``` - -#### VideoState - -```typescript -interface VideoState { - playing: boolean // Oynatılıyor mu - currentTime: number // Geçerli zaman (saniye) - duration: number // Toplam süre (saniye) - buffered: number // Buffered zaman (saniye) - volume: number // Ses seviyesi (0-1) - muted: boolean // Sessiz mi - playbackRate: number // Oynatma hızı (0.25-2) - fullscreen: boolean // Tam ekran mı - pictureInPicture: boolean // PIP modunda mı - loading: boolean // Yükleniyor mu - error: Error | null // Hata varsa - seeking: boolean // Seek ediliyor mu -} -``` - -#### UIState - -```typescript -interface UIState { - controlsVisible: boolean // Kontroller görünür mü - settingsOpen: boolean // Ayarlar menüsü açık mı - volumeControlOpen: boolean // Ses kontrolü açık mı - qualityMenuOpen: boolean // Kalite menüsü açık mı - subtitleMenuOpen: boolean // Altyazı menüsü açık mı -} -``` - -#### PlayerSettings - -```typescript -interface PlayerSettings { - quality: VideoQuality | null // Seçili kalite (null = auto) - subtitle: SubtitleTrack | null // Seçili altyazı (null = off) - audioTrack: AudioTrack | null // Seçili ses parçası - playbackRate: number // Oynatma hızı -} -``` - -#### PlayerContextValue - -```typescript -interface PlayerContextValue { - // State - videoState: VideoState - uiState: UIState - settings: PlayerSettings - - // Refs - videoRef: MutableRefObject - containerRef: MutableRefObject - - // Video kontrol fonksiyonları - play: () => void - pause: () => void - togglePlay: () => void - seek: (time: number) => void - setVolume: (volume: number) => void - toggleMute: () => void - setPlaybackRate: (rate: number) => void - - // Fullscreen & PIP - toggleFullscreen: () => void - togglePictureInPicture: () => void - - // UI kontrolleri - showControls: () => void - hideControls: () => void - toggleSettings: () => void - - // Ayarlar - setQuality: (quality: VideoQuality | null) => void - setSubtitle: (subtitle: SubtitleTrack | null) => void - setAudioTrack: (audioTrack: AudioTrack | null) => void -} -``` - -### Exported Components - -```typescript -// Ana bileşen -import { VideoPlayer } from '@source/player' -``` - -### Exported Hooks - -```typescript -// Player context hook -import { usePlayerContext } from '@source/player' - -// Klavye kısayolları hook'u -import { useKeyboardShortcuts } from '@source/player' - -// Dokunmatik jest hook'u -import { useTouchGestures } from '@source/player' -``` - -**usePlayerContext Kullanımı:** -```tsx -function CustomControl() { - const { - videoState, - play, - pause, - seek, - setVolume - } = usePlayerContext() - - return ( - - Oynat - Duraklat - seek(30)}>30s İleri - {videoState.currentTime}s / {videoState.duration}s - - ) -} - -// PlayerProvider içinde kullanılmalı - - + controlsLeftExtra={Bookmark} + controlsRightExtra={Share} +> + Custom overlay ``` -### Exported Utilities +## 5. Streaming and protocol handling -```typescript -// Zaman formatlama -import { formatTime, parseTime } from '@source/player' +### 5.1 Auto protocol detection -formatTime(125) // "2:05" -formatTime(3665) // "1:01:05" -parseTime("2:05") // 125 -parseTime("1:01:05") // 3665 +When `protocol="auto"` (default), `detectVideoProtocol(src)` chooses by URL pattern. -// Altyazı utilities -import { - parseSRT, - createSubtitleBlobURL, - fetchSubtitle -} from '@source/player' +| Pattern | Detected protocol | Live hint | Needs special engine | +| --- | --- | --- | --- | +| `rtmp://`, `rtmps://`, `rtmpt://`, `rtmpe://` | `rtmp` | true | true | +| contains `.m3u8` | `hls` | true if contains `/live/` or `live.m3u8` | true | +| contains `.mpd` | `dash` | true if contains `/live/` or `live.mpd` | true | +| contains `.flv` or `flv?` | `rtmp` | true if contains `/live/` or `live.flv` | true | +| `.ts` stream URL | `mpegts` | true | true | +| other URLs | `native` | false | false | -const srtContent = "1\n00:00:01,000 --> 00:00:04,000\nMerhaba" -const vttContent = parseSRT(srtContent) -const blobUrl = createSubtitleBlobURL(vttContent) -const subtitle = await fetchSubtitle('/subtitle.srt') +You can override with `protocol="native" | "hls" | "rtmp" | "dash" | "mpegts"`. -// CORS helpers -import { - validateVideoURL, - getCORSErrorMessage, - isCORSError, - checkVideoCORS -} from '@source/player' +### 5.2 HLS flow -const validation = validateVideoURL(url) +- For HLS URLs: +- Safari/native HLS path is preferred when appropriate. +- Other browsers use `hls.js`. + +Loading strategy: + +1. Dynamic import from npm (`hls.js`) +2. If unavailable, CDN fallback: `https://cdn.jsdelivr.net/npm/hls.js@1.6.13/dist/hls.min.js` + +HLS setup behavior: + +- Tracks audio tracks, quality levels, subtitle tracks +- Handles fatal errors: +- `NETWORK_ERROR` -> `startLoad()` +- `MEDIA_ERROR` -> `recoverMediaError()` +- others -> emit `onError` + +### 5.3 RTMP / FLV flow + +- Uses `flv.js` runtime loader. +- npm dynamic import first, then CDN fallback: +- `https://cdn.jsdelivr.net/npm/flv.js@1.6.2/dist/flv.min.js` + +Important note: + +- Browser playback is effectively HTTP-FLV based. +- Direct RTMP endpoints usually require server-side proxy/conversion to HTTP-FLV. + +### 5.4 MPEG-TS flow + +- Uses `mpegts.js` runtime loader. +- npm dynamic import first, then CDN fallback: +- `https://cdn.jsdelivr.net/npm/mpegts.js@1.7.3/dist/mpegts.js` + +### 5.5 DASH status + +- DASH (`.mpd`) is detected but not implemented. +- Player emits `Error('DASH streaming is not yet supported')`. + +### 5.6 Live broadcast UI behavior + +`videoState.isLiveBroadcast` is set from media duration (`Infinity` or `0`): + +- Progress bar and time display are hidden +- Live badge is shown in controls + +## 6. Subtitles and subtitle style editor + +### 6.1 Subtitle sources + +`subtitles` prop accepts manual tracks: + +```ts +type SubtitleTrack = { + src: string + lang: string + label: string + default?: boolean +} +``` + +HLS subtitle tracks are merged with manual tracks internally. + +### 6.2 SRT support + +- `.srt` tracks are fetched and converted to VTT Blob URLs. +- Blob URLs are revoked on cleanup. + +### 6.3 Custom subtitle rendering + +- Native text track rendering is disabled. +- Active cues are read from selected `TextTrack` and rendered inside `.sp-subtitle-overlay`. +- Cue markup is stripped before rendering. + +### 6.4 Subtitle styling + +`subtitleStyle` supports: + +- `fontFamily` +- `fontSize` +- `fontWeight` +- `color` +- `backgroundColor` +- `backgroundOpacity` (0..1) + +Applied style is merged as: + +- base: `subtitleStyle` prop +- override: context `settings.subtitleStyle` (including editor changes) + +### 6.5 Subtitle style editor + +Enable via: + +```tsx +subtitleStyleEditor={true} +// or +subtitleStyleEditor={{ enabled: true, storageKey: 'my-key' }} +``` + +Behavior: + +- Editor appears under `Settings -> Subtitles` +- Draft updates preview in real time +- `Save` persists to localStorage +- `Cancel` reverts to last committed style +- `Reset` sets editor defaults (not persisted until save) + +Default storage key: + +- `source-player-subtitle-style` + +## 7. Customization and modular composition + +### 7.1 Theme API + +`theme` maps to CSS variables: + +- `primaryColor` -> `--player-primary` +- `accentColor` -> `--player-primary-hover` +- `backgroundColor` -> `--player-bg` +- `textColor` -> `--player-text` +- `fontFamily` -> `--player-font-family` +- `borderRadius` -> `--player-radius` +- `overlayOpacity` -> `--player-overlay-soft` +- `controlsBackground` -> `--player-surface` +- `textSecondaryColor` -> `--player-text-secondary` +- `textMutedColor` -> `--player-text-muted` + +### 7.2 Layout and slots + +- `aspectRatio`: `'16:9' | '4:3' | '21:9' | '1:1' | '9:16' | number` +- `children`: overlay content slot +- `controlsLeftExtra`: inject extra controls on left side +- `controlsRightExtra`: inject extra controls on right side + +### 7.3 Controls auto-hide + +`controlsAutoHideDelay` (default `3000`) affects full-screen auto-hide while: + +- video is playing +- full-screen is active +- no settings sub-menu is open + +## 8. Keyboard and touch interaction + +### 8.1 Keyboard shortcuts + +Enabled by default (`keyboardShortcuts=true`). + +The player only reacts when active/focused (clicked/touched/focused instance). + +Default shortcuts: + +- `Space` / `K`: play-pause +- `ArrowLeft` / `ArrowRight`: seek `5s` +- `J` / `L`: seek `10s` +- `ArrowUp` / `ArrowDown`: volume +/- `0.1` +- `M`: mute toggle +- `F`: fullscreen toggle +- `P`: picture-in-picture toggle +- `0` / `Home`: seek to start +- `End`: seek to end +- `1..9`: seek to 10%..90% + +Config: + +```ts +type KeyboardShortcutConfig = { + seekSmall?: number + seekLarge?: number + volumeStep?: number + disabled?: string[] +} +``` + +### 8.2 Touch gestures + +Enabled via `useTouchGestures` in control layer. + +Default gestures: + +- single tap: play-pause +- double tap left/right: seek -/+ `10s` +- horizontal swipe: seek proportional to swipe distance +- vertical swipe: volume change proportional to swipe distance + +Config: + +```ts +type TouchConfig = { + maxSeekSeconds?: number + maxVolumeChange?: number + doubleTapSeekSeconds?: number +} +``` + +Default values: + +- `maxSeekSeconds = 30` +- `maxVolumeChange = 0.5` +- `doubleTapSeekSeconds = 10` + +## 9. Internationalization + +Built-in locales: + +- `en` +- `tr` + +Selection flow: + +- If `language` prop is provided, it is used. +- Else browser language is detected. +- Region codes fallback to base language (`en-US` -> `en`). +- Unknown language fallback: `en`. + +You can override any key with `translations?: Partial`. + +## 10. API reference + +### 10.1 `VideoPlayerProps` + +#### Source and playback + +| Prop | Type | Default | Notes | +| --- | --- | --- | --- | +| `src` | `string` | required | media URL | +| `protocol` | `'auto' \| 'native' \| 'hls' \| 'rtmp' \| 'dash' \| 'mpegts'` | `'auto'` | force engine | +| `poster` | `string` | - | poster image | +| `autoplay` | `boolean` | `false` | autoplay attempt on load | +| `loop` | `boolean` | `false` | loop playback | +| `muted` | `boolean` | `false` | initial muted state | +| `volume` | `number` | - | clamped to `0..1` | +| `playbackRate` | `number` | - | initial/current rate | +| `currentTime` | `number` | - | seeks when difference is significant | + +#### Media element attributes + +| Prop | Type | Default | +| --- | --- | --- | +| `crossOrigin` | `'' \| 'anonymous' \| 'use-credentials'` | - | +| `preload` | `'none' \| 'metadata' \| 'auto'` | `'metadata'` | +| `playsInline` | `boolean` | `true` | +| `controlsList` | `string` | - | + +#### UI and feature toggles + +| Prop | Type | Default | Notes | +| --- | --- | --- | --- | +| `controls` | `boolean` | `true` | hide entire control layer when false | +| `keyboardShortcuts` | `boolean` | `true` | enables keyboard hook | +| `pictureInPicture` | `boolean` | `true` | PIP button toggle | +| `controlsAutoHideDelay` | `number` | `3000` | ms | +| `playbackRates` | `number[]` | `[0.25,0.5,0.75,1,1.25,1.5,1.75,2]` | settings menu speeds | +| `aspectRatio` | `'16:9' \| '4:3' \| '21:9' \| '1:1' \| '9:16' \| number` | `'16:9'` equivalent | CSS ratio | + +#### Subtitles and settings + +| Prop | Type | Default | +| --- | --- | --- | +| `subtitles` | `SubtitleTrack[]` | `[]` | +| `subtitleStyle` | `SubtitleStyle` | - | +| `subtitleStyleEditor` | `boolean \| SubtitleStyleEditorConfig` | `false` | +| `subtitlePosition` | `'top' \| 'center' \| 'bottom'` | `'bottom'` | +| `subtitleOffset` | `number \| string` | - | + +#### Styling and composition + +| Prop | Type | Default | +| --- | --- | --- | +| `theme` | `PlayerTheme` | - | +| `className` | `string` | `''` | +| `style` | `CSSProperties` | - | +| `children` | `ReactNode` | - | +| `controlsLeftExtra` | `ReactNode` | - | +| `controlsRightExtra` | `ReactNode` | - | + +#### Localization and input config + +| Prop | Type | Default | +| --- | --- | --- | +| `language` | `string` | browser language | +| `translations` | `Partial` | - | +| `keyboardShortcutConfig` | `KeyboardShortcutConfig` | - | +| `touchConfig` | `TouchConfig` | - | + +#### Events + +| Prop | Type | +| --- | --- | +| `onPlay` | `() => void` | +| `onPause` | `() => void` | +| `onEnded` | `() => void` | +| `onTimeUpdate` | `(currentTime: number) => void` | +| `onVolumeChange` | `(volume: number) => void` | +| `onError` | `(error: Error) => void` | +| `onLoadedMetadata` | `() => void` | +| `onSeeking` | `() => void` | +| `onSeeked` | `() => void` | +| `onProgress` | `(buffered: number) => void` | +| `onDurationChange` | `(duration: number) => void` | +| `onRateChange` | `(playbackRate: number) => void` | +| `onFullscreenChange` | `(isFullscreen: boolean) => void` | +| `onPictureInPictureChange` | `(isPictureInPicture: boolean) => void` | +| `onWaiting` | `() => void` | +| `onCanPlay` | `() => void` | +| `onQualityChange` | `(quality: VideoQuality) => void` | +| `onBufferStart` | `() => void` | +| `onBufferEnd` | `() => void` | +| `onFirstPlay` | `() => void` | + +### 10.2 `VideoPlayerHandle` + +```ts +interface VideoPlayerHandle { + video: HTMLVideoElement | null + container: HTMLDivElement | null + play(): void + pause(): void + seek(time: number): void + setVolume(volume: number): void + toggleMute(): void + toggleFullscreen(): void + togglePictureInPicture(): void + setPlaybackRate(rate: number): void +} +``` + +### 10.3 `PlayerErrorBoundaryProps` + +```ts +interface PlayerErrorBoundaryProps { + children: React.ReactNode + fallback?: React.ReactNode | ((error: Error, retry: () => void) => React.ReactNode) + onError?: (error: Error, errorInfo: React.ErrorInfo) => void + onReset?: () => void + resetKeys?: ReadonlyArray +} +``` + +### 10.4 Core types + +```ts +type SubtitleTrack = { + src: string + lang: string + label: string + default?: boolean +} + +type SubtitleStyle = { + fontFamily?: string + fontSize?: number | string + fontWeight?: number | string + color?: string + backgroundColor?: string + backgroundOpacity?: number +} + +type SubtitleStyleEditorConfig = { + enabled?: boolean + storageKey?: string +} + +type VideoQuality = { + height?: number + label: string + url?: string + width?: number + bitrate?: number + levelIndex?: number +} + +type AudioTrack = { + name: string + language: string + url: string + groupId: string + default?: boolean + autoselect?: boolean +} + +type PlayerTheme = { + primaryColor?: string + accentColor?: string + backgroundColor?: string + textColor?: string + fontFamily?: string + borderRadius?: number | string + overlayOpacity?: number + controlsBackground?: string + textSecondaryColor?: string + textMutedColor?: string +} +``` + +## 11. Public exports + +From `src/index.ts`: + +- Components +- `VideoPlayer` +- `PlayerErrorBoundary` + +- Context +- `PlayerProvider` +- `usePlayerContext` + +- Hooks +- `useKeyboardShortcuts` +- `useTouchGestures` + +- i18n +- `getTranslations` +- `detectBrowserLanguage` +- `translations` + +- Utilities +- `formatTime`, `parseTime` +- `parseSRT`, `createSubtitleBlobURL`, `fetchSubtitle` +- `validateVideoURL`, `getCORSErrorMessage`, `isCORSError`, `checkVideoCORS` +- `initializePolyfills`, `features` + +- Types +- `VideoPlayerProps`, `VideoPlayerHandle`, `SubtitleTrack`, `SubtitleStyle`, `SubtitleStyleEditorConfig` +- `SubtitlePosition`, `AudioTrack`, `VideoQuality`, `PlayerTheme` +- `KeyboardShortcutConfig`, `TouchConfig` +- `VideoState`, `UIState`, `PlayerSettings`, `PlayerContextValue` +- `Translations` + +## 12. Error handling and reliability + +### 12.1 URL and CORS helpers + +Use exported helpers before rendering: + +```ts +import { validateVideoURL, checkVideoCORS } from '@source/player' + +const validation = validateVideoURL(src) if (!validation.valid) { - console.error(validation.error) + throw new Error(validation.error) } -const corsCheck = await checkVideoCORS(url) -if (!corsCheck.supported) { - console.error(corsCheck.error) +const cors = await checkVideoCORS(src) +if (!cors.supported) { + console.warn(cors.error) } - -if (isCORSError(error)) { - const message = getCORSErrorMessage(error, videoUrl) - console.log(message) -} - -// i18n -import { - getTranslations, - detectBrowserLanguage, - translations -} from '@source/player' - -const lang = detectBrowserLanguage() // "tr", "en", vb. -const t = getTranslations('tr') -console.log(t.subtitles) // "Altyazı" -console.log(translations.tr.quality) // "Kalite" ``` ---- +### 12.2 Error boundary -## 💡 Kullanım Örnekleri +`VideoPlayer` already wraps content in `PlayerErrorBoundary` with `resetKeys={[src]}`. -### Temel MP4 Oynatma +You can also wrap manually for custom fallback UI: ```tsx -import { VideoPlayer } from '@source/player' -import '@source/player/styles.css' +import { PlayerErrorBoundary, VideoPlayer } from '@source/player' -function App() { - return ( - - ) -} + ( + + {error.message} + Retry + +)}> + + ``` -### HLS Streaming ile Altyazı +### 12.3 Stream instance lifecycle -```tsx - +For HLS/FLV/MPEG-TS setups: + +- instance is attached to video element for runtime control +- cleanup runs on source/protocol change and unmount +- orphan instances are explicitly destroyed if still present + +## 13. Browser behavior and known limitations + +- PIP button renders only when browser supports PIP APIs. +- iOS Safari lacks full programmatic volume control. +- Direct RTMP URLs usually need HTTP-FLV proxying. +- DASH detection exists but playback is not implemented yet. +- MPEG-TS behavior depends on MSE support and stream/server conditions. + +Polyfill and feature utilities: + +```ts +import { initializePolyfills, features } from '@source/player' + +initializePolyfills() + +features.hasNativeHLS() +features.hasMSE() +features.hasPIP() +features.hasFullscreen() +features.hasTouch() +features.isIOSSafari() +features.hasVolumeControl() ``` -### Event Tracking +## 14. Testing and development -```tsx -function VideoWithAnalytics() { - const handlePlay = () => { - // Analytics gönder - analytics.track('video_play') - } - - const handleTimeUpdate = (time: number) => { - // Her 30 saniyede milestone kaydet - if (time % 30 === 0) { - analytics.track('video_progress', { seconds: time }) - } - } - - const handleEnded = () => { - analytics.track('video_completed') - } - - return ( - - ) -} -``` - -### RTMP Live Stream - -```tsx -// HTTP-FLV proxy üzerinden RTMP stream - -``` - -### Custom Kontroller - -```tsx -import { VideoPlayer, usePlayerContext } from '@source/player' - -function CustomControls() { - const { - videoState, - play, - pause, - seek, - setPlaybackRate - } = usePlayerContext() - - return ( - - {videoState.playing ? ( - ⏸️ Duraklat - ) : ( - ▶️ Oynat - )} - - seek(videoState.currentTime - 10)}> - ⏪ 10s Geri - - - seek(videoState.currentTime + 10)}> - ⏩ 10s İleri - - - setPlaybackRate(Number(e.target.value))} - > - 0.5x - Normal - 1.5x - 2x - - - - {formatTime(videoState.currentTime)} / {formatTime(videoState.duration)} - - - ) -} - -function App() { - return ( - - - - ) -} -``` - -### Çoklu Video Playlist - -```tsx -import { useState } from 'react' -import { VideoPlayer } from '@source/player' - -const videos = [ - { id: 1, src: 'video1.mp4', title: 'Video 1' }, - { id: 2, src: 'video2.mp4', title: 'Video 2' }, - { id: 3, src: 'video3.mp4', title: 'Video 3' }, -] - -function Playlist() { - const [currentIndex, setCurrentIndex] = useState(0) - const currentVideo = videos[currentIndex] - - const handleEnded = () => { - // Sonraki videoya geç - if (currentIndex < videos.length - 1) { - setCurrentIndex(currentIndex + 1) - } - } - - return ( - - - - - {videos.map((video, index) => ( - setCurrentIndex(index)} - className={index === currentIndex ? 'active' : ''} - > - {video.title} - - ))} - - - ) -} -``` - -### CORS Hata Yönetimi - -```tsx -import { VideoPlayer, isCORSError, getCORSErrorMessage } from '@source/player' -import { useState } from 'react' - -function VideoWithCORSHandling() { - const [error, setError] = useState(null) - const videoUrl = 'https://example.com/video.mp4' - - const handleError = (err: Error) => { - if (isCORSError(err)) { - const message = getCORSErrorMessage(err, videoUrl) - setError(message) - console.error('CORS Hatası:', message) - } else { - setError(err.message) - } - } - - return ( - - - - {error && ( - - Video Yükleme Hatası - {error} - Çözüm: Video sunucunuzda CORS headers ekleyin: - - Access-Control-Allow-Origin: *{'\n'} - Access-Control-Allow-Methods: GET, HEAD{'\n'} - Access-Control-Allow-Headers: Range - - - )} - - ) -} -``` - ---- - -## 🔧 Gelişmiş Kullanım - -### Feature Detection - -```typescript -import { - hasNativeHLS, - hasMSE, - hasPIP, - hasFullscreen, - hasTouch, - isIOSSafari, - hasVolumeControl -} from '@source/player' - -// Safari'de native HLS var mı kontrol et -if (hasNativeHLS()) { - console.log('Native HLS destekleniyor') -} - -// MSE desteği kontrol et (HLS.js için gerekli) -if (hasMSE()) { - console.log('HLS.js kullanılabilir') -} - -// PIP desteği kontrol et -if (hasPIP()) { - console.log('Picture-in-Picture kullanılabilir') -} - -// iOS Safari kontrolü -if (isIOSSafari()) { - console.log('iOS Safari - volume control yok') -} - -// Ses kontrolü desteği -if (hasVolumeControl()) { - // Volume slider göster -} else { - // Volume slider gizle (iOS) -} -``` - -### Manual HLS.js Setup - -```typescript -import { loadHls, setupHls } from '@source/player' - -async function customHlsSetup() { - const videoElement = document.querySelector('video') - const Hls = await loadHls() - - if (Hls) { - const { instance, cleanup } = setupHls( - Hls, - videoElement, - 'https://example.com/stream.m3u8', - { - onManifestParsed: (qualities, audioTracks, subtitles) => { - console.log('Qualities:', qualities) - console.log('Audio tracks:', audioTracks) - console.log('Subtitles:', subtitles) - }, - onError: (error) => { - console.error('HLS error:', error) - } - } - ) - - // instance → hls.js instance - // cleanup → cleanup function (unmount'ta çağır) - - return { instance, cleanup } - } -} -``` - -### Custom Subtitle Processing - -```typescript -import { parseSRT, createSubtitleBlobURL, fetchSubtitle } from '@source/player' - -async function loadCustomSubtitle(url: string) { - // SRT dosyasını fetch et - const srtContent = await fetchSubtitle(url) - - // VTT'ye dönüştür - const vttContent = parseSRT(srtContent) - - // Blob URL oluştur - const blobUrl = createSubtitleBlobURL(vttContent) - - // Video elementine ekle - const track = document.createElement('track') - track.kind = 'subtitles' - track.label = 'Custom' - track.srclang = 'en' - track.src = blobUrl - - videoElement.appendChild(track) - - // Cleanup - return () => URL.revokeObjectURL(blobUrl) -} -``` - -### Progressive Download Monitoring - -```tsx -function VideoWithProgress() { - const [buffered, setBuffered] = useState(0) - const { videoRef } = usePlayerContext() - - useEffect(() => { - const video = videoRef.current - if (!video) return - - const updateBuffer = () => { - if (video.buffered.length > 0) { - const bufferedEnd = video.buffered.end(video.buffered.length - 1) - setBuffered((bufferedEnd / video.duration) * 100) - } - } - - video.addEventListener('progress', updateBuffer) - return () => video.removeEventListener('progress', updateBuffer) - }, [videoRef]) - - return ( - - - - Buffer: {buffered.toFixed(0)}% - - - ) -} -``` - ---- - -## 🎨 Tema ve Özelleştirme - -### CSS Variables - -Tüm tema CSS değişkenleri ile özelleştirilebilir: - -```css -:root { - /* Ana renkler */ - --player-primary: #ef4444; - --player-primary-hover: #dc2626; - --player-bg: #000000; - --player-text: #ffffff; - - /* Spacing */ - --player-spacing-xs: 4px; - --player-spacing-sm: 8px; - --player-spacing-md: 12px; - --player-spacing-lg: 16px; - - /* Border radius */ - --player-radius: 14px; - --player-radius-sm: 8px; - - /* Transitions */ - --player-transition-fast: 120ms; - --player-transition-normal: 200ms; - --player-transition-slow: 300ms; - - /* Z-index layers */ - --player-z-video: 1; - --player-z-overlay: 10; - --player-z-controls: 20; - --player-z-menu: 30; - --player-z-loading: 40; -} -``` - -### Custom Theme Örneği - -**Mavi Tema:** -```tsx - -``` - -**Yeşil Tema:** -```tsx - -``` - -**Dark Theme:** -```tsx - -``` - -### CSS Override ile Özelleştirme - -```css -/* Progress bar yüksekliğini artır */ -.video-player .progress-bar { - height: 8px !important; -} - -/* Kontrol butonlarını büyüt */ -.video-player .control-button { - width: 48px !important; - height: 48px !important; -} - -/* Loading spinner rengini değiştir */ -.video-player .loading-spinner { - border-color: #10b981 transparent transparent transparent !important; -} - -/* Settings menü arka planı */ -.video-player .settings-menu { - background: rgba(30, 41, 59, 0.95) !important; - backdrop-filter: blur(10px) !important; -} -``` - -### Responsive Design - -```css -/* Mobil cihazlar için özelleştirme */ -@media (max-width: 768px) { - .video-player { - --player-spacing-md: 8px; - --player-radius: 8px; - } - - .video-player .control-button { - width: 40px; - height: 40px; - } - - .video-player .time-display { - font-size: 12px; - } -} - -/* Tablet için */ -@media (min-width: 769px) and (max-width: 1024px) { - .video-player { - --player-spacing-md: 10px; - } -} - -/* Desktop için */ -@media (min-width: 1025px) { - .video-player { - --player-spacing-md: 12px; - } -} -``` - ---- - -## 🌍 Uluslararasılaştırma (i18n) - -### Desteklenen Diller - -- **English (en)** - Varsayılan -- **Turkish (tr)** - Türkçe - -### Otomatik Dil Tespiti - -```tsx -// Tarayıcı dilini otomatik tespit eder - -``` - -### Manuel Dil Seçimi - -```tsx -// Türkçe zorla - - -// İngilizce zorla - -``` - -### Translation Keys - -```typescript -interface Translations { - noSubtitlesAvailable: string // "No subtitles available" / "Altyazı mevcut değil" - subtitles: string // "Subtitles" / "Altyazı" - off: string // "Off" / "Kapalı" - auto: string // "Auto" / "Otomatik" - quality: string // "Quality" / "Kalite" - speed: string // "Speed" / "Hız" - normal: string // "Normal" / "Normal" - default: string // "Default" / "Varsayılan" - audioTrack: string // "Audio Track" / "Ses" - settings: string // "Settings" / "Ayarlar" - level: string // "Level" / "Seviye" -} -``` - -### Programmatic Access - -```typescript -import { getTranslations, detectBrowserLanguage } from '@source/player' - -// Tarayıcı dilini tespit et -const browserLang = detectBrowserLanguage() // "tr", "en-US", vb. - -// Translation'ları al -const t = getTranslations('tr') -console.log(t.subtitles) // "Altyazı" -console.log(t.quality) // "Kalite" - -// Fallback ile -const t2 = getTranslations('fr') // Fransızca yok -console.log(t2.subtitles) // "Subtitles" (İngilizce fallback) -``` - -### Yeni Dil Ekleme - -`src/i18n/index.ts` dosyasını düzenleyin: - -```typescript -export const translations: Record = { - en: { /* ... */ }, - tr: { /* ... */ }, - // Yeni dil ekle - es: { - noSubtitlesAvailable: 'No hay subtítulos disponibles', - subtitles: 'Subtítulos', - off: 'Desactivado', - auto: 'Automático', - quality: 'Calidad', - speed: 'Velocidad', - normal: 'Normal', - default: 'Predeterminado', - audioTrack: 'Pista de audio', - settings: 'Configuración', - level: 'Nivel', - } -} -``` - ---- - -## ⚡ Performans - -### Bundle Size - -| Component | Size (gzipped) | -|-----------|----------------| -| Core library | ~8KB | -| CSS | ~7KB | -| **Total** | **~15KB** | -| HLS.js (lazy) | ~200KB | -| FLV.js (lazy) | ~150KB | - -### Optimizasyon Teknikleri - -1. **Lazy Loading** - - HLS.js ve FLV.js sadece ihtiyaç duyulduğunda yüklenir - - Settings menü lazy-loaded - - CDN fallback ile yükleme garantisi - -2. **Tree Shaking** - - ES Module format - - Kullanılmayan kod elimine edilir - - Named exports ile selective import - -3. **Code Splitting** - - Async component loading - - Dynamic imports - - Route-based splitting için hazır - -4. **CSS Optimization** - - CSS minification - - Tek dosyada bundle - - CSS-only animasyonlar (JS yok) - - Critical CSS inline (opsiyonel) - -5. **React Optimization** - - React.memo kullanımı - - useCallback/useMemo - - Gereksiz re-render önleme - - Event delegation - -6. **Memory Management** - - Proper cleanup (useEffect return) - - Blob URL revocation - - HLS/FLV instance destroy - - Event listener removal - -### Performance Monitoring - -```tsx -import { useEffect } from 'react' -import { VideoPlayer } from '@source/player' - -function MonitoredVideo() { - useEffect(() => { - // Bundle size monitoring - console.log('Initial bundle loaded') - - // Measure loading time - const start = performance.now() - - return () => { - const end = performance.now() - console.log(`Player active for ${end - start}ms`) - } - }, []) - - return ( - { - console.log('Video metadata loaded') - }} - /> - ) -} -``` - -### Loading Strategy - -```tsx -// Lazy load player component -import { lazy, Suspense } from 'react' - -const VideoPlayer = lazy(() => - import('@source/player').then(module => ({ - default: module.VideoPlayer - })) -) - -function App() { - return ( - Loading player...}> - - - ) -} -``` - ---- - -## 🌐 Tarayıcı Uyumluluğu - -### Desteklenen Tarayıcılar - -| Tarayıcı | Versiyon | MP4 | HLS | RTMP/FLV | PIP | Fullscreen | -|----------|----------|-----|-----|----------|-----|------------| -| Chrome | 90+ | ✅ | ✅ | ✅ | ✅ | ✅ | -| Edge | 90+ | ✅ | ✅ | ✅ | ✅ | ✅ | -| Firefox | 88+ | ✅ | ✅ | ✅ | ❌ | ✅ | -| Safari | 14+ | ✅ | ✅ (native) | ❌ | ✅ | ✅ | -| iOS Safari | 14+ | ✅ | ✅ (native) | ❌ | ✅ | ✅ | -| Chrome Mobile | 90+ | ✅ | ✅ | ✅ | ✅ | ✅ | - -### Feature Support Matrix - -| Özellik | Desktop | Mobile | Safari | iOS | -|---------|---------|--------|--------|-----| -| MP4/WebM | ✅ | ✅ | ✅ | ✅ | -| HLS (native) | ❌ | ❌ | ✅ | ✅ | -| HLS (hls.js) | ✅ | ✅ | ❌ | ❌ | -| RTMP/FLV | ✅ | ✅ | ❌ | ❌ | -| Subtitles | ✅ | ✅ | ✅ | ✅ | -| Quality switching | ✅ | ✅ | ✅ | ✅ | -| Audio tracks | ✅ | ✅ | ✅ | ✅ | -| Keyboard shortcuts | ✅ | ❌ | ✅ | ❌ | -| Touch gestures | ❌ | ✅ | ❌ | ✅ | -| Picture-in-Picture | ✅ | ✅ | ✅ | ✅ | -| Fullscreen | ✅ | ✅ | ✅ | ✅ | -| Volume control | ✅ | ✅ | ✅ | ❌* | - -*iOS Safari'de programmatic volume control yok (donanım butonları) - -### Polyfills - -Kütüphane, eski tarayıcılar için otomatik polyfill içerir: - -```typescript -// src/utils/polyfills.ts - -// Fullscreen API polyfill (vendor prefixes) -- requestFullscreen -- exitFullscreen -- fullscreenElement -- fullscreenchange event - -// Picture-in-Picture polyfill -- requestPictureInPicture -- exitPictureInPicture -- pictureInPictureElement -``` - -### Known Issues - -**Safari:** -- HLS.js kullanılmaz (native HLS var) -- FLV.js MSE desteği sınırlı - -**iOS Safari:** -- Volume control programmatic olarak değiştirilemez -- Autoplay sadece `muted: true` ile çalışır -- Fullscreen API sınırlı (video element fullscreen olur, container değil) - -**Firefox:** -- Picture-in-Picture API henüz desteklenmiyor (planned) - -**Eski Tarayıcılar (IE11, Edge <90):** -- Desteklenmez -- MSE/EME yok → HLS/FLV çalışmaz -- ES6 features gerektirir - ---- - -## 🚨 Hata Yönetimi - -### CORS Errors - -**Problem:** Video farklı origin'den geliyorsa CORS hatası - -**Tespit:** -```typescript -import { isCORSError, getCORSErrorMessage } from '@source/player' - -const handleError = (error: Error) => { - if (isCORSError(error)) { - const message = getCORSErrorMessage(error, videoUrl) - console.error(message) - } -} -``` - -**Çözüm:** -Video sunucunuzda CORS headers ekleyin: - -``` -Access-Control-Allow-Origin: * -Access-Control-Allow-Methods: GET, HEAD, OPTIONS -Access-Control-Allow-Headers: Range, Content-Type -Access-Control-Expose-Headers: Content-Length, Content-Range -``` - -**Nginx Örneği:** -```nginx -location /videos/ { - add_header Access-Control-Allow-Origin *; - add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS'; - add_header Access-Control-Allow-Headers 'Range'; - add_header Access-Control-Expose-Headers 'Content-Length, Content-Range'; - - if ($request_method = OPTIONS) { - return 204; - } -} -``` - -**Apache Örneği:** -```apache - - Header set Access-Control-Allow-Origin "*" - Header set Access-Control-Allow-Methods "GET, HEAD, OPTIONS" - Header set Access-Control-Allow-Headers "Range" - Header set Access-Control-Expose-Headers "Content-Length, Content-Range" - -``` - -### HLS Errors - -**Manifest Parse Error:** -```typescript -// HLS manifest yüklenemedi veya parse edilemedi -// Çözüm: .m3u8 URL'inin doğru olduğundan emin olun -``` - -**Network Error:** -```typescript -// HLS segment indirilemiyor -// Otomatik recovery: 3 kez retry -``` - -**Media Error:** -```typescript -// Video decode hatası -// Otomatik recovery: HLS instance restart -``` - -**Fatal Error:** -```typescript -// Kurtarılamaz hata -// Error state'e düşer, onError callback çağrılır -``` - -### RTMP/FLV Errors - -**Network Exception:** -```typescript -// FLV stream bağlantı hatası -// Otomatik retry -``` - -**Media Error:** -```typescript -// FLV decode hatası -// Error callback çağrılır -``` - -### Error Handling Best Practices - -```tsx -function RobustVideoPlayer() { - const [error, setError] = useState(null) - const [retryCount, setRetryCount] = useState(0) - const maxRetries = 3 - - const handleError = (err: Error) => { - console.error('Video error:', err) - - if (isCORSError(err)) { - setError('CORS hatası: Video sunucusu CORS headers eklemeli') - } else if (retryCount < maxRetries) { - // Retry - setRetryCount(retryCount + 1) - setTimeout(() => { - window.location.reload() - }, 2000) - } else { - setError(`Video yüklenemedi: ${err.message}`) - } - } - - if (error) { - return ( - - Video Hatası - {error} - window.location.reload()}> - Yeniden Dene - - - ) - } - - return ( - - ) -} -``` - ---- - -## 🛠️ Geliştirme - -### Kurulum +### 14.1 Useful commands ```bash -# Repository'yi klonlayın -git clone https://gits.hibna.com.tr/hibna/video-player.git -cd video-player - -# Bağımlılıkları yükleyin npm install -# veya -pnpm install -``` - -### Development Server - -```bash -# Dev server'ı başlat (examples/ klasörü) npm run dev - -# Tarayıcıda açın: http://localhost:5173 -``` - -### Build - -```bash -# Kütüphane build -npm run build:lib - -# Development build (examples) -npm run build - -# Çıktı: dist/ klasörü -``` - -### Testing - -```bash -# Unit testleri çalıştır -npm run test - -# Test UI -npm run test:ui - -# Coverage raporu -npm run test:coverage -``` - -### Linting - -```bash -# ESLint kontrolü npm run lint -``` - -### Type Checking - -```bash -# TypeScript kontrolü -npx tsc --noEmit -``` - -### Package Yayınlama - -```bash -# 1. Version bump -npm version patch # 0.1.5 -> 0.1.6 -npm version minor # 0.1.5 -> 0.2.0 -npm version major # 0.1.5 -> 1.0.0 - -# 2. Build +npm run typecheck +npm run test:run npm run build:lib - -# 3. Gitea'ya push -git push origin main --tags - -# 4. NPM'e yayınla (Gitea registry) -npm publish - -# NOT: .npmrc dosyasında authentication token olmalı +npm run validate:publish ``` -### Project Scripts +### 14.2 Test coverage areas in repository -```json -{ - "scripts": { - "dev": "vite", // Dev server - "build": "tsc && vite build", // Build examples - "build:lib": "vite build --config vite.config.lib.ts", // Build library - "preview": "vite preview", // Preview build - "lint": "eslint . --max-warnings 0", // ESLint - "test": "vitest", // Run tests - "test:ui": "vitest --ui", // Test UI - "test:coverage": "vitest --coverage" // Coverage - } -} -``` +- Core rendering and props (`VideoPlayer.test.tsx`) +- Settings interactions (`SettingsMenu.test.tsx`) +- Error boundary reset and fallback behavior +- Keyboard and touch hook behavior +- Protocol detection and streaming setup utilities +- CORS helper validation --- -## 🤝 Katkıda Bulunma - -Katkılarınızı bekliyoruz! Lütfen şu adımları takip edin: - -### 1. Fork & Clone - -```bash -# Repository'yi fork edin (Gitea UI'dan) -# Fork'unuzu klonlayın -git clone https://gits.hibna.com.tr/YOUR_USERNAME/video-player.git -cd video-player -``` - -### 2. Branch Oluşturun - -```bash -git checkout -b feature/amazing-feature -``` - -### 3. Değişiklik Yapın - -```bash -# Kodunuzu yazın -# Testlerinizi ekleyin -# Linting'i kontrol edin -npm run lint -npm run test -``` - -### 4. Commit & Push - -```bash -git add . -git commit -m "feat: Add amazing feature" -git push origin feature/amazing-feature -``` - -### 5. Pull Request - -Gitea'da Pull Request oluşturun. - -### Commit Conventions - -``` -feat: Yeni özellik -fix: Bug fix -docs: Dökümantasyon -style: Kod formatı -refactor: Refactoring -test: Test ekleme -chore: Build/config değişikliği -``` - -### Code Style - -- TypeScript strict mode -- ESLint rules -- Prettier formatting (opsiyonel) -- Component/function comment'leri - ---- - -## 📝 Lisans - -MIT License - -Copyright (c) 2024 Alper - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ---- - -## 📞 İletişim - -- **Repository:** https://gits.hibna.com.tr/hibna/video-player -- **NPM Registry:** https://gits.hibna.com.tr/api/packages/hibna/npm/ -- **Issues:** https://gits.hibna.com.tr/hibna/video-player/issues -- **Author:** Alper - ---- - -## 🎉 Teşekkürler - -Bu proje, aşağıdaki açık kaynak kütüphanelerden ilham almıştır: - -- [hls.js](https://github.com/video-dev/hls.js) - HLS streaming -- [flv.js](https://github.com/bilibili/flv.js) - FLV streaming -- [React](https://react.dev/) - UI framework -- [Vite](https://vitejs.dev/) - Build tool -- [TypeScript](https://www.typescriptlang.org/) - Type safety - ---- - -**Built with ❤️ using React, TypeScript, and Vite** - -*Son güncelleme: 2024* - - - +If you maintain this document, update it together with changes to `src/types/index.ts`, `src/index.ts`, and `src/components/VideoPlayer.tsx` to avoid API drift. diff --git a/README.md b/README.md index eac92ad..947260e 100644 --- a/README.md +++ b/README.md @@ -1,112 +1,31 @@ -# 🎬 Modern Video Player +# @source/player -[](https://www.npmjs.com/package/@source/player) -[](https://bundlephobia.com/package/@source/player) -[](https://www.typescriptlang.org/) -[](https://opensource.org/licenses/MIT) +`@source/player` is a modular, highly customizable React video player library for VOD and live streaming workflows. -**A compact, feature-rich React video player** with zero runtime dependencies. +Current package version: `3.1.0`. -A feature-rich, modern video player library built with React, TypeScript, and Vite. Designed for reusability across multiple projects. +## Why this player -## 🏆 Why Choose This Player? +- React-first component architecture (`VideoPlayer`, `PlayerProvider`, `usePlayerContext`) +- Protocol-aware playback with automatic detection (`native`, `hls`, `rtmp/flv`, `mpegts`) +- Runtime loading for streaming engines (`hls.js`, `flv.js`, `mpegts.js`) with CDN fallback +- Built-in settings UI for speed, quality, audio track, subtitles, and subtitle style editor +- Slot-style customization (`children`, `controlsLeftExtra`, `controlsRightExtra`) +- Configurable keyboard and touch interaction +- TypeScript-first API with strong exported types and an imperative ref handle -| Feature | @source/player | video.js | react-player | plyr | -| ------------------------- | ---------------------------- | ---------- | ------------ | ---------- | -| **Bundle Size (gzipped)** | **~18KB JS + ~3.5KB CSS** ✅ | ~500KB ❌ | ~50KB ⚠️ | ~30KB ⚠️ | -| **Runtime Dependencies** | **0** ✅ | Many ❌ | Few ⚠️ | Few ⚠️ | -| **React (Web)** | **Yes** ✅ | Wrapper ⚠️ | **Yes** ✅ | Wrapper ⚠️ | -| **TypeScript Native** | **Yes** ✅ | Types ⚠️ | Partial ⚠️ | Types ⚠️ | -| **HLS Support** | **Yes** ✅ | Yes ✅ | Yes ✅ | No ❌ | -| **Quality Switching** | **Yes** ✅ | Yes ✅ | Limited ⚠️ | No ❌ | -| **Touch Gestures** | **15+** ✅ | Limited ⚠️ | No ❌ | Limited ⚠️ | -| **Keyboard Shortcuts** | **15+** ✅ | ~8 ⚠️ | Basic ⚠️ | ~10 ⚠️ | -| **i18n Support** | **Yes** ✅ | Yes ✅ | No ❌ | Yes ✅ | +## Installation -### Key Advantages +This package is published to a private registry. -- 📦 **Compact bundle** - Core player ships around ~18KB gzipped JS (+ ~3.5KB CSS) -- ⚡ **Blazing fast** - Zero runtime dependencies means faster load times -- 🎯 **React-first** - Built specifically for React, not a wrapper -- 🔧 **Full TypeScript** - Complete type safety out of the box -- 🎨 **Easy customization** - CSS variables for theming -- 📱 **Mobile-ready** - Comprehensive touch gesture support -- 🌍 **Internationalized** - Built-in i18n with English and Turkish -- ♿ **Accessible** - ARIA labels and keyboard navigation - -## ✨ Features - -### 🎮 Core Playback - -- ▶️ Play/Pause controls -- ⏭️ Seek/scrub with progress bar -- 🔊 Volume control with slider -- 🎚️ Playback speed control -- 🔄 Loop support -- 🖼️ Custom poster/thumbnail - -### 🎨 Modern UI - -- Clean, minimalist design with red theme -- Smooth animations and transitions -- Auto-hiding controls -- Responsive layout (desktop & mobile) -- Custom SVG icons -- Loading spinner -- Center play button - -### ⌨️ Keyboard Shortcuts - -- `Space` or `K` - Play/Pause -- `←` / `→` - Seek 5 seconds -- `J` / `L` - Seek 10 seconds -- `↑` / `↓` - Volume up/down -- `M` - Mute/Unmute -- `F` - Fullscreen toggle -- `P` - Picture-in-Picture -- `0-9` - Jump to percentage (10%-90%) -- `Home` / `End` - Jump to start/end -- Shortcuts only work for the currently active/focused player instance - -### 📱 Touch Gestures - -- **Tap** - Play/Pause -- **Double tap left** - Rewind 10 seconds -- **Double tap right** - Forward 10 seconds -- **Swipe left/right** - Seek -- **Swipe up/down** - Volume control - -### 🚀 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 -- **Subtitle Style Editor** - Optional in-player style controls with live preview and local persistence -- **Multiple Audio Tracks** - Switch between different audio streams -- **Picture-in-Picture** - Native browser PIP support -- **Fullscreen** - Native fullscreen API -- **Buffer Indicator** - Visual buffering progress -- **Error Handling** - Graceful error states - -## 📦 Installation - -This package is distributed through a private registry. - -### 1. Configure `.npmrc` - -Create `.npmrc` in your app root: +1. Configure `.npmrc`: ```ini @source:registry=https://gits.hibna.com.tr/api/packages/hibna/npm/ //gits.hibna.com.tr/api/packages/hibna/npm/:_authToken=${GITS_NPM_TOKEN} ``` -### 2. Set token - -Set your token in environment variables (`GITS_NPM_TOKEN`) and do not commit `.npmrc` with a hardcoded token. - -### 3. Install package +2. Install: ```bash npm install @source/player @@ -116,436 +35,167 @@ pnpm add @source/player yarn add @source/player ``` -> **Note:** This package requires `react` (>=18) and `react-dom` (>=18) at runtime but does **not** list them as `peerDependencies` to avoid install conflicts with private registries. Make sure your project already has React installed. +3. Ensure your app already has React and ReactDOM installed (`>=18`). -> **Streaming libraries (optional):** HLS, FLV and MPEG-TS streaming libraries are loaded automatically from CDN when needed. If you prefer to bundle them locally, install them separately: -> -> ```bash -> npm install hls.js # HLS (.m3u8) streams -> npm install flv.js # FLV/RTMP streams -> npm install mpegts.js # MPEG-TS (.ts) streams -> ``` - -### Local development (optional) +Optional local dependencies (if you prefer bundling instead of CDN fallback): ```bash -# In this repository -npm run build:lib -npm link - -# In your consuming app -npm link @source/player +npm install hls.js flv.js mpegts.js ``` -## 🚀 Usage - -### Basic Example +## Quick start ```tsx import { VideoPlayer } from '@source/player' import '@source/player/styles.css' -function App() { - return +export function App() { + return } ``` -### Error Boundary (Optional) +## Common usage scenarios + +### 1. Standard VOD playback ```tsx -import { VideoPlayer, PlayerErrorBoundary } from '@source/player' -import '@source/player/styles.css' + +``` + +### 2. HLS playback with quality/audio menus + +```tsx + console.log('quality', quality.label)} +/> +``` + +### 3. IPTV MPEG-TS stream + +```tsx + +``` + +### 4. Subtitle style editor with local persistence + +```tsx + +``` + +### 5. Modular control customization + +```tsx +Bookmark} + controlsRightExtra={Share} + theme={{ + primaryColor: '#22c55e', + accentColor: '#16a34a', + borderRadius: 18, + fontFamily: 'Manrope, sans-serif', + }} +/> +``` + +## API at a glance + +Key `VideoPlayer` props: + +- Playback: `src`, `protocol`, `autoplay`, `loop`, `muted`, `volume`, `playbackRate`, `currentTime` +- Media element config: `crossOrigin`, `preload`, `playsInline`, `controlsList` +- UI toggles: `controls`, `keyboardShortcuts`, `pictureInPicture` +- Customization: `theme`, `className`, `style`, `aspectRatio`, `playbackRates` +- Subtitles: `subtitles`, `subtitleStyle`, `subtitleStyleEditor`, `subtitlePosition`, `subtitleOffset` +- Extensibility: `children`, `controlsLeftExtra`, `controlsRightExtra` +- Input config: `keyboardShortcutConfig`, `touchConfig` +- Localization: `language`, `translations` +- Events: `onPlay`, `onPause`, `onEnded`, `onTimeUpdate`, `onError`, `onQualityChange`, `onBufferStart`, `onBufferEnd`, `onFirstPlay`, and more + +## Imperative control via ref + +```tsx +import { useRef } from 'react' +import { VideoPlayer, type VideoPlayerHandle } from '@source/player' + +export function PlayerWithRef() { + const playerRef = useRef(null) -function App() { return ( - - - + <> + + playerRef.current?.seek(120)}> + Jump to 02:00 + + > ) } ``` -### With Subtitles +## Other exports -```tsx - +```ts +import { + PlayerErrorBoundary, + PlayerProvider, + usePlayerContext, + useKeyboardShortcuts, + useTouchGestures, + formatTime, + parseTime, + parseSRT, + createSubtitleBlobURL, + fetchSubtitle, + validateVideoURL, + checkVideoCORS, + initializePolyfills, + features, + getTranslations, + detectBrowserLanguage, +} from '@source/player' ``` -### Subtitle Styling (Custom Renderer) +## Documentation map -```tsx - -``` +For complete details, see `DOCUMENTATION.md`: -### Subtitle Style Editor (Optional) +- Full `VideoPlayerProps` and event reference +- Streaming architecture (HLS/RTMP-FLV/MPEG-TS) +- Subtitle rendering and style editor internals +- Theme tokens and CSS variable mapping +- Keyboard and touch behavior +- Error handling and browser caveats -```tsx - -``` - -`enabled: true` adds a subtitle style editor in `Settings > Subtitles`. -Changes are previewed in real-time and saved to `localStorage` only when the user presses `Save`. - -### HLS Streaming - -```tsx - -``` - -### Force Protocol (Override Auto Detection) - -```tsx - -``` - -### IPTV Streaming - -```tsx -// Note: Browser support for direct .ts streams is limited -// For best compatibility, request .m3u8 (HLS) links from your IPTV provider - { - console.error('Stream error - try requesting .m3u8 format:', error) - }} -/> -``` - -### Custom Theme - -```tsx - -``` - -### With Volume and Playback Control - -```tsx - -``` - -### With Event Handlers - -```tsx - console.log('Video started playing')} - onPause={() => console.log('Video paused')} - onEnded={() => console.log('Video ended')} - onTimeUpdate={(time) => console.log('Current time:', time)} - onVolumeChange={(volume) => console.log('Volume:', volume)} - onRateChange={(rate) => console.log('Playback rate:', rate)} - onFullscreenChange={(isFs) => console.log('Fullscreen:', isFs)} - onError={(error) => console.error('Video error:', error)} -/> -``` - -### Feature Detection & Polyfills - -```tsx -import { features, initializePolyfills } from '@source/player' - -// Initialize polyfills manually (optional - auto-initialized on VideoPlayer mount) -initializePolyfills() - -// Check browser capabilities -console.log('Has PIP support:', features.hasPIP()) -console.log('Has native HLS:', features.hasNativeHLS()) -console.log('Has MSE for HLS.js:', features.hasMSE()) -console.log('Is iOS Safari:', features.isIOSSafari()) -console.log('Has volume control:', features.hasVolumeControl()) - -// Hide PIP button on unsupported devices - -``` - -### CORS Error Handling - -```tsx -import { validateVideoURL, checkVideoCORS } from '@source/player' - -// Validate URL before loading -const validation = validateVideoURL(videoUrl) -if (!validation.valid) { - console.error(validation.error) -} - -// Check CORS support (async) -const corsCheck = await checkVideoCORS(videoUrl) -if (!corsCheck.supported) { - console.error('CORS issue:', corsCheck.error) - console.log('Needs proxy:', corsCheck.needsProxy) -} -``` - -## 🛠️ Development - -### Setup +## Development ```bash -# Install dependencies npm install - -# Start development server npm run dev - -# Build library +npm run test:run npm run build:lib - -# Type check -npx tsc --noEmit - -# Format check -npm run format:check - -# Full publish validation -npm run validate:publish ``` -### Project Structure +Useful scripts: -``` -video-player/ -├── src/ -│ ├── components/ # React components -│ │ ├── controls/ # Control UI components -│ │ ├── overlays/ # Overlay components (loading, etc.) -│ │ └── menus/ # Settings menus -│ ├── contexts/ # React Context (PlayerContext) -│ ├── hooks/ # Custom React hooks -│ ├── utils/ # Utility functions -│ ├── types/ # TypeScript type definitions -│ ├── icons/ # Custom SVG icons -│ ├── styles/ # Global styles and CSS variables -│ └── index.ts # Main export file -├── examples/ # Demo application -├── public/ # Static assets -└── dist/ # Built library (generated) -``` +- `npm run lint` +- `npm run typecheck` +- `npm run validate:publish` -## 📖 API Reference - -### VideoPlayer Props - -#### Basic Props - -| Prop | Type | Default | Description | -| --------------------- | ------------------------------------------------------------- | ------------ | -------------------------------------------------------------- | -| `src` | `string` | **required** | Video source URL (MP4, WebM, HLS, IPTV .ts) | -| `poster` | `string` | - | Poster image URL | -| `protocol` | `'auto' \| 'native' \| 'hls' \| 'rtmp' \| 'dash' \| 'mpegts'` | `'auto'` | Force playback engine instead of URL auto-detection | -| `autoplay` | `boolean` | `false` | Auto-play video on load | -| `loop` | `boolean` | `false` | Loop video playback | -| `muted` | `boolean` | `false` | Start muted | -| `volume` | `number` | - | Initial volume (0-1) | -| `playbackRate` | `number` | - | Playback speed (0.25, 0.5, 1, 1.5, 2, etc.) | -| `currentTime` | `number` | - | Initial playback position in seconds | -| `crossOrigin` | `'' \| 'anonymous' \| 'use-credentials'` | - | Sets the video `crossOrigin` attribute | -| `preload` | `'none' \| 'metadata' \| 'auto'` | `'metadata'` | Sets the video preload strategy | -| `playsInline` | `boolean` | `true` | Enables inline playback on mobile browsers | -| `controlsList` | `string` | - | Passes `controlsList` attribute to the video element | -| `controls` | `boolean` | `true` | Show player controls | -| `subtitles` | `SubtitleTrack[]` | `[]` | Subtitle tracks | -| `subtitleStyle` | `SubtitleStyle` | - | Custom subtitle text/background style | -| `subtitleStyleEditor` | `boolean \| SubtitleStyleEditorConfig` | `false` | Optional subtitle style editor UI and localStorage persistence | -| `subtitlePosition` | `'top' \| 'center' \| 'bottom'` | `'bottom'` | Subtitle vertical placement | -| `subtitleOffset` | `number \| string` | - | Subtitle offset (`px` if number) | -| `theme` | `PlayerTheme` | - | Custom theme colors | -| `language` | `string` | `'en'` | UI language ('en' or 'tr') | -| `keyboardShortcuts` | `boolean` | `true` | Enable keyboard shortcuts | -| `pictureInPicture` | `boolean` | `true` | Enable PIP button | -| `className` | `string` | - | Custom CSS class | -| `style` | `CSSProperties` | - | Inline styles | - -#### Event Handlers - -| Prop | Type | Description | -| -------------------------- | --------------------------------- | ------------------------------------------- | -| `onPlay` | `() => void` | Fired when playback starts | -| `onPause` | `() => void` | Fired when playback pauses | -| `onEnded` | `() => void` | Fired when playback ends | -| `onTimeUpdate` | `(currentTime: number) => void` | Fired during playback with current time | -| `onVolumeChange` | `(volume: number) => void` | Fired when volume changes | -| `onError` | `(error: Error) => void` | Fired on playback error | -| `onLoadedMetadata` | `() => void` | Fired when video metadata is loaded | -| `onSeeking` | `() => void` | Fired when seeking starts | -| `onSeeked` | `() => void` | Fired when seeking completes | -| `onProgress` | `(buffered: number) => void` | Fired during download progress | -| `onDurationChange` | `(duration: number) => void` | Fired when duration changes | -| `onRateChange` | `(playbackRate: number) => void` | Fired when playback rate changes | -| `onFullscreenChange` | `(isFullscreen: boolean) => void` | Fired when fullscreen state changes | -| `onPictureInPictureChange` | `(isPip: boolean) => void` | Fired when PIP state changes | -| `onWaiting` | `() => void` | Fired when buffering starts | -| `onCanPlay` | `() => void` | Fired when enough data is available to play | - -### PlayerErrorBoundary Props - -| Prop | Type | Description | -| ----------- | ------------------------------------------------------------- | -------------------------------------- | -| `children` | `ReactNode` | Wrapped player/content tree | -| `fallback` | `ReactNode \| (error: Error, retry: () => void) => ReactNode` | Optional custom fallback UI | -| `onError` | `(error: Error, errorInfo: React.ErrorInfo) => void` | Called when render errors are captured | -| `onReset` | `() => void` | Called when retry/reset is triggered | -| `resetKeys` | `readonly unknown[]` | Resets boundary when any key changes | - -### SubtitleTrack - -```typescript -interface SubtitleTrack { - src: string // Subtitle file URL (.vtt or .srt) - lang: string // Language code (e.g., 'en', 'tr') - label: string // Display label - default?: boolean // Set as default subtitle -} -``` - -```typescript -interface SubtitleStyleEditorConfig { - enabled?: boolean // Enables subtitle style editor in settings - storageKey?: string // localStorage key (default: 'source-player-subtitle-style') -} -``` - -### PlayerTheme - -```typescript -interface PlayerTheme { - primaryColor?: string // Primary color (default: #ef4444) - accentColor?: string // Accent/hover color (default: #dc2626) - backgroundColor?: string // Background color (default: #000000) - textColor?: string // Text color (default: #ffffff) -} -``` - -## 🎯 Browser Support - -- Chrome/Edge 90+ -- Firefox 88+ -- Safari 14+ -- Mobile Safari 14+ -- Chrome Mobile 90+ - -## 📊 Bundle Size - -- Core player JS bundle: **~18KB** (gzipped) -- Core player CSS bundle: **~3.5KB** (gzipped) -- HLS.js (optional, lazy-loaded): **~200KB** (only when using HLS streams) -- MPEGTS.js (optional, lazy-loaded): **~72KB** (gzipped, only for `.ts` streams) -- Zero runtime dependencies (React is a prerequisite, see installation notes) - -## 🔧 Technical Details - -### Native Browser APIs Used - -- HTML5 Video API -- Fullscreen API -- Picture-in-Picture API -- Fetch API (Range Requests) -- TextTrack API (subtitles) -- Touch Events API -- Keyboard Events API - -### Streaming Support - -**MP4/WebM (Progressive Download)** - -- Uses HTTP Range Requests -- Browser automatically chunks the download -- No additional library needed -- Works with any standard web server that supports Range headers - -**HLS (.m3u8)** - -- Automatically detects HLS sources -- Lazy loads hls.js library when needed -- Safari has native HLS support (no library needed) -- Adaptive bitrate streaming - -### Performance Optimizations - -- Lazy loading of HLS.js with CDN fallback -- CSS-only animations -- Debounced control hiding -- Optimized re-renders with React.memo -- Tree-shakeable exports -- Memory leak prevention with proper cleanup -- Polyfills for older browser support - -### Error Handling & Reliability - -- **CORS Detection**: Automatically detects and reports CORS issues with helpful error messages -- **HLS.js Fallback**: If npm package fails to load, automatically falls back to CDN -- **Memory Management**: Proper cleanup of HLS instances to prevent memory leaks -- **Browser Polyfills**: Vendor prefix support for Fullscreen and PIP APIs -- **URL Validation**: Validates video URLs before attempting to load -- **Feature Detection**: Checks browser capabilities before using advanced features - -## 🚧 TODO / Future Enhancements - -- [x] Multiple audio track UI and switching -- [x] Quality selector for HLS streams -- [x] Playback speed menu -- [x] Settings panel -- [ ] Chapters/markers support -- [ ] Thumbnail preview on hover -- [ ] Playlist support -- [ ] Chromecast support -- [ ] AirPlay support -- [ ] DASH streaming support -- [ ] Accessibility improvements (ARIA labels) -- [x] Unit tests -- [ ] E2E tests -- [ ] Storybook documentation - -## 📝 License +## License MIT - -## 👤 Author - -Alper - ---- - -Built with ❤️ using React, TypeScript, and Vite
{error.message}
{error}
Çözüm: Video sunucunuzda CORS headers ekleyin:
- Access-Control-Allow-Origin: *{'\n'} - Access-Control-Allow-Methods: GET, HEAD{'\n'} - Access-Control-Allow-Headers: Range -