KekikStream 2.1.4__py3-none-any.whl → 2.1.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.
Files changed (36) hide show
  1. KekikStream/Core/Plugin/PluginBase.py +20 -21
  2. KekikStream/Extractors/CloseLoad.py +17 -2
  3. KekikStream/Extractors/Filemoon.py +78 -0
  4. KekikStream/Extractors/RapidVid.py +15 -5
  5. KekikStream/Extractors/VidHide.py +11 -2
  6. KekikStream/Plugins/BelgeselX.py +7 -7
  7. KekikStream/Plugins/DiziBox.py +2 -2
  8. KekikStream/Plugins/DiziPal.py +8 -8
  9. KekikStream/Plugins/DiziYou.py +8 -8
  10. KekikStream/Plugins/Dizilla.py +2 -2
  11. KekikStream/Plugins/FilmBip.py +2 -2
  12. KekikStream/Plugins/FilmMakinesi.py +2 -2
  13. KekikStream/Plugins/FilmModu.py +8 -8
  14. KekikStream/Plugins/FullHDFilm.py +81 -22
  15. KekikStream/Plugins/FullHDFilmizlesene.py +2 -2
  16. KekikStream/Plugins/HDFilmCehennemi.py +79 -39
  17. KekikStream/Plugins/JetFilmizle.py +2 -2
  18. KekikStream/Plugins/KultFilmler.py +10 -9
  19. KekikStream/Plugins/RecTV.py +15 -15
  20. KekikStream/Plugins/RoketDizi.py +2 -2
  21. KekikStream/Plugins/SelcukFlix.py +68 -44
  22. KekikStream/Plugins/SetFilmIzle.py +21 -21
  23. KekikStream/Plugins/SezonlukDizi.py +19 -2
  24. KekikStream/Plugins/SineWix.py +13 -13
  25. KekikStream/Plugins/Sinefy.py +6 -6
  26. KekikStream/Plugins/SinemaCX.py +31 -22
  27. KekikStream/Plugins/Sinezy.py +2 -2
  28. KekikStream/Plugins/SuperFilmGeldi.py +30 -19
  29. KekikStream/Plugins/UgurFilm.py +2 -2
  30. KekikStream/__init__.py +15 -15
  31. {kekikstream-2.1.4.dist-info → kekikstream-2.1.9.dist-info}/METADATA +5 -3
  32. {kekikstream-2.1.4.dist-info → kekikstream-2.1.9.dist-info}/RECORD +36 -35
  33. {kekikstream-2.1.4.dist-info → kekikstream-2.1.9.dist-info}/WHEEL +0 -0
  34. {kekikstream-2.1.4.dist-info → kekikstream-2.1.9.dist-info}/entry_points.txt +0 -0
  35. {kekikstream-2.1.4.dist-info → kekikstream-2.1.9.dist-info}/licenses/LICENSE +0 -0
  36. {kekikstream-2.1.4.dist-info → kekikstream-2.1.9.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
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
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult
4
4
  from parsel import Selector
5
5
  from Kekik.Sifreleme import StringCodec
6
6
  import json, re
@@ -100,7 +100,7 @@ class FullHDFilmizlesene(PluginBase):
100
100
  duration = duration
101
101
  )
102
102
 
103
- async def load_links(self, url: str) -> list[dict]:
103
+ async def load_links(self, url: str) -> list[ExtractResult]:
104
104
  istek = await self.httpx.get(url)
105
105
  secici = Selector(istek.text)
106
106
 
@@ -1,6 +1,6 @@
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, Subtitle
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, Subtitle, ExtractResult
4
4
  from parsel import Selector
5
5
  from Kekik.Sifreleme import Packer, StreamDecoder
6
6
  import random, string, re
@@ -71,41 +71,81 @@ class HDFilmCehennemi(PluginBase):
71
71
 
72
72
  return results
73
73
 
74
- async def load_item(self, url: str) -> MovieInfo:
74
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
75
75
  istek = await self.httpx.get(url, headers = {"Referer": f"{self.main_url}/"})
