KekikStream 2.4.2__py3-none-any.whl → 2.4.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.
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 +71 -164
  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 +11 -12
  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.2.dist-info → kekikstream-2.4.4.dist-info}/METADATA +1 -1
  67. kekikstream-2.4.4.dist-info/RECORD +93 -0
  68. kekikstream-2.4.2.dist-info/RECORD +0 -93
  69. {kekikstream-2.4.2.dist-info → kekikstream-2.4.4.dist-info}/WHEEL +0 -0
  70. {kekikstream-2.4.2.dist-info → kekikstream-2.4.4.dist-info}/entry_points.txt +0 -0
  71. {kekikstream-2.4.2.dist-info → kekikstream-2.4.4.dist-info}/licenses/LICENSE +0 -0
  72. {kekikstream-2.4.2.dist-info → kekikstream-2.4.4.dist-info}/top_level.txt +0 -0
@@ -98,151 +98,51 @@ class Full4kizle(PluginBase):
98
98
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
99
99
  istek = await self.httpx.get(url)
100
100
  helper = HTMLHelper(istek.text)
101
-
102
- title_raw = helper.select_text("h1") or "Bilinmiyor"
103
- title = re.sub(r"(?i)izle", "", title_raw).strip()
104
-
105
- poster_el = helper.select_first(".poster img")
106
- poster = self.fix_url(poster_el.attrs.get("src")) if poster_el else None
107
-
108
- description = helper.select_text(".excerpt p")
109
-
110
- # Robust metadata extraction using Regex
111
-
112
- # Initialize year first
113
- year = None
114
-
115
- # Try .release first (legacy) or directly regex
116
- rel_text = helper.select_text(".release")
117
- if rel_text:
118
- m_y = re.search(r"(\d{4})", rel_text)
119
- if m_y: year = m_y.group(1)
120
-
121
- # Year fallbacks
122
- if not year:
123
- # Try finding year in text like "Yapım: 2024" or just isolated year in release date
124
- m_year = helper.regex_first(r"Yapım:\s*(\d{4})") or helper.regex_first(r"Yıl:\s*(\d{4})")
125
- if m_year:
126
- year = m_year
127
-
128
- # Rating
129
- rating_text = helper.select_text(".imdb-rating")
130
- if rating_text:
131
- rating = rating_text.replace("IMDB Puanı", "").strip()
132
- else:
133
- rating = helper.regex_first(r"IMDB\s*:\s*([\d\.]+)")
134
-
135
- # Duration
136
- duration = None
137
- duration_val = helper.regex_first(r"Süre:\s*(\d+)")
138
- if duration_val:
139
- duration = int(duration_val)
140
101
 
141
- # Actors - Extract from actor links
142
- actors = None
143
- actors_list = []
144
-
145
- # Site uses: <a href=".../oyuncular/...">Actor Name</a>
146
- actor_els = helper.select("a[href*='/oyuncular/']")
147
- if actor_els:
148
- actors_list = [el.text(strip=True) for el in actor_els if el.text(strip=True)]
149
-
150
- # Fallback: Try .cast-list selector
151
- if not actors_list:
152
- actor_els = helper.select(".cast-list .actor-name, .cast-list a")
153
- if actor_els:
154
- actors_list = [el.text(strip=True) for el in actor_els if el.text(strip=True)]
155
-
156
- if actors_list:
157
- actors = ", ".join(actors_list)
158
-
159
-
160
- # Tags (Genres) - Extract from genre links
161
- tags = None
162
- tags_list = []
163
-
164
- # Site uses: <a href=".../tur/...">Genre Name</a> or <a href=".../Kategori/tur/...">
165
- tag_els = helper.select("a[href*='/tur/'], a[href*='/Kategori/tur/']")
166
- if tag_els:
167
- tags_list = [el.text(strip=True) for el in tag_els if el.text(strip=True)]
168
-
169
- # Fallback: Try .genres selector
170
- if not tags_list:
171
- tag_els = helper.select(".genres a, .genre a")
172
- if tag_els:
173
- tags_list = [el.text(strip=True) for el in tag_els if el.text(strip=True)]
174
-
175
- # Remove duplicates while preserving order
176
- if tags_list:
177
- seen = set()
178
- unique_tags = []
179
- for tag in tags_list:
180
- if tag not in seen:
181
- seen.add(tag)
182
- unique_tags.append(tag)
183
- tags = unique_tags if unique_tags else None
102
+ title = self.clean_title(helper.select_text("h1"))
103
+ poster = helper.select_poster(".poster img")
104
+ description = helper.select_text(".excerpt p")
105
+ year = helper.extract_year(".release", ".movie-info")
106
+ rating = helper.regex_first(r"([\d\.]+)", helper.select_text(".imdb-rating"))
107
+ duration = int(helper.regex_first(r"(\d+)", helper.select_text(".movie-info")) or 0)
108
+ tags = helper.select_texts("a[href*='/tur/'], a[href*='/Kategori/tur/']")
109
+ actors = helper.select_texts("a[href*='/oyuncular/']") or helper.select_texts(".cast-list .actor-name, .cast-list a")
184
110
 
185
-
186
- # Check for Episodes to decide if Series or Movie
111
+ # Bölüm linklerini kontrol et
187
112
  ep_elements = helper.select(".parts-middle a, .parts-middle .part.active")
188
-
113
+
189
114
  if not ep_elements:
190
- # Movie
191
115
  return MovieInfo(
192
116
  url = url,
193
- title = title,
117
+ title = title or "Bilinmiyor",
194
118
  description = description,
195
- poster = poster,
196
- year = year,
119
+ poster = self.fix_url(poster) if poster else None,
120
+ year = str(year) if year else None,
197
121
  rating = rating,
198
122
  duration = duration,
199
123
  tags = tags,
200
124
  actors = actors
201
125
  )
202
- else:
203
- # Series
204
- episodes = []
205
- for i, el in enumerate(ep_elements):
206
- ep_name = helper.select_text(".part-name", el) or f"Bölüm {i+1}"
207
- ep_href = el.attrs.get("href")
208
- if not ep_href:
209
- ep_href = url # Current page if href is empty/active?
210
- ep_href = self.fix_url(ep_href)
211
-
212
- # Parse season/episode from name if possible
213
- # Kotlin: find digit for season, substringAfter("Sezon") digit for episode
214
- season = 1
215
- episode = i + 1
216
-
217
- # Simple heuristic similar to Kotlin
218
- # "1. Sezon 5. Bölüm"
219
- s_match = re.search(r"(\d+)\.\s*Sezon", ep_name)
220
- e_match = re.search(r"(\d+)\.\s*Bölüm", ep_name)
221
-
222
- if s_match:
223
- season = int(s_match.group(1))
224
- if e_match:
225
- episode = int(e_match.group(1))
226
-
227
- episodes.append(Episode(
228
- season = season,
229
- episode = episode,
230
- title = ep_name,
231
- url = ep_href
232
- ))
233
-
234
- return SeriesInfo(
235
- url = url,
236
- title = title,
237
- description = description,
238
- poster = poster,
239
- year = year,
240
- rating = rating,
241
- duration = duration,
242
- tags = tags,
243
- actors = actors,
244
- episodes = episodes
245
- )
126
+
127
+ episodes = []
128
+ for i, el in enumerate(ep_elements):
129
+ name = helper.select_text(".part-name", el) or f"Bölüm {i+1}"
130
+ href = el.attrs.get("href") or url
131
+ s, e = helper.extract_season_episode(name)
132
+ episodes.append(Episode(season=s or 1, episode=e or (i + 1), title=name, url=self.fix_url(href)))
133
+
134
+ return SeriesInfo(
135
+ url = url,
136
+ title = title or "Bilinmiyor",
137
+ description = description,
138
+ poster = self.fix_url(poster) if poster else None,
139
+ year = str(year) if year else None,
140
+ rating = rating,
141
+ duration = duration,
142
+ tags = tags,
143
+ actors = actors,
144
+ episodes = episodes
145
+ )
246
146
 
247
147
  async def load_links(self, url: str) -> list[ExtractResult]:
248
148
  istek = await self.httpx.get(url)
@@ -87,108 +87,38 @@ class FullHDFilm(PluginBase):
87
87
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
88
88
  istek = await self.httpx.get(url)
89
89
  secici = HTMLHelper(istek.text)
90
- html_text = istek.text
91
-
92
- title = secici.select_text("h1") or ""
93
-
94
- poster = secici.select_attr("div.poster img", "src") or ""
95
- poster = self.fix_url(poster)
96
-
97
- actors = []
98
- actors_text = secici.select_text("div.oyuncular.info")
99
- if actors_text:
100
- actors_text = actors_text.replace("Oyuncular:", "").strip()
101
- actors = [a.strip() for a in actors_text.split(",")]
102
-
103
- # Year: önce div.yayin-tarihi.info dene
104
- year = secici.extract_year("div.yayin-tarihi.info")
105
-
106
- # Fallback: h1'in parent'ından (2019) formatında ara
107
- if not year:
108
- # Parent'ın tüm text içeriğinden yıl çıkar
109
- title_text = secici.select_text("h1")
110
- if title_text:
111
- # h1 parent'ındaki (2019) gibi text'i bul
112
- year = secici.regex_first(r"\((\d{4})\)")
113
-
114
- tags = secici.select_all_text("div.tur.info a")
115
-
116
- # Rating: regex
117
- rating_text = secici.select_text("div.imdb") or ""
118
- rating = HTMLHelper(rating_text).regex_first(r"IMDb\s*([\d\.]+)")
119
-
120
- # Description: önce div.film dene, yoksa og:description meta tag'ını kullan
121
- description = secici.select_text("div.film")
122
-
123
- # Fallback: og:description meta tag'ı
124
- if not description:
125
- og_desc = secici.select_attr("meta[property='og:description']", "content")
126
- if og_desc:
127
- description = og_desc
128
-
129
- # Kotlin referansı: URL'de -dizi kontrolü veya tags içinde "dizi" kontrolü
90
+
91
+ title = self.clean_title(secici.select_text("h1"))
92
+ poster = secici.select_poster("div.poster img")
93
+ description = secici.select_text("div.film") or secici.select_attr("meta[property='og:description']", "content")
94
+ year = secici.extract_year("div.yayin-tarihi.info") or secici.regex_first(r"\((\d{4})\)")
95
+ tags = secici.select_texts("div.tur.info a")
96
+ rating = secici.regex_first(r"IMDb\s*([\d\.]+)", secici.select_text("div.imdb"))
97
+ actors = secici.select_direct_text("div.oyuncular")
98
+
99
+ # Kotlin referansı: URL'de -dizi veya tags içinde dizi kontrolü
130
100
  is_series = "-dizi" in url.lower() or any("dizi" in tag.lower() for tag in tags)
131
101
 
132
102
  if is_series:
133
103
  episodes = []
134
- part_elements = secici.select("li.psec")
135
-
136
- # pdata değerlerini çıkar
137
- pdata_matches = HTMLHelper(html_text).regex_all(r"pdata\['([^']+)'\]\s*=\s*'([^']+)'")
138
-
139
- for idx, el in enumerate(part_elements):
140
- part_id = el.attrs.get("id")
141
- part_name = secici.select_text("a", el)
142
-
143
- if not part_name:
144
- continue
145
-
146
- # Fragman'ları atla
147
- if "fragman" in part_name.lower() or (part_id and "fragman" in part_id.lower()):
148
- continue
104
+ for idx, el in enumerate(secici.select("li.psec")):
105
+ part_id = el.attrs.get("id")
106
+ part_name = secici.select_text("a", el) or ""
107
+ if not part_name or "fragman" in part_name.lower(): continue
149
108
 
