KekikStream 2.4.1__py3-none-any.whl → 2.4.3__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 (72) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +3 -2
  2. KekikStream/Core/HTMLHelper.py +134 -40
  3. KekikStream/Core/Plugin/PluginBase.py +3 -2
  4. KekikStream/Extractors/CloseLoad.py +30 -54
  5. KekikStream/Extractors/ContentX.py +27 -72
  6. KekikStream/Extractors/DonilasPlay.py +33 -77
  7. KekikStream/Extractors/DzenRu.py +10 -24
  8. KekikStream/Extractors/ExPlay.py +20 -38
  9. KekikStream/Extractors/Filemoon.py +19 -45
  10. KekikStream/Extractors/HDMomPlayer.py +24 -56
  11. KekikStream/Extractors/HDPlayerSystem.py +13 -31
  12. KekikStream/Extractors/HotStream.py +14 -32
  13. KekikStream/Extractors/JFVid.py +3 -24
  14. KekikStream/Extractors/JetTv.py +21 -34
  15. KekikStream/Extractors/MailRu.py +11 -29
  16. KekikStream/Extractors/MixPlayHD.py +15 -28
  17. KekikStream/Extractors/MixTiger.py +17 -40
  18. KekikStream/Extractors/MolyStream.py +17 -21
  19. KekikStream/Extractors/Odnoklassniki.py +28 -104
  20. KekikStream/Extractors/PeaceMakerst.py +18 -45
  21. KekikStream/Extractors/PixelDrain.py +8 -16
  22. KekikStream/Extractors/PlayerFilmIzle.py +22 -41
  23. KekikStream/Extractors/RapidVid.py +21 -35
  24. KekikStream/Extractors/SetPlay.py +18 -43
  25. KekikStream/Extractors/SibNet.py +7 -17
  26. KekikStream/Extractors/Sobreatsesuyp.py +23 -45
  27. KekikStream/Extractors/TRsTX.py +23 -53
  28. KekikStream/Extractors/TurboImgz.py +7 -14
  29. KekikStream/Extractors/VCTPlay.py +10 -28
  30. KekikStream/Extractors/VidHide.py +10 -31
  31. KekikStream/Extractors/VidMoly.py +65 -99
  32. KekikStream/Extractors/VidMoxy.py +16 -27
  33. KekikStream/Extractors/VidPapi.py +24 -54
  34. KekikStream/Extractors/VideoSeyred.py +19 -40
  35. KekikStream/Extractors/Videostr.py +42 -99
  36. KekikStream/Extractors/Vidoza.py +8 -15
  37. KekikStream/Extractors/YildizKisaFilm.py +13 -31
  38. KekikStream/Plugins/BelgeselX.py +63 -69
  39. KekikStream/Plugins/DiziBox.py +16 -36
  40. KekikStream/Plugins/DiziMom.py +37 -129
  41. KekikStream/Plugins/DiziPal.py +26 -75
  42. KekikStream/Plugins/DiziYou.py +44 -152
  43. KekikStream/Plugins/Dizilla.py +18 -44
  44. KekikStream/Plugins/FilmBip.py +10 -24
  45. KekikStream/Plugins/FilmEkseni.py +12 -32
  46. KekikStream/Plugins/FilmMakinesi.py +24 -77
  47. KekikStream/Plugins/FilmModu.py +11 -18
  48. KekikStream/Plugins/Filmatek.py +13 -39
  49. KekikStream/Plugins/Full4kizle.py +33 -133
  50. KekikStream/Plugins/FullHDFilm.py +23 -93
  51. KekikStream/Plugins/FullHDFilmizlesene.py +10 -29
  52. KekikStream/Plugins/HDFilmCehennemi.py +27 -66
  53. KekikStream/Plugins/JetFilmizle.py +19 -20
  54. KekikStream/Plugins/KultFilmler.py +16 -50
  55. KekikStream/Plugins/RecTV.py +47 -85
  56. KekikStream/Plugins/SelcukFlix.py +29 -47
  57. KekikStream/Plugins/SetFilmIzle.py +28 -84
  58. KekikStream/Plugins/SezonlukDizi.py +27 -59
  59. KekikStream/Plugins/Sinefy.py +37 -100
  60. KekikStream/Plugins/SinemaCX.py +12 -18
  61. KekikStream/Plugins/Sinezy.py +12 -13
  62. KekikStream/Plugins/SuperFilmGeldi.py +8 -13
  63. KekikStream/Plugins/UgurFilm.py +14 -14
  64. KekikStream/Plugins/Watch32.py +42 -74
  65. KekikStream/Plugins/YabanciDizi.py +33 -87
  66. {kekikstream-2.4.1.dist-info → kekikstream-2.4.3.dist-info}/METADATA +1 -1
  67. kekikstream-2.4.3.dist-info/RECORD +93 -0
  68. {kekikstream-2.4.1.dist-info → kekikstream-2.4.3.dist-info}/WHEEL +1 -1
  69. kekikstream-2.4.1.dist-info/RECORD +0 -93
  70. {kekikstream-2.4.1.dist-info → kekikstream-2.4.3.dist-info}/entry_points.txt +0 -0
  71. {kekikstream-2.4.1.dist-info → kekikstream-2.4.3.dist-info}/licenses/LICENSE +0 -0
  72. {kekikstream-2.4.1.dist-info → kekikstream-2.4.3.dist-info}/top_level.txt +0 -0
@@ -187,54 +187,55 @@ class SelcukFlix(PluginBase):
187
187
 
188
188
  next_data_text = sel.select_text("script#__NEXT_DATA__")
189
189
  if not next_data_text:
190
- return SeriesInfo(url=url, title=sel.select_text("h1") or "Bilinmeyen")
190
+ return SeriesInfo(url=url, title=self.clean_title(sel.select_text("h1")) or "Bilinmeyen")
191
191
 
192
192
  try:
193
193
  next_data = json.loads(next_data_text)
194
194
  secure_data_raw = next_data["props"]["pageProps"].get("secureData")
195
195
  if not secure_data_raw:
196
- return SeriesInfo(url=url, title=sel.select_text("h1") or "Bilinmeyen")
196
+ return SeriesInfo(url=url, title=self.clean_title(sel.select_text("h1")) or "Bilinmeyen")
197
197
 
198
198
  # Clean possible quotes from string before decoding
199
199
  if isinstance(secure_data_raw, str):
200
200
  secure_data_raw = secure_data_raw.strip('"')
201
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)
202
+ content_details = json.loads(base64.b64decode(secure_data_raw).decode('utf-8'))
203
+ if isinstance(content_details, str): content_details = json.loads(content_details)
208
204
 
209
- print(f"DEBUG: type(content_details)={type(content_details)}")
210
205
  item = content_details.get("contentItem", {})
211
- print(f"DEBUG: type(item)={type(item)}")
212
206
  related_results = content_details.get("RelatedResults", {})
213
207
 
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")
208
+ title = self.clean_title(item.get("original_title") or item.get("culture_title") or item.get("originalTitle") or "")
209
+ poster = self.clean_image_url(item.get("poster_url") or item.get("posterUrl") or item.get("face_url"))
210
+ description = item.get("description") or item.get("used_description")
211
+ rating = str(item.get("imdb_point") or item.get("imdbPoint") or "")
212
+ year = str(item.get("release_year") or item.get("releaseYear") or "")
213
+ duration = item.get("total_minutes") or item.get("totalMinutes")
220
214
 
221
215
  tags = []
222
216
  tags_raw = item.get("category_names") or item.get("categoryNames") or item.get("categories")
223
217
  if isinstance(tags_raw, str):
224
- tags = [t.strip() for t in tags_raw.split(",")]
218
+ tags = [t.strip() for t in tags_raw.split(",") if t.strip()]
225
219
  elif isinstance(tags_raw, list):
226
220
  tags = [c.get("title") if isinstance(c, dict) else str(c) for c in tags_raw]
227
221
 
228
222
  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
223
  casts_data = related_results.get("getSerieCastsById") or related_results.get("getMovieCastsById")
235
224
  if casts_data and isinstance(casts_data, dict) and casts_data.get("result"):
236
225
  actors = [cast.get("name") for cast in casts_data["result"] if cast.get("name")]
237
226
 
227
+ common_info = {
228
+ "url" : url,
229
+ "poster" : poster,
230
+ "title" : title or "Bilinmiyor",
231
+ "description" : description,
232
+ "tags" : tags,
233
+ "rating" : rating,
234
+ "year" : year,
235
+ "actors" : actors,
236
+ "duration" : duration
237
+ }
238
+
238
239
  series_data = related_results.get("getSerieSeasonAndEpisodes")
239
240
  if series_data and isinstance(series_data, dict) and series_data.get("result"):
240
241
  episodes = []
@@ -249,31 +250,12 @@ class SelcukFlix(PluginBase):
249
250
  title = ep.get("ep_text") or ep.get("epText") or "",
250
251
  url = self.fix_url(ep_slug)
251
252
  ))
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
- )
253
+ return SeriesInfo(**common_info, episodes=episodes)
254
+
255
+ return MovieInfo(**common_info)
256
+
257
+ except Exception:
258
+ return SeriesInfo(url=url, title=self.clean_title(sel.select_text("h1")) or "Bilinmeyen")
277
259
 
278
260
  except Exception:
279
261
  return SeriesInfo(url=url, title=self.clean_title(sel.select_text("h1")) or "Bilinmeyen")
@@ -125,95 +125,39 @@ class SetFilmIzle(PluginBase):
125
125
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
126
126
  istek = self.cloudscraper.get(url)
127
127
  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
128
 
129
+ title = self.clean_title(secici.select_text("h1") or secici.select_text(".titles h1") or secici.select_attr("meta[property='og:title']", "content"))
130
+ poster = secici.select_poster("div.poster img")
135
131
  description = secici.select_text("div.wp-content p")
132
+ rating = secici.select_text("b#repimdb strong") or secici.regex_first(r"([\d.]+)", secici.select_text("div.imdb"))
133
+ year = secici.extract_year("div.extra span.valor")
134
+ tags = secici.select_texts("div.sgeneros a")
135
+ duration = int(secici.regex_first(r"(\d+)", secici.select_text("span.runtime")) or 0)
136
+ actors = secici.select_texts("span.valor a[href*='/oyuncu/']")
137
+
138
+ common_info = {
139
+ "url" : url,
140
+ "poster" : self.fix_url(poster) if poster else None,
141
+ "title" : title or "Bilinmiyor",
142
+ "description" : description,
143
+ "tags" : tags,
144
+ "rating" : rating,
145
+ "year" : str(year) if year else None,
146
+ "duration" : duration,
147
+ "actors" : actors
148
+ }
136
149
 
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
-
150
+ if "/dizi/" in url:
172
151
  episodes = []
173
152
  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)
