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

Files changed (38) hide show
  1. KekikStream/Core/HTMLHelper.py +2 -2
  2. KekikStream/Core/Plugin/PluginBase.py +15 -4
  3. KekikStream/Extractors/Odnoklassniki.py +14 -2
  4. KekikStream/Extractors/YTDLP.py +2 -2
  5. KekikStream/Plugins/BelgeselX.py +30 -23
  6. KekikStream/Plugins/DiziBox.py +14 -11
  7. KekikStream/Plugins/DiziMom.py +110 -86
  8. KekikStream/Plugins/DiziPal.py +37 -23
  9. KekikStream/Plugins/DiziYou.py +23 -11
  10. KekikStream/Plugins/Dizilla.py +35 -30
  11. KekikStream/Plugins/FilmBip.py +83 -18
  12. KekikStream/Plugins/FilmEkseni.py +100 -58
  13. KekikStream/Plugins/FilmMakinesi.py +71 -19
  14. KekikStream/Plugins/FilmModu.py +17 -20
  15. KekikStream/Plugins/Filmatek.py +103 -98
  16. KekikStream/Plugins/{Full4kizle.py → FilmciBaba.py} +61 -80
  17. KekikStream/Plugins/FullHDFilmizlesene.py +12 -14
  18. KekikStream/Plugins/HDFilm.py +243 -0
  19. KekikStream/Plugins/HDFilmCehennemi.py +194 -125
  20. KekikStream/Plugins/JetFilmizle.py +5 -5
  21. KekikStream/Plugins/KultFilmler.py +6 -6
  22. KekikStream/Plugins/RoketDizi.py +5 -5
  23. KekikStream/Plugins/SelcukFlix.py +2 -2
  24. KekikStream/Plugins/SetFilmIzle.py +5 -5
  25. KekikStream/Plugins/SezonlukDizi.py +4 -4
  26. KekikStream/Plugins/Sinefy.py +5 -5
  27. KekikStream/Plugins/SinemaCX.py +5 -5
  28. KekikStream/Plugins/Sinezy.py +5 -5
  29. KekikStream/Plugins/SuperFilmGeldi.py +5 -5
  30. KekikStream/Plugins/UgurFilm.py +4 -4
  31. KekikStream/Plugins/YabanciDizi.py +5 -5
  32. {kekikstream-2.4.6.dist-info → kekikstream-2.4.8.dist-info}/METADATA +1 -1
  33. {kekikstream-2.4.6.dist-info → kekikstream-2.4.8.dist-info}/RECORD +37 -37
  34. KekikStream/Plugins/FullHDFilm.py +0 -179
  35. {kekikstream-2.4.6.dist-info → kekikstream-2.4.8.dist-info}/WHEEL +0 -0
  36. {kekikstream-2.4.6.dist-info → kekikstream-2.4.8.dist-info}/entry_points.txt +0 -0
  37. {kekikstream-2.4.6.dist-info → kekikstream-2.4.8.dist-info}/licenses/LICENSE +0 -0
  38. {kekikstream-2.4.6.dist-info → kekikstream-2.4.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,243 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, Subtitle, HTMLHelper
4
+ import base64, asyncio, contextlib
5
+
6
+ class HDFilm(PluginBase):
7
+ name = "HDFilm"
8
+ language = "tr"
9
+ main_url = "https://hdfilm.us"
10
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
11
+ description = "Full HD Film izle, Türkçe Dublaj ve Altyazılı filmler."
12
+
13
+ main_page = {
14
+ f"{main_url}/tur/turkce-altyazili-film-izle" : "Altyazılı Filmler",
15
+ f"{main_url}/tur/netflix-filmleri-izle" : "Netflix",
16
+ f"{main_url}/tur/yerli-film-izle" : "Yerli Film",
17
+ f"{main_url}/category/aile-filmleri-izle" : "Aile",
18
+ f"{main_url}/category/aksiyon-filmleri-izle" : "Aksiyon",
19
+ f"{main_url}/category/animasyon-filmleri-izle" : "Animasyon",
20
+ f"{main_url}/category/belgesel-filmleri-izle" : "Belgesel",
21
+ f"{main_url}/category/bilim-kurgu-filmleri-izle" : "Bilim Kurgu",
22
+ f"{main_url}/category/biyografi-filmleri-izle" : "Biyografi",
23
+ f"{main_url}/category/dram-filmleri-izle" : "Dram",
24
+ f"{main_url}/category/fantastik-filmler-izle" : "Fantastik",
25
+ f"{main_url}/category/gerilim-filmleri-izle" : "Gerilim",
26
+ f"{main_url}/category/gizem-filmleri-izle" : "Gizem",
27
+ f"{main_url}/category/kisa" : "Kısa",
28
+ f"{main_url}/category/komedi-filmleri-izle" : "Komedi",
29
+ f"{main_url}/category/korku-filmleri-izle" : "Korku",
30
+ f"{main_url}/category/macera-filmleri-izle" : "Macera",
31
+ f"{main_url}/category/muzik" : "Müzik",
32
+ f"{main_url}/category/muzikal-filmleri-izle" : "Müzikal",
33
+ f"{main_url}/category/romantik-filmler-izle" : "Romantik",
34
+ f"{main_url}/category/savas-filmleri-izle" : "Savaş",
35
+ f"{main_url}/category/spor-filmleri-izle" : "Spor",
36
+ f"{main_url}/category/suc-filmleri-izle" : "Suç",
37
+ f"{main_url}/category/tarih-filmleri-izle" : "Tarih",
38
+ f"{main_url}/category/western-filmleri-izle" : "Western",
39
+ }
40
+
41
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
42
+ istek = await self.httpx.get(url if page == 1 else f"{url}/page/{page}")
43
+ secici = HTMLHelper(istek.text)
44
+
45
+ results = []
46
+ for veri in secici.select("div.movie-poster"):
47
+ title = secici.select_attr("img", "alt", veri)
48
+ poster = secici.select_attr("img", "src", veri)
49
+ href = secici.select_attr("a", "href", veri)
50
+
51
+ if title and href:
52
+ results.append(MainPageResult(
53
+ category = category,
54
+ title = self.clean_title(title),
55
+ url = self.fix_url(href),
56
+ poster = self.fix_url(poster)
57
+ ))
58
+
59
+ return results
60
+
61
+ async def search(self, query: str) -> list[SearchResult]:
62
+ istek = await self.httpx.get(f"{self.main_url}/?s={query}")
63
+ secici = HTMLHelper(istek.text)
64
+
65
+ results = []
66
+ for veri in secici.select("div.movie-poster"):
67
+ title = secici.select_attr("img", "alt", veri)
68
+ poster = secici.select_attr("img", "src", veri)
69
+ href = secici.select_attr("a", "href", veri)
70
+
71
+ if title and href:
72
+ results.append(SearchResult(
73
+ title = self.clean_title(title),
74
+ url = self.fix_url(href),
75
+ poster = self.fix_url(poster)
76
+ ))
77
+
78
+ return results
79
+
80
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
81
+ istek = await self.httpx.get(url)
82
+ secici = HTMLHelper(istek.text)
83
+
84
+ title = self.clean_title(secici.select_text("h1"))
85
+ poster = secici.select_poster("div.poster img")
86
+ description = secici.select_text("div.film") or secici.select_attr("meta[property='og:description']", "content")
87
+ year = secici.extract_year("div.yayin-tarihi.info") or secici.regex_first(r"\((\d{4})\)")
88
+ tags = secici.select_texts("div.tur.info a")
89
+ rating = secici.regex_first(r"IMDb\s*([\d\.]+)", secici.select_text("div.imdb"))
90
+ actors = secici.select_direct_text("div.oyuncular")
91
+
92
+ is_series = "-dizi" in url.lower() or any("dizi" in tag.lower() for tag in tags)
93
+ if is_series:
94
+ episodes = []
95
+ for idx, el in enumerate(secici.select("li.psec")):
96
+ part_id = el.attrs.get("id")
97
+ part_name = secici.select_text("a", el) or ""
98
+ if not part_name or "fragman" in part_name.lower():
99
+ continue
100
+
101
+ s, e = secici.extract_season_episode(f"{part_id} {part_name}")
102
+ episodes.append(Episode(
103
+ season = s or 1,
104
+ episode = e or (idx+1),
105
+ title = f"{s or 1}. Sezon {e or idx+1}. Bölüm",
106
+ url = url
107
+ ))
108
+
109
+ return SeriesInfo(
110
+ url = url,
111
+ poster = self.fix_url(poster),
112
+ title = title,
113
+ description = description,
114
+ tags = tags,
115
+ year = year,
116
+ actors = actors,
117
+ rating = rating,
118
+ episodes = episodes
119
+ )
120
+
121
+ return MovieInfo(
122
+ url = url,
123
+ poster = self.fix_url(poster),
124
+ title = title,
125
+ description = description,
126
+ tags = tags,
127
+ year = year,
128
+ actors = actors,
129
+ rating = rating
130
+ )
131
+
132
+ def _get_iframe(self, source_code: str) -> str:
133
+ """Base64 kodlu iframe'i çözümle"""
134
+ script_val = HTMLHelper(source_code).regex_first(r'<script[^>]*>(PCEtLWJhc2xpazp[^<]*)</script>')
135
+ if not script_val:
136
+ return ""
137
+
138
+ try:
139
+ decoded_html = base64.b64decode(script_val).decode("utf-8")
140
+ iframe_src = HTMLHelper(decoded_html).regex_first(r'<iframe[^>]+src=["\']([^"\']+)["\']')
141
+ return self.fix_url(iframe_src) if iframe_src else ""
142
+ except Exception:
143
+ return ""
144
+
145
+ def _extract_subtitle_url(self, source_code: str) -> str | None:
146
+ """playerjsSubtitle değişkeninden .srt URL çıkar"""
147
+ patterns = [
148
+ r'var playerjsSubtitle = "\[Türkçe\](https?://[^\s"]+?\.srt)";',
149
+ r'var playerjsSubtitle = "(https?://[^\s"]+?\.srt)";',
150
+ r'subtitle:\s*"(https?://[^\s"]+?\.srt)"',
151
+ ]
152
+
153
+ for pattern in patterns:
154
+ val = HTMLHelper(source_code).regex_first(pattern)
155
+ if val:
156
+ return val
157
+
158
+ return None
159
+
160
+ async def _get_source_links(self, url: str, initial_text: str | None = None) -> list[ExtractResult]:
161
+ results = []
162
+ try:
163
+ if initial_text:
164
+ source_code = initial_text
165
+ secici = HTMLHelper(source_code)
166
+ else:
167
+ resp = await self.httpx.get(url)
168
+ source_code = resp.text
169
+ secici = HTMLHelper(source_code)
170
+
171
+ iframe_src = self._get_iframe(source_code)
172
+ subtitle_url = self._extract_subtitle_url(source_code)
173
+
174
+ # İsim Oluştur (Dil | Player)
175
+ parts = []
176
+
177
+ if action_parts := secici.select_first("div#action-parts"):
178
+ # Aktif olan wrapper'ları bul
179
+ for wrapper in secici.select("div.button-custom-wrapper", action_parts):
180
+ # Aktif buton/link
181
+ active_el = secici.select_first("button", wrapper) or secici.select_first("a.button", wrapper)
182
+ if active_el:
183
+ parts.append(active_el.text(strip=True))
184
+
185
+ final_name = " | ".join(parts) if parts else "HDFilm"
186
+
187
+ if not subtitle_url and iframe_src:
188
+ with contextlib.suppress(Exception):
189
+ iframe_istek = await self.httpx.get(iframe_src)
190
+ subtitle_url = self._extract_subtitle_url(iframe_istek.text)
191
+
192
+ if iframe_src:
193
+ data = await self.extract(iframe_src, name_override=final_name)
194
+ if data:
195
+ sub = Subtitle(name="Türkçe", url=subtitle_url) if subtitle_url else None
196
+ if isinstance(data, list):
197
+ for d in data:
198
+ if sub:
199
+ d.subtitles.append(sub)
200
+ results.append(d)
201
+ else:
202
+ if sub:
203
+ data.subtitles.append(sub)
204
+ results.append(data)
205
+
206
+ return results
207
+ except Exception:
208
+ return []
209
+
210
+ async def load_links(self, url: str) -> list[ExtractResult]:
211
+ initial_istek = await self.httpx.get(url)
212
+ initial_text = initial_istek.text
213
+ secici = HTMLHelper(initial_text)
214
+
215
+ base_url = url.split("?")[0].rstrip("/")
216
+ unique_urls = {base_url} # ?page=1 varsa da base_url olarak sakla
217
+
218
+ if action_parts := secici.select_first("div#action-parts"):
219
+ for link in secici.select("a[href]", action_parts):
220
+ href = link.attrs.get("href", "")
221
+ if "?page=" in href:
222
+ if href.endswith("?page=1") or href == "?page=1":
223
+ unique_urls.add(base_url)
224
+ elif href.startswith("?"):
225
+ unique_urls.add(f"{base_url}{href}")
226
+ else:
227
+ unique_urls.add(self.fix_url(href))
228
+
229
+ tasks = []
230
+ for p_url in unique_urls:
231
+ # Eğer p_url şu anki url ile eşleşiyorsa (veya ?page=1 farkı varsa) metni kullan
232
+ # Basit eşleşme: Eğer p_url == base_url ve (url == base_url veya url == base_url + "?page=1")
233
+
234
+ use_initial = False
235
+ if p_url == base_url:
236
+ if url.rstrip("/") == base_url or url.rstrip("/") == f"{base_url}?page=1":
237
+ use_initial = True
238
+ elif p_url == url.rstrip("/"):
239
+ use_initial = True
240
+
241
+ tasks.append(self._get_source_links(p_url, initial_text if use_initial else None))
242
+
243
+ return [item for sublist in await asyncio.gather(*tasks) for item in sublist]
@@ -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"
@@ -34,8 +34,8 @@ class HDFilmCehennemi(PluginBase):
34
34
 
35
35
  results = []
36
36
  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")
37
+ title = secici.select_text("strong.poster-title", veri)
38
+ href = veri.attrs.get("href")
39
39
  poster = secici.select_attr("img", "data-src", veri)
40
40
 
41
41
  if title and href:
@@ -43,7 +43,7 @@ class HDFilmCehennemi(PluginBase):
43
43
  category = category,
44
44
  title = title,
45
45
  url = self.fix_url(href),
46
- poster = self.fix_url(poster) if poster else None,
46
+ poster = self.fix_url(poster),
47
47
  ))
48
48
 
49
49
  return results
@@ -98,117 +98,163 @@ class HDFilmCehennemi(PluginBase):
98
98
  href = ep.attrs.get("href")
99
99
  if name and href:
100
100
  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)))
101
+ episodes.append(Episode(
102
+ season = s or 1,
103
+ episode = e or 1,
104
+ title = name,
105
+ url = self.fix_url(href)
106
+ ))
102
107
 
103
108
  return SeriesInfo(
104
- url=url, poster=self.fix_url(poster) if poster else None, title=title or "Bilinmiyor",
105
- description=description, tags=tags, rating=rating, year=year, actors=actors, episodes=episodes
109
+ url = url,
110
+ poster = self.fix_url(poster),
111
+ title = title,
112
+ description = description,
113
+ tags = tags,
114
+ rating = rating,
115
+ year = year,
116
+ actors = actors,
117
+ episodes = episodes
106
118
  )
107
119
 
108
120
  return MovieInfo(
109
- url=url, poster=self.fix_url(poster) if poster else None, title=title or "Bilinmiyor",
110
- description=description, tags=tags, rating=rating, year=year, actors=actors, duration=duration
121
+ url = url,
122
+ poster = self.fix_url(poster),
123
+ title = title,
124
+ description = description,
125
+ tags = tags,
126
+ rating = rating,
127
+ year = year,
128
+ actors = actors,
129
+ duration = duration
111
130
  )
