KekikStream 0.7.14__tar.gz → 2.0.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/CLI/pypi_kontrol.py +6 -6
  2. kekikstream-2.0.9/KekikStream/Core/Extractor/ExtractorBase.py +49 -0
  3. kekikstream-2.0.9/KekikStream/Core/Extractor/ExtractorLoader.py +98 -0
  4. kekikstream-2.0.9/KekikStream/Core/Extractor/ExtractorManager.py +75 -0
  5. {kekikstream-0.7.14/KekikStream/Core → kekikstream-2.0.9/KekikStream/Core/Extractor}/ExtractorModels.py +5 -7
  6. kekikstream-2.0.9/KekikStream/Core/Extractor/YTDLPCache.py +35 -0
  7. {kekikstream-0.7.14/KekikStream/Core → kekikstream-2.0.9/KekikStream/Core/Media}/MediaHandler.py +66 -25
  8. {kekikstream-0.7.14/KekikStream/Managers → kekikstream-2.0.9/KekikStream/Core/Media}/MediaManager.py +1 -4
  9. kekikstream-2.0.9/KekikStream/Core/Plugin/PluginBase.py +122 -0
  10. kekikstream-2.0.9/KekikStream/Core/Plugin/PluginLoader.py +79 -0
  11. {kekikstream-0.7.14/KekikStream/Managers → kekikstream-2.0.9/KekikStream/Core/Plugin}/PluginManager.py +6 -1
  12. kekikstream-2.0.9/KekikStream/Core/Plugin/PluginModels.py +79 -0
  13. {kekikstream-0.7.14/KekikStream/Managers → kekikstream-2.0.9/KekikStream/Core/UI}/UIManager.py +1 -1
  14. kekikstream-2.0.9/KekikStream/Core/__init__.py +19 -0
  15. kekikstream-2.0.9/KekikStream/Extractors/CloseLoad.py +26 -0
  16. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/ContentX.py +17 -4
  17. kekikstream-2.0.9/KekikStream/Extractors/DonilasPlay.py +86 -0
  18. kekikstream-2.0.9/KekikStream/Extractors/DzenRu.py +38 -0
  19. kekikstream-2.0.9/KekikStream/Extractors/ExPlay.py +53 -0
  20. kekikstream-2.0.9/KekikStream/Extractors/HDPlayerSystem.py +41 -0
  21. kekikstream-2.0.9/KekikStream/Extractors/JetTv.py +45 -0
  22. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/MailRu.py +4 -6
  23. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/MixPlayHD.py +2 -2
  24. kekikstream-2.0.9/KekikStream/Extractors/MixTiger.py +57 -0
  25. kekikstream-2.0.9/KekikStream/Extractors/MolyStream.py +34 -0
  26. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/Odnoklassniki.py +22 -11
  27. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/PeaceMakerst.py +10 -4
  28. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/PixelDrain.py +1 -2
  29. kekikstream-2.0.9/KekikStream/Extractors/PlayerFilmIzle.py +65 -0
  30. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/RapidVid.py +38 -15
  31. kekikstream-2.0.9/KekikStream/Extractors/SetPlay.py +63 -0
  32. kekikstream-2.0.9/KekikStream/Extractors/SetPrime.py +45 -0
  33. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/SibNet.py +2 -3
  34. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/Sobreatsesuyp.py +4 -4
  35. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/TRsTX.py +4 -4
  36. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/TauVideo.py +2 -2
  37. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/TurboImgz.py +2 -2
  38. kekikstream-2.0.9/KekikStream/Extractors/TurkeyPlayer.py +34 -0
  39. kekikstream-2.0.9/KekikStream/Extractors/VCTPlay.py +41 -0
  40. kekikstream-2.0.9/KekikStream/Extractors/VidHide.py +72 -0
  41. kekikstream-2.0.9/KekikStream/Extractors/VidMoly.py +112 -0
  42. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/VidMoxy.py +2 -3
  43. kekikstream-2.0.9/KekikStream/Extractors/VidPapi.py +89 -0
  44. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/Extractors/VideoSeyred.py +9 -3
  45. kekikstream-2.0.9/KekikStream/Extractors/YTDLP.py +211 -0
  46. kekikstream-2.0.9/KekikStream/Extractors/YildizKisaFilm.py +41 -0
  47. kekikstream-2.0.9/KekikStream/Plugins/BelgeselX.py +204 -0
  48. kekikstream-2.0.9/KekikStream/Plugins/DiziBox.py +212 -0
  49. kekikstream-2.0.9/KekikStream/Plugins/DiziPal.py +246 -0
  50. kekikstream-2.0.9/KekikStream/Plugins/DiziYou.py +197 -0
  51. kekikstream-2.0.9/KekikStream/Plugins/Dizilla.py +203 -0
  52. kekikstream-2.0.9/KekikStream/Plugins/FilmBip.py +145 -0
  53. kekikstream-2.0.9/KekikStream/Plugins/FilmMakinesi.py +121 -0
  54. kekikstream-2.0.9/KekikStream/Plugins/FilmModu.py +142 -0
  55. kekikstream-2.0.9/KekikStream/Plugins/FullHDFilm.py +164 -0
  56. kekikstream-2.0.9/KekikStream/Plugins/FullHDFilmizlesene.py +128 -0
  57. kekikstream-2.0.9/KekikStream/Plugins/HDFilmCehennemi.py +296 -0
  58. kekikstream-2.0.9/KekikStream/Plugins/JetFilmizle.py +149 -0
  59. kekikstream-2.0.9/KekikStream/Plugins/KultFilmler.py +219 -0
  60. kekikstream-2.0.9/KekikStream/Plugins/RecTV.py +158 -0
  61. kekikstream-2.0.9/KekikStream/Plugins/RoketDizi.py +225 -0
  62. kekikstream-2.0.9/KekikStream/Plugins/SelcukFlix.py +308 -0
  63. kekikstream-2.0.9/KekikStream/Plugins/SetFilmIzle.py +259 -0
  64. kekikstream-2.0.9/KekikStream/Plugins/SezonlukDizi.py +163 -0
  65. kekikstream-2.0.9/KekikStream/Plugins/SineWix.py +166 -0
  66. kekikstream-2.0.9/KekikStream/Plugins/Sinefy.py +241 -0
  67. kekikstream-2.0.9/KekikStream/Plugins/SinemaCX.py +153 -0
  68. kekikstream-2.0.9/KekikStream/Plugins/Sinezy.py +146 -0
  69. kekikstream-2.0.9/KekikStream/Plugins/SuperFilmGeldi.py +127 -0
  70. kekikstream-2.0.9/KekikStream/Plugins/UgurFilm.py +109 -0
  71. kekikstream-2.0.9/KekikStream/__init__.py +357 -0
  72. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/requirements.txt +3 -4
  73. kekikstream-2.0.9/KekikStream.egg-info/PKG-INFO +309 -0
  74. kekikstream-2.0.9/KekikStream.egg-info/SOURCES.txt +84 -0
  75. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream.egg-info/requires.txt +2 -1
  76. kekikstream-2.0.9/PKG-INFO +309 -0
  77. kekikstream-2.0.9/README.md +272 -0
  78. {kekikstream-0.7.14 → kekikstream-2.0.9}/setup.py +5 -4
  79. kekikstream-0.7.14/KekikStream/Core/ExtractorBase.py +0 -42
  80. kekikstream-0.7.14/KekikStream/Core/ExtractorLoader.py +0 -82
  81. kekikstream-0.7.14/KekikStream/Core/PluginBase.py +0 -80
  82. kekikstream-0.7.14/KekikStream/Core/PluginLoader.py +0 -61
  83. kekikstream-0.7.14/KekikStream/Core/PluginModels.py +0 -63
  84. kekikstream-0.7.14/KekikStream/Core/__init__.py +0 -9
  85. kekikstream-0.7.14/KekikStream/Extractors/CloseLoad.py +0 -31
  86. kekikstream-0.7.14/KekikStream/Extractors/FourCX.py +0 -7
  87. kekikstream-0.7.14/KekikStream/Extractors/FourPichive.py +0 -7
  88. kekikstream-0.7.14/KekikStream/Extractors/FourPlayRu.py +0 -7
  89. kekikstream-0.7.14/KekikStream/Extractors/HDStreamAble.py +0 -7
  90. kekikstream-0.7.14/KekikStream/Extractors/Hotlinger.py +0 -7
  91. kekikstream-0.7.14/KekikStream/Extractors/OkRuHTTP.py +0 -7
  92. kekikstream-0.7.14/KekikStream/Extractors/OkRuSSL.py +0 -7
  93. kekikstream-0.7.14/KekikStream/Extractors/Pichive.py +0 -7
  94. kekikstream-0.7.14/KekikStream/Extractors/PlayRu.py +0 -7
  95. kekikstream-0.7.14/KekikStream/Extractors/VidMoly.py +0 -85
  96. kekikstream-0.7.14/KekikStream/Managers/ExtractorManager.py +0 -27
  97. kekikstream-0.7.14/KekikStream/Managers/__init__.py +0 -6
  98. kekikstream-0.7.14/KekikStream/Plugins/DiziBox.py +0 -143
  99. kekikstream-0.7.14/KekikStream/Plugins/DiziYou.py +0 -127
  100. kekikstream-0.7.14/KekikStream/Plugins/Dizilla.py +0 -106
  101. kekikstream-0.7.14/KekikStream/Plugins/FilmMakinesi.py +0 -65
  102. kekikstream-0.7.14/KekikStream/Plugins/FullHDFilmizlesene.py +0 -78
  103. kekikstream-0.7.14/KekikStream/Plugins/JetFilmizle.py +0 -92
  104. kekikstream-0.7.14/KekikStream/Plugins/RecTV.py +0 -114
  105. kekikstream-0.7.14/KekikStream/Plugins/SezonlukDizi.py +0 -108
  106. kekikstream-0.7.14/KekikStream/Plugins/SineWix.py +0 -108
  107. kekikstream-0.7.14/KekikStream/Plugins/UgurFilm.py +0 -75
  108. kekikstream-0.7.14/KekikStream/__init__.py +0 -255
  109. kekikstream-0.7.14/KekikStream.egg-info/PKG-INFO +0 -94
  110. kekikstream-0.7.14/KekikStream.egg-info/SOURCES.txt +0 -63
  111. kekikstream-0.7.14/PKG-INFO +0 -94
  112. kekikstream-0.7.14/README.md +0 -59
  113. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/CLI/__init__.py +0 -0
  114. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream/__main__.py +0 -0
  115. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream.egg-info/dependency_links.txt +0 -0
  116. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream.egg-info/entry_points.txt +0 -0
  117. {kekikstream-0.7.14 → kekikstream-2.0.9}/KekikStream.egg-info/top_level.txt +0 -0
  118. {kekikstream-0.7.14 → kekikstream-2.0.9}/LICENSE +0 -0
  119. {kekikstream-0.7.14 → kekikstream-2.0.9}/MANIFEST.in +0 -0
  120. {kekikstream-0.7.14 → kekikstream-2.0.9}/setup.cfg +0 -0
