KekikStream 1.4.4__py3-none-any.whl → 2.0.2__py3-none-any.whl

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 (81) hide show
  1. KekikStream/CLI/pypi_kontrol.py +6 -6
  2. KekikStream/Core/Extractor/ExtractorBase.py +13 -12
  3. KekikStream/Core/Extractor/ExtractorLoader.py +25 -17
  4. KekikStream/Core/Extractor/ExtractorManager.py +53 -9
  5. KekikStream/Core/Extractor/ExtractorModels.py +5 -7
  6. KekikStream/Core/Extractor/YTDLPCache.py +35 -0
  7. KekikStream/Core/Media/MediaHandler.py +52 -31
  8. KekikStream/Core/Media/MediaManager.py +0 -3
  9. KekikStream/Core/Plugin/PluginBase.py +47 -21
  10. KekikStream/Core/Plugin/PluginLoader.py +11 -7
  11. KekikStream/Core/Plugin/PluginModels.py +25 -25
  12. KekikStream/Core/__init__.py +1 -0
  13. KekikStream/Extractors/CloseLoad.py +6 -26
  14. KekikStream/Extractors/ContentX_.py +40 -0
  15. KekikStream/Extractors/DzenRu.py +38 -0
  16. KekikStream/Extractors/ExPlay.py +53 -0
  17. KekikStream/Extractors/FirePlayer.py +60 -0
  18. KekikStream/Extractors/HDPlayerSystem.py +41 -0
  19. KekikStream/Extractors/JetTv.py +45 -0
  20. KekikStream/Extractors/MailRu.py +2 -4
  21. KekikStream/Extractors/MixTiger.py +57 -0
  22. KekikStream/Extractors/MolyStream.py +25 -7
  23. KekikStream/Extractors/Odnoklassniki.py +16 -11
  24. KekikStream/Extractors/{OkRuHTTP.py → Odnoklassniki_.py} +5 -1
  25. KekikStream/Extractors/{HDStreamAble.py → PeaceMakerst_.py} +1 -1
  26. KekikStream/Extractors/PixelDrain.py +0 -1
  27. KekikStream/Extractors/PlayerFilmIzle.py +62 -0
  28. KekikStream/Extractors/RapidVid.py +30 -13
  29. KekikStream/Extractors/RapidVid_.py +7 -0
  30. KekikStream/Extractors/SetPlay.py +57 -0
  31. KekikStream/Extractors/SetPrime.py +45 -0
  32. KekikStream/Extractors/SibNet.py +0 -1
  33. KekikStream/Extractors/TurkeyPlayer.py +34 -0
  34. KekikStream/Extractors/VidHide.py +72 -0
  35. KekikStream/Extractors/VidMoly.py +20 -19
  36. KekikStream/Extractors/{VidMolyMe.py → VidMoly_.py} +1 -1
  37. KekikStream/Extractors/VidMoxy.py +0 -1
  38. KekikStream/Extractors/VidPapi.py +89 -0
  39. KekikStream/Extractors/YTDLP.py +177 -0
  40. KekikStream/Extractors/YildizKisaFilm.py +41 -0
  41. KekikStream/Plugins/DiziBox.py +28 -16
  42. KekikStream/Plugins/DiziPal.py +246 -0
  43. KekikStream/Plugins/DiziYou.py +58 -31
  44. KekikStream/Plugins/Dizilla.py +97 -68
  45. KekikStream/Plugins/FilmBip.py +145 -0
  46. KekikStream/Plugins/FilmMakinesi.py +61 -52
  47. KekikStream/Plugins/FilmModu.py +138 -0
  48. KekikStream/Plugins/FullHDFilm.py +164 -0
  49. KekikStream/Plugins/FullHDFilmizlesene.py +38 -37
  50. KekikStream/Plugins/HDFilmCehennemi.py +44 -54
  51. KekikStream/Plugins/JetFilmizle.py +68 -42
  52. KekikStream/Plugins/KultFilmler.py +219 -0
  53. KekikStream/Plugins/RecTV.py +41 -37
  54. KekikStream/Plugins/RoketDizi.py +232 -0
  55. KekikStream/Plugins/SelcukFlix.py +309 -0
  56. KekikStream/Plugins/SezonlukDizi.py +16 -14
  57. KekikStream/Plugins/SineWix.py +39 -30
  58. KekikStream/Plugins/Sinefy.py +238 -0
  59. KekikStream/Plugins/SinemaCX.py +157 -0
  60. KekikStream/Plugins/Sinezy.py +146 -0
  61. KekikStream/Plugins/SuperFilmGeldi.py +121 -0
  62. KekikStream/Plugins/UgurFilm.py +10 -10
  63. KekikStream/__init__.py +296 -319
  64. KekikStream/requirements.txt +3 -4
  65. kekikstream-2.0.2.dist-info/METADATA +309 -0
  66. kekikstream-2.0.2.dist-info/RECORD +82 -0
  67. {kekikstream-1.4.4.dist-info → kekikstream-2.0.2.dist-info}/WHEEL +1 -1
  68. KekikStream/Extractors/FourCX.py +0 -7
  69. KekikStream/Extractors/FourPichive.py +0 -7
  70. KekikStream/Extractors/FourPlayRu.py +0 -7
  71. KekikStream/Extractors/Hotlinger.py +0 -7
  72. KekikStream/Extractors/OkRuSSL.py +0 -7
  73. KekikStream/Extractors/Pichive.py +0 -7
  74. KekikStream/Extractors/PlayRu.py +0 -7
  75. KekikStream/Helpers/Unpack.py +0 -75
  76. KekikStream/Plugins/Shorten.py +0 -225
  77. kekikstream-1.4.4.dist-info/METADATA +0 -108
  78. kekikstream-1.4.4.dist-info/RECORD +0 -63
  79. {kekikstream-1.4.4.dist-info → kekikstream-2.0.2.dist-info}/entry_points.txt +0 -0
  80. {kekikstream-1.4.4.dist-info → kekikstream-2.0.2.dist-info/licenses}/LICENSE +0 -0
  81. {kekikstream-1.4.4.dist-info → kekikstream-2.0.2.dist-info}/top_level.txt +0 -0