112
131
 
113
132
  def generate_random_cookie(self):
114
133
  return "".join(random.choices(string.ascii_letters + string.digits, k=16))
115
134
 
116
- async def cehennempass(self, video_id: str) -> list:
135
+ async def cehennempass(self, video_id: str, name_prefix: str = "", subtitles: list[Subtitle] = None) -> list[ExtractResult]:
117
136
  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
- ))
137
+ subs = subtitles or []
138
+
139
+ for quality, label in [("low", "Düşük Kalite"), ("high", "Yüksek Kalite")]:
140
+ with contextlib.suppress(Exception):
141
+ istek = await self.httpx.post(
142
+ url = "https://cehennempass.pw/process_quality_selection.php",
143
+ headers = {
144
+ "Referer" : f"https://cehennempass.pw/download/{video_id}",
145
+ "X-Requested-With" : "fetch",
146
+ "authority" : "cehennempass.pw",
147
+ "Cookie" : f"PHPSESSID={self.generate_random_cookie()}"
148
+ },
149
+ data = {"video_id": video_id, "selected_quality": quality},
150
+ )
151
+ if video_url := istek.json().get("download_link"):
152
+ results.append(ExtractResult(
153
+ url = self.fix_url(video_url),
154
+ name = f"{name_prefix} | {label}" if name_prefix else label,
155
+ referer = f"https://cehennempass.pw/download/{video_id}",
156
+ subtitles = subs
157
+ ))
152
158
 
153
159
  return results
154
160
 
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>')
161
+ def _extract_video_url(self, html: str) -> str | None:
162
+ """Video URL'sini çeşitli yöntemlerle (JSON-LD, Regex, Packer) çıkarır"""
163
+ secici = HTMLHelper(html)
164
+
165
+ # 1. JSON-LD'den dene
166
+ json_ld = secici.regex_first(r'(?s)<script[^>]+type=["\']application/ld\+json["\'][^>]*>(.*?)</script>')
159
167
  if json_ld:
160
- try:
161
- import json
168
+ with contextlib.suppress(Exception):
162
169
  data = json.loads(json_ld.strip())
163
170
  if content_url := data.get("contentUrl"):
164
171
  if content_url.startswith("http"):
165
172
  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
173
+
174
+ # 2. Regex ile contentUrl dene
175
+ content_url = secici.regex_first(r'"contentUrl"\s*:\s*"([^"]+)"')
176
+ if content_url and content_url.startswith("http"):
177
+ return content_url
178
+
179
+ # 3. Packed JavaScript (eval(function...)) dene
180
+ if eval_script := secici.regex_first(r'(eval\(function[\s\S]+)'):
181
+ with contextlib.suppress(Exception):
182
+ unpacked = Packer.unpack(eval_script)
183
+ return StreamDecoder.extract_stream_url(unpacked)
184
+
171
185
  return None