@@ -1,16 +1,16 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from . import konsol
4
- from rich.panel import Panel
5
- from pkg_resources import get_distribution
6
- from requests import get
7
- from subprocess import check_call
3
+ from . import konsol
4
+ from rich.panel import Panel
5
+ from importlib import metadata
6
+ from requests import get
7
+ from subprocess import check_call
8
8
  import sys
9
9
 
10
10
  def pypi_kontrol_guncelle(paket_adi: str):
11
11
  try:
12
12
  konsol.print(f"[bold cyan] {paket_adi} Güncellemesi kontrol ediliyor...[/bold cyan]")
13
- mevcut_surum = get_distribution(paket_adi).version
13
+ mevcut_surum = metadata.version(paket_adi)
14
14
  konsol.print(Panel(f"[cyan]Yüklü sürüm:[/cyan] [bold yellow]{mevcut_surum}[/bold yellow]"))
15
15
 
16
16
  istek = get(f"https://pypi.org/pypi/{paket_adi}/json")
@@ -0,0 +1,49 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from abc import ABC, abstractmethod
4
+ from cloudscraper import CloudScraper
5
+ from httpx import AsyncClient
6
+ from typing import Optional
7
+ from .ExtractorModels import ExtractResult
8
+ from urllib.parse import urljoin
9
+
10
+ class ExtractorBase(ABC):
11
+ # Çıkarıcının temel özellikleri
12
+ name = "Extractor"
13
+ main_url = ""
14
+
15
+ def __init__(self):
16
+ # cloudscraper - for bypassing Cloudflare
17
+ self.cloudscraper = CloudScraper()
18
+
19
+ # httpx - lightweight and safe for most HTTP requests
20
+ self.httpx = AsyncClient(timeout = 10)
21
+ self.httpx.headers.update(self.cloudscraper.headers)
22
+ self.httpx.cookies.update(self.cloudscraper.cookies)
23
+ self.httpx.headers.update({
24
+ "User-Agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 15.7; rv:135.0) Gecko/20100101 Firefox/135.0",
25
+ "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
26
+ })
27
+
28
+ def can_handle_url(self, url: str) -> bool:
29
+ # URL'nin bu çıkarıcı tarafından işlenip işlenemeyeceğini kontrol et
30
+ return self.main_url in url
31
+
32
+ @abstractmethod
33
+ async def extract(self, url: str, referer: Optional[str] = None) -> ExtractResult:
34
+ # Alt sınıflar tarafından uygulanacak medya çıkarma fonksiyonu
35
+ pass
36
+
37
+ async def close(self):
38
+ """Close HTTP client."""
39
+ await self.httpx.aclose()
40
+
41
+ def fix_url(self, url: str) -> str:
42
+ # Eksik URL'leri düzelt ve tam URL formatına çevir
43
+ if not url:
44
+ return ""
45
+
46
+ if url.startswith("http") or url.startswith("{\""):
47
+ return url
48
+
49
+ return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
@@ -0,0 +1,98 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from ...CLI import konsol, cikis_yap
4
+ from .ExtractorBase import ExtractorBase
5
+ from pathlib import Path
6
+ import os, importlib.util, traceback
7
+
8
+ class ExtractorLoader:
9
+ def __init__(self, extractors_dir: str):
10
+ # Yerel ve global çıkarıcı dizinlerini ayarla
11
+ self.local_extractors_dir = Path(extractors_dir)
12
+ self.global_extractors_dir = Path(__file__).parent.parent.parent / extractors_dir
13
+
14
+ # Dizin kontrolü
15
+ if not self.local_extractors_dir.exists() and not self.global_extractors_dir.exists():
16
+ # konsol.log(f"[red][!] Extractor dizini bulunamadı: {self.global_extractors_dir}[/red]")
17
+ cikis_yap(False)
18
+
19
+ def load_all(self) -> list[ExtractorBase]:
20
+ extractors = []
21
+
22
+ # Eğer yerel dizinde Extractor varsa, sadece onları yükle (eklenti geliştirme modu)
23
+ if self.local_extractors_dir.exists():
24
+ # konsol.log(f"[green][*] Yerel Extractor dizininden yükleniyor: {self.local_extractors_dir}[/green]")
25
+ local_extractors = self._load_from_directory(self.local_extractors_dir)
26
+ # konsol.log(f"[green]Yerel Extractor'lar: {[e.__name__ for e in local_extractors]}[/green]")
27
+
28
+ if local_extractors:
29
+ # konsol.log("[cyan][*] Yerel Extractor bulundu, global Extractor'lar atlanıyor (eklenti geliştirme modu)[/cyan]")
30
+ extractors.extend(local_extractors)
31
+
32
+ # Yerel dizinde Extractor yoksa, global'leri yükle
33
+ if not extractors and self.global_extractors_dir.exists():
34
+ # konsol.log(f"[green][*] Global Extractor dizininden yükleniyor: {self.global_extractors_dir}[/green]")
35
+ global_extractors = self._load_from_directory(self.global_extractors_dir)
36
+ # konsol.log(f"[green]Global Extractor'lar: {[e.__name__ for e in global_extractors]}[/green]")
37
+ extractors.extend(global_extractors)
38
+
39
+ # Benzersizliği sağlama (modül adı + sınıf adı bazında)
40
+ unique_extractors = []
41
+ seen_names = set()
42
+ for ext in extractors:
43
+ identifier = f"{ext.__module__}.{ext.__name__}"
44
+ if identifier not in seen_names:
45
+ unique_extractors.append(ext)
46
+ seen_names.add(identifier)
47
+
48
+ # konsol.log(f"[blue]Sonuç Extractor'lar: {[e.__name__ for e in unique_extractors]}[/blue]")
49
+
50
+ if not unique_extractors:
51
+ konsol.log("[yellow][!] Yüklenecek bir Extractor bulunamadı![/yellow]")
52
+
53
+ return unique_extractors
54
+
55
+ def _load_from_directory(self, directory: Path) -> list[ExtractorBase]:
56
+ extractors = []
57
+
58
+ # Dizindeki tüm .py dosyalarını tara
59
+ for file in os.listdir(directory):
60
+ if file.endswith(".py") and not file.startswith("__"):
61
+ module_name = file[:-3] # .py uzantısını kaldır
62
+ # konsol.log(f"[cyan]Okunan Dosya\t\t: {module_name}[/cyan]")
63
+ module_extractors = self._load_extractor(directory, module_name)
64
+ if module_extractors:
65
+ # konsol.log(f"[magenta]Extractor Yüklendi\t: {[e.__name__ for e in module_extractors]}[/magenta]")
66
+ extractors.extend(module_extractors)
67
+
68
+ # konsol.log(f"[yellow]{directory} dizininden yüklenen Extractor'lar: {[e.__name__ for e in extractors]}[/yellow]")
69
+ return extractors
70
+
71
+ def _load_extractor(self, directory: Path, module_name: str):
72
+ try:
73
+ # Modül dosyasını bul ve yükle
74
+ path = directory / f"{module_name}.py"
75
+ spec = importlib.util.spec_from_file_location(module_name, path)
76
+ if not spec or not spec.loader:
77
+ return []
78
+
79
+ # Modülü içe aktar
80
+ module = importlib.util.module_from_spec(spec)
81
+ spec.loader.exec_module(module)
82
+
83
+ # Yalnızca doğru modülden gelen ExtractorBase sınıflarını yükle (TÜM CLASS'LAR)
84
+ extractors = []
85
+ for attr in dir(module):
86
+ obj = getattr(module, attr)
87
+ # isinstance kontrolünü __module__ kontrolünden ÖNCE yap
88
+ if isinstance(obj, type) and issubclass(obj, ExtractorBase) and obj is not ExtractorBase and obj.__module__ == module_name:
89
+ # konsol.log(f"[green]Yüklenen sınıf\t\t: {module_name}.{obj.__name__} ({obj.__module__}.{obj.__name__})[/green]")
90
+ extractors.append(obj)
91
+
92
+ return extractors
93
+
94
+ except Exception as hata:
95
+ konsol.log(f"[red][!] Extractor yüklenirken hata oluştu: {module_name}\nHata: {hata}")
96
+ konsol.print(f"[dim]{traceback.format_exc()}[/dim]")
97
+
98
+ return []
@@ -0,0 +1,75 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from .ExtractorLoader import ExtractorLoader
4
+ from .ExtractorBase import ExtractorBase
5
+
6
+ class ExtractorManager:
7
+ def __init__(self, extractor_dir="Extractors"):
8
+ # Çıkarıcı yükleyiciyi başlat ve tüm çıkarıcıları yükle
9
+ self.extractor_loader = ExtractorLoader(extractor_dir)
10
+ self.extractors = self.extractor_loader.load_all() # Sadece class'lar
11
+
12
+ # Lazy loading: Instance'lar ilk kullanımda oluşturulacak
13
+ self._extractor_instances = None # None = henüz oluşturulmadı
14
+ self._ytdlp_extractor = None
15
+ self._initialized = False
16
+
17
+ def _ensure_initialized(self):
18
+ """
19
+ Lazy initialization: İlk kullanımda TÜM extractorları initialize et
20
+
21
+ Startup'ta sadece class'ları yükledik (hızlı).
22
+ Şimdi instance'ları oluştur ve cache'le (bir kere).
23
+ """
24
+ if self._initialized:
25
+ return
26
+
27
+ # Instance listesi oluştur
28
+ self._extractor_instances = []
29
+
30
+ # TÜM extractorları instance'la
31
+ for extractor_cls in self.extractors:
32
+ instance = extractor_cls()
33
+
34
+ # YTDLP'yi ayrı tut
35
+ if instance.name == "yt-dlp":
36
+ self._ytdlp_extractor = instance
37
+ else:
38
+ self._extractor_instances.append(instance)
39
+
40
+ # YTDLP'yi EN BAŞA ekle
41
+ if self._ytdlp_extractor:
42
+ self._extractor_instances.insert(0, self._ytdlp_extractor)
43
+
44
+ self._initialized = True
45
+
46
+ def find_extractor(self, link) -> ExtractorBase:
47
+ """
48
+ Verilen bağlantıyı işleyebilecek çıkarıcıyı bul
49
+ """
50
+ # Lazy loading: İlk kullanımda extractorları initialize et
51
+ self._ensure_initialized()
52
+
53
+ # Cached instance'ları kullan
54
+ for extractor in self._extractor_instances:
55
+ if extractor.can_handle_url(link):
56
+ return extractor
57
+
58
+ return None
59
+
60
+ def map_links_to_extractors(self, links) -> dict:
61
+ """
62
+ Bağlantıları uygun çıkarıcılarla eşleştir
63
+ """
64
+ # Lazy loading: İlk kullanımda extractorları initialize et
65
+ self._ensure_initialized()
66
+
67
+ mapping = {}
68
+ for link in links:
69
+ # Cached instance'ları kullan
70
+ for extractor in self._extractor_instances:
71
+ if extractor.can_handle_url(link):
72
+ mapping[link] = f"{extractor.name:<30} » {link.replace(extractor.main_url, '')}"
73
+ break # İlk eşleşmede dur
74
+
75
+ return mapping
@@ -1,8 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
3
  from pydantic import BaseModel