76
76
  secici = Selector(istek.text)
77
77
 
78
- title = secici.css("h1.section-title::text").get().strip()
79
- poster = secici.css("aside.post-info-poster img.lazyload::attr(data-src)").get().strip()
80
- description = secici.css("article.post-info-content > p::text").get().strip()
78
+ title = secici.css("h1.section-title::text").get()
79
+ title = title.strip() if title else ""
80
+ poster = secici.css("aside.post-info-poster img.lazyload::attr(data-src)").get() or ""
81
+ poster = poster.strip() if poster else ""
82
+ description = secici.css("article.post-info-content > p::text").get() or ""
83
+ description = description.strip() if description else ""
81
84
  tags = secici.css("div.post-info-genres a::text").getall()
82
- rating = secici.css("div.post-info-imdb-rating span::text").get().strip()
83
- year = secici.css("div.post-info-year-country a::text").get().strip()
85
+ rating = secici.css("div.post-info-imdb-rating span::text").get() or ""
86
+ rating = rating.strip() if rating else ""
87
+ year = secici.css("div.post-info-year-country a::text").get() or ""
88
+ year = year.strip() if year else ""
84
89
  actors = secici.css("div.post-info-cast a > strong::text").getall()
85
- duration = secici.css("div.post-info-duration::text").get().replace("dakika", "").strip()
90
+ duration = secici.css("div.post-info-duration::text").get() or "0"
91
+ duration = duration.replace("dakika", "").strip()
86
92
 
87
-
88
93
  try:
89
- duration_minutes = int(duration[2:-1])
94
+ duration_minutes = int(re.search(r'\d+', duration).group()) if re.search(r'\d+', duration) else 0
90
95
  except Exception:
91
96
  duration_minutes = 0
92
97
 
93
- return MovieInfo(
94
- url = url,
95
- poster = self.fix_url(poster),
96
- title = self.clean_title(title),
97
- description = description,
98
- tags = tags,
99
- rating = rating,
100
- year = year,
101
- actors = actors,
102
- duration = duration_minutes
103
- )
98
+ # Dizi mi film mi kontrol et (Kotlin referansı: div.seasons kontrolü)
99
+ is_series = len(secici.css("div.seasons").getall()) > 0
100
+
101
+ if is_series:
102
+ episodes = []
103
+ for ep in secici.css("div.seasons-tab-content a"):
104
+ ep_name = ep.css("h4::text").get()
105
+ ep_href = ep.css("::attr(href)").get()
106
+ if ep_name and ep_href:
107
+ ep_name = ep_name.strip()
108
+ # Regex ile sezon ve bölüm numarası çıkar
109
+ ep_match = re.search(r'(\d+)\.\s*Bölüm', ep_name)
110
+ sz_match = re.search(r'(\d+)\.\s*Sezon', ep_name)
111
+ ep_num = int(ep_match.group(1)) if ep_match else 1
112
+ sz_num = int(sz_match.group(1)) if sz_match else 1
113
+
114
+ episodes.append(Episode(
115
+ season = sz_num,
116
+ episode = ep_num,
117
+ title = ep_name,
118
+ url = self.fix_url(ep_href)
119
+ ))
120
+
121
+ return SeriesInfo(
122
+ url = url,
123
+ poster = self.fix_url(poster),
124
+ title = self.clean_title(title),
125
+ description = description,
126
+ tags = tags,
127
+ rating = rating,
128
+ year = year,
129
+ actors = actors,
130
+ episodes = episodes
131
+ )
132
+ else:
133
+ return MovieInfo(
134
+ url = url,
135
+ poster = self.fix_url(poster),
136
+ title = self.clean_title(title),
137
+ description = description,
138
+ tags = tags,
139
+ rating = rating,
140
+ year = year,
141
+ actors = actors,
142
+ duration = duration_minutes
143
+ )
104
144
 
105
145
  def generate_random_cookie(self):
106
146
  return "".join(random.choices(string.ascii_letters + string.digits, k=16))
107
147
 