KekikStream/__init__.py CHANGED
@@ -2,368 +2,345 @@
2
2
 
3
3
  from .CLI import konsol, cikis_yap, hata_yakala, pypi_kontrol_guncelle
4
4
  from .Core import PluginManager, ExtractorManager, UIManager, MediaManager, PluginBase, ExtractorBase, SeriesInfo
5
- from asyncio import run
5
+ from asyncio import run, TaskGroup, Semaphore
6
6
  from contextlib import suppress
7
7
 
8
8
  class KekikStream:
9
9
  def __init__(self):
10
- """
11
- KekikStream sınıfı, eklenti, çıkarıcı, arayüz ve medya yönetimini yürütür.
12
- """
13
- self.eklentiler_yonetici = PluginManager()
14
- self.cikaricilar_yonetici = ExtractorManager()
15
- self.arayuz_yonetici = UIManager()
16
- self.medya_yonetici = MediaManager()
17
- self.suanki_eklenti: PluginBase = None
18
- self.secilen_sonuc = None
19
- self.dizi = False
20
- self.bolum_baslik = ""
21
-
22
- async def _temizle_ve_baslik_goster(self, baslik: str):
23
- """
24
- Konsolu temizler ve verilen başlıkla bir kural (separator) gösterir.
25
- """
26
- self.arayuz_yonetici.clear_console()
27
- konsol.rule(baslik)
28
-
29
- async def baslat(self):
30
- """
31
- Uygulamayı başlatır: konsolu temizler, başlığı gösterir ve eklenti seçimiyle devam eder.
32
- """
33
- await self._temizle_ve_baslik_goster("[bold cyan]KekikStream Başlatılıyor[/bold cyan]")
34
- # Eklenti kontrolü
35
- if not self.eklentiler_yonetici.get_plugin_names():
36
- return konsol.print("[bold red]Hiçbir eklenti bulunamadı![/bold red]")
10
+ self.plugin = PluginManager()
11
+ self.extractor = ExtractorManager()
12
+ self.ui = UIManager()
13
+ self.media = MediaManager()
14
+
15
+ self.current_plugin: PluginBase = None
16
+ self.is_series = False
17
+ self.series_info: SeriesInfo = None
18
+ self.current_episode_index = -1
19
+ self.episode_title = ""
20
+
21
+ async def show_header(self, title: str):
22
+ """Konsolu temizle ve başlık göster"""
23
+ self.ui.clear_console()
24
+ konsol.rule(title)
25
+
26
+ def update_title(self, new_info: str):
27
+ """Medya başlığına yeni bilgi ekle"""
28
+ if not new_info:
29
+ return
30
+ current = self.media.get_title()
31
+ if new_info not in current:
32
+ self.media.set_title(f"{current} | {new_info}")
33
+
34
+ async def load_media_info(self, url: str, retries=3):
35
+ """Medya bilgilerini yükle"""
36
+ for _ in range(retries):
37
+ with suppress(Exception):
38
+ return await self.current_plugin.load_item(url)
39
+ konsol.print("[bold red]Medya bilgileri yüklenemedi![/bold red]")
40
+ return None
41
+
42
+ async def start(self):
43
+ """Uygulamayı başlat"""
44
+ await self.show_header("[bold cyan]KekikStream[/bold cyan]")
45
+
46
+ if not self.plugin.get_plugin_names():
47
+ konsol.print("[bold red]Eklenti bulunamadı![/bold red]")
48
+ return
37
49
 