172
186
 
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)
187
+ def _extract_subtitles(self, html: str) -> list[Subtitle]:
188
+ """HTML içeriğinden çeşitli formatlardaki altyazıları çıkarır"""
189
+ subtitles = []
190
+ secici = HTMLHelper(html)
191
+
192
+ # 1. JWPlayer / Plyr / Generic JS Object (tracks: [ ... ])
193
+ if match := secici.regex_first(r'tracks\s*:\s*(\[[^\]]+\])'):
194
+ # JSON parse denemesi
195
+ with contextlib.suppress(Exception):
196
+ track_data = json.loads(match)
197
+ for t in track_data:
198
+ if file_url := t.get("file"):
199
+ label = t.get("label") or t.get("language") or "TR"
200
+ if t.get("kind", "captions") in ["captions", "subtitles"]:
201
+ subtitles.append(Subtitle(name=label.upper(), url=self.fix_url(file_url)))
202
+ return subtitles # JSON başarılıysa dön
203
+
204
+ # Regex fallback
205
+ for m in HTMLHelper(match).regex_all(r'file\s*:\s*["\']([^"\']+)["\'].*?(?:label|language)\s*:\s*["\']([^"\']+)["\']'):
206
+ file_url, lang = m
207
+ subtitles.append(Subtitle(name=lang.upper(), url=self.fix_url(file_url.replace("\\", ""))))
208
+
209
+ # 2. PlayerJS (subtitle: "url,name;url,name")
210
+ if not subtitles:
211
+ if sub_str := secici.regex_first(r'subtitle\s*:\s*["\']([^"\']+)["\']'):
212
+ for sub_item in sub_str.split(";"):
213
+ if "," in sub_item:
214
+ # [TR]url,[EN]url gibi yapılar için split mantığı
215
+ # Basitçe virgülle ayırıp http kontrolü yapalım
216
+ parts = sub_item.split(",")
217
+ u, n = (parts[0], parts[1]) if "http" in parts[0] else (parts[1], parts[0])
218
+ subtitles.append(Subtitle(name=n.strip(), url=self.fix_url(u.strip())))
219
+ elif "http" in sub_item:
220
+ subtitles.append(Subtitle(name="TR", url=self.fix_url(sub_item.strip())))
221
+
222
+ # 3. HTML5 Track Tags
223
+ if not subtitles:
224
+ for track in secici.select("track[kind='captions'], track[kind='subtitles']"):
225
+ src = track.attrs.get("src")
226
+ label = track.attrs.get("label") or track.attrs.get("srclang") or "TR"
227
+ if src:
228
+ subtitles.append(Subtitle(name=label.upper(), url=self.fix_url(src)))
229
+
230
+ return subtitles
231
+
232
+ async def invoke_local_source(self, iframe: str, source: str, url: str) -> list[ExtractResult]:
233
+ istek = await self.httpx.get(
234
+ url = iframe,
235
+ headers = {
236
+ "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",
237
+ "X-Requested-With" : "XMLHttpRequest",
238
+ "Referer" : self.main_url + "/"
239
+ }
240
+ )
179
241
 
180
- if not istek.text:
181
- return await self.cehennempass(iframe.split("/")[-1])
242
+ # ID'yi güvenli al
243
+ video_id = iframe.rstrip("/").split("/")[-1]
182
244
 
183
- # Önce JSON-LD'den dene (Kotlin versiyonu gibi - daha güvenilir)
184
- video_url = self._extract_from_json_ld(istek.text)
245
+ # Boş yanıt kontrolü
246
+ if not istek.text or len(istek.text) < 50:
247
+ return await self.cehennempass(video_id, source, [])
185
248
 
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])
249
+ # 1. Altyazıları Çıkar
250
+ subtitles = self._extract_subtitles(istek.text)
192
251
 
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])
252
+ # 2. Video URL'sini Çıkar
253
+ video_url = self._extract_video_url(istek.text)
201
254
 
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
255
+ # 3. Eğer Video URL yoksa CehennemPass'a git
256
+ if not video_url:
257
+ return await self.cehennempass(video_id, source, subtitles)
212
258
 
213
259
  return [ExtractResult(
214
260
  url = video_url,
@@ -217,49 +263,72 @@ class HDFilmCehennemi(PluginBase):
217
263
  subtitles = subtitles
218
264
  )]
219
265
 
266
+ async def _get_video_source(self, video_id: str, source_name: str, referer: str) -> list[ExtractResult]:
267
+ try:
268
+ api_get = await self.httpx.get(
269
+ url = f"{self.main_url}/video/{video_id}/",
270
+ headers = {
271
+ "Content-Type" : "application/json",
272
+ "X-Requested-With" : "fetch",
273
+ "Referer" : referer,
274
+ }
275
+ )
276
+
277
+ # JSON Parse (Daha güvenli)
278
+ # Response: {"success": true, "data": {"html": "<iframe class=\"rapidrame\" data-src=\"...\" ...></iframe>"}}
279
+ try:
280
+ json_data = api_get.json()
281
+ html_content = json_data.get("data", {}).get("html", "")
282
+ iframe = HTMLHelper(html_content).select_attr("iframe", "data-src")
283
+ except:
284
+ # RegEx fallback
285
+ iframe = HTMLHelper(api_get.text).regex_first(r'data-src=\\\"([^\"]+)')
286
+ iframe = iframe.replace("\\", "") if iframe else None
287
+
288
+ if not iframe:
289
+ return []
290
+
291
+ # mobi URL'si varsa direkt kullan
292
+ if "mobi" in iframe: # m.hdfilmcehennemi.nl veya /mobi/
293
+ iframe = iframe.split("?")[0]
294
+ # rapidrame ve query varsa
295
+ elif "rapidrame" in iframe and "?rapidrame_id=" in iframe:
296
+ # /rplayer/ID/ formatına çevir
297
+ rap_id = iframe.split('?rapidrame_id=')[1]
298
+ iframe = f"{self.main_url}/rplayer/{rap_id}"
299
+
300
+ return await self.invoke_local_source(iframe, source_name, referer)
301
+ except Exception:
302
+ return []
303
+
220
304
  async def load_links(self, url: str) -> list[ExtractResult]:
221
305
  istek = await self.httpx.get(url)
222
306
  secici = HTMLHelper(istek.text)
223
307
 
224
- results = []
308
+ sources = []
225
309
  for alternatif in secici.select("div.alternative-links"):
226
310
  lang_code = alternatif.attrs.get("data-lang", "").upper()
227
311
 
312
+ # Dil metnini bul
313
+ if lang_code:
314
+ if lang_btn := secici.select_first(f"button.language-link[data-lang='{lang_code.lower()}']"):
315
+ lang_text = lang_btn.text(strip=True)
316
+ # "DUAL (Türkçe Dublaj & Altyazılı)" -> "DUAL" yap, diğerleri aynen kalsın
317
+ if "DUAL" in lang_text:
318
+ lang_code = "DUAL"
319
+ else:
320
+ lang_code = lang_text
321
+
228
322
  for link in secici.select("button.alternative-link", alternatif):
229
323
  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]}"
324
+ source_name = f"{lang_code} | {source_text}".strip()
325
+ video_id = link.attrs.get("data-video")
257
326
 
258
- video_data_list = await self.invoke_local_source(iframe, source, url)
259
- if not video_data_list:
260
- continue
327
+ if video_id:
328
+ sources.append((video_id, source_name, url))
261
329
 
262
- for video_data in video_data_list:
263
- results.append(video_data)
330
+ tasks = []
331
+ for vid, name, ref in sources:
332
+ tasks.append(self._get_video_source(vid, name, ref))
264
333
 
265
- return results
334
+ return [item for sublist in await asyncio.gather(*tasks) for item in sublist]
@@ -59,7 +59,7 @@ class JetFilmizle(PluginBase):
59
59
  category = category,
60
60
  title = title,
61
61
  url = self.fix_url(href),
62
- poster = self.fix_url(poster) if poster else None,
62
+ poster = self.fix_url(poster),
63
63
  ))
64
64
 
65
65
  return results
@@ -90,7 +90,7 @@ class JetFilmizle(PluginBase):
90
90
  results.append(SearchResult(
91
91
  title = title,
92
92
  url = self.fix_url(href),
93
- poster = self.fix_url(poster) if poster else None,
93
+ poster = self.fix_url(poster),
94
94
  ))
95
95
 
96
96
  return results
@@ -119,12 +119,12 @@ class JetFilmizle(PluginBase):
119
119
 
120
120
  return MovieInfo(
121
121
  url = url,
122
- poster = self.fix_url(poster) if poster else None,
123
- title = title or "Bilinmiyor",
122
+ poster = self.fix_url(poster),
123
+ title = title,
124
124
  description = description,
125
125
  tags = tags,
126
126
  rating = rating,
127
- year = str(year) if year else None,
127
+ year = year,
128
128
  actors = actors,
129
129
  duration = int(total_minutes) if duration else None
130
130
  )