108
- async def cehennempass(self, video_id: str) -> list[dict]:
148
+ async def cehennempass(self, video_id: str) -> list:
109
149
  results = []
110
150
 
111
151
  istek = await self.httpx.post(
@@ -119,11 +159,11 @@ class HDFilmCehennemi(PluginBase):
119
159
  data = {"video_id": video_id, "selected_quality": "low"},
120
160
  )
121
161
  if video_url := istek.json().get("download_link"):
122
- results.append({
123
- "url" : self.fix_url(video_url),
124
- "name" : "Düşük Kalite",
125
- "referer" : f"https://cehennempass.pw/download/{video_id}"
126
- })
162
+ results.append(ExtractResult(
163
+ url = self.fix_url(video_url),
164
+ name = "Düşük Kalite",
165
+ referer = f"https://cehennempass.pw/download/{video_id}"
166
+ ))
127
167
 
128
168
  istek = await self.httpx.post(
129
169
  url = "https://cehennempass.pw/process_quality_selection.php",
@@ -136,11 +176,11 @@ class HDFilmCehennemi(PluginBase):
136
176
  data = {"video_id": video_id, "selected_quality": "high"},
137
177
  )
138
178
  if video_url := istek.json().get("download_link"):
139
- results.append({
140
- "url" : self.fix_url(video_url),
141
- "name" : "Yüksek Kalite",
142
- "referer" : f"https://cehennempass.pw/download/{video_id}"
143
- })
179
+ results.append(ExtractResult(
180
+ url = self.fix_url(video_url),
181
+ name = "Yüksek Kalite",
182
+ referer = f"https://cehennempass.pw/download/{video_id}"
183
+ ))
144
184
 
145
185
  return results
146
186
 
@@ -206,14 +246,14 @@ class HDFilmCehennemi(PluginBase):
206
246
  except Exception:
207
247
  pass
208
248
 
209
- return [{
210
- "url" : video_url,
211
- "name" : source,
212
- "referer" : url,
213
- "subtitles" : subtitles
214
- }]
249
+ return [ExtractResult(
250
+ url = video_url,
251
+ name = source,
252
+ referer = url,
253
+ subtitles = subtitles
254
+ )]
215
255
 
216
- async def load_links(self, url: str) -> list[dict]:
256
+ async def load_links(self, url: str) -> list[ExtractResult]:
217
257
  istek = await self.httpx.get(url)
218
258
  secici = Selector(istek.text)
219
259
 
@@ -1,6 +1,6 @@
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
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult
4
4
  from parsel import Selector
5
5
 
6
6
  class JetFilmizle(PluginBase):
@@ -96,7 +96,7 @@ class JetFilmizle(PluginBase):
96
96
  actors = actors
97
97
  )
98
98
 
99
- async def load_links(self, url: str) -> list[dict]:
99
+ async def load_links(self, url: str) -> list[ExtractResult]:
100
100
  istek = await self.httpx.get(url)
101
101
  secici = Selector(istek.text)
102
102
 
@@ -160,7 +160,7 @@ class KultFilmler(PluginBase):
160
160
  match = re.search(r"(https?://[^\s\"]+\.srt)", source_code)
161
161
  return match[1] if match else None
162
162
 
163
- async def load_links(self, url: str) -> list[dict]:
163
+ async def load_links(self, url: str) -> list[ExtractResult]:
164
164
  istek = await self.httpx.get(url)
165
165
  secici = Selector(istek.text)
166
166
 
@@ -195,12 +195,12 @@ class KultFilmler(PluginBase):
195
195
  m3u_match = re.search(r'file:"([^"]+)"', iframe_istek.text)
196
196
 
197
197
  if m3u_match:
198
- results.append({
199
- "name" : "VidMoly",
200
- "url" : m3u_match[1],
201
- "referer" : self.main_url,
202
- "subtitles" : []
203
- })
198
+ results.append(ExtractResult(
199
+ name = "VidMoly",
200
+ url = m3u_match[1],
201
+ referer = self.main_url,
202
+ subtitles = []
203
+ ))
204
204
  continue