38
50
  try:
39
- await self.eklenti_secimi()
51
+ await self.select_plugin()
40
52
  finally:
41
- # Program kapanırken tüm eklentileri kapat
42
- await self.eklentiler_yonetici.close_plugins()
43
-
44
- async def bi_bolum_daha(self):
45
- await self._temizle_ve_baslik_goster(f"[bold cyan]{self.suanki_eklenti.name} » Bi Bölüm Daha?[/bold cyan]")
46
- return await self.sonuc_detaylari_goster(self.secilen_sonuc)
47
-
48
- async def icerik_bitti(self):
49
- return await self.bi_bolum_daha() if self.dizi else await self.baslat()
50
-
51
- async def sonuc_bulunamadi(self):
52
- """
53
- Arama sonucunda hiçbir içerik bulunamadığında kullanıcıya seçenekler sunar.
54
- """
55
- secim = await self.arayuz_yonetici.select_from_list(
56
- message = "Ne yapmak istersiniz?",
57
- choices = ["Tüm Eklentilerde Ara", "Ana Menü", "Çıkış"]
58
- )
53
+ await self.plugin.close_plugins()
59
54
 
60
- match secim:
61
- case "Tüm Eklentilerde Ara":
62
- await self.tum_eklentilerde_arama()
63
- case "Ana Menü":
64
- await self.baslat()
65
- case "Çıkış":
66
- cikis_yap(False)
67
-
68
- async def eklenti_secimi(self):
69
- """
70
- Kullanıcıdan eklenti seçimi alır ve seçime göre arama işlemini başlatır.
71
- """
72
- eklenti_adi = await self.arayuz_yonetici.select_from_fuzzy(
73
- message = "Arama yapılacak eklentiyi seçin:",
74
- choices = ["Tüm Eklentilerde Ara", *self.eklentiler_yonetici.get_plugin_names()]
55
+ async def select_plugin(self):
56
+ """Eklenti seçimi"""
57
+ plugin_name = await self.ui.select_from_fuzzy(
58
+ message = "Eklenti seçin:",
59
+ choices = ["Tüm Eklentilerde Ara", *self.plugin.get_plugin_names()]
75
60
  )
76
61
 
77
- if eklenti_adi == "Tüm Eklentilerde Ara":
78
- await self.tum_eklentilerde_arama()
62
+ if plugin_name == "Tüm Eklentilerde Ara":
63
+ await self.search_all_plugins()
79
64
  else:
80
- self.suanki_eklenti = self.eklentiler_yonetici.select_plugin(eklenti_adi)
81
- await self.eklenti_ile_arama()
82
-
83
- async def eklenti_ile_arama(self):
84
- """
85
- Seçilen eklentiyle arama yapar; kullanıcıdan sorgu alır, sonuçları listeler ve seçim sonrası detayları gösterir.
86
- """
87
- await self._temizle_ve_baslik_goster(f"[bold cyan]{self.suanki_eklenti.name} Eklentisinde Arama Yapın[/bold cyan]")
88
-
89
- # Kullanıcıdan sorgu al ve ara
90
- sorgu = await self.arayuz_yonetici.prompt_text("Arama sorgusu girin:")
91
- sonuclar = await self.suanki_eklenti.search(sorgu)
92
-
93
- if not sonuclar:
94
- konsol.print("[bold red]Arama sonucu bulunamadı![/bold red]")
95
- return await self.sonuc_bulunamadi()
96
-
97
- secilen_sonuc = await self.eklenti_sonuc_secimi(sonuclar)
98
- if secilen_sonuc:
99
- await self.sonuc_detaylari_goster({"plugin": self.suanki_eklenti.name, "url": secilen_sonuc})
100
-
101
- async def eklenti_sonuc_secimi(self, sonuclar: list):
102
- """
103
- Arama sonuçlarından kullanıcıya seçim yaptırır.
104
- """
105
- return await self.arayuz_yonetici.select_from_fuzzy(
106
- message = "İçerik sonuçlarından birini seçin:",
107
- choices = [{"name": sonuc.title, "value": sonuc.url} for sonuc in sonuclar]
65
+ self.current_plugin = self.plugin.select_plugin(plugin_name)
66
+ await self.search_in_plugin()
67
+
68
+ async def search_in_plugin(self):
69
+ """Seçili eklentide ara"""
70
+ await self.show_header(f"[bold cyan]{self.current_plugin.name}[/bold cyan]")
71
+
72
+ query = await self.ui.prompt_text("Arama sorgusu:")
73
+ results = await self.current_plugin.search(query)
74
+
75
+ if not results:
76
+ konsol.print("[bold red]Sonuç bulunamadı![/bold red]")
77
+ return await self.handle_no_results()
78
+
79
+ choice = await self.ui.select_from_fuzzy(
80
+ message = "Sonuç seçin:",
81
+ choices = [{"name": r.title, "value": r.url} for r in results]
108
82
  )
109
-
110
- async def tum_eklentilerde_arama(self):
111
- """
112
- Tüm eklentilerde arama yapar ve sonuçlara göre işlem yapar.
113
- """
114
- await self._temizle_ve_baslik_goster("[bold cyan]Tüm Eklentilerde Arama Yapın[/bold cyan]")
115
- sorgu = await self.arayuz_yonetici.prompt_text("Arama sorgusu girin:")
116
- sonuclar = await self.tum_eklentilerde_arama_sorgula(sorgu)
117
-
118
- if not sonuclar:
119
- return await self.sonuc_bulunamadi()
120
-
121
- secilen_sonuc = await self.tum_sonuc_secimi(sonuclar)
122
-
123
- if secilen_sonuc:
124
- await self.sonuc_detaylari_goster(secilen_sonuc)
125
-
126
- async def tum_eklentilerde_arama_sorgula(self, sorgu: str) -> list:
127
- """
128
- Tüm eklentilerde arama yapar ve bulunan sonuçları listeler.
129
- """
130
- tum_sonuclar = []
131
- # Her eklentide arama yap
132
- for eklenti_adi, eklenti in self.eklentiler_yonetici.plugins.items():
133
- # Eklenti türü kontrolü
134
- if not isinstance(eklenti, PluginBase):
135
- konsol.print(f"[yellow][!] {eklenti_adi} geçerli bir PluginBase değil, atlanıyor...[/yellow]")
136
- continue
137
-
138
- if eklenti_adi in ["Shorten"]:
139
- continue
140
-
141
- konsol.log(f"[yellow][~] {eklenti_adi:<19} aranıyor...[/]")
142
- try:
143
- sonuclar = await eklenti.search(sorgu)
144
- if sonuclar:
145
- # Sonuçları listeye ekle
146
- tum_sonuclar.extend(
147
- [
148
- {
149
- "plugin" : eklenti_adi,
150
- "title" : sonuc.title,
151
- "url" : sonuc.url,
152
- "poster" : sonuc.poster
153
- }
154
- for sonuc in sonuclar
83
+
84
+ if choice:
85
+ await self.show_media_details({"plugin": self.current_plugin.name, "url": choice})
86
+
87
+ async def search_all_plugins(self):
88
+ """Tüm eklentilerde ara"""
89
+ await self.show_header("[bold cyan]Tüm Eklentilerde Ara[/bold cyan]")
90
+
91
+ query = await self.ui.prompt_text("Arama sorgusu:")
92
+ all_results = []
93
+
94
+ # Maksimum 5 eşzamanlı arama için semaphore
95
+ semaphore = Semaphore(5)
96
+
97
+ async def search_plugin(name: str, plugin: PluginBase):
98
+ """Tek bir plugin'de ara (semaphore ile sınırlandırılmış)"""
99
+ async with semaphore:
100
+ konsol.log(f"[yellow][~] {name:<19} aranıyor...[/]")
101
+ try:
102
+ results = await plugin.search(query)
103
+ if results:
104
+ return [
105
+ {"plugin": name, "title": r.title, "url": r.url, "poster": r.poster}
106
+ for r in results
155
107
  ]
156
- )
157
- except Exception as hata:
158
- konsol.print(f"[bold red]{eklenti_adi} » hata oluştu: {hata}[/bold red]")
159
-
160
- if not tum_sonuclar:
161
- konsol.print("[bold red]Hiçbir sonuç bulunamadı![/bold red]")
162
- await self.sonuc_bulunamadi()
163
- return tum_sonuclar
164
-
165
- async def tum_sonuc_secimi(self, sonuclar: list):
166
- """
167
- Tüm arama sonuçlarından kullanıcıya seçim yaptırır.
168
- """
169
- secenekler = [
170
- {"name": f"[{sonuc['plugin']}]".ljust(21) + f" » {sonuc['title']}", "value": sonuc}
171
- for sonuc in sonuclar
172
- ]
108
+ except Exception as e:
109
+ konsol.print(f"[bold red]{name} hatası: {e}[/bold red]")
110
+ return []
173
111
 
174
- return await self.arayuz_yonetici.select_from_fuzzy(
175
- message = "Arama sonuçlarından bir içerik seçin:",
176
- choices = secenekler
177
- )
112
+ # Tüm plugin'leri paralel olarak ara
113
+ async with TaskGroup() as tg:
114
+ tasks = []
115
+ for name, plugin in self.plugin.plugins.items():
116
+ tasks.append(tg.create_task(search_plugin(name, plugin)))
178
117
 
179
- async def __medya_bilgisi_yukle(self, url: str, deneme: int = 3):
180
- """
181
- Belirtilen URL için medya bilgilerini, belirlenen deneme sayısı kadar yüklemeye çalışır.
182
- """
183
- for _ in range(deneme):
184
- with suppress(Exception):
185
- return await self.suanki_eklenti.load_item(url)
118
+ # Sonuçları topla
119
+ for task in tasks:
120
+ all_results.extend(task.result())
186
121
 
187
- konsol.print("[bold red]Medya bilgileri yüklenemedi![/bold red]")
188
- return None
122
+ if not all_results:
123
+ return await self.handle_no_results()
189
124
 
190
- async def sonuc_detaylari_goster(self, secilen_sonuc):
191
- """
192
- Seçilen sonucun detaylarını gösterir; medya bilgilerini yükler, dizi ise bölüm seçimi sağlar.
193
- """
194
- self.secilen_sonuc = secilen_sonuc
195
- try:
196
- # Seçilen sonucun detaylarını al
197
- if isinstance(secilen_sonuc, dict) and "plugin" in secilen_sonuc:
198
- eklenti_adi = secilen_sonuc["plugin"]
199
- url = secilen_sonuc["url"]
125
+ choice = await self.ui.select_from_fuzzy(
126
+ message = "Sonuç seçin:",
127
+ choices = [
128
+ {"name": f"[{r['plugin']}]".ljust(21) + f" » {r['title']}", "value": r}
129
+ for r in all_results
130
+ ]
131
+ )
132
+
133
+ if choice:
134
+ await self.show_media_details(choice)
200
135
 
201
- self.suanki_eklenti = self.eklentiler_yonetici.select_plugin(eklenti_adi)
136
+ async def show_media_details(self, choice):
137
+ """Seçilen medyanın detaylarını göster"""
138
+ try:
139
+ if isinstance(choice, dict) and "plugin" in choice:
140
+ self.current_plugin = self.plugin.select_plugin(choice["plugin"])
141
+ url = choice["url"]
202
142
  else:
203
- url = secilen_sonuc
143
+ url = choice
204
144
 
205
- medya_bilgi = await self.__medya_bilgisi_yukle(url)
206
- if not medya_bilgi:
207
- return await self.sonuc_bulunamadi()
145
+ media_info = await self.load_media_info(url)
146
+ if not media_info:
147
+ return await self.handle_no_results()
208
148
 
209
- except Exception as hata:
210
- konsol.log(secilen_sonuc)
211
- return hata_yakala(hata)
149
+ except Exception as e:
150
+ return hata_yakala(e)
212
151
 
213
- # Medya bilgilerini göster ve başlığı ayarla
214
- self.medya_yonetici.set_title(f"{self.suanki_eklenti.name} | {medya_bilgi.title}")
215
- self.arayuz_yonetici.display_media_info(f"{self.suanki_eklenti.name} | {medya_bilgi.title}", medya_bilgi)
152
+ self.media.set_title(f"{self.current_plugin.name} | {media_info.title}")
153
+ self.ui.display_media_info(f"{self.current_plugin.name} | {media_info.title}", media_info)
216
154
 
217
- # Eğer medya bilgisi dizi ise bölüm seçimi yapılır
218
- if isinstance(medya_bilgi, SeriesInfo):
219
- self.dizi = True
220
- await self.dizi_bolum_secimi(medya_bilgi)
155
+ if isinstance(media_info, SeriesInfo):
156
+ self.is_series = True
157
+ self.series_info = media_info
158
+ await self.select_episode(media_info)
221
159
  else:
222
- self.dizi = False
223
- self.bolum_baslik = ""
224
- baglantilar = await self.suanki_eklenti.load_links(medya_bilgi.url)
225
- await self.baglanti_secenekleri_goster(baglantilar)
226
-
227
- async def dizi_bolum_secimi(self, medya_bilgi: SeriesInfo):
228
- """
229
- Dizi içeriği için bölüm seçimi yapar ve seçilen bölümün bağlantılarını yükler.
230
- """
231
- bolumler = {
232
- bolum.url: f"{bolum.season}. Sezon {bolum.episode}. Bölüm" + (f" - {bolum.title}" if bolum.title else "")
233
- for bolum in medya_bilgi.episodes
234
- }
235
- secilen_bolum = await self.arayuz_yonetici.select_from_fuzzy(
236
- message = "İzlemek istediğiniz bölümü seçin:",
160
+ self.reset_series_state()
161
+ links = await self.current_plugin.load_links(media_info.url)
162
+ await self.show_link_options(links)
163
+
164
+ def reset_series_state(self):
165
+ """Dizi durumunu sıfırla"""
166
+ self.is_series = False
167
+ self.series_info = None
168
+ self.current_episode_index = -1
169
+ self.episode_title = ""
170
+
171
+ async def select_episode(self, series_info: SeriesInfo):
172
+ """Bölüm seç"""
173
+ selected = await self.ui.select_from_fuzzy(
174
+ message = "Bölüm seçin:",
237
175
  choices = [
238
- {"name": f"{bolum.season}. Sezon {bolum.episode}. Bölüm" + (f" - {bolum.title}" if bolum.title else ""), "value": bolum.url}
239
- for bolum in medya_bilgi.episodes
176
+ {
177
+ "name" : f"{ep.season}. Sezon {ep.episode}. Bölüm" + (f" - {ep.title}" if ep.title else ""),
178
+ "value" : ep.url
179
+ }
180
+ for ep in series_info.episodes
240
181
  ]
241
182
  )
242
- if secilen_bolum:
243
- self.bolum_baslik = bolumler[secilen_bolum]
244
-
245
- baglantilar = await self.suanki_eklenti.load_links(secilen_bolum)
246
- await self.baglanti_secenekleri_goster(baglantilar)
247
-
248
- async def baglanti_secenekleri_goster(self, baglantilar):
249
- """
250
- Bağlantı seçeneklerini kullanıcıya sunar ve seçilen bağlantıya göre oynatma işlemini gerçekleştirir.
251
- """
252
- if not baglantilar:
253
- konsol.print("[bold red]Hiçbir bağlantı bulunamadı![/bold red]")
254
- return await self.sonuc_bulunamadi()
255
-
256
- # Doğrudan oynatma seçeneği
257
- if hasattr(self.suanki_eklenti, "play") and callable(getattr(self.suanki_eklenti, "play", None)):
258
- return await self.direkt_oynat(baglantilar)
259
-
260
- # Bağlantıları çıkarıcılarla eşleştir
261
- haritalama = self.cikaricilar_yonetici.map_links_to_extractors(baglantilar)
262
-
263
- # Uygun çıkarıcı kontrolü
264
- if not haritalama:
265
- konsol.print("[bold red]Hiçbir Extractor bulunamadı![/bold red]")
266
- konsol.print(baglantilar)
267
- return await self.sonuc_bulunamadi()
268
-
269
- # Kullanıcı seçenekleri
270
- secim = await self.arayuz_yonetici.select_from_list(
271
- message = "Ne yapmak istersiniz?",
272
- choices = ["İzle", "Tüm Eklentilerde Ara", "Ana Menü"]
273
- )
274
183
 
275
- match secim:
276
- case "İzle":
277
- secilen_link = await self.arayuz_yonetici.select_from_list(
278
- message = "İzlemek için bir bağlantı seçin:",
279
- choices = [{"name": cikarici_adi, "value": link} for link, cikarici_adi in haritalama.items()]
280
- )
281
- if secilen_link:
282
- await self.extractor_ile_oynat(secilen_link)
184
+ if not selected:
185
+ return await self.content_finished()
186
+
187
+ # Bölüm bilgilerini kaydet
188
+ for idx, ep in enumerate(series_info.episodes):
189
+ if ep.url == selected:
190
+ self.current_episode_index = idx
191
+ self.episode_title = (f"{ep.season}. Sezon {ep.episode}. Bölüm" + (f" - {ep.title}" if ep.title else ""))
192
+ break
193
+
194
+ links = await self.current_plugin.load_links(selected)
195
+ await self.show_link_options(links)
196
+
197
+ async def play_next_episode(self):
198
+ """Sonraki bölümü oynat"""
199
+ self.current_episode_index += 1
200
+ next_ep = self.series_info.episodes[self.current_episode_index]
201
+ self.episode_title = (f"{next_ep.season}. Sezon {next_ep.episode}. Bölüm" + (f" - {next_ep.title}" if next_ep.title else ""))
202
+ links = await self.current_plugin.load_links(next_ep.url)
203
+ await self.show_link_options(links)
204
+
205
+ async def ask_next_episode(self):
206
+ """Dizi bittikten sonra ne yapılsın?"""
207
+ await self.show_header(f"[bold cyan]{self.current_plugin.name}[/bold cyan]")
208
+ self.ui.display_media_info(f"{self.current_plugin.name} | {self.series_info.title}", self.series_info)
209
+ konsol.print(f"[yellow][+][/yellow] [bold green]{self.episode_title}[/bold green] izlendi!")
210
+
211
+ options = ["Bölüm Seç", "Ana Menü", "Çıkış"]
212
+ if self.current_episode_index + 1 < len(self.series_info.episodes):
213
+ options.insert(0, "Sonraki Bölüm")
214
+ else:
215
+ konsol.print("[bold yellow]Son bölümdü![/bold yellow]")
283
216
 
284
- case "Tüm Eklentilerde Ara":
285
- await self.tum_eklentilerde_arama()
217
+ choice = await self.ui.select_from_list("Ne yapmak istersiniz?", options)
286
218
 
287
- case _:
288
- await self.baslat()
219
+ match choice:
220
+ case "Sonraki Bölüm":
221
+ await self.play_next_episode()
222
+ case "Bölüm Seç":
223
+ await self.select_episode(self.series_info)
224
+ case "Ana Menü":
225
+ await self.start()
226
+ case "Çıkış":
227
+ cikis_yap(False)
289
228
 
290
- async def direkt_oynat(self, baglantilar):
291
- """
292
- Extractor eşleşmesi yoksa, doğrudan oynatma seçeneği sunar.
293
- """
294
- secilen_link = await self.arayuz_yonetici.select_from_list(
295
- message = "Doğrudan oynatmak için bir bağlantı seçin:",
296
- choices = [
297
- {"name": value["ext_name"], "value": key}
298
- for key, value in self.suanki_eklenti._data.items() if key in baglantilar
299
- ]
229
+ async def show_link_options(self, links: list[dict]):
230
+ """Bağlantı seçeneklerini göster"""
231
+ if not links:
232
+ konsol.print("[bold red]Bağlantı bulunamadı![/bold red]")
233
+ return await self.handle_no_results()
234
+
235
+ # Direkt oynatma varsa
236
+ if hasattr(self.current_plugin, "play"):
237
+ return await self.play_direct(links)
238
+
239
+ # Extractor ile oynat
240
+ url_list = [link["url"] for link in links]
241
+ mapping = self.extractor.map_links_to_extractors(url_list)
242
+
243
+ if not mapping:
244
+ konsol.print("[bold red]Extractor bulunamadı![/bold red]")
245
+ return await self.handle_no_results()
246
+
247
+ choice = await self.ui.select_from_list("Ne yapmak istersiniz?", ["İzle", "Tüm Eklentilerde Ara", "Ana Menü"])
248
+
249
+ match choice:
250
+ case "İzle":
251
+ await self.play_with_extractor(links, mapping)
252
+ case "Tüm Eklentilerde Ara":
253
+ await self.search_all_plugins()
254
+ case "Ana Menü":
255
+ await self.start()
256
+
257
+ async def play_direct(self, links: list[dict]):
258
+ """Plugin'in kendi metoduyla oynat"""
259
+ selected = await self.ui.select_from_list(
260
+ message = "Bağlantı seçin:",
261
+ choices = [{"name": link.get("name", "Bilinmiyor"), "value": link} for link in links]
300
262
  )
301
- if not secilen_link:
302
- return await self.icerik_bitti()
303
-
304
- data = self.suanki_eklenti._data.get(secilen_link, {})
305
- await self.suanki_eklenti.play(
306
- name = data.get("name"),
307
- url = secilen_link,
308
- referer = data.get("referer"),
309
- subtitles = data.get("subtitles")
263
+
264
+ if not selected:
265
+ return await self.content_finished()
266
+
267
+ self.update_title(self.episode_title)
268
+ self.update_title(selected.get("name"))
269
+
270
+ await self.current_plugin.play(
271
+ name = self.media.get_title(),
272
+ url = selected.get("url"),
273
+ user_agent = selected.get("user_agent"),
274
+ referer = selected.get("referer"),
275
+ subtitles = selected.get("subtitles", [])
310
276
  )
277
+ return await self.content_finished()
311
278
 
312
- return await self.icerik_bitti()
279
+ async def play_with_extractor(self, links: list[dict], mapping: dict):
280
+ """Extractor ile oynat"""
281
+ options = [
282
+ {"name": link.get("name", mapping[link["url"]]), "value": link}
283
+ for link in links if link["url"] in mapping
284
+ ]
313
285
 
314
- async def extractor_ile_oynat(self, secilen_link: str):
315
- """
316
- Seçilen bağlantıya göre medya oynatma işlemini gerçekleştirir.
317
- """
318
- # Uygun çıkarıcıyı bul
319
- cikarici: ExtractorBase = self.cikaricilar_yonetici.find_extractor(secilen_link)
320
- if not cikarici:
321
- return konsol.print("[bold red]Uygun Extractor bulunamadı.[/bold red]")
286
+ if not options:
287
+ konsol.print("[bold red]İzlenebilir bağlantı yok![/bold red]")
288
+ return await self.content_finished()
322
289
 
323
- try:
324
- # Medya bilgilerini çıkar
325
- extract_data = await cikarici.extract(secilen_link, referer=self.suanki_eklenti.main_url)
326
- except Exception as hata:
327
- konsol.print(f"[bold red]{cikarici.name} » hata oluştu: {hata}[/bold red]")
328
- return await self.sonuc_bulunamadi()
329
-
330
- secilen_data = await self.__baglanti_secimi_yap(extract_data)
331
- if not secilen_data:
332
- return
290
+ selected = await self.ui.select_from_list("Bağlantı seçin:", options)
291
+ if not selected:
292
+ return await self.content_finished()
293
+
294
+ url = selected.get("url")
295
+ extractor: ExtractorBase = self.extractor.find_extractor(url)
296
+
297
+ if not extractor:
298
+ konsol.print("[bold red]Extractor bulunamadı![/bold red]")
299
+ return await self.handle_no_results()
333
300
 
334
- await self.__medya_ayarla(secilen_data)
335
- self.medya_yonetici.play_media(secilen_data)
336
-
337
- await self.icerik_bitti()
301
+ try:
302
+ referer = selected.get("referer", self.current_plugin.main_url)
303
+ extract_data = await extractor.extract(url, referer=referer)
304
+ except Exception as e:
305
+ konsol.print(f"[bold red]{extractor.name} hatası: {e}[/bold red]")
306
+ return await self.handle_no_results()
338
307
 
339
- async def __baglanti_secimi_yap(self, extract_data):
340
- """
341
- Birden fazla bağlantı varsa seçim yapar.
342
- """
308
+ # Birden fazla link varsa seç
343
309
  if isinstance(extract_data, list):
344
- return await self.arayuz_yonetici.select_from_list(
345
- message = "Birden fazla bağlantı bulundu, lütfen birini seçin:",
346
- choices = [{"name": data.name, "value": data} for data in extract_data]
310
+ extract_data = await self.ui.select_from_list(
311
+ message = "Bağlantı seçin:",
312
+ choices = [{"name": d.name, "value": d} for d in extract_data]
347
313
  )
348
- return extract_data
349
314
 
350
- async def __medya_ayarla(self, secilen_data):
351
- """
352
- Medya bilgilerini ayarlar.
353
- """
354
- self.medya_yonetici.set_headers(secilen_data.headers)
315
+ if not extract_data:
316
+ return await self.content_finished()
317
+
318
+ # Başlıkları güncelle ve oynat
319
+ self.update_title(self.episode_title)
320
+ self.update_title(selected.get("name"))
321
+ self.update_title(extract_data.name)
355
322
 
356
- if secilen_data.referer and not secilen_data.headers.get("Referer"):
357
- self.medya_yonetici.set_headers({"Referer": secilen_data.referer})
323
+ self.media.play_media(extract_data)
324
+ await self.content_finished()
358
325
 
359
- if self.suanki_eklenti.name not in self.medya_yonetici.get_title():
360
- self.medya_yonetici.set_title(f"{self.suanki_eklenti.name} | {self.medya_yonetici.get_title()}")
326
+ async def content_finished(self):
327
+ """İçerik bittiğinde ne yapsın?"""
328
+ if self.is_series:
329
+ await self.ask_next_episode()
330
+ else:
331
+ await self.start()
361
332
 
362
- if self.bolum_baslik:
363
- self.medya_yonetici.set_title(f"{self.medya_yonetici.get_title()} | {self.bolum_baslik}")
333
+ async def handle_no_results(self):
334
+ """Sonuç bulunamadığında"""
335
+ choice = await self.ui.select_from_list("Ne yapmak istersiniz?", ["Tüm Eklentilerde Ara", "Ana Menü", "Çıkış"])
336
+ match choice:
337
+ case "Tüm Eklentilerde Ara":
338
+ await self.search_all_plugins()
339
+ case "Ana Menü":
340
+ await self.start()
341
+ case "Çıkış":
342
+ cikis_yap(False)
364
343
 
365
- if secilen_data.name not in self.medya_yonetici.get_title():
366
- self.medya_yonetici.set_title(f"{self.medya_yonetici.get_title()} | {secilen_data.name}")
367
344
 
368
345
  def basla():
369
346
  try:
@@ -372,9 +349,9 @@ def basla():
372
349
 
373
350
  # Uygulamayı başlat
374
351
  app = KekikStream()
375
- run(app.baslat())
352
+ run(app.start())
376
353
  cikis_yap(False)
377
354
  except KeyboardInterrupt:
378
355
  cikis_yap(True)
379
356
  except Exception as hata:
380
- hata_yakala(hata)
357
+ hata_yakala(hata)