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