KekikStream 2.3.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.
- KekikStream/Core/Extractor/ExtractorBase.py +3 -2
- KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
- KekikStream/Core/HTMLHelper.py +120 -49
- KekikStream/Core/Plugin/PluginBase.py +30 -12
- KekikStream/Core/Plugin/PluginLoader.py +12 -14
- KekikStream/Core/Plugin/PluginManager.py +2 -2
- KekikStream/Core/Plugin/PluginModels.py +0 -3
- KekikStream/Extractors/Abstream.py +27 -0
- KekikStream/Extractors/CloseLoad.py +30 -54
- KekikStream/Extractors/ContentX.py +27 -72
- KekikStream/Extractors/DonilasPlay.py +33 -77
- KekikStream/Extractors/DzenRu.py +10 -24
- KekikStream/Extractors/ExPlay.py +20 -38
- KekikStream/Extractors/Filemoon.py +21 -46
- 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 +15 -28
- KekikStream/Extractors/MixTiger.py +17 -40
- KekikStream/Extractors/MolyStream.py +17 -21
- KekikStream/Extractors/Odnoklassniki.py +40 -104
- KekikStream/Extractors/PeaceMakerst.py +18 -45
- KekikStream/Extractors/PixelDrain.py +8 -16
- KekikStream/Extractors/PlayerFilmIzle.py +22 -41
- KekikStream/Extractors/RapidVid.py +21 -35
- KekikStream/Extractors/SetPlay.py +18 -43
- KekikStream/Extractors/SibNet.py +7 -17
- KekikStream/Extractors/Sobreatsesuyp.py +23 -45
- KekikStream/Extractors/TRsTX.py +23 -53
- KekikStream/Extractors/TurboImgz.py +7 -14
- KekikStream/Extractors/VCTPlay.py +10 -28
- KekikStream/Extractors/Veev.py +145 -0
- KekikStream/Extractors/VidBiz.py +62 -0
- KekikStream/Extractors/VidHide.py +58 -30
- KekikStream/Extractors/VidMoly.py +65 -99
- KekikStream/Extractors/VidMoxy.py +16 -27
- KekikStream/Extractors/VidPapi.py +24 -54
- KekikStream/Extractors/VideoSeyred.py +19 -40
- 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 +97 -77
- KekikStream/Plugins/DiziBox.py +28 -45
- KekikStream/Plugins/DiziMom.py +179 -0
- KekikStream/Plugins/DiziPal.py +95 -161
- KekikStream/Plugins/DiziYou.py +51 -147
- KekikStream/Plugins/Dizilla.py +40 -61
- KekikStream/Plugins/FilmBip.py +90 -39
- KekikStream/Plugins/FilmEkseni.py +199 -0
- KekikStream/Plugins/FilmMakinesi.py +72 -73
- KekikStream/Plugins/FilmModu.py +25 -35
- KekikStream/Plugins/Filmatek.py +184 -0
- KekikStream/Plugins/FilmciBaba.py +155 -0
- KekikStream/Plugins/FullHDFilmizlesene.py +16 -37
- KekikStream/Plugins/HDFilm.py +243 -0
- KekikStream/Plugins/HDFilmCehennemi.py +242 -189
- KekikStream/Plugins/JetFilmizle.py +101 -69
- KekikStream/Plugins/KultFilmler.py +138 -104
- KekikStream/Plugins/RecTV.py +52 -73
- KekikStream/Plugins/RoketDizi.py +18 -27
- KekikStream/Plugins/SelcukFlix.py +30 -48
- KekikStream/Plugins/SetFilmIzle.py +76 -104
- KekikStream/Plugins/SezonlukDizi.py +90 -94
- KekikStream/Plugins/Sinefy.py +195 -167
- KekikStream/Plugins/SinemaCX.py +148 -78
- KekikStream/Plugins/Sinezy.py +29 -31
- KekikStream/Plugins/SuperFilmGeldi.py +12 -17
- KekikStream/Plugins/UgurFilm.py +85 -38
- KekikStream/Plugins/Watch32.py +160 -0
- KekikStream/Plugins/YabanciDizi.py +176 -211
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/METADATA +1 -1
- kekikstream-2.5.3.dist-info/RECORD +99 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/WHEEL +1 -1
- KekikStream/Plugins/FullHDFilm.py +0 -249
- kekikstream-2.3.9.dist-info/RECORD +0 -84
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/top_level.txt +0 -0
|
@@ -11,55 +11,36 @@ class PlayerFilmIzle(ExtractorBase):
|
|
|
11
11
|
return "filmizle.in" in url or "fireplayer" in url.lower()
|
|
12
12
|
|
|
13
13
|
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
self.httpx.headers.update({"Referer": ext_ref})
|
|
14
|
+
ref = referer or self.main_url
|
|
15
|
+
self.httpx.headers.update({"Referer": ref})
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
resp = await self.httpx.get(url)
|
|
18
|
+
sel = HTMLHelper(resp.text)
|
|
20
19
|
|
|
21
20
|
subtitles = []
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# Format örneği: [dil]url
|
|
26
|
-
if "]" in sub_yakala:
|
|
27
|
-
sub_lang_raw, sub_url = sub_yakala.split("]", 1)
|
|
28
|
-
sub_lang = sub_lang_raw.replace("[", "")
|
|
29
|
-
subtitles.append(Subtitle(name=sub_lang, url=sub_url))
|
|
21
|
+
if raw_subs := sel.regex_first(r'playerjsSubtitle\s*=\s*"([^"]*)"'):
|
|
22
|
+
for lang, link in HTMLHelper(raw_subs).regex_all(r'\[(.*?)\](https?://[^\s\",]+)'):
|
|
23
|
+
subtitles.append(Subtitle(name=lang.strip(), url=link.strip()))
|
|
30
24
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
# Data yakalama: FirePlayer("DATA", ...) formatından
|
|
35
|
-
data_val = HTMLHelper(unpacked).regex_first(r'(?i)FirePlayer\s*\(\s*["\']([a-f0-9]+)["\']')
|
|
25
|
+
content = Packer.unpack(resp.text) if Packer.detect_packed(resp.text) else resp.text
|
|
26
|
+
data_val = HTMLHelper(content).regex_first(r'FirePlayer\s*\(\s*["\']([a-f0-9]+)["\']')
|
|
36
27
|
|
|
37
28
|
if not data_val:
|
|
38
|
-
raise ValueError("PlayerFilmIzle: Data
|
|
39
|
-
|
|
40
|
-
url_post = f"{self.main_url}/player/index.php?data={data_val}&do=getVideo"
|
|
41
|
-
|
|
42
|
-
post_headers = {
|
|
43
|
-
"Referer": ext_ref,
|
|
44
|
-
"X-Requested-With": "XMLHttpRequest"
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
# Kotlin'de post data: "hash" -> data, "r" -> ""
|
|
48
|
-
post_data = {"hash": data_val, "r": ""}
|
|
49
|
-
|
|
50
|
-
response = await self.httpx.post(url_post, data=post_data, headers=post_headers)
|
|
51
|
-
get_url = response.text.replace("\\", "")
|
|
52
|
-
|
|
53
|
-
m3u8_url = ""
|
|
54
|
-
m3u8_url = HTMLHelper(get_url).regex_first(r'(?i)"securedLink":"([^\\"]*)"') or m3u8_url
|
|
29
|
+
raise ValueError(f"PlayerFilmIzle: Data bulunamadı. {url}")
|
|
55
30
|
|
|
31
|
+
resp_vid = await self.httpx.post(
|
|
32
|
+
f"{self.main_url}/player/index.php?data={data_val}&do=getVideo",
|
|
33
|
+
data = {"hash": data_val, "r": ""},
|
|
34
|
+
headers = {"X-Requested-With": "XMLHttpRequest"}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
m3u8_url = HTMLHelper(resp_vid.text).regex_first(r'"securedLink":"([^"]+)"')
|
|
56
38
|
if not m3u8_url:
|
|
57
|
-
raise ValueError("PlayerFilmIzle:
|
|
39
|
+
raise ValueError(f"PlayerFilmIzle: Video URL bulunamadı. {url}")
|
|
58
40
|
|
|
59
41
|
return ExtractResult(
|
|
60
|
-
name
|
|
61
|
-
url
|
|
62
|
-
referer
|
|
63
|
-
|
|
64
|
-
subtitles = subtitles
|
|
42
|
+
name = self.name,
|
|
43
|
+
url = m3u8_url.replace("\\", ""),
|
|
44
|
+
referer = ref,
|
|
45
|
+
subtitles = subtitles
|
|
65
46
|
)
|
|
@@ -14,57 +14,43 @@ class RapidVid(ExtractorBase):
|
|
|
14
14
|
def can_handle_url(self, url: str) -> bool:
|
|
15
15
|
return any(domain in url for domain in self.supported_domains)
|
|
16
16
|
|
|
17
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
17
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
18
18
|
if referer:
|
|
19
19
|
self.httpx.headers.update({"Referer": referer})
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
resp = await self.httpx.get(url)
|
|
22
|
+
sel = HTMLHelper(resp.text)
|
|
23
23
|
|
|
24
|
-
subtitles
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
for sub_url, sub_lang in subtitle_matches:
|
|
30
|
-
if sub_url in seen_subtitles:
|
|
31
|
-
continue
|
|
32
|
-
|
|
33
|
-
seen_subtitles.add(sub_url)
|
|
34
|
-
decoded_lang = (
|
|
35
|
-
sub_lang.replace("\\u0131", "ı")
|
|
36
|
-
.replace("\\u0130", "İ")
|
|
37
|
-
.replace("\\u00fc", "ü")
|
|
38
|
-
.replace("\\u00e7", "ç")
|
|
39
|
-
)
|
|
40
|
-
subtitles.append(Subtitle(name=decoded_lang, url=sub_url.replace("\\", "")))
|
|
24
|
+
subtitles = []
|
|
25
|
+
for s_url, s_lang in sel.regex_all(r'captions","file":"([^\"]+)","label":"([^\"]+)"'):
|
|
26
|
+
decoded_lang = s_lang.encode().decode('unicode_escape')
|
|
27
|
+
subtitles.append(Subtitle(name=decoded_lang, url=s_url.replace("\\", "")))
|
|
41
28
|
|
|
42
29
|
try:
|
|
43
|
-
|
|
30
|
+
video_url = None
|
|
44
31
|
|
|
45
|
-
# Method 1:
|
|
46
|
-
if
|
|
47
|
-
|
|
48
|
-
decoded_url = HexCodec.decode(escaped_hex)
|
|
32
|
+
# Method 1: HexCodec pattern
|
|
33
|
+
if hex_data := sel.regex_first(r'file": "(.*)",'):
|
|
34
|
+
video_url = HexCodec.decode(hex_data)
|
|
49
35
|
|
|
50
36
|
# Method 2: av('...') pattern
|
|
51
|
-
elif
|
|
52
|
-
|
|
37
|
+
elif av_data := sel.regex_first(r"av\('([^']+)'\)"):
|
|
38
|
+
video_url = self.decode_secret(av_data)
|
|
53
39
|
|
|
54
|
-
# Method 3: Packed
|
|
55
|
-
elif Packer.detect_packed(
|
|
56
|
-
unpacked
|
|
57
|
-
|
|
40
|
+
# Method 3: Packed dc_*
|
|
41
|
+
elif Packer.detect_packed(resp.text):
|
|
42
|
+
unpacked = Packer.unpack(resp.text)
|
|
43
|
+
video_url = StreamDecoder.extract_stream_url(unpacked)
|
|
58
44
|
|
|
59
|
-
if not
|
|
60
|
-
raise ValueError("
|
|
45
|
+
if not video_url:
|
|
46
|
+
raise ValueError(f"RapidVid: Video URL bulunamadı. {url}")
|
|
61
47
|
|
|
62
48
|
except Exception as hata:
|
|
63
|
-
raise RuntimeError(f"Extraction failed: {hata}") from hata
|
|
49
|
+
raise RuntimeError(f"RapidVid: Extraction failed: {hata}") from hata
|
|
64
50
|
|
|
65
51
|
return ExtractResult(
|
|
66
52
|
name = self.name,
|
|
67
|
-
url =
|
|
53
|
+
url = video_url,
|
|
68
54
|
referer = self.main_url,
|
|
69
55
|
subtitles = subtitles
|
|
70
56
|
)
|
|
@@ -7,60 +7,35 @@ class SetPlay(ExtractorBase):
|
|
|
7
7
|
name = "SetPlay"
|
|
8
8
|
main_url = "https://setplay.shop"
|
|
9
9
|
|
|
10
|
-
# Birden fazla domain destekle
|
|
11
10
|
supported_domains = ["setplay.cfd", "setplay.shop", "setplay.site"]
|
|
12
11
|
|
|
13
12
|
def can_handle_url(self, url: str) -> bool:
|
|
14
13
|
return any(domain in url for domain in self.supported_domains)
|
|
15
14
|
|
|
16
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if referer:
|
|
20
|
-
self.httpx.headers.update({"Referer": referer})
|
|
21
|
-
|
|
22
|
-
# Dinamik base URL kullan
|
|
15
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
16
|
+
self.httpx.headers.update({"Referer": referer or url})
|
|
23
17
|
base_url = self.get_base_url(url)
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
hp = HTMLHelper(istek.text)
|
|
19
|
+
resp = await self.httpx.get(url)
|
|
20
|
+
sel = HTMLHelper(resp.text)
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if not
|
|
33
|
-
raise ValueError("
|
|
34
|
-
video_url = video_url.replace("\\", "")
|
|
22
|
+
v_url = sel.regex_first(r'videoUrl":"([^",]+)"')
|
|
23
|
+
v_srv = sel.regex_first(r'videoServer":"([^",]+)"')
|
|
24
|
+
if not v_url or not v_srv:
|
|
25
|
+
raise ValueError(f"SetPlay: Video url/server bulunamadı. {url}")
|
|
35
26
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if not video_server:
|
|
39
|
-
raise ValueError("videoServer not found")
|
|
40
|
-
|
|
41
|
-
# title çıkar (opsiyonel)
|
|
42
|
-
title_base = hp.regex_first(r'title":"([^",]+)"')
|
|
43
|
-
title_base = title_base.split(".")[-1] if title_base else "Unknown"
|
|
44
|
-
|
|
45
|
-
# partKey logic
|
|
46
|
-
parsed = urlparse(url)
|
|
47
|
-
params = parse_qs(parsed.query)
|
|
48
|
-
part_key = params.get("partKey", [""])[0]
|
|
27
|
+
params = parse_qs(urlparse(url).query)
|
|
28
|
+
part_key = params.get("partKey", [""])[0].lower()
|
|
49
29
|
|
|
50
|
-
|
|
51
|
-
if "turkcedublaj" in part_key
|
|
52
|
-
|
|
53
|
-
elif "turkcealtyazi" in part_key.lower():
|
|
54
|
-
name_suffix = "Altyazı"
|
|
30
|
+
suffix = "Bilinmiyor"
|
|
31
|
+
if "turkcedublaj" in part_key: suffix = "Dublaj"
|
|
32
|
+
elif "turkcealtyazi" in part_key: suffix = "Altyazı"
|
|
55
33
|
else:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# M3U8 link oluştur - base_url kullan (main_url yerine)
|
|
59
|
-
m3u_link = f"{base_url}{video_url}?s={video_server}"
|
|
34
|
+
title = sel.regex_first(r'title":"([^",]+)"')
|
|
35
|
+
if title: suffix = title.split(".")[-1]
|
|
60
36
|
|
|
61
37
|
return ExtractResult(
|
|
62
|
-
name = f"{self.name} - {
|
|
63
|
-
url =
|
|
64
|
-
referer = url
|
|
65
|
-
subtitles = []
|
|
38
|
+
name = f"{self.name} - {suffix}",
|
|
39
|
+
url = f"{base_url}{v_url.replace('\\', '')}?s={v_srv}",
|
|
40
|
+
referer = url
|
|
66
41
|
)
|
KekikStream/Extractors/SibNet.py
CHANGED
|
@@ -6,22 +6,12 @@ class SibNet(ExtractorBase):
|
|
|
6
6
|
name = "SibNet"
|
|
7
7
|
main_url = "https://video.sibnet.ru"
|
|
8
8
|
|
|
9
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
10
|
-
|
|
11
|
-
self.httpx.headers.update({"Referer": referer})
|
|
9
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
10
|
+
self.httpx.headers.update({"Referer": referer or url})
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
resp = await self.httpx.get(url)
|
|
13
|
+
path = HTMLHelper(resp.text).regex_first(r'player\.src\(\[\{src: "([^\"]+)"')
|
|
14
|
+
if not path:
|
|
15
|
+
raise ValueError(f"SibNet: Video yolu bulunamadı. {url}")
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
if not m3u_suffix:
|
|
18
|
-
raise ValueError("m3u bağlantısı bulunamadı.")
|
|
19
|
-
|
|
20
|
-
m3u_link = f"{self.main_url}{m3u_suffix}"
|
|
21
|
-
|
|
22
|
-
return ExtractResult(
|
|
23
|
-
name = self.name,
|
|
24
|
-
url = m3u_link,
|
|
25
|
-
referer = url,
|
|
26
|
-
subtitles = []
|
|
27
|
-
)
|
|
17
|
+
return ExtractResult(name=self.name, url=f"{self.main_url}{path}", referer=url)
|
|
@@ -7,53 +7,31 @@ class Sobreatsesuyp(ExtractorBase):
|
|
|
7
7
|
name = "Sobreatsesuyp"
|
|
8
8
|
main_url = "https://sobreatsesuyp.com"
|
|
9
9
|
|
|
10
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
async def extract(self, url: str, referer: str = None) -> list[ExtractResult] | ExtractResult:
|
|
11
|
+
ref = referer or self.main_url
|
|
12
|
+
self.httpx.headers.update({"Referer": ref})
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
resp = await self.httpx.get(url)
|
|
15
|
+
path = HTMLHelper(resp.text).regex_first(r'file":"([^\"]+)')
|
|
16
|
+
if not path:
|
|
17
|
+
raise ValueError(f"Sobreatsesuyp: File path bulunamadı. {url}")
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
if
|
|
19
|
-
raise ValueError("File not found in response.")
|
|
19
|
+
post_resp = await self.httpx.post(f"{self.main_url}/{path.replace('\\', '')}")
|
|
20
|
+
data_list = post_resp.json()[1:] if isinstance(post_resp.json(), list) else []
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
post_istek = await self.httpx.post(post_link)
|
|
25
|
-
post_istek.raise_for_status()
|
|
26
|
-
|
|
27
|
-
try:
|
|
28
|
-
post_json = json.loads(post_istek.text)
|
|
29
|
-
except json.JSONDecodeError as hata:
|
|
30
|
-
raise ValueError("Failed to parse JSON response.") from hata
|
|
31
|
-
|
|
32
|
-
video_data_list = post_json[1:] if isinstance(post_json, list) else []
|
|
33
|
-
|
|
34
|
-
all_results = []
|
|
35
|
-
|
|
36
|
-
for item in video_data_list:
|
|
22
|
+
results = []
|
|
23
|
+
for item in data_list:
|
|
37
24
|
title = item.get("title")
|
|
38
25
|
file = item.get("file")
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
referer = self.main_url,
|
|
52
|
-
subtitles = []
|
|
53
|
-
)
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
if not all_results:
|
|
57
|
-
raise ValueError("No videos found in response.")
|
|
58
|
-
|
|
59
|
-
return all_results[0] if len(all_results) == 1 else all_results
|
|
26
|
+
if title and file:
|
|
27
|
+
playlist_resp = await self.httpx.post(f"{self.main_url}/playlist/{file.lstrip('/')}.txt")
|
|
28
|
+
results.append(ExtractResult(
|
|
29
|
+
name = f"{self.name} - {title}",
|
|
30
|
+
url = playlist_resp.text,
|
|
31
|
+
referer = self.main_url
|
|
32
|
+
))
|
|
33
|
+
|
|
34
|
+
if not results:
|
|
35
|
+
raise ValueError(f"Sobreatsesuyp: Video bulunamadı. {url}")
|
|
36
|
+
|
|
37
|
+
return results[0] if len(results) == 1 else results
|
KekikStream/Extractors/TRsTX.py
CHANGED
|
@@ -7,61 +7,31 @@ class TRsTX(ExtractorBase):
|
|
|
7
7
|
name = "TRsTX"
|
|
8
8
|
main_url = "https://trstx.org"
|
|
9
9
|
|
|
10
|
-
async def extract(self, url, referer=None) -> list[ExtractResult]:
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
async def extract(self, url: str, referer: str = None) -> list[ExtractResult] | ExtractResult:
|
|
11
|
+
ref = referer or self.main_url
|
|
12
|
+
self.httpx.headers.update({"Referer": ref})
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
resp = await self.httpx.get(url)
|
|
15
|
+
path = HTMLHelper(resp.text).regex_first(r'file":"([^\"]+)')
|
|
16
|
+
if not path:
|
|
17
|
+
raise ValueError(f"TRsTX: File path bulunamadı. {url}")
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
if
|
|
19
|
-
raise ValueError("File not found in response.")
|
|
19
|
+
post_resp = await self.httpx.post(f"{self.main_url}/{path.replace('\\', '')}")
|
|
20
|
+
data_list = post_resp.json()[1:] if isinstance(post_resp.json(), list) else []
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
post_istek = await self.httpx.post(post_link)
|
|
25
|
-
post_istek.raise_for_status()
|
|
26
|
-
|
|
27
|
-
try:
|
|
28
|
-
post_json = json.loads(post_istek.text)
|
|
29
|
-
except json.JSONDecodeError as hata:
|
|
30
|
-
raise ValueError("Failed to parse JSON response.") from hata
|
|
31
|
-
|
|
32
|
-
video_data_list = post_json[1:] if isinstance(post_json, list) else []
|
|
33
|
-
|
|
34
|
-
video_links = set()
|
|
35
|
-
all_results = []
|
|
36
|
-
|
|
37
|
-
for item in video_data_list:
|
|
22
|
+
results = []
|
|
23
|
+
for item in data_list:
|
|
38
24
|
title = item.get("title")
|
|
39
25
|
file = item.get("file")
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
video_links.add(video_data)
|
|
54
|
-
|
|
55
|
-
all_results.append(
|
|
56
|
-
ExtractResult(
|
|
57
|
-
name = f"{self.name} - {title}",
|
|
58
|
-
url = video_data,
|
|
59
|
-
referer = self.main_url,
|
|
60
|
-
subtitles = []
|
|
61
|
-
)
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
if not all_results:
|
|
65
|
-
raise ValueError("No videos found in response.")
|
|
66
|
-
|
|
67
|
-
return all_results[0] if len(all_results) == 1 else all_results
|
|
26
|
+
if title and file:
|
|
27
|
+
playlist_resp = await self.httpx.post(f"{self.main_url}/playlist/{file.lstrip('/')}.txt")
|
|
28
|
+
results.append(ExtractResult(
|
|
29
|
+
name = f"{self.name} - {title}",
|
|
30
|
+
url = playlist_resp.text,
|
|
31
|
+
referer = self.main_url
|
|
32
|
+
))
|
|
33
|
+
|
|
34
|
+
if not results:
|
|
35
|
+
raise ValueError(f"TRsTX: Video bulunamadı. {url}")
|
|
36
|
+
|
|
37
|
+
return results[0] if len(results) == 1 else results
|
|
@@ -6,19 +6,12 @@ class TurboImgz(ExtractorBase):
|
|
|
6
6
|
name = "TurboImgz"
|
|
7
7
|
main_url = "https://turbo.imgz.me"
|
|
8
8
|
|
|
9
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
10
|
-
|
|
11
|
-
self.httpx.headers.update({"Referer": referer})
|
|
9
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
10
|
+
self.httpx.headers.update({"Referer": referer or url})
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
resp = await self.httpx.get(url)
|
|
13
|
+
v_url = HTMLHelper(resp.text).regex_first(r'file: "(.*)",')
|
|
14
|
+
if not v_url:
|
|
15
|
+
raise ValueError(f"TurboImgz: Video bulunamadı. {url}")
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
return ExtractResult(
|
|
18
|
-
name = self.name,
|
|
19
|
-
url = video,
|
|
20
|
-
referer = referer or self.main_url,
|
|
21
|
-
subtitles = []
|
|
22
|
-
)
|
|
23
|
-
else:
|
|
24
|
-
raise ValueError("File not found in response.")
|
|
17
|
+
return ExtractResult(name=self.name, url=v_url, referer=referer or self.main_url)
|
|
@@ -7,35 +7,17 @@ class VCTPlay(ExtractorBase):
|
|
|
7
7
|
name = "VCTPlay"
|
|
8
8
|
main_url = "https://vctplay.site"
|
|
9
9
|
|
|
10
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
11
|
+
v_id = url.split("/")[-1].split("?")[0]
|
|
12
|
+
params = parse_qs(urlparse(url).query)
|
|
13
|
+
part_key = params.get("partKey", [""])[0].lower()
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if "?" in video_id:
|
|
18
|
-
video_id = video_id.split("?")[0]
|
|
19
|
-
|
|
20
|
-
# Manifests URL oluştur
|
|
21
|
-
master_url = f"{self.main_url}/manifests/{video_id}/master.txt"
|
|
22
|
-
|
|
23
|
-
# partKey'den isim belirle
|
|
24
|
-
parsed = urlparse(url)
|
|
25
|
-
params = parse_qs(parsed.query)
|
|
26
|
-
part_key = params.get("partKey", [""])[0]
|
|
27
|
-
|
|
28
|
-
name_suffix = ""
|
|
29
|
-
if "turkcedublaj" in part_key.lower():
|
|
30
|
-
name_suffix = "Dublaj"
|
|
31
|
-
elif "turkcealtyazi" in part_key.lower():
|
|
32
|
-
name_suffix = "Altyazı"
|
|
33
|
-
|
|
34
|
-
display_name = f"{self.name} - {name_suffix}" if name_suffix else self.name
|
|
15
|
+
suffix = ""
|
|
16
|
+
if "turkcedublaj" in part_key: suffix = "Dublaj"
|
|
17
|
+
elif "turkcealtyazi" in part_key: suffix = "Altyazı"
|
|
35
18
|
|
|
36
19
|
return ExtractResult(
|
|
37
|
-
name
|
|
38
|
-
url
|
|
39
|
-
referer
|
|
40
|
-
subtitles = []
|
|
20
|
+
name = f"{self.name} - {suffix}" if suffix else self.name,
|
|
21
|
+
url = f"{self.main_url}/manifests/{v_id}/master.txt",
|
|
22
|
+
referer = f"{self.main_url}/"
|
|
41
23
|
)
|
|
@@ -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}")
|