KekikStream 2.4.7__py3-none-any.whl → 2.4.9__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.

Potentially problematic release.


This version of KekikStream might be problematic. Click here for more details.

@@ -1,8 +1,8 @@
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, SeriesInfo, Episode, Subtitle, ExtractResult, HTMLHelper
4
- from Kekik.Sifreleme import Packer, StreamDecoder
5
- import random, string
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, Subtitle, ExtractResult, HTMLHelper
4
+ from Kekik.Sifreleme import Packer, StreamDecoder
5
+ import random, string, json, asyncio, contextlib
6
6
 
7
7
  class HDFilmCehennemi(PluginBase):
8
8
  name = "HDFilmCehennemi"
@@ -12,20 +12,43 @@ class HDFilmCehennemi(PluginBase):
12
12
  description = "Türkiye'nin en hızlı hd film izleme sitesi. Tek ve gerçek hdfilmcehennemi sitesi."
13
13
 
14
14
  main_page = {
15
- f"{main_url}" : "Yeni Eklenen Filmler",
16
- f"{main_url}/yabancidiziizle-2" : "Yeni Eklenen Diziler",
17
- f"{main_url}/category/tavsiye-filmler-izle2" : "Tavsiye Filmler",
18
- f"{main_url}/imdb-7-puan-uzeri-filmler" : "IMDB 7+ Filmler",
19
- f"{main_url}/en-cok-yorumlananlar-1" : "En Çok Yorumlananlar",
20
- f"{main_url}/en-cok-begenilen-filmleri-izle" : "En Çok Beğenilenler",
21
- f"{main_url}/tur/aile-filmleri-izleyin-6" : "Aile Filmleri",
22
- f"{main_url}/tur/aksiyon-filmleri-izleyin-3" : "Aksiyon Filmleri",
23
- f"{main_url}/tur/animasyon-filmlerini-izleyin-4" : "Animasyon Filmleri",
24
- f"{main_url}/tur/belgesel-filmlerini-izle-1" : "Belgesel Filmleri",
25
- f"{main_url}/tur/bilim-kurgu-filmlerini-izleyin-2" : "Bilim Kurgu Filmleri",
26
- f"{main_url}/tur/komedi-filmlerini-izleyin-1" : "Komedi Filmleri",
27
- f"{main_url}/tur/korku-filmlerini-izle-2/" : "Korku Filmleri",
28
- f"{main_url}/tur/romantik-filmleri-izle-1" : "Romantik Filmleri"
15
+ f"{main_url}" : "Yeni Eklenen Filmler",
16
+ f"{main_url}/yabancidiziizle-5" : "Yeni Eklenen Diziler",
17
+ f"{main_url}/dil/turkce-dublajli-film-izleyin-5" : "Türkçe Dublaj Filmler",
18
+ f"{main_url}/dil/turkce-altyazili-filmleri-izleme-sitesi-3" : "Türkçe Altyazılı Filmler",
19
+ f"{main_url}/category/tavsiye-filmler-izle3" : "Tavsiye Filmler",
20
+ f"{main_url}/imdb-7-puan-uzeri-filmler-2" : "IMDB 7+ Filmler",
21
+ f"{main_url}/en-cok-yorumlananlar-2" : "En Çok Yorumlananlar",
22
+ f"{main_url}/en-cok-begenilen-filmleri-izle-4" : "En Çok Beğenilenler",
23
+ f"{main_url}/serifilmlerim-4" : "Seri Filmler",
24
+ f"{main_url}/category/nette-ilk-filmler" : "Nette İlk Filmler",
25
+ f"{main_url}/category/4k-film-izle-5" : "4K Filmler",
26
+ f"{main_url}/category/1080p-hd-film-izle-5" : "1080p Filmler",
27
+ f"{main_url}/category/amazon-yapimlarini-izle" : "Amazon Yapımları",
28
+ f"{main_url}/category/netflix-yapimlari-izle" : "Netflix Yapımları",
29
+ f"{main_url}/category/marvel-yapimlarini-izle-5" : "Marvel Filmleri",
30
+ f"{main_url}/category/dc-yapimlarini-izle-1" : "DC Filmleri",
31
+ f"{main_url}/tur/aile-filmleri-izleyin-7" : "Aile Filmleri",
32
+ f"{main_url}/tur/aksiyon-filmleri-izleyin-6" : "Aksiyon Filmleri",
33
+ f"{main_url}/tur/animasyon-filmlerini-izleyin-5" : "Animasyon Filmleri",
34
+ f"{main_url}/tur/belgesel-filmlerini-izle-2" : "Belgesel Filmleri",
35
+ f"{main_url}/tur/bilim-kurgu-filmlerini-izleyin-5" : "Bilim Kurgu Filmleri",
36
+ f"{main_url}/tur/biyografi-filmleri-izle-3" : "Biyografi Filmleri",
37
+ f"{main_url}/tur/dram-filmlerini-izle-2" : "Dram Filmleri",
38
+ f"{main_url}/tur/fantastik-filmlerini-izleyin-3" : "Fantastik Filmleri",
39
+ f"{main_url}/tur/gerilim-filmlerini-izle-2" : "Gerilim Filmleri",
40
+ f"{main_url}/tur/gizem-filmleri-izle-3" : "Gizem Filmleri",
41
+ f"{main_url}/tur/komedi-filmlerini-izleyin-2" : "Komedi Filmleri",
42
+ f"{main_url}/tur/korku-filmlerini-izle-5" : "Korku Filmleri",
43
+ f"{main_url}/tur/macera-filmlerini-izleyin-4" : "Macera Filmleri",
44
+ f"{main_url}/tur/muzik-filmlerini-izle-844" : "Müzik Filmleri",
45
+ f"{main_url}/tur/polisiye-filmleri-izle" : "Polisiye Filmleri",
46
+ f"{main_url}/tur/romantik-filmleri-izle-3" : "Romantik Filmleri",
47
+ f"{main_url}/tur/savas-filmleri-izle-5" : "Savaş Filmleri",
48
+ f"{main_url}/tur/spor-filmleri-izle-3" : "Spor Filmleri",
49
+ f"{main_url}/tur/suc-filmleri-izle-3" : "Suç Filmleri",
50
+ f"{main_url}/tur/tarih-filmleri-izle-5" : "Tarih Filmleri",
51
+ f"{main_url}/tur/western-filmleri-izle-3" : "Western Filmleri"
29
52
  }
30
53
 
31
54
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
@@ -34,8 +57,8 @@ class HDFilmCehennemi(PluginBase):
34
57
 
35
58
  results = []
36
59
  for veri in secici.select("div.section-content a.poster"):
37
- title = secici.select_text("strong.poster-title", veri)
38
- href = veri.attrs.get("href")
60
+ title = secici.select_text("strong.poster-title", veri)
61
+ href = veri.attrs.get("href")
39
62
  poster = secici.select_attr("img", "data-src", veri)
40
63
 
41
64
  if title and href:
@@ -98,117 +121,163 @@ class HDFilmCehennemi(PluginBase):
98
121
  href = ep.attrs.get("href")
99
122
  if name and href:
100
123
  s, e = secici.extract_season_episode(name)
101
- episodes.append(Episode(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
124
+ episodes.append(Episode(
125
+ season = s or 1,
126
+ episode = e or 1,
127
+ title = name,
128
+ url = self.fix_url(href)
129
+ ))
102
130
 
103
131
  return SeriesInfo(
104
- url=url, poster=self.fix_url(poster), title=title,
105
- description=description, tags=tags, rating=rating, year=year, actors=actors, episodes=episodes
132
+ url = url,
133
+ poster = self.fix_url(poster),
134
+ title = title,
135
+ description = description,
136
+ tags = tags,
137
+ rating = rating,
138
+ year = year,
139
+ actors = actors,
140
+ episodes = episodes
106
141
  )
107
142
 
108
143
  return MovieInfo(
109
- url=url, poster=self.fix_url(poster), title=title,
110
- description=description, tags=tags, rating=rating, year=year, actors=actors, duration=duration
144
+ url = url,
145
+ poster = self.fix_url(poster),
146
+ title = title,
147
+ description = description,
148
+ tags = tags,
149
+ rating = rating,
150
+ year = year,
151
+ actors = actors,
152
+ duration = duration
111
153
  )
112
154
 
113
155
  def generate_random_cookie(self):
114
156
  return "".join(random.choices(string.ascii_letters + string.digits, k=16))
115
157
 
116
- async def cehennempass(self, video_id: str) -> list:
158
+ async def cehennempass(self, video_id: str, name_prefix: str = "", subtitles: list[Subtitle] = None) -> list[ExtractResult]:
117
159
  results = []
118
-
119
- istek = await self.httpx.post(
120
- url = "https://cehennempass.pw/process_quality_selection.php",
121
- headers = {
122
- "Referer" : f"https://cehennempass.pw/download/{video_id}",
123
- "X-Requested-With" : "fetch",
124
- "authority" : "cehennempass.pw",
125
- "Cookie" : f"PHPSESSID={self.generate_random_cookie()}"
126
- },
127
- data = {"video_id": video_id, "selected_quality": "low"},
128
- )
129
- if video_url := istek.json().get("download_link"):
130
- results.append(ExtractResult(
131
- url = self.fix_url(video_url),
132
- name = "Düşük Kalite",
133
- referer = f"https://cehennempass.pw/download/{video_id}"
134
- ))
135
-
136
- istek = await self.httpx.post(
137
- url = "https://cehennempass.pw/process_quality_selection.php",
138
- headers = {
139
- "Referer" : f"https://cehennempass.pw/download/{video_id}",
140
- "X-Requested-With" : "fetch",
141
- "authority" : "cehennempass.pw",
142
- "Cookie" : f"PHPSESSID={self.generate_random_cookie()}"
143
- },
144
- data = {"video_id": video_id, "selected_quality": "high"},
145
- )
146
- if video_url := istek.json().get("download_link"):
147
- results.append(ExtractResult(
148
- url = self.fix_url(video_url),
149
- name = "Yüksek Kalite",
150
- referer = f"https://cehennempass.pw/download/{video_id}"
151
- ))
160
+ subs = subtitles or []
161
+
162
+ for quality, label in [("low", "Düşük Kalite"), ("high", "Yüksek Kalite")]:
163
+ with contextlib.suppress(Exception):
164
+ istek = await self.httpx.post(
165
+ url = "https://cehennempass.pw/process_quality_selection.php",
166
+ headers = {
167
+ "Referer" : f"https://cehennempass.pw/download/{video_id}",
168
+ "X-Requested-With" : "fetch",
169
+ "authority" : "cehennempass.pw",
170
+ "Cookie" : f"PHPSESSID={self.generate_random_cookie()}"
171
+ },
172
+ data = {"video_id": video_id, "selected_quality": quality},
173
+ )
174
+ if video_url := istek.json().get("download_link"):
175
+ results.append(ExtractResult(
176
+ url = self.fix_url(video_url),
177
+ name = f"{name_prefix} | {label}" if name_prefix else label,
178
+ referer = f"https://cehennempass.pw/download/{video_id}",
179
+ subtitles = subs
180
+ ))
152
181
 
153
182
  return results
154
183
 
155
- def _extract_from_json_ld(self, html: str) -> str | None:
156
- """JSON-LD script tag'inden contentUrl'i çıkar (Kotlin versiyonundaki gibi)"""
157
- # Önce JSON-LD'den dene
158
- json_ld = HTMLHelper(html).regex_first(r'(?s)<script[^>]+type=["\']application/ld\+json["\'][^>]*>(.*?)</script>')
184
+ def _extract_video_url(self, html: str) -> str | None:
185
+ """Video URL'sini çeşitli yöntemlerle (JSON-LD, Regex, Packer) çıkarır"""
186
+ secici = HTMLHelper(html)
187
+
188
+ # 1. JSON-LD'den dene
189
+ json_ld = secici.regex_first(r'(?s)<script[^>]+type=["\']application/ld\+json["\'][^>]*>(.*?)</script>')
159
190
  if json_ld:
160
- try:
161
- import json
191
+ with contextlib.suppress(Exception):
162
192
  data = json.loads(json_ld.strip())
163
193
  if content_url := data.get("contentUrl"):
164
194
  if content_url.startswith("http"):
165
195
  return content_url
166
- except Exception:
167
- # Regex ile contentUrl'i çıkarmayı dene
168
- content_url = HTMLHelper(html).regex_first(r'"contentUrl"\s*:\s*"([^"]+)"')
169
- if content_url and content_url.startswith("http"):
170
- return content_url
196
+
197
+ # 2. Regex ile contentUrl dene
198
+ content_url = secici.regex_first(r'"contentUrl"\s*:\s*"([^"]+)"')
199
+ if content_url and content_url.startswith("http"):
200
+ return content_url
201
+
202
+ # 3. Packed JavaScript (eval(function...)) dene
203
+ if eval_script := secici.regex_first(r'(eval\(function[\s\S]+)'):
204
+ with contextlib.suppress(Exception):
205
+ unpacked = Packer.unpack(eval_script)
206
+ return StreamDecoder.extract_stream_url(unpacked)
207
+
171
208
  return None
172
209
 
173
- async def invoke_local_source(self, iframe: str, source: str, url: str):
174
- self.httpx.headers.update({
175
- "Referer": f"{self.main_url}/",
176
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0"
177
- })
178
- istek = await self.httpx.get(iframe)
210
+ def _extract_subtitles(self, html: str) -> list[Subtitle]:
211
+ """HTML içeriğinden çeşitli formatlardaki altyazıları çıkarır"""
212
+ subtitles = []
213
+ secici = HTMLHelper(html)
214
+
215
+ # 1. JWPlayer / Plyr / Generic JS Object (tracks: [ ... ])
216
+ if match := secici.regex_first(r'tracks\s*:\s*(\[[^\]]+\])'):
217
+ # JSON parse denemesi
218
+ with contextlib.suppress(Exception):
219
+ track_data = json.loads(match)
220
+ for t in track_data:
221
+ if file_url := t.get("file"):
222
+ label = t.get("label") or t.get("language") or "TR"
223
+ if t.get("kind", "captions") in ["captions", "subtitles"]:
224
+ subtitles.append(Subtitle(name=label.upper(), url=self.fix_url(file_url)))
225
+ return subtitles # JSON başarılıysa dön
226
+
227
+ # Regex fallback
228
+ for m in HTMLHelper(match).regex_all(r'file\s*:\s*["\']([^"\']+)["\'].*?(?:label|language)\s*:\s*["\']([^"\']+)["\']'):
229
+ file_url, lang = m
230
+ subtitles.append(Subtitle(name=lang.upper(), url=self.fix_url(file_url.replace("\\", ""))))
231
+
232
+ # 2. PlayerJS (subtitle: "url,name;url,name")
233
+ if not subtitles:
234
+ if sub_str := secici.regex_first(r'subtitle\s*:\s*["\']([^"\']+)["\']'):
235
+ for sub_item in sub_str.split(";"):
236
+ if "," in sub_item:
237
+ # [TR]url,[EN]url gibi yapılar için split mantığı
238
+ # Basitçe virgülle ayırıp http kontrolü yapalım
239
+ parts = sub_item.split(",")
240
+ u, n = (parts[0], parts[1]) if "http" in parts[0] else (parts[1], parts[0])
241
+ subtitles.append(Subtitle(name=n.strip(), url=self.fix_url(u.strip())))
242
+ elif "http" in sub_item:
243
+ subtitles.append(Subtitle(name="TR", url=self.fix_url(sub_item.strip())))
244
+
245
+ # 3. HTML5 Track Tags
246
+ if not subtitles:
247
+ for track in secici.select("track[kind='captions'], track[kind='subtitles']"):
248
+ src = track.attrs.get("src")
249
+ label = track.attrs.get("label") or track.attrs.get("srclang") or "TR"
250
+ if src:
251
+ subtitles.append(Subtitle(name=label.upper(), url=self.fix_url(src)))
252
+
253
+ return subtitles
254
+
255
+ async def invoke_local_source(self, iframe: str, source: str, url: str) -> list[ExtractResult]:
256
+ istek = await self.httpx.get(
257
+ url = iframe,
258
+ headers = {
259
+ "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
260
+ "X-Requested-With" : "XMLHttpRequest",
261
+ "Referer" : self.main_url + "/"
262
+ }
263
+ )
179
264
 
180
- if not istek.text:
181
- return await self.cehennempass(iframe.split("/")[-1])
265
+ # ID'yi güvenli al
266
+ video_id = iframe.rstrip("/").split("/")[-1]
182
267
 
183
- # Önce JSON-LD'den dene (Kotlin versiyonu gibi - daha güvenilir)
184
- video_url = self._extract_from_json_ld(istek.text)
268
+ # Boş yanıt kontrolü
269
+ if not istek.text or len(istek.text) < 50:
270
+ return await self.cehennempass(video_id, source, [])
185
271
 
186
- # Fallback: Packed JavaScript'ten çıkar
187
- if not video_url:
188
- # eval(function...) içeren packed script bul
189
- eval_script = HTMLHelper(istek.text).regex_first(r'(eval\(function[\s\S]+)')
190
- if not eval_script:
191
- return await self.cehennempass(iframe.split("/")[-1])
272
+ # 1. Altyazıları Çıkar
273
+ subtitles = self._extract_subtitles(istek.text)
192
274
 
193
- try:
194
- unpacked = Packer.unpack(eval_script)
195
- video_url = StreamDecoder.extract_stream_url(unpacked)
196
- except Exception:
197
- return await self.cehennempass(iframe.split("/")[-1])
198
-
199
- if not video_url:
200
- return await self.cehennempass(iframe.split("/")[-1])
275
+ # 2. Video URL'sini Çıkar
276
+ video_url = self._extract_video_url(istek.text)
201
277
 
202
- subtitles = []
203
- try:
204
- sub_data = istek.text.split("tracks: [")[1].split("]")[0]
205
- for file_url, lang in HTMLHelper(sub_data).regex_all(r'(?s)file":"([^\\"]+)".*?"language":"([^\\"]+)"'):
206
- subtitles.append(Subtitle(
207
- name = lang.upper(),
208
- url = self.fix_url(file_url.replace("\\", "")),
209
- ))
210
- except Exception:
211
- pass
278
+ # 3. Eğer Video URL yoksa CehennemPass'a git
279
+ if not video_url:
280
+ return await self.cehennempass(video_id, source, subtitles)
212
281
 
213
282
  return [ExtractResult(
214
283
  url = video_url,
@@ -217,49 +286,72 @@ class HDFilmCehennemi(PluginBase):
217
286
  subtitles = subtitles
218
287
  )]
219
288
 
289
+ async def _get_video_source(self, video_id: str, source_name: str, referer: str) -> list[ExtractResult]:
290
+ try:
291
+ api_get = await self.httpx.get(
292
+ url = f"{self.main_url}/video/{video_id}/",
293
+ headers = {
294
+ "Content-Type" : "application/json",
295
+ "X-Requested-With" : "fetch",
296
+ "Referer" : referer,
297
+ }
298
+ )
299
+
300
+ # JSON Parse (Daha güvenli)
301
+ # Response: {"success": true, "data": {"html": "<iframe class=\"rapidrame\" data-src=\"...\" ...></iframe>"}}
302
+ try:
303
+ json_data = api_get.json()
304
+ html_content = json_data.get("data", {}).get("html", "")
305
+ iframe = HTMLHelper(html_content).select_attr("iframe", "data-src")
306
+ except:
307
+ # RegEx fallback
308
+ iframe = HTMLHelper(api_get.text).regex_first(r'data-src=\\\"([^\"]+)')
309
+ iframe = iframe.replace("\\", "") if iframe else None
310
+
311
+ if not iframe:
312
+ return []
313
+
314
+ # mobi URL'si varsa direkt kullan
315
+ if "mobi" in iframe: # m.hdfilmcehennemi.nl veya /mobi/
316
+ iframe = iframe.split("?")[0]
317
+ # rapidrame ve query varsa
318
+ elif "rapidrame" in iframe and "?rapidrame_id=" in iframe:
319
+ # /rplayer/ID/ formatına çevir
320
+ rap_id = iframe.split('?rapidrame_id=')[1]
321
+ iframe = f"{self.main_url}/rplayer/{rap_id}"
322
+
323
+ return await self.invoke_local_source(iframe, source_name, referer)
324
+ except Exception:
325
+ return []
326
+
220
327
  async def load_links(self, url: str) -> list[ExtractResult]:
221
328
  istek = await self.httpx.get(url)
222
329
  secici = HTMLHelper(istek.text)
223
330
 
224
- results = []
331
+ sources = []
225
332
  for alternatif in secici.select("div.alternative-links"):
226
333
  lang_code = alternatif.attrs.get("data-lang", "").upper()
227
334
 
335
+ # Dil metnini bul
336
+ if lang_code:
337
+ if lang_btn := secici.select_first(f"button.language-link[data-lang='{lang_code.lower()}']"):
338
+ lang_text = lang_btn.text(strip=True)
339
+ # "DUAL (Türkçe Dublaj & Altyazılı)" -> "DUAL" yap, diğerleri aynen kalsın
340
+ if "DUAL" in lang_text:
341
+ lang_code = "DUAL"
342
+ else:
343
+ lang_code = lang_text
344
+
228
345
  for link in secici.select("button.alternative-link", alternatif):
229
346
  source_text = link.text(strip=True).replace('(HDrip Xbet)', '').strip()
230
- source = f"{source_text} {lang_code}"
231
- video_id = link.attrs.get("data-video")
232
-
233
- if not video_id:
234
- continue
235
-
236
- api_get = await self.httpx.get(
237
- url = f"{self.main_url}/video/{video_id}/",
238
- headers = {
239
- "Content-Type" : "application/json",
240
- "X-Requested-With" : "fetch",
241
- "Referer" : url,
242
- },
243
- )
244
-
245
- iframe = HTMLHelper(api_get.text).regex_first(r'data-src=\\\"([^\"]+)')
246
- iframe = iframe.replace("\\", "") if iframe else None
247
-
248
- if not iframe:
249
- continue
250
-
251
- # mobi URL'si varsa direkt kullan (query string'i kaldır)
252
- if "mobi" in iframe:
253
- iframe = iframe.split("?")[0] # rapidrame_id query param'ı kaldır
254
- # mobi değilse ve rapidrame varsa rplayer kullan
255
- elif "rapidrame" in iframe and "?rapidrame_id=" in iframe:
256
- iframe = f"{self.main_url}/rplayer/{iframe.split('?rapidrame_id=')[1]}"
347
+ source_name = f"{lang_code} | {source_text}".strip()
348
+ video_id = link.attrs.get("data-video")
257
349
 
258
- video_data_list = await self.invoke_local_source(iframe, source, url)
259
- if not video_data_list:
260
- continue
350
+ if video_id:
351
+ sources.append((video_id, source_name, url))
261
352
 
262
- for video_data in video_data_list:
263
- results.append(video_data)
353
+ tasks = []
354
+ for vid, name, ref in sources:
355
+ tasks.append(self._get_video_source(vid, name, ref))
264
356
 
265
- return results
357
+ return [item for sublist in await asyncio.gather(*tasks) for item in sublist]
@@ -1,6 +1,7 @@
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, HTMLHelper
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
4
+ import asyncio
4
5
 
5
6
  class JetFilmizle(PluginBase):
6
7
  name = "JetFilmizle"
@@ -42,17 +43,15 @@ class JetFilmizle(PluginBase):
42
43
 
43
44
  results = []
44
45
  for veri in secici.select("article.movie"):
45
- # h2-h6 içindeki a linki
46
46
  title_text = None
47
47
  for h_tag in ["h2", "h3", "h4", "h5", "h6"]:
48
48
  title_text = secici.select_text(f"{h_tag} a", veri)
49
49
  if title_text:
50
50
  break
51
51
 
52
- href = secici.select_attr("a", "href", veri)
53
- poster = secici.select_poster("img", veri)
54
-
55
52
  title = self.clean_title(title_text) if title_text else None
53
+ href = secici.select_attr("a", "href", veri)
54
+ poster = secici.select_poster("img", veri)
56
55
 
57
56
  if title and href:
58
57
  results.append(MainPageResult(
@@ -74,17 +73,15 @@ class JetFilmizle(PluginBase):
74
73
 
75
74
  results = []
76
75
  for article in secici.select("article.movie"):
77
- # h2-h6 içindeki a linki
78
76
  title_text = None
79
77
  for h_tag in ["h2", "h3", "h4", "h5", "h6"]:
80
78
  title_text = secici.select_text(f"{h_tag} a", article)
81
79
  if title_text:
82
80
  break
83
81
 
84
- href = secici.select_attr("a", "href", article)
85
- poster = secici.select_poster("img", article)
86
-
87
82
  title = self.clean_title(title_text) if title_text else None
83
+ href = secici.select_attr("a", "href", article)
84
+ poster = secici.select_poster("img", article)
88
85
 
89
86
  if title and href:
90
87
  results.append(SearchResult(
@@ -126,53 +123,89 @@ class JetFilmizle(PluginBase):
126
123
  rating = rating,
127
124
  year = year,
128
125
  actors = actors,
129
- duration = int(total_minutes) if duration else None
126
+ duration = total_minutes if total_minutes else None
130
127
  )
131
128
 
132
- async def load_links(self, url: str) -> list[ExtractResult]:
133
- istek = await self.httpx.get(url)
134
- secici = HTMLHelper(istek.text)
135
-
129
+ async def _process_source(self, url: str, name: str, html: str | None) -> list[ExtractResult]:
136
130
  results = []
137
-
138
- # 1) Ana iframe'leri kontrol et
139
- for iframe in secici.select("iframe"):
140
- src = (iframe.attrs.get("src") or
141
- iframe.attrs.get("data-src") or
142
- iframe.attrs.get("data-lazy-src"))
143
-
144
- if src and src != "about:blank":
145
- iframe_url = self.fix_url(src)
146
- data = await self.extract(iframe_url)
147
- if data:
148
- results.append(data)
149
-
150
- # 2) Sayfa numaralarından linkleri topla (Fragman hariç)
151
- page_links = []
152
- for link in secici.select("a.post-page-numbers"):
153
- isim = secici.select_text("span", link) or ""
154
- if isim != "Fragman":
155
- href = link.attrs.get("href")
156
- if href:
157
- page_links.append((self.fix_url(href), isim))
158
-
159
- # 3) Her sayfa linkindeki iframe'leri bul
160
- for page_url, isim in page_links:
161
- try:
162
- page_resp = await self.httpx.get(page_url)
163
- page_sel = HTMLHelper(page_resp.text)
164
-
165
- for iframe in page_sel.select("div#movie iframe"):
131
+ try:
132
+ if html:
133
+ secici = HTMLHelper(html)
134
+ else:
135
+ resp = await self.httpx.get(url)
136
+ secici = HTMLHelper(resp.text)
137
+
138
+ # Iframe'leri bul
139
+ container = secici.select_first("div#movie") or secici.select_first("div.film-content")
140
+
141
+ if container:
142
+ for iframe in secici.select("iframe", container):
166
143
  src = (iframe.attrs.get("src") or
167
144
  iframe.attrs.get("data-src") or
168
145
  iframe.attrs.get("data-lazy-src"))
169
-
146
+
170
147
  if src and src != "about:blank":
171
148
  iframe_url = self.fix_url(src)
172
- data = await self.extract(iframe_url, prefix=isim)
149
+ # name_override KULLANMA, extractor kendi ismini versin
150
+ # Sonra biz düzenleriz
151
+ data = await self.extract(iframe_url)
152
+
173
153
  if data:
174
- results.append(data)
175
- except Exception:
176
- continue
154
+ items = data if isinstance(data, list) else [data]
155
+
156
+ for item in items:
157
+ # Sadece kalite bilgisi içeriyorsa ekle, yoksa sadece buton adını kullan
158
+ # Özellikle Zeus için kalite önemli (1080p, 720p)
159
+ # Diğerlerinde plugin adı (Apollo, JetPlay vb.) önemsiz
160
+
161
+ # Kalite kontrolü (basitçe)
162
+ quality_indicators = ["1080p", "720p", "480p", "360p", "240p", "144p", "4k", "2k"]
163
+ has_quality = any(q in item.name.lower() for q in quality_indicators)
164
+
165
+ if has_quality:
166
+ # Buton Adı | Extractor Adı (Kalite içerdiği için)
167
+ # Örn: Zeus | 1080p
168
+ # Eğer Extractor adı zaten Buton adını içeriyorsa (Zeus | 1080p -> Zeus) tekrar ekleme
169
+ if name.lower() not in item.name.lower():
170
+ item.name = f"{name} | {item.name}"
171
+ else:
172
+ # Kalite yoksa sadece Buton adını kullan
173
+ # Örn: Apollo | JetTv -> JetTv
174
+ item.name = name
175
+
176
+ results.append(item)
177
+ return results
178
+ except Exception:
179
+ return []
177
180
 
178
- return results
181
+ async def load_links(self, url: str) -> list[ExtractResult]:
182
+ istek = await self.httpx.get(url)
183
+ secici = HTMLHelper(istek.text)
184
+
185
+ sources = []
186
+ if film_part := secici.select_first("div.film_part"):
187
+ # Tüm spanları gez
188
+ for span in secici.select("span", film_part):
189
+ # Eğer bu span bir <a> etiketi içinde değilse, aktif kaynaktır
190
+ if span.parent.tag != "a":
191
+ name = span.text(strip=True)
192
+ if name:
193
+ sources.append((url, name, istek.text)) # html content var
194
+ break
195
+
196
+ # Diğer kaynak linkleri
197
+ for link in secici.select("a.post-page-numbers", film_part):
198
+ name = secici.select_text("span", link) or link.text(strip=True)
199
+ href = link.attrs.get("href")
200
+ if name != "Fragman" and href:
201
+ sources.append((self.fix_url(href), name, None)) # html yok, çekilecek
202
+
203
+ # Eğer film_part yoksa, sadece mevcut sayfayı tara (Tek part olabilir)
204
+ if not sources:
205
+ sources.append((url, "JetFilmizle", istek.text))
206
+
207
+ tasks = []
208
+ for page_url, source_name, html_content in sources:
209
+ tasks.append(self._process_source(page_url, source_name, html_content))
210
+
211
+ return [item for sublist in await asyncio.gather(*tasks) for item in sublist]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: KekikStream
3
- Version: 2.4.7
3
+ Version: 2.4.9
4
4
  Summary: terminal üzerinden medya içeriği aramanızı ve VLC/MPV gibi popüler medya oynatıcılar aracılığıyla doğrudan izlemenizi sağlayan modüler ve genişletilebilir bir bıdı bıdı
5
5
  Home-page: https://github.com/keyiflerolsun/KekikStream
6
6
  Author: keyiflerolsun