KekikStream 2.0.2__py3-none-any.whl → 2.2.0__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 (54) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +7 -2
  2. KekikStream/Core/Plugin/PluginBase.py +61 -13
  3. KekikStream/Extractors/CloseLoad.py +17 -2
  4. KekikStream/Extractors/ContentX.py +19 -2
  5. KekikStream/Extractors/DonilasPlay.py +86 -0
  6. KekikStream/Extractors/Filemoon.py +78 -0
  7. KekikStream/Extractors/MixTiger.py +5 -5
  8. KekikStream/Extractors/Odnoklassniki.py +6 -0
  9. KekikStream/Extractors/PeaceMakerst.py +6 -0
  10. KekikStream/Extractors/PlayerFilmIzle.py +8 -5
  11. KekikStream/Extractors/RapidVid.py +21 -5
  12. KekikStream/Extractors/SetPlay.py +13 -4
  13. KekikStream/Extractors/VCTPlay.py +41 -0
  14. KekikStream/Extractors/VidHide.py +11 -2
  15. KekikStream/Extractors/VidMoly.py +52 -30
  16. KekikStream/Extractors/YTDLP.py +88 -54
  17. KekikStream/Plugins/BelgeselX.py +196 -0
  18. KekikStream/Plugins/DiziBox.py +8 -12
  19. KekikStream/Plugins/DiziPal.py +11 -22
  20. KekikStream/Plugins/DiziYou.py +9 -17
  21. KekikStream/Plugins/Dizilla.py +19 -14
  22. KekikStream/Plugins/FilmBip.py +5 -8
  23. KekikStream/Plugins/FilmMakinesi.py +31 -21
  24. KekikStream/Plugins/FilmModu.py +14 -18
  25. KekikStream/Plugins/FullHDFilm.py +83 -27
  26. KekikStream/Plugins/FullHDFilmizlesene.py +6 -8
  27. KekikStream/Plugins/HDFilmCehennemi.py +133 -57
  28. KekikStream/Plugins/JetFilmizle.py +29 -14
  29. KekikStream/Plugins/KultFilmler.py +13 -15
  30. KekikStream/Plugins/RecTV.py +16 -24
  31. KekikStream/Plugins/RoketDizi.py +22 -32
  32. KekikStream/Plugins/SelcukFlix.py +99 -80
  33. KekikStream/Plugins/SetFilmIzle.py +252 -0
  34. KekikStream/Plugins/SezonlukDizi.py +43 -9
  35. KekikStream/Plugins/SineWix.py +13 -21
  36. KekikStream/Plugins/Sinefy.py +13 -10
  37. KekikStream/Plugins/SinemaCX.py +35 -38
  38. KekikStream/Plugins/Sinezy.py +5 -8
  39. KekikStream/Plugins/SuperFilmGeldi.py +36 -27
  40. KekikStream/Plugins/UgurFilm.py +7 -9
  41. KekikStream/__init__.py +17 -36
  42. {kekikstream-2.0.2.dist-info → kekikstream-2.2.0.dist-info}/METADATA +6 -3
  43. kekikstream-2.2.0.dist-info/RECORD +81 -0
  44. KekikStream/Extractors/ContentX_.py +0 -40
  45. KekikStream/Extractors/FirePlayer.py +0 -60
  46. KekikStream/Extractors/Odnoklassniki_.py +0 -11
  47. KekikStream/Extractors/PeaceMakerst_.py +0 -7
  48. KekikStream/Extractors/RapidVid_.py +0 -7
  49. KekikStream/Extractors/VidMoly_.py +0 -7
  50. kekikstream-2.0.2.dist-info/RECORD +0 -82
  51. {kekikstream-2.0.2.dist-info → kekikstream-2.2.0.dist-info}/WHEEL +0 -0
  52. {kekikstream-2.0.2.dist-info → kekikstream-2.2.0.dist-info}/entry_points.txt +0 -0
  53. {kekikstream-2.0.2.dist-info → kekikstream-2.2.0.dist-info}/licenses/LICENSE +0 -0
  54. {kekikstream-2.0.2.dist-info → kekikstream-2.2.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, Subtitle
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, Subtitle, ExtractResult
4
4
  from parsel import Selector
5
5
  from Kekik.Sifreleme import Packer, StreamDecoder
6
6
  import random, string, re
@@ -10,9 +10,7 @@ class HDFilmCehennemi(PluginBase):
10
10
  language = "tr"
11
11
  main_url = "https://www.hdfilmcehennemi.ws"
12
12
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
13
- description = "Türkiye'nin en hızlı hd film izleme sitesi"
14
-
15
-
13
+ description = "Türkiye'nin en hızlı hd film izleme sitesi. Tek ve gerçek hdfilmcehennemi sitesi."
16
14
 
17
15
  main_page = {
18
16
  f"{main_url}" : "Yeni Eklenen Filmler",
@@ -73,41 +71,81 @@ class HDFilmCehennemi(PluginBase):
73
71
 
74
72
  return results
75
73
 
76
- async def load_item(self, url: str) -> MovieInfo:
74
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
77
75
  istek = await self.httpx.get(url, headers = {"Referer": f"{self.main_url}/"})
78
76
  secici = Selector(istek.text)
79
77
 
80
- title = secici.css("h1.section-title::text").get().strip()
81
- poster = secici.css("aside.post-info-poster img.lazyload::attr(data-src)").get().strip()
82
- description = secici.css("article.post-info-content > p::text").get().strip()
78
+ title = secici.css("h1.section-title::text").get()
79
+ title = title.strip() if title else ""
80
+ poster = secici.css("aside.post-info-poster img.lazyload::attr(data-src)").get() or ""
81
+ poster = poster.strip() if poster else ""
82
+ description = secici.css("article.post-info-content > p::text").get() or ""
83
+ description = description.strip() if description else ""
83
84
  tags = secici.css("div.post-info-genres a::text").getall()
84
- rating = secici.css("div.post-info-imdb-rating span::text").get().strip()
85
- year = secici.css("div.post-info-year-country a::text").get().strip()
85
+ rating = secici.css("div.post-info-imdb-rating span::text").get() or ""
86
+ rating = rating.strip() if rating else ""
87
+ year = secici.css("div.post-info-year-country a::text").get() or ""
88
+ year = year.strip() if year else ""
86
89
  actors = secici.css("div.post-info-cast a > strong::text").getall()
87
- duration = secici.css("div.post-info-duration::text").get().replace("dakika", "").strip()
90
+ duration = secici.css("div.post-info-duration::text").get() or "0"
91
+ duration = duration.replace("dakika", "").strip()
88
92
 
89
-
90
93
  try:
91
- duration_minutes = int(duration[2:-1])
94
+ duration_minutes = int(re.search(r'\d+', duration).group()) if re.search(r'\d+', duration) else 0
92
95
  except Exception:
93
96
  duration_minutes = 0
94
97
 
95
- return MovieInfo(
96
- url = url,
97
- poster = self.fix_url(poster),
98
- title = self.clean_title(title),
99
- description = description,
100
- tags = tags,
101
- rating = rating,
102
- year = year,
103
- actors = actors,
104
- duration = duration_minutes
105
- )
98
+ # Dizi mi film mi kontrol et (Kotlin referansı: div.seasons kontrolü)
99
+ is_series = len(secici.css("div.seasons").getall()) > 0
100
+
101
+ if is_series:
102
+ episodes = []
103
+ for ep in secici.css("div.seasons-tab-content a"):
104
+ ep_name = ep.css("h4::text").get()
105
+ ep_href = ep.css("::attr(href)").get()
106
+ if ep_name and ep_href:
107
+ ep_name = ep_name.strip()
108
+ # Regex ile sezon ve bölüm numarası çıkar
109
+ ep_match = re.search(r'(\d+)\.\s*Bölüm', ep_name)
110
+ sz_match = re.search(r'(\d+)\.\s*Sezon', ep_name)
111
+ ep_num = int(ep_match.group(1)) if ep_match else 1
112
+ sz_num = int(sz_match.group(1)) if sz_match else 1
113
+
114
+ episodes.append(Episode(
115
+ season = sz_num,
116
+ episode = ep_num,
117
+ title = ep_name,
118
+ url = self.fix_url(ep_href)
119
+ ))
120
+
121
+ return SeriesInfo(
122
+ url = url,
123
+ poster = self.fix_url(poster),
124
+ title = self.clean_title(title),
125
+ description = description,
126
+ tags = tags,
127
+ rating = rating,
128
+ year = year,
129
+ actors = actors,
130
+ episodes = episodes
131
+ )
132
+ else:
133
+ return MovieInfo(
134
+ url = url,
135
+ poster = self.fix_url(poster),
136
+ title = self.clean_title(title),
137
+ description = description,
138
+ tags = tags,
139
+ rating = rating,
140
+ year = year,
141
+ actors = actors,
142
+ duration = duration_minutes
143
+ )
106
144
 
107
145
  def generate_random_cookie(self):
108
146
  return "".join(random.choices(string.ascii_letters + string.digits, k=16))
109
147
 
110
- async def cehennempass(self, video_id: str) -> list[dict]:
148
+ async def cehennempass(self, video_id: str) -> list:
111
149
  results = []
112
150
 
113
151
  istek = await self.httpx.post(
@@ -121,11 +159,11 @@ class HDFilmCehennemi(PluginBase):
121
159
  data = {"video_id": video_id, "selected_quality": "low"},
122
160
  )
123
161
  if video_url := istek.json().get("download_link"):
124
- results.append({
125
- "url" : self.fix_url(video_url),
126
- "name" : "Düşük Kalite",
127
- "referer" : f"https://cehennempass.pw/download/{video_id}"
128
- })
162
+ results.append(ExtractResult(
163
+ url = self.fix_url(video_url),
164
+ name = "Düşük Kalite",
165
+ referer = f"https://cehennempass.pw/download/{video_id}"
166
+ ))
129
167
 
130
168
  istek = await self.httpx.post(
131
169
  url = "https://cehennempass.pw/process_quality_selection.php",
@@ -138,25 +176,64 @@ class HDFilmCehennemi(PluginBase):
138
176
  data = {"video_id": video_id, "selected_quality": "high"},
139
177
  )
140
178
  if video_url := istek.json().get("download_link"):
141
- results.append({
142
- "url" : self.fix_url(video_url),
143
- "name" : "Yüksek Kalite",
144
- "referer" : f"https://cehennempass.pw/download/{video_id}"
145
- })
179
+ results.append(ExtractResult(
180
+ url = self.fix_url(video_url),
181
+ name = "Yüksek Kalite",
182
+ referer = f"https://cehennempass.pw/download/{video_id}"
183
+ ))
146
184
 
147
185
  return results
148
186
 
187
+ def extract_hdch_url(self, unpacked: str) -> str:
188
+ """HDFilmCehennemi unpacked script'ten video URL'sini çıkar"""
189
+ # 1) Decode fonksiyonunun adını bul: function <NAME>(value_parts)
190
+ match_fn = re.search(r'function\s+(\w+)\s*\(\s*value_parts\s*\)', unpacked)
191
+ if not match_fn:
192
+ return ""
193
+
194
+ fn_name = match_fn.group(1)
195
+
196
+ # 2) Bu fonksiyonun array ile çağrıldığı yeri bul: <NAME>([ ... ])
197
+ array_call_regex = re.compile(rf'{re.escape(fn_name)}\(\s*\[(.*?)\]\s*\)', re.DOTALL)
198
+ match_call = array_call_regex.search(unpacked)
199
+ if not match_call:
200
+ return ""
201
+
202
+ array_body = match_call.group(1)
203
+
204
+ # 3) Array içindeki string parçalarını topla
205
+ parts = re.findall(r'["\']([^"\']+)["\']', array_body)
206
+ if not parts:
207
+ return ""
208
+
209
+ # 4) Özel decoder ile çöz
210
+ return StreamDecoder._brute_force(parts)
211
+
149
212
  async def invoke_local_source(self, iframe: str, source: str, url: str):
150
- self.httpx.headers.update({"Referer": f"{self.main_url}/"})
213
+ self.httpx.headers.update({
214
+ "Referer": f"{self.main_url}/",
215
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0"
216
+ })
151
217
  istek = await self.httpx.get(iframe)
152
218
 
219
+ if not istek.text:
220
+ return await self.cehennempass(iframe.split("/")[-1])
221
+
222
+ # eval(function...) içeren packed script bul
223
+ eval_match = re.search(r'(eval\(function[\s\S]+)', istek.text)
224
+ if not eval_match:
225
+ return await self.cehennempass(iframe.split("/")[-1])
226
+
153
227
  try:
154
- eval_func = re.compile(r'\s*(eval\(function[\s\S].*)\s*').findall(istek.text)[0]
228
+ unpacked = Packer.unpack(eval_match.group(1))
155
229
  except Exception:
156
230
  return await self.cehennempass(iframe.split("/")[-1])
157
231
 
158
- unpacked = Packer.unpack(eval_func)
159
- video_url = StreamDecoder.extract_stream_url(unpacked)
232
+ # HDFilmCehennemi özel decoder ile video URL'sini çıkar
233
+ video_url = self.extract_hdch_url(unpacked)
234
+
235
+ if not video_url:
236
+ return await self.cehennempass(iframe.split("/")[-1])
160
237
 
161
238
  subtitles = []
162
239
  try:
@@ -169,14 +246,14 @@ class HDFilmCehennemi(PluginBase):
169
246
  except Exception:
170
247
  pass
171
248
 
172
- return [{
173
- "url" : video_url,
174
- "name" : source,
175
- "referer" : url,
176
- "subtitles" : subtitles
177
- }]
249
+ return [ExtractResult(
250
+ url = video_url,
251
+ name = source,
252
+ referer = url,
253
+ subtitles = subtitles
254
+ )]
178
255
 
179
- async def load_links(self, url: str) -> list[dict]:
256
+ async def load_links(self, url: str) -> list[ExtractResult]:
180
257
  istek = await self.httpx.get(url)
181
258
  secici = Selector(istek.text)
182
259
 
@@ -200,8 +277,15 @@ class HDFilmCehennemi(PluginBase):
200
277
  match = re.search(r'data-src=\\\"([^"]+)', api_get.text)
201
278
  iframe = match[1].replace("\\", "") if match else None
202
279
 
203
- if iframe and "?rapidrame_id=" in iframe:
204
- iframe = f"{self.main_url}/playerr/{iframe.split('?rapidrame_id=')[1]}"
280
+ if not iframe:
281
+ continue
282
+
283
+ # mobi URL'si varsa direkt kullan (query string'i kaldır)
284
+ if "mobi" in iframe:
285
+ iframe = iframe.split("?")[0] # rapidrame_id query param'ı kaldır
286
+ # mobi değilse ve rapidrame varsa rplayer kullan
287
+ elif "rapidrame" in iframe and "?rapidrame_id=" in iframe:
288
+ iframe = f"{self.main_url}/rplayer/{iframe.split('?rapidrame_id=')[1]}"
205
289
 
206
290
  video_data_list = await self.invoke_local_source(iframe, source, url)
207
291
  if not video_data_list:
@@ -210,12 +294,4 @@ class HDFilmCehennemi(PluginBase):
210
294
  for video_data in video_data_list:
211
295
  results.append(video_data)
212
296
 
213
- return results
214
-
215
- async def play(self, **kwargs):
216
- extract_result = ExtractResult(**kwargs)
217
- self.media_handler.title = kwargs.get("name")
218
- if self.name not in self.media_handler.title:
219
- self.media_handler.title = f"{self.name} | {self.media_handler.title}"
220
-
221
- self.media_handler.play_media(extract_result)
297
+ return results
@@ -1,14 +1,15 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult
4
4
  from parsel import Selector
5
+ import re
5
6
 
6
7
  class JetFilmizle(PluginBase):
7
8
  name = "JetFilmizle"
8
9
  language = "tr"
9
10
  main_url = "https://jetfilmizle.website"
10
11
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
11
- description = "Binlerce Film İzleme Seçeneğiyle En İyi Film İzleme Sitesi"
12
+ description = "Film izle, Yerli, Yabancı film izle, Türkçe dublaj, alt yazılı seçenekleriyle ödül almış filmleri Full HD kalitesiyle ve jetfilmizle hızıyla donmadan ücretsizce izleyebilirsiniz."
12
13
 
13
14
  main_page = {
14
15
  f"{main_url}/page/" : "Son Filmler",
@@ -16,7 +17,25 @@ class JetFilmizle(PluginBase):
16
17
  f"{main_url}/editorun-secimi/page/" : "Editörün Seçimi",
17
18
  f"{main_url}/turk-film-izle/page/" : "Türk Filmleri",
18
19
  f"{main_url}/cizgi-filmler-izle/page/" : "Çizgi Filmler",
19
- f"{main_url}/kategoriler/yesilcam-filmleri-izlee/page/" : "Yeşilçam Filmleri"
20
+ f"{main_url}/kategoriler/yesilcam-filmleri-izlee/page/" : "Yeşilçam Filmleri",
21
+ f"{main_url}/film-turu/aile-filmleri-izle/page/" : "Aile Filmleri",
22
+ f"{main_url}/film-turu/aksiyon-filmleri/page/" : "Aksiyon Filmleri",
23
+ f"{main_url}/film-turu/animasyon-filmler-izle/page/" : "Animasyon Filmleri",
24
+ f"{main_url}/film-turu/bilim-kurgu-filmler/page/" : "Bilim Kurgu Filmleri",
25
+ f"{main_url}/film-turu/dram-filmleri-izle/page/" : "Dram Filmleri",
26
+ f"{main_url}/film-turu/fantastik-filmleri-izle/page/" : "Fantastik Filmler",
27
+ f"{main_url}/film-turu/gerilim-filmleri/page/" : "Gerilim Filmleri",
28
+ f"{main_url}/film-turu/gizem-filmleri/page/" : "Gizem Filmleri",
29
+ f"{main_url}/film-turu/komedi-film-full-izle/page/" : "Komedi Filmleri",
30
+ f"{main_url}/film-turu/korku-filmleri-izle/page/" : "Korku Filmleri",
31
+ f"{main_url}/film-turu/macera-filmleri/page/" : "Macera Filmleri",
32
+ f"{main_url}/film-turu/muzikal/page/" : "Müzikal Filmler",
33
+ f"{main_url}/film-turu/polisiye/page/" : "Polisiye Filmler",
34
+ f"{main_url}/film-turu/romantik-film-izle/page/" : "Romantik Filmler",
35
+ f"{main_url}/film-turu/savas-filmi-izle/page/" : "Savaş Filmleri",
36
+ f"{main_url}/film-turu/spor/page/" : "Spor Filmleri",
37
+ f"{main_url}/film-turu/suc-filmleri/page/" : "Suç Filmleri",
38
+ f"{main_url}/film-turu/tarihi-filmler/page/" : "Tarihi Filmleri",
20
39
  }
21
40
 
22
41
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
@@ -96,7 +115,7 @@ class JetFilmizle(PluginBase):
96
115
  actors = actors
97
116
  )
98
117
 
99
- async def load_links(self, url: str) -> list[dict]:
118
+ async def load_links(self, url: str) -> list[ExtractResult]:
100
119
  istek = await self.httpx.get(url)
101
120
  secici = Selector(istek.text)
102
121
 
@@ -110,11 +129,9 @@ class JetFilmizle(PluginBase):
110
129
 
111
130
  if src and src != "about:blank":
112
131
  iframe_url = self.fix_url(src)
113
- extractor = self.ex_manager.find_extractor(iframe_url)
114
- results.append({
115
- "url": iframe_url,
116
- "name": extractor.name if extractor else "Ana Player"
117
- })
132
+ data = await self.extract(iframe_url)
133
+ if data:
134
+ results.append(data)
118
135
 
119
136
  # 2) Sayfa numaralarından linkleri topla (Fragman hariç)
120
137
  page_links = []
@@ -138,11 +155,9 @@ class JetFilmizle(PluginBase):
138
155
 
139
156
  if src and src != "about:blank":
140
157
  iframe_url = self.fix_url(src)
141
- extractor = self.ex_manager.find_extractor(iframe_url)
142
- results.append({
143
- "url": iframe_url,
144
- "name": f"{extractor.name if extractor else 'Player'} | {isim}"
145
- })
158
+ data = await self.extract(iframe_url, prefix=isim)
159
+ if data:
160
+ results.append(data)
146
161
  except Exception:
147
162
  continue
148
163
 
@@ -9,7 +9,7 @@ class KultFilmler(PluginBase):
9
9
  language = "tr"
10
10
  main_url = "https://kultfilmler.net"
11
11
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
- description = "Kült film ve dizi izleme sitesi."
12
+ description = "Kült Filmler özenle en iyi filmleri derler ve iyi bir altyazılı film izleme deneyimi sunmayı amaçlar. Reklamsız 1080P Altyazılı Film izle..."
13
13
 
14
14
  main_page = {
15
15
  f"{main_url}/category/aile-filmleri-izle" : "Aile",
@@ -160,7 +160,7 @@ class KultFilmler(PluginBase):
160
160
  match = re.search(r"(https?://[^\s\"]+\.srt)", source_code)
161
161
  return match[1] if match else None
162
162
 
163
- async def load_links(self, url: str) -> list[dict]:
163
+ async def load_links(self, url: str) -> list[ExtractResult]:
164
164
  istek = await self.httpx.get(url)
165
165
  secici = Selector(istek.text)
166
166
 
@@ -195,12 +195,12 @@ class KultFilmler(PluginBase):
195
195
  m3u_match = re.search(r'file:"([^"]+)"', iframe_istek.text)
196
196
 
197
197
  if m3u_match:
198
- results.append({
199
- "name" : "VidMoly",
200
- "url" : m3u_match[1],
201
- "referer" : self.main_url,
202
- "subtitles" : []
203
- })
198
+ results.append(ExtractResult(
199
+ name = "VidMoly",
200
+ url = m3u_match[1],
201
+ referer = self.main_url,
202
+ subtitles = []
203
+ ))
204
204
  continue
205
205
 
206
206
  # Altyazı çıkar
@@ -208,12 +208,10 @@ class KultFilmler(PluginBase):
208
208
  if subtitle_url:
209
209
  subtitles.append(Subtitle(name="Türkçe", url=subtitle_url))
210
210
 
211
- extractor = self.ex_manager.find_extractor(iframe)
212
- results.append({
213
- "name" : extractor.name if extractor else "Player",
214
- "url" : iframe,
215
- "referer" : f"{self.main_url}/",
216
- "subtitles" : subtitles
217
- })
211
+ data = await self.extract(iframe)
212
+ if data:
213
+ # ExtractResult objesi immutable, yeni bir kopya oluştur
214
+ updated_data = data.model_copy(update={"subtitles": subtitles}) if subtitles else data
215
+ results.append(updated_data)
218
216
 
219
217
  return results
@@ -1,6 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Episode, SeriesInfo, ExtractResult, Subtitle
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Episode, SeriesInfo, ExtractResult
4
4
  from json import dumps, loads
5
5
  import re
6
6
 
@@ -116,21 +116,21 @@ class RecTV(PluginBase):
116
116
  actors = []
117
117
  )
118
118
 
119
- async def load_links(self, url: str) -> list[dict]:
119
+ async def load_links(self, url: str) -> list[ExtractResult]:
120
120
  try:
121
121
  veri = loads(url)
122
122
  except Exception:
123
123
  # JSON değilse düz URL'dir (eski yapı veya hata)
124
- return [{"url": url, "name": "Video"}]
124
+ return [ExtractResult(url=url, name="Video")]
125
125
 
126
126
  # Eğer dizi bölümü ise (bizim oluşturduğumuz yapı)
127
127
  if veri.get("is_episode"):
128
- return [{
129
- "url" : veri.get("url"),
130
- "name" : veri.get("title", "Bölüm"),
131
- "user_agent" : "googleusercontent",
132
- "referer" : "https://twitter.com/"
133
- }]
128
+ return [ExtractResult(
129
+ url = veri.get("url"),
130
+ name = veri.get("title", "Bölüm"),
131
+ user_agent = "googleusercontent",
132
+ referer = "https://twitter.com/"
133
+ )]
134
134
 
135
135
  # Film ise (RecTV API yapısı)
136
136
  results = []
@@ -140,19 +140,11 @@ class RecTV(PluginBase):
140
140
  if "otolinkaff" in video_link:
141
141
  continue
142
142
 
143
- results.append({
144
- "url" : video_link,
145
- "name" : f"{veri.get('title')} - {kaynak.get('title')}",
146
- "user_agent" : "googleusercontent",
147
- "referer" : "https://twitter.com/"
148
- })
143
+ results.append(ExtractResult(
144
+ url = video_link,
145
+ name = f"{veri.get('title')} - {kaynak.get('title')}",
146
+ user_agent = "googleusercontent",
147
+ referer = "https://twitter.com/"
148
+ ))
149
149
 
150
- return results
151
-
152
- async def play(self, **kwargs):
153
- extract_result = ExtractResult(**kwargs)
154
- self.media_handler.title = kwargs.get("name")
155
- if self.name not in self.media_handler.title:
156
- self.media_handler.title = f"{self.name} | {self.media_handler.title}"
157
-
158
- self.media_handler.play_media(extract_result)
150
+ return results
@@ -1,6 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, MovieInfo
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult, MovieInfo
4
4
  from parsel import Selector
5
5
  import re, base64, json
6
6
 
@@ -169,7 +169,7 @@ class RoketDizi(PluginBase):
169
169
  year = year
170
170
  )
171
171
 
172
- async def load_links(self, url: str) -> list[dict]:
172
+ async def load_links(self, url: str) -> list[ExtractResult]:
173
173
  resp = await self.httpx.get(url)
174
174
  sel = Selector(resp.text)
175
175
 
@@ -185,6 +185,7 @@ class RoketDizi(PluginBase):
185
185
  # secureData içindeki RelatedResults -> getEpisodeSources -> result dizisini al
186
186
  sources = decoded_json.get("RelatedResults", {}).get("getEpisodeSources", {}).get("result", [])
187
187
 
188
+ seen_urls = set()
188
189
  results = []
189
190
  for source in sources:
190
191
  source_content = source.get("source_content", "")
@@ -195,36 +196,25 @@ class RoketDizi(PluginBase):
195
196
  continue
196
197
 
197
198
  iframe_url = iframe_match.group(1)
198
- if "http" not in iframe_url:
199
- if iframe_url.startswith("//"):
200
- iframe_url = "https:" + iframe_url
201
- else:
202
- iframe_url = "https://" + iframe_url
203
-
204
- # Check extractor
205
- extractor = self.ex_manager.find_extractor(iframe_url)
206
- ext_name = extractor.name if extractor else ""
207
-
208
- # Metadata'dan bilgileri al
209
- source_name = source.get("source_name", "")
210
- language_name = source.get("language_name", "")
211
- quality_name = source.get("quality_name", "")
212
-
213
- # İsmi oluştur
214
- name_parts = []
215
- if source_name:
216
- name_parts.append(source_name)
217
- if ext_name:
218
- name_parts.append(ext_name)
219
- if language_name:
220
- name_parts.append(language_name)
221
- if quality_name:
222
- name_parts.append(quality_name)
223
-
224
- results.append({
225
- "url" : iframe_url,
226
- "name" : " | ".join(name_parts)
227
- })
199
+
200
+ # Fix URL protocol
201
+ if not iframe_url.startswith("http"):
202
+ if iframe_url.startswith("//"):
203
+ iframe_url = "https:" + iframe_url
204
+ else:
205
+ iframe_url = "https://" + iframe_url
206
+
207
+ iframe_url = self.fix_url(iframe_url)
208
+
209
+ # Deduplicate
210
+ if iframe_url in seen_urls:
211
+ continue
212
+ seen_urls.add(iframe_url)
213
+
214
+ # Extract with helper
215
+ data = await self.extract(iframe_url)
216
+ if data:
217
+ results.append(data)
228
218
 
229
219
  return results
230
220