205
205
 
206
206
  # Altyazı çıkar
@@ -210,7 +210,8 @@ class KultFilmler(PluginBase):
210
210
 
211
211
  data = await self.extract(iframe)
212
212
  if data:
213
- data["subtitles"] = subtitles
214
- results.append(data)
213
+ # ExtractResult objesi immutable, yeni bir kopya oluştur
214
+ updated_data = data.model_copy(update={"subtitles": subtitles}) if subtitles else data
215
+ results.append(updated_data)
215
216
 
216
217
  return results
@@ -1,6 +1,6 @@
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, Episode, SeriesInfo
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Episode, SeriesInfo, ExtractResult
4
4
  from json import dumps, loads
5
5
  import re
6
6
 
@@ -116,21 +116,21 @@ class RecTV(PluginBase):
116
116
  actors = []
117
117
  )
118
118
 
119
- async def load_links(self, url: str) -> list[dict]:
119
+ async def load_links(self, url: str) -> list[ExtractResult]:
120
120
  try:
121
121
  veri = loads(url)
122
122
  except Exception:
123
123
  # JSON değilse düz URL'dir (eski yapı veya hata)
124
- return [{"url": url, "name": "Video"}]
124
+ return [ExtractResult(url=url, name="Video")]
125
125
 
126
126
  # Eğer dizi bölümü ise (bizim oluşturduğumuz yapı)
127
127
  if veri.get("is_episode"):
128
- return [{
129
- "url" : veri.get("url"),
130
- "name" : veri.get("title", "Bölüm"),
131
- "user_agent" : "googleusercontent",
132
- "referer" : "https://twitter.com/"
133
- }]
128
+ return [ExtractResult(
129
+ url = veri.get("url"),
130
+ name = veri.get("title", "Bölüm"),
131
+ user_agent = "googleusercontent",
132
+ referer = "https://twitter.com/"
133
+ )]
134
134
 
135
135
  # Film ise (RecTV API yapısı)
136
136
  results = []
@@ -140,11 +140,11 @@ class RecTV(PluginBase):
140
140
  if "otolinkaff" in video_link:
141
141
  continue
142
142
 
143
- results.append({
144
- "url" : video_link,
145
- "name" : f"{veri.get('title')} - {kaynak.get('title')}",
146
- "user_agent" : "googleusercontent",
147
- "referer" : "https://twitter.com/"
148
- })
143
+ results.append(ExtractResult(
144
+ url = video_link,
145
+ name = f"{veri.get('title')} - {kaynak.get('title')}",
146
+ user_agent = "googleusercontent",
147
+ referer = "https://twitter.com/"
148
+ ))
149
149
 
150
150
  return results
@@ -1,6 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, MovieInfo
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult, MovieInfo
4
4
  from parsel import Selector
5
5
  import re, base64, json
6
6
 
@@ -169,7 +169,7 @@ class RoketDizi(PluginBase):
169
169
  year = year
170
170
  )
171
171
 
172
- async def load_links(self, url: str) -> list[dict]:
172
+ async def load_links(self, url: str) -> list[ExtractResult]:
173
173
  resp = await self.httpx.get(url)
174
174
  sel = Selector(resp.text)
175
175
 
@@ -1,6 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult
4
4
  from parsel import Selector
5
5
  import re, base64, json, urllib.parse
6
6
 
@@ -137,8 +137,13 @@ class SelcukFlix(PluginBase):
137
137
  search_url = f"{self.main_url}/api/bg/searchcontent?searchterm={query}"
138
138
 
139
139
  headers = {
140
+ "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0",
140
141
  "Accept" : "application/json, text/plain, */*",
142
+ "Accept-Language" : "en-US,en;q=0.5",
141
143
  "X-Requested-With" : "XMLHttpRequest",
144
+ "Sec-Fetch-Site" : "same-origin",
145
+ "Sec-Fetch-Mode" : "cors",
146
+ "Sec-Fetch-Dest" : "empty",
142
147
  "Referer" : f"{self.main_url}/"
143
148
  }
