KekikStream 1.9.9__py3-none-any.whl → 2.0.8__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 +5 -5
- KekikStream/Core/Extractor/ExtractorLoader.py +25 -17
- KekikStream/Core/Extractor/ExtractorManager.py +1 -1
- KekikStream/Core/Extractor/YTDLPCache.py +35 -0
- KekikStream/Core/Plugin/PluginBase.py +4 -1
- KekikStream/Core/Plugin/PluginLoader.py +11 -7
- KekikStream/Core/__init__.py +1 -0
- KekikStream/Extractors/CloseLoad.py +1 -1
- KekikStream/Extractors/ContentX.py +13 -0
- KekikStream/Extractors/DonilasPlay.py +86 -0
- KekikStream/Extractors/Odnoklassniki.py +6 -0
- KekikStream/Extractors/PeaceMakerst.py +6 -0
- KekikStream/Extractors/RapidVid.py +6 -0
- KekikStream/Extractors/SetPlay.py +7 -1
- KekikStream/Extractors/VCTPlay.py +41 -0
- KekikStream/Extractors/VidMoly.py +52 -30
- KekikStream/Extractors/YTDLP.py +97 -58
- KekikStream/Plugins/BelgeselX.py +204 -0
- KekikStream/Plugins/Dizilla.py +22 -14
- KekikStream/Plugins/FilmMakinesi.py +3 -1
- KekikStream/Plugins/FilmModu.py +6 -2
- KekikStream/Plugins/FullHDFilmizlesene.py +1 -1
- KekikStream/Plugins/HDFilmCehennemi.py +83 -8
- KekikStream/Plugins/JetFilmizle.py +53 -38
- KekikStream/Plugins/KultFilmler.py +1 -1
- KekikStream/Plugins/RoketDizi.py +17 -24
- KekikStream/Plugins/SelcukFlix.py +51 -52
- KekikStream/Plugins/SetFilmIzle.py +259 -0
- KekikStream/Plugins/SezonlukDizi.py +28 -7
- KekikStream/Plugins/Sinefy.py +15 -9
- KekikStream/Plugins/SinemaCX.py +3 -7
- KekikStream/Plugins/Sinezy.py +16 -1
- KekikStream/Plugins/SuperFilmGeldi.py +1 -1
- KekikStream/Plugins/UgurFilm.py +1 -1
- {kekikstream-1.9.9.dist-info → kekikstream-2.0.8.dist-info}/METADATA +27 -1
- kekikstream-2.0.8.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.9.9.dist-info/RECORD +0 -86
- {kekikstream-1.9.9.dist-info → kekikstream-2.0.8.dist-info}/WHEEL +0 -0
- {kekikstream-1.9.9.dist-info → kekikstream-2.0.8.dist-info}/entry_points.txt +0 -0
- {kekikstream-1.9.9.dist-info → kekikstream-2.0.8.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-1.9.9.dist-info → kekikstream-2.0.8.dist-info}/top_level.txt +0 -0
|
@@ -8,7 +8,7 @@ class JetFilmizle(PluginBase):
|
|
|
8
8
|
language = "tr"
|
|
9
9
|
main_url = "https://jetfilmizle.website"
|
|
10
10
|
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
11
|
-
description = "
|
|
11
|
+
description = "Film izle, Yerli, Yabancı film izle, Türkçe dublaj, alt yazılı seçenekleriyle ödül almış filmleri Full HD kalitesiyle ve jetfilmizle hızıyla donmadan ücretsizce izleyebilirsiniz."
|
|
12
12
|
|
|
13
13
|
main_page = {
|
|
14
14
|
f"{main_url}/page/" : "Son Filmler",
|
|
@@ -74,8 +74,15 @@ class JetFilmizle(PluginBase):
|
|
|
74
74
|
rating_raw = secici.css("section.movie-exp div.imdb_puan span::text").get()
|
|
75
75
|
rating = rating_raw.strip() if rating_raw else None
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
|
|
78
|
+
# Year - div.yap içinde 4 haneli sayı ara
|
|
79
|
+
year_div = secici.xpath("//div[@class='yap' and (contains(., 'Vizyon') or contains(., 'Yapım'))]/text()").get()
|
|
80
|
+
year = None
|
|
81
|
+
if year_div:
|
|
82
|
+
year_match = re.search(r'(\d{4})', year_div.strip())
|
|
83
|
+
if year_match:
|
|
84
|
+
year = year_match.group(1)
|
|
85
|
+
|
|
79
86
|
actors = secici.css("div[itemprop='actor'] a span::text").getall()
|
|
80
87
|
|
|
81
88
|
return MovieInfo(
|
|
@@ -93,42 +100,50 @@ class JetFilmizle(PluginBase):
|
|
|
93
100
|
istek = await self.httpx.get(url)
|
|
94
101
|
secici = Selector(istek.text)
|
|
95
102
|
|
|
96
|
-
|
|
97
|
-
if main_iframe := secici.css("div#movie iframe::attr(data-src), div#movie iframe::attr(data), div#movie iframe::attr(src)").get():
|
|
98
|
-
iframes.append(self.fix_url(main_iframe))
|
|
103
|
+
results = []
|
|
99
104
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
# 1) Ana iframe'leri kontrol et
|
|
106
|
+
for iframe in secici.css("iframe"):
|
|
107
|
+
src = (iframe.css("::attr(src)").get() or
|
|
108
|
+
iframe.css("::attr(data-src)").get() or
|
|
109
|
+
iframe.css("::attr(data-lazy-src)").get())
|
|
110
|
+
|
|
111
|
+
if src and src != "about:blank":
|
|
112
|
+
iframe_url = self.fix_url(src)
|
|
113
|
+
extractor = self.ex_manager.find_extractor(iframe_url)
|
|
114
|
+
results.append({
|
|
115
|
+
"url": iframe_url,
|
|
116
|
+
"name": extractor.name if extractor else "Ana Player"
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
# 2) Sayfa numaralarından linkleri topla (Fragman hariç)
|
|
120
|
+
page_links = []
|
|
121
|
+
for link in secici.css("a.post-page-numbers"):
|
|
122
|
+
isim = link.css("span::text").get() or ""
|
|
123
|
+
if isim != "Fragman":
|
|
124
|
+
href = link.css("::attr(href)").get()
|
|
125
|
+
if href:
|
|
126
|
+
page_links.append((self.fix_url(href), isim))
|
|
127
|
+
|
|
128
|
+
# 3) Her sayfa linkindeki iframe'leri bul
|
|
129
|
+
for page_url, isim in page_links:
|
|
130
|
+
try:
|
|
131
|
+
page_resp = await self.httpx.get(page_url)
|
|
132
|
+
page_sel = Selector(page_resp.text)
|
|
133
|
+
|
|
134
|
+
for iframe in page_sel.css("div#movie iframe"):
|
|
135
|
+
src = (iframe.css("::attr(src)").get() or
|
|
136
|
+
iframe.css("::attr(data-src)").get() or
|
|
137
|
+
iframe.css("::attr(data-lazy-src)").get())
|
|
138
|
+
|
|
139
|
+
if src and src != "about:blank":
|
|
140
|
+
iframe_url = self.fix_url(src)
|
|
141
|
+
extractor = self.ex_manager.find_extractor(iframe_url)
|
|
142
|
+
results.append({
|
|
143
|
+
"url": iframe_url,
|
|
144
|
+
"name": f"{extractor.name if extractor else 'Player'} | {isim}"
|
|
145
|
+
})
|
|
146
|
+
except Exception:
|
|
103
147
|
continue
|
|
104
148
|
|
|
105
|
-
part_istek = await self.httpx.get(part_href)
|
|
106
|
-
part_secici = Selector(part_istek.text)
|
|
107
|
-
|
|
108
|
-
if iframe := part_secici.css("div#movie iframe::attr(data-src), div#movie iframe::attr(data), div#movie iframe::attr(src)").get():
|
|
109
|
-
iframes.append(self.fix_url(iframe))
|
|
110
|
-
else:
|
|
111
|
-
for link in part_secici.css("div#movie p a"):
|
|
112
|
-
if download_link := link.attrib.get("href"):
|
|
113
|
-
iframes.append(self.fix_url(download_link))
|
|
114
|
-
|
|
115
|
-
processed_iframes = []
|
|
116
|
-
for iframe in iframes:
|
|
117
|
-
if "jetv.xyz" in iframe:
|
|
118
|
-
jetv_istek = await self.httpx.get(iframe)
|
|
119
|
-
jetv_secici = Selector(jetv_istek.text)
|
|
120
|
-
|
|
121
|
-
if jetv_iframe := jetv_secici.css("iframe::attr(src)").get():
|
|
122
|
-
processed_iframes.append(self.fix_url(jetv_iframe))
|
|
123
|
-
else:
|
|
124
|
-
processed_iframes.append(iframe)
|
|
125
|
-
|
|
126
|
-
results = []
|
|
127
|
-
for idx, iframe in enumerate(processed_iframes):
|
|
128
|
-
extractor = self.ex_manager.find_extractor(iframe)
|
|
129
|
-
results.append({
|
|
130
|
-
"url" : iframe,
|
|
131
|
-
"name" : f"{extractor.name if extractor else f'Player {idx + 1}'}"
|
|
132
|
-
})
|
|
133
|
-
|
|
134
149
|
return results
|
|
@@ -9,7 +9,7 @@ class KultFilmler(PluginBase):
|
|
|
9
9
|
language = "tr"
|
|
10
10
|
main_url = "https://kultfilmler.net"
|
|
11
11
|
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
12
|
-
description = "Kült
|
|
12
|
+
description = "Kült Filmler özenle en iyi filmleri derler ve iyi bir altyazılı film izleme deneyimi sunmayı amaçlar. Reklamsız 1080P Altyazılı Film izle..."
|
|
13
13
|
|
|
14
14
|
main_page = {
|
|
15
15
|
f"{main_url}/category/aile-filmleri-izle" : "Aile",
|
KekikStream/Plugins/RoketDizi.py
CHANGED
|
@@ -185,6 +185,7 @@ class RoketDizi(PluginBase):
|
|
|
185
185
|
# secureData içindeki RelatedResults -> getEpisodeSources -> result dizisini al
|
|
186
186
|
sources = decoded_json.get("RelatedResults", {}).get("getEpisodeSources", {}).get("result", [])
|
|
187
187
|
|
|
188
|
+
seen_urls = set()
|
|
188
189
|
results = []
|
|
189
190
|
for source in sources:
|
|
190
191
|
source_content = source.get("source_content", "")
|
|
@@ -195,35 +196,27 @@ class RoketDizi(PluginBase):
|
|
|
195
196
|
continue
|
|
196
197
|
|
|
197
198
|
iframe_url = iframe_match.group(1)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
199
|
+
|
|
200
|
+
# Fix URL protocol
|
|
201
|
+
if not iframe_url.startswith("http"):
|
|
202
|
+
if iframe_url.startswith("//"):
|
|
203
|
+
iframe_url = "https:" + iframe_url
|
|
204
|
+
else:
|
|
205
|
+
iframe_url = "https://" + iframe_url
|
|
206
|
+
|
|
207
|
+
iframe_url = self.fix_url(iframe_url)
|
|
208
|
+
|
|
209
|
+
# Deduplicate
|
|
210
|
+
if iframe_url in seen_urls:
|
|
211
|
+
continue
|
|
212
|
+
seen_urls.add(iframe_url)
|
|
203
213
|
|
|
204
214
|
# Check extractor
|
|
205
215
|
extractor = self.ex_manager.find_extractor(iframe_url)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
# Metadata'dan bilgileri al
|
|
209
|
-
source_name = source.get("source_name", "")
|
|
210
|
-
language_name = source.get("language_name", "")
|
|
211
|
-
quality_name = source.get("quality_name", "")
|
|
212
|
-
|
|
213
|
-
# İsmi oluştur
|
|
214
|
-
name_parts = []
|
|
215
|
-
if source_name:
|
|
216
|
-
name_parts.append(source_name)
|
|
217
|
-
if ext_name:
|
|
218
|
-
name_parts.append(ext_name)
|
|
219
|
-
if language_name:
|
|
220
|
-
name_parts.append(language_name)
|
|
221
|
-
if quality_name:
|
|
222
|
-
name_parts.append(quality_name)
|
|
223
|
-
|
|
216
|
+
|
|
224
217
|
results.append({
|
|
225
218
|
"url" : iframe_url,
|
|
226
|
-
"name" :
|
|
219
|
+
"name" : extractor.name if extractor else "Player"
|
|
227
220
|
})
|
|
228
221
|
|
|
229
222
|
return results
|
|
@@ -9,7 +9,7 @@ class SelcukFlix(PluginBase):
|
|
|
9
9
|
lang = "tr"
|
|
10
10
|
main_url = "https://selcukflix.net"
|
|
11
11
|
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
12
|
-
description = "Selcukflix
|
|
12
|
+
description = "Selcukflix'te her türden en yeni ve en popüler dizi ve filmleri izlemenin keyfini çıkarın. Aksiyondan romantiğe, bilim kurgudan dramaya, geniş kütüphanemizde herkes için bir şey var."
|
|
13
13
|
|
|
14
14
|
main_page = {
|
|
15
15
|
f"{main_url}/tum-bolumler" : "Yeni Eklenen Bölümler",
|
|
@@ -240,60 +240,59 @@ class SelcukFlix(PluginBase):
|
|
|
240
240
|
sel = Selector(resp.text)
|
|
241
241
|
|
|
242
242
|
next_data = sel.css("script#__NEXT_DATA__::text").get()
|
|
243
|
-
if not next_data:
|
|
243
|
+
if not next_data:
|
|
244
|
+
return []
|
|
244
245
|
|
|
245
246
|
try:
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
247
|
+
data = json.loads(next_data)
|
|
248
|
+
secure_data = data["props"]["pageProps"]["secureData"]
|
|
249
|
+
raw_data = base64.b64decode(secure_data.replace('"', ''))
|
|
250
|
+
|
|
251
|
+
# Try UTF-8 first, fallback to ISO-8859-1 (matching Kotlin)
|
|
252
|
+
try:
|
|
253
|
+
decoded_str = raw_data.decode('utf-8')
|
|
254
|
+
except UnicodeDecodeError:
|
|
255
|
+
decoded_str = raw_data.decode('iso-8859-1')
|
|
253
256
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
"name": extractor.name if extractor else "Iframe"
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
return results
|
|
257
|
+
content_details = json.loads(decoded_str)
|
|
258
|
+
related_data = content_details.get("relatedData", {})
|
|
259
|
+
|
|
260
|
+
source_content = None
|
|
261
|
+
|
|
262
|
+
# Check if Series (episode) or Movie
|
|
263
|
+
if "/dizi/" in url:
|
|
264
|
+
if related_data.get("episodeSources", {}).get("state"):
|
|
265
|
+
res = related_data["episodeSources"].get("result", [])
|
|
266
|
+
if res:
|
|
267
|
+
source_content = res[0].get("sourceContent")
|
|
268
|
+
else:
|
|
269
|
+
# Movie
|
|
270
|
+
if related_data.get("movieParts", {}).get("state"):
|
|
271
|
+
movie_parts = related_data["movieParts"].get("result", [])
|
|
272
|
+
if movie_parts:
|
|
273
|
+
first_part_id = movie_parts[0].get("id")
|
|
274
|
+
# RelatedResults -> getMoviePartSourcesById_ID
|
|
275
|
+
rr = content_details.get("RelatedResults", {})
|
|
276
|
+
key = f"getMoviePartSourcesById_{first_part_id}"
|
|
277
|
+
if key in rr:
|
|
278
|
+
res = rr[key].get("result", [])
|
|
279
|
+
if res:
|
|
280
|
+
source_content = res[0].get("source_content")
|
|
281
|
+
|
|
282
|
+
if source_content:
|
|
283
|
+
iframe_sel = Selector(source_content)
|
|
284
|
+
iframe_src = iframe_sel.css("iframe::attr(src)").get()
|
|
285
|
+
if iframe_src:
|
|
286
|
+
iframe_src = self.fix_url(iframe_src)
|
|
287
|
+
extractor = self.ex_manager.find_extractor(iframe_src)
|
|
288
|
+
|
|
289
|
+
if extractor: # Only return if extractor found
|
|
290
|
+
return [{
|
|
291
|
+
"url" : iframe_src,
|
|
292
|
+
"name" : extractor.name
|
|
293
|
+
}]
|
|
294
|
+
|
|
295
|
+
return []
|
|
297
296
|
|
|
298
297
|
except Exception:
|
|
299
298
|
return []
|
|
@@ -0,0 +1,259 @@
|
|
|
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
|
|
4
|
+
from parsel import Selector
|
|
5
|
+
import re, json
|
|
6
|
+
|
|
7
|
+
class SetFilmIzle(PluginBase):
|
|
8
|
+
name = "SetFilmIzle"
|
|
9
|
+
language = "tr"
|
|
10
|
+
main_url = "https://www.setfilmizle.uk"
|
|
11
|
+
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
12
|
+
description = "Setfilmizle sitemizde, donma yaşamadan Türkçe dublaj ve altyazılı filmleri ile dizileri muhteşem 1080p full HD kalitesinde izleyebilirsiniz."
|
|
13
|
+
|
|
14
|
+
main_page = {
|
|
15
|
+
f"{main_url}/tur/aile/" : "Aile",
|
|
16
|
+
f"{main_url}/tur/aksiyon/" : "Aksiyon",
|
|
17
|
+
f"{main_url}/tur/animasyon/" : "Animasyon",
|
|
18
|
+
f"{main_url}/tur/belgesel/" : "Belgesel",
|
|
19
|
+
f"{main_url}/tur/bilim-kurgu/" : "Bilim-Kurgu",
|
|
20
|
+
f"{main_url}/tur/biyografi/" : "Biyografi",
|
|
21
|
+
f"{main_url}/tur/dini/" : "Dini",
|
|
22
|
+
f"{main_url}/tur/dram/" : "Dram",
|
|
23
|
+
f"{main_url}/tur/fantastik/" : "Fantastik",
|
|
24
|
+
f"{main_url}/tur/genclik/" : "Gençlik",
|
|
25
|
+
f"{main_url}/tur/gerilim/" : "Gerilim",
|
|
26
|
+
f"{main_url}/tur/gizem/" : "Gizem",
|
|
27
|
+
f"{main_url}/tur/komedi/" : "Komedi",
|
|
28
|
+
f"{main_url}/tur/korku/" : "Korku",
|
|
29
|
+
f"{main_url}/tur/macera/" : "Macera",
|
|
30
|
+
f"{main_url}/tur/romantik/" : "Romantik",
|
|
31
|
+
f"{main_url}/tur/savas/" : "Savaş",
|
|
32
|
+
f"{main_url}/tur/suc/" : "Suç",
|
|
33
|
+
f"{main_url}/tur/tarih/" : "Tarih",
|
|
34
|
+
f"{main_url}/tur/western/" : "Western"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
38
|
+
istek = self.cloudscraper.get(url)
|
|
39
|
+
secici = Selector(istek.text)
|
|
40
|
+
|
|
41
|
+
results = []
|
|
42
|
+
for item in secici.css("div.items article"):
|
|
43
|
+
title = item.css("h2::text").get()
|
|
44
|
+
href = item.css("a::attr(href)").get()
|
|
45
|
+
poster = item.css("img::attr(data-src)").get()
|
|
46
|
+
|
|
47
|
+
if title and href:
|
|
48
|
+
results.append(MainPageResult(
|
|
49
|
+
category = category,
|
|
50
|
+
title = title.strip(),
|
|
51
|
+
url = self.fix_url(href),
|
|
52
|
+
poster = self.fix_url(poster) if poster else None
|
|
53
|
+
))
|
|
54
|
+
|
|
55
|
+
return results
|
|
56
|
+
|
|
57
|
+
async def search(self, query: str) -> list[SearchResult]:
|
|
58
|
+
# Ana sayfadan nonce değerini al
|
|
59
|
+
main_resp = self.cloudscraper.get(self.main_url)
|
|
60
|
+
|
|
61
|
+
# Birden fazla nonce pattern dene
|
|
62
|
+
nonce = ""
|
|
63
|
+
nonce_patterns = [
|
|
64
|
+
r'nonces:\s*\{\s*search:\s*"([^"]+)"', # STMOVIE_AJAX.nonces.search
|
|
65
|
+
r'"search":\s*"([a-zA-Z0-9]+)"', # JSON format
|
|
66
|
+
r"nonce:\s*'([^']*)'",
|
|
67
|
+
r'"nonce":"([^"]+)"',
|
|
68
|
+
r'data-nonce="([^"]+)"',
|
|
69
|
+
]
|
|
70
|
+
for pattern in nonce_patterns:
|
|
71
|
+
match = re.search(pattern, main_resp.text)
|
|
72
|
+
if match:
|
|
73
|
+
nonce = match.group(1)
|
|
74
|
+
break
|
|
75
|
+
|
|
76
|
+
search_resp = self.cloudscraper.post(
|
|
77
|
+
f"{self.main_url}/wp-admin/admin-ajax.php",
|
|
78
|
+
headers = {
|
|
79
|
+
"X-Requested-With" : "XMLHttpRequest",
|
|
80
|
+
"Content-Type" : "application/x-www-form-urlencoded",
|
|
81
|
+
"Referer" : f"{self.main_url}/"
|
|
82
|
+
},
|
|
83
|
+
data = {
|
|
84
|
+
"action" : "ajax_search",
|
|
85
|
+
"search" : query,
|
|
86
|
+
"original_search" : query,
|
|
87
|
+
"nonce" : nonce
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
data = search_resp.json()
|
|
93
|
+
html = data.get("html", "")
|
|
94
|
+
except:
|
|
95
|
+
return []
|
|
96
|
+
|
|
97
|
+
secici = Selector(text=html)
|
|
98
|
+
results = []
|
|
99
|
+
|
|
100
|
+
for item in secici.css("div.items article"):
|
|
101
|
+
title = item.css("h2::text").get()
|
|
102
|
+
href = item.css("a::attr(href)").get()
|
|
103
|
+
poster = item.css("img::attr(data-src)").get()
|
|
104
|
+
|
|
105
|
+
if title and href:
|
|
106
|
+
results.append(SearchResult(
|
|
107
|
+
title = title.strip(),
|
|
108
|
+
url = self.fix_url(href),
|
|
109
|
+
poster = self.fix_url(poster) if poster else None
|
|
110
|
+
))
|
|
111
|
+
|
|
112
|
+
return results
|
|
113
|
+
|
|
114
|
+
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
115
|
+
istek = await self.httpx.get(url)
|
|
116
|
+
secici = Selector(istek.text)
|
|
117
|
+
|
|
118
|
+
raw_title = secici.css("h1::text").get() or ""
|
|
119
|
+
title = re.sub(r"\s*izle.*$", "", raw_title, flags=re.IGNORECASE).strip()
|
|
120
|
+
poster = secici.css("div.poster img::attr(src)").get()
|
|
121
|
+
description = secici.css("div.wp-content p::text").get()
|
|
122
|
+
year = secici.css("div.extra span.C a::text").get()
|
|
123
|
+
if year:
|
|
124
|
+
year_match = re.search(r"\d{4}", year)
|
|
125
|
+
year = year_match.group() if year_match else None
|
|
126
|
+
tags = [a.css("::text").get().strip() for a in secici.css("div.sgeneros a") if a.css("::text").get()]
|
|
127
|
+
duration = secici.css("span.runtime::text").get()
|
|
128
|
+
if duration:
|
|
129
|
+
dur_match = re.search(r"\d+", duration)
|
|
130
|
+
duration = int(dur_match.group()) if dur_match else None
|
|
131
|
+
|
|
132
|
+
actors = [span.css("::text").get().strip() for span in secici.css("span.valor a > span") if span.css("::text").get()]
|
|
133
|
+
|
|
134
|
+
trailer_match = re.search(r'embed/([^?]*)\?rel', istek.text)
|
|
135
|
+
trailer = f"https://www.youtube.com/embed/{trailer_match.group(1)}" if trailer_match else None
|
|
136
|
+
|
|
137
|
+
# Dizi mi film mi kontrol et
|
|
138
|
+
is_series = "/dizi/" in url
|
|
139
|
+
|
|
140
|
+
if is_series:
|
|
141
|
+
year_elem = secici.css("a[href*='/yil/']::text").get()
|
|
142
|
+
if year_elem:
|
|
143
|
+
year_match = re.search(r"\d{4}", year_elem)
|
|
144
|
+
year = year_match.group() if year_match else year
|
|
145
|
+
|
|
146
|
+
dur_elem = secici.css("div#info span:contains('Dakika')::text").get()
|
|
147
|
+
if dur_elem:
|
|
148
|
+
dur_match = re.search(r"\d+", dur_elem)
|
|
149
|
+
duration = int(dur_match.group()) if dur_match else duration
|
|
150
|
+
|
|
151
|
+
episodes = []
|
|
152
|
+
for ep_item in secici.css("div#episodes ul.episodios li"):
|
|
153
|
+
ep_href = ep_item.css("h4.episodiotitle a::attr(href)").get()
|
|
154
|
+
ep_name = ep_item.css("h4.episodiotitle a::text").get()
|
|
155
|
+
|
|
156
|
+
if not ep_href or not ep_name:
|
|
157
|
+
continue
|
|
158
|
+
|
|
159
|
+
ep_detail = ep_name.strip()
|
|
160
|
+
season_match = re.search(r"(\d+)\.\s*Sezon", ep_detail)
|
|
161
|
+
episode_match = re.search(r"Sezon\s+(\d+)\.\s*Bölüm", ep_detail)
|
|
162
|
+
|
|
163
|
+
ep_season = int(season_match.group(1)) if season_match else 1
|
|
164
|
+
ep_episode = int(episode_match.group(1)) if episode_match else None
|
|
165
|
+
|
|
166
|
+
episodes.append(Episode(
|
|
167
|
+
season = ep_season,
|
|
168
|
+
episode = ep_episode,
|
|
169
|
+
title = ep_name.strip(),
|
|
170
|
+
url = self.fix_url(ep_href)
|
|
171
|
+
))
|
|
172
|
+
|
|
173
|
+
return SeriesInfo(
|
|
174
|
+
url = url,
|
|
175
|
+
poster = self.fix_url(poster) if poster else None,
|
|
176
|
+
title = title,
|
|
177
|
+
description = description.strip() if description else None,
|
|
178
|
+
tags = tags,
|
|
179
|
+
year = year,
|
|
180
|
+
duration = duration,
|
|
181
|
+
actors = actors,
|
|
182
|
+
episodes = episodes
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
return MovieInfo(
|
|
186
|
+
url = url,
|
|
187
|
+
poster = self.fix_url(poster) if poster else None,
|
|
188
|
+
title = title,
|
|
189
|
+
description = description.strip() if description else None,
|
|
190
|
+
tags = tags,
|
|
191
|
+
year = year,
|
|
192
|
+
duration = duration,
|
|
193
|
+
actors = actors
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
async def load_links(self, url: str) -> list[dict]:
|
|
197
|
+
istek = await self.httpx.get(url)
|
|
198
|
+
secici = Selector(istek.text)
|
|
199
|
+
|
|
200
|
+
nonce = secici.css("div#playex::attr(data-nonce)").get() or ""
|
|
201
|
+
|
|
202
|
+
# partKey to dil label mapping
|
|
203
|
+
part_key_labels = {
|
|
204
|
+
"turkcedublaj" : "Türkçe Dublaj",
|
|
205
|
+
"turkcealtyazi" : "Türkçe Altyazı",
|
|
206
|
+
"orijinal" : "Orijinal"
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
links = []
|
|
210
|
+
for player in secici.css("nav.player a"):
|
|
211
|
+
source_id = player.css("::attr(data-post-id)").get()
|
|
212
|
+
player_name = player.css("::attr(data-player-name)").get()
|
|
213
|
+
part_key = player.css("::attr(data-part-key)").get()
|
|
214
|
+
|
|
215
|
+
if not source_id or "event" in source_id or source_id == "":
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
# Multipart form request
|
|
219
|
+
try:
|
|
220
|
+
resp = self.cloudscraper.post(
|
|
221
|
+
f"{self.main_url}/wp-admin/admin-ajax.php",
|
|
222
|
+
headers = {"Referer": url},
|
|
223
|
+
data = {
|
|
224
|
+
"action" : "get_video_url",
|
|
225
|
+
"nonce" : nonce,
|
|
226
|
+
"post_id" : source_id,
|
|
227
|
+
"player_name" : player_name or "",
|
|
228
|
+
"part_key" : part_key or ""
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
data = resp.json()
|
|
232
|
+
except:
|
|
233
|
+
continue
|
|
234
|
+
|
|
235
|
+
iframe_url = data.get("data", {}).get("url")
|
|
236
|
+
if not iframe_url:
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
# SetPlay URL'si için part_key ekleme
|
|
240
|
+
if "setplay" not in iframe_url and part_key:
|
|
241
|
+
iframe_url = f"{iframe_url}?partKey={part_key}"
|
|
242
|
+
|
|
243
|
+
# Dil etiketi oluştur
|
|
244
|
+
label = part_key_labels.get(part_key, "")
|
|
245
|
+
if not label and part_key:
|
|
246
|
+
label = part_key.replace("_", " ").title()
|
|
247
|
+
|
|
248
|
+
extractor = self.ex_manager.find_extractor(iframe_url)
|
|
249
|
+
name = extractor.name if extractor else player_name or "Direct Link"
|
|
250
|
+
if label:
|
|
251
|
+
name = f"{name} | {label}"
|
|
252
|
+
|
|
253
|
+
links.append({
|
|
254
|
+
"url" : iframe_url,
|
|
255
|
+
"name" : name,
|
|
256
|
+
"referer" : self.main_url
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
return links
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
|
|
4
4
|
from parsel import Selector
|
|
5
|
+
import re
|
|
5
6
|
|
|
6
7
|
class SezonlukDizi(PluginBase):
|
|
7
8
|
name = "SezonlukDizi"
|
|
@@ -99,6 +100,20 @@ class SezonlukDizi(PluginBase):
|
|
|
99
100
|
actors = actors
|
|
100
101
|
)
|
|
101
102
|
|
|
103
|
+
async def get_asp_data(self) -> tuple[str, str]:
|
|
104
|
+
"""Fetch dynamic ASP version numbers from site.min.js"""
|
|
105
|
+
try:
|
|
106
|
+
js_content = await self.httpx.get(f"{self.main_url}/js/site.min.js")
|
|
107
|
+
alternatif_match = re.search(r'dataAlternatif(.*?)\.asp', js_content.text)
|
|
108
|
+
embed_match = re.search(r'dataEmbed(.*?)\.asp', js_content.text)
|
|
109
|
+
|
|
110
|
+
alternatif_ver = alternatif_match.group(1) if alternatif_match else "22"
|
|
111
|
+
embed_ver = embed_match.group(1) if embed_match else "22"
|
|
112
|
+
|
|
113
|
+
return (alternatif_ver, embed_ver)
|
|
114
|
+
except Exception:
|
|
115
|
+
return ("22", "22") # Fallback to default versions
|
|
116
|
+
|
|
102
117
|
async def load_links(self, url: str) -> list[dict]:
|
|
103
118
|
istek = await self.httpx.get(url)
|
|
104
119
|
secici = Selector(istek.text)
|
|
@@ -107,10 +122,13 @@ class SezonlukDizi(PluginBase):
|
|
|
107
122
|
if not bid:
|
|
108
123
|
return []
|
|
109
124
|
|
|
125
|
+
# Get dynamic ASP versions
|
|
126
|
+
alternatif_ver, embed_ver = await self.get_asp_data()
|
|
127
|
+
|
|
110
128
|
results = []
|
|
111
129
|
for dil, label in [("1", "Altyazı"), ("0", "Dublaj")]:
|
|
112
130
|
dil_istek = await self.httpx.post(
|
|
113
|
-
url = f"{self.main_url}/ajax/
|
|
131
|
+
url = f"{self.main_url}/ajax/dataAlternatif{alternatif_ver}.asp",
|
|
114
132
|
headers = {"X-Requested-With": "XMLHttpRequest"},
|
|
115
133
|
data = {"bid": bid, "dil": dil},
|
|
116
134
|
)
|
|
@@ -123,7 +141,7 @@ class SezonlukDizi(PluginBase):
|
|
|
123
141
|
if dil_json.get("status") == "success":
|
|
124
142
|
for idx, veri in enumerate(dil_json.get("data", [])):
|
|
125
143
|
veri_response = await self.httpx.post(
|
|
126
|
-
url = f"{self.main_url}/ajax/
|
|
144
|
+
url = f"{self.main_url}/ajax/dataEmbed{embed_ver}.asp",
|
|
127
145
|
headers = {"X-Requested-With": "XMLHttpRequest"},
|
|
128
146
|
data = {"id": veri.get("id")},
|
|
129
147
|
)
|
|
@@ -133,10 +151,13 @@ class SezonlukDizi(PluginBase):
|
|
|
133
151
|
if "link.asp" in iframe:
|
|
134
152
|
continue
|
|
135
153
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
154
|
+
iframe_url = self.fix_url(iframe)
|
|
155
|
+
extractor = self.ex_manager.find_extractor(iframe_url)
|
|
156
|
+
|
|
157
|
+
if extractor: # Only add if extractor found
|
|
158
|
+
results.append({
|
|
159
|
+
"url" : iframe_url,
|
|
160
|
+
"name" : f"{extractor.name} | {label}"
|
|
161
|
+
})
|
|
141
162
|
|
|
142
163
|
return results
|