KekikStream 2.3.9__py3-none-any.whl → 2.5.4__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 (85) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +3 -2
  2. KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
  3. KekikStream/Core/HTMLHelper.py +120 -49
  4. KekikStream/Core/Plugin/PluginBase.py +35 -12
  5. KekikStream/Core/Plugin/PluginLoader.py +12 -14
  6. KekikStream/Core/Plugin/PluginManager.py +2 -2
  7. KekikStream/Core/Plugin/PluginModels.py +0 -3
  8. KekikStream/Extractors/Abstream.py +27 -0
  9. KekikStream/Extractors/CloseLoad.py +30 -54
  10. KekikStream/Extractors/ContentX.py +27 -72
  11. KekikStream/Extractors/DonilasPlay.py +33 -77
  12. KekikStream/Extractors/DzenRu.py +10 -24
  13. KekikStream/Extractors/ExPlay.py +20 -38
  14. KekikStream/Extractors/Filemoon.py +21 -46
  15. KekikStream/Extractors/HDMomPlayer.py +30 -0
  16. KekikStream/Extractors/HDPlayerSystem.py +13 -31
  17. KekikStream/Extractors/HotStream.py +27 -0
  18. KekikStream/Extractors/JFVid.py +3 -24
  19. KekikStream/Extractors/JetTv.py +21 -34
  20. KekikStream/Extractors/JetV.py +55 -0
  21. KekikStream/Extractors/MailRu.py +11 -29
  22. KekikStream/Extractors/MixPlayHD.py +15 -28
  23. KekikStream/Extractors/MixTiger.py +17 -40
  24. KekikStream/Extractors/MolyStream.py +17 -21
  25. KekikStream/Extractors/Odnoklassniki.py +40 -104
  26. KekikStream/Extractors/PeaceMakerst.py +18 -45
  27. KekikStream/Extractors/PixelDrain.py +8 -16
  28. KekikStream/Extractors/PlayerFilmIzle.py +22 -41
  29. KekikStream/Extractors/RapidVid.py +21 -35
  30. KekikStream/Extractors/SetPlay.py +18 -43
  31. KekikStream/Extractors/SibNet.py +7 -17
  32. KekikStream/Extractors/Sobreatsesuyp.py +23 -45
  33. KekikStream/Extractors/TRsTX.py +23 -53
  34. KekikStream/Extractors/TurboImgz.py +7 -14
  35. KekikStream/Extractors/VCTPlay.py +10 -28
  36. KekikStream/Extractors/Veev.py +145 -0
  37. KekikStream/Extractors/VidBiz.py +62 -0
  38. KekikStream/Extractors/VidHide.py +58 -30
  39. KekikStream/Extractors/VidMoly.py +65 -99
  40. KekikStream/Extractors/VidMoxy.py +16 -27
  41. KekikStream/Extractors/VidPapi.py +24 -54
  42. KekikStream/Extractors/VideoSeyred.py +19 -40
  43. KekikStream/Extractors/Videostr.py +58 -0
  44. KekikStream/Extractors/Vidoza.py +18 -0
  45. KekikStream/Extractors/Vtbe.py +38 -0
  46. KekikStream/Extractors/YTDLP.py +2 -2
  47. KekikStream/Extractors/YildizKisaFilm.py +13 -31
  48. KekikStream/Extractors/Zeus.py +61 -0
  49. KekikStream/Plugins/BelgeselX.py +97 -77
  50. KekikStream/Plugins/DiziBox.py +28 -45
  51. KekikStream/Plugins/DiziMom.py +179 -0
  52. KekikStream/Plugins/DiziPal.py +95 -161
  53. KekikStream/Plugins/DiziYou.py +51 -147
  54. KekikStream/Plugins/Dizilla.py +40 -61
  55. KekikStream/Plugins/FilmBip.py +90 -39
  56. KekikStream/Plugins/FilmEkseni.py +199 -0
  57. KekikStream/Plugins/FilmMakinesi.py +72 -73
  58. KekikStream/Plugins/FilmModu.py +25 -35
  59. KekikStream/Plugins/Filmatek.py +184 -0
  60. KekikStream/Plugins/FilmciBaba.py +155 -0
  61. KekikStream/Plugins/FullHDFilmizlesene.py +16 -37
  62. KekikStream/Plugins/HDFilm.py +243 -0
  63. KekikStream/Plugins/HDFilmCehennemi.py +242 -189
  64. KekikStream/Plugins/JetFilmizle.py +101 -69
  65. KekikStream/Plugins/KultFilmler.py +138 -104
  66. KekikStream/Plugins/RecTV.py +52 -73
  67. KekikStream/Plugins/RoketDizi.py +18 -27
  68. KekikStream/Plugins/SelcukFlix.py +30 -48
  69. KekikStream/Plugins/SetFilmIzle.py +76 -104
  70. KekikStream/Plugins/SezonlukDizi.py +90 -94
  71. KekikStream/Plugins/Sinefy.py +195 -167
  72. KekikStream/Plugins/SinemaCX.py +148 -78
  73. KekikStream/Plugins/Sinezy.py +29 -31
  74. KekikStream/Plugins/SuperFilmGeldi.py +12 -17
  75. KekikStream/Plugins/UgurFilm.py +85 -38
  76. KekikStream/Plugins/Watch32.py +160 -0
  77. KekikStream/Plugins/YabanciDizi.py +176 -211
  78. {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/METADATA +1 -1
  79. kekikstream-2.5.4.dist-info/RECORD +99 -0
  80. {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/WHEEL +1 -1
  81. KekikStream/Plugins/FullHDFilm.py +0 -249
  82. kekikstream-2.3.9.dist-info/RECORD +0 -84
  83. {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/entry_points.txt +0 -0
  84. {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/licenses/LICENSE +0 -0
  85. {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/top_level.txt +0 -0
@@ -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:
@@ -43,7 +66,7 @@ class HDFilmCehennemi(PluginBase):
43
66
  category = category,
44
67
  title = title,
45
68
  url = self.fix_url(href),
46
- poster = self.fix_url(poster) if poster else None,
69
+ poster = self.fix_url(poster),
47
70
  ))
48
71
 
49
72
  return results
@@ -78,57 +101,37 @@ class HDFilmCehennemi(PluginBase):
78
101
  istek = await self.httpx.get(url, headers = {"Referer": f"{self.main_url}/"})
79
102
  secici = HTMLHelper(istek.text)
80
103
 
81
- title = secici.select_text("h1.section-title") or ""
104
+ title = self.clean_title(secici.select_text("h1.section-title"))
105
+ poster = secici.select_poster("aside.post-info-poster img.lazyload")
106
+ description = secici.select_text("article.post-info-content > p")
107
+ tags = secici.select_texts("div.post-info-genres a")
108
+ rating = secici.select_text("div.post-info-imdb-rating span")
109
+ rating = rating.split("(")[0] if rating else None
110
+ year = secici.select_text("div.post-info-year-country a")
111
+ actors = secici.select_texts("div.post-info-cast a > strong")
112
+ duration = int(secici.regex_first(r"(\d+)", secici.select_text("div.post-info-duration")) or 0)
82
113
 
83
- poster = secici.select_attr("aside.post-info-poster img.lazyload", "data-src") or ""
84
- poster = poster.strip()
114
+ # Dizi mi film mi kontrol et
115
+ ep_links = secici.select("div.seasons-tab-content a")
85
116
 
86
- description = secici.select_text("article.post-info-content > p") or ""
87
-
88
- tags = secici.select_all_text("div.post-info-genres a")
89
-
90
- rating = secici.select_text("div.post-info-imdb-rating span") or ""
91
-
92
- year = secici.select_text("div.post-info-year-country a") or ""
93
-
94
- actors = secici.select_all_text("div.post-info-cast a > strong")
95
-
96
- duration_str = secici.select_text("div.post-info-duration") or "0"
97
- duration_str = duration_str.replace("dakika", "").strip()
98
-
99
- try:
100
- duration_val = HTMLHelper(duration_str).regex_first(r'\d+')
101
- duration_minutes = int(duration_val) if duration_val else 0
102
- except Exception:
103
- duration_minutes = 0
104
-
105
- # Dizi mi film mi kontrol et (Kotlin referansı: div.seasons kontrolü)
106
- is_series = len(secici.select("div.seasons")) > 0
107
-
108
- if is_series:
117
+ if ep_links:
109
118
  episodes = []
110
- for ep in secici.select("div.seasons-tab-content a"):
111
- ep_name = secici.select_text("h4", ep)
112
- ep_href = ep.attrs.get("href")
113
-
114
- if ep_name and ep_href:
115
- # Regex ile sezon ve bölüm numarası çıkar
116
- ep_val = HTMLHelper(ep_name).regex_first(r'(\d+)\.\s*Bölüm')
117
- sz_val = HTMLHelper(ep_name).regex_first(r'(\d+)\.\s*Sezon')
118
- ep_num = int(ep_val) if ep_val and ep_val.isdigit() else 1
119
- sz_num = int(sz_val) if sz_val and sz_val.isdigit() else 1
120
-
119
+ for ep in ep_links:
120
+ name = secici.select_text("h4", ep)
121
+ href = ep.attrs.get("href")
122
+ if name and href:
123
+ s, e = secici.extract_season_episode(name)
121
124
  episodes.append(Episode(
122
- season = sz_num,
123
- episode = ep_num,
124
- title = ep_name,
125
- url = self.fix_url(ep_href)
125
+ season = s or 1,
126
+ episode = e or 1,
127
+ title = name,
128
+ url = self.fix_url(href)
126
129
  ))
127
130
 
128
131
  return SeriesInfo(
129
132
  url = url,
130
- poster = self.fix_url(poster) if poster else None,
131
- title = self.clean_title(title),
133
+ poster = self.fix_url(poster),
134
+ title = title,
132
135
  description = description,
133
136
  tags = tags,
134
137
  rating = rating,
@@ -136,118 +139,145 @@ class HDFilmCehennemi(PluginBase):
136
139
  actors = actors,
137
140
  episodes = episodes
138
141
  )
139
- else:
140
- return MovieInfo(
141
- url = url,
142
- poster = self.fix_url(poster) if poster else None,
143
- title = self.clean_title(title),
144
- description = description,
145
- tags = tags,
146
- rating = rating,
147
- year = year,
148
- actors = actors,
149
- duration = duration_minutes
150
- )
142
+
143
+ return MovieInfo(
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
153
+ )
151
154
 
152
155
  def generate_random_cookie(self):
153
156
  return "".join(random.choices(string.ascii_letters + string.digits, k=16))
154
157
 
155
- 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]:
156
159
  results = []
157
-
158
- istek = await self.httpx.post(
159
- url = "https://cehennempass.pw/process_quality_selection.php",
160
- headers = {
161
- "Referer" : f"https://cehennempass.pw/download/{video_id}",
162
- "X-Requested-With" : "fetch",
163
- "authority" : "cehennempass.pw",
164
- "Cookie" : f"PHPSESSID={self.generate_random_cookie()}"
165
- },
166
- data = {"video_id": video_id, "selected_quality": "low"},
167
- )
168
- if video_url := istek.json().get("download_link"):
169
- results.append(ExtractResult(
170
- url = self.fix_url(video_url),
171
- name = "Düşük Kalite",
172
- referer = f"https://cehennempass.pw/download/{video_id}"
173
- ))
174
-
175
- istek = await self.httpx.post(
176
- url = "https://cehennempass.pw/process_quality_selection.php",
177
- headers = {
178
- "Referer" : f"https://cehennempass.pw/download/{video_id}",
179
- "X-Requested-With" : "fetch",
180
- "authority" : "cehennempass.pw",
181
- "Cookie" : f"PHPSESSID={self.generate_random_cookie()}"
182
- },
183
- data = {"video_id": video_id, "selected_quality": "high"},
184
- )
185
- if video_url := istek.json().get("download_link"):
186
- results.append(ExtractResult(
187
- url = self.fix_url(video_url),
188
- name = "Yüksek Kalite",
189
- referer = f"https://cehennempass.pw/download/{video_id}"
190
- ))
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
+ ))
191
181
 
192
182
  return results
193
183
 
194
- def _extract_from_json_ld(self, html: str) -> str | None:
195
- """JSON-LD script tag'inden contentUrl'i çıkar (Kotlin versiyonundaki gibi)"""
196
- # Önce JSON-LD'den dene
197
- 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>')
198
190
  if json_ld:
199
- try:
200
- import json
191
+ with contextlib.suppress(Exception):
201
192
  data = json.loads(json_ld.strip())
202
193
  if content_url := data.get("contentUrl"):
203
194
  if content_url.startswith("http"):
204
195
  return content_url
205
- except Exception:
206
- # Regex ile contentUrl'i çıkarmayı dene
207
- content_url = HTMLHelper(html).regex_first(r'"contentUrl"\s*:\s*"([^"]+)"')
208
- if content_url and content_url.startswith("http"):
209
- 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
+
210
208
  return None
211
209
 
212
- async def invoke_local_source(self, iframe: str, source: str, url: str):
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
- })
217
- 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
+ )
218
264
 
219
- if not istek.text:
220
- return await self.cehennempass(iframe.split("/")[-1])
265
+ # ID'yi güvenli al
266
+ video_id = iframe.rstrip("/").split("/")[-1]
221
267
 
222
- # Önce JSON-LD'den dene (Kotlin versiyonu gibi - daha güvenilir)
223
- 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, [])
224
271
 
225
- # Fallback: Packed JavaScript'ten çıkar
226
- if not video_url:
227
- # eval(function...) içeren packed script bul
228
- eval_script = HTMLHelper(istek.text).regex_first(r'(eval\(function[\s\S]+)')
229
- if not eval_script:
230
- return await self.cehennempass(iframe.split("/")[-1])
272
+ # 1. Altyazıları Çıkar
273
+ subtitles = self._extract_subtitles(istek.text)
231
274
 
232
- try:
233
- unpacked = Packer.unpack(eval_script)
234
- video_url = StreamDecoder.extract_stream_url(unpacked)
235
- except Exception:
236
- return await self.cehennempass(iframe.split("/")[-1])
237
-
238
- if not video_url:
239
- return await self.cehennempass(iframe.split("/")[-1])
275
+ # 2. Video URL'sini Çıkar
276
+ video_url = self._extract_video_url(istek.text)
240
277
 
241
- subtitles = []
242
- try:
243
- sub_data = istek.text.split("tracks: [")[1].split("]")[0]
244
- for file_url, lang in HTMLHelper(sub_data).regex_all(r'(?s)file":"([^\\"]+)".*?"language":"([^\\"]+)"'):
245
- subtitles.append(Subtitle(
246
- name = lang.upper(),
247
- url = self.fix_url(file_url.replace("\\", "")),
248
- ))
249
- except Exception:
250
- 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)
251
281
 
252
282
  return [ExtractResult(
253
283
  url = video_url,
@@ -256,49 +286,72 @@ class HDFilmCehennemi(PluginBase):
256
286
  subtitles = subtitles
257
287
  )]
258
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
+
259
327
  async def load_links(self, url: str) -> list[ExtractResult]:
260
328
  istek = await self.httpx.get(url)
261
329
  secici = HTMLHelper(istek.text)
262
330
 
263
- results = []
331
+ sources = []
264
332
  for alternatif in secici.select("div.alternative-links"):
265
333
  lang_code = alternatif.attrs.get("data-lang", "").upper()
266
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
+
267
345
  for link in secici.select("button.alternative-link", alternatif):
268
346
  source_text = link.text(strip=True).replace('(HDrip Xbet)', '').strip()
269
- source = f"{source_text} {lang_code}"
270
- video_id = link.attrs.get("data-video")
271
-
272
- if not video_id:
273
- continue
274
-
275
- api_get = await self.httpx.get(
276
- url = f"{self.main_url}/video/{video_id}/",
277
- headers = {
278
- "Content-Type" : "application/json",
279
- "X-Requested-With" : "fetch",
280
- "Referer" : url,
281
- },
282
- )
283
-
284
- iframe = HTMLHelper(api_get.text).regex_first(r'data-src=\\\"([^\"]+)')
285
- iframe = iframe.replace("\\", "") if iframe else None
286
-
287
- if not iframe:
288
- continue
289
-
290
- # mobi URL'si varsa direkt kullan (query string'i kaldır)
291
- if "mobi" in iframe:
292
- iframe = iframe.split("?")[0] # rapidrame_id query param'ı kaldır
293
- # mobi değilse ve rapidrame varsa rplayer kullan
294
- elif "rapidrame" in iframe and "?rapidrame_id=" in iframe:
295
- 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")
296
349
 
297
- video_data_list = await self.invoke_local_source(iframe, source, url)
298
- if not video_data_list:
299
- continue
350
+ if video_id:
351
+ sources.append((video_id, source_name, url))
300
352
 
301
- for video_data in video_data_list:
302
- results.append(video_data)
353
+ tasks = []
354
+ for vid, name, ref in sources:
355
+ tasks.append(self._get_video_source(vid, name, ref))
303
356
 
304
- return results
357
+ return [item for sublist in await asyncio.gather(*tasks) for item in sublist]