KekikStream 1.3.3__py3-none-any.whl → 1.7.1__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/CLI/pypi_kontrol.py +6 -6
- KekikStream/Core/Extractor/ExtractorBase.py +6 -11
- KekikStream/Core/Extractor/ExtractorModels.py +2 -2
- KekikStream/Core/Media/MediaHandler.py +9 -6
- KekikStream/Core/Plugin/PluginBase.py +35 -21
- KekikStream/Core/Plugin/PluginModels.py +1 -0
- KekikStream/Extractors/CloseLoad.py +8 -27
- KekikStream/Extractors/ContentX.py +6 -4
- KekikStream/Extractors/MailRu.py +4 -5
- KekikStream/Extractors/MixPlayHD.py +3 -2
- KekikStream/Extractors/MolyStream.py +23 -5
- KekikStream/Extractors/Odnoklassniki.py +14 -9
- KekikStream/Extractors/PeaceMakerst.py +5 -4
- KekikStream/Extractors/PixelDrain.py +2 -2
- KekikStream/Extractors/RapidVid.py +33 -15
- KekikStream/Extractors/SibNet.py +3 -3
- KekikStream/Extractors/Sobreatsesuyp.py +5 -4
- KekikStream/Extractors/TRsTX.py +5 -4
- KekikStream/Extractors/TauVideo.py +3 -2
- KekikStream/Extractors/TurboImgz.py +3 -2
- KekikStream/Extractors/VidMoly.py +23 -21
- KekikStream/Extractors/VidMoxy.py +3 -3
- KekikStream/Extractors/VideoSeyred.py +4 -3
- KekikStream/Plugins/DiziBox.py +46 -29
- KekikStream/Plugins/DiziPal.py +246 -0
- KekikStream/Plugins/DiziYou.py +22 -20
- KekikStream/Plugins/Dizilla.py +62 -48
- KekikStream/Plugins/FilmMakinesi.py +65 -54
- KekikStream/Plugins/FilmModu.py +138 -0
- KekikStream/Plugins/FullHDFilmizlesene.py +44 -39
- KekikStream/Plugins/HDFilmCehennemi.py +98 -74
- KekikStream/Plugins/JetFilmizle.py +22 -14
- KekikStream/Plugins/RecTV.py +43 -35
- KekikStream/Plugins/SezonlukDizi.py +52 -20
- KekikStream/Plugins/SineWix.py +43 -30
- KekikStream/Plugins/UgurFilm.py +50 -15
- KekikStream/__init__.py +285 -318
- KekikStream/requirements.txt +1 -1
- {kekikstream-1.3.3.dist-info → kekikstream-1.7.1.dist-info}/METADATA +11 -4
- kekikstream-1.7.1.dist-info/RECORD +63 -0
- {kekikstream-1.3.3.dist-info → kekikstream-1.7.1.dist-info}/WHEEL +1 -1
- KekikStream/Helpers/Unpack.py +0 -75
- KekikStream/Plugins/Shorten.py +0 -225
- kekikstream-1.3.3.dist-info/RECORD +0 -63
- {kekikstream-1.3.3.dist-info → kekikstream-1.7.1.dist-info}/entry_points.txt +0 -0
- {kekikstream-1.3.3.dist-info → kekikstream-1.7.1.dist-info/licenses}/LICENSE +0 -0
- {kekikstream-1.3.3.dist-info → kekikstream-1.7.1.dist-info}/top_level.txt +0 -0
KekikStream/Extractors/TRsTX.py
CHANGED
|
@@ -9,9 +9,9 @@ class TRsTX(ExtractorBase):
|
|
|
9
9
|
|
|
10
10
|
async def extract(self, url, referer=None) -> list[ExtractResult]:
|
|
11
11
|
if referer:
|
|
12
|
-
self.
|
|
12
|
+
self.cffi.headers.update({"Referer": referer})
|
|
13
13
|
|
|
14
|
-
istek = await self.
|
|
14
|
+
istek = await self.cffi.get(url)
|
|
15
15
|
istek.raise_for_status()
|
|
16
16
|
|
|
17
17
|
file_match = re.search(r'file\":\"([^\"]+)', istek.text)
|
|
@@ -21,7 +21,7 @@ class TRsTX(ExtractorBase):
|
|
|
21
21
|
file_path = file_match[1].replace("\\", "")
|
|
22
22
|
post_link = f"{self.main_url}/{file_path}"
|
|
23
23
|
|
|
24
|
-
post_istek = await self.
|
|
24
|
+
post_istek = await self.cffi.post(post_link)
|
|
25
25
|
post_istek.raise_for_status()
|
|
26
26
|
|
|
27
27
|
try:
|
|
@@ -42,7 +42,7 @@ class TRsTX(ExtractorBase):
|
|
|
42
42
|
continue
|
|
43
43
|
|
|
44
44
|
playlist_url = f"{self.main_url}/playlist/{file.lstrip('/')}.txt"
|
|
45
|
-
playlist_request = await self.
|
|
45
|
+
playlist_request = await self.cffi.post(playlist_url, headers={"Referer": referer or self.main_url})
|
|
46
46
|
playlist_request.raise_for_status()
|
|
47
47
|
|
|
48
48
|
video_data = playlist_request.text
|
|
@@ -57,6 +57,7 @@ class TRsTX(ExtractorBase):
|
|
|
57
57
|
name = f"{self.name} - {title}",
|
|
58
58
|
url = video_data,
|
|
59
59
|
referer = self.main_url,
|
|
60
|
+
headers = {},
|
|
60
61
|
subtitles = []
|
|
61
62
|
)
|
|
62
63
|
)
|
|
@@ -8,12 +8,12 @@ class TauVideo(ExtractorBase):
|
|
|
8
8
|
|
|
9
9
|
async def extract(self, url, referer=None) -> list[ExtractResult]:
|
|
10
10
|
if referer:
|
|
11
|
-
self.
|
|
11
|
+
self.cffi.headers.update({"Referer": referer})
|
|
12
12
|
|
|
13
13
|
video_key = url.split("/")[-1]
|
|
14
14
|
api_url = f"{self.main_url}/api/video/{video_key}"
|
|
15
15
|
|
|
16
|
-
response = await self.
|
|
16
|
+
response = await self.cffi.get(api_url)
|
|
17
17
|
response.raise_for_status()
|
|
18
18
|
|
|
19
19
|
api_data = response.json()
|
|
@@ -26,6 +26,7 @@ class TauVideo(ExtractorBase):
|
|
|
26
26
|
name = f"{self.name} - {video['label']}",
|
|
27
27
|
url = video["url"],
|
|
28
28
|
referer = referer or self.main_url,
|
|
29
|
+
headers = {},
|
|
29
30
|
subtitles = []
|
|
30
31
|
)
|
|
31
32
|
for video in api_data["urls"]
|
|
@@ -9,9 +9,9 @@ class TurboImgz(ExtractorBase):
|
|
|
9
9
|
|
|
10
10
|
async def extract(self, url, referer=None) -> ExtractResult:
|
|
11
11
|
if referer:
|
|
12
|
-
self.
|
|
12
|
+
self.cffi.headers.update({"Referer": referer})
|
|
13
13
|
|
|
14
|
-
istek = await self.
|
|
14
|
+
istek = await self.cffi.get(url)
|
|
15
15
|
istek.raise_for_status()
|
|
16
16
|
|
|
17
17
|
if video_match := re.search(r'file: "(.*)",', istek.text):
|
|
@@ -19,6 +19,7 @@ class TurboImgz(ExtractorBase):
|
|
|
19
19
|
name = self.name,
|
|
20
20
|
url = video_match[1],
|
|
21
21
|
referer = referer or self.main_url,
|
|
22
|
+
headers = {},
|
|
22
23
|
subtitles = []
|
|
23
24
|
)
|
|
24
25
|
else:
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
# ! https://github.com/recloudstream/cloudstream/blob/master/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidmoly.kt
|
|
3
3
|
|
|
4
4
|
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
5
|
-
import
|
|
5
|
+
from parsel import Selector
|
|
6
|
+
import re, contextlib, json
|
|
6
7
|
|
|
7
8
|
class VidMoly(ExtractorBase):
|
|
8
9
|
name = "VidMoly"
|
|
@@ -10,32 +11,33 @@ class VidMoly(ExtractorBase):
|
|
|
10
11
|
|
|
11
12
|
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
12
13
|
if referer:
|
|
13
|
-
self.
|
|
14
|
+
self.cffi.headers.update({"Referer": referer})
|
|
14
15
|
|
|
15
|
-
self.
|
|
16
|
-
"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
|
16
|
+
self.cffi.headers.update({
|
|
17
17
|
"Sec-Fetch-Dest" : "iframe",
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
if self.main_url.endswith(".me"):
|
|
21
|
-
self.main_url = self.main_url.replace(".me", ".
|
|
22
|
-
url
|
|
21
|
+
self.main_url = self.main_url.replace(".me", ".net")
|
|
22
|
+
url = url.replace(".me", ".net")
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
response = await self.cffi.get(url)
|
|
25
|
+
if "Select number" in response.text:
|
|
26
|
+
secici = Selector(response.text)
|
|
27
|
+
response = await self.cffi.post(
|
|
28
|
+
url = url,
|
|
29
|
+
data = {
|
|
30
|
+
"op" : secici.css("input[name='op']::attr(value)").get(),
|
|
31
|
+
"file_code" : secici.css("input[name='file_code']::attr(value)").get(),
|
|
32
|
+
"answer" : secici.css("div.vhint b::text").get(),
|
|
33
|
+
"ts" : secici.css("input[name='ts']::attr(value)").get(),
|
|
34
|
+
"nonce" : secici.css("input[name='nonce']::attr(value)").get(),
|
|
35
|
+
"ctok" : secici.css("input[name='ctok']::attr(value)").get()
|
|
36
|
+
}
|
|
37
|
+
)
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
attempts += 1
|
|
32
|
-
response = await self.httpx.get(embed_url)
|
|
33
|
-
response.raise_for_status()
|
|
34
|
-
|
|
35
|
-
script_match = re.search(r"sources:\s*\[(.*?)\],", response.text, re.DOTALL)
|
|
36
|
-
script_content = script_match[1] if script_match else None
|
|
37
|
-
if not script_content:
|
|
38
|
-
await asyncio.sleep(0.5)
|
|
39
|
+
script_match = re.search(r"sources:\s*\[(.*?)\],", response.text, re.DOTALL)
|
|
40
|
+
script_content = script_match[1] if script_match else None
|
|
39
41
|
|
|
40
42
|
if not script_content:
|
|
41
43
|
raise ValueError("Gerekli script bulunamadı.")
|
|
@@ -74,11 +76,11 @@ class VidMoly(ExtractorBase):
|
|
|
74
76
|
if not video_url:
|
|
75
77
|
raise ValueError("Video URL bulunamadı.")
|
|
76
78
|
|
|
77
|
-
await self.close()
|
|
78
79
|
return ExtractResult(
|
|
79
80
|
name = self.name,
|
|
80
81
|
url = video_url,
|
|
81
82
|
referer = self.main_url,
|
|
83
|
+
headers = {},
|
|
82
84
|
subtitles = subtitles
|
|
83
85
|
)
|
|
84
86
|
|
|
@@ -10,9 +10,9 @@ class VidMoxy(ExtractorBase):
|
|
|
10
10
|
|
|
11
11
|
async def extract(self, url, referer=None) -> ExtractResult:
|
|
12
12
|
if referer:
|
|
13
|
-
self.
|
|
13
|
+
self.cffi.headers.update({"Referer": referer})
|
|
14
14
|
|
|
15
|
-
istek = await self.
|
|
15
|
+
istek = await self.cffi.get(url)
|
|
16
16
|
istek.raise_for_status()
|
|
17
17
|
|
|
18
18
|
subtitles = []
|
|
@@ -41,10 +41,10 @@ class VidMoxy(ExtractorBase):
|
|
|
41
41
|
|
|
42
42
|
m3u_link = HexCodec.decode(escaped_hex)
|
|
43
43
|
|
|
44
|
-
await self.close()
|
|
45
44
|
return ExtractResult(
|
|
46
45
|
name = self.name,
|
|
47
46
|
url = m3u_link,
|
|
48
47
|
referer = self.main_url,
|
|
48
|
+
headers = {},
|
|
49
49
|
subtitles = subtitles
|
|
50
50
|
)
|
|
@@ -9,18 +9,18 @@ class VideoSeyred(ExtractorBase):
|
|
|
9
9
|
|
|
10
10
|
async def extract(self, url, referer=None) -> ExtractResult:
|
|
11
11
|
if referer:
|
|
12
|
-
self.
|
|
12
|
+
self.cffi.headers.update({"Referer": referer})
|
|
13
13
|
|
|
14
14
|
video_id = url.split("embed/")[1].split("?")[0]
|
|
15
15
|
if len(video_id) > 10:
|
|
16
|
-
kontrol = await self.
|
|
16
|
+
kontrol = await self.cffi.get(url)
|
|
17
17
|
kontrol.raise_for_status()
|
|
18
18
|
|
|
19
19
|
video_id = re.search(r"playlist\/(.*)\.json", kontrol.text)[1]
|
|
20
20
|
|
|
21
21
|
video_url = f"{self.main_url}/playlist/{video_id}.json"
|
|
22
22
|
|
|
23
|
-
response = await self.
|
|
23
|
+
response = await self.cffi.get(video_url)
|
|
24
24
|
response.raise_for_status()
|
|
25
25
|
|
|
26
26
|
try:
|
|
@@ -43,6 +43,7 @@ class VideoSeyred(ExtractorBase):
|
|
|
43
43
|
name = self.name,
|
|
44
44
|
url = self.fix_url(source["file"]),
|
|
45
45
|
referer = self.main_url,
|
|
46
|
+
headers = {},
|
|
46
47
|
subtitles = subtitles,
|
|
47
48
|
)
|
|
48
49
|
for source in response_data.get("sources", [])
|
KekikStream/Plugins/DiziBox.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from KekikStream.Core import kekik_cache, PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
|
|
4
4
|
from Kekik.Sifreleme import CryptoJS
|
|
5
5
|
from parsel import Selector
|
|
6
|
-
import re, urllib.parse, base64, contextlib, asyncio
|
|
6
|
+
import re, urllib.parse, base64, contextlib, asyncio, time
|
|
7
7
|
|
|
8
8
|
class DiziBox(PluginBase):
|
|
9
9
|
name = "DiziBox"
|
|
@@ -40,11 +40,15 @@ class DiziBox(PluginBase):
|
|
|
40
40
|
f"{main_url}/dizi-arsivi/page/SAYFA/?tur[0]=yarisma&yil&imdb" : "Yarışma"
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
#@kekik_cache(ttl=60*60)
|
|
44
44
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
45
|
-
|
|
45
|
+
self.cffi.cookies.update({
|
|
46
|
+
"isTrustedUser" : "true",
|
|
47
|
+
"dbxu" : str(time.time() * 1000).split(".")[0]
|
|
48
|
+
})
|
|
49
|
+
istek = await self.cffi.get(
|
|
46
50
|
url = f"{url.replace('SAYFA', str(page))}",
|
|
47
|
-
|
|
51
|
+
allow_redirects = True
|
|
48
52
|
)
|
|
49
53
|
secici = Selector(istek.text)
|
|
50
54
|
|
|
@@ -58,14 +62,13 @@ class DiziBox(PluginBase):
|
|
|
58
62
|
for veri in secici.css("article.detailed-article")
|
|
59
63
|
]
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
#@kekik_cache(ttl=60*60)
|
|
62
66
|
async def search(self, query: str) -> list[SearchResult]:
|
|
63
|
-
self.
|
|
64
|
-
"LockUser" : "true",
|
|
67
|
+
self.cffi.cookies.update({
|
|
65
68
|
"isTrustedUser" : "true",
|
|
66
|
-
"dbxu" : "
|
|
69
|
+
"dbxu" : str(time.time() * 1000).split(".")[0]
|
|
67
70
|
})
|
|
68
|
-
istek = await self.
|
|
71
|
+
istek = await self.cffi.get(f"{self.main_url}/?s={query}")
|
|
69
72
|
secici = Selector(istek.text)
|
|
70
73
|
|
|
71
74
|
return [
|
|
@@ -77,9 +80,9 @@ class DiziBox(PluginBase):
|
|
|
77
80
|
for item in secici.css("article.detailed-article")
|
|
78
81
|
]
|
|
79
82
|
|
|
80
|
-
|
|
83
|
+
#@kekik_cache(ttl=60*60)
|
|
81
84
|
async def load_item(self, url: str) -> SeriesInfo:
|
|
82
|
-
istek = await self.
|
|
85
|
+
istek = await self.cffi.get(url)
|
|
83
86
|
secici = Selector(istek.text)
|
|
84
87
|
|
|
85
88
|
title = secici.css("div.tv-overview h1 a::text").get()
|
|
@@ -93,7 +96,7 @@ class DiziBox(PluginBase):
|
|
|
93
96
|
episodes = []
|
|
94
97
|
for sezon_link in secici.css("div#seasons-list a::attr(href)").getall():
|
|
95
98
|
sezon_url = self.fix_url(sezon_link)
|
|
96
|
-
sezon_istek = await self.
|
|
99
|
+
sezon_istek = await self.cffi.get(sezon_url)
|
|
97
100
|
sezon_secici = Selector(sezon_istek.text)
|
|
98
101
|
|
|
99
102
|
for bolum in sezon_secici.css("article.grid-box"):
|
|
@@ -124,20 +127,25 @@ class DiziBox(PluginBase):
|
|
|
124
127
|
actors = actors,
|
|
125
128
|
)
|
|
126
129
|
|
|
127
|
-
|
|
130
|
+
#@kekik_cache(ttl=60*60)
|
|
128
131
|
async def _iframe_decode(self, name:str, iframe_link:str, referer:str) -> list[str]:
|
|
129
132
|
results = []
|
|
130
133
|
|
|
134
|
+
self.cffi.headers.update({"Referer": referer})
|
|
135
|
+
self.cffi.cookies.update({
|
|
136
|
+
"isTrustedUser" : "true",
|
|
137
|
+
"dbxu" : str(time.time() * 1000).split(".")[0]
|
|
138
|
+
})
|
|
139
|
+
|
|
131
140
|
if "/player/king/king.php" in iframe_link:
|
|
132
141
|
iframe_link = iframe_link.replace("king.php?v=", "king.php?wmode=opaque&v=")
|
|
133
|
-
self.httpx.headers.update({"Referer": referer})
|
|
134
142
|
|
|
135
|
-
istek = await self.
|
|
143
|
+
istek = await self.cffi.get(iframe_link)
|
|
136
144
|
secici = Selector(istek.text)
|
|
137
145
|
iframe = secici.css("div#Player iframe::attr(src)").get()
|
|
138
146
|
|
|
139
|
-
self.
|
|
140
|
-
istek = await self.
|
|
147
|
+
self.cffi.headers.update({"Referer": self.main_url})
|
|
148
|
+
istek = await self.cffi.get(iframe)
|
|
141
149
|
|
|
142
150
|
crypt_data = re.search(r"CryptoJS\.AES\.decrypt\(\"(.*)\",\"", istek.text)[1]
|
|
143
151
|
crypt_pass = re.search(r"\",\"(.*)\"\);", istek.text)[1]
|
|
@@ -150,11 +158,10 @@ class DiziBox(PluginBase):
|
|
|
150
158
|
|
|
151
159
|
elif "/player/moly/moly.php" in iframe_link:
|
|
152
160
|
iframe_link = iframe_link.replace("moly.php?h=", "moly.php?wmode=opaque&h=")
|
|
153
|
-
self.httpx.headers.update({"Referer": referer})
|
|
154
161
|
while True:
|
|
155
162
|
await asyncio.sleep(.3)
|
|
156
163
|
with contextlib.suppress(Exception):
|
|
157
|
-
istek = await self.
|
|
164
|
+
istek = await self.cffi.get(iframe_link)
|
|
158
165
|
|
|
159
166
|
if atob_data := re.search(r"unescape\(\"(.*)\"\)", istek.text):
|
|
160
167
|
decoded_atob = urllib.parse.unquote(atob_data[1])
|
|
@@ -171,15 +178,20 @@ class DiziBox(PluginBase):
|
|
|
171
178
|
|
|
172
179
|
return results
|
|
173
180
|
|
|
174
|
-
|
|
175
|
-
async def load_links(self, url: str) -> list[
|
|
176
|
-
istek = await self.
|
|
181
|
+
#@kekik_cache(ttl=15*60)
|
|
182
|
+
async def load_links(self, url: str) -> list[dict]:
|
|
183
|
+
istek = await self.cffi.get(url)
|
|
177
184
|
secici = Selector(istek.text)
|
|
178
185
|
|
|
179
|
-
|
|
186
|
+
results = []
|
|
180
187
|
if main_iframe := secici.css("div#video-area iframe::attr(src)").get():
|
|
181
188
|
if decoded := await self._iframe_decode(self.name, main_iframe, url):
|
|
182
|
-
|
|
189
|
+
for iframe in decoded:
|
|
190
|
+
extractor = self.ex_manager.find_extractor(iframe)
|
|
191
|
+
results.append({
|
|
192
|
+
"url" : iframe,
|
|
193
|
+
"name" : f"{extractor.name if extractor else 'Main Player'}"
|
|
194
|
+
})
|
|
183
195
|
|
|
184
196
|
for alternatif in secici.css("div.video-toolbar option[value]"):
|
|
185
197
|
alt_name = alternatif.css("::text").get()
|
|
@@ -188,13 +200,18 @@ class DiziBox(PluginBase):
|
|
|
188
200
|
if not alt_link:
|
|
189
201
|
continue
|
|
190
202
|
|
|
191
|
-
self.
|
|
192
|
-
alt_istek = await self.
|
|
203
|
+
self.cffi.headers.update({"Referer": url})
|
|
204
|
+
alt_istek = await self.cffi.get(alt_link)
|
|
193
205
|
alt_istek.raise_for_status()
|
|
194
206
|
|
|
195
207
|
alt_secici = Selector(alt_istek.text)
|
|
196
208
|
if alt_iframe := alt_secici.css("div#video-area iframe::attr(src)").get():
|
|
197
209
|
if decoded := await self._iframe_decode(alt_name, alt_iframe, url):
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
210
|
+
for iframe in decoded:
|
|
211
|
+
extractor = self.ex_manager.find_extractor(iframe)
|
|
212
|
+
results.append({
|
|
213
|
+
"url" : iframe,
|
|
214
|
+
"name" : f"{extractor.name if extractor else alt_name}"
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
return results
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, Subtitle
|
|
4
|
+
from parsel import Selector
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
class DiziPal(PluginBase):
|
|
8
|
+
name = "DiziPal"
|
|
9
|
+
language = "tr"
|
|
10
|
+
main_url = "https://dizipal1222.com"
|
|
11
|
+
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
12
|
+
description = "Yabancı Dizi ve Film izle."
|
|
13
|
+
|
|
14
|
+
main_page = {
|
|
15
|
+
f"{main_url}/diziler/son-bolumler" : "Son Bölümler",
|
|
16
|
+
f"{main_url}/diziler" : "Yeni Diziler",
|
|
17
|
+
f"{main_url}/filmler" : "Yeni Filmler",
|
|
18
|
+
f"{main_url}/koleksiyon/netflix" : "Netflix",
|
|
19
|
+
f"{main_url}/koleksiyon/exxen" : "Exxen",
|
|
20
|
+
f"{main_url}/koleksiyon/blutv" : "BluTV",
|
|
21
|
+
f"{main_url}/koleksiyon/disney" : "Disney+",
|
|
22
|
+
f"{main_url}/koleksiyon/amazon-prime" : "Amazon Prime",
|
|
23
|
+
f"{main_url}/koleksiyon/tod-bein" : "TOD (beIN)",
|
|
24
|
+
f"{main_url}/koleksiyon/gain" : "Gain",
|
|
25
|
+
f"{main_url}/tur/mubi" : "Mubi",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
29
|
+
istek = await self.cffi.get(url)
|
|
30
|
+
secici = Selector(istek.text)
|
|
31
|
+
|
|
32
|
+
results = []
|
|
33
|
+
|
|
34
|
+
if "/son-bolumler" in url:
|
|
35
|
+
for veri in secici.css("div.episode-item"):
|
|
36
|
+
name = veri.css("div.name::text").get()
|
|
37
|
+
episode = veri.css("div.episode::text").get()
|
|
38
|
+
href = veri.css("a::attr(href)").get()
|
|
39
|
+
poster = veri.css("img::attr(src)").get()
|
|
40
|
+
|
|
41
|
+
if name and href:
|
|
42
|
+
ep_text = episode.strip().replace(". Sezon ", "x").replace(". Bölüm", "") if episode else ""
|
|
43
|
+
title = f"{name} {ep_text}"
|
|
44
|
+
# Son bölümler linkini dizi sayfasına çevir
|
|
45
|
+
dizi_url = href.split("/sezon")[0] if "/sezon" in href else href
|
|
46
|
+
|
|
47
|
+
results.append(MainPageResult(
|
|
48
|
+
category = category,
|
|
49
|
+
title = title,
|
|
50
|
+
url = self.fix_url(dizi_url),
|
|
51
|
+
poster = self.fix_url(poster) if poster else None,
|
|
52
|
+
))
|
|
53
|
+
else:
|
|
54
|
+
for veri in secici.css("article.type2 ul li"):
|
|
55
|
+
title = veri.css("span.title::text").get()
|
|
56
|
+
href = veri.css("a::attr(href)").get()
|
|
57
|
+
poster = veri.css("img::attr(src)").get()
|
|
58
|
+
|
|
59
|
+
if title and href:
|
|
60
|
+
results.append(MainPageResult(
|
|
61
|
+
category = category,
|
|
62
|
+
title = title,
|
|
63
|
+
url = self.fix_url(href),
|
|
64
|
+
poster = self.fix_url(poster) if poster else None,
|
|
65
|
+
))
|
|
66
|
+
|
|
67
|
+
return results
|
|
68
|
+
|
|
69
|
+
async def search(self, query: str) -> list[SearchResult]:
|
|
70
|
+
self.cffi.headers.update({
|
|
71
|
+
"Accept" : "application/json, text/javascript, */*; q=0.01",
|
|
72
|
+
"X-Requested-With" : "XMLHttpRequest"
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
istek = await self.cffi.post(
|
|
76
|
+
url = f"{self.main_url}/api/search-autocomplete",
|
|
77
|
+
data = {"query": query}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
data = istek.json()
|
|
82
|
+
except Exception:
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
results = []
|
|
86
|
+
|
|
87
|
+
# API bazen dict, bazen list döner
|
|
88
|
+
items = data.values() if isinstance(data, dict) else data
|
|
89
|
+
|
|
90
|
+
for item in items:
|
|
91
|
+
if not isinstance(item, dict):
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
title = item.get("title")
|
|
95
|
+
url = item.get("url")
|
|
96
|
+
poster = item.get("poster")
|
|
97
|
+
|
|
98
|
+
if title and url:
|
|
99
|
+
results.append(SearchResult(
|
|
100
|
+
title = title,
|
|
101
|
+
url = f"{self.main_url}{url}",
|
|
102
|
+
poster = self.fix_url(poster) if poster else None,
|
|
103
|
+
))
|
|
104
|
+
|
|
105
|
+
return results
|
|
106
|
+
|
|
107
|
+
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
108
|
+
# Reset headers to get HTML response
|
|
109
|
+
self.cffi.headers.update({
|
|
110
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
111
|
+
})
|
|
112
|
+
self.cffi.headers.pop("X-Requested-With", None)
|
|
113
|
+
|
|
114
|
+
istek = await self.cffi.get(url)
|
|
115
|
+
secici = Selector(text=istek.text, type="html")
|
|
116
|
+
|
|
117
|
+
poster = self.fix_url(secici.css("meta[property='og:image']::attr(content)").get())
|
|
118
|
+
year = secici.xpath("//div[text()='Yapım Yılı']//following-sibling::div/text()").get()
|
|
119
|
+
description = secici.css("div.summary p::text").get()
|
|
120
|
+
rating = secici.xpath("//div[text()='IMDB Puanı']//following-sibling::div/text()").get()
|
|
121
|
+
tags_raw = secici.xpath("//div[text()='Türler']//following-sibling::div/text()").get()
|
|
122
|
+
tags = [t.strip() for t in tags_raw.split() if t.strip()] if tags_raw else None
|
|
123
|
+
|
|
124
|
+
dur_text = secici.xpath("//div[text()='Ortalama Süre']//following-sibling::div/text()").get()
|
|
125
|
+
dur_match = re.search(r"(\d+)", dur_text or "")
|
|
126
|
+
duration = int(dur_match[1]) if dur_match else None
|
|
127
|
+
|
|
128
|
+
if "/dizi/" in url:
|
|
129
|
+
title = secici.css("div.cover h5::text").get()
|
|
130
|
+
|
|
131
|
+
episodes = []
|
|
132
|
+
for ep in secici.css("div.episode-item"):
|
|
133
|
+
ep_name = ep.css("div.name::text").get()
|
|
134
|
+
ep_href = ep.css("a::attr(href)").get()
|
|
135
|
+
ep_text = ep.css("div.episode::text").get() or ""
|
|
136
|
+
ep_parts = ep_text.strip().split(" ")
|
|
137
|
+
|
|
138
|
+
ep_season = None
|
|
139
|
+
ep_episode = None
|
|
140
|
+
if len(ep_parts) >= 4:
|
|
141
|
+
try:
|
|
142
|
+
ep_season = int(ep_parts[0].replace(".", ""))
|
|
143
|
+
ep_episode = int(ep_parts[2].replace(".", ""))
|
|
144
|
+
except ValueError:
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
if ep_name and ep_href:
|
|
148
|
+
episodes.append(Episode(
|
|
149
|
+
season = ep_season,
|
|
150
|
+
episode = ep_episode,
|
|
151
|
+
title = ep_name.strip(),
|
|
152
|
+
url = self.fix_url(ep_href),
|
|
153
|
+
))
|
|
154
|
+
|
|
155
|
+
return SeriesInfo(
|
|
156
|
+
url = url,
|
|
157
|
+
poster = poster,
|
|
158
|
+
title = title,
|
|
159
|
+
description = description.strip() if description else None,
|
|
160
|
+
tags = tags,
|
|
161
|
+
rating = rating.strip() if rating else None,
|
|
162
|
+
year = year.strip() if year else None,
|
|
163
|
+
duration = duration,
|
|
164
|
+
episodes = episodes if episodes else None,
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
title = secici.xpath("//div[@class='g-title'][2]/div/text()").get()
|
|
168
|
+
|
|
169
|
+
return MovieInfo(
|
|
170
|
+
url = url,
|
|
171
|
+
poster = poster,
|
|
172
|
+
title = title.strip() if title else None,
|
|
173
|
+
description = description.strip() if description else None,
|
|
174
|
+
tags = tags,
|
|
175
|
+
rating = rating.strip() if rating else None,
|
|
176
|
+
year = year.strip() if year else None,
|
|
177
|
+
duration = duration,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
async def load_links(self, url: str) -> list[dict]:
|
|
181
|
+
# Reset headers to get HTML response
|
|
182
|
+
self.cffi.headers.update({
|
|
183
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
184
|
+
})
|
|
185
|
+
self.cffi.headers.pop("X-Requested-With", None)
|
|
186
|
+
|
|
187
|
+
istek = await self.cffi.get(url)
|
|
188
|
+
secici = Selector(istek.text)
|
|
189
|
+
|
|
190
|
+
iframe = secici.css(".series-player-container iframe::attr(src)").get()
|
|
191
|
+
if not iframe:
|
|
192
|
+
iframe = secici.css("div#vast_new iframe::attr(src)").get()
|
|
193
|
+
|
|
194
|
+
if not iframe:
|
|
195
|
+
return []
|
|
196
|
+
|
|
197
|
+
results = []
|
|
198
|
+
|
|
199
|
+
self.cffi.headers.update({"Referer": f"{self.main_url}/"})
|
|
200
|
+
i_istek = await self.cffi.get(iframe)
|
|
201
|
+
i_text = i_istek.text
|
|
202
|
+
|
|
203
|
+
# m3u link çıkar
|
|
204
|
+
m3u_match = re.search(r'file:"([^"]+)"', i_text)
|
|
205
|
+
if m3u_match:
|
|
206
|
+
m3u_link = m3u_match[1]
|
|
207
|
+
|
|
208
|
+
# Altyazıları çıkar
|
|
209
|
+
subtitles = []
|
|
210
|
+
sub_match = re.search(r'"subtitle":"([^"]+)"', i_text)
|
|
211
|
+
if sub_match:
|
|
212
|
+
sub_text = sub_match[1]
|
|
213
|
+
if "," in sub_text:
|
|
214
|
+
for sub in sub_text.split(","):
|
|
215
|
+
lang = sub.split("[")[1].split("]")[0] if "[" in sub else "Türkçe"
|
|
216
|
+
sub_url = sub.replace(f"[{lang}]", "")
|
|
217
|
+
subtitles.append(Subtitle(name=lang, url=self.fix_url(sub_url)))
|
|
218
|
+
else:
|
|
219
|
+
lang = sub_text.split("[")[1].split("]")[0] if "[" in sub_text else "Türkçe"
|
|
220
|
+
sub_url = sub_text.replace(f"[{lang}]", "")
|
|
221
|
+
subtitles.append(Subtitle(name=lang, url=self.fix_url(sub_url)))
|
|
222
|
+
|
|
223
|
+
results.append({
|
|
224
|
+
"name" : self.name,
|
|
225
|
+
"url" : m3u_link,
|
|
226
|
+
"referer" : f"{self.main_url}/",
|
|
227
|
+
"subtitles" : subtitles
|
|
228
|
+
})
|
|
229
|
+
else:
|
|
230
|
+
# Extractor'a yönlendir
|
|
231
|
+
extractor = self.ex_manager.find_extractor(iframe)
|
|
232
|
+
results.append({
|
|
233
|
+
"name" : f"{extractor.name if extractor else self.name}",
|
|
234
|
+
"url" : iframe,
|
|
235
|
+
"referer" : f"{self.main_url}/",
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
return results
|
|
239
|
+
|
|
240
|
+
async def play(self, name: str, url: str, referer: str, subtitles: list[Subtitle]):
|
|
241
|
+
extract_result = ExtractResult(name=name, url=url, referer=referer, subtitles=subtitles)
|
|
242
|
+
self.media_handler.title = name
|
|
243
|
+
if self.name not in self.media_handler.title:
|
|
244
|
+
self.media_handler.title = f"{self.name} | {self.media_handler.title}"
|
|
245
|
+
|
|
246
|
+
self.media_handler.play_media(extract_result)
|