150
- # Sezon ve bölüm numarası çıkar
151
- sz_val = HTMLHelper(part_id.lower() if part_id else "").regex_first(r'(\d+)\s*sezon')
152
- ep_val = HTMLHelper(part_name).regex_first(r'^(\d+)\.')
153
-
154
- sz_num = int(sz_val) if sz_val and sz_val.isdigit() else 1
155
- ep_num = int(ep_val) if ep_val and ep_val.isdigit() else idx + 1
156
-
157
- # pdata'dan video URL'si çık (varsa)
158
- video_url = url # Varsayılan olarak ana URL kullan
159
- if idx < len(pdata_matches):
160
- video_url = pdata_matches[idx][1] if pdata_matches[idx][1] else url
161
-
162
- episodes.append(Episode(
163
- season = sz_num,
164
- episode = ep_num,
165
- title = f"{sz_num}. Sezon {ep_num}. Bölüm",
166
- url = url # Bölüm URL'leri load_links'te işlenecek
167
- ))
109
+ s, e = secici.extract_season_episode(f"{part_id} {part_name}")
110
+ episodes.append(Episode(season=s or 1, episode=e or (idx+1), title=f"{s or 1}. Sezon {e or idx+1}. Bölüm", url=url))
168
111
 
169
112
  return SeriesInfo(
170
- url = url,
171
- poster = poster,
172
- title = self.clean_title(title) if title else "",
173
- description = description,
174
- tags = tags,
175
- year = year,
176
- actors = actors,
177
- rating = rating,
178
- episodes = episodes
179
- )
180
- else:
181
- return MovieInfo(
182
- url = url,
183
- poster = poster,
184
- title = self.clean_title(title) if title else "",
185
- description = description,
186
- tags = tags,
187
- year = year,
188
- actors = actors,
189
- rating = rating,
113
+ url=url, poster=self.fix_url(poster) if poster else None, title=title or "", description=description,
114
+ tags=tags, year=str(year) if year else None, actors=actors, rating=rating, episodes=episodes
190
115
  )
191
116
 
117
+ return MovieInfo(
118
+ url=url, poster=self.fix_url(poster) if poster else None, title=title or "", description=description,
119
+ tags=tags, year=str(year) if year else None, actors=actors, rating=rating
120
+ )
121
+
192
122
  def _get_iframe(self, source_code: str) -> str:
193
123
  """Base64 kodlu iframe'i çözümle"""
194
124
  script_val = HTMLHelper(source_code).regex_first(r'<script[^>]*>(PCEtLWJhc2xpazp[^<]*)</script>')
@@ -81,43 +81,24 @@ class FullHDFilmizlesene(PluginBase):
81
81
  async def load_item(self, url: str) -> MovieInfo:
82
82
  istek = await self.httpx.get(url)
83
83
  secici = HTMLHelper(istek.text)
84
- html_text = istek.text
85
-
86
- # Title: normalize-space yerine doğrudan class ile
87
- title = secici.select_text("div.izle-titles") or ""
88
-
89
- poster = secici.select_attr("div img[data-src]", "data-src") or ""
90
- poster = poster.strip()
91
-
92
- description = secici.select_text("div.ozet-ic p") or ""
93
-
94
- tags = secici.select_all_text("a[rel='category tag']")
95
-
96
- # Rating: regex ile sayısal değeri yakala
97
- rating_text = secici.select_text("div.puanx-puan") or ""
98
- rating = secici.regex_first(r"(\d+\.\d+|\d+)", rating_text)
99
-
100
- # Year: ilk yıl formatında değer
101
- year_text = secici.select_text("div.dd a.category") or ""
102
- year = secici.regex_first(r"(\d{4})", year_text)
103
-
104
- # Actors: nth-child yerine tüm li'leri alıp 2. index
105
- lis = secici.select("div.film-info ul li")
106
- actors = []
107
- if len(lis) >= 2:
108
- actors = secici.select_all_text("a > span", lis[1])
109
84
 
110
- # Duration: regex ile yakala (örn: 201 dk)
111
- duration = secici.regex_first(r"(\d+)\s*(?:dk|dakika)", html_text)
85
+ title = self.clean_title(secici.select_text("div.izle-titles"))
86
+ poster = secici.select_poster("div img[data-src]")
87
+ description = secici.select_text("div.ozet-ic p")
88
+ tags = secici.select_texts("a[rel='category tag']")
89
+ rating = secici.regex_first(r"(\d+\.\d+|\d+)", secici.select_text("div.puanx-puan"))
90
+ year = secici.extract_year("div.dd a.category")
91
+ actors = secici.select_texts("a > span", secici.select_first("div.film-info ul li:nth-child(2)"))
92
+ duration = int(secici.regex_first(r"(\d+)", secici.select_text("div.film-info ul li:nth-child(4)")) or 0)
112
93
 
113
94
  return MovieInfo(
114
95
  url = url,
115
96
  poster = self.fix_url(poster) if poster else None,
116
- title = title,
97
+ title = title or "Bilinmiyor",
117
98
  description = description,
118
99
  tags = tags,
119
100
  rating = rating,
120
- year = year,
101
+ year = str(year) if year else None,
121
102
  actors = actors,
122
103
  duration = duration
123
104
  )
@@ -78,77 +78,38 @@ class HDFilmCehennemi(PluginBase):
78
78
  istek = await self.httpx.get(url, headers = {"Referer": f"{self.main_url}/"})
79
79
  secici = HTMLHelper(istek.text)
80
80
 
81
- title = secici.select_text("h1.section-title") or ""
82
-
83
- poster = secici.select_attr("aside.post-info-poster img.lazyload", "data-src") or ""
84
- poster = poster.strip()
85
-
86
- description = secici.select_text("article.post-info-content > p") or ""
87
-
88
- tags = secici.select_all_text("div.post-info-genres a")
89
-
90
- rating = secici.select_text("div.post-info-imdb-rating span") or ""
91
-
92
- year = secici.select_text("div.post-info-year-country a") or ""
93
-
94
- actors = secici.select_all_text("div.post-info-cast a > strong")
95
-
96
- duration_str = secici.select_text("div.post-info-duration") or "0"
97
- duration_str = duration_str.replace("dakika", "").strip()
98
-
99
- try:
100
- duration_val = HTMLHelper(duration_str).regex_first(r'\d+')
101
- duration_minutes = int(duration_val) if duration_val else 0
102
- except Exception:
103
- duration_minutes = 0
104
-
105
- # Dizi mi film mi kontrol et (Kotlin referansı: div.seasons kontrolü)
106
- is_series = len(secici.select("div.seasons")) > 0
107
-
108
- if is_series:
81
+ title = self.clean_title(secici.select_text("h1.section-title"))
82
+ poster = secici.select_poster("aside.post-info-poster img.lazyload")
83
+ description = secici.select_text("article.post-info-content > p")
84
+ tags = secici.select_texts("div.post-info-genres a")
85
+ rating = secici.select_text("div.post-info-imdb-rating span")
86
+ rating = rating.split("(")[0] if rating else None
87
+ year = secici.select_text("div.post-info-year-country a")
88
+ actors = secici.select_texts("div.post-info-cast a > strong")
89
+ duration = int(secici.regex_first(r"(\d+)", secici.select_text("div.post-info-duration")) or 0)
90
+
91
+ # Dizi mi film mi kontrol et
92
+ ep_links = secici.select("div.seasons-tab-content a")
93
+
94
+ if ep_links:
109
95
  episodes = []