183
-
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
- )
153
+ href = secici.select_attr("h4.episodiotitle a", "href", ep_item)
154
+ name = secici.select_direct_text("h4.episodiotitle a", ep_item)
155
+ if href and name:
156
+ s, e = secici.extract_season_episode(name)
157
+ episodes.append(Episode(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
158
+ return SeriesInfo(**common_info, episodes=episodes)
159
+
160
+ return MovieInfo(**common_info)
217
161
 
218
162
  async def load_links(self, url: str) -> list[ExtractResult]:
219
163
  istek = await self.httpx.get(url)
@@ -70,7 +70,7 @@ class SezonlukDizi(PluginBase):
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 = []
@@ -92,63 +92,31 @@ 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(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
152
120
 
153
121
  return SeriesInfo(
154
122
  url = url,
@@ -157,7 +125,7 @@ class SezonlukDizi(PluginBase):
157
125
  description = description,
158
126
  tags = tags,
159
127
  rating = rating,
160
- year = year,
128
+ year = str(year) if year else None,
161
129
  episodes = episodes,
162
130
  actors = actors
163
131
  )
@@ -135,114 +135,51 @@ class Sinefy(PluginBase):
135
135
  pass
136
136
  return []
137
137
 
138
- async def load_item(self, url: str) -> SeriesInfo:
138
+ async def load_item(self, url: str) -> SeriesInfo | MovieInfo:
139
139
  resp = await self.httpx.get(url)
140
140
  sel = HTMLHelper(resp.text)
141
141
 
142
- title = sel.select_text("h1")
143
-
144
- poster_info = sel.select_attr("div.ui.items img", "data-srcset")
145
- poster = None
146
- if poster_info:
147
- parts = str(poster_info).split(",")
148
- for p in parts:
149
- if "1x" in p:
150
- poster = p.strip().split(" ")[0]
151
- break
152
-
142
+ title = sel.select_direct_text("h1")
143
+ poster_attr = sel.select_attr("img.series-profile-thumb", "data-srcset") or sel.select_attr("img.series-profile-thumb", "srcset")
144
+ if poster_attr:
145
+ # "url 1x, url 2x" -> en sondakini (en yüksek kalite) al
146
+ poster = poster_attr.split(",")[-1].strip().split(" ")[0]
147
+ else:
148
+ poster = sel.select_poster("img.series-profile-thumb")
153
149
  description = sel.select_text("p#tv-series-desc")
150
+ tags = sel.select_texts("div.item.categories a")
151
+ rating = sel.select_text("span.color-imdb")
152
+ actors = sel.select_texts("div.content h5")
153
+ year = sel.extract_year("div.truncate")
154
+ duration = sel.regex_first(r"(\d+)", sel.select_text(".media-meta td:last-child"))
155
+ if duration == year or int(duration) < 40:
156
+ duration = None
157
+
158
+ common_info = {
159
+ "url" : url,
160
+ "poster" : self.fix_url(poster) if poster else None,
161
+ "title" : title or "Bilinmiyor",
162
+ "description" : description,
163
+ "tags" : tags,
164
+ "rating" : rating,
165
+ "year" : str(year) if year else None,
166
+ "actors" : actors,
167
+ "duration" : duration
168
+ }
154
169
 
155
- tags = [a.text(strip=True) for a in sel.select("div.item.categories a") if a.text(strip=True)]
156
-
157
- rating = sel.select_text("span.color-imdb")
158
-
159
- actors = [h5.text(strip=True) for h5 in sel.select("div.content h5") if h5.text(strip=True)]
160
-
161
- year = sel.select_text("span.item.year")
162
- if not year and title:
163
- # Try to extract year from title like "Movie Name(2024)"
164
- year_match = sel.regex_first(r"\((\d{4})\)", title)
165
- if year_match:
166
- year = year_match
167
-
168
170
  episodes = []
169
- episodes_box_list = sel.select("section.episodes-box")
170
-
171
- if episodes_box_list:
172
- episodes_box = episodes_box_list[0]
173
- # Sezon menüsünden sezon linklerini al
174
- season_menu = sel.select("div.ui.vertical.fluid.tabular.menu a.item", episodes_box)
175
-
176
- # Sezon tab içeriklerini al
177
- season_tabs = sel.select("div.ui.tab", episodes_box)
178
-
179
- # Eğer birden fazla sezon varsa, her sezon tab'ından bölümleri çek
180
- if season_tabs:
181
- for idx, season_tab in enumerate(season_tabs):
182
- # Sezon numarasını belirle
183
- current_season_no = idx + 1
184
-
185
- # Menüden sezon numarasını almaya çalış
186
- if idx < len(season_menu):
187
- menu_href = season_menu[idx].attrs.get("href", "")
188
- match = sel.regex_first(r"sezon-(\d+)", menu_href)
189
- if match:
190
- current_season_no = int(match)
191
-
192
- # Bu sezon tab'ından bölüm linklerini çek
193
- ep_links = sel.select("a[href*='bolum']", season_tab)
194
-
195
- seen_urls = set()
196
- for ep_link in ep_links:
197
- href = ep_link.attrs.get("href")
198
- if not href or href in seen_urls:
199
- continue
200
- seen_urls.add(href)
201
-
202
- # Bölüm numarasını URL'den çıkar
203
- ep_no = 0
204
- match_ep = sel.regex_first(r"bolum-(\d+)", href)
205
- if match_ep:
206
- ep_no = int(match_ep)
207
-
208
- # Bölüm başlığını çıkar (önce title attribute, sonra text)
209
- name = ep_link.attrs.get("title", "")
210
- if not name:
211
- name = sel.select_text("div.content div.header", ep_link)
212
- if not name:
213
- name = ep_link.text(strip=True)
214
-
215
- if href and ep_no > 0:
216
- episodes.append(Episode(
217
- season = current_season_no,
218
- episode = ep_no,
219
- title = name.strip() if name else f"{ep_no}. Bölüm",
220
- url = self.fix_url(href)
221
- ))
171
+ for tab in sel.select("div.ui.tab"):
172
+ for link in sel.select("a[href*='bolum']", tab):
173
+ href = link.attrs.get("href")
174
+ if href:
175
+ s, e = sel.extract_season_episode(href)
176
+ name = sel.select_text("div.content div.header", link) or link.text(strip=True)
177
+ episodes.append(Episode(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
222
178
 
223
179
  if episodes:
224
- return SeriesInfo(
225
- title = title,
226
- url = url,
227
- poster = self.fix_url(poster) if poster else None,
228
- description = description,
229
- rating = rating,
230
- tags = tags,
231
- actors = actors,
232
- year = year,
233
- episodes = episodes
234
- )
235
- else:
236
- return MovieInfo(
237
- title = title,
238
- url = url,
239
- poster = self.fix_url(poster) if poster else None,
240
- description = description,
241
- rating = rating,
242
- tags = tags,
243
- actors = actors,
244
- year = year
245
- )
180
+ return SeriesInfo(**common_info, episodes=episodes)
181
+
182
+ return MovieInfo(**common_info)
246
183
 
247
184
  async def load_links(self, url: str) -> list[ExtractResult]:
248
185
  resp = await self.httpx.get(url)
@@ -83,36 +83,30 @@ class SinemaCX(PluginBase):
83
83
  istek = await self.httpx.get(url)
84
84
  secici = HTMLHelper(istek.text)
85
85
 
86
- duration_match = secici.regex_first(r"Süre:.*?(\d+)\s*Dakika")
87
-
86
+ title = secici.select_text("div.f-bilgi h1")
87
+ poster = secici.select_poster("div.resim img")
88
88
  description = secici.select_text("div.ackl div.scroll-liste")
89
-
90
- poster = secici.select_attr("link[rel='image_src']", "href")
91
-
92
- title = secici.select_text("div.f-bilgi h1")
93
-
94
- tags = secici.select_all_text("div.f-bilgi div.tur a")
95
-
96
- year = secici.select_text("div.f-bilgi ul.detay a[href*='yapim']")
97
-
98
- actors = secici.select_all_text("li.oync li.oyuncu-k span.isim")
89
+ rating = secici.select_text("b.puandegistir")
90
+ tags = secici.select_texts("div.f-bilgi div.tur a")
91
+ year = secici.extract_year("ul.detay a[href*='yapim']")
92
+ actors = secici.select_texts("li.oync li.oyuncu-k span.isim")
99
93
 
100
94
  return MovieInfo(
101
95
  url = url,
102
96
  poster = self.fix_url(poster) if poster else None,
103
- title = title,
97
+ title = title or "Bilinmiyor",
104
98
  description = description,
99
+ rating = rating,
105
100
  tags = tags,
106
- year = year,
107
- actors = actors,
108
- duration = int(duration_match) if duration_match else None,
101
+ year = str(year) if year else None,
102
+ actors = actors
109
103
  )
110
104
 
111
105
  async def load_links(self, url: str) -> list[ExtractResult]:
112
106
  istek = await self.httpx.get(url)
113
107
  secici = HTMLHelper(istek.text)
114
108
 
115
- iframe_list = secici.select_all_attr("iframe", "data-vsrc")
109
+ iframe_list = secici.select_attrs("iframe", "data-vsrc")
116
110
 
117
111
  # Sadece fragman varsa /2/ sayfasından dene
118
112
  has_only_trailer = all(
@@ -125,7 +119,7 @@ class SinemaCX(PluginBase):
125
119
  alt_istek = await self.httpx.get(alt_url)
126
120
  alt_istek = await self.httpx.get(alt_url)
127
121
  alt_sec = HTMLHelper(alt_istek.text)
128
- iframe_list = alt_sec.select_all_attr("iframe", "data-vsrc")
122
+ iframe_list = alt_sec.select_attrs("iframe", "data-vsrc")
129
123
 
130
124
  if not iframe_list:
131
125
  return []
@@ -6,7 +6,7 @@ import base64
6
6
  class Sinezy(PluginBase):
7
7
  name = "Sinezy"
8
8
  language = "tr"
9
- main_url = "https://sinezy.fit"
9
+ main_url = "https://sinezy.ink"
10
10
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
11
11
  description = "Yerli ve yabancı film izle! Türkçe Dublaj ve Alt Yazılı Seçenekleriyle full hd film izlemek için En çok tercih edilen adres!"
12
12
 
@@ -83,29 +83,28 @@ class Sinezy(PluginBase):
83
83
  return results
84
84
 
85
85
  async def load_item(self, url: str) -> MovieInfo:
86
- resp = await self.httpx.get(url)
86
+ resp = await self.httpx.get(url)
87
87
  secici = HTMLHelper(resp.text)
88
88
 
89
- title = secici.select_attr("div.detail", "title")
90
- poster = secici.select_attr("div.move_k img", "data-src")
89
+ title = secici.select_attr("div.detail", "title")
90
+ poster = secici.select_poster("div.move_k img")
91
91
  description = secici.select_text("div.desc.yeniscroll p")
92
- rating = secici.select_text("span.info span.imdb")
93
-
94
- tags = secici.select_all_text("div.detail span a")
95
- actors = secici.select_all_text("span.oyn p")
92
+ rating = secici.select_text("span.info span.imdb")
93
+ tags = secici.select_texts("div.detail span a")
94
+ actors = secici.select_texts("span.oyn p")
95
+ year = secici.extract_year()
96
+ duration = secici.regex_first(r"(\d+) Dakika", secici.select_text("div.detail p"))
96
97
 
97
- year = secici.regex_first(r"\b(19\d{2}|20\d{2})\b")
98
- if not year:
99
- year = secici.regex_first(r"\b(19\d{2}|20\d{2})\b", secici.html)
100
98
  return MovieInfo(
101
- title = title,
102
99
  url = url,
103
100
  poster = self.fix_url(poster) if poster else None,
101
+ title = title or "Bilinmiyor",
104
102
  description = description,
105
103
  tags = tags,
106
104
  rating = rating,
105
+ year = str(year) if year else None,
107
106
  actors = actors,
108
- year = year
107
+ duration = duration
109
108
  )
110
109
 
111
110
  async def load_links(self, url: str) -> list[ExtractResult]:
@@ -79,26 +79,21 @@ class SuperFilmGeldi(PluginBase):
79
79
  istek = await self.httpx.get(url)
80
80
  secici = HTMLHelper(istek.text)
81
81
 
82
- title = secici.select_text("div.title h1") or ""
83
- title = self.clean_title(title.split(" izle")[0]) if title else ""
84
-
85
- poster = secici.select_attr("div.poster img", "src")
86
-
87
- year = secici.extract_year("div.release a")
88
-
82
+ title = self.clean_title(secici.select_text("div.title h1").split(" izle")[0]) if secici.select_text("div.title h1") else ""
83
+ poster = secici.select_poster("div.poster img")
84
+ year = secici.extract_year("div.release a")
89
85
  description = secici.select_text("div.excerpt p")
90
-
91
- tags = secici.select_all_text("div.categories a")
92
- actors = secici.select_all_text("div.actor a")
86
+ tags = secici.select_texts("div.categories a")
87
+ actors = secici.select_texts("div.actor a")
93
88
 
94
89
  return MovieInfo(
95
90
  url = url,
96
91
  poster = self.fix_url(poster) if poster else None,
97
- title = title,
92
+ title = title or "Bilinmiyor",
98
93
  description = description,
99
94
  tags = tags,
100
- year = year,
101
- actors = actors,
95
+ year = str(year) if year else None,
96
+ actors = actors
102
97
  )
103
98
 
104
99
  async def load_links(self, url: str) -> list[ExtractResult]: