Files
player/DOCUMENTATION.md
T
hibna c5ca776a60 Update MPEG-TS IPTV handling to use native playback
Changed protocol detection for direct .ts IPTV streams from 'hls' to 'native', reflecting that modern browsers can play MPEG-TS streams without a special player. Updated documentation, README, and tests to clarify browser support and recommend requesting .m3u8 links for best compatibility.
2025-11-04 05:34:29 +03:00

2648 lines
61 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🎬 @alper/video-player - Tam Dökümantasyon
**Modern, zengin özellikli ve hafif React video oynatıcı kütüphanesi**
[![npm version](https://img.shields.io/npm/v/@alper/video-player.svg?style=flat-square)](https://www.npmjs.com/package/@alper/video-player)
[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@alper/video-player?style=flat-square)](https://bundlephobia.com/package/@alper/video-player)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue?style=flat-square)](https://www.typescriptlang.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
---
## 📑 İçindekiler
- [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)
---
## 🌟 Genel Bakış
`@alper/video-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.
### Neden @alper/video-player?
| Özellik | @alper/video-player | video.js | react-player | plyr |
|---------|---------------------|----------|--------------|------|
| **Paket Boyutu (gzipped)** | **~15KB** ✅ | ~500KB ❌ | ~50KB ⚠️ | ~30KB ⚠️ |
| **Runtime Bağımlılıkları** | **0** ✅ | Çok ❌ | Az ⚠️ | Az ⚠️ |
| **React Native** | **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 ✅ |
### Ana Avantajlar
- 📦 **%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ı
---
## 📦 Kurulum
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.
### 1. NPM/PNPM ile Kurulum
Öncelikle projenizde npm veya pnpm kurulu olduğundan emin olun:
```bash
# npm kurulu mu kontrol et
npm --version
# pnpm kurulu mu kontrol et (opsiyonel, hızlı alternatif)
pnpm --version
# pnpm kurulu değilse
npm install -g pnpm
```
### 2. .npmrc Yapılandırması
`@alper` 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
# @alper scope'u için Gitea registry'yi kullan
@alper:registry=https://gitea.hibna.com.tr/api/packages/hibna/npm/
# Authentication token
# Token'ınızı almak için: https://gitea.hibna.com.tr/user/settings/applications
# "Generate New Token" butonuna tıklayın ve "read:package" yetkisini seçin
//gitea.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://gitea.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
@alper:registry=https://gitea.hibna.com.tr/api/packages/hibna/npm/
//gitea.hibna.com.tr/api/packages/hibna/npm/:_authToken=${GITEA_TOKEN}
```
**Environment variable ayarlama:**
```bash
# Linux/Mac (.bashrc veya .zshrc dosyasına ekleyin)
export GITEA_TOKEN=your-actual-token-here
# Windows (PowerShell)
$env:GITEA_TOKEN="your-actual-token-here"
# Windows (Kalıcı - Sistem Environment Variables)
# Sistem Özellikler → Gelişmiş → Ortam Değişkenleri → Yeni
# Değişken adı: GITEA_TOKEN
# Değişken değeri: your-actual-token-here
```
**CI/CD için (GitHub Actions, GitLab CI, vb.):**
Repository settings → Secrets → `GITEA_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 @alper/video-player
# veya pnpm ile (önerilen - daha hızlı)
pnpm add @alper/video-player
# veya yarn ile
yarn add @alper/video-player
```
### 4. Peer Dependencies
Kütüphane, React 18+ gerektirir. Eğer projenizde yoksa:
```bash
npm install react@^18.0.0 react-dom@^18.0.0
# veya
pnpm add react@^18.0.0 react-dom@^18.0.0
```
### 5. Optional Dependencies (Otomatik)
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 @alper/video-player
# veya
pnpm list @alper/video-player
```
Çıktı şöyle olmalı:
```
your-project@1.0.0
└─┬ @alper/video-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
- `@alper:registry=https://gitea.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 '@alper/video-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
```tsx
import { VideoPlayer } from '@alper/video-player'
import '@alper/video-player/styles.css'
function App() {
return (
<VideoPlayer
src="https://example.com/video.mp4"
poster="https://example.com/poster.jpg"
/>
)
}
```
### HLS Streaming ile Kullanım
```tsx
<VideoPlayer
src="https://example.com/stream/master.m3u8"
autoplay={false}
subtitles={[
{ src: '/subtitles/en.vtt', lang: 'en', label: 'English', default: true },
{ src: '/subtitles/tr.srt', lang: 'tr', label: 'Türkçe' }
]}
/>
```
### Event Handler'lar ile Kullanım
```tsx
<VideoPlayer
src="https://example.com/video.mp4"
onPlay={() => 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)}
/>
```
### Tema Özelleştirmesi
```tsx
<VideoPlayer
src="https://example.com/video.mp4"
theme={{
primaryColor: '#ef4444',
accentColor: '#dc2626',
backgroundColor: '#000000',
textColor: '#ffffff',
}}
/>
```
---
## 🏗️ 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)
│ │ └── <video> 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 `<video>` 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
<VideoPlayer src="https://example.com/video.mp4" />
```
#### 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
<VideoPlayer src="https://example.com/stream/master.m3u8" />
```
**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
<VideoPlayer src="https://example.com/live.flv" />
// RTMP (HTTP-FLV proxy gerektirir)
<VideoPlayer src="rtmp://example.com/live/stream" />
```
#### 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
<VideoPlayer
src="http://server.com:8080/live/username/password/12345.ts"
poster="http://example.com/channel-logo.png"
onError={(error) => {
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 '@alper/video-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 `<track>` 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 '@alper/video-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. `<track>` 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
<VideoPlayer
src="video.mp4"
subtitles={[
{
src: '/subtitles/english.vtt',
lang: 'en',
label: 'English',
default: true
},
{
src: '/subtitles/turkish.srt',
lang: 'tr',
label: 'Türkçe'
}
]}
/>
```
**HLS ile Otomatik Altyazı (manifest'ten):**
```tsx
<VideoPlayer src="stream.m3u8" />
// HLS manifest'te altyazılar varsa otomatik ekler
```
**Programmatic Control:**
```tsx
import { usePlayerContext } from '@alper/video-player'
function CustomSubtitleToggle() {
const { settings, setSubtitle } = usePlayerContext()
return (
<button onClick={() => setSubtitle(settings.subtitle ? null : mySubtitle)}>
{settings.subtitle ? 'Altyazıyı Kapat' : 'Altyazıyı Aç'}
</button>
)
}
```
### 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 '@alper/video-player'
function AudioTrackSelector() {
const { settings, setAudioTrack } = usePlayerContext()
const [tracks] = useState<AudioTrack[]>([...]) // HLS'den alınan parçalar
return (
<select
value={settings.audioTrack?.name}
onChange={(e) => {
const track = tracks.find(t => t.name === e.target.value)
setAudioTrack(track || null)
}}
>
{tracks.map(track => (
<option key={track.name} value={track.name}>
{track.label} ({track.language})
</option>
))}
</select>
)
}
```
### 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 '@alper/video-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 '@alper/video-player'
function QualitySelector() {
const { settings, setQuality } = usePlayerContext()
const [qualities] = useState<VideoQuality[]>([...]) // HLS'den alınan
return (
<select
value={settings.quality?.label || 'auto'}
onChange={(e) => {
if (e.target.value === 'auto') {
setQuality(null) // Auto
} else {
const quality = qualities.find(q => q.label === e.target.value)
setQuality(quality || null)
}
}}
>
<option value="auto">Auto</option>
{qualities.map(q => (
<option key={q.label} value={q.label}>
{q.label} ({(q.bitrate! / 1000000).toFixed(1)} Mbps)
</option>
))}
</select>
)
}
```
### 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ışı
- ✅ Default tarayıcı davranışını önler
- ✅ Enable/disable ile açılıp kapatılabilir
- ✅ Fullscreen'de de çalışır
#### Kullanım
**Otomatik (default):**
```tsx
<VideoPlayer src="video.mp4" />
// Klavye kısayolları otomatik aktif
```
**Manuel kontrol:**
```tsx
<VideoPlayer
src="video.mp4"
keyboardShortcuts={false} // Devre dışı
/>
```
**Custom hook ile:**
```tsx
import { useKeyboardShortcuts } from '@alper/video-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
<VideoPlayer src="video.mp4" />
// Touch jestleri otomatik aktif (touch destekli cihazlarda)
```
**Custom hook ile:**
```tsx
import { useTouchGestures } from '@alper/video-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
// Otomatik oynat
autoplay?: boolean
// Döngü
loop?: boolean
// Başlangıçta sessiz
muted?: boolean
// 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<HTMLVideoElement | null>
containerRef: MutableRefObject<HTMLDivElement | null>
// 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 '@alper/video-player'
```
### Exported Hooks
```typescript
// Player context hook
import { usePlayerContext } from '@alper/video-player'
// Klavye kısayolları hook'u
import { useKeyboardShortcuts } from '@alper/video-player'
// Dokunmatik jest hook'u
import { useTouchGestures } from '@alper/video-player'
```
**usePlayerContext Kullanımı:**
```tsx
function CustomControl() {
const {
videoState,
play,
pause,
seek,
setVolume
} = usePlayerContext()
return (
<div>
<button onClick={play}>Oynat</button>
<button onClick={pause}>Duraklat</button>
<button onClick={() => seek(30)}>30s İleri</button>
<span>{videoState.currentTime}s / {videoState.duration}s</span>
</div>
)
}
// PlayerProvider içinde kullanılmalı
<VideoPlayer src="video.mp4">
<CustomControl />
</VideoPlayer>
```
### Exported Utilities
```typescript
// Zaman formatlama
import { formatTime, parseTime } from '@alper/video-player'
formatTime(125) // "2:05"
formatTime(3665) // "1:01:05"
parseTime("2:05") // 125
parseTime("1:01:05") // 3665
// Altyazı utilities
import {
parseSRT,
createSubtitleBlobURL,
fetchSubtitle
} from '@alper/video-player'
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')
// CORS helpers
import {
validateVideoURL,
getCORSErrorMessage,
isCORSError,
checkVideoCORS
} from '@alper/video-player'
const validation = validateVideoURL(url)
if (!validation.valid) {
console.error(validation.error)
}
const corsCheck = await checkVideoCORS(url)
if (!corsCheck.supported) {
console.error(corsCheck.error)
}
if (isCORSError(error)) {
const message = getCORSErrorMessage(error, videoUrl)
console.log(message)
}
// i18n
import {
getTranslations,
detectBrowserLanguage,
translations
} from '@alper/video-player'
const lang = detectBrowserLanguage() // "tr", "en", vb.
const t = getTranslations('tr')
console.log(t.subtitles) // "Altyazı"
console.log(translations.tr.quality) // "Kalite"
```
---
## 💡 Kullanım Örnekleri
### Temel MP4 Oynatma
```tsx
import { VideoPlayer } from '@alper/video-player'
import '@alper/video-player/styles.css'
function App() {
return (
<VideoPlayer
src="https://example.com/video.mp4"
poster="https://example.com/poster.jpg"
/>
)
}
```
### HLS Streaming ile Altyazı
```tsx
<VideoPlayer
src="https://example.com/stream/master.m3u8"
subtitles={[
{
src: '/subtitles/english.vtt',
lang: 'en',
label: 'English',
default: true
},
{
src: '/subtitles/turkish.srt',
lang: 'tr',
label: 'Türkçe'
}
]}
theme={{
primaryColor: '#3b82f6',
accentColor: '#2563eb'
}}
/>
```
### Event Tracking
```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 (
<VideoPlayer
src="video.mp4"
onPlay={handlePlay}
onTimeUpdate={handleTimeUpdate}
onEnded={handleEnded}
/>
)
}
```
### RTMP Live Stream
```tsx
// HTTP-FLV proxy üzerinden RTMP stream
<VideoPlayer
src="https://your-server.com/live/stream.flv"
autoplay
muted
/>
```
### Custom Kontroller
```tsx
import { VideoPlayer, usePlayerContext } from '@alper/video-player'
function CustomControls() {
const {
videoState,
play,
pause,
seek,
setPlaybackRate
} = usePlayerContext()
return (
<div className="custom-controls">
{videoState.playing ? (
<button onClick={pause}>⏸️ Duraklat</button>
) : (
<button onClick={play}>▶️ Oynat</button>
)}
<button onClick={() => seek(videoState.currentTime - 10)}>
⏪ 10s Geri
</button>
<button onClick={() => seek(videoState.currentTime + 10)}>
⏩ 10s İleri
</button>
<select
value={videoState.playbackRate}
onChange={(e) => setPlaybackRate(Number(e.target.value))}
>
<option value="0.5">0.5x</option>
<option value="1">Normal</option>
<option value="1.5">1.5x</option>
<option value="2">2x</option>
</select>
<span>
{formatTime(videoState.currentTime)} / {formatTime(videoState.duration)}
</span>
</div>
)
}
function App() {
return (
<VideoPlayer src="video.mp4">
<CustomControls />
</VideoPlayer>
)
}
```
### Çoklu Video Playlist
```tsx
import { useState } from 'react'
import { VideoPlayer } from '@alper/video-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 (
<div>
<VideoPlayer
key={currentVideo.id}
src={currentVideo.src}
onEnded={handleEnded}
/>
<div className="playlist">
{videos.map((video, index) => (
<button
key={video.id}
onClick={() => setCurrentIndex(index)}
className={index === currentIndex ? 'active' : ''}
>
{video.title}
</button>
))}
</div>
</div>
)
}
```
### CORS Hata Yönetimi
```tsx
import { VideoPlayer, isCORSError, getCORSErrorMessage } from '@alper/video-player'
import { useState } from 'react'
function VideoWithCORSHandling() {
const [error, setError] = useState<string | null>(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 (
<div>
<VideoPlayer
src={videoUrl}
onError={handleError}
/>
{error && (
<div className="error-message">
<h3>Video Yükleme Hatası</h3>
<p>{error}</p>
<p>Çözüm: Video sunucunuzda CORS headers ekleyin:</p>
<pre>
Access-Control-Allow-Origin: *{'\n'}
Access-Control-Allow-Methods: GET, HEAD{'\n'}
Access-Control-Allow-Headers: Range
</pre>
</div>
)}
</div>
)
}
```
---
## 🔧 Gelişmiş Kullanım
### Feature Detection
```typescript
import {
hasNativeHLS,
hasMSE,
hasPIP,
hasFullscreen,
hasTouch,
isIOSSafari,
hasVolumeControl
} from '@alper/video-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 '@alper/video-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 '@alper/video-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 (
<div>
<VideoPlayer src="large-video.mp4" />
<div className="buffer-indicator">
Buffer: {buffered.toFixed(0)}%
</div>
</div>
)
}
```
---
## 🎨 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
<VideoPlayer
src="video.mp4"
theme={{
primaryColor: '#3b82f6',
accentColor: '#2563eb',
backgroundColor: '#1e293b',
textColor: '#f1f5f9'
}}
/>
```
**Yeşil Tema:**
```tsx
<VideoPlayer
src="video.mp4"
theme={{
primaryColor: '#10b981',
accentColor: '#059669',
backgroundColor: '#064e3b',
textColor: '#ecfdf5'
}}
/>
```
**Dark Theme:**
```tsx
<VideoPlayer
src="video.mp4"
theme={{
primaryColor: '#8b5cf6',
accentColor: '#7c3aed',
backgroundColor: '#18181b',
textColor: '#fafafa'
}}
/>
```
### 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
<VideoPlayer src="video.mp4" />
```
### Manuel Dil Seçimi
```tsx
// Türkçe zorla
<VideoPlayer src="video.mp4" language="tr" />
// İngilizce zorla
<VideoPlayer src="video.mp4" language="en" />
```
### 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 '@alper/video-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<string, Translations> = {
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 '@alper/video-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 (
<VideoPlayer
src="video.mp4"
onLoadedMetadata={() => {
console.log('Video metadata loaded')
}}
/>
)
}
```
### Loading Strategy
```tsx
// Lazy load player component
import { lazy, Suspense } from 'react'
const VideoPlayer = lazy(() =>
import('@alper/video-player').then(module => ({
default: module.VideoPlayer
}))
)
function App() {
return (
<Suspense fallback={<div>Loading player...</div>}>
<VideoPlayer src="video.mp4" />
</Suspense>
)
}
```
---
## 🌐 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 '@alper/video-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
<IfModule mod_headers.c>
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"
</IfModule>
```
### 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<string | null>(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 (
<div className="error-container">
<h3>Video Hatası</h3>
<p>{error}</p>
<button onClick={() => window.location.reload()}>
Yeniden Dene
</button>
</div>
)
}
return (
<VideoPlayer
src="video.mp4"
onError={handleError}
/>
)
}
```
---
## 🛠️ Geliştirme
### Kurulum
```bash
# Repository'yi klonlayın
git clone https://gitea.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 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ı
```
### Project Scripts
```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
}
}
```
---
## 🤝 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://gitea.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://gitea.hibna.com.tr/hibna/video-player
- **NPM Registry:** https://gitea.hibna.com.tr/api/packages/hibna/npm/
- **Issues:** https://gitea.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*