KekikStream 2.3.7__tar.gz → 2.3.8__tar.gz
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.
- kekikstream-2.3.8/KekikStream/Extractors/MolyStream.py +83 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/VidMoly.py +13 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/DiziBox.py +25 -15
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/DiziPal.py +20 -9
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/DiziWatch.py +6 -6
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/DiziYou.py +14 -18
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/Dizilla.py +50 -59
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/FilmBip.py +8 -4
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/FilmMakinesi.py +1 -1
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/FullHDFilmizlesene.py +7 -16
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/JetFilmizle.py +6 -1
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/KultFilmler.py +2 -1
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/RecTV.py +27 -5
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/RoketDizi.py +76 -78
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/SelcukFlix.py +90 -67
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/SetFilmIzle.py +27 -8
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/YabanciDizi.py +25 -14
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream.egg-info/PKG-INFO +1 -1
- {kekikstream-2.3.7 → kekikstream-2.3.8}/PKG-INFO +1 -1
- {kekikstream-2.3.7 → kekikstream-2.3.8}/setup.py +1 -1
- kekikstream-2.3.7/KekikStream/Extractors/MolyStream.py +0 -41
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/CLI/__init__.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/CLI/pypi_kontrol.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/Extractor/ExtractorBase.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/Extractor/ExtractorModels.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/Extractor/YTDLPCache.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/HTMLHelper.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/Media/MediaHandler.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/Media/MediaManager.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/Plugin/PluginBase.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/Plugin/PluginManager.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/Plugin/PluginModels.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/UI/UIManager.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Core/__init__.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/CloseLoad.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/ContentX.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/DonilasPlay.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/DzenRu.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/ExPlay.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/Filemoon.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/HDPlayerSystem.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/JFVid.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/JetTv.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/MailRu.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/MixPlayHD.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/MixTiger.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/Odnoklassniki.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/PeaceMakerst.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/PixelDrain.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/PlayerFilmIzle.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/RapidVid.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/SetPlay.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/SetPrime.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/SibNet.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/Sobreatsesuyp.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/TRsTX.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/TauVideo.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/TurboImgz.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/TurkeyPlayer.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/VCTPlay.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/VidHide.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/VidMoxy.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/VidPapi.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/VideoSeyred.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/YTDLP.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Extractors/YildizKisaFilm.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/BelgeselX.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/FilmModu.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/FullHDFilm.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/HDFilmCehennemi.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/SezonlukDizi.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/SineWix.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/Sinefy.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/SinemaCX.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/Sinezy.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/SuperFilmGeldi.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/Plugins/UgurFilm.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/__init__.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/__main__.py +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream/requirements.txt +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream.egg-info/SOURCES.txt +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream.egg-info/dependency_links.txt +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream.egg-info/entry_points.txt +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream.egg-info/requires.txt +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/KekikStream.egg-info/top_level.txt +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/LICENSE +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/MANIFEST.in +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/README.md +0 -0
- {kekikstream-2.3.7 → kekikstream-2.3.8}/setup.cfg +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
class MolyStream(ExtractorBase):
|
|
7
|
+
name = "MolyStream"
|
|
8
|
+
main_url = "https://dbx.molystream.org"
|
|
9
|
+
|
|
10
|
+
# Birden fazla domain destekle
|
|
11
|
+
supported_domains = [
|
|
12
|
+
"dbx.molystream.org",
|
|
13
|
+
"ydx.molystream.org",
|
|
14
|
+
"yd.sheila.stream",
|
|
15
|
+
"ydf.popcornvakti.net",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
def can_handle_url(self, url: str) -> bool:
|
|
19
|
+
return any(domain in url for domain in self.supported_domains)
|
|
20
|
+
|
|
21
|
+
async def extract(self, url, referer=None) -> ExtractResult:
|
|
22
|
+
if "doctype html" in url.lower():
|
|
23
|
+
text = url
|
|
24
|
+
else:
|
|
25
|
+
# Sheila-style referer fix
|
|
26
|
+
if "/embed/sheila/" in url:
|
|
27
|
+
referer = url.replace("/embed/sheila/", "/embed/")
|
|
28
|
+
|
|
29
|
+
self.httpx.headers.update({
|
|
30
|
+
"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",
|
|
31
|
+
"Referer" : referer or self.main_url
|
|
32
|
+
})
|
|
33
|
+
istek = await self.httpx.get(url, follow_redirects=True)
|
|
34
|
+
text = istek.text
|
|
35
|
+
|
|
36
|
+
# 1. Sheila-style links often have the m3u8 directly as the first http line or in script
|
|
37
|
+
m3u8 = None
|
|
38
|
+
if "#EXTM3U" in text:
|
|
39
|
+
for line in text.splitlines():
|
|
40
|
+
line = line.strip().replace('"', '').replace("'", "")
|
|
41
|
+
if line.startswith("http"):
|
|
42
|
+
m3u8 = line
|
|
43
|
+
break
|
|
44
|
+
|
|
45
|
+
if not m3u8:
|
|
46
|
+
for line in text.splitlines():
|
|
47
|
+
line = line.strip().replace('"', '').replace("'", "")
|
|
48
|
+
if line.startswith("http") and ".m3u8" in line:
|
|
49
|
+
m3u8 = line
|
|
50
|
+
break
|
|
51
|
+
|
|
52
|
+
if not m3u8:
|
|
53
|
+
secici = HTMLHelper(text)
|
|
54
|
+
# 2. Try video tag
|
|
55
|
+
m3u8 = secici.select_attr("video#sheplayer source", "src") or secici.select_attr("video source", "src")
|
|
56
|
+
|
|
57
|
+
if not m3u8:
|
|
58
|
+
# 3. Try regex
|
|
59
|
+
m3u8 = HTMLHelper(text).regex_first(r'["\'](https?://[^"\']+\.m3u8[^"\']*)["\']')
|
|
60
|
+
|
|
61
|
+
if not m3u8:
|
|
62
|
+
# Fallback to any http link in a script if it looks like a video link
|
|
63
|
+
m3u8 = HTMLHelper(text).regex_first(r'["\'](https?://[^"\']+/q/\d+)["\']')
|
|
64
|
+
|
|
65
|
+
if not m3u8:
|
|
66
|
+
m3u8 = url # Final fallback
|
|
67
|
+
|
|
68
|
+
# Subtitles (Sheila style addSrtFile)
|
|
69
|
+
resp_sec = HTMLHelper(text)
|
|
70
|
+
matches = resp_sec.regex_all(r"addSrtFile\(['\"]([^'\"]+\.srt)['\"]\s*,\s*['\"][a-z]{2}['\"]\s*,\s*['\"]([^'\"]+)['\"]")
|
|
71
|
+
|
|
72
|
+
subtitles = [
|
|
73
|
+
Subtitle(name = name, url = self.fix_url(url))
|
|
74
|
+
for url, name in matches
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
return ExtractResult(
|
|
78
|
+
name = self.name,
|
|
79
|
+
url = m3u8,
|
|
80
|
+
referer = url,
|
|
81
|
+
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",
|
|
82
|
+
subtitles = subtitles
|
|
83
|
+
)
|
|
@@ -24,6 +24,8 @@ class VidMoly(ExtractorBase):
|
|
|
24
24
|
|
|
25
25
|
if ".me" in url:
|
|
26
26
|
url = url.replace(".me", ".net")
|
|
27
|
+
if ".to" in url:
|
|
28
|
+
url = url.replace(".to", ".net")
|
|
27
29
|
|
|
28
30
|
# VidMoly bazen redirect ediyor, takip et
|
|
29
31
|
response = await self.httpx.get(url, follow_redirects=True)
|
|
@@ -70,6 +72,17 @@ class VidMoly(ExtractorBase):
|
|
|
70
72
|
if sub.get("kind") == "captions"
|
|
71
73
|
]
|
|
72
74
|
|
|
75
|
+
if "#EXTM3U" in response.text:
|
|
76
|
+
for line in response.text.splitlines():
|
|
77
|
+
line = line.strip().replace('"', '').replace("'", "")
|
|
78
|
+
if line.startswith("http"):
|
|
79
|
+
return ExtractResult(
|
|
80
|
+
name = self.name,
|
|
81
|
+
url = line,
|
|
82
|
+
referer = self.main_url,
|
|
83
|
+
subtitles = subtitles
|
|
84
|
+
)
|
|
85
|
+
|
|
73
86
|
if script_str := resp_sec.regex_first(r"sources:\s*\[(.*?)\],", flags= re.DOTALL):
|
|
74
87
|
script_content = script_str
|
|
75
88
|
# Video kaynaklarını ayrıştır
|
|
@@ -167,34 +167,44 @@ class DiziBox(PluginBase):
|
|
|
167
167
|
|
|
168
168
|
crypt_data = iframe_secici.regex_first(r"CryptoJS\.AES\.decrypt\(\"(.*)\",\"", iframe_istek.text)
|
|
169
169
|
crypt_pass = iframe_secici.regex_first(r"\",\"(.*)\"\);", iframe_istek.text)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
if crypt_data and crypt_pass:
|
|
171
|
+
decode = CryptoJS.decrypt(crypt_pass, crypt_data)
|
|
172
|
+
if video_match := iframe_secici.regex_first(r"file: '(.*)',", decode):
|
|
173
|
+
results.append(video_match)
|
|
174
|
+
else:
|
|
175
|
+
results.append(decode)
|
|
176
176
|
|
|
177
177
|
elif "/player/moly/moly.php" in iframe_link:
|
|
178
178
|
iframe_link = iframe_link.replace("moly.php?h=", "moly.php?wmode=opaque&h=")
|
|
179
|
-
|
|
179
|
+
for _ in range(3): # Max 3 attempts
|
|
180
180
|
await asyncio.sleep(.3)
|
|
181
181
|
with contextlib.suppress(Exception):
|
|
182
182
|
moly_istek = await self.httpx.get(iframe_link)
|
|
183
183
|
moly_secici = HTMLHelper(moly_istek.text)
|
|
184
184
|
|
|
185
|
-
if atob_data := moly_secici.regex_first(r"unescape\(\"(.*)\"\)"
|
|
185
|
+
if atob_data := moly_secici.regex_first(r"unescape\(\"(.*)\"\)"):
|
|
186
186
|
decoded_atob = urllib.parse.unquote(atob_data)
|
|
187
187
|
str_atob = base64.b64decode(decoded_atob).decode("utf-8")
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
189
|
+
iframe_src = HTMLHelper(str_atob).select_attr("div#Player iframe", "src")
|
|
190
|
+
if iframe_src:
|
|
191
|
+
# ! Sheila replacement (Kotlin referansı)
|
|
192
|
+
if "/embed/" in iframe_src:
|
|
193
|
+
iframe_src = iframe_src.replace("/embed/", "/embed/sheila/").replace("vidmoly.me", "vidmoly.net")
|
|
194
|
+
|
|
195
|
+
results.append(iframe_src)
|
|
196
|
+
break
|
|
197
|
+
elif embed_matches := moly_secici.regex_all(r'iframe.*?src="(.*?)"'):
|
|
198
|
+
for src in embed_matches:
|
|
199
|
+
if "/embed/" in src:
|
|
200
|
+
src = src.replace("/embed/", "/embed/sheila/").replace("vidmoly.me", "vidmoly.net")
|
|
201
|
+
results.append(src)
|
|
202
|
+
break
|
|
194
203
|
|
|
195
204
|
elif "/player/haydi.php" in iframe_link:
|
|
196
|
-
|
|
197
|
-
|
|
205
|
+
with contextlib.suppress(Exception):
|
|
206
|
+
okru_url = base64.b64decode(iframe_link.split("?v=")[-1]).decode("utf-8")
|
|
207
|
+
results.append(okru_url)
|
|
198
208
|
|
|
199
209
|
return results
|
|
200
210
|
|
|
@@ -127,18 +127,27 @@ class DiziPal(PluginBase):
|
|
|
127
127
|
|
|
128
128
|
poster = self.fix_url(secici.select_attr("meta[property='og:image']", "content")) if secici.select_attr("meta[property='og:image']", "content") else None
|
|
129
129
|
|
|
130
|
-
#
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
# Sidebar bilgilerini topla
|
|
131
|
+
info = {}
|
|
132
|
+
for li in secici.select("li"):
|
|
133
|
+
key = secici.select_text("div.key", li)
|
|
134
|
+
val = secici.select_text("div.value", li)
|
|
135
|
+
if key and val:
|
|
136
|
+
info[key.strip(":")] = val.strip()
|
|
137
|
+
|
|
138
|
+
year = info.get("Yapım Yılı")
|
|
139
|
+
rating = info.get("IMDB Puanı")
|
|
140
|
+
|
|
141
|
+
tags_raw = info.get("Türler", "")
|
|
142
|
+
tags = [t.strip() for t in tags_raw.split() if t.strip()] if tags_raw else None
|
|
134
143
|
|
|
135
|
-
|
|
144
|
+
actors_raw = info.get("Oyuncular")
|
|
145
|
+
actors = [a.strip() for a in actors_raw.split(",") if a.strip()] if actors_raw else None
|
|
136
146
|
|
|
137
|
-
|
|
138
|
-
tags = [t.strip() for t in tags_raw.split() if t.strip()] if tags_raw else None
|
|
147
|
+
description = secici.select_text("div.summary p")
|
|
139
148
|
|
|
140
|
-
duration_raw =
|
|
141
|
-
duration = int(duration_raw) if duration_raw else None
|
|
149
|
+
duration_raw = info.get("Ortalama Süre")
|
|
150
|
+
duration = int(secici.regex_first(r"(\d+)", duration_raw)) if duration_raw else None
|
|
142
151
|
|
|
143
152
|
if "/dizi/" in url:
|
|
144
153
|
title = secici.select_text("div.cover h5")
|
|
@@ -177,6 +186,7 @@ class DiziPal(PluginBase):
|
|
|
177
186
|
year = year,
|
|
178
187
|
duration = duration,
|
|
179
188
|
episodes = episodes if episodes else None,
|
|
189
|
+
actors = actors,
|
|
180
190
|
)
|
|
181
191
|
else:
|
|
182
192
|
# Film için title - g-title div'lerinin 2. olanı
|
|
@@ -192,6 +202,7 @@ class DiziPal(PluginBase):
|
|
|
192
202
|
rating = rating,
|
|
193
203
|
year = year,
|
|
194
204
|
duration = duration,
|
|
205
|
+
actors = actors,
|
|
195
206
|
)
|
|
196
207
|
|
|
197
208
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
@@ -27,10 +27,10 @@ class DiziWatch(PluginBase):
|
|
|
27
27
|
"1" : "Suç",
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
c_key = ""
|
|
31
|
+
c_value = ""
|
|
32
|
+
|
|
30
33
|
async def _init_session(self):
|
|
31
|
-
if getattr(self, "c_key", None) and getattr(self, "c_value", None):
|
|
32
|
-
return
|
|
33
|
-
|
|
34
34
|
# Fetch anime-arsivi to get CSRF tokens
|
|
35
35
|
resp = await self.httpx.get(f"{self.main_url}/anime-arsivi")
|
|
36
36
|
sel = HTMLHelper(resp.text)
|
|
@@ -137,9 +137,9 @@ class DiziWatch(PluginBase):
|
|
|
137
137
|
resp = await self.httpx.get(url)
|
|
138
138
|
sel = HTMLHelper(resp.text)
|
|
139
139
|
|
|
140
|
-
title = sel.select_text("h2")
|
|
141
|
-
poster = sel.select_attr("img.rounded-md", "src")
|
|
142
|
-
description = sel.select_text("div.text-sm")
|
|
140
|
+
title = sel.select_text("h2") or sel.select_text("h1")
|
|
141
|
+
poster = sel.select_attr("img.rounded-md", "src") or sel.select_attr("meta[property='og:image']", "content")
|
|
142
|
+
description = sel.select_text("div.text-sm") or sel.select_text("div.summary")
|
|
143
143
|
|
|
144
144
|
year = sel.regex_first(r"Yap\u0131m Y\u0131l\u0131\s*:\s*(\d+)", resp.text)
|
|
145
145
|
|
|
@@ -72,7 +72,7 @@ class DiziYou(PluginBase):
|
|
|
72
72
|
html_text = istek.text
|
|
73
73
|
|
|
74
74
|
# Title - div.title h1 içinde
|
|
75
|
-
title = secici.select_text("div.title h1")
|
|
75
|
+
title = (secici.select_text("div.title h1") or "").strip()
|
|
76
76
|
|
|
77
77
|
# Fallback: Eğer title boşsa URL'den çıkar (telif kısıtlaması olan sayfalar için)
|
|
78
78
|
if not title:
|
|
@@ -81,23 +81,19 @@ class DiziYou(PluginBase):
|
|
|
81
81
|
title = slug.replace('-', ' ').title()
|
|
82
82
|
|
|
83
83
|
# Poster
|
|
84
|
-
poster_src = secici.select_attr("div.category_image img", "src")
|
|
84
|
+
poster_src = secici.select_attr("div.category_image img", "src") or secici.select_attr("meta[property='og:image']", "content")
|
|
85
85
|
poster = self.fix_url(poster_src) if poster_src else ""
|
|
86
86
|
|
|
87
87
|
# Year - regex ile çıkarma (xpath yerine)
|
|
88
88
|
year = secici.regex_first(r"(?is)Yapım Yılı.*?(\d{4})", secici.html)
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
desc_html = HTMLHelper(desc_html).regex_replace(r"(?s)<div id=\"icerikcat2\".*", "")
|
|
98
|
-
# Kalan HTML'den text çıkar
|
|
99
|
-
clean_sel = HTMLHelper(desc_html)
|
|
100
|
-
description = clean_sel.select_text()
|
|
90
|
+
description_el = secici.select("div.diziyou_desc") or secici.select("div#icerikcat")
|
|
91
|
+
description = ""
|
|
92
|
+
if description_el:
|
|
93
|
+
# Scriptleri temizle
|
|
94
|
+
for script in secici.select("script", description_el[0]):
|
|
95
|
+
script.decompose()
|
|
96
|
+
description = secici.select_text(None, description_el[0])
|
|
101
97
|
|
|
102
98
|
tags = [secici.select_text(None, a) for a in secici.select("div.genres a") if secici.select_text(None, a)]
|
|
103
99
|
|
|
@@ -109,9 +105,9 @@ class DiziYou(PluginBase):
|
|
|
109
105
|
actors = [actor.strip() for actor in actors_raw.split(",") if actor.strip()] if actors_raw else []
|
|
110
106
|
|
|
111
107
|
episodes = []
|
|
112
|
-
# Episodes -
|
|
113
|
-
for link in secici.select("a"):
|
|
114
|
-
ep_href = secici.select_attr(
|
|
108
|
+
# Episodes - div#scrollbar-container a (kısıtlı alan)
|
|
109
|
+
for link in secici.select("div#scrollbar-container a"):
|
|
110
|
+
ep_href = secici.select_attr(None, "href", link)
|
|
115
111
|
if not ep_href:
|
|
116
112
|
continue
|
|
117
113
|
|
|
@@ -179,9 +175,9 @@ class DiziYou(PluginBase):
|
|
|
179
175
|
# Player src'den item_id çıkar - önce özel player seçicisini dene
|
|
180
176
|
player_src = None
|
|
181
177
|
# Yaygın locatorlar
|
|
182
|
-
for sel in ["iframe#diziyouPlayer", "div.player iframe", "iframe[src*='/episodes/']", "iframe"]:
|
|
178
|
+
for sel in ["iframe#diziyouPlayer", "div.player iframe", "iframe[src*='/player/']", "iframe[src*='/episodes/']", "iframe"]:
|
|
183
179
|
p = secici.select_attr(sel, "src")
|
|
184
|
-
if p and
|
|
180
|
+
if p and any(x in p.lower() for x in ["/player/", "/episodes/", "diziyou"]):
|
|
185
181
|
player_src = p
|
|
186
182
|
break
|
|
187
183
|
|
|
@@ -137,70 +137,60 @@ class Dizilla(PluginBase):
|
|
|
137
137
|
istek = await self.httpx.get(url)
|
|
138
138
|
secici = HTMLHelper(istek.text)
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
if not
|
|
140
|
+
next_data_text = secici.select_text("script#__NEXT_DATA__")
|
|
141
|
+
if not next_data_text:
|
|
142
142
|
return None
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
description =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
for
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if not links:
|
|
184
|
-
continue
|
|
185
|
-
|
|
186
|
-
ep_link = links[-1]
|
|
187
|
-
ep_name = sezon_secici.select_text("a", ep_link)
|
|
188
|
-
ep_href = sezon_secici.select_attr("a", "href", ep_link)
|
|
189
|
-
ep_href = self.fix_url(ep_href)
|
|
144
|
+
next_data = loads(next_data_text)
|
|
145
|
+
secure_data = next_data.get("props", {}).get("pageProps", {}).get("secureData")
|
|
146
|
+
if not secure_data:
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
decrypted = await self.decrypt_response(secure_data)
|
|
150
|
+
content = decrypted.get("contentItem", {})
|
|
151
|
+
if not content:
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
title = content.get("original_title") or content.get("used_title")
|
|
155
|
+
description = content.get("description") or content.get("used_description")
|
|
156
|
+
rating = content.get("imdb_point") or content.get("local_vote_avg")
|
|
157
|
+
year = content.get("release_year")
|
|
158
|
+
|
|
159
|
+
# Poster and Backdrop - prefer backdrop if available for SeriesInfo
|
|
160
|
+
poster = self.fix_poster_url(self.fix_url(content.get("back_url") or content.get("poster_url")))
|
|
161
|
+
|
|
162
|
+
# Tags
|
|
163
|
+
tags = []
|
|
164
|
+
categories = decrypted.get("RelatedResults", {}).get("getSerieCategoriesById", {}).get("result", [])
|
|
165
|
+
for cat in categories:
|
|
166
|
+
tags.append(cat.get("name"))
|
|
167
|
+
|
|
168
|
+
# Actors
|
|
169
|
+
actors = []
|
|
170
|
+
casts = decrypted.get("RelatedResults", {}).get("getSerieCastsById", {}).get("result", [])
|
|
171
|
+
for cast in casts:
|
|
172
|
+
actors.append(cast.get("name"))
|
|
173
|
+
|
|
174
|
+
# Episodes
|
|
175
|
+
episodes = []
|
|
176
|
+
seasons_data = decrypted.get("RelatedResults", {}).get("getSerieSeasonAndEpisodes", {}).get("result", [])
|
|
177
|
+
for season_item in seasons_data:
|
|
178
|
+
season_num = season_item.get("season_no")
|
|
179
|
+
for ep_item in season_item.get("episodes", []):
|
|
180
|
+
ep_num = ep_item.get("episode_no")
|
|
181
|
+
ep_slug = ep_item.get("used_slug")
|
|
182
|
+
ep_name = ep_item.get("episode_text") or ""
|
|
190
183
|
|
|
191
|
-
#
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
pass
|
|
198
|
-
|
|
199
|
-
episodeses.append(Episode(
|
|
184
|
+
# Filter out duplicate language entries if any (we just need one link per episode)
|
|
185
|
+
# Usually they share the same slug for the episode page
|
|
186
|
+
if any(e.season == season_num and e.episode == ep_num for e in episodes):
|
|
187
|
+
continue
|
|
188
|
+
|
|
189
|
+
episodes.append(Episode(
|
|
200
190
|
season = season_num,
|
|
201
191
|
episode = ep_num,
|
|
202
192
|
title = ep_name,
|
|
203
|
-
url =
|
|
193
|
+
url = self.fix_url(f"{self.main_url}/{ep_slug}")
|
|
204
194
|
))
|
|
205
195
|
|
|
206
196
|
return SeriesInfo(
|
|
@@ -209,8 +199,9 @@ class Dizilla(PluginBase):
|
|
|
209
199
|
title = title,
|
|
210
200
|
description = description,
|
|
211
201
|
tags = tags,
|
|
202
|
+
rating = str(rating) if rating else None,
|
|
212
203
|
year = str(year) if year else None,
|
|
213
|
-
episodes =
|
|
204
|
+
episodes = episodes,
|
|
214
205
|
actors = actors
|
|
215
206
|
)
|
|
216
207
|
|
|
@@ -108,18 +108,22 @@ class FilmBip(PluginBase):
|
|
|
108
108
|
tags = secici.select_all_text("div.series-profile-type.tv-show-profile-type a")
|
|
109
109
|
|
|
110
110
|
# XPath yerine regex kullanarak yıl, süre vs. çıkarma
|
|
111
|
-
year = secici.regex_first(r
|
|
111
|
+
year = secici.regex_first(r"(?is)Yap\u0131m y\u0131l\u0131.*?<p[^>]*>(.*?)<\/p>")
|
|
112
|
+
if not year:
|
|
113
|
+
# Fallback: Başlığın sonundaki parantezli yılı yakala
|
|
114
|
+
year = secici.regex_first(r"\((\d{4})\)", title)
|
|
112
115
|
|
|
113
|
-
|
|
116
|
+
duration_raw = secici.regex_first(r"(?is)S\u00fcre.*?<p[^>]*>(.*?)<\/p>")
|
|
117
|
+
duration = secici.regex_first(r"(\d+)", duration_raw) if duration_raw else None
|
|
114
118
|
|
|
115
|
-
rating = secici.regex_first(r
|
|
119
|
+
rating = secici.regex_first(r"(?is)IMDB Puan\u0131.*?<span[^>]*>(.*?)<\/span>")
|
|
116
120
|
|
|
117
121
|
actors = [img.attrs.get("alt") for img in secici.select("div.series-profile-cast ul li a img") if img.attrs.get("alt")]
|
|
118
122
|
|
|
119
123
|
return MovieInfo(
|
|
120
124
|
url = url,
|
|
121
125
|
poster = self.fix_url(poster) if poster else None,
|
|
122
|
-
title =
|
|
126
|
+
title = HTMLHelper(title).regex_replace(r"\(\d{4}\)", "").strip() if title else "",
|
|
123
127
|
description = description,
|
|
124
128
|
tags = tags,
|
|
125
129
|
year = year,
|
|
@@ -91,7 +91,7 @@ class FilmMakinesi(PluginBase):
|
|
|
91
91
|
year = secici.select_text("span.date a") or ""
|
|
92
92
|
|
|
93
93
|
actors = secici.select_all_text("div.cast-name")
|
|
94
|
-
tags = secici.
|
|
94
|
+
tags = [a.text(strip=True) for a in secici.select("div.type a") if "/tur/" in (a.attrs.get("href") or "")]
|
|
95
95
|
|
|
96
96
|
duration = None
|
|
97
97
|
duration_text = secici.select_text("div.time") or None
|
|
@@ -93,19 +93,13 @@ class FullHDFilmizlesene(PluginBase):
|
|
|
93
93
|
|
|
94
94
|
tags = secici.select_all_text("a[rel='category tag']")
|
|
95
95
|
|
|
96
|
-
# Rating:
|
|
97
|
-
rating_text = secici.select_text("div.puanx-puan") or
|
|
98
|
-
rating =
|
|
99
|
-
if rating_text:
|
|
100
|
-
parts = rating_text.split()
|
|
101
|
-
rating = parts[-1] if parts else None
|
|
96
|
+
# Rating: regex ile sayısal değeri yakala
|
|
97
|
+
rating_text = secici.select_text("div.puanx-puan") or ""
|
|
98
|
+
rating = secici.regex_first(r"(\d+\.\d+|\d+)", rating_text)
|
|
102
99
|
|
|
103
100
|
# Year: ilk yıl formatında değer
|
|
104
|
-
year_text = secici.select_text("div.dd a.category") or
|
|
105
|
-
year =
|
|
106
|
-
if year_text:
|
|
107
|
-
parts = year_text.split()
|
|
108
|
-
year = parts[0] if parts else None
|
|
101
|
+
year_text = secici.select_text("div.dd a.category") or ""
|
|
102
|
+
year = secici.regex_first(r"(\d{4})", year_text)
|
|
109
103
|
|
|
110
104
|
# Actors: nth-child yerine tüm li'leri alıp 2. index
|
|
111
105
|
lis = secici.select("div.film-info ul li")
|
|
@@ -113,11 +107,8 @@ class FullHDFilmizlesene(PluginBase):
|
|
|
113
107
|
if len(lis) >= 2:
|
|
114
108
|
actors = secici.select_all_text("a > span", lis[1])
|
|
115
109
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if duration_text:
|
|
119
|
-
duration_parts = duration_text.split()
|
|
120
|
-
duration = duration_parts[0] if duration_parts else "0"
|
|
110
|
+
# Duration: regex ile yakala (örn: 201 dk)
|
|
111
|
+
duration = secici.regex_first(r"(\d+)\s*(?:dk|dakika)", html_text)
|
|
121
112
|
|
|
122
113
|
return MovieInfo(
|
|
123
114
|
url = url,
|
|
@@ -113,6 +113,10 @@ class JetFilmizle(PluginBase):
|
|
|
113
113
|
year = secici.extract_year("div.yap")
|
|
114
114
|
|
|
115
115
|
actors = secici.select_all_text("div[itemprop='actor'] a span")
|
|
116
|
+
if not actors: # Fallback to img alt
|
|
117
|
+
actors = [img.attrs.get("alt") for img in secici.select("div.oyuncular div.oyuncu img") if img.attrs.get("alt")]
|
|
118
|
+
|
|
119
|
+
duration = secici.regex_first(r"(\d+)\s*dk", istek.text)
|
|
116
120
|
|
|
117
121
|
return MovieInfo(
|
|
118
122
|
url = url,
|
|
@@ -122,7 +126,8 @@ class JetFilmizle(PluginBase):
|
|
|
122
126
|
tags = tags,
|
|
123
127
|
rating = rating,
|
|
124
128
|
year = year,
|
|
125
|
-
actors = actors
|
|
129
|
+
actors = actors,
|
|
130
|
+
duration = int(duration) if duration else None
|
|
126
131
|
)
|
|
127
132
|
|
|
128
133
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
@@ -91,7 +91,8 @@ class KultFilmler(PluginBase):
|
|
|
91
91
|
time_text = secici.select_text("li.time span")
|
|
92
92
|
duration = secici.regex_first(r"(\d+)", time_text) if time_text else None
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
rating_text = secici.select_text("div.imdb-count")
|
|
95
|
+
rating = secici.regex_first(r"(\d+\.\d+|\d+)", rating_text) if rating_text else None
|
|
95
96
|
|
|
96
97
|
actors = [a.text(strip=True) for a in secici.select("div.actors a") if a.text(strip=True)]
|
|
97
98
|
|
|
@@ -96,27 +96,49 @@ class RecTV(PluginBase):
|
|
|
96
96
|
|
|
97
97
|
episodes.append(ep_model)
|
|
98
98
|
|
|
99
|
+
# Süreyi dakikaya çevir (Örn: "1h 59min")
|
|
100
|
+
duration_raw = veri.get("duration")
|
|
101
|
+
duration = None
|
|
102
|
+
if duration_raw:
|
|
103
|
+
try:
|
|
104
|
+
h = int(HTMLHelper(duration_raw).regex_first(r"(\d+)h") or 0)
|
|
105
|
+
m = int(HTMLHelper(duration_raw).regex_first(r"(\d+)min") or 0)
|
|
106
|
+
duration = h * 60 + m
|
|
107
|
+
except: pass
|
|
108
|
+
|
|
99
109
|
return SeriesInfo(
|
|
100
110
|
url = url,
|
|
101
111
|
poster = self.fix_url(veri.get("image")),
|
|
102
112
|
title = veri.get("title"),
|
|
103
113
|
description = veri.get("description"),
|
|
104
114
|
tags = [genre.get("title") for genre in veri.get("genres")] if veri.get("genres") else [],
|
|
105
|
-
rating = veri.get("imdb") or veri.get("rating"),
|
|
106
|
-
year = veri.get("year"),
|
|
115
|
+
rating = str(veri.get("imdb") or veri.get("rating") or ""),
|
|
116
|
+
year = str(veri.get("year") or ""),
|
|
107
117
|
actors = [],
|
|
118
|
+
duration = duration,
|
|
108
119
|
episodes = episodes
|
|
109
120
|
)
|
|
110
121
|
case _:
|
|
122
|
+
# Süreyi dakikaya çevir
|
|
123
|
+
duration_raw = veri.get("duration")
|
|
124
|
+
duration = None
|
|
125
|
+
if duration_raw:
|
|
126
|
+
try:
|
|
127
|
+
h = int(HTMLHelper(duration_raw).regex_first(r"(\d+)h") or 0)
|
|
128
|
+
m = int(HTMLHelper(duration_raw).regex_first(r"(\d+)min") or 0)
|
|
129
|
+
duration = h * 60 + m
|
|
130
|
+
except: pass
|
|
131
|
+
|
|
111
132
|
return MovieInfo(
|
|
112
133
|
url = url,
|
|
113
134
|
poster = self.fix_url(veri.get("image")),
|
|
114
135
|
title = veri.get("title"),
|
|
115
136
|
description = veri.get("description"),
|
|
116
137
|
tags = [genre.get("title") for genre in veri.get("genres")] if veri.get("genres") else [],
|
|
117
|
-
rating = veri.get("imdb") or veri.get("rating"),
|
|
118
|
-
year = veri.get("year"),
|
|
119
|
-
actors = []
|
|
138
|
+
rating = str(veri.get("imdb") or veri.get("rating") or ""),
|
|
139
|
+
year = str(veri.get("year") or ""),
|
|
140
|
+
actors = [],
|
|
141
|
+
duration = duration
|
|
120
142
|
)
|
|
121
143
|
|
|
122
144
|
async def load_links(self, url: str) -> list[ExtractResult]:
|