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
@@ -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:
@@ -65,94 +65,56 @@ class RecTV(PluginBase):
65
65
  for veri in tum_veri
66
66
  ]
67
67
 
68
- async def load_item(self, url: str) -> MovieInfo:
68
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
69
69
  self.httpx.headers.update({"user-agent": "okhttp/4.12.0"})
70
70
  veri = loads(url)
71
71
 
72
- match veri.get("type"):
73
- case "serie":
74
- dizi_istek = await self.httpx.get(f"{self.main_url}/api/season/by/serie/{veri.get('id')}/{self.sw_key}/")
75
- dizi_veri = dizi_istek.json()
76
-
77
- episodes = []
78
- for season in dizi_veri:
79
- season_title = season.get("title", "").strip()
80
- for episode in season.get("episodes"):
81
- ep_label = episode.get("title", "").strip()
82
- for source in episode.get("sources"):
83
- # Bölüm için gerekli bilgileri JSON olarak sakla
84
- ep_data = {
85
- "url" : self.fix_url(source.get("url")),
86
- "title" : f"{veri.get('title')} | {season_title} {ep_label} - {source.get('title')}",
87
- "is_episode" : True
88
- }
89
-
90
- # Extract season/episode numbers using helper
91
- s1, _ = HTMLHelper.extract_season_episode(season_title or "")
92
- _, e2 = HTMLHelper.extract_season_episode(ep_label or "")
93
-
94
- tag = ""
95
- clean_season = season_title
96
- if "dublaj" in season_title.lower():
97
- tag = " (Dublaj)"
98
- clean_season = re.sub(r"\s*dublaj\s*", "", season_title, flags=re.I).strip()
99
- elif any(x in season_title.lower() for x in ["altyazı", "altyazi"]):
100
- tag = " (Altyazı)"
101
- clean_season = re.sub(r"\s*altyaz[ıi]\s*", "", season_title, flags=re.I).strip()
102
-
103
- ep_model = Episode(
104
- season = s1 or 1,
105
- episode = e2 or 1,
106
- title = f"{clean_season} {ep_label}{tag} - {source.get('title')}",
107
- url = dumps(ep_data),
108
- )
109
-
110
- episodes.append(ep_model)
111
-
112
- # Süreyi dakikaya çevir (Örn: "1h 59min")
113
- duration_raw = veri.get("duration")
114
- duration = None
115
- if duration_raw:
116
- try:
117
- h = int(HTMLHelper(duration_raw).regex_first(r"(\d+)h") or 0)
118
- m = int(HTMLHelper(duration_raw).regex_first(r"(\d+)min") or 0)
119
- duration = h * 60 + m
120
- except: pass
121
-
122
- return SeriesInfo(
123
- url = url,
124
- poster = self.fix_url(veri.get("image")),
125
- title = veri.get("title"),
126
- description = veri.get("description"),
127
- tags = [genre.get("title") for genre in veri.get("genres")] if veri.get("genres") else [],
128
- rating = str(veri.get("imdb") or veri.get("rating") or ""),
129
- year = str(veri.get("year") or ""),
130
- actors = [],
131
- duration = duration,
132
- episodes = episodes
133
- )
134
- case _:
135
- # Süreyi dakikaya çevir
136
- duration_raw = veri.get("duration")
137
- duration = None
138
- if duration_raw:
139
- try:
140
- h = int(HTMLHelper(duration_raw).regex_first(r"(\d+)h") or 0)
141
- m = int(HTMLHelper(duration_raw).regex_first(r"(\d+)min") or 0)
142
- duration = h * 60 + m
143
- except: pass
144
-
145
- return MovieInfo(
146
- url = url,
147
- poster = self.fix_url(veri.get("image")),
148
- title = veri.get("title"),
149
- description = veri.get("description"),
150
- tags = [genre.get("title") for genre in veri.get("genres")] if veri.get("genres") else [],
151
- rating = str(veri.get("imdb") or veri.get("rating") or ""),
152
- year = str(veri.get("year") or ""),
153
- actors = [],
154
- duration = duration
155
- )
72
+ # Süreyi dakikaya çevir (Örn: "1h 59min")
73
+ duration_raw = veri.get("duration")
74
+ duration = None
75
+ if duration_raw:
76
+ try:
77
+ h = int(HTMLHelper(duration_raw).regex_first(r"(\d+)h") or 0)
78
+ m = int(HTMLHelper(duration_raw).regex_first(r"(\d+)min") or 0)
79
+ duration = h * 60 + m
80
+ except: pass
81
+
82
+ common_info = {
83
+ "url" : url,
84
+ "poster" : self.fix_url(veri.get("image")),
85
+ "title" : veri.get("title"),
86
+ "description" : veri.get("description"),
87
+ "tags" : [genre.get("title") for genre in veri.get("genres")] if veri.get("genres") else [],
88
+ "rating" : str(veri.get("imdb") or veri.get("rating") or ""),
89
+ "year" : str(veri.get("year") or ""),
90
+ "duration" : duration
91
+ }
92
+
93
+ if veri.get("type") == "serie":
94
+ dizi_istek = await self.httpx.get(f"{self.main_url}/api/season/by/serie/{veri.get('id')}/{self.sw_key}/")
95
+ dizi_veri = dizi_istek.json()
96
+
97
+ episodes = []
98
+ for season in dizi_veri:
99
+ s_title = season.get("title", "").strip()
100
+ s, _ = HTMLHelper.extract_season_episode(s_title)
101
+ for ep in season.get("episodes"):
102
+ e_title = ep.get("title", "").strip()
103
+ _, e = HTMLHelper.extract_season_episode(e_title)
104
+ for source in ep.get("sources"):
105
+ tag = ""
106
+ clean_s = s_title
107
+ if "dublaj" in s_title.lower():
108
+ tag = " (Dublaj)"; clean_s = re.sub(r"\s*dublaj\s*", "", s_title, flags=re.I).strip()
109
+ elif "altyaz" in s_title.lower():
110
+ tag = " (Altyazı)"; clean_s = re.sub(r"\s*altyaz[ıi]\s*", "", s_title, flags=re.I).strip()
111
+
112
+ ep_data = {"url": self.fix_url(source.get("url")), "title": f"{veri.get('title')} | {s_title} {e_title} - {source.get('title')}", "is_episode": True}
113
+ episodes.append(Episode(season=s or 1, episode=e or 1, title=f"{clean_s} {e_title}{tag} - {source.get('title')}", url=dumps(ep_data)))
114
+
115
+ return SeriesInfo(**common_info, episodes=episodes, actors=[])
116
+
117
+ return MovieInfo(**common_info, actors=[])
156
118
 
157
119
  async def load_links(self, url: str) -> list[ExtractResult]:
158
120
  try: