KekikStream 2.3.6__py3-none-any.whl → 2.3.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/Plugin/PluginBase.py +6 -1
- KekikStream/Extractors/MolyStream.py +50 -8
- KekikStream/Extractors/VidMoly.py +13 -0
- KekikStream/Plugins/DiziBox.py +25 -15
- KekikStream/Plugins/DiziPal.py +20 -9
- KekikStream/Plugins/DiziWatch.py +6 -6
- KekikStream/Plugins/DiziYou.py +14 -18
- KekikStream/Plugins/Dizilla.py +50 -59
- KekikStream/Plugins/FilmBip.py +8 -4
- KekikStream/Plugins/FilmMakinesi.py +1 -1
- KekikStream/Plugins/FullHDFilmizlesene.py +7 -16
- KekikStream/Plugins/JetFilmizle.py +6 -1
- KekikStream/Plugins/KultFilmler.py +2 -1
- KekikStream/Plugins/RecTV.py +27 -5
- KekikStream/Plugins/RoketDizi.py +76 -78
- KekikStream/Plugins/SelcukFlix.py +90 -67
- KekikStream/Plugins/SetFilmIzle.py +27 -8
- KekikStream/Plugins/YabanciDizi.py +25 -14
- {kekikstream-2.3.6.dist-info → kekikstream-2.3.8.dist-info}/METADATA +1 -1
- {kekikstream-2.3.6.dist-info → kekikstream-2.3.8.dist-info}/RECORD +24 -24
- {kekikstream-2.3.6.dist-info → kekikstream-2.3.8.dist-info}/WHEEL +0 -0
- {kekikstream-2.3.6.dist-info → kekikstream-2.3.8.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.3.6.dist-info → kekikstream-2.3.8.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.3.6.dist-info → kekikstream-2.3.8.dist-info}/top_level.txt +0 -0
KekikStream/Plugins/RoketDizi.py
CHANGED
|
@@ -84,89 +84,87 @@ class RoketDizi(PluginBase):
|
|
|
84
84
|
except Exception:
|
|
85
85
|
return []
|
|
86
86
|
|
|
87
|
-
async def load_item(self, url: str) -> SeriesInfo:
|
|
88
|
-
# Note: Handling both Movie and Series logic in one, returning SeriesInfo generally or MovieInfo
|
|
87
|
+
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
89
88
|
resp = await self.httpx.get(url)
|
|
90
89
|
sel = HTMLHelper(resp.text)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
poster = sel.select_attr("div.w-full.page-top img", "src")
|
|
96
|
-
|
|
97
|
-
description = sel.select_text("div.mt-2.text-sm")
|
|
98
|
-
|
|
99
|
-
# Tags - genre bilgileri (Detaylar bölümünde)
|
|
100
|
-
tags = []
|
|
101
|
-
genre_text = sel.select_text("h3.text-white.opacity-90")
|
|
102
|
-
if genre_text:
|
|
103
|
-
tags = [t.strip() for t in genre_text.split(",")]
|
|
104
|
-
|
|
105
|
-
# Rating
|
|
106
|
-
rating = sel.select_text("span.text-white.text-sm.font-bold")
|
|
107
|
-
|
|
108
|
-
# Year ve Actors - Detaylar (Details) bölümünden
|
|
109
|
-
year = None
|
|
110
|
-
actors = []
|
|
90
|
+
|
|
91
|
+
next_data_text = sel.select_text("script#__NEXT_DATA__")
|
|
92
|
+
if not next_data_text:
|
|
93
|
+
return SeriesInfo(url=url, title=sel.select_text("h1") or "Bilinmeyen")
|
|
111
94
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
value = sel.select_text("span.text-sm.opacity-90", item)
|
|
95
|
+
try:
|
|
96
|
+
next_data = json.loads(next_data_text)
|
|
97
|
+
secure_data_raw = next_data["props"]["pageProps"]["secureData"]
|
|
98
|
+
secure_data = json.loads(base64.b64decode(secure_data_raw).decode('utf-8'))
|
|
117
99
|
|
|
118
|
-
|
|
119
|
-
|
|
100
|
+
content_item = secure_data.get("contentItem", {})
|
|
101
|
+
content = secure_data.get("content", {}).get("result", {})
|
|
120
102
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
103
|
+
title = content_item.get("original_title") or content_item.get("culture_title")
|
|
104
|
+
poster = content_item.get("poster_url") or content_item.get("face_url")
|
|
105
|
+
description = content_item.get("description")
|
|
106
|
+
rating = str(content_item.get("imdb_point") or "")
|
|
107
|
+
year = str(content_item.get("release_year") or "")
|
|
108
|
+
tags = content_item.get("categories", "").split(",")
|
|
109
|
+
|
|
110
|
+
# Actors extraction from getSerieCastsById or getMovieCastsById
|
|
111
|
+
actors = []
|
|
112
|
+
casts_data = content.get("getSerieCastsById") or content.get("getMovieCastsById")
|
|
113
|
+
if casts_data and casts_data.get("result"):
|
|
114
|
+
actors = [cast.get("name") for cast in casts_data["result"] if cast.get("name")]
|
|
115
|
+
|
|
116
|
+
# Episodes extraction
|
|
117
|
+
episodes = []
|
|
118
|
+
if "Series" in str(content.get("FindedType")):
|
|
119
|
+
# Check for episodes in SecureData -> RelatedResults -> getEpisodeSources (this might be for the current episode)
|
|
120
|
+
# Usually full episode list isn't in secureData, but we can get it from HTML or another API
|
|
121
|
+
# However, many times Next.js pages have them in props
|
|
122
|
+
# Let's fallback to the previous regex method for episodes if not in JSON
|
|
123
|
+
all_urls = HTMLHelper(resp.text).regex_all(r'"url":"([^"]*)"')
|
|
124
|
+
episodes_dict = {}
|
|
125
|
+
for u in all_urls:
|
|
126
|
+
if "bolum" in u and u not in episodes_dict:
|
|
127
|
+
s_match = HTMLHelper(u).regex_first(r'/sezon-(\d+)')
|
|
128
|
+
e_match = HTMLHelper(u).regex_first(r'/bolum-(\d+)')
|
|
129
|
+
s_val = int(s_match) if s_match else 1
|
|
130
|
+
e_val = int(e_match) if e_match else 1
|
|
131
|
+
episodes_dict[(s_val, e_val)] = Episode(
|
|
132
|
+
season = s_val,
|
|
133
|
+
episode = e_val,
|
|
134
|
+
title = f"{s_val}. Sezon {e_val}. Bölüm",
|
|
135
|
+
url = self.fix_url(u)
|
|
136
|
+
)
|
|
137
|
+
episodes = [episodes_dict[key] for key in sorted(episodes_dict.keys())]
|
|
138
|
+
|
|
139
|
+
return SeriesInfo(
|
|
140
|
+
url = url,
|
|
141
|
+
poster = self.fix_url(poster) if poster else None,
|
|
142
|
+
title = self.clean_title(title),
|
|
143
|
+
description = description,
|
|
144
|
+
tags = tags,
|
|
145
|
+
rating = rating,
|
|
146
|
+
year = year,
|
|
147
|
+
actors = actors,
|
|
148
|
+
episodes = episodes
|
|
149
|
+
)
|
|
150
|
+
else:
|
|
151
|
+
return MovieInfo(
|
|
152
|
+
url = url,
|
|
153
|
+
poster = self.fix_url(poster) if poster else None,
|
|
154
|
+
title = self.clean_title(title),
|
|
155
|
+
description = description,
|
|
156
|
+
tags = tags,
|
|
157
|
+
rating = rating,
|
|
158
|
+
year = year,
|
|
159
|
+
actors = actors
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
except Exception:
|
|
163
|
+
# Fallback to simple extraction if JSON parsing fails
|
|
164
|
+
return SeriesInfo(
|
|
165
|
+
url = url,
|
|
166
|
+
title = self.clean_title(sel.select_text("h1")) or "Bilinmeyen"
|
|
167
|
+
)
|
|
170
168
|
|
|
171
169
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
172
170
|
resp = await self.httpx.get(url)
|
|
@@ -184,76 +184,99 @@ class SelcukFlix(PluginBase):
|
|
|
184
184
|
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
185
185
|
resp = await self.httpx.get(url)
|
|
186
186
|
sel = HTMLHelper(resp.text)
|
|
187
|
+
|
|
188
|
+
next_data_text = sel.select_text("script#__NEXT_DATA__")
|
|
189
|
+
if not next_data_text:
|
|
190
|
+
return SeriesInfo(url=url, title=sel.select_text("h1") or "Bilinmeyen")
|
|
187
191
|
|
|
188
|
-
next_data = sel.select_text("script#__NEXT_DATA__")
|
|
189
|
-
if not next_data:
|
|
190
|
-
return None
|
|
191
|
-
|
|
192
|
-
data = json.loads(next_data)
|
|
193
|
-
secure_data = data["props"]["pageProps"]["secureData"]
|
|
194
|
-
raw_data = base64.b64decode(secure_data.replace('"', ''))
|
|
195
192
|
try:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
content_details = json.loads(decoded_str)
|
|
201
|
-
item = content_details.get("contentItem", {})
|
|
202
|
-
|
|
203
|
-
title = item.get("original_title") or item.get("originalTitle") or ""
|
|
204
|
-
poster = self.clean_image_url(item.get("poster_url") or item.get("posterUrl"))
|
|
205
|
-
description = item.get("description") or item.get("used_description")
|
|
206
|
-
rating = str(item.get("imdb_point") or item.get("imdbPoint", ""))
|
|
207
|
-
year = item.get("release_year") or item.get("releaseYear")
|
|
208
|
-
duration = item.get("total_minutes") or item.get("totalMinutes")
|
|
209
|
-
|
|
210
|
-
series_data = content_details.get("relatedData", {}).get("seriesData")
|
|
211
|
-
if not series_data and "RelatedResults" in content_details:
|
|
212
|
-
series_data = content_details["RelatedResults"].get("getSerieSeasonAndEpisodes", {}).get("result")
|
|
213
|
-
if series_data and isinstance(series_data, list):
|
|
214
|
-
pass
|
|
215
|
-
|
|
216
|
-
# Dizi mi film mi kontrol et (Kotlin referansı)
|
|
217
|
-
if series_data:
|
|
218
|
-
episodes = []
|
|
219
|
-
seasons_list = []
|
|
220
|
-
if isinstance(series_data, dict):
|
|
221
|
-
seasons_list = series_data.get("seasons", [])
|
|
222
|
-
elif isinstance(series_data, list):
|
|
223
|
-
seasons_list = series_data
|
|
224
|
-
|
|
225
|
-
for season in seasons_list:
|
|
226
|
-
if not isinstance(season, dict): continue
|
|
227
|
-
s_no = season.get("season_no") or season.get("seasonNo")
|
|
228
|
-
ep_list = season.get("episodes", [])
|
|
229
|
-
for ep in ep_list:
|
|
230
|
-
episodes.append(Episode(
|
|
231
|
-
season = s_no,
|
|
232
|
-
episode = ep.get("episode_no") or ep.get("episodeNo"),
|
|
233
|
-
title = ep.get("ep_text") or ep.get("epText"),
|
|
234
|
-
url = self.fix_url(ep.get("used_slug") or ep.get("usedSlug"))
|
|
235
|
-
))
|
|
193
|
+
next_data = json.loads(next_data_text)
|
|
194
|
+
secure_data_raw = next_data["props"]["pageProps"].get("secureData")
|
|
195
|
+
if not secure_data_raw:
|
|
196
|
+
return SeriesInfo(url=url, title=sel.select_text("h1") or "Bilinmeyen")
|
|
236
197
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
)
|
|
198
|
+
# Clean possible quotes from string before decoding
|
|
199
|
+
if isinstance(secure_data_raw, str):
|
|
200
|
+
secure_data_raw = secure_data_raw.strip('"')
|
|
201
|
+
|
|
202
|
+
decoded_str = base64.b64decode(secure_data_raw).decode('utf-8')
|
|
203
|
+
content_details = json.loads(decoded_str)
|
|
204
|
+
|
|
205
|
+
# Sometimes content_details might be a string (double encoded)
|
|
206
|
+
if isinstance(content_details, str):
|
|
207
|
+
content_details = json.loads(content_details)
|
|
208
|
+
|
|
209
|
+
print(f"DEBUG: type(content_details)={type(content_details)}")
|
|
210
|
+
item = content_details.get("contentItem", {})
|
|
211
|
+
print(f"DEBUG: type(item)={type(item)}")
|
|
212
|
+
related_results = content_details.get("RelatedResults", {})
|
|
213
|
+
|
|
214
|
+
title = item.get("original_title") or item.get("culture_title") or item.get("originalTitle") or ""
|
|
215
|
+
poster = self.clean_image_url(item.get("poster_url") or item.get("posterUrl") or item.get("face_url"))
|
|
216
|
+
description = item.get("description") or item.get("used_description")
|
|
217
|
+
rating = str(item.get("imdb_point") or item.get("imdbPoint") or "")
|
|
218
|
+
year = str(item.get("release_year") or item.get("releaseYear") or "")
|
|
219
|
+
duration = item.get("total_minutes") or item.get("totalMinutes")
|
|
220
|
+
|
|
221
|
+
tags = []
|
|
222
|
+
tags_raw = item.get("category_names") or item.get("categoryNames") or item.get("categories")
|
|
223
|
+
if isinstance(tags_raw, str):
|
|
224
|
+
tags = [t.strip() for t in tags_raw.split(",")]
|
|
225
|
+
elif isinstance(tags_raw, list):
|
|
226
|
+
tags = [c.get("title") if isinstance(c, dict) else str(c) for c in tags_raw]
|
|
227
|
+
|
|
228
|
+
actors = []
|
|
229
|
+
actors_raw = item.get("actor_names") or item.get("actorNames")
|
|
230
|
+
if isinstance(actors_raw, str):
|
|
231
|
+
actors = [a.strip() for a in actors_raw.split(",")]
|
|
232
|
+
|
|
233
|
+
# Casts from RelatedResults
|
|
234
|
+
casts_data = related_results.get("getSerieCastsById") or related_results.get("getMovieCastsById")
|
|
235
|
+
if casts_data and isinstance(casts_data, dict) and casts_data.get("result"):
|
|
236
|
+
actors = [cast.get("name") for cast in casts_data["result"] if cast.get("name")]
|
|
237
|
+
|
|
238
|
+
series_data = related_results.get("getSerieSeasonAndEpisodes")
|
|
239
|
+
if series_data and isinstance(series_data, dict) and series_data.get("result"):
|
|
240
|
+
episodes = []
|
|
241
|
+
for season in series_data["result"]:
|
|
242
|
+
s_no = season.get("season_no") or season.get("seasonNo") or 1
|
|
243
|
+
for ep in season.get("episodes", []):
|
|
244
|
+
ep_slug = ep.get("used_slug") or ep.get("usedSlug")
|
|
245
|
+
if ep_slug:
|
|
246
|
+
episodes.append(Episode(
|
|
247
|
+
season = s_no,
|
|
248
|
+
episode = ep.get("episode_no") or ep.get("episodeNo") or 1,
|
|
249
|
+
title = ep.get("ep_text") or ep.get("epText") or "",
|
|
250
|
+
url = self.fix_url(ep_slug)
|
|
251
|
+
))
|
|
252
|
+
|
|
253
|
+
return SeriesInfo(
|
|
254
|
+
url = url,
|
|
255
|
+
poster = poster,
|
|
256
|
+
title = self.clean_title(title),
|
|
257
|
+
description = description,
|
|
258
|
+
tags = tags,
|
|
259
|
+
rating = rating,
|
|
260
|
+
year = year,
|
|
261
|
+
actors = actors,
|
|
262
|
+
duration = duration,
|
|
263
|
+
episodes = episodes
|
|
264
|
+
)
|
|
265
|
+
else:
|
|
266
|
+
return MovieInfo(
|
|
267
|
+
url = url,
|
|
268
|
+
poster = poster,
|
|
269
|
+
title = self.clean_title(title),
|
|
270
|
+
description = description,
|
|
271
|
+
tags = tags,
|
|
272
|
+
rating = rating,
|
|
273
|
+
year = year,
|
|
274
|
+
actors = actors,
|
|
275
|
+
duration = duration
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
except Exception:
|
|
279
|
+
return SeriesInfo(url=url, title=self.clean_title(sel.select_text("h1")) or "Bilinmeyen")
|
|
257
280
|
|
|
258
281
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
259
282
|
resp = await self.httpx.get(url)
|
|
@@ -33,8 +33,8 @@ class SetFilmIzle(PluginBase):
|
|
|
33
33
|
f"{main_url}/tur/western/" : "Western"
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
def _get_nonce(self, nonce_type: str = "
|
|
37
|
-
"""Site cache'lenmiş nonce'ları expire olabiliyor, fresh nonce al"""
|
|
36
|
+
def _get_nonce(self, nonce_type: str = "video", referer: str = None) -> str:
|
|
37
|
+
"""Site cache'lenmiş nonce'ları expire olabiliyor, fresh nonce al veya sayfadan çek"""
|
|
38
38
|
try:
|
|
39
39
|
resp = self.cloudscraper.post(
|
|
40
40
|
f"{self.main_url}/wp-admin/admin-ajax.php",
|
|
@@ -45,8 +45,19 @@ class SetFilmIzle(PluginBase):
|
|
|
45
45
|
},
|
|
46
46
|
data = "action=st_cache_refresh_nonces"
|
|
47
47
|
)
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
data = resp.json()
|
|
49
|
+
if data and data.get("success"):
|
|
50
|
+
nonces = data.get("data", {}).get("nonces", {})
|
|
51
|
+
return nonces.get(nonce_type if nonce_type != "search" else "dt_ajax_search", "")
|
|
52
|
+
except:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
# AJAX başarısızsa sayfadan çekmeyi dene
|
|
56
|
+
try:
|
|
57
|
+
main_resp = self.cloudscraper.get(referer or self.main_url)
|
|
58
|
+
# STMOVIE_AJAX = { ... nonces: { search: "...", ... } }
|
|
59
|
+
nonce = HTMLHelper(main_resp.text).regex_first(rf'"{nonce_type}":\s*"([^"]+)"')
|
|
60
|
+
return nonce or ""
|
|
50
61
|
except:
|
|
51
62
|
return ""
|
|
52
63
|
|
|
@@ -112,7 +123,7 @@ class SetFilmIzle(PluginBase):
|
|
|
112
123
|
return results
|
|
113
124
|
|
|
114
125
|
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
115
|
-
istek =
|
|
126
|
+
istek = self.cloudscraper.get(url)
|
|
116
127
|
secici = HTMLHelper(istek.text)
|
|
117
128
|
html_text = istek.text
|
|
118
129
|
|
|
@@ -123,15 +134,21 @@ class SetFilmIzle(PluginBase):
|
|
|
123
134
|
|
|
124
135
|
description = secici.select_text("div.wp-content p")
|
|
125
136
|
|
|
126
|
-
|
|
127
|
-
|
|
137
|
+
rating = secici.select_text("b#repimdb strong") or secici.regex_first(r'repimdb"><strong>\s*([^<]+)</strong>', html_text)
|
|
138
|
+
|
|
139
|
+
# Yıl için info bölümünden veya regex ile yakala
|
|
140
|
+
year = secici.regex_first(r'(\d{4})', secici.select_text("div.extra span.valor") or secici.select_text("span.valor") or "")
|
|
141
|
+
if not year:
|
|
142
|
+
year = secici.regex_first(r'<span>(\d{4})</span>', html_text) or secici.regex_first(r'(\d{4})', html_text)
|
|
128
143
|
|
|
129
144
|
tags = [a.text(strip=True) for a in secici.select("div.sgeneros a") if a.text(strip=True)]
|
|
130
145
|
|
|
131
146
|
duration_text = secici.select_text("span.runtime")
|
|
132
147
|
duration = int(secici.regex_first(r"\d+", duration_text)) if duration_text and secici.regex_first(r"\d+", duration_text) else None
|
|
133
148
|
|
|
134
|
-
actors = [
|
|
149
|
+
actors = [a.text(strip=True) for a in secici.select("span.valor a") if "/oyuncu/" in (a.attrs.get("href") or "")]
|
|
150
|
+
if not actors:
|
|
151
|
+
actors = secici.regex_all(r'href="[^"]*/oyuncu/[^"]*">([^<]+)</a>')
|
|
135
152
|
|
|
136
153
|
trailer = None
|
|
137
154
|
if trailer_id := secici.regex_first(r'embed/([^?]*)\?rel', html_text):
|
|
@@ -179,6 +196,7 @@ class SetFilmIzle(PluginBase):
|
|
|
179
196
|
title = title,
|
|
180
197
|
description = description,
|
|
181
198
|
tags = tags,
|
|
199
|
+
rating = rating,
|
|
182
200
|
year = year,
|
|
183
201
|
duration = duration,
|
|
184
202
|
actors = actors,
|
|
@@ -191,6 +209,7 @@ class SetFilmIzle(PluginBase):
|
|
|
191
209
|
title = title,
|
|
192
210
|
description = description,
|
|
193
211
|
tags = tags,
|
|
212
|
+
rating = rating,
|
|
194
213
|
year = year,
|
|
195
214
|
duration = duration,
|
|
196
215
|
actors = actors
|
|
@@ -90,14 +90,17 @@ class YabanciDizi(PluginBase):
|
|
|
90
90
|
poster = sel.select_attr("meta[property='og:image']", "content")
|
|
91
91
|
description = sel.select_text("p#tv-series-desc")
|
|
92
92
|
|
|
93
|
-
year
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
# Try to extract year from table first
|
|
94
|
+
year_cell = sel.select_text("td div.truncate")
|
|
95
|
+
year = None
|
|
96
|
+
if year_cell:
|
|
97
|
+
year_match = sel.regex_first(r"(\d{4})", year_cell)
|
|
98
|
+
if year_match:
|
|
99
|
+
year = year_match
|
|
96
100
|
|
|
97
101
|
tags = []
|
|
98
102
|
rating = None
|
|
99
103
|
duration = None
|
|
100
|
-
year = None
|
|
101
104
|
actors = []
|
|
102
105
|
for item in sel.select("div.item"):
|
|
103
106
|
text = item.text(strip=True)
|
|
@@ -174,13 +177,14 @@ class YabanciDizi(PluginBase):
|
|
|
174
177
|
)
|
|
175
178
|
|
|
176
179
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
177
|
-
|
|
180
|
+
# Use cloudscraper to bypass Cloudflare
|
|
181
|
+
resp = self.cloudscraper.get(url, headers={"Referer": f"{self.main_url}/"})
|
|
178
182
|
sel = HTMLHelper(resp.text)
|
|
179
183
|
|
|
180
184
|
results = []
|
|
181
185
|
|
|
182
|
-
# Method 1: alternatives-for-this
|
|
183
|
-
for alt in sel.select("div.alternatives-for-this div.item
|
|
186
|
+
# Method 1: alternatives-for-this (include active too)
|
|
187
|
+
for alt in sel.select("div.alternatives-for-this div.item"):
|
|
184
188
|
data_hash = alt.attrs.get("data-hash")
|
|
185
189
|
data_link = alt.attrs.get("data-link")
|
|
186
190
|
q_type = alt.attrs.get("data-querytype")
|
|
@@ -188,7 +192,7 @@ class YabanciDizi(PluginBase):
|
|
|
188
192
|
if not data_hash or not data_link: continue
|
|
189
193
|
|
|
190
194
|
try:
|
|
191
|
-
post_resp =
|
|
195
|
+
post_resp = self.cloudscraper.post(
|
|
192
196
|
f"{self.main_url}/ajax/service",
|
|
193
197
|
data = {
|
|
194
198
|
"link" : data_link,
|
|
@@ -219,7 +223,7 @@ class YabanciDizi(PluginBase):
|
|
|
219
223
|
data_eid = id_el.attrs.get("data-eid")
|
|
220
224
|
|
|
221
225
|
try:
|
|
222
|
-
post_resp =
|
|
226
|
+
post_resp = self.cloudscraper.post(
|
|
223
227
|
f"{self.main_url}/ajax/service",
|
|
224
228
|
data = {
|
|
225
229
|
"e_id" : data_eid,
|
|
@@ -244,9 +248,10 @@ class YabanciDizi(PluginBase):
|
|
|
244
248
|
|
|
245
249
|
return results
|
|
246
250
|
|
|
247
|
-
|
|
251
|
+
def _fetch_and_extract_sync(self, iframe_url, prefix=""):
|
|
252
|
+
"""Synchronous helper for _fetch_and_extract using cloudscraper."""
|
|
248
253
|
# Initial fetch
|
|
249
|
-
resp =
|
|
254
|
+
resp = self.cloudscraper.get(
|
|
250
255
|
iframe_url,
|
|
251
256
|
headers = {"Referer": f"{self.main_url}/"},
|
|
252
257
|
cookies = {"udys": "1760709729873", "level": "1"}
|
|
@@ -254,11 +259,12 @@ class YabanciDizi(PluginBase):
|
|
|
254
259
|
|
|
255
260
|
# Handle "Lütfen bekleyiniz" check from Kotlin
|
|
256
261
|
if "Lütfen bekleyiniz" in resp.text:
|
|
257
|
-
|
|
258
|
-
|
|
262
|
+
import time as time_module
|
|
263
|
+
time_module.sleep(1)
|
|
264
|
+
timestamp = int(time_module.time())
|
|
259
265
|
# Retry with t=timestamp as in Kotlin
|
|
260
266
|
sep = "&" if "?" in iframe_url else "?"
|
|
261
|
-
resp =
|
|
267
|
+
resp = self.cloudscraper.get(
|
|
262
268
|
f"{iframe_url}{sep}t={timestamp}",
|
|
263
269
|
headers = {"Referer": f"{self.main_url}/"},
|
|
264
270
|
cookies = resp.cookies # Use cookies from first response
|
|
@@ -267,6 +273,11 @@ class YabanciDizi(PluginBase):
|
|
|
267
273
|
sel = HTMLHelper(resp.text)
|
|
268
274
|
final_iframe = sel.select_attr("iframe", "src")
|
|
269
275
|
|
|
276
|
+
return final_iframe
|
|
277
|
+
|
|
278
|
+
async def _fetch_and_extract(self, iframe_url, prefix=""):
|
|
279
|
+
final_iframe = self._fetch_and_extract_sync(iframe_url, prefix)
|
|
280
|
+
|
|
270
281
|
if final_iframe:
|
|
271
282
|
final_url = self.fix_url(final_iframe)
|
|
272
283
|
return await self.extract(final_url, referer=f"{self.main_url}/", prefix=prefix)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: KekikStream
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.8
|
|
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
|
|
@@ -12,7 +12,7 @@ KekikStream/Core/Extractor/ExtractorModels.py,sha256=Qj_gbIeGRewaZXNfYkTi4FFRRq6
|
|
|
12
12
|
KekikStream/Core/Extractor/YTDLPCache.py,sha256=sRg5kwFxkRXA_8iRwsV29E51g9qQJvg8dWUnzfr7EwA,984
|
|
13
13
|
KekikStream/Core/Media/MediaHandler.py,sha256=MEn3spPAThVloN3WcoCwWhpoyMA7tAZvcwYjmjJsX3U,7678
|
|
14
14
|
KekikStream/Core/Media/MediaManager.py,sha256=AaUq2D7JSJIphjoAj2fjLOJjswm7Qf5hjYCbBdrbnDU,438
|
|
15
|
-
KekikStream/Core/Plugin/PluginBase.py,sha256=
|
|
15
|
+
KekikStream/Core/Plugin/PluginBase.py,sha256=DMuYf6fRZeRJWYh3UdGcV1FpmDjUnjO1pgIBMRqHjiY,6638
|
|
16
16
|
KekikStream/Core/Plugin/PluginLoader.py,sha256=6LE5id0571bB-gJZxaLfd973XcG6oaGeMhLVcYYY7kw,3768
|
|
17
17
|
KekikStream/Core/Plugin/PluginManager.py,sha256=6a0Q2mHtzIpx1ttdSTsVHg2HfLJIO0r_iHjK3Kui1Rw,939
|
|
18
18
|
KekikStream/Core/Plugin/PluginModels.py,sha256=Yvx-6Fkn8QCIcuqAkFbCP5EJcq3XBkK_P8S0tRNhS6E,2476
|
|
@@ -29,7 +29,7 @@ KekikStream/Extractors/JetTv.py,sha256=2X1vYDQ0hxBTcpnE_XTcbw9tMS1aXFURcobnPdN8Z
|
|
|
29
29
|
KekikStream/Extractors/MailRu.py,sha256=xQVCWwYqNoG5T43VAW1_m0v4e80FbO-1pNPKkwhTccU,1218
|
|
30
30
|
KekikStream/Extractors/MixPlayHD.py,sha256=u5fUePHfjOI3n7KlNsWhXIv7HA_NMj5bPw1ug-eiXLU,1557
|
|
31
31
|
KekikStream/Extractors/MixTiger.py,sha256=4VbOYgE4s5H-BGVvJI0AI57M-WBWqnek_LGfCFHAucw,2116
|
|
32
|
-
KekikStream/Extractors/MolyStream.py,sha256=
|
|
32
|
+
KekikStream/Extractors/MolyStream.py,sha256=sEyJE8L_49GOVOhkA_FvZjwXhzLhyue0gw3gQT-dW8A,3125
|
|
33
33
|
KekikStream/Extractors/Odnoklassniki.py,sha256=hajKPhWKiIuu_i441TXrWVORpLo2CdTcoJiyU3WQAuI,4038
|
|
34
34
|
KekikStream/Extractors/PeaceMakerst.py,sha256=BJ5Cv5X2GEaMTwn_XFpAVVmts1h5xGno3l5rL7Ugob4,2335
|
|
35
35
|
KekikStream/Extractors/PixelDrain.py,sha256=xPud8W_hqLUXJSU5O-MiCOblcmzrlDJpnEtuxr4ZdI4,1011
|
|
@@ -45,30 +45,30 @@ KekikStream/Extractors/TurboImgz.py,sha256=zLGMUwoThZ_LoW0bThyV8TSCcIYHsUme1RTqq
|
|
|
45
45
|
KekikStream/Extractors/TurkeyPlayer.py,sha256=zdX0IOO3M-kgAYWex2WwJJu9aGf8WhOY-ZIrRmZRiC0,1246
|
|
46
46
|
KekikStream/Extractors/VCTPlay.py,sha256=1BCl2_vVIrwvG56LCzl2KE5g2CUaMAhzImOZMdZpZCQ,1377
|
|
47
47
|
KekikStream/Extractors/VidHide.py,sha256=38ly2Gi44rxIyjeM-6H_HjmwXbKEpSqK4ba16ToQUYs,2707
|
|
48
|
-
KekikStream/Extractors/VidMoly.py,sha256=
|
|
48
|
+
KekikStream/Extractors/VidMoly.py,sha256=4k5z68MrUASUuDMEWZ_Ynvp1Z7njjRcXPBZAnpbtGB4,5500
|
|
49
49
|
KekikStream/Extractors/VidMoxy.py,sha256=dM7yBfrXSESvYyqc2uP_gLSgV61gpIAY940NAQ58Mts,1843
|
|
50
50
|
KekikStream/Extractors/VidPapi.py,sha256=9y8TN-o4C3JvRyr2V8Ox908tFE1I2BItQLHZlqs8AuI,3175
|
|
51
51
|
KekikStream/Extractors/VideoSeyred.py,sha256=KJxbJkuupmn4wWBj_ejnoDvmjUXwEXkzStYha3EsSpA,1995
|
|
52
52
|
KekikStream/Extractors/YTDLP.py,sha256=Hy8loCSFSquu2zaL3INord-Jm6T8CM6K2-VcDA2K79g,7390
|
|
53
53
|
KekikStream/Extractors/YildizKisaFilm.py,sha256=R_JlrOVeMiDlXYcuTdItnKvidyx8_u3B14fSrxew2aE,1316
|
|
54
54
|
KekikStream/Plugins/BelgeselX.py,sha256=smoLjEJTdptjb7h4m6LhG7ZUmJQtIhYyi0CUFBsk970,8696
|
|
55
|
-
KekikStream/Plugins/DiziBox.py,sha256=
|
|
56
|
-
KekikStream/Plugins/DiziPal.py,sha256=
|
|
57
|
-
KekikStream/Plugins/DiziWatch.py,sha256=
|
|
58
|
-
KekikStream/Plugins/DiziYou.py,sha256=
|
|
59
|
-
KekikStream/Plugins/Dizilla.py,sha256=
|
|
60
|
-
KekikStream/Plugins/FilmBip.py,sha256=
|
|
61
|
-
KekikStream/Plugins/FilmMakinesi.py,sha256=
|
|
55
|
+
KekikStream/Plugins/DiziBox.py,sha256=CKfbMAtyE6J7QgD19iryzhD5VJlc0Tz2s4nSYW7XSrg,11354
|
|
56
|
+
KekikStream/Plugins/DiziPal.py,sha256=tHUqAN8UvvzBAkJaGS4hFvdLo-eRO4EdQ_C9HYkj_0U,10576
|
|
57
|
+
KekikStream/Plugins/DiziWatch.py,sha256=NK9xccX4-HwWq67FFVfqDDzKwCDc_HvDJDW2QIvqjig,8900
|
|
58
|
+
KekikStream/Plugins/DiziYou.py,sha256=4KOvxHg-84mUHuHWsXoYlIG2SX4DCV2dm6GblHQ5wGo,11162
|
|
59
|
+
KekikStream/Plugins/Dizilla.py,sha256=PLN0pOkWB4IaGC7Toe-8f5rksmaNm_WfdSFMTAtt--0,13624
|
|
60
|
+
KekikStream/Plugins/FilmBip.py,sha256=40eSECwMHSKTWoUmF90UXxTJkbx6f71J_98Ht4Hnoj8,6352
|
|
61
|
+
KekikStream/Plugins/FilmMakinesi.py,sha256=0bVN28aCEfrxrvXrGyL6XtgipzUKUD9vN2QkHie2gY0,7859
|
|
62
62
|
KekikStream/Plugins/FilmModu.py,sha256=ou1BrFNR4RQaJdxVqPB5FI8vnQ0UmD-siVdwLnpp7x0,7147
|
|
63
63
|
KekikStream/Plugins/FullHDFilm.py,sha256=08NF5qEydmxT0rGYDWpTOSIYSad8Uv1H1V8yCKG_568,10525
|
|
64
|
-
KekikStream/Plugins/FullHDFilmizlesene.py,sha256=
|
|
64
|
+
KekikStream/Plugins/FullHDFilmizlesene.py,sha256=OpdndVQ7LjZ-sJdILGEqhYX-0D18yRqTS7Kpu-HrXmY,6870
|
|
65
65
|
KekikStream/Plugins/HDFilmCehennemi.py,sha256=jntMKgE81k_jl3pFzJI3akqvi3g8U961dVx7bj5Pf2w,13140
|
|
66
|
-
KekikStream/Plugins/JetFilmizle.py,sha256=
|
|
67
|
-
KekikStream/Plugins/KultFilmler.py,sha256=
|
|
68
|
-
KekikStream/Plugins/RecTV.py,sha256=
|
|
69
|
-
KekikStream/Plugins/RoketDizi.py,sha256=
|
|
70
|
-
KekikStream/Plugins/SelcukFlix.py,sha256=
|
|
71
|
-
KekikStream/Plugins/SetFilmIzle.py,sha256=
|
|
66
|
+
KekikStream/Plugins/JetFilmizle.py,sha256=9sH9Z3y4SP8vta9v-gJOQOxFWAQnbZomFea1_G5EbmM,8100
|
|
67
|
+
KekikStream/Plugins/KultFilmler.py,sha256=rvIkd2OXRxuAXHMjiHCRmKrS5m09gy2JoMBgJh7ZIOk,9478
|
|
68
|
+
KekikStream/Plugins/RecTV.py,sha256=p-RELvvD1-gyUgitwosYryH9gRWQTjmn1O9tw-0YInk,8295
|
|
69
|
+
KekikStream/Plugins/RoketDizi.py,sha256=KiX7Xf5IyPPJ-CVcJLM9qc0M6Fi2dhg7zU3EgWkICXA,9318
|
|
70
|
+
KekikStream/Plugins/SelcukFlix.py,sha256=nJ7I5e5vBkn9AbLC_2bSu9bSSgMQeDhCQZBZovK00bc,15299
|
|
71
|
+
KekikStream/Plugins/SetFilmIzle.py,sha256=Z8A_Ivbe65i13RocGZXwmpwrVxNOwdj7Gh3CS2-Fslg,11437
|
|
72
72
|
KekikStream/Plugins/SezonlukDizi.py,sha256=h8mIglL2ORUklnAvEwH_5z6tT3WYxiNnbkeIvxtGUTE,9751
|
|
73
73
|
KekikStream/Plugins/SineWix.py,sha256=z0r90lggAugEWE1g9vg8gZsInBObUZPnVFQwq7GYmJs,7052
|
|
74
74
|
KekikStream/Plugins/Sinefy.py,sha256=ShX13Q-_5KFBobxZufI5V_4zwWvEWfNYuP-g5CkBuww,11099
|
|
@@ -76,10 +76,10 @@ KekikStream/Plugins/SinemaCX.py,sha256=11kzAZWgjkitIonDHHiFHMgnViBj-GjyvTXg7k28M
|
|
|
76
76
|
KekikStream/Plugins/Sinezy.py,sha256=fUj-3WaJMEsKZRnDpHFPxl5Eq2RPLroY80DcftLqvjM,5743
|
|
77
77
|
KekikStream/Plugins/SuperFilmGeldi.py,sha256=StW0ue4qDj8p7CiWy19Lfr2aWtfYvslPExZJuR-3xiY,6348
|
|
78
78
|
KekikStream/Plugins/UgurFilm.py,sha256=H6AA2iTaM0fn6uN8_Dfvr-OqUtM9gDdkg0BKIcZEj7U,4930
|
|
79
|
-
KekikStream/Plugins/YabanciDizi.py,sha256=
|
|
80
|
-
kekikstream-2.3.
|
|
81
|
-
kekikstream-2.3.
|
|
82
|
-
kekikstream-2.3.
|
|
83
|
-
kekikstream-2.3.
|
|
84
|
-
kekikstream-2.3.
|
|
85
|
-
kekikstream-2.3.
|
|
79
|
+
KekikStream/Plugins/YabanciDizi.py,sha256=QXzifSl2JMcVOwkwn2vafYIw1jqB5vBTrf-usvsyMBc,11947
|
|
80
|
+
kekikstream-2.3.8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
81
|
+
kekikstream-2.3.8.dist-info/METADATA,sha256=9xG4vCFR9UYkg6ZuhfdW9WEIMlLDVVYZoRp5mJqdqSs,10761
|
|
82
|
+
kekikstream-2.3.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
83
|
+
kekikstream-2.3.8.dist-info/entry_points.txt,sha256=dFwdiTx8djyehI0Gsz-rZwjAfZzUzoBSrmzRu9ubjJc,50
|
|
84
|
+
kekikstream-2.3.8.dist-info/top_level.txt,sha256=DNmGJDXl27Drdfobrak8KYLmocW_uznVYFJOzcjUgmY,12
|
|
85
|
+
kekikstream-2.3.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|