110
- for ep in secici.select("div.seasons-tab-content a"):
111
- ep_name = secici.select_text("h4", ep)
112
- ep_href = ep.attrs.get("href")
113
-
114
- if ep_name and ep_href:
115
- # Regex ile sezon ve bölüm numarası çıkar
116
- ep_val = HTMLHelper(ep_name).regex_first(r'(\d+)\.\s*Bölüm')
117
- sz_val = HTMLHelper(ep_name).regex_first(r'(\d+)\.\s*Sezon')
118
- ep_num = int(ep_val) if ep_val and ep_val.isdigit() else 1
119
- sz_num = int(sz_val) if sz_val and sz_val.isdigit() else 1
120
-
121
- episodes.append(Episode(
122
- season = sz_num,
123
- episode = ep_num,
124
- title = ep_name,
125
- url = self.fix_url(ep_href)
126
- ))
96
+ for ep in ep_links:
97
+ name = secici.select_text("h4", ep)
98
+ href = ep.attrs.get("href")
99
+ if name and href:
100
+ s, e = secici.extract_season_episode(name)
101
+ episodes.append(Episode(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
127
102
 
128
103
  return SeriesInfo(
129
- url = url,
130
- poster = self.fix_url(poster) if poster else None,
131
- title = self.clean_title(title),
132
- description = description,
133
- tags = tags,
134
- rating = rating,
135
- year = year,
136
- actors = actors,
137
- episodes = episodes
138
- )
139
- else:
140
- return MovieInfo(
141
- url = url,
142
- poster = self.fix_url(poster) if poster else None,
143
- title = self.clean_title(title),
144
- description = description,
145
- tags = tags,
146
- rating = rating,
147
- year = year,
148
- actors = actors,
149
- duration = duration_minutes
104
+ url=url, poster=self.fix_url(poster) if poster else None, title=title or "Bilinmiyor",
105
+ description=description, tags=tags, rating=rating, year=year, actors=actors, episodes=episodes
150
106
  )
151
107
 
108
+ return MovieInfo(
109
+ url=url, poster=self.fix_url(poster) if poster else None, title=title or "Bilinmiyor",
110
+ description=description, tags=tags, rating=rating, year=year, actors=actors, duration=duration
111
+ )
112
+
152
113
  def generate_random_cookie(self):
153
114
  return "".join(random.choices(string.ascii_letters + string.digits, k=16))
154
115
 
@@ -99,35 +99,34 @@ class JetFilmizle(PluginBase):
99
99
  istek = await self.httpx.get(url)
100
100
  secici = HTMLHelper(istek.text)
101
101
 
102
- title = self.clean_title(secici.select_text("div.movie-exp-title")) if secici.select_text("div.movie-exp-title") else None
103
-
104
- poster = secici.select_poster("section.movie-exp img")
105
- poster = poster.strip() if poster else None
106
-
102
+ title = self.clean_title(secici.select_text("div.movie-exp-title"))
103
+ poster = secici.select_poster("section.movie-exp img")
107
104
  description = secici.select_text("section.movie-exp p.aciklama")
108
-
109
- tags = secici.select_all_text("section.movie-exp div.catss a")
110
-
111
- rating = secici.select_text("section.movie-exp div.imdb_puan span")
112
-
113
- year = secici.extract_year("div.yap")
114
-
115
- actors = secici.select_all_text("div[itemprop='actor'] a span")
116
- if not actors: # Fallback to img alt
117
- actors = [img.attrs.get("alt") for img in secici.select("div.oyuncular div.oyuncu img") if img.attrs.get("alt")]
118
-
119
- duration = secici.regex_first(r"(\d+)\s*dk", istek.text)
105
+ tags = secici.select_texts("section.movie-exp div.catss a")
106
+ rating = secici.select_text("section.movie-exp div.imdb_puan span")
107
+ year = secici.meta_value("Yayın Yılı")
108
+ actors = secici.select_texts("div[itemprop='actor'] a span") or [img.attrs.get("alt") for img in secici.select("div.oyuncular div.oyuncu img") if img.attrs.get("alt")]
109
+ duration = secici.meta_value("Süre")
110
+ duration = duration.split() if duration else None
111
+
112
+ total_minutes = 0
113
+ if duration:
114
+ for i, p in enumerate(duration):
115
+ if p == "saat":
116
+ total_minutes += int(duration[i-1]) * 60
117
+ elif p == "dakika":
118
+ total_minutes += int(duration[i-1])
120
119
 
121
120
  return MovieInfo(
122
121
  url = url,
123
122
  poster = self.fix_url(poster) if poster else None,
124
- title = title,
123
+ title = title or "Bilinmiyor",
125
124
  description = description,
126
125
  tags = tags,
127
126
  rating = rating,
128
- year = year,
127
+ year = str(year) if year else None,
129
128
  actors = actors,
130
- duration = int(duration) if duration else None
129
+ duration = int(total_minutes) if duration else None
131
130
  )
132
131
 
133
132
  async def load_links(self, url: str) -> list[ExtractResult]:
@@ -78,68 +78,34 @@ class KultFilmler(PluginBase):
78
78
  istek = await self.httpx.get(url)
79
79
  secici = HTMLHelper(istek.text)
80
80
 
81
- title = secici.select_attr("div.film-bilgileri img", "alt") or secici.select_attr("[property='og:title']", "content")
82
- poster = self.fix_url(secici.select_attr("[property='og:image']", "content")) if secici.select_attr("[property='og:image']", "content") else None
83
-
81
+ title = self.clean_title(secici.select_attr("div.film-bilgileri img", "alt") or secici.select_attr("[property='og:title']", "content"))
82
+ poster = self.fix_url(secici.select_attr("[property='og:image']", "content"))
84
83
  description = secici.select_text("div.description")
84
+ tags = secici.select_texts("ul.post-categories a")
85
+ year = secici.extract_year("li.release span a")
86
+ duration = int(secici.regex_first(r"(\d+)", secici.select_text("li.time span")) or 0)
87
+ rating = secici.regex_first(r"(\d+\.\d+|\d+)", secici.select_text("div.imdb-count"))
88
+ actors = secici.select_texts("div.actors a")
85
89
 
86
- tags = [a.text(strip=True) for a in secici.select("ul.post-categories a") if a.text(strip=True)]
87
-
88
- # HTML analizine göre güncellenen alanlar
89
- year = secici.select_text("li.release span a")
90
-
91
- time_text = secici.select_text("li.time span")
92
- duration = secici.regex_first(r"(\d+)", time_text) if time_text else None
93
-
94
- rating_text = secici.select_text("div.imdb-count")
95
- rating = secici.regex_first(r"(\d+\.\d+|\d+)", rating_text) if rating_text else None
96
-
97
- actors = [a.text(strip=True) for a in secici.select("div.actors a") if a.text(strip=True)]
98
-
99
- # Dizi mi kontrol et
100
90
  if "/dizi/" in url:
101
91
  episodes = []
102
92
  for bolum in secici.select("div.episode-box"):
103
- ep_href = secici.select_attr("div.name a", "href", bolum)
104
-
93
+ href = secici.select_attr("div.name a", "href", bolum)
105
94
  ssn_detail = secici.select_text("span.episodetitle", bolum) or ""
106
95
  ep_detail = secici.select_text("span.episodetitle b", bolum) or ""
107
-
108
- ep_name = f"{ssn_detail} - {ep_detail}"
109
-
110
- if ep_href:
111
- ep_season = secici.regex_first(r"(\d+)\.", ssn_detail)
112
- ep_episode = secici.regex_first(r"(\d+)\.", ep_detail)
113
-
114
- episodes.append(Episode(
115
- season = int(ep_season) if ep_season else 1,
116
- episode = int(ep_episode) if ep_episode else 1,
117
- title = ep_name.strip(" -"),
118
- url = self.fix_url(ep_href),
119
- ))
96
+ if href:
97
+ s, e = secici.extract_season_episode(f"{ssn_detail} {ep_detail}")
98
+ name = f"{ssn_detail} - {ep_detail}".strip(" -")
99
+ episodes.append(Episode(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
120
100
 
121
101
  return SeriesInfo(
122
- url = url,
123
- poster = poster,
124
- title = self.clean_title(title) if title else "",
125
- description = description,
126
- tags = tags,
127
- year = year,
128
- actors = actors,
129
- rating = rating,
130
- episodes = episodes,
102
+ url=url, poster=poster, title=title or "Bilinmiyor", description=description,
103
+ tags=tags, year=str(year) if year else None, actors=actors, rating=rating, episodes=episodes
131
104
  )
132
105
 
133
106
  return MovieInfo(
134
- url = url,
135
- poster = poster,
136
- title = self.clean_title(title) if title else "",
137
- description = description,
138
- tags = tags,
139
- year = year,
140
- rating = rating,
141
- actors = actors,
142
- duration = int(duration) if duration else None,
107
+ url=url, poster=poster, title=title or "Bilinmiyor", description=description,
108
+ tags=tags, year=str(year) if year else None, rating=rating, actors=actors, duration=duration
143
109
  )
144
110
 
145
111
  def _get_iframe(self, source_code: str) -> str: