KekikStream 2.2.8__tar.gz → 2.3.0__tar.gz
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-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/Filemoon.py +41 -26
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/Dizilla.py +64 -37
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/FilmMakinesi.py +57 -2
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/SetFilmIzle.py +47 -44
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/SezonlukDizi.py +51 -51
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/Sinefy.py +44 -33
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream.egg-info/PKG-INFO +1 -1
- {kekikstream-2.2.8 → kekikstream-2.3.0}/PKG-INFO +1 -1
- {kekikstream-2.2.8 → kekikstream-2.3.0}/setup.py +1 -1
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/CLI/__init__.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/CLI/pypi_kontrol.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/Extractor/ExtractorBase.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/Extractor/ExtractorModels.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/Extractor/YTDLPCache.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/Media/MediaHandler.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/Media/MediaManager.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/Plugin/PluginBase.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/Plugin/PluginManager.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/Plugin/PluginModels.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/UI/UIManager.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Core/__init__.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/CloseLoad.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/ContentX.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/DonilasPlay.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/DzenRu.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/ExPlay.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/HDPlayerSystem.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/JFVid.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/JetTv.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/MailRu.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/MixPlayHD.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/MixTiger.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/MolyStream.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/Odnoklassniki.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/PeaceMakerst.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/PixelDrain.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/PlayerFilmIzle.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/RapidVid.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/SetPlay.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/SetPrime.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/SibNet.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/Sobreatsesuyp.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/TRsTX.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/TauVideo.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/TurboImgz.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/TurkeyPlayer.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/VCTPlay.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/VidHide.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/VidMoly.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/VidMoxy.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/VidPapi.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/VideoSeyred.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/YTDLP.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Extractors/YildizKisaFilm.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/BelgeselX.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/DiziBox.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/DiziPal.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/DiziYou.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/FilmBip.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/FilmModu.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/FullHDFilm.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/FullHDFilmizlesene.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/HDFilmCehennemi.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/JetFilmizle.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/KultFilmler.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/RecTV.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/RoketDizi.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/SelcukFlix.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/SineWix.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/SinemaCX.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/Sinezy.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/SuperFilmGeldi.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/Plugins/UgurFilm.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/__init__.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/__main__.py +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream/requirements.txt +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream.egg-info/SOURCES.txt +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream.egg-info/dependency_links.txt +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream.egg-info/entry_points.txt +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream.egg-info/requires.txt +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/KekikStream.egg-info/top_level.txt +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/LICENSE +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/MANIFEST.in +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/README.md +0 -0
- {kekikstream-2.2.8 → kekikstream-2.3.0}/setup.cfg +0 -0
|
@@ -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
|
)
|
|
@@ -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 []
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult
|
|
4
4
|
from selectolax.parser import HTMLParser
|
|
5
|
+
import re
|
|
5
6
|
|
|
6
7
|
class FilmMakinesi(PluginBase):
|
|
7
8
|
name = "FilmMakinesi"
|
|
@@ -81,7 +82,7 @@ class FilmMakinesi(PluginBase):
|
|
|
81
82
|
|
|
82
83
|
return results
|
|
83
84
|
|
|
84
|
-
async def load_item(self, url: str) -> MovieInfo:
|
|
85
|
+
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
85
86
|
istek = await self.httpx.get(url)
|
|
86
87
|
secici = HTMLParser(istek.text)
|
|
87
88
|
|
|
@@ -115,6 +116,60 @@ class FilmMakinesi(PluginBase):
|
|
|
115
116
|
if len(parts) > 1:
|
|
116
117
|
duration = parts[1].strip()
|
|
117
118
|
|
|
119
|
+
# Dizi mi kontrol et - sezon/bölüm linkleri var mı?
|
|
120
|
+
episodes = []
|
|
121
|
+
all_links = secici.css("a[href]")
|
|
122
|
+
for link in all_links:
|
|
123
|
+
href = link.attrs.get("href", "")
|
|
124
|
+
match = re.search(r"/sezon-(\d+)/bolum-(\d+)", href)
|
|
125
|
+
if match:
|
|
126
|
+
season_no = int(match.group(1))
|
|
127
|
+
ep_no = int(match.group(2))
|
|
128
|
+
|
|
129
|
+
# Bölüm başlığını çıkar - text'ten gerçek ismi al
|
|
130
|
+
# Format: "22 Eylül 2014 / 44 dk /1. Sezon / 1. BölümPilot"
|
|
131
|
+
full_text = link.text(strip=True)
|
|
132
|
+
# "Bölüm" kelimesinden sonraki kısmı al
|
|
133
|
+
ep_title = ""
|
|
134
|
+
if "Bölüm" in full_text:
|
|
135
|
+
parts = full_text.split("Bölüm")
|
|
136
|
+
if len(parts) > 1:
|
|
137
|
+
ep_title = parts[-1].strip()
|
|
138
|
+
|
|
139
|
+
episodes.append(Episode(
|
|
140
|
+
season = season_no,
|
|
141
|
+
episode = ep_no,
|
|
142
|
+
title = ep_title,
|
|
143
|
+
url = self.fix_url(href)
|
|
144
|
+
))
|
|
145
|
+
|
|
146
|
+
# Bölümler varsa SeriesInfo döndür
|
|
147
|
+
if episodes:
|
|
148
|
+
# Tekrar eden bölümleri kaldır
|
|
149
|
+
seen = set()
|
|
150
|
+
unique_episodes = []
|
|
151
|
+
for ep in episodes:
|
|
152
|
+
key = (ep.season, ep.episode)
|
|
153
|
+
if key not in seen:
|
|
154
|
+
seen.add(key)
|
|
155
|
+
unique_episodes.append(ep)
|
|
156
|
+
|
|
157
|
+
# Sırala
|
|
158
|
+
unique_episodes.sort(key=lambda x: (x.season or 0, x.episode or 0))
|
|
159
|
+
|
|
160
|
+
return SeriesInfo(
|
|
161
|
+
url = url,
|
|
162
|
+
poster = self.fix_url(poster) if poster else None,
|
|
163
|
+
title = self.clean_title(title),
|
|
164
|
+
description = description,
|
|
165
|
+
tags = tags,
|
|
166
|
+
rating = rating,
|
|
167
|
+
year = year,
|
|
168
|
+
actors = actors,
|
|
169
|
+
duration = duration,
|
|
170
|
+
episodes = unique_episodes
|
|
171
|
+
)
|
|
172
|
+
|
|
118
173
|
return MovieInfo(
|
|
119
174
|
url = url,
|
|
120
175
|
poster = self.fix_url(poster) if poster else None,
|
|
@@ -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]
|
|
@@ -175,49 +175,60 @@ class Sinefy(PluginBase):
|
|
|
175
175
|
year = year_el.text(strip=True) if year_el else None
|
|
176
176
|
|
|
177
177
|
episodes = []
|
|
178
|
-
|
|
178
|
+
episodes_box = sel.css_first("section.episodes-box")
|
|
179
179
|
|
|
180
|
-
if
|
|
181
|
-
#
|
|
182
|
-
|
|
183
|
-
menu = sel.css("div.ui.vertical.fluid.tabular.menu a")
|
|
184
|
-
for link in menu:
|
|
185
|
-
href = link.attrs.get("href")
|
|
186
|
-
if href:
|
|
187
|
-
season_links.append(self.fix_url(href))
|
|
180
|
+
if episodes_box:
|
|
181
|
+
# Sezon menüsünden sezon linklerini al
|
|
182
|
+
season_menu = episodes_box.css("div.ui.vertical.fluid.tabular.menu a.item")
|
|
188
183
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
184
|
+
# Sezon tab içeriklerini al
|
|
185
|
+
season_tabs = episodes_box.css("div.ui.tab")
|
|
186
|
+
|
|
187
|
+
# Eğer birden fazla sezon varsa, her sezon tab'ından bölümleri çek
|
|
188
|
+
if season_tabs:
|
|
189
|
+
for idx, season_tab in enumerate(season_tabs):
|
|
190
|
+
# Sezon numarasını belirle
|
|
191
|
+
current_season_no = idx + 1
|
|
192
|
+
|
|
193
|
+
# Menüden sezon numarasını almaya çalış
|
|
194
|
+
if idx < len(season_menu):
|
|
195
|
+
menu_href = season_menu[idx].attrs.get("href", "")
|
|
196
|
+
match = re.search(r"sezon-(\d+)", menu_href)
|
|
197
|
+
if match:
|
|
198
|
+
current_season_no = int(match.group(1))
|
|
196
199
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if match:
|
|
200
|
-
current_season_no = int(match.group(1))
|
|
200
|
+
# Bu sezon tab'ından bölüm linklerini çek
|
|
201
|
+
ep_links = season_tab.css("a[href*='bolum']")
|
|
201
202
|
|
|
203
|
+
seen_urls = set()
|
|
202
204
|
for ep_link in ep_links:
|
|
203
205
|
href = ep_link.attrs.get("href")
|
|
204
|
-
|
|
205
|
-
|
|
206
|
+
if not href or href in seen_urls:
|
|
207
|
+
continue
|
|
208
|
+
seen_urls.add(href)
|
|
206
209
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
210
|
+
# Bölüm numarasını URL'den çıkar
|
|
211
|
+
ep_no = 0
|
|
212
|
+
match_ep = re.search(r"bolum-(\d+)", href)
|
|
213
|
+
if match_ep:
|
|
214
|
+
ep_no = int(match_ep.group(1))
|
|
215
|
+
|
|
216
|
+
# Bölüm başlığını çıkar (önce title attribute, sonra text)
|
|
217
|
+
name = ep_link.attrs.get("title", "")
|
|
218
|
+
if not name:
|
|
219
|
+
name_el = ep_link.css_first("div.content div.header")
|
|
220
|
+
if name_el:
|
|
221
|
+
name = name_el.text(strip=True)
|
|
222
|
+
else:
|
|
223
|
+
name = ep_link.text(strip=True)
|
|
224
|
+
|
|
225
|
+
if href and ep_no > 0:
|
|
213
226
|
episodes.append(Episode(
|
|
214
|
-
season
|
|
227
|
+
season = current_season_no,
|
|
215
228
|
episode = ep_no,
|
|
216
|
-
title
|
|
217
|
-
url
|
|
229
|
+
title = name.strip() if name else f"{ep_no}. Bölüm",
|
|
230
|
+
url = self.fix_url(href)
|
|
218
231
|
))
|
|
219
|
-
except Exception:
|
|
220
|
-
pass
|
|
221
232
|
|
|
222
233
|
if episodes:
|
|
223
234
|
return SeriesInfo(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: KekikStream
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: KekikStream
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
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
|
|
@@ -6,7 +6,7 @@ from io import open
|
|
|
6
6
|
setup(
|
|
7
7
|
# ? Genel Bilgiler
|
|
8
8
|
name = "KekikStream",
|
|
9
|
-
version = "2.
|
|
9
|
+
version = "2.3.0",
|
|
10
10
|
url = "https://github.com/keyiflerolsun/KekikStream",
|
|
11
11
|
description = "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ı",
|
|
12
12
|
keywords = ["KekikStream", "KekikAkademi", "keyiflerolsun"],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|