KekikStream 2.2.7__py3-none-any.whl → 2.2.9__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/Extractors/CloseLoad.py +41 -4
- KekikStream/Extractors/Filemoon.py +41 -26
- KekikStream/Plugins/Dizilla.py +64 -37
- KekikStream/Plugins/HDFilmCehennemi.py +31 -35
- KekikStream/Plugins/SetFilmIzle.py +47 -44
- KekikStream/Plugins/SezonlukDizi.py +51 -51
- {kekikstream-2.2.7.dist-info → kekikstream-2.2.9.dist-info}/METADATA +1 -1
- {kekikstream-2.2.7.dist-info → kekikstream-2.2.9.dist-info}/RECORD +12 -12
- {kekikstream-2.2.7.dist-info → kekikstream-2.2.9.dist-info}/WHEEL +0 -0
- {kekikstream-2.2.7.dist-info → kekikstream-2.2.9.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.2.7.dist-info → kekikstream-2.2.9.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.2.7.dist-info → kekikstream-2.2.9.dist-info}/top_level.txt +0 -0
|
@@ -3,22 +3,59 @@
|
|
|
3
3
|
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
4
|
from Kekik.Sifreleme import Packer, StreamDecoder
|
|
5
5
|
from selectolax.parser import HTMLParser
|
|
6
|
-
import re
|
|
6
|
+
import re, json
|
|
7
7
|
|
|
8
8
|
class CloseLoadExtractor(ExtractorBase):
|
|
9
9
|
name = "CloseLoad"
|
|
10
10
|
main_url = "https://closeload.filmmakinesi.to"
|
|
11
11
|
|
|
12
|
+
def _extract_from_json_ld(self, html: str) -> str | None:
|
|
13
|
+
"""JSON-LD script tag'inden contentUrl'i çıkar (Kotlin versiyonundaki gibi)"""
|
|
14
|
+
secici = HTMLParser(html)
|
|
15
|
+
for script in secici.css("script[type='application/ld+json']"):
|
|
16
|
+
try:
|
|
17
|
+
data = json.loads(script.text(strip=True))
|
|
18
|
+
if content_url := data.get("contentUrl"):
|
|
19
|
+
if content_url.startswith("http"):
|
|
20
|
+
return content_url
|
|
21
|
+
except (json.JSONDecodeError, TypeError):
|
|
22
|
+
# Regex ile contentUrl'i çıkarmayı dene
|
|
23
|
+
match = re.search(r'"contentUrl"\s*:\s*"([^"]+)"', script.text())
|
|
24
|
+
if match and match.group(1).startswith("http"):
|
|
25
|
+
return match.group(1)
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
def _extract_from_packed(self, html: str) -> str | None:
|
|
29
|
+
"""Packed JavaScript'ten video URL'sini çıkar (fallback)"""
|
|
30
|
+
try:
|
|
31
|
+
eval_func = re.compile(r'\s*(eval\(function[\s\S].*)').findall(html)
|
|
32
|
+
if eval_func:
|
|
33
|
+
return StreamDecoder.extract_stream_url(Packer.unpack(eval_func[0]))
|
|
34
|
+
except Exception:
|
|
35
|
+
pass
|
|
36
|
+
return None
|
|
37
|
+
|
|
12
38
|
async def extract(self, url, referer=None) -> ExtractResult:
|
|
13
39
|
if referer:
|
|
14
40
|
self.httpx.headers.update({"Referer": referer})
|
|
41
|
+
|
|
42
|
+
self.httpx.headers.update({
|
|
43
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0",
|
|
44
|
+
"Origin": self.main_url
|
|
45
|
+
})
|
|
15
46
|
|
|
16
47
|
istek = await self.httpx.get(url)
|
|
17
48
|
istek.raise_for_status()
|
|
18
49
|
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
50
|
+
# Önce JSON-LD'den dene (daha güvenilir - Kotlin versiyonu gibi)
|
|
51
|
+
m3u_link = self._extract_from_json_ld(istek.text)
|
|
52
|
+
|
|
53
|
+
# Fallback: Packed JavaScript'ten çıkar
|
|
54
|
+
if not m3u_link:
|
|
55
|
+
m3u_link = self._extract_from_packed(istek.text)
|
|
56
|
+
|
|
57
|
+
if not m3u_link:
|
|
58
|
+
raise Exception("Video URL bulunamadı (ne JSON-LD ne de packed script'ten)")
|
|
22
59
|
|
|
23
60
|
# Subtitle'ları parse et (Kotlin referansı: track elementleri)
|
|
24
61
|
subtitles = []
|
|
@@ -22,13 +22,14 @@ class Filemoon(ExtractorBase):
|
|
|
22
22
|
return any(domain in url for domain in self.supported_domains)
|
|
23
23
|
|
|
24
24
|
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
25
|
-
|
|
26
|
-
"Referer"
|
|
27
|
-
"Sec-Fetch-Dest"
|
|
28
|
-
"Sec-Fetch-Mode"
|
|
29
|
-
"Sec-Fetch-Site"
|
|
25
|
+
default_headers = {
|
|
26
|
+
"Referer" : url,
|
|
27
|
+
"Sec-Fetch-Dest" : "iframe",
|
|
28
|
+
"Sec-Fetch-Mode" : "navigate",
|
|
29
|
+
"Sec-Fetch-Site" : "cross-site",
|
|
30
|
+
"User-Agent" : "Mozilla/5.0 (X11; Linux x86_64; rv:137.0) Gecko/20100101 Firefox/137.0"
|
|
30
31
|
}
|
|
31
|
-
self.httpx.headers.update(
|
|
32
|
+
self.httpx.headers.update(default_headers)
|
|
32
33
|
|
|
33
34
|
# İlk sayfayı al
|
|
34
35
|
istek = await self.httpx.get(url)
|
|
@@ -38,31 +39,44 @@ class Filemoon(ExtractorBase):
|
|
|
38
39
|
# Eğer iframe varsa, iframe'e git
|
|
39
40
|
iframe_el = secici.css_first("iframe")
|
|
40
41
|
iframe_src = iframe_el.attrs.get("src") if iframe_el else None
|
|
41
|
-
|
|
42
|
-
iframe_url = self.fix_url(iframe_src)
|
|
43
|
-
self.httpx.headers.update({
|
|
44
|
-
"Accept-Language" : "en-US,en;q=0.5",
|
|
45
|
-
"Sec-Fetch-Dest" : "iframe"
|
|
46
|
-
})
|
|
47
|
-
istek = await self.httpx.get(iframe_url)
|
|
48
|
-
response = istek.text
|
|
49
|
-
|
|
50
|
-
# Packed script'i bul ve unpack et
|
|
42
|
+
|
|
51
43
|
m3u8_url = None
|
|
52
|
-
|
|
53
|
-
if
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if
|
|
44
|
+
|
|
45
|
+
if not iframe_src:
|
|
46
|
+
# Fallback: Script içinde ara (Kotlin: selectFirst("script:containsData(function(p,a,c,k,e,d))"))
|
|
47
|
+
script_data = ""
|
|
48
|
+
for script in secici.css("script"):
|
|
49
|
+
if "function(p,a,c,k,e,d)" in script.text():
|
|
50
|
+
script_data = script.text()
|
|
51
|
+
break
|
|
52
|
+
|
|
53
|
+
if script_data:
|
|
54
|
+
unpacked = Packer.unpack(script_data)
|
|
55
|
+
if match := re.search(r'sources:\[\{file:"(.*?)"', unpacked):
|
|
58
56
|
m3u8_url = match.group(1)
|
|
59
|
-
|
|
57
|
+
else:
|
|
58
|
+
# Iframe varsa devam et
|
|
59
|
+
iframe_url = self.fix_url(iframe_src)
|
|
60
|
+
iframe_headers = default_headers.copy()
|
|
61
|
+
iframe_headers["Accept-Language"] = "en-US,en;q=0.5"
|
|
62
|
+
|
|
63
|
+
istek = await self.httpx.get(iframe_url, headers=iframe_headers)
|
|
64
|
+
response = istek.text
|
|
65
|
+
secici = HTMLParser(response)
|
|
66
|
+
|
|
67
|
+
script_data = ""
|
|
68
|
+
for script in secici.css("script"):
|
|
69
|
+
if "function(p,a,c,k,e,d)" in script.text():
|
|
70
|
+
script_data = script.text()
|
|
71
|
+
break
|
|
72
|
+
|
|
73
|
+
if script_data:
|
|
74
|
+
unpacked = Packer.unpack(script_data)
|
|
75
|
+
if match := re.search(r'sources:\[\{file:"(.*?)"', unpacked):
|
|
60
76
|
m3u8_url = match.group(1)
|
|
61
|
-
except Exception:
|
|
62
|
-
pass
|
|
63
77
|
|
|
64
|
-
# Fallback: Doğrudan response'ta ara
|
|
65
78
|
if not m3u8_url:
|
|
79
|
+
# Son çare: Normal response içinde ara
|
|
66
80
|
if match := re.search(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"', response):
|
|
67
81
|
m3u8_url = match.group(1)
|
|
68
82
|
elif match := re.search(r'file:\s*"([^"]*?\.m3u8[^"]*)"', response):
|
|
@@ -75,5 +89,6 @@ class Filemoon(ExtractorBase):
|
|
|
75
89
|
name = self.name,
|
|
76
90
|
url = self.fix_url(m3u8_url),
|
|
77
91
|
referer = f"{self.main_url}/",
|
|
92
|
+
user_agent = default_headers["User-Agent"],
|
|
78
93
|
subtitles = []
|
|
79
94
|
)
|
KekikStream/Plugins/Dizilla.py
CHANGED
|
@@ -142,44 +142,70 @@ class Dizilla(PluginBase):
|
|
|
142
142
|
istek = await self.httpx.get(url)
|
|
143
143
|
secici = HTMLParser(istek.text)
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if not
|
|
145
|
+
title = secici.css_first("div.poster.poster h2")
|
|
146
|
+
title = title.text(strip=True) if title else None
|
|
147
|
+
if not title:
|
|
148
148
|
return None
|
|
149
149
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
veri = loads(last_script.text(strip=True))
|
|
150
|
+
poster_el = secici.css_first("div.w-full.page-top.relative img")
|
|
151
|
+
poster = self.fix_url(poster_el.attrs.get("src")) if poster_el else None
|
|
153
152
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
# Year extraction (Kotlin: [1] index for w-fit min-w-fit)
|
|
154
|
+
info_boxes = secici.css("div.w-fit.min-w-fit")
|
|
155
|
+
year = None
|
|
156
|
+
if len(info_boxes) > 1:
|
|
157
|
+
year_el = info_boxes[1].css_first("span.text-sm.opacity-60")
|
|
158
|
+
if year_el:
|
|
159
|
+
year_text = year_el.text(strip=True)
|
|
160
|
+
year = year_text.split(" ")[-1] if " " in year_text else year_text
|
|
161
|
+
|
|
162
|
+
description_el = secici.css_first("div.mt-2.text-sm")
|
|
163
|
+
description = description_el.text(strip=True) if description_el else None
|
|
157
164
|
|
|
158
|
-
poster = self.fix_url(veri.get("image"))
|
|
159
|
-
description = veri.get("description")
|
|
160
|
-
year = veri.get("datePublished").split("-")[0]
|
|
161
|
-
|
|
162
|
-
# Tags extraction from page content (h3 tag)
|
|
163
165
|
tags_el = secici.css_first("div.poster.poster h3")
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if isinstance(episodes, dict):
|
|
175
|
-
episodes = [episodes]
|
|
166
|
+
tags = [t.strip() for t in tags_el.text(strip=True).split(",")] if tags_el else []
|
|
167
|
+
|
|
168
|
+
actors = [h5.text(strip=True) for h5 in secici.css("div.global-box h5")]
|
|
169
|
+
|
|
170
|
+
episodeses = []
|
|
171
|
+
# Seasons links iteration
|
|
172
|
+
season_links = secici.css("div.flex.items-center.flex-wrap.gap-2.mb-4 a")
|
|
173
|
+
for sezon in season_links:
|
|
174
|
+
sezon_href = self.fix_url(sezon.attrs.get("href"))
|
|
175
|
+
sezon_req = await self.httpx.get(sezon_href)
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
season_num = None
|
|
178
|
+
try:
|
|
179
|
+
# URL'den sezon numarasını çek: ...-sezon-X
|
|
180
|
+
season_match = re.search(r"sezon-(\d+)", sezon_href)
|
|
181
|
+
if season_match:
|
|
182
|
+
season_num = int(season_match.group(1))
|
|
183
|
+
except:
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
sezon_secici = HTMLParser(sezon_req.text)
|
|
187
|
+
for bolum in sezon_secici.css("div.episodes div.cursor-pointer"):
|
|
188
|
+
# Kotlin: bolum.select("a").last()
|
|
189
|
+
links = bolum.css("a")
|
|
190
|
+
if not links:
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
ep_link = links[-1]
|
|
194
|
+
ep_name = ep_link.text(strip=True)
|
|
195
|
+
ep_href = self.fix_url(ep_link.attrs.get("href"))
|
|
196
|
+
|
|
197
|
+
# Episode number (first link's text usually)
|
|
198
|
+
ep_num = None
|
|
199
|
+
try:
|
|
200
|
+
ep_num = int(links[0].text(strip=True))
|
|
201
|
+
except:
|
|
202
|
+
pass
|
|
203
|
+
|
|
204
|
+
episodeses.append(Episode(
|
|
205
|
+
season = season_num,
|
|
206
|
+
episode = ep_num,
|
|
207
|
+
title = ep_name,
|
|
208
|
+
url = ep_href
|
|
183
209
|
))
|
|
184
210
|
|
|
185
211
|
return SeriesInfo(
|
|
@@ -188,9 +214,8 @@ class Dizilla(PluginBase):
|
|
|
188
214
|
title = title,
|
|
189
215
|
description = description,
|
|
190
216
|
tags = tags,
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
episodes = bolumler,
|
|
217
|
+
year = str(year) if year else None,
|
|
218
|
+
episodes = episodeses,
|
|
194
219
|
actors = actors
|
|
195
220
|
)
|
|
196
221
|
|
|
@@ -212,7 +237,7 @@ class Dizilla(PluginBase):
|
|
|
212
237
|
|
|
213
238
|
# Get first source (matching Kotlin)
|
|
214
239
|
first_result = results[0]
|
|
215
|
-
source_content = first_result.get("source_content", "")
|
|
240
|
+
source_content = str(first_result.get("source_content", ""))
|
|
216
241
|
|
|
217
242
|
# Clean the source_content string (matching Kotlin: .replace("\"", "").replace("\\", ""))
|
|
218
243
|
cleaned_source = source_content.replace('"', '').replace('\\', '')
|
|
@@ -220,10 +245,12 @@ class Dizilla(PluginBase):
|
|
|
220
245
|
# Parse cleaned HTML
|
|
221
246
|
iframe_el = HTMLParser(cleaned_source).css_first("iframe")
|
|
222
247
|
iframe_src = iframe_el.attrs.get("src") if iframe_el else None
|
|
248
|
+
|
|
249
|
+
# Referer check (matching Kotlin: loadExtractor(iframe, "${mainUrl}/", ...))
|
|
223
250
|
iframe_url = self.fix_url(iframe_src) if iframe_src else None
|
|
224
251
|
|
|
225
252
|
if not iframe_url:
|
|
226
253
|
return []
|
|
227
254
|
|
|
228
|
-
data = await self.extract(iframe_url, prefix=first_result.get('language_name', 'Unknown'))
|
|
255
|
+
data = await self.extract(iframe_url, referer=f"{self.main_url}/", prefix=first_result.get('language_name', 'Unknown'))
|
|
229
256
|
return [data] if data else []
|
|
@@ -205,30 +205,23 @@ class HDFilmCehennemi(PluginBase):
|
|
|
205
205
|
|
|
206
206
|
return results
|
|
207
207
|
|
|
208
|
-
def
|
|
209
|
-
"""
|
|
210
|
-
#
|
|
211
|
-
|
|
212
|
-
if
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
# 3) Array içindeki string parçalarını topla
|
|
226
|
-
parts = re.findall(r'["\']([^"\']+)["\']', array_body)
|
|
227
|
-
if not parts:
|
|
228
|
-
return ""
|
|
229
|
-
|
|
230
|
-
# 4) Özel decoder ile çöz
|
|
231
|
-
return StreamDecoder.extract_stream_url(unpacked)
|
|
208
|
+
def _extract_from_json_ld(self, html: str) -> str | None:
|
|
209
|
+
"""JSON-LD script tag'inden contentUrl'i çıkar (Kotlin versiyonundaki gibi)"""
|
|
210
|
+
# Önce JSON-LD'den dene
|
|
211
|
+
json_ld_match = re.search(r'<script[^>]+type=["\']application/ld\+json["\'][^>]*>(.*?)</script>', html, re.DOTALL)
|
|
212
|
+
if json_ld_match:
|
|
213
|
+
try:
|
|
214
|
+
import json
|
|
215
|
+
data = json.loads(json_ld_match.group(1).strip())
|
|
216
|
+
if content_url := data.get("contentUrl"):
|
|
217
|
+
if content_url.startswith("http"):
|
|
218
|
+
return content_url
|
|
219
|
+
except Exception:
|
|
220
|
+
# Regex ile contentUrl'i çıkarmayı dene
|
|
221
|
+
match = re.search(r'"contentUrl"\s*:\s*"([^"]+)"', html)
|
|
222
|
+
if match and match.group(1).startswith("http"):
|
|
223
|
+
return match.group(1)
|
|
224
|
+
return None
|
|
232
225
|
|
|
233
226
|
async def invoke_local_source(self, iframe: str, source: str, url: str):
|
|
234
227
|
self.httpx.headers.update({
|
|
@@ -240,18 +233,21 @@ class HDFilmCehennemi(PluginBase):
|
|
|
240
233
|
if not istek.text:
|
|
241
234
|
return await self.cehennempass(iframe.split("/")[-1])
|
|
242
235
|
|
|
243
|
-
#
|
|
244
|
-
|
|
245
|
-
if not eval_match:
|
|
246
|
-
return await self.cehennempass(iframe.split("/")[-1])
|
|
236
|
+
# Önce JSON-LD'den dene (Kotlin versiyonu gibi - daha güvenilir)
|
|
237
|
+
video_url = self._extract_from_json_ld(istek.text)
|
|
247
238
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
239
|
+
# Fallback: Packed JavaScript'ten çıkar
|
|
240
|
+
if not video_url:
|
|
241
|
+
# eval(function...) içeren packed script bul
|
|
242
|
+
eval_match = re.search(r'(eval\(function[\s\S]+)', istek.text)
|
|
243
|
+
if not eval_match:
|
|
244
|
+
return await self.cehennempass(iframe.split("/")[-1])
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
unpacked = Packer.unpack(eval_match.group(1))
|
|
248
|
+
video_url = StreamDecoder.extract_stream_url(unpacked)
|
|
249
|
+
except Exception:
|
|
250
|
+
return await self.cehennempass(iframe.split("/")[-1])
|
|
255
251
|
|
|
256
252
|
if not video_url:
|
|
257
253
|
return await self.cehennempass(iframe.split("/")[-1])
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult
|
|
4
4
|
from selectolax.parser import HTMLParser
|
|
5
|
-
import re, json
|
|
5
|
+
import re, json, asyncio
|
|
6
6
|
|
|
7
7
|
class SetFilmIzle(PluginBase):
|
|
8
8
|
name = "SetFilmIzle"
|
|
@@ -229,7 +229,7 @@ class SetFilmIzle(PluginBase):
|
|
|
229
229
|
istek = await self.httpx.get(url)
|
|
230
230
|
secici = HTMLParser(istek.text)
|
|
231
231
|
|
|
232
|
-
nonce =
|
|
232
|
+
nonce = secici.css_first("div#playex").attrs.get("data-nonce") if secici.css_first("div#playex") else ""
|
|
233
233
|
|
|
234
234
|
# partKey to dil label mapping
|
|
235
235
|
part_key_labels = {
|
|
@@ -238,46 +238,49 @@ class SetFilmIzle(PluginBase):
|
|
|
238
238
|
"orijinal" : "Orijinal"
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
semaphore = asyncio.Semaphore(5)
|
|
242
|
+
tasks = []
|
|
243
|
+
|
|
244
|
+
async def fetch_and_extract(player):
|
|
245
|
+
async with semaphore:
|
|
246
|
+
source_id = player.attrs.get("data-post-id")
|
|
247
|
+
player_name = player.attrs.get("data-player-name")
|
|
248
|
+
part_key = player.attrs.get("data-part-key")
|
|
249
|
+
|
|
250
|
+
if not source_id or "event" in source_id or source_id == "":
|
|
251
|
+
return None
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
resp = self.cloudscraper.post(
|
|
255
|
+
f"{self.main_url}/wp-admin/admin-ajax.php",
|
|
256
|
+
headers = {"Referer": url},
|
|
257
|
+
data = {
|
|
258
|
+
"action" : "get_video_url",
|
|
259
|
+
"nonce" : nonce,
|
|
260
|
+
"post_id" : source_id,
|
|
261
|
+
"player_name" : player_name or "",
|
|
262
|
+
"part_key" : part_key or ""
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
data = resp.json()
|
|
266
|
+
except:
|
|
267
|
+
return None
|
|
268
|
+
|
|
269
|
+
iframe_url = data.get("data", {}).get("url")
|
|
270
|
+
if not iframe_url:
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
if "setplay" not in iframe_url and part_key:
|
|
274
|
+
iframe_url = f"{iframe_url}?partKey={part_key}"
|
|
275
|
+
|
|
276
|
+
label = part_key_labels.get(part_key, "")
|
|
277
|
+
if not label and part_key:
|
|
278
|
+
label = part_key.replace("_", " ").title()
|
|
279
|
+
|
|
280
|
+
return await self.extract(iframe_url, prefix=label if label else None)
|
|
281
|
+
|
|
242
282
|
for player in secici.css("nav.player a"):
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if not source_id or "event" in source_id or source_id == "":
|
|
248
|
-
continue
|
|
249
|
-
|
|
250
|
-
try:
|
|
251
|
-
resp = self.cloudscraper.post(
|
|
252
|
-
f"{self.main_url}/wp-admin/admin-ajax.php",
|
|
253
|
-
headers = {"Referer": url},
|
|
254
|
-
data = {
|
|
255
|
-
"action" : "get_video_url",
|
|
256
|
-
"nonce" : nonce,
|
|
257
|
-
"post_id" : source_id,
|
|
258
|
-
"player_name" : player_name or "",
|
|
259
|
-
"part_key" : part_key or ""
|
|
260
|
-
}
|
|
261
|
-
)
|
|
262
|
-
data = resp.json()
|
|
263
|
-
except:
|
|
264
|
-
continue
|
|
265
|
-
|
|
266
|
-
iframe_url = data.get("data", {}).get("url")
|
|
267
|
-
if not iframe_url:
|
|
268
|
-
continue
|
|
269
|
-
|
|
270
|
-
# SetPlay URL'si için part_key ekleme
|
|
271
|
-
if "setplay" not in iframe_url and part_key:
|
|
272
|
-
iframe_url = f"{iframe_url}?partKey={part_key}"
|
|
273
|
-
|
|
274
|
-
# Dil etiketi oluştur
|
|
275
|
-
label = part_key_labels.get(part_key, "")
|
|
276
|
-
if not label and part_key:
|
|
277
|
-
label = part_key.replace("_", " ").title()
|
|
278
|
-
|
|
279
|
-
data = await self.extract(iframe_url, prefix=label if label else None)
|
|
280
|
-
if data:
|
|
281
|
-
links.append(data)
|
|
282
|
-
|
|
283
|
-
return links
|
|
283
|
+
tasks.append(fetch_and_extract(player))
|
|
284
|
+
|
|
285
|
+
results = await asyncio.gather(*tasks)
|
|
286
|
+
return [r for r in results if r]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
|
|
4
4
|
from selectolax.parser import HTMLParser
|
|
5
|
-
import re
|
|
5
|
+
import re, asyncio
|
|
6
6
|
|
|
7
7
|
class SezonlukDizi(PluginBase):
|
|
8
8
|
name = "SezonlukDizi"
|
|
@@ -39,6 +39,16 @@ class SezonlukDizi(PluginBase):
|
|
|
39
39
|
f"{main_url}/diziler.asp?siralama_tipi=id&tur=western&s=" : "Western"
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
async def _get_asp_data(self) -> dict:
|
|
43
|
+
js_req = await self.httpx.get(f"{self.main_url}/js/site.min.js")
|
|
44
|
+
alt_match = re.search(r"dataAlternatif(.*?)\.asp", js_req.text)
|
|
45
|
+
embed_match = re.search(r"dataEmbed(.*?)\.asp", js_req.text)
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
"alternatif": alt_match.group(1) if alt_match else "",
|
|
49
|
+
"embed": embed_match.group(1) if embed_match else ""
|
|
50
|
+
}
|
|
51
|
+
|
|
42
52
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
43
53
|
istek = await self.httpx.get(f"{url}{page}")
|
|
44
54
|
secici = HTMLParser(istek.text)
|
|
@@ -67,7 +77,7 @@ class SezonlukDizi(PluginBase):
|
|
|
67
77
|
secici = HTMLParser(istek.text)
|
|
68
78
|
|
|
69
79
|
results = []
|
|
70
|
-
for afis in secici.css("div.afis a
|
|
80
|
+
for afis in secici.css("div.afis a"):
|
|
71
81
|
desc_el = afis.css_first("div.description")
|
|
72
82
|
img_el = afis.css_first("img")
|
|
73
83
|
|
|
@@ -169,64 +179,54 @@ class SezonlukDizi(PluginBase):
|
|
|
169
179
|
actors = actors
|
|
170
180
|
)
|
|
171
181
|
|
|
172
|
-
async def get_asp_data(self) -> tuple[str, str]:
|
|
173
|
-
"""Fetch dynamic ASP version numbers from site.min.js"""
|
|
174
|
-
try:
|
|
175
|
-
js_content = await self.httpx.get(f"{self.main_url}/js/site.min.js")
|
|
176
|
-
alternatif_match = re.search(r'dataAlternatif(.*?)\.asp', js_content.text)
|
|
177
|
-
embed_match = re.search(r'dataEmbed(.*?)\.asp', js_content.text)
|
|
178
|
-
|
|
179
|
-
alternatif_ver = alternatif_match.group(1) if alternatif_match else "22"
|
|
180
|
-
embed_ver = embed_match.group(1) if embed_match else "22"
|
|
181
|
-
|
|
182
|
-
return (alternatif_ver, embed_ver)
|
|
183
|
-
except Exception:
|
|
184
|
-
return ("22", "22") # Fallback to default versions
|
|
185
|
-
|
|
186
182
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
187
183
|
istek = await self.httpx.get(url)
|
|
188
184
|
secici = HTMLParser(istek.text)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
bid =
|
|
185
|
+
asp_data = await self._get_asp_data()
|
|
186
|
+
|
|
187
|
+
bid = secici.css_first("div#dilsec").attrs.get("data-id") if secici.css_first("div#dilsec") else None
|
|
192
188
|
if not bid:
|
|
193
189
|
return []
|
|
194
190
|
|
|
195
|
-
|
|
196
|
-
|
|
191
|
+
semaphore = asyncio.Semaphore(5)
|
|
192
|
+
tasks = []
|
|
197
193
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
194
|
+
async def fetch_and_extract(veri, dil_etiketi):
|
|
195
|
+
async with semaphore:
|
|
196
|
+
try:
|
|
197
|
+
embed_resp = await self.httpx.post(
|
|
198
|
+
f"{self.main_url}/ajax/dataEmbed{asp_data['embed']}.asp",
|
|
199
|
+
headers = {"X-Requested-With": "XMLHttpRequest"},
|
|
200
|
+
data = {"id": str(veri.get("id"))}
|
|
201
|
+
)
|
|
202
|
+
embed_secici = HTMLParser(embed_resp.text)
|
|
203
|
+
iframe_el = embed_secici.css_first("iframe")
|
|
204
|
+
iframe_src = iframe_el.attrs.get("src") if iframe_el else None
|
|
205
|
+
|
|
206
|
+
if iframe_src:
|
|
207
|
+
if "link.asp" in iframe_src:
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
iframe_url = self.fix_url(iframe_src)
|
|
211
|
+
return await self.extract(iframe_url, referer=f"{self.main_url}/", prefix=f"{dil_etiketi} - {veri.get('baslik')}")
|
|
212
|
+
except:
|
|
213
|
+
pass
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
for dil_kodu, dil_etiketi in [("1", "Altyazı"), ("0", "Dublaj")]:
|
|
217
|
+
altyazi_resp = await self.httpx.post(
|
|
218
|
+
f"{self.main_url}/ajax/dataAlternatif{asp_data['alternatif']}.asp",
|
|
202
219
|
headers = {"X-Requested-With": "XMLHttpRequest"},
|
|
203
|
-
data = {"bid": bid, "dil":
|
|
220
|
+
data = {"bid": bid, "dil": dil_kodu}
|
|
204
221
|
)
|
|
205
|
-
|
|
222
|
+
|
|
206
223
|
try:
|
|
207
|
-
|
|
208
|
-
|
|
224
|
+
data_json = altyazi_resp.json()
|
|
225
|
+
if data_json.get("status") == "success" and data_json.get("data"):
|
|
226
|
+
for veri in data_json["data"]:
|
|
227
|
+
tasks.append(fetch_and_extract(veri, dil_etiketi))
|
|
228
|
+
except:
|
|
209
229
|
continue
|
|
210
230
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
veri_response = await self.httpx.post(
|
|
214
|
-
url = f"{self.main_url}/ajax/dataEmbed{embed_ver}.asp",
|
|
215
|
-
headers = {"X-Requested-With": "XMLHttpRequest"},
|
|
216
|
-
data = {"id": veri.get("id")},
|
|
217
|
-
)
|
|
218
|
-
veri_secici = HTMLParser(veri_response.text)
|
|
219
|
-
|
|
220
|
-
iframe_el = veri_secici.css_first("iframe")
|
|
221
|
-
iframe = iframe_el.attrs.get("src") if iframe_el else None
|
|
222
|
-
|
|
223
|
-
if iframe:
|
|
224
|
-
if "link.asp" in iframe:
|
|
225
|
-
continue
|
|
226
|
-
|
|
227
|
-
iframe_url = self.fix_url(iframe)
|
|
228
|
-
data = await self.extract(iframe_url, prefix=label)
|
|
229
|
-
if data:
|
|
230
|
-
results.append(data)
|
|
231
|
-
|
|
232
|
-
return results
|
|
231
|
+
results = await asyncio.gather(*tasks)
|
|
232
|
+
return [r for r in results if r]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: KekikStream
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.9
|
|
4
4
|
Summary: terminal üzerinden medya içeriği aramanızı ve VLC/MPV gibi popüler medya oynatıcılar aracılığıyla doğrudan izlemenizi sağlayan modüler ve genişletilebilir bir bıdı bıdı
|
|
5
5
|
Home-page: https://github.com/keyiflerolsun/KekikStream
|
|
6
6
|
Author: keyiflerolsun
|
|
@@ -16,12 +16,12 @@ KekikStream/Core/Plugin/PluginLoader.py,sha256=GcDqN1u3nJeoGKH_oDFHCpwteJlLCxHNb
|
|
|
16
16
|
KekikStream/Core/Plugin/PluginManager.py,sha256=CZVg1eegi8vfMfccx0DRV0Box8kXz-aoULTQLgbPbvM,893
|
|
17
17
|
KekikStream/Core/Plugin/PluginModels.py,sha256=Yvx-6Fkn8QCIcuqAkFbCP5EJcq3XBkK_P8S0tRNhS6E,2476
|
|
18
18
|
KekikStream/Core/UI/UIManager.py,sha256=T4V_kdTTWa-UDamgLSKa__dWJuzcvRK9NuwBlzU9Bzc,1693
|
|
19
|
-
KekikStream/Extractors/CloseLoad.py,sha256=
|
|
19
|
+
KekikStream/Extractors/CloseLoad.py,sha256=xlNcUnPfCJJAu1O-YxzjbbA-i14KZ7DADAfTK1biF-s,3197
|
|
20
20
|
KekikStream/Extractors/ContentX.py,sha256=x0j67e1OAw4L1m7ejUTyiIxqr1EhvpjaA_0U-s4IQ-I,3617
|
|
21
21
|
KekikStream/Extractors/DonilasPlay.py,sha256=Lr60pEht96SMlXICYWo9J5dOwV4ty8fetBCCqJ3ARUY,3221
|
|
22
22
|
KekikStream/Extractors/DzenRu.py,sha256=X0Rhm1-W4YjQwVrJs8YFqVcCxMaZi8rsKiLhK_ZsYlU,1185
|
|
23
23
|
KekikStream/Extractors/ExPlay.py,sha256=EJNVKAbaIxlbOsCx7J9aLfNHKOFoqSLZZUw7W4QYeH0,1827
|
|
24
|
-
KekikStream/Extractors/Filemoon.py,sha256=
|
|
24
|
+
KekikStream/Extractors/Filemoon.py,sha256=kkiV9TTox-7t4v3OislRtsUkRa-2xsYgs_qzE-gvVsU,3507
|
|
25
25
|
KekikStream/Extractors/HDPlayerSystem.py,sha256=EgnFzx5Q4PkuwAtuff5SYU9k59B-CyOdySl7lbCZ9hM,1312
|
|
26
26
|
KekikStream/Extractors/JFVid.py,sha256=_6A0zmYrWZxIfkCCKAaNxMRLjU-_0Z0hCxCNSApcknk,1350
|
|
27
27
|
KekikStream/Extractors/JetTv.py,sha256=aA3WeOvR-tszac-WSwunZZb1NRy25TQH8vxY3TDscRI,1596
|
|
@@ -54,29 +54,29 @@ KekikStream/Plugins/BelgeselX.py,sha256=Rr8fxpAOjApBVLH0r6f8xxxWVw8csyYF6HR8UQ_N
|
|
|
54
54
|
KekikStream/Plugins/DiziBox.py,sha256=2RGkYEpKsoznE0Gr822-Cw7n-aF_cjyi7Z-79RdJFao,11754
|
|
55
55
|
KekikStream/Plugins/DiziPal.py,sha256=oo8pA6mfgpZoZV7B6EBwMec4dlUkMlDpaLnvQsxVhg0,11481
|
|
56
56
|
KekikStream/Plugins/DiziYou.py,sha256=FryU5EQR7xcMTkfcc6l-V73kqlbsFVCAPk5ss2zu8gM,9906
|
|
57
|
-
KekikStream/Plugins/Dizilla.py,sha256=
|
|
57
|
+
KekikStream/Plugins/Dizilla.py,sha256=HZ-A-ecFW2zdykNIjpv38n0FYRh1t8D03CrPresMSkk,13914
|
|
58
58
|
KekikStream/Plugins/FilmBip.py,sha256=tTp1gLHC1rVn0vY39jwxpF_fHC5_F0pqcCUCDItgHn0,7103
|
|
59
59
|
KekikStream/Plugins/FilmMakinesi.py,sha256=jdQ1Ger72Wf402h-RpOx1TmvCWD0_gDSafKkAFMUJSg,6571
|
|
60
60
|
KekikStream/Plugins/FilmModu.py,sha256=ZUrBAq1mK2na8YuZEmev64tGhLrql-n-KK1wYDLICn0,7730
|
|
61
61
|
KekikStream/Plugins/FullHDFilm.py,sha256=B8ckb2TftuzfAgxNBs_rkIuAHc9YNVqjG_H9Y3QqGQM,10822
|
|
62
62
|
KekikStream/Plugins/FullHDFilmizlesene.py,sha256=Y6wzW4JnALT91FR_RAmbi1KhM6m7NYlCBh8UGXkKeSs,7900
|
|
63
|
-
KekikStream/Plugins/HDFilmCehennemi.py,sha256=
|
|
63
|
+
KekikStream/Plugins/HDFilmCehennemi.py,sha256=Snwdnt1AhmKN535J4G8US8DeJVNpXZtxxW7hUo7Szp0,13867
|
|
64
64
|
KekikStream/Plugins/JetFilmizle.py,sha256=zqSk1NbOsClViJfETX64jiqREaEDfRskQseIBOzwl-c,8860
|
|
65
65
|
KekikStream/Plugins/KultFilmler.py,sha256=eUWXuo3I_qg3Z8k9uM-Xyy4DLfK1jKeFR2I284MjNks,10240
|
|
66
66
|
KekikStream/Plugins/RecTV.py,sha256=Nj4AdeetPMzvZ-VKUdUGhBC1SiFSBRYRebODgE2UeI8,7228
|
|
67
67
|
KekikStream/Plugins/RoketDizi.py,sha256=Ps6kyBKpSPjMuzFYBm1ryr7gfl_nk8jEj0zQe4SUU0o,9260
|
|
68
68
|
KekikStream/Plugins/SelcukFlix.py,sha256=hsoKh5cZ0xDeJ7MtRUQPX-mWFcrTrsaLy2dDaVwuyKg,14099
|
|
69
|
-
KekikStream/Plugins/SetFilmIzle.py,sha256=
|
|
70
|
-
KekikStream/Plugins/SezonlukDizi.py,sha256=
|
|
69
|
+
KekikStream/Plugins/SetFilmIzle.py,sha256=cCl5dO77GyTAv0IAsvybBZyz74ZAwySxoIJMSOtLg5A,11501
|
|
70
|
+
KekikStream/Plugins/SezonlukDizi.py,sha256=xIc6Ez8Xt3iyKykJDB52MygpotIjrTTLjgMD1HVL85c,10828
|
|
71
71
|
KekikStream/Plugins/SineWix.py,sha256=z0r90lggAugEWE1g9vg8gZsInBObUZPnVFQwq7GYmJs,7052
|
|
72
72
|
KekikStream/Plugins/Sinefy.py,sha256=v75WZ4tcjTuD6jPJ6erUqjwG0Nje0Im9-fknui9rAAM,10843
|
|
73
73
|
KekikStream/Plugins/SinemaCX.py,sha256=6mYz7Yqja_weEfCiLrzMhji1eiSKaYHj0vX4aomDWvs,8628
|
|
74
74
|
KekikStream/Plugins/Sinezy.py,sha256=zBpxUpIIfdnZdolPdkxLMkTsWeGUMW1lht3dNwp_AYU,6756
|
|
75
75
|
KekikStream/Plugins/SuperFilmGeldi.py,sha256=zrTMpAP4NTxhQ4lgprBPXkihE7oQu2jNY7IFA7NWWYA,7144
|
|
76
76
|
KekikStream/Plugins/UgurFilm.py,sha256=S4Zrml9I3W3iW_2feLJWSkvsVZHpQQQlXRJk4E8li-c,5999
|
|
77
|
-
kekikstream-2.2.
|
|
78
|
-
kekikstream-2.2.
|
|
79
|
-
kekikstream-2.2.
|
|
80
|
-
kekikstream-2.2.
|
|
81
|
-
kekikstream-2.2.
|
|
82
|
-
kekikstream-2.2.
|
|
77
|
+
kekikstream-2.2.9.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
78
|
+
kekikstream-2.2.9.dist-info/METADATA,sha256=4p0VW2Lg3Asz8x9KQ2CFCdbsuO6nPfPLy7_yyOMqJr8,10761
|
|
79
|
+
kekikstream-2.2.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
80
|
+
kekikstream-2.2.9.dist-info/entry_points.txt,sha256=dFwdiTx8djyehI0Gsz-rZwjAfZzUzoBSrmzRu9ubjJc,50
|
|
81
|
+
kekikstream-2.2.9.dist-info/top_level.txt,sha256=DNmGJDXl27Drdfobrak8KYLmocW_uznVYFJOzcjUgmY,12
|
|
82
|
+
kekikstream-2.2.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|