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.
- KekikStream/Core/HTMLHelper.py +2 -2
- KekikStream/Core/Plugin/PluginBase.py +15 -4
- KekikStream/Extractors/Odnoklassniki.py +14 -2
- KekikStream/Extractors/YTDLP.py +2 -2
- KekikStream/Plugins/BelgeselX.py +30 -23
- KekikStream/Plugins/DiziBox.py +14 -11
- KekikStream/Plugins/DiziMom.py +110 -86
- KekikStream/Plugins/DiziPal.py +37 -23
- KekikStream/Plugins/DiziYou.py +23 -11
- KekikStream/Plugins/Dizilla.py +35 -30
- KekikStream/Plugins/FilmBip.py +83 -18
- KekikStream/Plugins/FilmEkseni.py +100 -58
- KekikStream/Plugins/FilmMakinesi.py +71 -19
- KekikStream/Plugins/FilmModu.py +17 -20
- KekikStream/Plugins/Filmatek.py +103 -98
- KekikStream/Plugins/{Full4kizle.py → FilmciBaba.py} +61 -80
- KekikStream/Plugins/FullHDFilmizlesene.py +12 -14
- KekikStream/Plugins/HDFilm.py +243 -0
- KekikStream/Plugins/HDFilmCehennemi.py +194 -125
- KekikStream/Plugins/JetFilmizle.py +5 -5
- KekikStream/Plugins/KultFilmler.py +6 -6
- KekikStream/Plugins/RoketDizi.py +5 -5
- KekikStream/Plugins/SelcukFlix.py +2 -2
- KekikStream/Plugins/SetFilmIzle.py +5 -5
- KekikStream/Plugins/SezonlukDizi.py +4 -4
- KekikStream/Plugins/Sinefy.py +5 -5
- KekikStream/Plugins/SinemaCX.py +5 -5
- KekikStream/Plugins/Sinezy.py +5 -5
- KekikStream/Plugins/SuperFilmGeldi.py +5 -5
- KekikStream/Plugins/UgurFilm.py +4 -4
- KekikStream/Plugins/YabanciDizi.py +5 -5
- {kekikstream-2.4.6.dist-info → kekikstream-2.4.8.dist-info}/METADATA +1 -1
- {kekikstream-2.4.6.dist-info → kekikstream-2.4.8.dist-info}/RECORD +37 -37
- KekikStream/Plugins/FullHDFilm.py +0 -179
- {kekikstream-2.4.6.dist-info → kekikstream-2.4.8.dist-info}/WHEEL +0 -0
- {kekikstream-2.4.6.dist-info → kekikstream-2.4.8.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.4.6.dist-info → kekikstream-2.4.8.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
4
|
-
from Kekik.Sifreleme
|
|
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
|
|
38
|
-
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)
|
|
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(
|
|
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,
|
|
105
|
-
|
|
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,
|
|
110
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
|
156
|
-
"""
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
181
|
-
|
|
242
|
+
# ID'yi güvenli al
|
|
243
|
+
video_id = iframe.rstrip("/").split("/")[-1]
|
|
182
244
|
|
|
183
|
-
#
|
|
184
|
-
|
|
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
|
-
#
|
|
187
|
-
|
|
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
|
-
|
|
194
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
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
|
-
|
|
231
|
-
video_id
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
continue
|
|
327
|
+
if video_id:
|
|
328
|
+
sources.append((video_id, source_name, url))
|
|
261
329
|
|
|
262
|
-
|
|
263
|
-
|
|
330
|
+
tasks = []
|
|
331
|
+
for vid, name, ref in sources:
|
|
332
|
+
tasks.append(self._get_video_source(vid, name, ref))
|
|
264
333
|
|
|
265
|
-
return
|
|
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)
|
|
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)
|
|
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)
|
|
123
|
-
title = title
|
|
122
|
+
poster = self.fix_url(poster),
|
|
123
|
+
title = title,
|
|
124
124
|
description = description,
|
|
125
125
|
tags = tags,
|
|
126
126
|
rating = rating,
|
|
127
|
-
year =
|
|
127
|
+
year = year,
|
|
128
128
|
actors = actors,
|
|
129
129
|
duration = int(total_minutes) if duration else None
|
|
130
130
|
)
|