144
149
 
@@ -151,15 +156,16 @@ class SelcukFlix(PluginBase):
151
156
  try:
152
157
  decoded_str = raw_data.decode('utf-8')
153
158
  except UnicodeDecodeError:
154
- decoded_str = raw_data.decode('iso-8859-1').encode('utf-8').decode('utf-8')
159
+ decoded_str = raw_data.decode('iso-8859-1')
155
160
 
156
161
  search_data = json.loads(decoded_str)
157
162
 
158
163
  results = []
159
164
  for item in search_data.get("result", []):
160
- title = item.get("title")
161
- slug = item.get("slug")
162
- poster = item.get("poster")
165
+ # API field isimleri: object_name, used_slug, object_poster_url
166
+ title = item.get("object_name") or item.get("title")
167
+ slug = item.get("used_slug") or item.get("slug")
168
+ poster = item.get("object_poster_url") or item.get("poster")
163
169
 
164
170
  if poster:
165
171
  poster = self.clean_image_url(poster)
@@ -176,7 +182,7 @@ class SelcukFlix(PluginBase):
176
182
  except Exception:
177
183
  return []
178
184
 
179
- async def load_item(self, url: str) -> SeriesInfo:
185
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
180
186
  resp = await self.httpx.get(url)
181
187
  sel = Selector(resp.text)
182
188
 
@@ -190,15 +196,17 @@ class SelcukFlix(PluginBase):
190
196
  try:
191
197
  decoded_str = raw_data.decode('utf-8')
192
198
  except UnicodeDecodeError:
193
- decoded_str = raw_data.decode('iso-8859-1') # .encode('utf-8').decode('utf-8') implied
199
+ decoded_str = raw_data.decode('iso-8859-1')
194
200
 
195
201
  content_details = json.loads(decoded_str)
196
202
  item = content_details.get("contentItem", {})
197
203
 
198
- title = item.get("original_title") or item.get("originalTitle")
204
+ title = item.get("original_title") or item.get("originalTitle") or ""
199
205
  poster = self.clean_image_url(item.get("poster_url") or item.get("posterUrl"))
200
206
  description = item.get("description") or item.get("used_description")
201
207
  rating = str(item.get("imdb_point") or item.get("imdbPoint", ""))
208
+ year = item.get("release_year") or item.get("releaseYear")
209
+ duration = item.get("total_minutes") or item.get("totalMinutes")
202
210
 
203
211
  series_data = content_details.get("relatedData", {}).get("seriesData")
204
212
  if not series_data and "RelatedResults" in content_details:
@@ -206,17 +214,18 @@ class SelcukFlix(PluginBase):
206
214
  if series_data and isinstance(series_data, list):
207
215
  pass
208
216
 
209
- episodes = []
217
+ # Dizi mi film mi kontrol et (Kotlin referansı)
210
218
  if series_data:
211
- seasons_list = []
212
- if isinstance(series_data, dict):
213
- seasons_list = series_data.get("seasons", [])
214
- elif isinstance(series_data, list):
215
- seasons_list = series_data
216
-
217
- for season in seasons_list:
219
+ episodes = []
220
+ seasons_list = []
221
+ if isinstance(series_data, dict):
222
+ seasons_list = series_data.get("seasons", [])
223
+ elif isinstance(series_data, list):
224
+ seasons_list = series_data
225
+
226
+ for season in seasons_list:
218
227
  if not isinstance(season, dict): continue
219
- s_no = season.get("season_no") or season.get("seasonNo") # Try snake_case too
228
+ s_no = season.get("season_no") or season.get("seasonNo")
220
229
  ep_list = season.get("episodes", [])
221
230
  for ep in ep_list:
222
231
  episodes.append(Episode(
@@ -225,17 +234,29 @@ class SelcukFlix(PluginBase):
225
234
  title = ep.get("ep_text") or ep.get("epText"),
226
235
  url = self.fix_url(ep.get("used_slug") or ep.get("usedSlug"))
227
236
  ))
228
-
229
- return SeriesInfo(
230
- title = title,
231
- url = url,
232
- poster = poster,
233
- description = description,
234
- rating = rating,
235
- episodes = episodes
236
- )
237
-
238
- async def load_links(self, url: str) -> list[dict]:
237
+
238
+ return SeriesInfo(
239
+ title = title,
240
+ url = url,
241
+ poster = poster,
242
+ description = description,
243
+ rating = rating,
244
+ year = year,
245
+ episodes = episodes
246
+ )
247
+ else:
248
+ # Film ise MovieInfo döndür
249
+ return MovieInfo(
250
+ title = title,
251
+ url = url,
252
+ poster = poster,
253
+ description = description,
254
+ rating = rating,
255
+ year = year,
256
+ duration = duration
257
+ )
258
+
259
+ async def load_links(self, url: str) -> list[ExtractResult]:
239
260
  resp = await self.httpx.get(url)
240
261
  sel = Selector(resp.text)
241
262
 
@@ -248,42 +269,45 @@ class SelcukFlix(PluginBase):
248
269
  secure_data = data["props"]["pageProps"]["secureData"]
249
270
  raw_data = base64.b64decode(secure_data.replace('"', ''))
250
271
 
251
- # Try UTF-8 first, fallback to ISO-8859-1 (matching Kotlin)
252
272
  try:
253
273
  decoded_str = raw_data.decode('utf-8')
254
274
  except UnicodeDecodeError:
255
275
  decoded_str = raw_data.decode('iso-8859-1')
256
276
 
257
277
  content_details = json.loads(decoded_str)
258
- related_data = content_details.get("relatedData", {})
278
+ related_results = content_details.get("RelatedResults", {})
259
279
 
260
280
  source_content = None
261
281
 
262
- # Check if Series (episode) or Movie
282
+ # Dizi (bölüm) için
263
283
  if "/dizi/" in url:
264
- if related_data.get("episodeSources", {}).get("state"):
265
- res = related_data["episodeSources"].get("result", [])
284
+ episode_sources = related_results.get("getEpisodeSources", {})
285
+ if episode_sources.get("state"):
286
+ res = episode_sources.get("result", [])
266
287
  if res:
267
- source_content = res[0].get("sourceContent")
288
+ source_content = res[0].get("source_content") or res[0].get("sourceContent")
268
289
  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", {})
290
+ # Film için
291
+ movie_parts = related_results.get("getMoviePartsById", {})
292
+ if movie_parts.get("state"):
293
+ parts = movie_parts.get("result", [])
294
+ if parts:
295
+ first_part_id = parts[0].get("id")
276
296
  key = f"getMoviePartSourcesById_{first_part_id}"
277
- if key in rr:
278
- res = rr[key].get("result", [])
297
+ if key in related_results:
298
+ res = related_results[key].get("result", [])
279
299
  if res:
280
- source_content = res[0].get("source_content")
300
+ source_content = res[0].get("source_content") or res[0].get("sourceContent")
281
301
 
282
302
  if source_content:
283
303
  iframe_sel = Selector(source_content)
284
304
  iframe_src = iframe_sel.css("iframe::attr(src)").get()
285
305
  if iframe_src:
286
306
  iframe_src = self.fix_url(iframe_src)
307
+ # Hotlinger domain değişimi (Kotlin referansı)
308
+ if "sn.dplayer74.site" in iframe_src:
309
+ iframe_src = iframe_src.replace("sn.dplayer74.site", "sn.hotlinger.com")
310
+
287
311
  data = await self.extract(iframe_src)
288
312
  if data:
289
313
  return [data]
@@ -1,6 +1,6 @@
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, SeriesInfo, Episode
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult
4
4
  from parsel import Selector
5
5
  import re, json
6
6
 
@@ -34,6 +34,23 @@ class SetFilmIzle(PluginBase):
34
34
  f"{main_url}/tur/western/" : "Western"
35
35
  }
36
36
 
37
+ def _get_nonce(self, nonce_type: str = "video_nonce", referer: str = None) -> str:
38
+ """Site cache'lenmiş nonce'ları expire olabiliyor, fresh nonce al"""
39
+ try:
40
+ resp = self.cloudscraper.post(
41
+ f"{self.main_url}/wp-admin/admin-ajax.php",
42
+ headers = {
43
+ "Referer" : referer or self.main_url,
44
+ "Origin" : self.main_url,
45
+ "Content-Type" : "application/x-www-form-urlencoded",
46
+ },
47
+ data = "action=st_cache_refresh_nonces"
48
+ )
49
+ nonces = resp.json().get("data", {}).get("nonces", {})
50
+ return nonces.get(nonce_type, "")
51
+ except:
52
+ return ""
53
+
37
54
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
38
55
  istek = self.cloudscraper.get(url)
39
56
  secici = Selector(istek.text)
@@ -55,23 +72,7 @@ class SetFilmIzle(PluginBase):
55
72
  return results
56
73
 
57
74
  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
+ nonce = self._get_nonce("search")
75
76
 
76
77
  search_resp = self.cloudscraper.post(
77
78
  f"{self.main_url}/wp-admin/admin-ajax.php",
@@ -193,11 +194,11 @@ class SetFilmIzle(PluginBase):
193
194
  actors = actors
194
195
  )
195
196
 
196
- async def load_links(self, url: str) -> list[dict]:
197
+ async def load_links(self, url: str) -> list[ExtractResult]:
197
198
  istek = await self.httpx.get(url)
198
199
  secici = Selector(istek.text)
199
200
 
200
- nonce = secici.css("div#playex::attr(data-nonce)").get() or ""
201
+ nonce = self._get_nonce("video_nonce", referer=url)
201
202
 
202
203
  # partKey to dil label mapping
203
204
  part_key_labels = {
@@ -215,7 +216,6 @@ class SetFilmIzle(PluginBase):
215
216
  if not source_id or "event" in source_id or source_id == "":
216
217
  continue
217
218
 
218
- # Multipart form request
219
219
  try:
220
220
  resp = self.cloudscraper.post(
221
221
  f"{self.main_url}/wp-admin/admin-ajax.php",
@@ -1,6 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
4
4
  from parsel import Selector
5
5
  import re
6
6
 
@@ -20,6 +20,23 @@ class SezonlukDizi(PluginBase):
20
20
  f"{main_url}/diziler.asp?siralama_tipi=id&kat=4&s=" : "Animasyonlar",
21
21
  f"{main_url}/diziler.asp?siralama_tipi=id&kat=5&s=" : "Animeler",
22
22
  f"{main_url}/diziler.asp?siralama_tipi=id&kat=6&s=" : "Belgeseller",
23
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=aile&s=" : "Aile",
24
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=aksiyon&s=" : "Aksiyon",
25
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=bilimkurgu&s=" : "Bilim Kurgu",
26
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=biyografik&s=" : "Biyografi",
27
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=dram&s=" : "Dram",
28
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=fantastik&s=" : "Fantastik",
29
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=gerilim&s=" : "Gerilim",
30
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=gizem&s=" : "Gizem",
31
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=korku&s=" : "Korku",
32
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=komedi&s=" : "Komedi",
33
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=macera&s=" : "Macera",
34
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=muzikal&s=" : "Müzikal",
35
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=suc&s=" : "Suç",
36
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=romantik&s=" : "Romantik",
37
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=savas&s=" : "Savaş",
38
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=tarihi&s=" : "Tarihi",
39
+ f"{main_url}/diziler.asp?siralama_tipi=id&tur=western&s=" : "Western"
23
40
  }
24
41
 
25
42
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
@@ -114,7 +131,7 @@ class SezonlukDizi(PluginBase):
114
131
  except Exception:
115
132
  return ("22", "22") # Fallback to default versions
116
133
 
117
- async def load_links(self, url: str) -> list[dict]:
134
+ async def load_links(self, url: str) -> list[ExtractResult]:
118
135
  istek = await self.httpx.get(url)
119
136
  secici = Selector(istek.text)
120
137