4
- from typing import List, Optional
5
-
6
4
 
7
5
  class Subtitle(BaseModel):
8
6
  """Altyazı modeli."""
@@ -12,8 +10,8 @@ class Subtitle(BaseModel):
12
10
 
13
11
  class ExtractResult(BaseModel):
14
12
  """Extractor'ın döndürmesi gereken sonuç modeli."""
15
- name : str
16
- url : str
17
- referer : str
18
- subtitles : List[Subtitle] = []
19
- headers : Optional[dict] = {}
13
+ name : str
14
+ url : str
15
+ referer : str | None = None
16
+ user_agent : str | None = None
17
+ subtitles : list[Subtitle] = []
@@ -0,0 +1,35 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from Kekik.cli import konsol
4
+ from yt_dlp.extractor import gen_extractors
5
+
6
+ # Global cache (module-level singleton)
7
+ _YTDLP_EXTRACTORS_CACHE = None
8
+ _CACHE_INITIALIZED = False
9
+
10
+ def get_ytdlp_extractors() -> list:
11
+ """
12
+ yt-dlp extractorlarını cache'le ve döndür
13
+
14
+ Returns:
15
+ list: yt-dlp extractor sınıfları
16
+ """
17
+ global _YTDLP_EXTRACTORS_CACHE, _CACHE_INITIALIZED
18
+
19
+ if _CACHE_INITIALIZED:
20
+ return _YTDLP_EXTRACTORS_CACHE
21
+
22
+ try:
23
+ extractors = list(gen_extractors())
24
+ extractors = [ie for ie in extractors if ie.ie_key() != 'Generic']
25
+
26
+ _YTDLP_EXTRACTORS_CACHE = extractors
27
+ _CACHE_INITIALIZED = True
28
+
29
+ return extractors
30
+
31
+ except Exception as e:
32
+ konsol.log(f"[red][⚠] yt-dlp extractor cache hatası: {e}[/red]")
33
+ _YTDLP_EXTRACTORS_CACHE = []
34
+ _CACHE_INITIALIZED = True
35
+ return []
@@ -1,30 +1,55 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from ..CLI import konsol
4
- from .ExtractorModels import ExtractResult
3
+ from ...CLI import konsol
4
+ from ..Extractor.ExtractorModels import ExtractResult
5
5
  import subprocess, os
6
6
 
7
7
  class MediaHandler:
8
- def __init__(self, title: str = "KekikStream", headers: dict = None):
9
- if headers is None:
10
- headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)"}
11
-
12
- self.headers = headers
8
+ def __init__(self, title: str = "KekikStream"):
13
9
  self.title = title
10
+ self.headers = {}
14
11
 
15
12
  def play_media(self, extract_data: ExtractResult):
16
- if self.headers.get("User-Agent") == "googleusercontent":
13
+ # user-agent ekle (varsayılan veya extract_data'dan)
14
+ user_agent = extract_data.user_agent or "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)"
15
+ self.headers["user-agent"] = user_agent
16
+
17
+ # referer ekle
18
+ if extract_data.referer:
19
+ self.headers["referer"] = extract_data.referer
20
+
21
+ # Özel Durumlar (RecTV vs. Googleusercontent)
22
+ if user_agent in ["googleusercontent", "Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0"]:
17
23
  return self.play_with_ytdlp(extract_data)
18
24
 
25
+ # İşletim sistemine göre oynatıcı seç (Android durumu)
19
26
  if subprocess.check_output(['uname', '-o']).strip() == b'Android':
20
27
  return self.play_with_android_mxplayer(extract_data)
21
28
 
22
- if "Cookie" in self.headers or extract_data.subtitles:
23
- return self.play_with_mpv(extract_data)
29
+ # Oynatıcı öncelik sırası (fallback zincirleme)
30
+ players = [
31
+ ("MPV", self.play_with_mpv),
32
+ ("VLC", self.play_with_vlc),
33
+ ("yt-dlp", self.play_with_ytdlp)
34
+ ]
35
+
36
+ # Fallback zincirleme
37
+ for player_name, player_func in players:
38
+ try:
39
+ result = player_func(extract_data)
40
+ if result or result is None: # None = MPV (exception yok)
41
+ konsol.log(f"[green][✓] {player_name} ile başarılı[/green]")
42
+ return True
43
+ except Exception as e:
44
+ konsol.log(f"[yellow][⚠] {player_name} hatası: {e}[/yellow]")
45
+ continue
24
46
 
