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
@@ -69,173 +69,65 @@ class DiziYou(PluginBase):
69
69
  async def load_item(self, url: str) -> SeriesInfo:
70
70
  istek = await self.httpx.get(url)
71
71
  secici = HTMLHelper(istek.text)
72
- html_text = istek.text
73
72
 
74
- # Title - div.title h1 içinde
75
- title = (secici.select_text("div.title h1") or "").strip()
76
-
77
- # Fallback: Eğer title boşsa URL'den çıkar (telif kısıtlaması olan sayfalar için)
78
- if not title:
79
- # URL'den slug'ı al: https://www.diziyou.one/jasmine/ -> jasmine -> Jasmine
80
- slug = url.rstrip('/').split('/')[-1]
81
- title = slug.replace('-', ' ').title()
82
-
83
- # Poster
84
- poster_src = secici.select_attr("div.category_image img", "src") or secici.select_attr("meta[property='og:image']", "content")
85
- poster = self.fix_url(poster_src) if poster_src else ""
86
-
87
- # Year - regex ile çıkarma (xpath yerine)
88
- year = secici.regex_first(r"(?is)Yapım Yılı.*?(\d{4})", secici.html)
89
-
90
- description_el = secici.select("div.diziyou_desc") or secici.select("div#icerikcat")
91
- description = ""
92
- if description_el:
93
- # Scriptleri temizle
94
- for script in secici.select("script", description_el[0]):
95
- script.decompose()
96
- description = secici.select_text(None, description_el[0])
97
-
98
- tags = [secici.select_text(None, a) for a in secici.select("div.genres a") if secici.select_text(None, a)]
99
-
100
- # Rating - daha spesifik regex ile
101
- rating = secici.regex_first(r"(?is)IMDB\s*:\s*</span>([0-9.]+)", secici.html)
102
-
103
- # Actors - regex ile
104
- actors_raw = secici.regex_first(r"(?is)Oyuncular.*?</span>([^<]+)", secici.html)
105
- actors = [actor.strip() for actor in actors_raw.split(",") if actor.strip()] if actors_raw else []
73
+ poster = secici.select_poster("div.category_image img")
74
+ title = secici.select_text("h1.title-border")
75
+ description = secici.select_direct_text("div#icerikcatright")
76
+ tags = secici.select_texts("div.genres a")
77
+ rating = secici.regex_first(r"(?is)IMDB\s*:\s*</span>([0-9.]+)", secici.html)
78
+ year = secici.extract_year("div#icerikcat2")
79
+ actors = secici.meta_value("Oyuncular", container_selector="div#icerikcat2")
80
+ actors = [a.strip() for a in actors.split(",")] if actors else []
106
81
 
107
82
  episodes = []
108
- # Episodes - div#scrollbar-container a (kısıtlı alan)
109
83
  for link in secici.select("div#scrollbar-container a"):
110
- ep_href = secici.select_attr(None, "href", link)
111
- if not ep_href:
112
- continue
113
-
114
- # Link metni veya alt başlık al
115
- ep_name = (secici.select_text(None, link) or "").strip()
116
- title_child = secici.select_text("div.baslik", link) or secici.select_text("div.bolumismi", link)
117
- if title_child:
118
- ep_name = title_child
119
-
120
- # Önce metin üzerinden sezon/bölüm çıkart
121
- s_val, e_val = HTMLHelper.extract_season_episode(ep_name)
122
-
123
- # URL bazlı kalıplar: -1-sezon-2-bolum gibi
124
- if not (s_val or e_val):
125
- pairs = HTMLHelper(ep_href).regex_all(r"-(\d+)-sezon-(\d+)-bolum")
126
- if pairs:
127
- s_val, e_val = int(pairs[0][0]), int(pairs[0][1])
128
- else:
129
- pairs = HTMLHelper(ep_href).regex_all(r"(\d+)-sezon-(\d+)-bolum")
130
- if pairs:
131
- s_val, e_val = int(pairs[0][0]), int(pairs[0][1])
132
- else:
133
- e_val_str = HTMLHelper(ep_href).regex_first(r"(\d+)-bolum")
134
- if e_val_str:
135
- e_val = int(e_val_str)
136
- # Metin üzerinden son bir deneme
137
- if not e_val:
138
- e_str = HTMLHelper(ep_name).regex_first(r"(\d+)\s*\.\s*[Bb]ölüm")
139
- if e_str:
140
- e_val = int(e_str)
141
- if not s_val:
142
- s_str = HTMLHelper(ep_name).regex_first(r"(\d+)\s*\.\s*[Ss]ezon")
143
- if s_str:
144
- s_val = int(s_str)
145
-
146
- if e_val or HTMLHelper(ep_href).regex_first(r"-\d+-sezon-\d+-bolum"):
147
- episodes.append(Episode(
148
- season = s_val,
149
- episode = e_val,
150
- title = ep_name if ep_name else None,
151
- url = self.fix_url(ep_href),
152
- ))
84
+ href = secici.select_attr(None, "href", link)
85
+ if href:
86
+ name = secici.select_text("div.bolumismi", link).strip("()")
87
+ s, e = secici.extract_season_episode(f"{name} {href}")
88
+ if e:
89
+ episodes.append(Episode(season=s or 1, episode=e, title=name, url=self.fix_url(href)))
153
90
 
154
91
  return SeriesInfo(
155
- url = url,
156
- poster = poster,
157
- title = title,
158
- description = description,
159
- tags = tags,
160
- rating = rating,
161
- year = year,
162
- episodes = episodes,
163
- actors = actors
92
+ url=url, poster=poster, title=title or "Bilinmiyor", description=description,
93
+ tags=tags, rating=rating, year=str(year) if year else None, episodes=episodes, actors=actors
164
94
  )
165
95
 
166
96
  async def load_links(self, url: str) -> list[ExtractResult]:
167
97
  istek = await self.httpx.get(url)
168
98
  secici = HTMLHelper(istek.text)
169
-
170
- # Title ve episode name - None kontrolü ekle
171
- item_title = secici.select_text("div.title h1")
172
- ep_name = secici.select_text("div#bolum-ismi")
173
99
 
174
- # Player src'den item_id çıkar
175
- # Player src'den item_id çıkar - önce özel player seçicisini dene
176
- player_src = None
177
- # Yaygın locatorlar
178
- for sel in ["iframe#diziyouPlayer", "div.player iframe", "iframe[src*='/player/']", "iframe[src*='/episodes/']", "iframe"]:
179
- p = secici.select_attr(sel, "src")
180
- if p and any(x in p.lower() for x in ["/player/", "/episodes/", "diziyou"]):
181
- player_src = p
182
- break
183
-
184
- # Eğer hâlâ bulunamadıysa, varsa bir bölüm sayfasına git ve oradan player'ı çek
185
- if not player_src:
186
- for a in secici.select("a"):
187
- href = secici.select_attr("a", "href", a)
188
- if not href:
189
- continue
190
- if HTMLHelper(href).regex_first(r"(-\d+-sezon-\d+-bolum|/bolum|/episode|/episodes|/play)"):
191
- break
192
-
193
- if not player_src:
194
- return [] # Player bulunamadıysa boş liste döndür
195
-
196
- item_id = player_src.split("/")[-1].replace(".html", "")
197
-
198
- subtitles = []
199
- stream_urls = []
200
-
201
- for secenek in secici.select("span.diziyouOption"):
202
- opt_id = secici.select_attr("span.diziyouOption", "id", secenek)
203
- op_name = secici.select_text("span.diziyouOption", secenek)
204
-
205
- match opt_id:
206
- case "turkceAltyazili":
207
- subtitles.append(Subtitle(
208
- name = op_name,
209
- url = self.fix_url(f"{self.main_url.replace('www', 'storage')}/subtitles/{item_id}/tr.vtt"),
210
- ))
211
- veri = {
212
- "dil": "Orjinal Dil (TR Altyazı)",
213
- "url": f"{self.main_url.replace('www', 'storage')}/episodes/{item_id}/play.m3u8"
214
- }
215
- if veri not in stream_urls:
216
- stream_urls.append(veri)
217
- case "ingilizceAltyazili":
218
- subtitles.append(Subtitle(
219
- name = op_name,
220
- url = self.fix_url(f"{self.main_url.replace('www', 'storage')}/subtitles/{item_id}/en.vtt"),
221
- ))
222
- veri = {
223
- "dil": "Orjinal Dil (EN Altyazı)",
224
- "url": f"{self.main_url.replace('www', 'storage')}/episodes/{item_id}/play.m3u8"
225
- }
226
- if veri not in stream_urls:
227
- stream_urls.append(veri)
228
- case "turkceDublaj":
229
- stream_urls.append({
230
- "dil": "Türkçe Dublaj",
231
- "url": f"{self.main_url.replace('www', 'storage')}/episodes/{item_id}_tr/play.m3u8"
232
- })
100
+ # Player iframe'inden ID'yi yakala
101
+ iframe_src = secici.select_attr("iframe#diziyouPlayer", "src") or secici.select_attr("iframe[src*='/player/']", "src")
102
+ if not iframe_src:
103
+ return []
104
+
105
+ item_id = iframe_src.split("/")[-1].replace(".html", "")
106
+ base_storage = self.main_url.replace("www", "storage")
107
+
108
+ subtitles = []
109
+ for sub in [("turkceAltyazili", "tr", "Türkçe"), ("ingilizceAltyazili", "en", "İngilizce")]:
110
+ if secici.select_first(f"span#{sub[0]}"):
111
+ subtitles.append(Subtitle(
112
+ name = f"{sub[2]} Altyazı",
113
+ url = f"{base_storage}/subtitles/{item_id}/{sub[1]}.vtt"
114
+ ))
233
115
 
234
116
  results = []
235
- for stream in stream_urls:
117
+ # Altyazılı Seçenek (Eğer varsa)
118
+ if secici.select_first("span#turkceAltyazili") or secici.select_first("span#ingilizceAltyazili"):
119
+ results.append(ExtractResult(
120
+ name = "Altyazılı",
121
+ url = f"{base_storage}/episodes/{item_id}/play.m3u8",
122
+ referer = url,
123
+ subtitles = subtitles
124
+ ))
125
+
126
+ # Dublaj Seçeneği (Eğer varsa)
127
+ if secici.select_first("span#turkceDublaj"):
236
128
  results.append(ExtractResult(
237
- url = stream.get("url"),
238
- name = f"{stream.get('dil')}",
129
+ name = "Türkçe Dublaj",
130
+ url = f"{base_storage}/episodes/{item_id}_tr/play.m3u8",
239
131
  referer = url,
240
132
  subtitles = subtitles
241
133
  ))
@@ -62,7 +62,7 @@ class Dizilla(PluginBase):
62
62
  # Detay sayfasından poster vb. bilgileri al
63
63
  ep_req = await self.httpx.get(self.fix_url(href))
64
64
  ep_secici = HTMLHelper(ep_req.text)
65
- poster = ep_secici.select_attr('img.imgt', 'src') or ep_secici.select_attr('img', 'src')
65
+ poster = ep_secici.select_poster('img.imgt') or ep_secici.select_poster('img')
66
66
 
67
67
  ana_sayfa.append(MainPageResult(
68
68
  category = category,
@@ -138,65 +138,39 @@ class Dizilla(PluginBase):
138
138
  secici = HTMLHelper(istek.text)
139
139
 
140
140
  next_data_text = secici.select_text("script#__NEXT_DATA__")
141
- if not next_data_text:
142
- return None
141
+ if not next_data_text: return None
143
142
 
144
143
  next_data = loads(next_data_text)
145
144
  secure_data = next_data.get("props", {}).get("pageProps", {}).get("secureData")
146
- if not secure_data:
147
- return None
145
+ if not secure_data: return None
148
146
 
149
147
  decrypted = await self.decrypt_response(secure_data)
150
148
  content = decrypted.get("contentItem", {})
151
- if not content:
152
- return None
149
+ if not content: return None
153
150
 
154
151
  title = content.get("original_title") or content.get("used_title")
155
152
  description = content.get("description") or content.get("used_description")
156
153
  rating = content.get("imdb_point") or content.get("local_vote_avg")
157
154
  year = content.get("release_year")
158
-
159
- # Poster and Backdrop - prefer backdrop if available for SeriesInfo
160
- poster = self.fix_poster_url(self.fix_url(content.get("back_url") or content.get("poster_url")))
161
-
162
- # Tags
163
- tags = []
164
- categories = decrypted.get("RelatedResults", {}).get("getSerieCategoriesById", {}).get("result", [])
165
- for cat in categories:
166
- tags.append(cat.get("name"))
167
-
168
- # Actors
169
- actors = []
170
- casts = decrypted.get("RelatedResults", {}).get("getSerieCastsById", {}).get("result", [])
171
- for cast in casts:
172
- actors.append(cast.get("name"))
173
-
174
- # Episodes
175
- episodes = []
176
- seasons_data = decrypted.get("RelatedResults", {}).get("getSerieSeasonAndEpisodes", {}).get("result", [])
177
- for season_item in seasons_data:
178
- season_num = season_item.get("season_no")
179
- for ep_item in season_item.get("episodes", []):
180
- ep_num = ep_item.get("episode_no")
181
- ep_slug = ep_item.get("used_slug")
182
- ep_name = ep_item.get("episode_text") or ""
183
-
184
- # Filter out duplicate language entries if any (we just need one link per episode)
185
- # Usually they share the same slug for the episode page
186
- if any(e.season == season_num and e.episode == ep_num for e in episodes):
187
- continue
155
+ poster = self.fix_poster_url(self.fix_url(content.get("back_url") or content.get("poster_url")))
188
156
 
189
- episodes.append(Episode(
190
- season = season_num,
191
- episode = ep_num,
192
- title = ep_name,
193
- url = self.fix_url(f"{self.main_url}/{ep_slug}")
194
- ))
157
+ tags = [cat.get("name") for cat in decrypted.get("RelatedResults", {}).get("getSerieCategoriesById", {}).get("result", [])]
158
+ actors = [cast.get("name") for cast in decrypted.get("RelatedResults", {}).get("getSerieCastsById", {}).get("result", [])]
159
+
160
+ episodes = []
161
+ for season in decrypted.get("RelatedResults", {}).get("getSerieSeasonAndEpisodes", {}).get("result", []):
162
+ s_no = season.get("season_no")
163
+ for ep in season.get("episodes", []):
164
+ e_no = ep.get("episode_no")
165
+ slug = ep.get("used_slug")
166
+ name = ep.get("episode_text") or ""
167
+ if not any(e.season == s_no and e.episode == e_no for e in episodes):
168
+ episodes.append(Episode(season=s_no, episode=e_no, title=name, url=self.fix_url(f"{self.main_url}/{slug}")))
195
169
 
196
170
  return SeriesInfo(
197
171
  url = url,
198
172
  poster = poster,
199
- title = title,
173
+ title = title or "Bilinmiyor",
200
174
  description = description,
201
175
  tags = tags,
202
176
  rating = str(rating) if rating else None,
@@ -95,38 +95,24 @@ class FilmBip(PluginBase):
95
95
  async def load_item(self, url: str) -> MovieInfo:
96
96
  istek = await self.httpx.get(url)
97
97
  secici = HTMLHelper(istek.text)
98
- html_text = istek.text
99
-
100
- title = secici.select_text("div.page-title h1") or ""
101
-
102
- poster = secici.select_attr("meta[property='og:image']", "content")
103
-
104
- trailer = secici.select_attr("div.series-profile-trailer", "data-yt")
105
98
 
99
+ title = self.clean_title(secici.select_direct_text("div.page-title h1"))
100
+ poster = secici.select_poster("div.series-profile-image a img")
106
101
  description = secici.select_text("div.series-profile-infos-in.article p") or secici.select_text("div.series-profile-summary p")
107
-
108
- tags = secici.select_all_text("div.series-profile-type.tv-show-profile-type a")
109
-
110
- # XPath yerine regex kullanarak yıl, süre vs. çıkarma
111
- year = secici.regex_first(r"(?is)Yap\u0131m y\u0131l\u0131.*?<p[^>]*>(.*?)<\/p>")
112
- if not year:
113
- # Fallback: Başlığın sonundaki parantezli yılı yakala
114
- year = secici.regex_first(r"\((\d{4})\)", title)
115
-
116
- duration_raw = secici.regex_first(r"(?is)S\u00fcre.*?<p[^>]*>(.*?)<\/p>")
117
- duration = secici.regex_first(r"(\d+)", duration_raw) if duration_raw else None
118
-
119
- rating = secici.regex_first(r"(?is)IMDB Puan\u0131.*?<span[^>]*>(.*?)<\/span>")
120
-
121
- actors = [img.attrs.get("alt") for img in secici.select("div.series-profile-cast ul li a img") if img.attrs.get("alt")]
102
+ tags = secici.select_texts("div.series-profile-type.tv-show-profile-type a")
103
+ year = secici.extract_year("div.series-profile-infos-in") or secici.regex_first(r"\((\d{4})\)", title)
104
+ duration = secici.regex_first(r"(\d+)", secici.meta_value("Süre", container_selector="div.series-profile-infos"))
105
+ rating = secici.meta_value("IMDB Puanı", container_selector="div.series-profile-infos")
106
+ rating = rating.split("(")[0] if rating else None
107
+ actors = secici.select_attrs("div.series-profile-cast ul li a img", "alt")
122
108
 
123
109
  return MovieInfo(
124
110
  url = url,
125
111
  poster = self.fix_url(poster) if poster else None,
126
- title = HTMLHelper(title).regex_replace(r"\(\d{4}\)", "").strip() if title else "",
112
+ title = title or "",
127
113
  description = description,
128
114
  tags = tags,
129
- year = year,
115
+ year = str(year) if year else None,
130
116
  rating = rating,
131
117
  duration = int(duration) if duration else None,
132
118
  actors = actors,
@@ -70,45 +70,25 @@ class FilmEkseni(PluginBase):
70
70
  istek = await self.httpx.get(url)
71
71
  helper = HTMLHelper(istek.text)
72
72
 
73
- raw_title = helper.select_text("div.page-title h1")
74
- title = raw_title.replace(" izle", "").strip() if raw_title else "Bilinmiyor"
75
-
76
- poster = helper.select_attr("picture.poster-auto > source:nth-child(2)", "data-srcset")
77
- description = helper.select_text("article.text-white")
78
- year = helper.select_text("strong a")
79
-
80
- tags_raw = helper.select_all_text("div.pb-2")
81
- tags = []
82
- for tag_str in tags_raw:
83
- if tag_str.startswith("Tür:"):
84
- tags.extend([t.strip() for t in tag_str.replace("Tür:", "").split(",")])
73
+ title = self.clean_title(helper.select_text("div.page-title h1"))
74
+ poster = helper.select_poster("picture.poster-auto img")
75
+ description = helper.select_direct_text("article.text-white p")
76
+ year = helper.extract_year("div.page-title", "strong a")
77
+ tags = helper.select_texts("div.pb-2 a[href*='/tur/']")
78
+ rating = helper.select_text("div.rate")
79
+ duration = helper.regex_first(r"(\d+)", helper.select_text("div.d-flex.flex-column.text-nowrap"))
80
+ actors = helper.select_texts("div.card-body.p-0.pt-2 .story-item .story-item-title")
85
81
 
86
- rating = helper.select_text("div.rate")
87
-
88
- duration = None
89
- duration_text = helper.select_text("div.d-flex.flex-column.text-nowrap")
90
- if duration_text:
91
- m = re.search(r"(\d+)", duration_text)
92
- if m:
93
- duration = int(m.group(1))
94
-
95
- actors_raw = helper.select("div.card-body.p-0.pt-2 .story-item")
96
- actors = []
97
- for actor in actors_raw:
98
- name = helper.select_text(".story-item-title", actor)
99
- if name:
100
- actors.append(name)
101
-
102
82
  return MovieInfo(
103
83
  url = url,
104
- poster = self.fix_url(poster),
105
- title = title,
84
+ poster = self.fix_url(poster) if poster else None,
85
+ title = title or "Bilinmiyor",
106
86
  description = description,
107
87
  tags = tags,
108
88
  rating = rating,
109
- year = year,
89
+ year = str(year) if year else None,
110
90
  actors = actors if actors else None,
111
- duration = duration
91
+ duration = int(duration) if duration else None
112
92
  )
113
93
 
114
94
  async def load_links(self, url: str) -> list[ExtractResult]:
@@ -76,94 +76,41 @@ class FilmMakinesi(PluginBase):
76
76
  istek = await self.httpx.get(url)
77
77
  secici = HTMLHelper(istek.text)
78
78
 
79
- title = secici.select_text("h1.title") or ""
79
+ title = self.clean_title(secici.select_text("h1.title"))
80
+ poster = secici.select_poster("img.cover-img")
81
+ description = secici.select_text("div.info-description p")
82
+ rating = secici.regex_first(r"(\d+[\d.]*)", secici.select_text("div.score"))
83
+ year = secici.select_text("span.date a")
84
+ actors = secici.select_texts("div.cast-name")
85
+ tags = secici.select_texts("div.type a[href*='/tur/']")
86
+ duration = secici.regex_first(r"(\d+)", secici.select_text("div.time"))
80
87
 
81
- poster = secici.select_attr("img.cover-img", "src") or ""
82
- poster = poster.strip()
83
-
84
- description = secici.select_text("div.info-description p") or ""
85
-
86
- rating_text = secici.select_text("div.score") or ""
87
- rating = None
88
- if rating_text:
89
- rating = rating_text.split()[0]
90
-
91
- year = secici.select_text("span.date a") or ""
92
-
93
- actors = secici.select_all_text("div.cast-name")
94
- tags = [a.text(strip=True) for a in secici.select("div.type a") if "/tur/" in (a.attrs.get("href") or "")]
95
-
96
- duration = None
97
- duration_text = secici.select_text("div.time") or None
98
- if duration_text:
99
- parts = duration_text.split()
100
- if len(parts) > 1:
101
- duration = parts[1].strip()
102
-
103
- # Dizi mi kontrol et - sezon/bölüm linkleri var mı?
104
88
  episodes = []
105
- all_links = secici.select("a[href]")
106
- for link in all_links:
89
+ for link in secici.select("a[href]"):
107
90
  href = link.attrs.get("href", "")
108
- pairs = HTMLHelper(href).regex_all(r"/sezon-(\d+)/bolum-(\d+)")
109
- if pairs:
110
- season_no = int(pairs[0][0])
111
- ep_no = int(pairs[0][1])
112
-
113
- # Bölüm başlığını çıkar - text'ten gerçek ismi al
114
- # Format: "22 Eylül 2014 / 44 dk /1. Sezon / 1. BölümPilot"
115
- full_text = link.text(strip=True)
116
- # "Bölüm" kelimesinden sonraki kısmı al
117
- ep_title = ""
118
- if "Bölüm" in full_text:
119
- parts = full_text.split("Bölüm")
120
- if len(parts) > 1:
121
- ep_title = parts[-1].strip()
122
-
123
- episodes.append(Episode(
124
- season = season_no,
125
- episode = ep_no,
126
- title = ep_title,
127
- url = self.fix_url(href)
128
- ))
91
+ s, e = secici.extract_season_episode(href)
92
+ if s and e:
93
+ name = link.text(strip=True).split("Bölüm")[-1].strip() if "Bölüm" in link.text() else ""
94
+ episodes.append(Episode(season=s, episode=e, title=name, url=self.fix_url(href)))
129
95
 
130
- # Bölümler varsa SeriesInfo döndür
96
+ # Tekrar edenleri temizle ve sırala
131
97
  if episodes:
132
- # Tekrar eden bölümleri kaldır
133
98
  seen = set()
134
- unique_episodes = []
99
+ unique = []
135
100
  for ep in episodes:
136
- key = (ep.season, ep.episode)
137
- if key not in seen:
138
- seen.add(key)
139
- unique_episodes.append(ep)
140
-
141
- # Sırala
142
- unique_episodes.sort(key=lambda x: (x.season or 0, x.episode or 0))
143
-
101
+ if (ep.season, ep.episode) not in seen:
102
+ seen.add((ep.season, ep.episode))
103
+ unique.append(ep)
104
+ unique.sort(key=lambda x: (x.season or 0, x.episode or 0))
105
+
144
106
  return SeriesInfo(
145
- url = url,
146
- poster = self.fix_url(poster) if poster else None,
147
- title = self.clean_title(title),
148
- description = description,
149
- tags = tags,
150
- rating = rating,
151
- year = year,
152
- actors = actors,
153
- duration = duration,
154
- episodes = unique_episodes
107
+ url=url, poster=self.fix_url(poster) if poster else None, title=title, description=description,
108
+ tags=tags, rating=rating, year=year, actors=actors, duration=duration, episodes=unique
155
109
  )
156
110
 
157
111
  return MovieInfo(
158
- url = url,
159
- poster = self.fix_url(poster) if poster else None,
160
- title = self.clean_title(title),
161
- description = description,
162
- tags = tags,
163
- rating = rating,
164
- year = year,
165
- actors = actors,
166
- duration = duration
112
+ url=url, poster=self.fix_url(poster) if poster else None, title=title, description=description,
113
+ tags=tags, rating=rating, year=year, actors=actors, duration=duration
167
114
  )
168
115
 
169
116
  async def load_links(self, url: str) -> list[ExtractResult]:
@@ -81,23 +81,15 @@ class FilmModu(PluginBase):
81
81
  istek = await self.httpx.get(url)
82
82
  secici = HTMLHelper(istek.text)
83
83
 
84
- org_title = secici.select_text("div.titles h1") or ""
85
- alt_title = secici.select_text("div.titles h2") or ""
86
- title = f"{org_title} - {alt_title}" if alt_title else org_title
87
-
88
- poster = secici.select_attr("img.img-responsive", "src") if secici.select_attr("img.img-responsive", "src") else None
89
-
90
- description = secici.select_text("p[itemprop='description']") or None
91
-
92
- tags = secici.select_all_text("a[href*='film-tur/']")
93
-
94
- year = secici.select_text("span[itemprop='dateCreated']") or None
95
-
96
- actors = []
97
- for a in secici.select("a[itemprop='actor']"):
98
- name = secici.select_text("span", a)
99
- if name:
100
- actors.append(name)
84
+ org_title = secici.select_text("div.titles h1")
85
+ alt_title = secici.select_text("div.titles h2")
86
+ title = f"{org_title} - {alt_title}" if alt_title else (org_title or "")
87
+ poster = secici.select_poster("img.img-responsive")
88
+ description = secici.select_text("p[itemprop='description']")
89
+ tags = secici.select_texts("a[href*='film-tur/']")
90
+ rating = secici.meta_value("IMDB")
91
+ year = secici.extract_year("span[itemprop='dateCreated']")
92
+ actors = secici.select_texts("a[itemprop='actor'] span")
101
93
 
102
94
  return MovieInfo(
103
95
  url = url,
@@ -105,7 +97,8 @@ class FilmModu(PluginBase):
105
97
  title = title,
106
98
  description = description,
107
99
  tags = tags,
108
- year = year,
100
+ rating = rating,
101
+ year = str(year) if year else None,
109
102
  actors = actors,
110
103
  )
111
104
 
@@ -81,52 +81,26 @@ class Filmatek(PluginBase):
81
81
  return results
82
82
 
83
83
  async def load_item(self, url: str) -> MovieInfo:
84
- istek = await self.httpx.get(url)
84
+ istek = await self.httpx.get(url)
85
85
  helper = HTMLHelper(istek.text)
86
86
 
87
- title = helper.select_text("div.data h1, h1") or "Bilinmiyor"
88
-
89
- poster_el = helper.select_first("div.poster img")
90
- poster = self.fix_url(poster_el.attrs.get("src")) if poster_el else None
91
- if not poster:
92
- poster = helper.select_attr("meta[property='og:image']", "content")
93
-
94
- description = helper.select_text("div.wp-content p")
95
- if not description:
96
- description = helper.select_attr("meta[property='og:description']", "content")
97
-
98
- year_text = helper.select_text("span.date")
99
- year = year_text.strip()[-4:] if year_text else None
100
-
101
- # Rating extraction updated
102
- rating = helper.select_text("span.dt_rating_vgs") or helper.select_text("span.dt_rating_vmanual")
103
-
104
- # Duration extraction
105
- duration = None
106
- duration_text = helper.select_text("span.runtime")
107
- if duration_text:
108
- # "80 Dak." -> "80"
109
- duration = duration_text.split()[0]
110
-
111
- tags = helper.select_all_text("div.sgeneros a")
112
-
113
- # Actors
114
- actors_list = []
115
- actor_els = helper.select("div.person")
116
- for el in actor_els:
117
- name = helper.select_text("div.name a", el)
118
- if name:
119
- actors_list.append(name.strip())
120
- actors = ", ".join(actors_list) if actors_list else None
87
+ title = self.clean_title(helper.select_text("div.data h1, h1"))
88
+ poster = helper.select_poster("div.poster img") or helper.select_attr("meta[property='og:image']", "content")
89
+ description = helper.select_text("div.wp-content p") or helper.select_attr("meta[property='og:description']", "content")
90
+ year = helper.extract_year("span.date")
91
+ rating = helper.select_text("span.dt_rating_vgs") or helper.select_text("span.dt_rating_vmanual")
92
+ duration = helper.regex_first(r"(\d+)", helper.select_text("span.runtime"))
93
+ tags = helper.select_texts("div.sgeneros a")
94
+ actors = helper.select_texts("div.person div.name a")
121
95
 
122
96
  return MovieInfo(
123
97
  url = url,
124
- title = title,
98
+ title = title or "Bilinmiyor",
125
99
  description = description,
126
- poster = poster,
127
- year = year,
100
+ poster = self.fix_url(poster) if poster else None,
101
+ year = str(year) if year else None,
128
102
  rating = rating,
129
- duration = duration,
103
+ duration = int(duration) if duration else None,
130
104
  tags = tags,
131
105
  actors = actors
132
106
  )