Files
fastwatcher/docs/implementation-plan.md
T
2026-04-29 21:28:39 +03:00

110 lines
7.6 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.
### 1. Temel Teknoloji Yığını ve Kütüphaneler
| Kategori | Kütüphane / Teknoloji | Neden Bu Tercih Edilmeli? |
| :--- | :--- | :--- |
| **State Management** | `flutter_riverpod` | SOLID'in Dependency Inversion (D) prensibini uygulamak ve servisleri izole etmek için en modern ve güvenli yöntem. |
| **Cihaz Keşfi (mDNS)** | `nsd` veya `bonsoir` | Zero-config ağ keşfi için endüstri standardı. TV kendini yayınlar, telefon bulur. |
| **Ağ İletişimi** | `shelf` (Local HTTP Server) | TV tarafında raw TCP socket yerine küçük bir REST API (HTTP Server) kurmak hata yönetimini ve loglamayı inanılmaz kolaylaştırır. |
| **Video Oynatıcı** | `media_kit` | *Chewie/Video_player yerine kesinlikle bunu kullanmalısın.* libmpv tabanlıdır. HLS (.m3u8), MKV ve özel codec'leri Android TV'de kusursuz oynatır. |
| **Link Yakalama** | `receive_sharing_intent` | Mobil cihazda tarayıcıdan "Paylaş" aksiyonunu dinlemek için. |
| **Video Extraction** | `flutter_inappwebview` & `html` | Sayfa içindeki gizli `.m3u8` veya `.mp4` linklerini headless (gizli) bir tarayıcıda yakalamak için. |
| **Lokal Depolama** | `shared_preferences` | Cihazın "TV" mi yoksa "Mobil" mi olduğunu ilk seçimden sonra hatırlamak için. |
---
### 2. Proje Klasör Yapısı (Clean Architecture)
Kodun test edilebilir ve modüler olması için *Feature-First* (Özellik Odaklı) bir Clean Architecture yapısı kurmalıyız:
```text
lib/
├── core/ # Tüm projede ortak kullanılan yapılar
│ ├── constants/ # Port numaraları, API endpoint'leri
│ ├── error/ # Custom exception sınıfları
│ └── network/ # Temel ağ arayüzleri
├── features/
│ ├── setup/ # Cihaz rolü (TV/Telefon) seçim ekranı
│ ├── discovery/ # mDNS ile cihaz arama ve eşleşme (Mobil)
│ ├── receiver/ # TV tarafı: Local server ve oynatıcı
│ └── sender/ # Mobil taraf: Link yakalama ve gönderme
└── main.dart # Riverpod ProviderScope ve Init işlemleri
```
Her `feature` (özellik) kendi içinde şu üç katmana ayrılmalıdır (SOLID uygulamasının kalbi):
* **domain:** Sadece saf Dart kodları. Arayüzler (`abstract class`), entity'ler.
* **data:** Dış dünyayla iletişim. API çağrıları, paket implementasyonları.
* **presentation:** UI (Widget'lar) ve State (Riverpod Notifier'lar).
---
### 3. Kritik İş Akışları ve "Best Practice" Yöntemler
#### A. İlk Açılış ve Rol Seçimi (Setup)
Kullanıcı uygulamayı ilk açtığında karşısına iki dev buton çıkar: "Ben bir Telefonum (Gönderici)" ve "Ben bir TV'yim (Alıcı)".
* **Best Practice:** Bu seçim yapıldığı an `SharedPreferences`'a kaydedilir (`role = 'sender' | 'receiver'`). Uygulamanın bir sonraki açılışında `main.dart` bu değeri okur ve kullanıcıyı doğrudan ilgili ana ekrana yönlendirir.
#### B. Cihaz Eşleştirme (Discovery - mDNS)
Bluetooth yerine aynı ağda **mDNS (Zero-conf)** kullanmak en stabil "Best Practice" yöntemdir.
1. **TV (Receiver):** Uygulama açılır açılmaz arka planda `nsd` ile bir servis kaydeder. Örnek servis adı: `FastWatcher._http._tcp`. Ayrıca `shelf` ile 8080 portunda bir HTTP server dinlemeye başlar (`POST /play`).
2. **Mobil (Sender):** "Eşleş" butonuna basıldığında ağı tarar. Bulunan cihazların IP'sini bir listeye koyar. Kullanıcı TV'sine dokunduğunda IP adresi telefona kaydedilir.
#### C. İletişim Protokolü (HTTP üzerinden)
Raw Socket'ler veriyi bölebilir veya bağlantı kopmalarında sorun yaratabilir. TV'de mini bir HTTP sunucusu kurmak çok daha "Solid" bir çözümdür.
* **Mobil'in Gönderdiği Payload (JSON):**
```json
{
"url": "https://site.com/master.m3u8",
"title": "The Instigators (Opsiyonel)",
"timestamp": 1714415045
}
```
* **TV'nin Yanıtı:** `200 OK` (Başarıyla aldım, oynatmaya başlıyorum).
#### D. Video Linki Ayrıştırma (Video Extraction)
Tarayıcıdan paylaşılan link bir web sayfası olduğunda, içindeki asıl videoyu bulmak için şu hiyerarşi uygulanır:
1. **Headless WebView (En Garanti Yöntem):** Kullanıcı linki paylaştığında, arka planda gizli bir `InAppWebView` açılır. `onLoadResource` veya `shouldInterceptRequest` metodları ile ağ trafiği dinlenir. `.m3u8` veya `.mp4` uzantılı bir istek yakalandığı an sayfa kapatılır ve link TV'ye gönderilir.
2. **Open Graph & Meta Tags:** Sayfa HTML'i `html` paketi ile indirilir. `<meta property="og:video" content="...">` veya `<video src="...">` etiketleri taranır.
3. **Site Özelinde Çözücüler (Scrapers):** YouTube için `youtube_explode_dart`, Twitter/X için özel Regex paternleri gibi "Provider" tabanlı sınıflar kullanılır.
* **SOLID Notu:** Bu işlemi bir `LinkParser` interface'inden türetilen `HeadlessParser`, `RegexParser` ve `MetaParser` gibi sınıflara bölmeliyiz.
---
### 4. SOLID Prensiplerinin Projeye Uygulanması
Bu projeyi "Solid" yapacak mimari kararlar şunlardır:
* **S (Tek Sorumluluk):** `DiscoveryService` sadece ağı tarar. UI'a karışmaz. Ekrana yazdırma işini `DiscoveryNotifier` (State) ve `DiscoveryScreen` yapar.
* **O (Açık/Kapalı):** İletişim katmanını bir `abstract class NetworkClient` olarak tanımla. Şu an Wi-Fi (HTTP) kullanıyoruz. Yarın Bluetooth eklemek istersen, mevcut kodu bozmadan yeni bir `BluetoothClient` implementasyonu ekleyebilirsin.
* **L (Liskov Yerine Geçme):** Eğer TV tarafında birden fazla oynatıcı motoru kullanma ihtimalin varsa (örn: `MediaKitPlayer` ve yedek olarak `ExoPlayer`), ikisi de bir `abstract class AppVideoPlayer` arayüzünden türemelidir.
* **I (Arayüz Ayrımı):** Sender ve Receiver çok farklı işler yapar. Her iki rol için devasa tek bir arayüz yazmak yerine, `ILinkSender` ve `ILinkReceiver` olarak küçük arayüzlere böl.
* **D (Bağımlılıkların Tersine Çevrilmesi):** UI sınıfların (Widget'lar) asla doğrudan `nsd` veya `shelf` paketini bilmemelidir. Araya yazdığın soyut Repository sınıflarını (Riverpod üzerinden) UI'a enjekte etmelisin.
---
### 5. Geliştirme Adımları (Faz Planı)
**Faz 1: Çekirdek ve İletişim (UI Olmadan)**
1. Proje yapısını ve Riverpod kurulumunu tamamla.
2. TV tarafı için `shelf` ile basit bir HTTP server yaz ve Postman ile test et.
3. TV tarafında `nsd` servisini başlat.
4. Mobil tarafta ağı tarayıp TV'nin IP'sini bulan kodu yaz.
**Faz 2: Gönderim ve Oynatma**
1. Mobil cihazdan HTTP POST ile TV'ye manuel bir test linki (.m3u8) gönder.
2. TV tarafında `media_kit` paketini kur ve gelen linki tam ekranda (kumanda odaklamalarına dikkat ederek) oynatan sayfayı tasarla.
**Faz 3: Entegrasyon ve Link Extraction**
1. Mobil tarafa `receive_sharing_intent` paketini kur ve Android Manifest ayarlarını yap.
2. `LinkExtractionService` oluştur:
* Gelen URL eğer doğrudan video ise (`.mp4`, `.m3u8`, `.mkv`) direkt TV'ye gönder.
* Eğer bir web sayfası ise, `InAppWebView` (headless) ile sayfayı yükleyip ağ trafiğini dinleyerek gerçek video linkini "crawle" et.
3. Yakalanan linki ve sayfa başlığını (Title) eşleşmiş olan TV'ye fırlat.
4. Hatalı/Desteklenmeyen linkler için kullanıcıya "Video bulunamadı" uyarısı göster.
Bu mimariyle yazdığın bir kod hem çok hızlı çalışacak hem de aylar sonra dönüp baktığında nerede ne olduğunu saniyeler içinde bulabileceksin.
İlk olarak projeyi ayağa kaldırdıktan sonra, `shelf` ile TV tarafındaki o mini HTTP sunucusunun arayüzünü (Interface) tasarlayarak başlamak ister misin? Sana o kısmın Dependency Inversion'a uygun örnek kodunu yazabilirim.