KekikStream 2.3.9__py3-none-any.whl → 2.5.4__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 +35 -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.4.dist-info}/METADATA +1 -1
- kekikstream-2.5.4.dist-info/RECORD +99 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.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.4.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/top_level.txt +0 -0
KekikStream/Extractors/JetTv.py
CHANGED
|
@@ -1,45 +1,32 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import ExtractorBase, ExtractResult,
|
|
4
|
-
import
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
4
|
+
import contextlib
|
|
5
5
|
|
|
6
6
|
class JetTv(ExtractorBase):
|
|
7
7
|
name = "JetTv"
|
|
8
8
|
main_url = "https://jetv.xyz"
|
|
9
9
|
|
|
10
10
|
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
resp = await self.httpx.get(url)
|
|
12
|
+
sel = HTMLHelper(resp.text)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
final_ref = f"{self.main_url}/"
|
|
14
|
+
m3u8_url = None
|
|
15
|
+
final_ref = self.main_url
|
|
17
16
|
|
|
18
17
|
if "id=" in url:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
hp = HTMLHelper(document)
|
|
35
|
-
master_url = hp.regex_first(r"(?i)file: '([^']*)'") or master_url
|
|
36
|
-
|
|
37
|
-
if not master_url:
|
|
38
|
-
raise ValueError(f"JetTv: Video kaynağı bulunamadı. {url}")
|
|
39
|
-
|
|
40
|
-
return ExtractResult(
|
|
41
|
-
name = self.name,
|
|
42
|
-
url = master_url,
|
|
43
|
-
referer = final_ref,
|
|
44
|
-
subtitles = []
|
|
45
|
-
)
|
|
18
|
+
v_id = url.split("id=")[-1]
|
|
19
|
+
with contextlib.suppress(Exception):
|
|
20
|
+
api_resp = await self.httpx.get(f"{self.main_url}/apollo/get_video.php?id={v_id}", headers={"Referer": url})
|
|
21
|
+
data = api_resp.json()
|
|
22
|
+
if data.get("success"):
|
|
23
|
+
m3u8_url = data.get("masterUrl")
|
|
24
|
+
final_ref = data.get("referrerUrl") or final_ref
|
|
25
|
+
|
|
26
|
+
if not m3u8_url:
|
|
27
|
+
m3u8_url = sel.regex_first(r"(?i)file: '([^']*)'")
|
|
28
|
+
|
|
29
|
+
if not m3u8_url:
|
|
30
|
+
raise ValueError(f"JetTv: Video URL bulunamadı. {url}")
|
|
31
|
+
|
|
32
|
+
return ExtractResult(name=self.name, url=m3u8_url, referer=final_ref)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
4
|
+
import json, re
|
|
5
|
+
|
|
6
|
+
class JetV(ExtractorBase):
|
|
7
|
+
name = "JetV"
|
|
8
|
+
main_url = "https://jetv.xyz"
|
|
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
|
+
# Script içindeki sources kısmını bul
|
|
15
|
+
# "sources": [ ... ]
|
|
16
|
+
sources_str = HTMLHelper(text).regex_first(r'"sources":\s*(\[.*?\])')
|
|
17
|
+
if not sources_str:
|
|
18
|
+
# Altenatif: sources: [ ... ] (tırnaksız sources)
|
|
19
|
+
sources_str = HTMLHelper(text).regex_first(r'sources:\s*(\[.*?\])')
|
|
20
|
+
|
|
21
|
+
if not sources_str:
|
|
22
|
+
raise ValueError(f"JetV: Sources bulunamadı. {url}")
|
|
23
|
+
|
|
24
|
+
# file: -> "file":
|
|
25
|
+
clean_json = re.sub(r'(\w+):', r'"\1":', sources_str)
|
|
26
|
+
# ' -> "
|
|
27
|
+
clean_json = clean_json.replace("'", '"')
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
sources = json.loads(clean_json)
|
|
31
|
+
except:
|
|
32
|
+
# Basit parser yetmediyse, manuel parse deneyelim (tek kaynak varsa)
|
|
33
|
+
file_url = HTMLHelper(sources_str).regex_first(r'file["\']?:\s*["\']([^"\']+)["\']')
|
|
34
|
+
label = HTMLHelper(sources_str).regex_first(r'label["\']?:\s*["\']([^"\']+)["\']')
|
|
35
|
+
if file_url:
|
|
36
|
+
sources = [{"file": file_url, "label": label or "Unknown"}]
|
|
37
|
+
else:
|
|
38
|
+
raise ValueError("JetV: JSON parse hatası")
|
|
39
|
+
|
|
40
|
+
results = []
|
|
41
|
+
for source in sources:
|
|
42
|
+
file_path = source.get("file")
|
|
43
|
+
label = source.get("label", "Unknown")
|
|
44
|
+
|
|
45
|
+
if not file_path:
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
results.append(ExtractResult(
|
|
49
|
+
name = f"{self.name} | {label}",
|
|
50
|
+
url = self.fix_url(file_path),
|
|
51
|
+
referer = url,
|
|
52
|
+
user_agent = self.httpx.headers.get("User-Agent", "")
|
|
53
|
+
))
|
|
54
|
+
|
|
55
|
+
return results
|
KekikStream/Extractors/MailRu.py
CHANGED
|
@@ -2,37 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
4
|
|
|
5
|
-
class
|
|
5
|
+
class MailRu(ExtractorBase):
|
|
6
6
|
name = "MailRu"
|
|
7
7
|
main_url = "https://my.mail.ru"
|
|
8
8
|
|
|
9
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
10
|
+
v_id = url.split("video/embed/")[-1].strip()
|
|
11
|
+
if referer: self.httpx.headers.update({"Referer": referer})
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
resp = await self.httpx.get(f"{self.main_url}/+/video/meta/{v_id}")
|
|
14
|
+
data = resp.json()
|
|
15
|
+
|
|
16
|
+
v_url = data.get("videos", [{}])[0].get("url")
|
|
17
|
+
if not v_url:
|
|
18
|
+
raise ValueError(f"MailRu: Video URL bulunamadı. {url}")
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
istek.raise_for_status()
|
|
18
|
-
|
|
19
|
-
video_key = istek.cookies.get("video_key")
|
|
20
|
-
if not video_key:
|
|
21
|
-
raise ValueError("Video key bulunamadı.")
|
|
22
|
-
|
|
23
|
-
video_data = istek.json()
|
|
24
|
-
videos = video_data.get("videos", [])
|
|
25
|
-
if not videos:
|
|
26
|
-
raise ValueError("Videolar bulunamadı.")
|
|
27
|
-
|
|
28
|
-
video = videos[0]
|
|
29
|
-
video_url = video["url"]
|
|
30
|
-
if video_url.startswith("//"):
|
|
31
|
-
video_url = f"https:{video_url}"
|
|
32
|
-
|
|
33
|
-
return ExtractResult(
|
|
34
|
-
name = self.name,
|
|
35
|
-
url = video_url,
|
|
36
|
-
referer = self.main_url,
|
|
37
|
-
subtitles = []
|
|
38
|
-
)
|
|
20
|
+
return ExtractResult(name=self.name, url=self.fix_url(v_url), referer=self.main_url)
|
|
@@ -8,34 +8,21 @@ class MixPlayHD(ExtractorBase):
|
|
|
8
8
|
name = "MixPlayHD"
|
|
9
9
|
main_url = "https://mixplayhd.com"
|
|
10
10
|
|
|
11
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
12
|
-
|
|
13
|
-
self.httpx.headers.update({"Referer": referer})
|
|
11
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
12
|
+
self.httpx.headers.update({"Referer": referer or self.main_url})
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
be_player_matches = hp.regex_all(r"bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);")
|
|
20
|
-
if not be_player_matches:
|
|
21
|
-
raise ValueError("bePlayer not found in the response.")
|
|
22
|
-
|
|
23
|
-
be_player_pass, be_player_data = be_player_matches[0]
|
|
14
|
+
resp = await self.httpx.get(url)
|
|
15
|
+
match = HTMLHelper(resp.text).regex_first(r"bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);", group=None)
|
|
16
|
+
if not match:
|
|
17
|
+
raise ValueError(f"MixPlayHD: bePlayer bulunamadı. {url}")
|
|
24
18
|
|
|
19
|
+
pass_val, data_val = match
|
|
25
20
|
try:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return ExtractResult(
|
|
35
|
-
name = self.name,
|
|
36
|
-
url = video_url,
|
|
37
|
-
referer = self.main_url,
|
|
38
|
-
subtitles = []
|
|
39
|
-
)
|
|
40
|
-
else:
|
|
41
|
-
raise ValueError("M3U8 video URL not found in the decrypted data.")
|
|
21
|
+
data = json.loads(AESManager.decrypt(data_val, pass_val))
|
|
22
|
+
v_url = HTMLHelper(data.get("schedule", {}).get("client", "")).regex_first(r'"video_location":"([^"]+)"')
|
|
23
|
+
if v_url:
|
|
24
|
+
return ExtractResult(name=self.name, url=v_url, referer=self.main_url)
|
|
25
|
+
except Exception as e:
|
|
26
|
+
raise ValueError(f"MixPlayHD: Decryption failed. {e}")
|
|
27
|
+
|
|
28
|
+
raise ValueError(f"MixPlayHD: Video URL bulunamadı. {url}")
|
|
@@ -6,52 +6,29 @@ class MixTiger(ExtractorBase):
|
|
|
6
6
|
name = "MixTiger"
|
|
7
7
|
main_url = "https://www.mixtiger.com"
|
|
8
8
|
|
|
9
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
vid_id = url.split("video/")[-1] if "video/" in url else ""
|
|
9
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
10
|
+
ref = referer or self.main_url
|
|
11
|
+
v_id = url.split("video/")[-1] if "video/" in url else ""
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
url
|
|
16
|
-
data = {"hash":
|
|
13
|
+
resp = await self.httpx.post(
|
|
14
|
+
f"{url}?do=getVideo",
|
|
15
|
+
data = {"hash": v_id, "r": ref, "s": ""},
|
|
17
16
|
headers = {
|
|
18
|
-
"Referer"
|
|
19
|
-
"
|
|
20
|
-
"X-Requested-With" : "XMLHttpRequest"
|
|
17
|
+
"Referer": ref,
|
|
18
|
+
"X-Requested-With": "XMLHttpRequest"
|
|
21
19
|
}
|
|
22
20
|
)
|
|
23
|
-
|
|
21
|
+
data = resp.json()
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
m3u8_url = data.get("videoSrc")
|
|
24
|
+
if not m3u8_url and data.get("videoSources"):
|
|
25
|
+
m3u8_url = data["videoSources"][-1].get("file")
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
m3u_link = video_data["videoSrc"]
|
|
30
|
-
# videoSources listesi varsa son elemanı al
|
|
31
|
-
elif video_data.get("videoSources"):
|
|
32
|
-
sources = video_data["videoSources"]
|
|
33
|
-
m3u_link = sources[-1].get("file") if sources else None
|
|
34
|
-
else:
|
|
35
|
-
m3u_link = None
|
|
36
|
-
|
|
37
|
-
if not m3u_link:
|
|
38
|
-
raise ValueError("Video URL not found in response")
|
|
39
|
-
|
|
40
|
-
# Recursive extraction check - başka extractor kullanılabilir mi?
|
|
41
|
-
try:
|
|
42
|
-
from KekikStream.Core.Extractor.ExtractorManager import ExtractorManager
|
|
43
|
-
manager = ExtractorManager()
|
|
44
|
-
if nested_extractor := manager.find_extractor(m3u_link):
|
|
45
|
-
# Nested extractor ile çıkar
|
|
46
|
-
return await nested_extractor.extract(m3u_link, referer=ext_ref)
|
|
47
|
-
except Exception:
|
|
48
|
-
# Recursive extraction başarısız olursa standart sonucu döndür
|
|
49
|
-
pass
|
|
27
|
+
if not m3u8_url:
|
|
28
|
+
raise ValueError(f"MixTiger: Video linki bulunamadı. {url}")
|
|
50
29
|
|
|
51
30
|
return ExtractResult(
|
|
52
|
-
name
|
|
53
|
-
url
|
|
54
|
-
referer
|
|
55
|
-
subtitles = []
|
|
31
|
+
name = self.name,
|
|
32
|
+
url = m3u8_url,
|
|
33
|
+
referer = None if "disk.yandex" in m3u8_url else ref
|
|
56
34
|
)
|
|
57
|
-
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
4
|
-
import re
|
|
5
4
|
|
|
6
5
|
class MolyStream(ExtractorBase):
|
|
7
6
|
name = "MolyStream"
|
|
@@ -9,34 +8,31 @@ class MolyStream(ExtractorBase):
|
|
|
9
8
|
|
|
10
9
|
# Birden fazla domain destekle
|
|
11
10
|
supported_domains = [
|
|
12
|
-
"dbx.molystream.org",
|
|
13
|
-
"
|
|
14
|
-
"yd.sheila.stream",
|
|
15
|
-
"ydf.popcornvakti.net",
|
|
11
|
+
"dbx.molystream.org", "ydx.molystream.org",
|
|
12
|
+
"yd.sheila.stream", "ydf.popcornvakti.net",
|
|
16
13
|
]
|
|
17
14
|
|
|
18
15
|
def can_handle_url(self, url: str) -> bool:
|
|
19
16
|
return any(domain in url for domain in self.supported_domains)
|
|
20
17
|
|
|
21
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
22
|
-
|
|
23
|
-
secici = HTMLHelper(url)
|
|
24
|
-
video = secici.select_attr("video#sheplayer source", "src")
|
|
25
|
-
else:
|
|
26
|
-
video = url
|
|
18
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
19
|
+
self.httpx.headers.update({"Referer": referer or self.main_url})
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
# Eğer url zaten bir HTML kaynağıysa (doctype html içeriyorsa)
|
|
22
|
+
if "doctype html" in url.lower():
|
|
23
|
+
sel = HTMLHelper(url)
|
|
24
|
+
v_url = sel.select_attr("video#sheplayer source", "src")
|
|
25
|
+
else:
|
|
26
|
+
v_url = url
|
|
30
27
|
|
|
31
|
-
subtitles = [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
]
|
|
28
|
+
subtitles = []
|
|
29
|
+
for s_url, s_name in HTMLHelper(url).regex_all(r"addSrtFile\(['\"]([^'\"]+\.srt)['\"]\s*,\s*['\"][a-z]{2}['\"]\s*,\s*['\"]([^'\"]+)['\"]"):
|
|
30
|
+
subtitles.append(Subtitle(name=s_name, url=self.fix_url(s_url)))
|
|
35
31
|
|
|
36
32
|
return ExtractResult(
|
|
37
|
-
name
|
|
38
|
-
url
|
|
39
|
-
referer
|
|
33
|
+
name = self.name,
|
|
34
|
+
url = v_url,
|
|
35
|
+
referer = v_url.replace("/sheila", "") if v_url else None,
|
|
40
36
|
user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0",
|
|
41
|
-
subtitles
|
|
37
|
+
subtitles = subtitles
|
|
42
38
|
)
|
|
@@ -1,117 +1,53 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
4
|
-
import
|
|
4
|
+
import json, html
|
|
5
5
|
|
|
6
6
|
class Odnoklassniki(ExtractorBase):
|
|
7
7
|
name = "Odnoklassniki"
|
|
8
8
|
main_url = "https://odnoklassniki.ru"
|
|
9
9
|
|
|
10
|
-
# Birden fazla domain destekle
|
|
11
10
|
supported_domains = ["odnoklassniki.ru", "ok.ru"]
|
|
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
|
-
if "/video/" in url:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
raise ValueError("Failed to parse video data.") from hata
|
|
56
|
-
|
|
57
|
-
quality_order = {
|
|
58
|
-
"ULTRA": 6, # 4K veya daha yüksek
|
|
59
|
-
"QUAD": 5, # 1440p
|
|
60
|
-
"FULL": 4, # 1080p
|
|
61
|
-
"HD": 3, # 720p
|
|
62
|
-
"SD": 2, # 480p
|
|
63
|
-
"LOW": 1, # 360p
|
|
64
|
-
"MOBILE": 0 # 144p
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
# Kaliteye göre en iyi videoyu seçme
|
|
68
|
-
best_video = None
|
|
69
|
-
best_quality_score = -1
|
|
70
|
-
|
|
71
|
-
for video in videos:
|
|
72
|
-
video_url = video.get("url")
|
|
73
|
-
quality_name = video.get("name", "").upper()
|
|
74
|
-
|
|
75
|
-
if not video_url or not quality_name:
|
|
76
|
-
continue
|
|
77
|
-
|
|
78
|
-
# Kalite sıralamasına göre puanla
|
|
79
|
-
quality_score = quality_order.get(quality_name, -1)
|
|
80
|
-
if quality_score > best_quality_score:
|
|
81
|
-
best_quality_score = quality_score
|
|
82
|
-
best_video = video_url
|
|
83
|
-
|
|
84
|
-
if not best_video:
|
|
85
|
-
raise ValueError("No valid video URLs found.")
|
|
86
|
-
|
|
87
|
-
if best_video.startswith("//"):
|
|
88
|
-
best_video = f"https:{best_video}"
|
|
89
|
-
|
|
90
|
-
return ExtractResult(
|
|
91
|
-
name = self.name,
|
|
92
|
-
url = best_video,
|
|
93
|
-
referer = referer,
|
|
94
|
-
user_agent = headers.get("User-Agent", None),
|
|
95
|
-
subtitles = []
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
async def fetch_with_redirects(self, url, max_redirects=5):
|
|
99
|
-
"""Yönlendirmeleri takip eden bir fonksiyon"""
|
|
100
|
-
redirects = 0
|
|
101
|
-
while redirects < max_redirects:
|
|
102
|
-
istek = await self.httpx.get(url, follow_redirects=False)
|
|
103
|
-
|
|
104
|
-
if istek.status_code not in [301, 302]:
|
|
105
|
-
break # Yönlendirme yoksa çık
|
|
106
|
-
|
|
107
|
-
redirected_url = istek.headers.get("Location")
|
|
108
|
-
if not redirected_url:
|
|
109
|
-
raise ValueError("Redirect location not found.")
|
|
110
|
-
|
|
111
|
-
url = redirected_url if redirected_url.startswith("http") else f"https://{redirected_url}"
|
|
112
|
-
redirects += 1
|
|
113
|
-
|
|
114
|
-
if redirects == max_redirects:
|
|
115
|
-
raise RuntimeError(f"Max redirects ({max_redirects}) reached.")
|
|
116
|
-
|
|
117
|
-
return istek
|
|
15
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
16
|
+
if "/video/" in url: url = url.replace("/video/", "/videoembed/")
|
|
17
|
+
self.httpx.headers.update({"Origin": self.main_url})
|
|
18
|
+
|
|
19
|
+
resp = await self.httpx.get(url, follow_redirects=True)
|
|
20
|
+
sel = HTMLHelper(resp.text)
|
|
21
|
+
|
|
22
|
+
# Metadata içinden videos array'ini al (esnek regex)
|
|
23
|
+
v_data = sel.regex_first(r'videos[^:]+:(\[.*?\])')
|
|
24
|
+
if not v_data:
|
|
25
|
+
if "Видео заблокировано" in resp.text or "copyrightsRestricted" in resp.text:
|
|
26
|
+
raise ValueError("Odnoklassniki: Video telif nedeniyle silinmiş/erişilemiyor.")
|
|
27
|
+
raise ValueError(f"Odnoklassniki: Video verisi bulunamadı. {url}")
|
|
28
|
+
|
|
29
|
+
# Kalite sıralaması (En yüksekten düşüğe)
|
|
30
|
+
order = ["ULTRA", "QUAD", "FULL", "HD", "SD", "LOW", "MOBILE"]
|
|
31
|
+
# Escaped string'i temizle
|
|
32
|
+
v_data = html.unescape(v_data)
|
|
33
|
+
v_data = v_data.replace('\\"', '"').replace('\\/', '/')
|
|
34
|
+
videos = json.loads(v_data)
|
|
35
|
+
|
|
36
|
+
best_url = None
|
|
37
|
+
for q in order:
|
|
38
|
+
best_url = next((v.get("url") for v in videos if v.get("name", "").upper() == q), None)
|
|
39
|
+
if best_url: break
|
|
40
|
+
|
|
41
|
+
if not best_url:
|
|
42
|
+
best_url = videos[0].get("url") if videos else None
|
|
43
|
+
|
|
44
|
+
if not best_url:
|
|
45
|
+
raise ValueError("Odnoklassniki: Geçerli video URL'si bulunamadı.")
|
|
46
|
+
|
|
47
|
+
# URL temizliği (u0026 -> & ve olası unicode kaçışları)
|
|
48
|
+
best_url = best_url.replace("u0026", "&").replace("\\u0026", "&")
|
|
49
|
+
# Eğer hala \uXXXX formatında unicode kaçışları varsa çöz
|
|
50
|
+
if "\\u" in best_url:
|
|
51
|
+
best_url = best_url.encode().decode('unicode-escape')
|
|
52
|
+
|
|
53
|
+
return ExtractResult(name=self.name, url=self.fix_url(best_url), referer=referer)
|
|
@@ -7,57 +7,30 @@ class PeaceMakerst(ExtractorBase):
|
|
|
7
7
|
name = "PeaceMakerst"
|
|
8
8
|
main_url = "https://peacemakerst.com"
|
|
9
9
|
|
|
10
|
-
# Birden fazla domain destekle
|
|
11
10
|
supported_domains = ["peacemakerst.com", "hdstreamable.com"]
|
|
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
|
-
if referer:
|
|
18
|
-
self.httpx.headers.update({"Referer": referer})
|
|
19
|
-
|
|
15
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
20
16
|
self.httpx.headers.update({
|
|
21
|
-
"
|
|
17
|
+
"Referer" : referer or url,
|
|
22
18
|
"X-Requested-With" : "XMLHttpRequest"
|
|
23
19
|
})
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
teve2_response = await self.httpx.get(teve2_url, headers={"Referer": f"https://www.teve2.com.tr/embed/{teve2_id}"})
|
|
43
|
-
teve2_response.raise_for_status()
|
|
44
|
-
teve2_json = teve2_response.json()
|
|
45
|
-
|
|
46
|
-
m3u_link = f"{teve2_json['Media']['Link']['ServiceUrl']}//{teve2_json['Media']['Link']['SecurePath']}"
|
|
47
|
-
else:
|
|
48
|
-
try:
|
|
49
|
-
video_response = response.json()
|
|
50
|
-
if video_sources := video_response.get("videoSources", []):
|
|
51
|
-
m3u_link = video_sources[-1]["file"]
|
|
52
|
-
except (json.JSONDecodeError, KeyError) as hata:
|
|
53
|
-
raise ValueError("Peace response is invalid or null.") from hata
|
|
54
|
-
|
|
55
|
-
if not m3u_link:
|
|
56
|
-
raise ValueError("m3u link not found.")
|
|
57
|
-
|
|
58
|
-
return ExtractResult(
|
|
59
|
-
name = self.name,
|
|
60
|
-
url = m3u_link,
|
|
61
|
-
referer = url,
|
|
62
|
-
subtitles = []
|
|
63
|
-
)
|
|
21
|
+
resp = await self.httpx.post(f"{url}?do=getVideo", data={"hash": url.split("video/")[-1], "r": referer or "", "s": ""})
|
|
22
|
+
data = resp.json()
|
|
23
|
+
|
|
24
|
+
m3u8_url = None
|
|
25
|
+
if "teve2.com.tr" in resp.text:
|
|
26
|
+
v_id = HTMLHelper(resp.text).regex_first(r"teve2\.com\.tr\\\/embed\\\/(\d+)")
|
|
27
|
+
t_resp = await self.httpx.get(f"https://www.teve2.com.tr/action/media/{v_id}")
|
|
28
|
+
t_data = t_resp.json()
|
|
29
|
+
m3u8_url = f"{t_data['Media']['Link']['ServiceUrl']}//{t_data['Media']['Link']['SecurePath']}"
|
|
30
|
+
elif sources := data.get("videoSources"):
|
|
31
|
+
m3u8_url = sources[-1]["file"]
|
|
32
|
+
|
|
33
|
+
if not m3u8_url:
|
|
34
|
+
raise ValueError(f"PeaceMakerst: Video linki bulunamadı. {url}")
|
|
35
|
+
|
|
36
|
+
return ExtractResult(name=self.name, url=m3u8_url, referer=url)
|
|
@@ -6,23 +6,15 @@ class PixelDrain(ExtractorBase):
|
|
|
6
6
|
name = "PixelDrain"
|
|
7
7
|
main_url = "https://pixeldrain.com"
|
|
8
8
|
|
|
9
|
-
async def extract(self, url, referer=None) -> ExtractResult:
|
|
10
|
-
if referer:
|
|
11
|
-
self.httpx.headers.update({"Referer": referer})
|
|
9
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
10
|
+
if referer: self.httpx.headers.update({"Referer": referer})
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
raise ValueError("PixelDrain bağlantısından ID çıkarılamadı.")
|
|
17
|
-
|
|
18
|
-
m = matches[0]
|
|
19
|
-
pixel_id = next((g for g in m if g), None)
|
|
20
|
-
download_link = f"{self.main_url}/api/file/{pixel_id}?download"
|
|
21
|
-
referer_link = f"{self.main_url}/u/{pixel_id}?download"
|
|
12
|
+
p_id = HTMLHelper(url).regex_first(r"/u/([^/?]+)|([^\/]+)(?=\?download)")
|
|
13
|
+
if not p_id:
|
|
14
|
+
raise ValueError(f"PixelDrain: ID bulunamadı. {url}")
|
|
22
15
|
|
|
23
16
|
return ExtractResult(
|
|
24
|
-
name
|
|
25
|
-
url
|
|
26
|
-
referer
|
|
27
|
-
subtitles = []
|
|
17
|
+
name = f"{self.name} - {p_id}",
|
|
18
|
+
url = f"{self.main_url}/api/file/{p_id}?download",
|
|
19
|
+
referer = f"{self.main_url}/u/{p_id}?download"
|
|
28
20
|
)
|