25
- return self.play_with_vlc(extract_data)
47
+ konsol.print("[red][✗] Hiçbir oynatıcı çalışmadı![/red]")
48
+ return False
26
49
 
27
50
  def play_with_vlc(self, extract_data: ExtractResult):
51
+ konsol.log(f"[yellow][»] VLC ile Oynatılıyor : {extract_data.url}")
52
+ # konsol.print(self.headers)
28
53
  try:
29
54
  vlc_command = ["vlc", "--quiet"]
30
55
 
@@ -34,11 +59,11 @@ class MediaHandler:
34
59
  f"--input-title-format={self.title}"
35
60
  ])
36
61
 
37
- if "User-Agent" in self.headers:
38
- vlc_command.append(f"--http-user-agent={self.headers.get('User-Agent')}")
62
+ if "user-agent" in self.headers:
63
+ vlc_command.append(f"--http-user-agent={self.headers.get('user-agent')}")
39
64
 
40
- if "Referer" in self.headers:
41
- vlc_command.append(f"--http-referrer={self.headers.get('Referer')}")
65
+ if "referer" in self.headers:
66
+ vlc_command.append(f"--http-referrer={self.headers.get('referer')}")
42
67
 
43
68
  vlc_command.extend(
44
69
  f"--sub-file={subtitle.url}" for subtitle in extract_data.subtitles
@@ -48,16 +73,21 @@ class MediaHandler:
48
73
  with open(os.devnull, "w") as devnull:
49
74
  subprocess.run(vlc_command, stdout=devnull, stderr=devnull, check=True)
50
75
 
76
+ return True
51
77
  except subprocess.CalledProcessError as hata:
52
78
  konsol.print(f"[red]VLC oynatma hatası: {hata}[/red]")
53
79
  konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
80
+ return False
54
81
  except FileNotFoundError:
55
82
  konsol.print("[red]VLC bulunamadı! VLC kurulu olduğundan emin olun.[/red]")
56
- konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
83
+ # konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
84
+ return False
57
85
 
58
86
  def play_with_mpv(self, extract_data: ExtractResult):
87
+ konsol.log(f"[yellow][»] MPV ile Oynatılıyor : {extract_data.url}")
88
+ # konsol.print(self.headers)
59
89
  try:
60
- mpv_command = ["mpv", "--really-quiet"]
90
+ mpv_command = ["mpv"]
61
91
 
62
92
  if self.title:
63
93
  mpv_command.append(f"--force-media-title={self.title}")
@@ -73,16 +103,21 @@ class MediaHandler:
73
103
  with open(os.devnull, "w") as devnull:
74
104
  subprocess.run(mpv_command, stdout=devnull, stderr=devnull, check=True)
75
105
 
106
+ return True
76
107
  except subprocess.CalledProcessError as hata:
77
108
  konsol.print(f"[red]mpv oynatma hatası: {hata}[/red]")
78
109
  konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
110
+ return False
79
111
  except FileNotFoundError:
80
112
  konsol.print("[red]mpv bulunamadı! mpv kurulu olduğundan emin olun.[/red]")
81
113
  konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
114
+ return False
82
115
 
83
116
  def play_with_ytdlp(self, extract_data: ExtractResult):
117
+ konsol.log(f"[yellow][»] yt-dlp ile Oynatılıyor : {extract_data.url}")
118
+ # konsol.print(self.headers)
84
119
  try:
85
- ytdlp_command = ["yt-dlp", "--quiet", "--no-warnings", "--downloader", "ffmpeg", "--hls-use-mpegts"]
120
+ ytdlp_command = ["yt-dlp", "--quiet", "--no-warnings"]
86
121
 
87
122
  for key, value in self.headers.items():
88
123
  ytdlp_command.extend(["--add-header", f"{key}: {value}"])
@@ -104,14 +139,19 @@ class MediaHandler:
104
139
  with subprocess.Popen(ytdlp_command, stdout=subprocess.PIPE) as ytdlp_proc:
105
140
  subprocess.run(mpv_command, stdin=ytdlp_proc.stdout, check=True)
106
141
 
142
+ return True
107
143
  except subprocess.CalledProcessError as hata:
108
- print(f"[red]Oynatma hatası: {hata}[/red]")
109
- print({"title": self.title, "url": extract_data.url, "headers": self.headers})
144
+ konsol.print(f"[red]Oynatma hatası: {hata}[/red]")
145
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
146
+ return False
110
147
  except FileNotFoundError:
111
- print("[red]yt-dlp veya mpv bulunamadı! Kurulumlarından emin olun.[/red]")
112
- print({"title": self.title, "url": extract_data.url, "headers": self.headers})
148
+ konsol.print("[red]yt-dlp veya mpv bulunamadı! Kurulumlarından emin olun.[/red]")
149
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
150
+ return False
113
151
 
114
152
  def play_with_android_mxplayer(self, extract_data: ExtractResult):
153
+ konsol.log(f"[yellow][»] MxPlayer ile Oynatılıyor : {extract_data.url}")
154
+ # konsol.print(self.headers)
115
155
  paketler = [
116
156
  "com.mxtech.videoplayer.ad/.ActivityScreen", # Free sürüm
117
157
  "com.mxtech.videoplayer.pro/.ActivityScreen" # Pro sürüm
@@ -132,11 +172,12 @@ class MediaHandler:
132
172
  with open(os.devnull, "w") as devnull:
133
173
  subprocess.run(android_command, stdout=devnull, stderr=devnull, check=True)
134
174
 
135
- return
136
-
175
+ return True
137
176
  except subprocess.CalledProcessError as hata:
138
177
  konsol.print(f"[red]{paket} oynatma hatası: {hata}[/red]")
139
178
  konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
179
+ return False
140
180
  except FileNotFoundError:
141
181
  konsol.print(f"Paket: {paket}, Hata: MX Player kurulu değil")
142
- konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
182
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
183
+ return False
@@ -1,6 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from ..Core import MediaHandler
3
+ from .MediaHandler import MediaHandler
4
4
 
5
5
  class MediaManager:
6
6
  def __init__(self):
@@ -12,8 +12,5 @@ class MediaManager:
12
12
  def get_title(self):
13
13
  return self.media_handler.title
14
14
 
15
- def set_headers(self, headers):
16
- self.media_handler.headers.update(headers)
17
-
18
15
  def play_media(self, extract_data):
19
16
  self.media_handler.play_media(extract_data)
@@ -0,0 +1,122 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from abc import ABC, abstractmethod
4
+ from cloudscraper import CloudScraper
5
+ from httpx import AsyncClient
6
+ from .PluginModels import MainPageResult, SearchResult, MovieInfo
7
+ from ..Media.MediaHandler import MediaHandler
8
+ from ..Extractor.ExtractorManager import ExtractorManager
9
+ from urllib.parse import urljoin
10
+ import re
11
+
12
+ class PluginBase(ABC):
13
+ name = "Plugin"
14
+ language = "tr"
15
+ main_url = "https://example.com"
16
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
17
+ description = "No description provided."
18
+
19
+ main_page = {}
20
+
21
+ async def url_update(self, new_url: str):
22
+ self.favicon = self.favicon.replace(self.main_url, new_url)
23
+ self.main_page = {url.replace(self.main_url, new_url): category for url, category in self.main_page.items()}
24
+ self.main_url = new_url
25
+
26
+ def __init__(self):
27
+ # cloudscraper - for bypassing Cloudflare
28
+ self.cloudscraper = CloudScraper()
29
+
30
+ # httpx - lightweight and safe for most HTTP requests
31
+ self.httpx = AsyncClient(
32
+ timeout = 3,
33
+ follow_redirects = True
34
+ )
35
+ self.httpx.headers.update(self.cloudscraper.headers)
36
+ self.httpx.cookies.update(self.cloudscraper.cookies)
37
+ self.httpx.headers.update({
38
+ "User-Agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 15.7; rv:135.0) Gecko/20100101 Firefox/135.0",
39
+ "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
40
+ })
41
+
42
+ self.media_handler = MediaHandler()
43
+ self.ex_manager = ExtractorManager()
44
+
45
+ @abstractmethod
46
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
47
+ """Ana sayfadaki popüler içerikleri döndürür."""
48
+ pass
49
+
50
+ @abstractmethod
51
+ async def search(self, query: str) -> list[SearchResult]:
52
+ """Kullanıcı arama sorgusuna göre sonuç döndürür."""
53
+ pass
54
+
55
+ @abstractmethod
56
+ async def load_item(self, url: str) -> MovieInfo:
57
+ """Bir medya öğesi hakkında detaylı bilgi döndürür."""
58
+ pass
59
+
60
+ @abstractmethod
61
+ async def load_links(self, url: str) -> list[dict]:
62
+ """
63
+ Bir medya öğesi için oynatma bağlantılarını döndürür.
64
+
65
+ Args:
66
+ url: Medya URL'si
67
+
68
+ Returns:
69
+ Dictionary listesi, her biri şu alanları içerir:
70
+ - url (str, zorunlu): Video URL'si
71
+ - name (str, zorunlu): Gösterim adı (tüm bilgileri içerir)
72
+ - referer (str, opsiyonel): Referer header
73
+ - subtitles (list, opsiyonel): Altyazı listesi
74
+
75
+ Example:
76
+ [
77
+ {
78
+ "url": "https://example.com/video.m3u8",
79
+ "name": "HDFilmCehennemi | 1080p TR Dublaj"
80
+ }
81
+ ]
82
+ """
83
+ pass
84
+
85
+ async def close(self):
86
+ """Close HTTP client."""
87
+ await self.httpx.aclose()
88
+
89
+ def fix_url(self, url: str) -> str:
90
+ if not url:
91
+ return ""
92
+
93
+ if url.startswith("http") or url.startswith("{\""):
94
+ return url
95
+
96
+ return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
97
+
98
+ @staticmethod
99
+ def clean_title(title: str) -> str:
100
+ suffixes = [
101
+ " izle",
102
+ " full film",
103
+ " filmini full",
104
+ " full türkçe",
105
+ " alt yazılı",
106
+ " altyazılı",
107
+ " tr dublaj",
108
+ " hd türkçe",
109
+ " türkçe dublaj",
110
+ " yeşilçam ",
111
+ " erotik fil",
112
+ " türkçe",
113
+ " yerli",
114
+ " tüekçe dublaj",
115
+ ]
116
+
117
+ cleaned_title = title.strip()
118
+
119
+ for suffix in suffixes:
120
+ cleaned_title = re.sub(f"{re.escape(suffix)}.*$", "", cleaned_title, flags=re.IGNORECASE).strip()
121
+
122
+ return cleaned_title