KekikStream 1.7.1__py3-none-any.whl → 2.2.0__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 +20 -9
- KekikStream/Core/Extractor/ExtractorLoader.py +25 -17
- KekikStream/Core/Extractor/ExtractorManager.py +53 -9
- KekikStream/Core/Extractor/ExtractorModels.py +5 -7
- KekikStream/Core/Extractor/YTDLPCache.py +35 -0
- KekikStream/Core/Media/MediaHandler.py +44 -26
- KekikStream/Core/Media/MediaManager.py +0 -3
- KekikStream/Core/Plugin/PluginBase.py +82 -22
- KekikStream/Core/Plugin/PluginLoader.py +11 -7
- KekikStream/Core/Plugin/PluginModels.py +25 -26
- KekikStream/Core/__init__.py +1 -0
- KekikStream/Extractors/CloseLoad.py +21 -7
- KekikStream/Extractors/ContentX.py +21 -6
- KekikStream/Extractors/DonilasPlay.py +86 -0
- KekikStream/Extractors/DzenRu.py +38 -0
- KekikStream/Extractors/ExPlay.py +53 -0
- KekikStream/Extractors/Filemoon.py +78 -0
- KekikStream/Extractors/HDPlayerSystem.py +41 -0
- KekikStream/Extractors/JetTv.py +45 -0
- KekikStream/Extractors/MailRu.py +3 -4
- KekikStream/Extractors/MixPlayHD.py +2 -3
- KekikStream/Extractors/MixTiger.py +57 -0
- KekikStream/Extractors/MolyStream.py +5 -5
- KekikStream/Extractors/Odnoklassniki.py +13 -7
- KekikStream/Extractors/PeaceMakerst.py +10 -5
- KekikStream/Extractors/PixelDrain.py +1 -2
- KekikStream/Extractors/PlayerFilmIzle.py +65 -0
- KekikStream/Extractors/RapidVid.py +23 -8
- KekikStream/Extractors/SetPlay.py +66 -0
- KekikStream/Extractors/SetPrime.py +45 -0
- KekikStream/Extractors/SibNet.py +2 -3
- KekikStream/Extractors/Sobreatsesuyp.py +4 -5
- KekikStream/Extractors/TRsTX.py +4 -5
- KekikStream/Extractors/TauVideo.py +2 -3
- KekikStream/Extractors/TurboImgz.py +2 -3
- KekikStream/Extractors/TurkeyPlayer.py +34 -0
- KekikStream/Extractors/VCTPlay.py +41 -0
- KekikStream/Extractors/VidHide.py +81 -0
- KekikStream/Extractors/VidMoly.py +55 -34
- KekikStream/Extractors/VidMoxy.py +2 -3
- KekikStream/Extractors/VidPapi.py +89 -0
- KekikStream/Extractors/VideoSeyred.py +3 -4
- KekikStream/Extractors/YTDLP.py +211 -0
- KekikStream/Extractors/YildizKisaFilm.py +41 -0
- KekikStream/Plugins/BelgeselX.py +196 -0
- KekikStream/Plugins/DiziBox.py +25 -34
- KekikStream/Plugins/DiziPal.py +24 -35
- KekikStream/Plugins/DiziYou.py +54 -37
- KekikStream/Plugins/Dizilla.py +66 -46
- KekikStream/Plugins/FilmBip.py +142 -0
- KekikStream/Plugins/FilmMakinesi.py +36 -28
- KekikStream/Plugins/FilmModu.py +20 -24
- KekikStream/Plugins/FullHDFilm.py +220 -0
- KekikStream/Plugins/FullHDFilmizlesene.py +9 -15
- KekikStream/Plugins/HDFilmCehennemi.py +141 -69
- KekikStream/Plugins/JetFilmizle.py +85 -52
- KekikStream/Plugins/KultFilmler.py +217 -0
- KekikStream/Plugins/RecTV.py +22 -34
- KekikStream/Plugins/RoketDizi.py +222 -0
- KekikStream/Plugins/SelcukFlix.py +328 -0
- KekikStream/Plugins/SetFilmIzle.py +252 -0
- KekikStream/Plugins/SezonlukDizi.py +54 -21
- KekikStream/Plugins/SineWix.py +17 -29
- KekikStream/Plugins/Sinefy.py +241 -0
- KekikStream/Plugins/SinemaCX.py +154 -0
- KekikStream/Plugins/Sinezy.py +143 -0
- KekikStream/Plugins/SuperFilmGeldi.py +130 -0
- KekikStream/Plugins/UgurFilm.py +13 -19
- KekikStream/__init__.py +47 -56
- KekikStream/requirements.txt +3 -4
- kekikstream-2.2.0.dist-info/METADATA +312 -0
- kekikstream-2.2.0.dist-info/RECORD +81 -0
- KekikStream/Extractors/FourCX.py +0 -7
- KekikStream/Extractors/FourPichive.py +0 -7
- KekikStream/Extractors/FourPlayRu.py +0 -7
- KekikStream/Extractors/HDStreamAble.py +0 -7
- KekikStream/Extractors/Hotlinger.py +0 -7
- KekikStream/Extractors/OkRuHTTP.py +0 -7
- KekikStream/Extractors/OkRuSSL.py +0 -7
- KekikStream/Extractors/Pichive.py +0 -7
- KekikStream/Extractors/PlayRu.py +0 -7
- KekikStream/Extractors/VidMolyMe.py +0 -7
- kekikstream-1.7.1.dist-info/METADATA +0 -109
- kekikstream-1.7.1.dist-info/RECORD +0 -63
- {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/WHEEL +0 -0
- {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/entry_points.txt +0 -0
- {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, field_validator, model_validator
|
|
4
|
-
from typing import List, Optional
|
|
5
4
|
|
|
6
5
|
class MainPageResult(BaseModel):
|
|
7
6
|
"""Ana sayfa sonucunda dönecek veri modeli."""
|
|
8
7
|
category : str
|
|
9
8
|
title : str
|
|
10
9
|
url : str
|
|
11
|
-
poster :
|
|
10
|
+
poster : str | None = None
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class SearchResult(BaseModel):
|
|
15
14
|
"""Arama sonucunda dönecek veri modeli."""
|
|
16
15
|
title : str
|
|
17
16
|
url : str
|
|
18
|
-
poster :
|
|
17
|
+
poster : str | None = None
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
class MovieInfo(BaseModel):
|
|
22
21
|
"""Bir medya öğesinin bilgilerini tutan model."""
|
|
23
22
|
url : str
|
|
24
|
-
poster :
|
|
25
|
-
title :
|
|
26
|
-
description :
|
|
27
|
-
tags :
|
|
28
|
-
rating :
|
|
29
|
-
year :
|
|
30
|
-
actors :
|
|
31
|
-
duration :
|
|
23
|
+
poster : str | None = None
|
|
24
|
+
title : str | None = None
|
|
25
|
+
description : str | None = None
|
|
26
|
+
tags : str | None = None
|
|
27
|
+
rating : str | None = None
|
|
28
|
+
year : str | None = None
|
|
29
|
+
actors : str | None = None
|
|
30
|
+
duration : int | None = None
|
|
32
31
|
|
|
33
32
|
@field_validator("tags", "actors", mode="before")
|
|
34
33
|
@classmethod
|
|
@@ -42,10 +41,10 @@ class MovieInfo(BaseModel):
|
|
|
42
41
|
|
|
43
42
|
|
|
44
43
|
class Episode(BaseModel):
|
|
45
|
-
season :
|
|
46
|
-
episode :
|
|
47
|
-
title :
|
|
48
|
-
url :
|
|
44
|
+
season : int | None = None
|
|
45
|
+
episode : int | None = None
|
|
46
|
+
title : str | None = None
|
|
47
|
+
url : str | None = None
|
|
49
48
|
|
|
50
49
|
@model_validator(mode="after")
|
|
51
50
|
def check_title(self) -> "Episode":
|
|
@@ -58,16 +57,16 @@ class Episode(BaseModel):
|
|
|
58
57
|
return self
|
|
59
58
|
|
|
60
59
|
class SeriesInfo(BaseModel):
|
|
61
|
-
url :
|
|
62
|
-
poster :
|
|
63
|
-
title :
|
|
64
|
-
description :
|
|
65
|
-
tags :
|
|
66
|
-
rating :
|
|
67
|
-
year :
|
|
68
|
-
actors :
|
|
69
|
-
duration :
|
|
70
|
-
episodes :
|
|
60
|
+
url : str | None = None
|
|
61
|
+
poster : str | None = None
|
|
62
|
+
title : str | None = None
|
|
63
|
+
description : str | None = None
|
|
64
|
+
tags : str | None = None
|
|
65
|
+
rating : str | None = None
|
|
66
|
+
year : str | None = None
|
|
67
|
+
actors : str | None = None
|
|
68
|
+
duration : int | None = None
|
|
69
|
+
episodes : list[Episode] | None = None
|
|
71
70
|
|
|
72
71
|
@field_validator("tags", "actors", mode="before")
|
|
73
72
|
@classmethod
|
|
@@ -77,4 +76,4 @@ class SeriesInfo(BaseModel):
|
|
|
77
76
|
@field_validator("rating", "year", mode="before")
|
|
78
77
|
@classmethod
|
|
79
78
|
def ensure_string(cls, value):
|
|
80
|
-
return str(value) if value is not None else value
|
|
79
|
+
return str(value) if value is not None else value
|
KekikStream/Core/__init__.py
CHANGED
|
@@ -13,6 +13,7 @@ from .Extractor.ExtractorManager import ExtractorManager
|
|
|
13
13
|
from .Extractor.ExtractorBase import ExtractorBase
|
|
14
14
|
from .Extractor.ExtractorLoader import ExtractorLoader
|
|
15
15
|
from .Extractor.ExtractorModels import ExtractResult, Subtitle
|
|
16
|
+
from .Extractor.YTDLPCache import get_ytdlp_extractors
|
|
16
17
|
|
|
17
18
|
from .Media.MediaManager import MediaManager
|
|
18
19
|
from .Media.MediaHandler import MediaHandler
|
|
@@ -1,27 +1,41 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
4
|
from Kekik.Sifreleme import Packer, StreamDecoder
|
|
5
|
+
from parsel import Selector
|
|
5
6
|
import re
|
|
6
7
|
|
|
7
8
|
class CloseLoadExtractor(ExtractorBase):
|
|
8
9
|
name = "CloseLoad"
|
|
9
|
-
main_url = "https://closeload.filmmakinesi.
|
|
10
|
+
main_url = "https://closeload.filmmakinesi.to"
|
|
10
11
|
|
|
11
12
|
async def extract(self, url, referer=None) -> ExtractResult:
|
|
12
13
|
if referer:
|
|
13
|
-
self.
|
|
14
|
+
self.httpx.headers.update({"Referer": referer})
|
|
14
15
|
|
|
15
|
-
istek = await self.
|
|
16
|
+
istek = await self.httpx.get(url)
|
|
16
17
|
istek.raise_for_status()
|
|
17
18
|
|
|
19
|
+
# Video URL'sini çıkar
|
|
18
20
|
eval_func = re.compile(r'\s*(eval\(function[\s\S].*)\s*').findall(istek.text)[0]
|
|
19
21
|
m3u_link = StreamDecoder.extract_stream_url(Packer.unpack(eval_func))
|
|
20
22
|
|
|
23
|
+
# Subtitle'ları parse et (Kotlin referansı: track elementleri)
|
|
24
|
+
subtitles = []
|
|
25
|
+
secici = Selector(istek.text)
|
|
26
|
+
for track in secici.css("track"):
|
|
27
|
+
raw_src = track.css("::attr(src)").get() or ""
|
|
28
|
+
raw_src = raw_src.strip()
|
|
29
|
+
label = track.css("::attr(label)").get() or track.css("::attr(srclang)").get() or "Altyazı"
|
|
30
|
+
|
|
31
|
+
if raw_src:
|
|
32
|
+
full_url = raw_src if raw_src.startswith("http") else f"{self.main_url}{raw_src}"
|
|
33
|
+
subtitles.append(Subtitle(name=label, url=full_url))
|
|
34
|
+
|
|
21
35
|
return ExtractResult(
|
|
22
36
|
name = self.name,
|
|
23
37
|
url = m3u_link,
|
|
24
38
|
referer = self.main_url,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
39
|
+
subtitles = subtitles
|
|
40
|
+
)
|
|
41
|
+
|
|
@@ -7,11 +7,27 @@ class ContentX(ExtractorBase):
|
|
|
7
7
|
name = "ContentX"
|
|
8
8
|
main_url = "https://contentx.me"
|
|
9
9
|
|
|
10
|
+
# Birden fazla domain destekle
|
|
11
|
+
supported_domains = [
|
|
12
|
+
"contentx.me", "four.contentx.me",
|
|
13
|
+
"dplayer82.site", "sn.dplayer82.site", "four.dplayer82.site", "org.dplayer82.site",
|
|
14
|
+
"dplayer74.site", "sn.dplayer74.site",
|
|
15
|
+
"hotlinger.com", "sn.hotlinger.com",
|
|
16
|
+
"playru.net", "four.playru.net",
|
|
17
|
+
"pichive.online", "four.pichive.online", "pichive.me", "four.pichive.me"
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
def can_handle_url(self, url: str) -> bool:
|
|
21
|
+
return any(domain in url for domain in self.supported_domains)
|
|
22
|
+
|
|
10
23
|
async def extract(self, url, referer=None) -> list[ExtractResult]:
|
|
11
24
|
if referer:
|
|
12
|
-
self.
|
|
25
|
+
self.httpx.headers.update({"Referer": referer})
|
|
26
|
+
|
|
27
|
+
# Dinamik base URL kullan
|
|
28
|
+
base_url = self.get_base_url(url)
|
|
13
29
|
|
|
14
|
-
istek = await self.
|
|
30
|
+
istek = await self.httpx.get(url)
|
|
15
31
|
istek.raise_for_status()
|
|
16
32
|
i_source = istek.text
|
|
17
33
|
|
|
@@ -39,7 +55,8 @@ class ContentX(ExtractorBase):
|
|
|
39
55
|
)
|
|
40
56
|
)
|
|
41
57
|
|
|
42
|
-
|
|
58
|
+
# base_url kullan (contentx.me yerine)
|
|
59
|
+
vid_source_request = await self.httpx.get(f"{base_url}/source2.php?v={i_extract_value}", headers={"Referer": referer or base_url})
|
|
43
60
|
vid_source_request.raise_for_status()
|
|
44
61
|
|
|
45
62
|
vid_source = vid_source_request.text
|
|
@@ -53,14 +70,13 @@ class ContentX(ExtractorBase):
|
|
|
53
70
|
name = self.name,
|
|
54
71
|
url = m3u_link,
|
|
55
72
|
referer = url,
|
|
56
|
-
headers = {},
|
|
57
73
|
subtitles = subtitles
|
|
58
74
|
)
|
|
59
75
|
]
|
|
60
76
|
|
|
61
77
|
if i_dublaj := re.search(r',\"([^"]+)\",\"Türkçe"', i_source):
|
|
62
78
|
dublaj_value = i_dublaj[1]
|
|
63
|
-
dublaj_source_request = await self.
|
|
79
|
+
dublaj_source_request = await self.httpx.get(f"{base_url}/source2.php?v={dublaj_value}", headers={"Referer": referer or base_url})
|
|
64
80
|
dublaj_source_request.raise_for_status()
|
|
65
81
|
|
|
66
82
|
dublaj_source = dublaj_source_request.text
|
|
@@ -74,7 +90,6 @@ class ContentX(ExtractorBase):
|
|
|
74
90
|
name = f"{self.name} Türkçe Dublaj",
|
|
75
91
|
url = dublaj_link,
|
|
76
92
|
referer = url,
|
|
77
|
-
headers = {},
|
|
78
93
|
subtitles = []
|
|
79
94
|
)
|
|
80
95
|
)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
|
+
from Kekik.Sifreleme import AESManager
|
|
5
|
+
import re, json
|
|
6
|
+
|
|
7
|
+
class DonilasPlay(ExtractorBase):
|
|
8
|
+
name = "DonilasPlay"
|
|
9
|
+
main_url = "https://donilasplay.com"
|
|
10
|
+
|
|
11
|
+
async def extract(self, url, referer=None) -> ExtractResult:
|
|
12
|
+
if referer:
|
|
13
|
+
self.httpx.headers.update({"Referer": referer})
|
|
14
|
+
|
|
15
|
+
istek = await self.httpx.get(url)
|
|
16
|
+
istek.raise_for_status()
|
|
17
|
+
i_source = istek.text
|
|
18
|
+
|
|
19
|
+
m3u_link = None
|
|
20
|
+
subtitles = []
|
|
21
|
+
|
|
22
|
+
# bePlayer pattern
|
|
23
|
+
be_player_match = re.search(r"bePlayer\('([^']+)',\s*'(\{[^}]+\})'\);", i_source)
|
|
24
|
+
if be_player_match:
|
|
25
|
+
be_player_pass = be_player_match.group(1)
|
|
26
|
+
be_player_data = be_player_match.group(2)
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
# AES decrypt
|
|
30
|
+
decrypted = AESManager.decrypt(be_player_data, be_player_pass)
|
|
31
|
+
data = json.loads(decrypted)
|
|
32
|
+
|
|
33
|
+
m3u_link = data.get("video_location")
|
|
34
|
+
|
|
35
|
+
# Altyazıları işle
|
|
36
|
+
str_subtitles = data.get("strSubtitles", [])
|
|
37
|
+
if str_subtitles:
|
|
38
|
+
for sub in str_subtitles:
|
|
39
|
+
label = sub.get("label", "")
|
|
40
|
+
file = sub.get("file", "")
|
|
41
|
+
# Forced altyazıları hariç tut
|
|
42
|
+
if "Forced" in label:
|
|
43
|
+
continue
|
|
44
|
+
if file:
|
|
45
|
+
# Türkçe kontrolü
|
|
46
|
+
keywords = ["tur", "tr", "türkçe", "turkce"]
|
|
47
|
+
language = "Turkish" if any(k in label.lower() for k in keywords) else label
|
|
48
|
+
subtitles.append(Subtitle(
|
|
49
|
+
name = language,
|
|
50
|
+
url = self.fix_url(file)
|
|
51
|
+
))
|
|
52
|
+
except Exception:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
# Fallback: file pattern
|
|
56
|
+
if not m3u_link:
|
|
57
|
+
file_match = re.search(r'file:"([^"]+)"', i_source)
|
|
58
|
+
if file_match:
|
|
59
|
+
m3u_link = file_match.group(1)
|
|
60
|
+
|
|
61
|
+
# tracks pattern for subtitles
|
|
62
|
+
tracks_match = re.search(r'tracks:\[([^\]]+)', i_source)
|
|
63
|
+
if tracks_match:
|
|
64
|
+
try:
|
|
65
|
+
tracks_str = f"[{tracks_match.group(1)}]"
|
|
66
|
+
tracks = json.loads(tracks_str)
|
|
67
|
+
for track in tracks:
|
|
68
|
+
file_url = track.get("file")
|
|
69
|
+
label = track.get("label", "")
|
|
70
|
+
if file_url and "Forced" not in label:
|
|
71
|
+
subtitles.append(Subtitle(
|
|
72
|
+
name = label,
|
|
73
|
+
url = self.fix_url(file_url)
|
|
74
|
+
))
|
|
75
|
+
except Exception:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
if not m3u_link:
|
|
79
|
+
raise ValueError("m3u link not found")
|
|
80
|
+
|
|
81
|
+
return ExtractResult(
|
|
82
|
+
name = self.name,
|
|
83
|
+
url = m3u_link,
|
|
84
|
+
referer = url,
|
|
85
|
+
subtitles = subtitles
|
|
86
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
class DzenRu(ExtractorBase):
|
|
7
|
+
name = "DzenRu"
|
|
8
|
+
main_url = "https://dzen.ru"
|
|
9
|
+
|
|
10
|
+
async def extract(self, url, referer=None) -> ExtractResult:
|
|
11
|
+
video_key = url.split("/")[-1]
|
|
12
|
+
video_url = f"{self.main_url}/embed/{video_key}"
|
|
13
|
+
|
|
14
|
+
if referer:
|
|
15
|
+
self.httpx.headers.update({"Referer": referer})
|
|
16
|
+
|
|
17
|
+
istek = await self.httpx.get(video_url)
|
|
18
|
+
istek.raise_for_status()
|
|
19
|
+
|
|
20
|
+
# okcdn.ru linklerini bul
|
|
21
|
+
matches = re.findall(r'https://vd\d+\.okcdn\.ru/\?[^"\'\\\s]+', istek.text)
|
|
22
|
+
|
|
23
|
+
if not matches:
|
|
24
|
+
raise ValueError("DzenRu video link not found")
|
|
25
|
+
|
|
26
|
+
# Benzersiz linkleri al, son kaliteyi kullan
|
|
27
|
+
unique_links = list(set(matches))
|
|
28
|
+
best_link = unique_links[-1] if unique_links else None
|
|
29
|
+
|
|
30
|
+
if not best_link:
|
|
31
|
+
raise ValueError("No valid video URL found")
|
|
32
|
+
|
|
33
|
+
return ExtractResult(
|
|
34
|
+
name = self.name,
|
|
35
|
+
url = best_link,
|
|
36
|
+
referer = self.main_url,
|
|
37
|
+
subtitles = []
|
|
38
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
|
+
import re
|
|
5
|
+
from urllib.parse import urlparse, parse_qs
|
|
6
|
+
|
|
7
|
+
class ExPlay(ExtractorBase):
|
|
8
|
+
name = "ExPlay"
|
|
9
|
+
main_url = "https://explay.store"
|
|
10
|
+
|
|
11
|
+
async def extract(self, url, referer=None) -> ExtractResult:
|
|
12
|
+
ext_ref = referer or ""
|
|
13
|
+
|
|
14
|
+
# URL parsing for partKey
|
|
15
|
+
parsed = urlparse(url)
|
|
16
|
+
params = parse_qs(parsed.query)
|
|
17
|
+
part_key = params.get("partKey", [""])[0]
|
|
18
|
+
clean_url = url.split("?partKey=")[0]
|
|
19
|
+
|
|
20
|
+
if referer:
|
|
21
|
+
self.httpx.headers.update({"Referer": referer})
|
|
22
|
+
|
|
23
|
+
istek = await self.httpx.get(clean_url)
|
|
24
|
+
istek.raise_for_status()
|
|
25
|
+
|
|
26
|
+
# videoUrl çıkar
|
|
27
|
+
video_url_match = re.search(r'videoUrl":"([^",]+)"', istek.text)
|
|
28
|
+
if not video_url_match:
|
|
29
|
+
raise ValueError("videoUrl not found")
|
|
30
|
+
video_url = video_url_match[1].replace("\\", "")
|
|
31
|
+
|
|
32
|
+
# videoServer çıkar
|
|
33
|
+
video_server_match = re.search(r'videoServer":"([^",]+)"', istek.text)
|
|
34
|
+
if not video_server_match:
|
|
35
|
+
raise ValueError("videoServer not found")
|
|
36
|
+
video_server = video_server_match[1]
|
|
37
|
+
|
|
38
|
+
# title çıkar
|
|
39
|
+
title_match = re.search(r'title":"([^",]+)"', istek.text)
|
|
40
|
+
title = title_match[1].split(".")[-1] if title_match else "Unknown"
|
|
41
|
+
|
|
42
|
+
if part_key and "turkce" in part_key.lower():
|
|
43
|
+
title = part_key # Or nicer formatting like SetPlay
|
|
44
|
+
|
|
45
|
+
# M3U8 link oluştur
|
|
46
|
+
m3u_link = f"{self.main_url}{video_url}?s={video_server}"
|
|
47
|
+
|
|
48
|
+
return ExtractResult(
|
|
49
|
+
name = f"{self.name} - {title}",
|
|
50
|
+
url = m3u_link,
|
|
51
|
+
referer = clean_url,
|
|
52
|
+
subtitles = []
|
|
53
|
+
)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
+
from Kekik.Sifreleme import Packer
|
|
5
|
+
from parsel import Selector
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
class Filemoon(ExtractorBase):
|
|
9
|
+
name = "Filemoon"
|
|
10
|
+
main_url = "https://filemoon.to"
|
|
11
|
+
|
|
12
|
+
# Filemoon'un farklı domainlerini destekle
|
|
13
|
+
supported_domains = [
|
|
14
|
+
"filemoon.to",
|
|
15
|
+
"filemoon.in",
|
|
16
|
+
"filemoon.sx",
|
|
17
|
+
"filemoon.nl",
|
|
18
|
+
"filemoon.com"
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
def can_handle_url(self, url: str) -> bool:
|
|
22
|
+
return any(domain in url for domain in self.supported_domains)
|
|
23
|
+
|
|
24
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
25
|
+
headers = {
|
|
26
|
+
"Referer" : url,
|
|
27
|
+
"Sec-Fetch-Dest" : "iframe",
|
|
28
|
+
"Sec-Fetch-Mode" : "navigate",
|
|
29
|
+
"Sec-Fetch-Site" : "cross-site",
|
|
30
|
+
}
|
|
31
|
+
self.httpx.headers.update(headers)
|
|
32
|
+
|
|
33
|
+
# İlk sayfayı al
|
|
34
|
+
istek = await self.httpx.get(url)
|
|
35
|
+
response = istek.text
|
|
36
|
+
secici = Selector(response)
|
|
37
|
+
|
|
38
|
+
# Eğer iframe varsa, iframe'e git
|
|
39
|
+
iframe_src = secici.css("iframe::attr(src)").get()
|
|
40
|
+
if iframe_src:
|
|
41
|
+
iframe_url = self.fix_url(iframe_src)
|
|
42
|
+
self.httpx.headers.update({
|
|
43
|
+
"Accept-Language" : "en-US,en;q=0.5",
|
|
44
|
+
"Sec-Fetch-Dest" : "iframe"
|
|
45
|
+
})
|
|
46
|
+
istek = await self.httpx.get(iframe_url)
|
|
47
|
+
response = istek.text
|
|
48
|
+
|
|
49
|
+
# Packed script'i bul ve unpack et
|
|
50
|
+
m3u8_url = None
|
|
51
|
+
|
|
52
|
+
if Packer.detect_packed(response):
|
|
53
|
+
try:
|
|
54
|
+
unpacked = Packer.unpack(response)
|
|
55
|
+
# sources:[{file:"..." pattern'ını ara
|
|
56
|
+
if match := re.search(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"', unpacked):
|
|
57
|
+
m3u8_url = match.group(1)
|
|
58
|
+
elif match := re.search(r'file:\s*"([^"]*?\.m3u8[^"]*)"', unpacked):
|
|
59
|
+
m3u8_url = match.group(1)
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
# Fallback: Doğrudan response'ta ara
|
|
64
|
+
if not m3u8_url:
|
|
65
|
+
if match := re.search(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"', response):
|
|
66
|
+
m3u8_url = match.group(1)
|
|
67
|
+
elif match := re.search(r'file:\s*"([^"]*?\.m3u8[^"]*)"', response):
|
|
68
|
+
m3u8_url = match.group(1)
|
|
69
|
+
|
|
70
|
+
if not m3u8_url:
|
|
71
|
+
raise ValueError(f"Filemoon: Video URL bulunamadı. {url}")
|
|
72
|
+
|
|
73
|
+
return ExtractResult(
|
|
74
|
+
name = self.name,
|
|
75
|
+
url = self.fix_url(m3u8_url),
|
|
76
|
+
referer = f"{self.main_url}/",
|
|
77
|
+
subtitles = []
|
|
78
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
+
|
|
5
|
+
class HDPlayerSystem(ExtractorBase):
|
|
6
|
+
name = "HDPlayerSystem"
|
|
7
|
+
main_url = "https://hdplayersystem.com"
|
|
8
|
+
|
|
9
|
+
async def extract(self, url, referer=None) -> ExtractResult:
|
|
10
|
+
ext_ref = referer or ""
|
|
11
|
+
|
|
12
|
+
if "video/" in url:
|
|
13
|
+
vid_id = url.split("video/")[-1]
|
|
14
|
+
else:
|
|
15
|
+
vid_id = url.split("?data=")[-1]
|
|
16
|
+
|
|
17
|
+
post_url = f"{self.main_url}/player/index.php?data={vid_id}&do=getVideo"
|
|
18
|
+
|
|
19
|
+
response = await self.httpx.post(
|
|
20
|
+
url = post_url,
|
|
21
|
+
data = {"hash": vid_id, "r": ext_ref},
|
|
22
|
+
headers = {
|
|
23
|
+
"Referer" : ext_ref,
|
|
24
|
+
"Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8",
|
|
25
|
+
"X-Requested-With" : "XMLHttpRequest"
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
response.raise_for_status()
|
|
29
|
+
|
|
30
|
+
video_data = response.json()
|
|
31
|
+
m3u_link = video_data.get("securedLink")
|
|
32
|
+
|
|
33
|
+
if not m3u_link:
|
|
34
|
+
raise ValueError("securedLink not found in response")
|
|
35
|
+
|
|
36
|
+
return ExtractResult(
|
|
37
|
+
name = self.name,
|
|
38
|
+
url = m3u_link,
|
|
39
|
+
referer = url,
|
|
40
|
+
subtitles = []
|
|
41
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
|
+
import re, json
|
|
5
|
+
|
|
6
|
+
class JetTv(ExtractorBase):
|
|
7
|
+
name = "JetTv"
|
|
8
|
+
main_url = "https://jetv.xyz"
|
|
9
|
+
|
|
10
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
11
|
+
istek = await self.httpx.get(url)
|
|
12
|
+
document = istek.text
|
|
13
|
+
|
|
14
|
+
# 1. Yöntem: API üzerinden alma
|
|
15
|
+
master_url = ""
|
|
16
|
+
final_ref = f"{self.main_url}/"
|
|
17
|
+
|
|
18
|
+
if "id=" in url:
|
|
19
|
+
vid_id = url.split("id=")[-1]
|
|
20
|
+
api_url = f"https://jetv.xyz/apollo/get_video.php?id={vid_id}"
|
|
21
|
+
try:
|
|
22
|
+
# Referer olarak video sayfasının kendisi gönderilmeli
|
|
23
|
+
api_resp = await self.httpx.get(api_url, headers={"Referer": url})
|
|
24
|
+
api_json = api_resp.json()
|
|
25
|
+
|
|
26
|
+
if api_json.get("success"):
|
|
27
|
+
master_url = api_json.get("masterUrl", "")
|
|
28
|
+
final_ref = api_json.get("referrerUrl") or final_ref
|
|
29
|
+
except Exception:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
# 2. Yöntem: Regex Fallback
|
|
33
|
+
if not master_url:
|
|
34
|
+
if match := re.search(r"file: '([^']*)'", document, re.IGNORECASE):
|
|
35
|
+
master_url = match.group(1)
|
|
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
|
+
)
|
KekikStream/Extractors/MailRu.py
CHANGED
|
@@ -11,9 +11,9 @@ class MailRuExtractor(ExtractorBase):
|
|
|
11
11
|
video_meta_url = f"{self.main_url}/+/video/meta/{vid_id}"
|
|
12
12
|
|
|
13
13
|
if referer:
|
|
14
|
-
self.
|
|
14
|
+
self.httpx.headers.update({"Referer": referer})
|
|
15
15
|
|
|
16
|
-
istek = await self.
|
|
16
|
+
istek = await self.httpx.get(video_meta_url)
|
|
17
17
|
istek.raise_for_status()
|
|
18
18
|
|
|
19
19
|
video_key = istek.cookies.get("video_key")
|
|
@@ -34,6 +34,5 @@ class MailRuExtractor(ExtractorBase):
|
|
|
34
34
|
name = self.name,
|
|
35
35
|
url = video_url,
|
|
36
36
|
referer = self.main_url,
|
|
37
|
-
headers = {"Cookie": f"video_key={video_key}"},
|
|
38
37
|
subtitles = []
|
|
39
|
-
)
|
|
38
|
+
)
|
|
@@ -10,9 +10,9 @@ class MixPlayHD(ExtractorBase):
|
|
|
10
10
|
|
|
11
11
|
async def extract(self, url, referer=None) -> ExtractResult:
|
|
12
12
|
if referer:
|
|
13
|
-
self.
|
|
13
|
+
self.httpx.headers.update({"Referer": referer})
|
|
14
14
|
|
|
15
|
-
istek = await self.
|
|
15
|
+
istek = await self.httpx.get(url)
|
|
16
16
|
istek.raise_for_status()
|
|
17
17
|
|
|
18
18
|
be_player_match = re.search(r"bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);", istek.text)
|
|
@@ -36,7 +36,6 @@ class MixPlayHD(ExtractorBase):
|
|
|
36
36
|
name = self.name,
|
|
37
37
|
url = video_url_match[1],
|
|
38
38
|
referer = self.main_url,
|
|
39
|
-
headers = {},
|
|
40
39
|
subtitles = []
|
|
41
40
|
)
|
|
42
41
|
else:
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
+
|
|
5
|
+
class MixTiger(ExtractorBase):
|
|
6
|
+
name = "MixTiger"
|
|
7
|
+
main_url = "https://www.mixtiger.com"
|
|
8
|
+
|
|
9
|
+
async def extract(self, url, referer=None) -> ExtractResult:
|
|
10
|
+
ext_ref = referer or ""
|
|
11
|
+
post_url = f"{url}?do=getVideo"
|
|
12
|
+
vid_id = url.split("video/")[-1] if "video/" in url else ""
|
|
13
|
+
|
|
14
|
+
response = await self.httpx.post(
|
|
15
|
+
url = post_url,
|
|
16
|
+
data = {"hash": vid_id, "r": ext_ref, "s": ""},
|
|
17
|
+
headers = {
|
|
18
|
+
"Referer" : ext_ref,
|
|
19
|
+
"Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8",
|
|
20
|
+
"X-Requested-With" : "XMLHttpRequest"
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
response.raise_for_status()
|
|
24
|
+
|
|
25
|
+
video_data = response.json()
|
|
26
|
+
|
|
27
|
+
# videoSrc varsa doğrudan kullan
|
|
28
|
+
if video_data.get("videoSrc"):
|
|
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
|
|
50
|
+
|
|
51
|
+
return ExtractResult(
|
|
52
|
+
name = self.name,
|
|
53
|
+
url = m3u_link,
|
|
54
|
+
referer = None if "disk.yandex" in m3u_link else ext_ref,
|
|
55
|
+
subtitles = []
|
|
56
|
+
)
|
|
57
|
+
|
|
@@ -26,9 +26,9 @@ class MolyStream(ExtractorBase):
|
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
return ExtractResult(
|
|
29
|
-
name
|
|
30
|
-
url
|
|
31
|
-
referer
|
|
32
|
-
|
|
33
|
-
subtitles
|
|
29
|
+
name = self.name,
|
|
30
|
+
url = video,
|
|
31
|
+
referer = video.replace("/sheila", ""),
|
|
32
|
+
user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0",
|
|
33
|
+
subtitles = subtitles
|
|
34
34
|
)
|