KekikStream 2.3.9__py3-none-any.whl → 2.5.4__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.

Potentially problematic release.


This version of KekikStream might be problematic. Click here for more details.

Files changed (85) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +3 -2
  2. KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
  3. KekikStream/Core/HTMLHelper.py +120 -49
  4. KekikStream/Core/Plugin/PluginBase.py +35 -12
  5. KekikStream/Core/Plugin/PluginLoader.py +12 -14
  6. KekikStream/Core/Plugin/PluginManager.py +2 -2
  7. KekikStream/Core/Plugin/PluginModels.py +0 -3
  8. KekikStream/Extractors/Abstream.py +27 -0
  9. KekikStream/Extractors/CloseLoad.py +30 -54
  10. KekikStream/Extractors/ContentX.py +27 -72
  11. KekikStream/Extractors/DonilasPlay.py +33 -77
  12. KekikStream/Extractors/DzenRu.py +10 -24
  13. KekikStream/Extractors/ExPlay.py +20 -38
  14. KekikStream/Extractors/Filemoon.py +21 -46
  15. KekikStream/Extractors/HDMomPlayer.py +30 -0
  16. KekikStream/Extractors/HDPlayerSystem.py +13 -31
  17. KekikStream/Extractors/HotStream.py +27 -0
  18. KekikStream/Extractors/JFVid.py +3 -24
  19. KekikStream/Extractors/JetTv.py +21 -34
  20. KekikStream/Extractors/JetV.py +55 -0
  21. KekikStream/Extractors/MailRu.py +11 -29
  22. KekikStream/Extractors/MixPlayHD.py +15 -28
  23. KekikStream/Extractors/MixTiger.py +17 -40
  24. KekikStream/Extractors/MolyStream.py +17 -21
  25. KekikStream/Extractors/Odnoklassniki.py +40 -104
  26. KekikStream/Extractors/PeaceMakerst.py +18 -45
  27. KekikStream/Extractors/PixelDrain.py +8 -16
  28. KekikStream/Extractors/PlayerFilmIzle.py +22 -41
  29. KekikStream/Extractors/RapidVid.py +21 -35
  30. KekikStream/Extractors/SetPlay.py +18 -43
  31. KekikStream/Extractors/SibNet.py +7 -17
  32. KekikStream/Extractors/Sobreatsesuyp.py +23 -45
  33. KekikStream/Extractors/TRsTX.py +23 -53
  34. KekikStream/Extractors/TurboImgz.py +7 -14
  35. KekikStream/Extractors/VCTPlay.py +10 -28
  36. KekikStream/Extractors/Veev.py +145 -0
  37. KekikStream/Extractors/VidBiz.py +62 -0
  38. KekikStream/Extractors/VidHide.py +58 -30
  39. KekikStream/Extractors/VidMoly.py +65 -99
  40. KekikStream/Extractors/VidMoxy.py +16 -27
  41. KekikStream/Extractors/VidPapi.py +24 -54
  42. KekikStream/Extractors/VideoSeyred.py +19 -40
  43. KekikStream/Extractors/Videostr.py +58 -0
  44. KekikStream/Extractors/Vidoza.py +18 -0
  45. KekikStream/Extractors/Vtbe.py +38 -0
  46. KekikStream/Extractors/YTDLP.py +2 -2
  47. KekikStream/Extractors/YildizKisaFilm.py +13 -31
  48. KekikStream/Extractors/Zeus.py +61 -0
  49. KekikStream/Plugins/BelgeselX.py +97 -77
  50. KekikStream/Plugins/DiziBox.py +28 -45
  51. KekikStream/Plugins/DiziMom.py +179 -0
  52. KekikStream/Plugins/DiziPal.py +95 -161
  53. KekikStream/Plugins/DiziYou.py +51 -147
  54. KekikStream/Plugins/Dizilla.py +40 -61
  55. KekikStream/Plugins/FilmBip.py +90 -39
  56. KekikStream/Plugins/FilmEkseni.py +199 -0
  57. KekikStream/Plugins/FilmMakinesi.py +72 -73
  58. KekikStream/Plugins/FilmModu.py +25 -35
  59. KekikStream/Plugins/Filmatek.py +184 -0
  60. KekikStream/Plugins/FilmciBaba.py +155 -0
  61. KekikStream/Plugins/FullHDFilmizlesene.py +16 -37
  62. KekikStream/Plugins/HDFilm.py +243 -0
  63. KekikStream/Plugins/HDFilmCehennemi.py +242 -189
  64. KekikStream/Plugins/JetFilmizle.py +101 -69
  65. KekikStream/Plugins/KultFilmler.py +138 -104
  66. KekikStream/Plugins/RecTV.py +52 -73
  67. KekikStream/Plugins/RoketDizi.py +18 -27
  68. KekikStream/Plugins/SelcukFlix.py +30 -48
  69. KekikStream/Plugins/SetFilmIzle.py +76 -104
  70. KekikStream/Plugins/SezonlukDizi.py +90 -94
  71. KekikStream/Plugins/Sinefy.py +195 -167
  72. KekikStream/Plugins/SinemaCX.py +148 -78
  73. KekikStream/Plugins/Sinezy.py +29 -31
  74. KekikStream/Plugins/SuperFilmGeldi.py +12 -17
  75. KekikStream/Plugins/UgurFilm.py +85 -38
  76. KekikStream/Plugins/Watch32.py +160 -0
  77. KekikStream/Plugins/YabanciDizi.py +176 -211
  78. {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/METADATA +1 -1
  79. kekikstream-2.5.4.dist-info/RECORD +99 -0
  80. {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/WHEEL +1 -1
  81. KekikStream/Plugins/FullHDFilm.py +0 -249
  82. kekikstream-2.3.9.dist-info/RECORD +0 -84
  83. {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/entry_points.txt +0 -0
  84. {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/licenses/LICENSE +0 -0
  85. {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
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, ExtractResult, HTMLHelper
4
- import json, asyncio
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, HTMLHelper
4
+ import asyncio, contextlib
5
5
 
6
6
  class SetFilmIzle(PluginBase):
7
7
  name = "SetFilmIzle"
@@ -35,7 +35,7 @@ class SetFilmIzle(PluginBase):
35
35
 
36
36
  def _get_nonce(self, nonce_type: str = "video", referer: str = None) -> str:
37
37
  """Site cache'lenmiş nonce'ları expire olabiliyor, fresh nonce al veya sayfadan çek"""
38
- try:
38
+ with contextlib.suppress(Exception):
39
39
  resp = self.cloudscraper.post(
40
40
  f"{self.main_url}/wp-admin/admin-ajax.php",
41
41
  headers = {
@@ -49,17 +49,15 @@ class SetFilmIzle(PluginBase):
49
49
  if data and data.get("success"):
50
50
  nonces = data.get("data", {}).get("nonces", {})
51
51
  return nonces.get(nonce_type if nonce_type != "search" else "dt_ajax_search", "")
52
- except:
53
- pass
54
52
 
55
53
  # AJAX başarısızsa sayfadan çekmeyi dene
56
- try:
54
+ with contextlib.suppress(Exception):
57
55
  main_resp = self.cloudscraper.get(referer or self.main_url)
58
56
  # STMOVIE_AJAX = { ... nonces: { search: "...", ... } }
59
57
  nonce = HTMLHelper(main_resp.text).regex_first(rf'"{nonce_type}":\s*"([^"]+)"')
60
58
  return nonce or ""
61
- except:
62
- return ""
59
+
60
+ return ""
63
61
 
64
62
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
65
63
  istek = self.cloudscraper.get(url)
@@ -76,7 +74,7 @@ class SetFilmIzle(PluginBase):
76
74
  category = category,
77
75
  title = title,
78
76
  url = self.fix_url(href),
79
- poster = self.fix_url(poster) if poster else None
77
+ poster = self.fix_url(poster)
80
78
  ))
81
79
 
82
80
  return results
@@ -106,8 +104,8 @@ class SetFilmIzle(PluginBase):
106
104
  return []
107
105
 
108
106
  secici = HTMLHelper(html)
109
- results = []
110
107
 
108
+ results = []
111
109
  for item in secici.select("div.items article"):
112
110
  title = secici.select_text("h2", item)
113
111
  href = secici.select_attr("a", "href", item)
@@ -117,7 +115,7 @@ class SetFilmIzle(PluginBase):
117
115
  results.append(SearchResult(
118
116
  title = title,
119
117
  url = self.fix_url(href),
120
- poster = self.fix_url(poster) if poster else None
118
+ poster = self.fix_url(poster)
121
119
  ))
122
120
 
123
121
  return results
@@ -125,95 +123,39 @@ class SetFilmIzle(PluginBase):
125
123
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
126
124
  istek = self.cloudscraper.get(url)
127
125
  secici = HTMLHelper(istek.text)
128
- html_text = istek.text
129
-
130
- raw_title = secici.select_text("h1") or secici.select_text(".titles h1") or secici.select_attr("meta[property='og:title']", "content") or ""
131
- title = HTMLHelper(raw_title).regex_replace(r"(?i)\s*izle.*$", "", flags=0).strip()
132
-
133
- poster = secici.select_attr("div.poster img", "src")
134
126
 
127
+ title = self.clean_title(secici.select_text("h1") or secici.select_text(".titles h1") or secici.select_attr("meta[property='og:title']", "content"))
128
+ poster = secici.select_poster("div.poster img")
135
129
  description = secici.select_text("div.wp-content p")
130
+ rating = secici.select_text("b#repimdb strong") or secici.regex_first(r"([\d.]+)", secici.select_text("div.imdb"))
131
+ year = secici.extract_year("div.extra span.valor")
132
+ tags = secici.select_texts("div.sgeneros a")
133
+ duration = int(secici.regex_first(r"(\d+)", secici.select_text("span.runtime")) or 0)
134
+ actors = secici.select_texts("span.valor a[href*='/oyuncu/']")
135
+
136
+ common_info = {
137
+ "url" : url,
138
+ "poster" : self.fix_url(poster),
139
+ "title" : title,
140
+ "description" : description,
141
+ "tags" : tags,
142
+ "rating" : rating,
143
+ "year" : year,
144
+ "duration" : duration,
145
+ "actors" : actors
146
+ }
136
147
 
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)
143
-
144
- tags = [a.text(strip=True) for a in secici.select("div.sgeneros a") if a.text(strip=True)]
145
-
146
- duration_text = secici.select_text("span.runtime")
147
- duration = int(secici.regex_first(r"\d+", duration_text)) if duration_text and secici.regex_first(r"\d+", duration_text) else None
148
-
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>')
152
-
153
- trailer = None
154
- if trailer_id := secici.regex_first(r'embed/([^?]*)\?rel', html_text):
155
- trailer = f"https://www.youtube.com/embed/{trailer_id}"
156
-
157
- # Dizi mi film mi kontrol et
158
- is_series = "/dizi/" in url
159
-
160
- if is_series:
161
- year_elem = secici.select_text("a[href*='/yil/']")
162
- if year_elem:
163
- year = secici.regex_first(r"\d{4}", year_elem) or year
164
-
165
- # Duration from info section
166
- for span in secici.select("div#info span"):
167
- span_text = span.text(strip=True) if span.text() else ""
168
- if "Dakika" in span_text:
169
- duration = secici.regex_first(r"\d+", span_text) and int(secici.regex_first(r"\d+", span_text))
170
- break
171
-
148
+ if "/dizi/" in url:
172
149
  episodes = []
173
150
  for ep_item in secici.select("div#episodes ul.episodios li"):
174
- ep_href = secici.select_attr("h4.episodiotitle a", "href", ep_item)
175
- ep_name = secici.select_text("h4.episodiotitle a", ep_item)
176
-
177
- if not ep_href or not ep_name:
178
- continue
179
-
180
- ep_detail = ep_name
181
- ep_season = secici.regex_first(r"(\d+)\.\s*Sezon", ep_detail) or 1
182
- ep_episode = secici.regex_first(r"Sezon\s+(\d+)\.\s*Bölüm", ep_detail)
151
+ href = secici.select_attr("h4.episodiotitle a", "href", ep_item)
152
+ name = secici.select_direct_text("h4.episodiotitle a", ep_item)
153
+ if href and name:
154
+ s, e = secici.extract_season_episode(name)
155
+ episodes.append(Episode(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
156
+ return SeriesInfo(**common_info, episodes=episodes)
183
157
 
184
- ep_season = int(ep_season) if isinstance(ep_season, str) and ep_season.isdigit() else 1
185
- ep_episode = int(ep_episode) if isinstance(ep_episode, str) and ep_episode.isdigit() else None
186
-
187
- episodes.append(Episode(
188
- season = ep_season,
189
- episode = ep_episode,
190
- title = ep_name,
191
- url = self.fix_url(ep_href)
192
- ))
193
- return SeriesInfo(
194
- url = url,
195
- poster = self.fix_url(poster) if poster else None,
196
- title = title,
197
- description = description,
198
- tags = tags,
199
- rating = rating,
200
- year = year,
201
- duration = duration,
202
- actors = actors,
203
- episodes = episodes
204
- )
205
-
206
- return MovieInfo(
207
- url = url,
208
- poster = self.fix_url(poster) if poster else None,
209
- title = title,
210
- description = description,
211
- tags = tags,
212
- rating = rating,
213
- year = year,
214
- duration = duration,
215
- actors = actors
216
- )
158
+ return MovieInfo(**common_info)
217
159
 
218
160
  async def load_links(self, url: str) -> list[ExtractResult]:
219
161
  istek = await self.httpx.get(url)
@@ -231,14 +173,14 @@ class SetFilmIzle(PluginBase):
231
173
  semaphore = asyncio.Semaphore(5)
232
174
  tasks = []
233
175
 
234
- async def fetch_and_extract(player):
176
+ async def fetch_and_extract(player) -> list[ExtractResult]:
235
177
  async with semaphore:
236
178
  source_id = player.attrs.get("data-post-id")
237
- player_name = player.attrs.get("data-player-name")
179
+ player_name = player.attrs.get("data-player-name") or secici.select_text("b", player)
238
180
  part_key = player.attrs.get("data-part-key")
239
181
 
240
182
  if not source_id or "event" in source_id or source_id == "":
241
- return None
183
+ return []
242
184
 
243
185
  try:
244
186
  resp = self.cloudscraper.post(
@@ -248,17 +190,17 @@ class SetFilmIzle(PluginBase):
248
190
  "action" : "get_video_url",
249
191
  "nonce" : nonce,
250
192
  "post_id" : source_id,
251
- "player_name" : player_name or "",
193
+ "player_name" : player.attrs.get("data-player-name") or "",
252
194
  "part_key" : part_key or ""
253
195
  }
254
196
  )
255
197
  data = resp.json()
256
198
  except:
257
- return None
199
+ return []
258
200
 
259
201
  iframe_url = data.get("data", {}).get("url")
260
202
  if not iframe_url:
261
- return None
203
+ return []
262
204
 
263
205
  if "setplay" not in iframe_url and part_key:
264
206
  iframe_url = f"{iframe_url}?partKey={part_key}"
@@ -267,10 +209,40 @@ class SetFilmIzle(PluginBase):
267
209
  if not label and part_key:
268
210
  label = part_key.replace("_", " ").title()
269
211
 
270
- return await self.extract(iframe_url, prefix=label if label else None)
212
+ # İsimlendirme Formatı: "FastPlay | Türkçe Dublaj"
213
+ final_name = player_name
214
+ if label:
215
+ final_name = f"{final_name} | {label}" if final_name else label
216
+
217
+ # Extract et
218
+ extracted = await self.extract(iframe_url)
219
+ if not extracted:
220
+ return []
221
+
222
+ results = []
223
+ items = extracted if isinstance(extracted, list) else [extracted]
224
+ for item in items:
225
+ if final_name:
226
+ item.name = final_name
227
+ results.append(item)
228
+
229
+ return results
271
230
 
272
- for player in secici.select("nav.player a"):
231
+ # Selector Güncellemesi: data-player-name içeren tüm a tagleri
232
+ players = secici.select("a[data-player-name]")
233
+ if not players:
234
+ # Fallback legacy selector
235
+ players = secici.select("nav.player a")
236
+
237
+ for player in players:
273
238
  tasks.append(fetch_and_extract(player))
274
239
 
275
- results = await asyncio.gather(*tasks)
276
- return [r for r in results if r]
240
+ results_groups = await asyncio.gather(*tasks)
241
+
242
+ # Flatten
243
+ final_results = []
244
+ for group in results_groups:
245
+ if group:
246
+ final_results.extend(group)
247
+
248
+ return final_results
@@ -1,7 +1,7 @@
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, ExtractResult, HTMLHelper
4
- import asyncio
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult, HTMLHelper
4
+ import asyncio, contextlib
5
5
 
6
6
  class SezonlukDizi(PluginBase):
7
7
  name = "SezonlukDizi"
@@ -40,10 +40,10 @@ class SezonlukDizi(PluginBase):
40
40
 
41
41
  async def _get_asp_data(self) -> dict:
42
42
  js_req = await self.httpx.get(f"{self.main_url}/js/site.min.js")
43
- js = HTMLHelper(js_req.text)
44
- alt = js.regex_first(r"dataAlternatif(.*?)\.asp")
45
- emb = js.regex_first(r"dataEmbed(.*?)\.asp")
46
-
43
+ js = HTMLHelper(js_req.text)
44
+ alt = js.regex_first(r"dataAlternatif(.*?)\.asp")
45
+ emb = js.regex_first(r"dataEmbed(.*?)\.asp")
46
+
47
47
  return {
48
48
  "alternatif": alt or "",
49
49
  "embed": emb or ""
@@ -64,13 +64,13 @@ class SezonlukDizi(PluginBase):
64
64
  category = category,
65
65
  title = title,
66
66
  url = self.fix_url(href),
67
- poster = self.fix_url(poster) if poster else None,
67
+ poster = self.fix_url(poster),
68
68
  ))
69
69
 
70
70
  return results
71
71
 
72
72
  async def search(self, query: str) -> list[SearchResult]:
73
- istek = await self.httpx.get(f"{self.main_url}/diziler.asp?adi={query}")
73
+ istek = await self.httpx.get(f"{self.main_url}/diziler.asp?q={query}")
74
74
  secici = HTMLHelper(istek.text)
75
75
 
76
76
  results = []
@@ -83,7 +83,7 @@ class SezonlukDizi(PluginBase):
83
83
  results.append(SearchResult(
84
84
  title = title,
85
85
  url = self.fix_url(href),
86
- poster = self.fix_url(poster) if poster else None,
86
+ poster = self.fix_url(poster),
87
87
  ))
88
88
 
89
89
  return results
@@ -92,67 +92,40 @@ class SezonlukDizi(PluginBase):
92
92
  istek = await self.httpx.get(url)
93
93
  secici = HTMLHelper(istek.text)
94
94
 
95
- title = secici.select_text("div.header") or ""
96
-
97
- poster = secici.select_attr("div.image img", "data-src") or ""
98
-
99
- # year: re_first yerine re.search
100
- year_text = secici.select_text("div.extra span") or ""
101
- year = secici.regex_first(r"(\d{4})", year_text)
102
-
103
- # xpath normalized-space yerine doğrudan ID ile element bulup text al
104
- description = secici.select_text("span#tartismayorum-konu") or ""
105
-
106
- tags = [a.text(strip=True) for a in secici.select("div.labels a[href*='tur']") if a.text(strip=True)]
107
-
108
- # rating: regex ile çıkar
109
- rating_text = secici.select_text("div.dizipuani a div") or ""
110
- rating = secici.regex_first(r"[\d.,]+", rating_text)
111
-
112
- actors = []
113
-
114
- actors_istek = await self.httpx.get(f"{self.main_url}/oyuncular/{url.split('/')[-1]}")
115
- actors_secici = HTMLHelper(actors_istek.text)
116
- for actor in actors_secici.select("div.doubling div.ui"):
117
- header_text = actors_secici.select_text("div.header", actor)
118
- if header_text:
119
- actors.append(header_text)
120
-
121
- episodes_istek = await self.httpx.get(f"{self.main_url}/bolumler/{url.split('/')[-1]}")
122
- episodes_secici = HTMLHelper(episodes_istek.text)
123
- episodes = []
124
-
125
- for sezon in episodes_secici.select("table.unstackable"):
126
- for bolum in episodes_secici.select("tbody tr", sezon):
127
- # td:nth-of-type selectolax'ta desteklenmiyor, alternatif yol: tüm td'leri alıp indexle
128
- tds = episodes_secici.select("td", bolum)
129
- if len(tds) < 4:
130
- continue
131
-
132
- # 4. td'den isim ve href
133
- ep_name = episodes_secici.select_text("a", tds[3])
134
- ep_href = episodes_secici.select_attr("a", "href", tds[3])
135
-
136
- # 3. td'den episode (re_first yerine regex)
137
- ep_episode_text = episodes_secici.select_text("a", tds[2]) or ""
138
- ep_episode = episodes_secici.regex_first(r"(\d+)", ep_episode_text)
139
-
140
- # 2. td'den season (re_first yerine regex)
141
- ep_season_text = tds[1].text(strip=True) if tds[1] else ""
142
- ep_season = secici.regex_first(r"(\d+)", ep_season_text)
143
-
144
- if ep_name and ep_href:
145
- episode = Episode(
146
- season = ep_season,
147
- episode = ep_episode,
148
- title = ep_name,
149
- url = self.fix_url(ep_href),
150
- )
151
- episodes.append(episode)
95
+ title = secici.select_text("div.header") or ""
96
+ poster = secici.select_poster("div.image img")
97
+ year = secici.extract_year("div.extra span")
98
+ description = secici.select_text("span#tartismayorum-konu")
99
+ tags = secici.select_texts("div.labels a[href*='tur']")
100
+ rating = secici.regex_first(r"[\d.,]+", secici.select_text("div.dizipuani a div"))
101
+
102
+ # Actors extraction
103
+ id_slug = url.split('/')[-1]
104
+ a_resp = await self.httpx.get(f"{self.main_url}/oyuncular/{id_slug}")
105
+ a_sel = HTMLHelper(a_resp.text)
106
+ actors = a_sel.select_texts("div.doubling div.ui div.header")
107
+
108
+ # Episodes extraction
109
+ e_resp = await self.httpx.get(f"{self.main_url}/bolumler/{id_slug}")
110
+ e_sel = HTMLHelper(e_resp.text)
111
+ episodes = []
112
+ for row in e_sel.select("table.unstackable tbody tr"):
113
+ tds = e_sel.select("td", row)
114
+ if len(tds) >= 4:
115
+ name = e_sel.select_text("a", tds[3])
116
+ href = e_sel.select_attr("a", "href", tds[3])
117
+ if name and href:
118
+ s, e = e_sel.extract_season_episode(f"{tds[1].text(strip=True)} {tds[2].text(strip=True)}")
119
+ episodes.append(Episode(
120
+ season = s or 1,
121
+ episode = e or 1,
122
+ title = name,
123
+ url = self.fix_url(href)
124
+ ))
152
125
 
153
126
  return SeriesInfo(
154
127
  url = url,
155
- poster = self.fix_url(poster) if poster else None,
128
+ poster = self.fix_url(poster),
156
129
  title = title,
157
130
  description = description,
158
131
  tags = tags,
@@ -163,10 +136,10 @@ class SezonlukDizi(PluginBase):
163
136
  )
164
137
 
165
138
  async def load_links(self, url: str) -> list[ExtractResult]:
166
- istek = await self.httpx.get(url)
167
- secici = HTMLHelper(istek.text)
139
+ istek = await self.httpx.get(url)
140
+ secici = HTMLHelper(istek.text)
168
141
  asp_data = await self._get_asp_data()
169
-
142
+
170
143
  bid = secici.select_attr("div#dilsec", "data-id")
171
144
  if not bid:
172
145
  return []
@@ -174,41 +147,64 @@ class SezonlukDizi(PluginBase):
174
147
  semaphore = asyncio.Semaphore(5)
175
148
  tasks = []
176
149
 
177
- async def fetch_and_extract(veri, dil_etiketi):
150
+ async def fetch_and_extract(veri, dil_etiketi) -> list[ExtractResult]:
178
151
  async with semaphore:
179
152
  try:
180
153
  embed_resp = await self.httpx.post(
181
- f"{self.main_url}/ajax/dataEmbed{asp_data['embed']}.asp",
154
+ url = f"{self.main_url}/ajax/dataEmbed{asp_data['embed']}.asp",
182
155
  headers = {"X-Requested-With": "XMLHttpRequest"},
183
156
  data = {"id": str(veri.get("id"))}
184
157
  )
185
158
  embed_secici = HTMLHelper(embed_resp.text)
186
- iframe_src = embed_secici.select_attr("iframe", "src")
187
-
188
- if iframe_src:
189
- if "link.asp" in iframe_src:
190
- return None
191
-
192
- iframe_url = self.fix_url(iframe_src)
193
- return await self.extract(iframe_url, referer=f"{self.main_url}/", prefix=f"{dil_etiketi} - {veri.get('baslik')}")
194
- except:
195
- pass
196
- return None
159
+ iframe_src = embed_secici.select_attr("iframe", "src") or embed_secici.regex_first(r'src="(.*?)"')
160
+
161
+ if not iframe_src:
162
+ return []
163
+
164
+ iframe_url = self.fix_url(iframe_src)
165
+
166
+ real_url = iframe_url
167
+ if "url=" in iframe_url:
168
+ real_url = HTMLHelper(iframe_url).regex_first(r"url=([^&]+)")
169
+ if real_url:
170
+ real_url = self.fix_url(real_url)
171
+
172
+ source_name = veri.get('baslik') or "SezonlukDizi"
173
+ full_name = f"{dil_etiketi} - {source_name}"
174
+
175
+ extracted = await self.extract(real_url, referer=f"{self.main_url}/")
176
+
177
+ if not extracted:
178
+ return []
179
+
180
+ results = []
181
+ items = extracted if isinstance(extracted, list) else [extracted]
182
+ for item in items:
183
+ item.name = full_name
184
+ results.append(item)
185
+ return results
186
+
187
+ except Exception:
188
+ return []
197
189
 
198
190
  for dil_kodu, dil_etiketi in [("1", "Altyazı"), ("0", "Dublaj")]:
199
- altyazi_resp = await self.httpx.post(
200
- f"{self.main_url}/ajax/dataAlternatif{asp_data['alternatif']}.asp",
201
- headers = {"X-Requested-With": "XMLHttpRequest"},
202
- data = {"bid": bid, "dil": dil_kodu}
203
- )
204
-
205
- try:
191
+ with contextlib.suppress(Exception):
192
+ altyazi_resp = await self.httpx.post(
193
+ url = f"{self.main_url}/ajax/dataAlternatif{asp_data['alternatif']}.asp",
194
+ headers = {"X-Requested-With": "XMLHttpRequest"},
195
+ data = {"bid": bid, "dil": dil_kodu}
196
+ )
197
+
206
198
  data_json = altyazi_resp.json()
207
199
  if data_json.get("status") == "success" and data_json.get("data"):
208
200
  for veri in data_json["data"]:
209
201
  tasks.append(fetch_and_extract(veri, dil_etiketi))
210
- except:
211
- continue
212
202
 
213
- results = await asyncio.gather(*tasks)
214
- return [r for r in results if r]
203
+ results_groups = await asyncio.gather(*tasks)
204
+
205
+ final_results = []
206
+ for group in results_groups:
207
+ if group:
208
+ final_results.extend(group)
209
+
210
+ return final_results