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.
- KekikStream/Core/Extractor/ExtractorBase.py +3 -2
- KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
- KekikStream/Core/HTMLHelper.py +205 -0
- KekikStream/Core/Plugin/PluginBase.py +48 -12
- KekikStream/Core/Plugin/PluginLoader.py +13 -14
- KekikStream/Core/Plugin/PluginManager.py +2 -2
- KekikStream/Core/Plugin/PluginModels.py +0 -3
- KekikStream/Core/__init__.py +2 -0
- KekikStream/Extractors/Abstream.py +27 -0
- KekikStream/Extractors/CloseLoad.py +31 -56
- KekikStream/Extractors/ContentX.py +28 -71
- KekikStream/Extractors/DonilasPlay.py +34 -78
- KekikStream/Extractors/DzenRu.py +11 -25
- KekikStream/Extractors/ExPlay.py +20 -38
- KekikStream/Extractors/Filemoon.py +23 -53
- KekikStream/Extractors/HDMomPlayer.py +30 -0
- KekikStream/Extractors/HDPlayerSystem.py +13 -31
- KekikStream/Extractors/HotStream.py +27 -0
- KekikStream/Extractors/JFVid.py +3 -24
- KekikStream/Extractors/JetTv.py +21 -34
- KekikStream/Extractors/JetV.py +55 -0
- KekikStream/Extractors/MailRu.py +11 -29
- KekikStream/Extractors/MixPlayHD.py +17 -31
- KekikStream/Extractors/MixTiger.py +17 -40
- KekikStream/Extractors/MolyStream.py +25 -22
- KekikStream/Extractors/Odnoklassniki.py +41 -105
- KekikStream/Extractors/PeaceMakerst.py +20 -47
- KekikStream/Extractors/PixelDrain.py +9 -16
- KekikStream/Extractors/PlayerFilmIzle.py +23 -46
- KekikStream/Extractors/RapidVid.py +23 -36
- KekikStream/Extractors/SetPlay.py +19 -44
- KekikStream/Extractors/SetPrime.py +3 -6
- KekikStream/Extractors/SibNet.py +8 -19
- KekikStream/Extractors/Sobreatsesuyp.py +25 -47
- KekikStream/Extractors/TRsTX.py +25 -55
- KekikStream/Extractors/TurboImgz.py +8 -16
- KekikStream/Extractors/TurkeyPlayer.py +5 -5
- KekikStream/Extractors/VCTPlay.py +10 -28
- KekikStream/Extractors/Veev.py +145 -0
- KekikStream/Extractors/VidBiz.py +62 -0
- KekikStream/Extractors/VidHide.py +59 -34
- KekikStream/Extractors/VidMoly.py +67 -89
- KekikStream/Extractors/VidMoxy.py +17 -29
- KekikStream/Extractors/VidPapi.py +26 -58
- KekikStream/Extractors/VideoSeyred.py +21 -42
- KekikStream/Extractors/Videostr.py +58 -0
- KekikStream/Extractors/Vidoza.py +18 -0
- KekikStream/Extractors/Vtbe.py +38 -0
- KekikStream/Extractors/YTDLP.py +2 -2
- KekikStream/Extractors/YildizKisaFilm.py +13 -31
- KekikStream/Extractors/Zeus.py +61 -0
- KekikStream/Plugins/BelgeselX.py +108 -99
- KekikStream/Plugins/DiziBox.py +61 -106
- KekikStream/Plugins/DiziMom.py +179 -0
- KekikStream/Plugins/DiziPal.py +104 -192
- KekikStream/Plugins/DiziYou.py +66 -149
- KekikStream/Plugins/Dizilla.py +93 -126
- KekikStream/Plugins/FilmBip.py +102 -72
- KekikStream/Plugins/FilmEkseni.py +199 -0
- KekikStream/Plugins/FilmMakinesi.py +101 -64
- KekikStream/Plugins/FilmModu.py +35 -59
- KekikStream/Plugins/Filmatek.py +184 -0
- KekikStream/Plugins/FilmciBaba.py +155 -0
- KekikStream/Plugins/FullHDFilmizlesene.py +32 -78
- KekikStream/Plugins/HDFilm.py +243 -0
- KekikStream/Plugins/HDFilmCehennemi.py +261 -222
- KekikStream/Plugins/JetFilmizle.py +117 -98
- KekikStream/Plugins/KultFilmler.py +153 -143
- KekikStream/Plugins/RecTV.py +53 -49
- KekikStream/Plugins/RoketDizi.py +92 -123
- KekikStream/Plugins/SelcukFlix.py +86 -95
- KekikStream/Plugins/SetFilmIzle.py +105 -143
- KekikStream/Plugins/SezonlukDizi.py +106 -128
- KekikStream/Plugins/Sinefy.py +194 -166
- KekikStream/Plugins/SinemaCX.py +159 -113
- KekikStream/Plugins/Sinezy.py +44 -73
- KekikStream/Plugins/SuperFilmGeldi.py +28 -52
- KekikStream/Plugins/UgurFilm.py +94 -72
- KekikStream/Plugins/Watch32.py +160 -0
- KekikStream/Plugins/YabanciDizi.py +250 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/METADATA +1 -1
- kekikstream-2.5.3.dist-info/RECORD +99 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/WHEEL +1 -1
- KekikStream/Plugins/FullHDFilm.py +0 -254
- kekikstream-2.2.9.dist-info/RECORD +0 -82
- {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
4
|
+
from contextlib import suppress
|
|
5
|
+
|
|
6
|
+
class Veev(ExtractorBase):
|
|
7
|
+
name = "Veev"
|
|
8
|
+
main_url = "https://veev.to"
|
|
9
|
+
|
|
10
|
+
supported_domains = ["veev.to", "kinoger.to", "poophq.com", "doods.pro", "dood.so", "dood.li", "dood.wf", "dood.cx", "dood.sh", "dood.watch", "dood.pm", "dood.to", "dood.ws"]
|
|
11
|
+
|
|
12
|
+
def can_handle_url(self, url: str) -> bool:
|
|
13
|
+
return any(domain in url for domain in self.supported_domains)
|
|
14
|
+
|
|
15
|
+
def veev_decode(self, encoded: str) -> str:
|
|
16
|
+
if not encoded:
|
|
17
|
+
return ""
|
|
18
|
+
|
|
19
|
+
result = []
|
|
20
|
+
# Python dictionary key integer, value string
|
|
21
|
+
# Başlangıçta 0-255 ascii karakterleri
|
|
22
|
+
lut = {i: chr(i) for i in range(256)}
|
|
23
|
+
n = 256
|
|
24
|
+
|
|
25
|
+
c = encoded[0]
|
|
26
|
+
result.append(c)
|
|
27
|
+
|
|
28
|
+
for char in encoded[1:]:
|
|
29
|
+
code = ord(char)
|
|
30
|
+
if code < 256:
|
|
31
|
+
nc = char
|
|
32
|
+
else:
|
|
33
|
+
nc = lut.get(code, c + c[0])
|
|
34
|
+
|
|
35
|
+
result.append(nc)
|
|
36
|
+
lut[n] = c + nc[0]
|
|
37
|
+
n += 1
|
|
38
|
+
c = nc
|
|
39
|
+
|
|
40
|
+
return "".join(result)
|
|
41
|
+
|
|
42
|
+
def build_array(self, encoded: str) -> list[list[int]]:
|
|
43
|
+
result = []
|
|
44
|
+
iterator = iter(encoded)
|
|
45
|
+
|
|
46
|
+
def next_int_or_zero():
|
|
47
|
+
try:
|
|
48
|
+
char = next(iterator)
|
|
49
|
+
if char.isdigit():
|
|
50
|
+
return int(char)
|
|
51
|
+
return 0
|
|
52
|
+
except StopIteration:
|
|
53
|
+
return 0
|
|
54
|
+
|
|
55
|
+
count = next_int_or_zero()
|
|
56
|
+
while count != 0:
|
|
57
|
+
row = []
|
|
58
|
+
for _ in range(count):
|
|
59
|
+
row.append(next_int_or_zero())
|
|
60
|
+
result.append(list(reversed(row)))
|
|
61
|
+
count = next_int_or_zero()
|
|
62
|
+
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
def decode_url(self, encoded: str, rules: list[int]) -> str:
|
|
66
|
+
text = encoded
|
|
67
|
+
for r in rules:
|
|
68
|
+
if r == 1:
|
|
69
|
+
text = text[::-1]
|
|
70
|
+
|
|
71
|
+
# Hex decode
|
|
72
|
+
with suppress(Exception):
|
|
73
|
+
# remove optional whitespace just in case
|
|
74
|
+
clean_hex = "".join(text.split())
|
|
75
|
+
arr = bytes.fromhex(clean_hex)
|
|
76
|
+
# utf-8 decode, replace errors
|
|
77
|
+
text = arr.decode('utf-8', errors='replace')
|
|
78
|
+
|
|
79
|
+
text = text.replace("dXRmOA==", "")
|
|
80
|
+
|
|
81
|
+
return text
|
|
82
|
+
|
|
83
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
84
|
+
# URL'den ID çıkar
|
|
85
|
+
# https://veev.to/e/lla8v3k6arev
|
|
86
|
+
video_id = url.split("/")[-1]
|
|
87
|
+
|
|
88
|
+
# Sayfayı al
|
|
89
|
+
# Referer lazım mı? Genelde lazım olabilir.
|
|
90
|
+
page_url = f"{self.main_url}/e/{video_id}"
|
|
91
|
+
resp = await self.httpx.get(page_url, headers={"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"})
|
|
92
|
+
html = resp.text
|
|
93
|
+
|
|
94
|
+
# Regex ile şifreli stringleri bul
|
|
95
|
+
# Regex: [.\s'](?:fc|_vvto\[[^]]*)(?:['\]]*)?\s*[:=]\s*['"]([^'"]+)
|
|
96
|
+
# Python regex için düzenleme gerekebilir.
|
|
97
|
+
found_values = HTMLHelper(html).regex_all(r"[.\s'](?:fc|_vvto\[[^]]*)(?:['\]]*)?\s*[:=]\s*['\"]([^'\"]+)")
|
|
98
|
+
|
|
99
|
+
if not found_values:
|
|
100
|
+
raise ValueError(f"Veev: Token bulunamadı. {url}")
|
|
101
|
+
|
|
102
|
+
# Kotlin kodunda sondan başlayıp deniyor (reversed)
|
|
103
|
+
for f in reversed(found_values):
|
|
104
|
+
try:
|
|
105
|
+
ch = self.veev_decode(f)
|
|
106
|
+
if ch == f:
|
|
107
|
+
continue # Decode olmadıysa geç
|
|
108
|
+
|
|
109
|
+
# API Call
|
|
110
|
+
dl_url = f"{self.main_url}/dl?op=player_api&cmd=gi&file_code={video_id}&r={self.main_url}&ch={ch}&ie=1"
|
|
111
|
+
api_resp = await self.httpx.get(dl_url, headers={"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"})
|
|
112
|
+
|
|
113
|
+
data = api_resp.json()
|
|
114
|
+
file_obj = data.get("file")
|
|
115
|
+
if not file_obj or file_obj.get("file_status") != "OK":
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
dv = file_obj.get("dv")
|
|
119
|
+
# dv json string içinde s key'inde olabilir (Kotlin: getString("s"))
|
|
120
|
+
# Ancak api yanıtını görmeden emin olamayız, json yapısına göre file->dv bir string mi object mi?
|
|
121
|
+
# Kotlin: file.getJSONArray("dv").getJSONObject(0).getString("s")
|
|
122
|
+
# Demek ki dv bir array
|
|
123
|
+
|
|
124
|
+
encoded_dv = None
|
|
125
|
+
if isinstance(dv, list) and len(dv) > 0:
|
|
126
|
+
if isinstance(dv[0], dict):
|
|
127
|
+
encoded_dv = dv[0].get("s")
|
|
128
|
+
|
|
129
|
+
if not encoded_dv:
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
# Decode
|
|
133
|
+
# rules = buildArray(ch)[0]
|
|
134
|
+
rules = self.build_array(ch)[0]
|
|
135
|
+
|
|
136
|
+
final_url = self.decode_url(self.veev_decode(encoded_dv), rules)
|
|
137
|
+
|
|
138
|
+
if final_url.startswith("http"):
|
|
139
|
+
return ExtractResult(name=self.name, url=self.fix_url(final_url), referer=self.main_url)
|
|
140
|
+
|
|
141
|
+
except Exception as e:
|
|
142
|
+
# print(f"Veev Error: {e}")
|
|
143
|
+
continue
|
|
144
|
+
|
|
145
|
+
raise ValueError(f"Veev: Video URL'si çözülemedi. {url}")
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
4
|
+
from Kekik.Sifreleme import Packer
|
|
5
|
+
|
|
6
|
+
class VidBiz(ExtractorBase):
|
|
7
|
+
name = "VidBiz"
|
|
8
|
+
main_url = "https://videolar.biz"
|
|
9
|
+
|
|
10
|
+
async def extract(self, url: str, referer: str = None) -> list[ExtractResult]:
|
|
11
|
+
istek = await self.httpx.get(url, headers={"Referer": referer} if referer else None)
|
|
12
|
+
text = istek.text
|
|
13
|
+
|
|
14
|
+
# Eval script bul (kaken içeriyor olmalı)
|
|
15
|
+
eval_script = HTMLHelper(text).regex_first(r'(eval\(function[\s\S]+?)<\/script>') or \
|
|
16
|
+
HTMLHelper(text).regex_first(r'(eval\(function[\s\S]+)')
|
|
17
|
+
if not eval_script:
|
|
18
|
+
raise ValueError(f"VidBiz: Packed script bulunamadı. {url}")
|
|
19
|
+
|
|
20
|
+
unpacked = ""
|
|
21
|
+
try:
|
|
22
|
+
unpacked = Packer.unpack(eval_script)
|
|
23
|
+
except:
|
|
24
|
+
raise ValueError("VidBiz: Unpack hatası")
|
|
25
|
+
|
|
26
|
+
# window.kaken="..."
|
|
27
|
+
kaken = HTMLHelper(unpacked).regex_first(r'window\.kaken\s*=\s*"([^"]+)"')
|
|
28
|
+
if not kaken:
|
|
29
|
+
raise ValueError("VidBiz: Kaken token bulunamadı")
|
|
30
|
+
|
|
31
|
+
# API POST
|
|
32
|
+
# Content-Type: text/plain önemli olabilir
|
|
33
|
+
resp = await self.httpx.post(
|
|
34
|
+
url = "https://s2.videolar.biz/api/",
|
|
35
|
+
content = kaken, # data yerine content=raw string
|
|
36
|
+
headers = {"Content-Type": "text/plain", "Referer": url}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
data = resp.json()
|
|
41
|
+
except:
|
|
42
|
+
raise ValueError("VidBiz: API yanıtı JSON değil")
|
|
43
|
+
|
|
44
|
+
if data.get("status") != "ok":
|
|
45
|
+
raise ValueError(f"VidBiz: API hatası {data}")
|
|
46
|
+
|
|
47
|
+
results = []
|
|
48
|
+
for source in data.get("sources", []):
|
|
49
|
+
file_url = source.get("file")
|
|
50
|
+
label = source.get("label", "Unknown")
|
|
51
|
+
|
|
52
|
+
if not file_url:
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
results.append(ExtractResult(
|
|
56
|
+
name = f"{self.name} | {label}",
|
|
57
|
+
url = self.fix_url(file_url),
|
|
58
|
+
referer = url,
|
|
59
|
+
user_agent = self.httpx.headers.get("User-Agent", "")
|
|
60
|
+
))
|
|
61
|
+
|
|
62
|
+
return results
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
4
4
|
from Kekik.Sifreleme import Packer
|
|
5
5
|
import re
|
|
6
6
|
|
|
@@ -9,7 +9,16 @@ class VidHide(ExtractorBase):
|
|
|
9
9
|
main_url = "https://vidhidepro.com"
|
|
10
10
|
|
|
11
11
|
# Birden fazla domain destekle
|
|
12
|
-
supported_domains = [
|
|
12
|
+
supported_domains = [
|
|
13
|
+
"vidhidepro.com", "vidhide.com", "rubyvidhub.com",
|
|
14
|
+
"vidhidevip.com", "vidhideplus.com", "vidhidepre.com",
|
|
15
|
+
"movearnpre.com", "oneupload.to",
|
|
16
|
+
"filelions.live", "filelions.online", "filelions.to",
|
|
17
|
+
"kinoger.be",
|
|
18
|
+
"smoothpre.com",
|
|
19
|
+
"dhtpre.com",
|
|
20
|
+
"peytonepre.com"
|
|
21
|
+
]
|
|
13
22
|
|
|
14
23
|
def can_handle_url(self, url: str) -> bool:
|
|
15
24
|
return any(domain in url for domain in self.supported_domains)
|
|
@@ -21,53 +30,70 @@ class VidHide(ExtractorBase):
|
|
|
21
30
|
return url.replace("/download/", "/v/")
|
|
22
31
|
elif "/file/" in url:
|
|
23
32
|
return url.replace("/file/", "/v/")
|
|
33
|
+
elif "/embed/" in url:
|
|
34
|
+
return url.replace("/embed/", "/v/")
|
|
24
35
|
else:
|
|
25
36
|
return url.replace("/f/", "/v/")
|
|
26
37
|
|
|
27
38
|
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
28
|
-
# Dinamik base URL kullan
|
|
29
39
|
base_url = self.get_base_url(url)
|
|
30
|
-
|
|
31
|
-
if referer:
|
|
32
|
-
self.httpx.headers.update({"Referer": referer})
|
|
33
|
-
|
|
34
40
|
self.httpx.headers.update({
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"Sec-Fetch-Site" : "cross-site",
|
|
38
|
-
"Origin" : base_url,
|
|
41
|
+
"Referer" : referer or base_url,
|
|
42
|
+
"Origin" : base_url,
|
|
39
43
|
})
|
|
40
44
|
|
|
41
45
|
embed_url = self.get_embed_url(url)
|
|
42
|
-
istek = await self.httpx.get(embed_url)
|
|
43
|
-
|
|
46
|
+
istek = await self.httpx.get(embed_url, follow_redirects=True)
|
|
47
|
+
text = istek.text
|
|
48
|
+
|
|
49
|
+
# Silinmiş dosya kontrolü
|
|
50
|
+
if "File is no longer available" in text or "File Not Found" in text:
|
|
51
|
+
raise ValueError(f"VidHide: Video silinmiş. {url}")
|
|
52
|
+
|
|
53
|
+
# JS Redirect Kontrolü (OneUpload vb.)
|
|
54
|
+
if js_redirect := HTMLHelper(text).regex_first(r"window\.location\.replace\(['\"]([^'\"]+)['\"]\)") or \
|
|
55
|
+
HTMLHelper(text).regex_first(r"window\.location\.href\s*=\s*['\"]([^'\"]+)['\"]"):
|
|
56
|
+
# Redirect url'i al
|
|
57
|
+
target_url = js_redirect
|
|
58
|
+
# Bazen path relative olabilir ama genelde full url
|
|
59
|
+
if not target_url.startswith("http"):
|
|
60
|
+
# urljoin gerekebilir ama şimdilik doğrudan deneyelim veya fix_url
|
|
61
|
+
target_url = self.fix_url(target_url) # fix_url base'e göre düzeltebilir mi? ExtractorBase.fix_url genelde şema ekler.
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
# Yeniden istek at
|
|
65
|
+
istek = await self.httpx.get(target_url, headers={"Referer": embed_url}, follow_redirects=True)
|
|
66
|
+
text = istek.text
|
|
44
67
|
|
|
45
|
-
|
|
46
|
-
|
|
68
|
+
sel = HTMLHelper(text)
|
|
69
|
+
|
|
70
|
+
unpacked = ""
|
|
71
|
+
# Eval script bul (regex ile daha sağlam)
|
|
72
|
+
if eval_match := sel.regex_first(r'(eval\s*\(\s*function[\s\S]+?)<\/script>'):
|
|
47
73
|
try:
|
|
48
|
-
unpacked = Packer.unpack(
|
|
74
|
+
unpacked = Packer.unpack(eval_match)
|
|
49
75
|
if "var links" in unpacked:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
script = unpacked
|
|
53
|
-
except Exception:
|
|
76
|
+
unpacked = unpacked.split("var links")[1]
|
|
77
|
+
except:
|
|
54
78
|
pass
|
|
55
79
|
|
|
56
|
-
|
|
57
|
-
if matches := re.search(r'sources:\s*(\[.*?\])', response, re.DOTALL):
|
|
58
|
-
script = matches.group(1)
|
|
80
|
+
content = unpacked or text
|
|
59
81
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if match := re.search(r':\s*"([^"]*?m3u8[^"]*?)"', script):
|
|
64
|
-
m3u8_url = match.group(1)
|
|
82
|
+
# Regex: Kotlin mantığı (: "url")
|
|
83
|
+
# Ayrıca sources: [...] mantığını da ekle
|
|
84
|
+
m3u8_url = HTMLHelper(content).regex_first(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"')
|
|
65
85
|
|
|
66
86
|
if not m3u8_url:
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
87
|
+
# Genel arama (hls:, file: vb.)
|
|
88
|
+
# Kotlin Regex: :\s*"(.*?m3u8.*?)"
|
|
89
|
+
match = HTMLHelper(content).regex_first(r':\s*["\']([^"\']+\.m3u8[^"\']*)["\']')
|
|
90
|
+
if match:
|
|
91
|
+
m3u8_url = match
|
|
92
|
+
|
|
93
|
+
if not m3u8_url:
|
|
94
|
+
# Son şans: herhangi bir m3u8 linki
|
|
95
|
+
m3u8_url = HTMLHelper(content).regex_first(r'["\']([^"\']+\.m3u8[^"\']*)["\']')
|
|
96
|
+
|
|
71
97
|
if not m3u8_url:
|
|
72
98
|
raise ValueError(f"VidHide: Video URL bulunamadı. {url}")
|
|
73
99
|
|
|
@@ -75,6 +101,5 @@ class VidHide(ExtractorBase):
|
|
|
75
101
|
name = self.name,
|
|
76
102
|
url = self.fix_url(m3u8_url),
|
|
77
103
|
referer = f"{base_url}/",
|
|
78
|
-
user_agent = self.httpx.headers.get("User-Agent", "")
|
|
79
|
-
subtitles = []
|
|
104
|
+
user_agent = self.httpx.headers.get("User-Agent", "")
|
|
80
105
|
)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
# ! https://github.com/recloudstream/cloudstream/blob/master/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidmoly.kt
|
|
3
3
|
|
|
4
|
-
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
5
|
-
from selectolax.parser import HTMLParser
|
|
4
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
6
5
|
import re, contextlib, json
|
|
7
6
|
|
|
8
7
|
class VidMoly(ExtractorBase):
|
|
@@ -10,7 +9,7 @@ class VidMoly(ExtractorBase):
|
|
|
10
9
|
main_url = "https://vidmoly.to"
|
|
11
10
|
|
|
12
11
|
# Birden fazla domain destekle
|
|
13
|
-
supported_domains = ["vidmoly.to", "vidmoly.me", "vidmoly.net"]
|
|
12
|
+
supported_domains = ["vidmoly.to", "vidmoly.me", "vidmoly.net", "vidmoly.biz"]
|
|
14
13
|
|
|
15
14
|
def can_handle_url(self, url: str) -> bool:
|
|
16
15
|
return any(domain in url for domain in self.supported_domains)
|
|
@@ -19,102 +18,81 @@ class VidMoly(ExtractorBase):
|
|
|
19
18
|
if referer:
|
|
20
19
|
self.httpx.headers.update({"Referer": referer})
|
|
21
20
|
|
|
22
|
-
self.httpx.headers.update({
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if "Select number" in
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"nonce" : nonce_el.attrs.get("value") if nonce_el else None,
|
|
49
|
-
"ctok" : ctok_el.attrs.get("value") if ctok_el else None
|
|
50
|
-
},
|
|
51
|
-
follow_redirects=True
|
|
52
|
-
)
|
|
53
|
-
|
|
21
|
+
self.httpx.headers.update({"Sec-Fetch-Dest" : "iframe"})
|
|
22
|
+
|
|
23
|
+
# Domain normalleştirme
|
|
24
|
+
url = url.replace(".me", ".net").replace(".to", ".net")
|
|
25
|
+
|
|
26
|
+
resp = await self.httpx.get(url, follow_redirects=True)
|
|
27
|
+
sel = HTMLHelper(resp.text)
|
|
28
|
+
|
|
29
|
+
# "Select number" kontrolü (Bot koruması)
|
|
30
|
+
if "Select number" in resp.text:
|
|
31
|
+
op_val = sel.select_attr("input[name='op']", "value")
|
|
32
|
+
file_code_val = sel.select_attr("input[name='file_code']", "value")
|
|
33
|
+
answer_val = sel.select_text("div.vhint b")
|
|
34
|
+
ts_val = sel.select_attr("input[name='ts']", "value")
|
|
35
|
+
nonce_val = sel.select_attr("input[name='nonce']", "value")
|
|
36
|
+
ctok_val = sel.select_attr("input[name='ctok']", "value")
|
|
37
|
+
|
|
38
|
+
resp = await self.httpx.post(url, data={
|
|
39
|
+
"op" : op_val,
|
|
40
|
+
"file_code" : file_code_val,
|
|
41
|
+
"answer" : answer_val,
|
|
42
|
+
"ts" : ts_val,
|
|
43
|
+
"nonce" : nonce_val,
|
|
44
|
+
"ctok" : ctok_val
|
|
45
|
+
}, follow_redirects=True)
|
|
46
|
+
sel = HTMLHelper(resp.text)
|
|
54
47
|
|
|
55
48
|
# Altyazı kaynaklarını ayrıştır
|
|
56
49
|
subtitles = []
|
|
57
|
-
if
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
if sub_str := sel.regex_first(r"(?s)tracks:\s*\[(.*?)\]"):
|
|
51
|
+
sub_data = self._add_marks(sub_str, "file")
|
|
52
|
+
sub_data = self._add_marks(sub_data, "label")
|
|
53
|
+
sub_data = self._add_marks(sub_data, "kind")
|
|
61
54
|
|
|
62
55
|
with contextlib.suppress(json.JSONDecodeError):
|
|
63
|
-
|
|
56
|
+
sub_sources = json.loads(f"[{sub_data}]")
|
|
64
57
|
subtitles = [
|
|
65
|
-
Subtitle(
|
|
66
|
-
|
|
67
|
-
url = self.fix_url(sub.get("file")),
|
|
68
|
-
)
|
|
69
|
-
for sub in subtitle_sources
|
|
70
|
-
if sub.get("kind") == "captions"
|
|
58
|
+
Subtitle(name=sub.get("label"), url=self.fix_url(sub.get("file")))
|
|
59
|
+
for sub in sub_sources if sub.get("kind") == "captions"
|
|
71
60
|
]
|
|
72
61
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
url_candidate = file_match.group(1)
|
|
105
|
-
# Resim dosyalarını hariç tut
|
|
106
|
-
if not url_candidate.endswith(('.jpg', '.png', '.jpeg')):
|
|
107
|
-
return ExtractResult(
|
|
108
|
-
name = self.name,
|
|
109
|
-
url = url_candidate,
|
|
110
|
-
referer = self.main_url,
|
|
111
|
-
subtitles = subtitles
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
raise ValueError("Video URL bulunamadı.")
|
|
62
|
+
# Video URL Bulma
|
|
63
|
+
video_url = None
|
|
64
|
+
if "#EXTM3U" in resp.text:
|
|
65
|
+
for line in resp.text.splitlines():
|
|
66
|
+
if line.strip().startswith("http"):
|
|
67
|
+
video_url = line.strip().replace('"', '').replace("'", "")
|
|
68
|
+
break
|
|
69
|
+
|
|
70
|
+
if not video_url:
|
|
71
|
+
if src_str := sel.regex_first(r"(?s)sources:\s*\[(.*?)\],"):
|
|
72
|
+
vid_data = self._add_marks(src_str, "file")
|
|
73
|
+
with contextlib.suppress(json.JSONDecodeError):
|
|
74
|
+
vid_sources = json.loads(f"[{vid_data}]")
|
|
75
|
+
for source in vid_sources:
|
|
76
|
+
if source.get("file"):
|
|
77
|
+
video_url = source.get("file")
|
|
78
|
+
break
|
|
79
|
+
|
|
80
|
+
if not video_url:
|
|
81
|
+
video_url = sel.regex_first(r'file\s*:\s*["\']([^"\']+\.m3u8[^"\']*)["\']') or \
|
|
82
|
+
sel.regex_first(r'file\s*:\s*["\']([^"\']+\.mp4[^"\']*)["\']')
|
|
83
|
+
|
|
84
|
+
if not video_url:
|
|
85
|
+
raise ValueError(f"VidMoly: Video URL bulunamadı. {url}")
|
|
86
|
+
|
|
87
|
+
return ExtractResult(
|
|
88
|
+
name = self.name,
|
|
89
|
+
url = video_url,
|
|
90
|
+
referer = f"{self.get_base_url(url)}/",
|
|
91
|
+
subtitles = subtitles
|
|
92
|
+
)
|
|
115
93
|
|
|
116
94
|
def _add_marks(self, text: str, field: str) -> str:
|
|
117
95
|
"""
|
|
118
96
|
Verilen alanı çift tırnak içine alır.
|
|
119
97
|
"""
|
|
120
|
-
return
|
|
98
|
+
return HTMLHelper(text).regex_replace(rf"\"?{field}\"?", f"\"{field}\"")
|
|
@@ -1,49 +1,37 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
4
4
|
from Kekik.Sifreleme import Packer, HexCodec
|
|
5
|
-
import re
|
|
6
5
|
|
|
7
6
|
class VidMoxy(ExtractorBase):
|
|
8
7
|
name = "VidMoxy"
|
|
9
8
|
main_url = "https://vidmoxy.com"
|
|
10
9
|
|
|
11
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
10
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
12
11
|
if referer:
|
|
13
12
|
self.httpx.headers.update({"Referer": referer})
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
resp = await self.httpx.get(url)
|
|
15
|
+
sel = HTMLHelper(resp.text)
|
|
17
16
|
|
|
18
|
-
subtitles
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
subtitles = []
|
|
18
|
+
for s_url, s_lang in sel.regex_all(r'captions","file":"([^\"]+)","label":"([^\"]+)"'):
|
|
19
|
+
decoded_lang = s_lang.encode().decode('unicode_escape')
|
|
20
|
+
subtitles.append(Subtitle(name=decoded_lang, url=s_url.replace("\\", "")))
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
hex_data = sel.regex_first(r'file": "(.*)",')
|
|
23
|
+
if not hex_data:
|
|
24
|
+
eval_data = sel.regex_first(r'\};\s*(eval\(function[\s\S]*?)var played = \d+;')
|
|
25
|
+
if eval_data:
|
|
26
|
+
unpacked = Packer.unpack(Packer.unpack(eval_data))
|
|
27
|
+
hex_data = HTMLHelper(unpacked).regex_first(r'file":"(.*)","label')
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
sub_lang.replace("\\u0131", "ı")
|
|
29
|
-
.replace("\\u0130", "İ")
|
|
30
|
-
.replace("\\u00fc", "ü")
|
|
31
|
-
.replace("\\u00e7", "ç")
|
|
32
|
-
)
|
|
33
|
-
subtitles.append(Subtitle(name=decoded_lang, url=sub_url.replace("\\", "")))
|
|
34
|
-
|
|
35
|
-
try:
|
|
36
|
-
escaped_hex = re.findall(r'file": "(.*)",', istek.text)[0]
|
|
37
|
-
except Exception:
|
|
38
|
-
eval_jwsetup = re.compile(r'\};\s*(eval\(function[\s\S]*?)var played = \d+;').findall(istek.text)[0]
|
|
39
|
-
jwsetup = Packer.unpack(Packer.unpack(eval_jwsetup))
|
|
40
|
-
escaped_hex = re.findall(r'file":"(.*)","label', jwsetup)[0]
|
|
41
|
-
|
|
42
|
-
m3u_link = HexCodec.decode(escaped_hex)
|
|
29
|
+
if not hex_data:
|
|
30
|
+
raise ValueError(f"VidMoxy: Hex data bulunamadı. {url}")
|
|
43
31
|
|
|
44
32
|
return ExtractResult(
|
|
45
33
|
name = self.name,
|
|
46
|
-
url =
|
|
34
|
+
url = HexCodec.decode(hex_data),
|
|
47
35
|
referer = self.main_url,
|
|
48
36
|
subtitles = subtitles
|
|
49
37
|
)
|