KekikStream 2.1.9__py3-none-any.whl → 2.2.7__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 (36) hide show
  1. KekikStream/Extractors/CloseLoad.py +7 -8
  2. KekikStream/Extractors/Filemoon.py +7 -6
  3. KekikStream/Extractors/JFVid.py +40 -0
  4. KekikStream/Extractors/MolyStream.py +6 -5
  5. KekikStream/Extractors/PlayerFilmIzle.py +6 -2
  6. KekikStream/Extractors/VidHide.py +0 -1
  7. KekikStream/Extractors/VidMoly.py +17 -9
  8. KekikStream/Plugins/BelgeselX.py +39 -20
  9. KekikStream/Plugins/DiziBox.py +115 -59
  10. KekikStream/Plugins/DiziPal.py +87 -40
  11. KekikStream/Plugins/DiziYou.py +105 -64
  12. KekikStream/Plugins/Dizilla.py +58 -29
  13. KekikStream/Plugins/FilmBip.py +60 -31
  14. KekikStream/Plugins/FilmMakinesi.py +75 -51
  15. KekikStream/Plugins/FilmModu.py +73 -36
  16. KekikStream/Plugins/FullHDFilm.py +82 -48
  17. KekikStream/Plugins/FullHDFilmizlesene.py +94 -39
  18. KekikStream/Plugins/HDFilmCehennemi.py +79 -54
  19. KekikStream/Plugins/JetFilmizle.py +98 -51
  20. KekikStream/Plugins/KultFilmler.py +64 -34
  21. KekikStream/Plugins/RoketDizi.py +43 -26
  22. KekikStream/Plugins/SelcukFlix.py +27 -14
  23. KekikStream/Plugins/SetFilmIzle.py +74 -43
  24. KekikStream/Plugins/SezonlukDizi.py +102 -46
  25. KekikStream/Plugins/Sinefy.py +130 -101
  26. KekikStream/Plugins/SinemaCX.py +82 -37
  27. KekikStream/Plugins/Sinezy.py +61 -47
  28. KekikStream/Plugins/SuperFilmGeldi.py +72 -36
  29. KekikStream/Plugins/UgurFilm.py +72 -34
  30. KekikStream/requirements.txt +1 -1
  31. {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/METADATA +40 -32
  32. {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/RECORD +36 -35
  33. {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/WHEEL +0 -0
  34. {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/entry_points.txt +0 -0
  35. {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/licenses/LICENSE +0 -0
  36. {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, Subtitle
4
- from parsel import Selector
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, Subtitle
4
+ from selectolax.parser import HTMLParser
5
5
  import re, base64
6
6
 
7
7
  class KultFilmler(PluginBase):
@@ -38,67 +38,95 @@ class KultFilmler(PluginBase):
38
38
 
39
39
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
40
40
  istek = await self.httpx.get(url)
41
- secici = Selector(istek.text)
41
+ secici = HTMLParser(istek.text)
42
42
 
43
43
  results = []
44
44
  for veri in secici.css("div.col-md-12 div.movie-box"):
45
- title = veri.css("div.img img::attr(alt)").get()
46
- href = self.fix_url(veri.css("a::attr(href)").get())
47
- poster = self.fix_url(veri.css("div.img img::attr(src)").get())
45
+ img_el = veri.css_first("div.img img")
46
+ link_el = veri.css_first("a")
47
+
48
+ title = img_el.attrs.get("alt") if img_el else None
49
+ href = link_el.attrs.get("href") if link_el else None
50
+ poster = img_el.attrs.get("src") if img_el else None
48
51
 
49
52
  if title and href:
50
53
  results.append(MainPageResult(
51
54
  category = category,
52
55
  title = title,
53
- url = href,
54
- poster = poster,
56
+ url = self.fix_url(href),
57
+ poster = self.fix_url(poster) if poster else None,
55
58
  ))
56
59
 
57
60
  return results
58
61
 
59
62
  async def search(self, query: str) -> list[SearchResult]:
60
63
  istek = await self.httpx.get(f"{self.main_url}?s={query}")
61
- secici = Selector(istek.text)
64
+ secici = HTMLParser(istek.text)
62
65
 
63
66
  results = []
64
67
  for veri in secici.css("div.movie-box"):
65
- title = veri.css("div.img img::attr(alt)").get()
66
- href = self.fix_url(veri.css("a::attr(href)").get())
67
- poster = self.fix_url(veri.css("div.img img::attr(src)").get())
68
+ img_el = veri.css_first("div.img img")
69
+ link_el = veri.css_first("a")
70
+
71
+ title = img_el.attrs.get("alt") if img_el else None
72
+ href = link_el.attrs.get("href") if link_el else None
73
+ poster = img_el.attrs.get("src") if img_el else None
68
74
 
69
75
  if title and href:
70
76
  results.append(SearchResult(
71
77
  title = title,
72
- url = href,
73
- poster = poster,
78
+ url = self.fix_url(href),
79
+ poster = self.fix_url(poster) if poster else None,
74
80
  ))
75
81
 
76
82
  return results
77
83
 
78
84
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
79
85
  istek = await self.httpx.get(url)
80
- secici = Selector(istek.text)
86
+ secici = HTMLParser(istek.text)
87
+
88
+ film_img = secici.css_first("div.film-bilgileri img")
89
+ og_title = secici.css_first("[property='og:title']")
90
+ og_image = secici.css_first("[property='og:image']")
91
+
92
+ title = (film_img.attrs.get("alt") if film_img else None) or (og_title.attrs.get("content") if og_title else None)
93
+ poster = self.fix_url(og_image.attrs.get("content")) if og_image else None
94
+
95
+ desc_el = secici.css_first("div.description")
96
+ description = desc_el.text(strip=True) if desc_el else None
97
+
98
+ tags = [a.text(strip=True) for a in secici.css("ul.post-categories a") if a.text(strip=True)]
81
99
 
82
- title = secici.css("div.film-bilgileri img::attr(alt)").get() or secici.css("[property='og:title']::attr(content)").get()
83
- poster = self.fix_url(secici.css("[property='og:image']::attr(content)").get())
84
- description = secici.css("div.description::text").get()
85
- tags = secici.css("ul.post-categories a::text").getall()
86
100
  # HTML analizine göre güncellenen alanlar
87
- year = secici.css("li.release span a::text").get()
88
- duration = secici.css("li.time span::text").re_first(r"(\d+)")
89
- rating = secici.css("div.imdb-count::text").get()
90
- actors = secici.css("div.actors a::text").getall()
91
- if rating:
92
- rating = rating.strip()
101
+ year_el = secici.css_first("li.release span a")
102
+ year = year_el.text(strip=True) if year_el else None
103
+
104
+ time_el = secici.css_first("li.time span")
105
+ duration = None
106
+ if time_el:
107
+ time_text = time_el.text(strip=True)
108
+ dur_match = re.search(r"(\d+)", time_text)
109
+ duration = dur_match.group(1) if dur_match else None
110
+
111
+ rating_el = secici.css_first("div.imdb-count")
112
+ rating = rating_el.text(strip=True) if rating_el else None
113
+
114
+ actors = [a.text(strip=True) for a in secici.css("div.actors a") if a.text(strip=True)]
93
115
 
94
116
  # Dizi mi kontrol et
95
117
  if "/dizi/" in url:
96
118
  episodes = []
97
119
  for bolum in secici.css("div.episode-box"):
98
- ep_href = self.fix_url(bolum.css("div.name a::attr(href)").get())
99
- ssn_detail = bolum.css("span.episodetitle::text").get() or ""
100
- ep_detail = bolum.css("span.episodetitle b::text").get() or ""
101
- ep_name = f"{ssn_detail} - {ep_detail}"
120
+ name_link = bolum.css_first("div.name a")
121
+ ep_href = name_link.attrs.get("href") if name_link else None
122
+
123
+ ssn_el = bolum.css_first("span.episodetitle")
124
+ ssn_detail = ssn_el.text(strip=True) if ssn_el else ""
125
+
126
+ ep_b_el = bolum.css_first("span.episodetitle b")
127
+ ep_detail = ep_b_el.text(strip=True) if ep_b_el else ""
128
+
129
+ ep_name = f"{ssn_detail} - {ep_detail}"
102
130
 
103
131
  if ep_href:
104
132
  ep_season = re.search(r"(\d+)\.", ssn_detail)
@@ -108,7 +136,7 @@ class KultFilmler(PluginBase):
108
136
  season = int(ep_season[1]) if ep_season else 1,
109
137
  episode = int(ep_episode[1]) if ep_episode else 1,
110
138
  title = ep_name.strip(" -"),
111
- url = ep_href,
139
+ url = self.fix_url(ep_href),
112
140
  ))
113
141
 
114
142
  return SeriesInfo(
@@ -150,8 +178,9 @@ class KultFilmler(PluginBase):
150
178
 
151
179
  try:
152
180
  decoded = base64.b64decode(atob).decode("utf-8")
153
- secici = Selector(text=decoded)
154
- return self.fix_url(secici.css("iframe::attr(src)").get()) or ""
181
+ secici = HTMLParser(decoded)
182
+ iframe_el = secici.css_first("iframe")
183
+ return self.fix_url(iframe_el.attrs.get("src")) if iframe_el else ""
155
184
  except Exception:
156
185
  return ""
157
186
 
@@ -162,7 +191,7 @@ class KultFilmler(PluginBase):
162
191
 
163
192
  async def load_links(self, url: str) -> list[ExtractResult]:
164
193
  istek = await self.httpx.get(url)
165
- secici = Selector(istek.text)
194
+ secici = HTMLParser(istek.text)
166
195
 
167
196
  iframes = set()
168
197
 
@@ -173,7 +202,8 @@ class KultFilmler(PluginBase):
173
202
 
174
203
  # Alternatif player'lar
175
204
  for player in secici.css("div.container#player"):
176
- alt_iframe = self.fix_url(player.css("iframe::attr(src)").get())
205
+ iframe_el = player.css_first("iframe")
206
+ alt_iframe = self.fix_url(iframe_el.attrs.get("src")) if iframe_el else None
177
207
  if alt_iframe:
178
208
  alt_istek = await self.httpx.get(alt_iframe)
179
209
  alt_frame = self._get_iframe(alt_istek.text)
@@ -1,7 +1,7 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult, MovieInfo
4
- from parsel import Selector
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult, MovieInfo
4
+ from selectolax.parser import HTMLParser
5
5
  import re, base64, json
6
6
 
7
7
  class RoketDizi(PluginBase):
@@ -24,21 +24,26 @@ class RoketDizi(PluginBase):
24
24
 
25
25
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
26
26
  istek = await self.httpx.get(f"{url}?&page={page}")
27
- secici = Selector(istek.text)
27
+ secici = HTMLParser(istek.text)
28
28
 
29
29
  results = []
30
30
 
31
- for item in secici.css("div.w-full.p-4 span.bg-\\[\\#232323\\]"):
32
- title = item.css("span.font-normal.line-clamp-1::text").get()
33
- href = item.css("a::attr(href)").get()
34
- poster = item.css("img::attr(src)").get()
31
+ # Use div.new-added-list to find the container, then get items
32
+ for item in secici.css("div.new-added-list > span"):
33
+ title_el = item.css_first("span.line-clamp-1")
34
+ link_el = item.css_first("a")
35
+ img_el = item.css_first("img")
36
+
37
+ title = title_el.text(strip=True) if title_el else None
38
+ href = link_el.attrs.get("href") if link_el else None
39
+ poster = img_el.attrs.get("src") if img_el else None
35
40
 
36
41
  if title and href:
37
42
  results.append(MainPageResult(
38
43
  category = category,
39
44
  title = self.clean_title(title),
40
45
  url = self.fix_url(href),
41
- poster = self.fix_url(poster)
46
+ poster = self.fix_url(poster) if poster else None
42
47
  ))
43
48
 
44
49
  return results
@@ -87,20 +92,29 @@ class RoketDizi(PluginBase):
87
92
  async def load_item(self, url: str) -> SeriesInfo:
88
93
  # Note: Handling both Movie and Series logic in one, returning SeriesInfo generally or MovieInfo
89
94
  resp = await self.httpx.get(url)
90
- sel = Selector(resp.text)
95
+ sel = HTMLParser(resp.text)
96
+ html_text = resp.text
97
+
98
+ title_el = sel.css_first("h1.text-white")
99
+ title = title_el.text(strip=True) if title_el else None
91
100
 
92
- title = sel.css("h1.text-white::text").get()
93
- poster = sel.css("div.w-full.page-top img::attr(src)").get()
94
- description = sel.css("div.mt-2.text-sm::text").get()
101
+ poster_el = sel.css_first("div.w-full.page-top img")
102
+ poster = poster_el.attrs.get("src") if poster_el else None
103
+
104
+ desc_el = sel.css_first("div.mt-2.text-sm")
105
+ description = desc_el.text(strip=True) if desc_el else None
95
106
 
96
107
  # Tags - genre bilgileri (Detaylar bölümünde)
97
108
  tags = []
98
- genre_text = sel.css("h3.text-white.opacity-90::text").get()
99
- if genre_text:
100
- tags = [t.strip() for t in genre_text.split(",")]
109
+ genre_el = sel.css_first("h3.text-white.opacity-90")
110
+ if genre_el:
111
+ genre_text = genre_el.text(strip=True)
112
+ if genre_text:
113
+ tags = [t.strip() for t in genre_text.split(",")]
101
114
 
102
115
  # Rating
103
- rating = sel.css("span.text-white.text-sm.font-bold::text").get()
116
+ rating_el = sel.css_first("span.text-white.text-sm.font-bold")
117
+ rating = rating_el.text(strip=True) if rating_el else None
104
118
 
105
119
  # Year ve Actors - Detaylar (Details) bölümünden
106
120
  year = None
@@ -109,14 +123,13 @@ class RoketDizi(PluginBase):
109
123
  # Detaylar bölümündeki tüm flex-col div'leri al
110
124
  detail_items = sel.css("div.flex.flex-col")
111
125
  for item in detail_items:
112
- # Label ve value yapısı: span.text-base ve span.text-sm.opacity-90
113
- label = item.css("span.text-base::text").get()
114
- value = item.css("span.text-sm.opacity-90::text").get()
126
+ label_el = item.css_first("span.text-base")
127
+ value_el = item.css_first("span.text-sm.opacity-90")
128
+
129
+ label = label_el.text(strip=True) if label_el else None
130
+ value = value_el.text(strip=True) if value_el else None
115
131
 
116
132
  if label and value:
117
- label = label.strip()
118
- value = value.strip()
119
-
120
133
  # Yayın tarihi (yıl)
121
134
  if label == "Yayın tarihi":
122
135
  # "16 Ekim 2018" formatından yılı çıkar
@@ -130,7 +143,7 @@ class RoketDizi(PluginBase):
130
143
  actors.append(value)
131
144
 
132
145
  # Check urls for episodes
133
- all_urls = re.findall(r'"url":"([^"]*)"', resp.text)
146
+ all_urls = re.findall(r'"url":"([^"]*)"', html_text)
134
147
  is_series = any("bolum-" in u for u in all_urls)
135
148
 
136
149
  episodes = []
@@ -160,7 +173,7 @@ class RoketDizi(PluginBase):
160
173
  return SeriesInfo(
161
174
  title = title,
162
175
  url = url,
163
- poster = self.fix_url(poster),
176
+ poster = self.fix_url(poster) if poster else None,
164
177
  description = description,
165
178
  tags = tags,
166
179
  rating = rating,
@@ -171,9 +184,13 @@ class RoketDizi(PluginBase):
171
184
 
172
185
  async def load_links(self, url: str) -> list[ExtractResult]:
173
186
  resp = await self.httpx.get(url)
174
- sel = Selector(resp.text)
187
+ sel = HTMLParser(resp.text)
175
188
 
176
- next_data = sel.css("script#__NEXT_DATA__::text").get()
189
+ next_data_el = sel.css_first("script#__NEXT_DATA__")
190
+ if not next_data_el:
191
+ return []
192
+
193
+ next_data = next_data_el.text(strip=True)
177
194
  if not next_data:
178
195
  return []
179
196
 
@@ -1,7 +1,7 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult
4
- from parsel import Selector
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult
4
+ from selectolax.parser import HTMLParser
5
5
  import re, base64, json, urllib.parse
6
6
 
7
7
  class SelcukFlix(PluginBase):
@@ -34,13 +34,17 @@ class SelcukFlix(PluginBase):
34
34
  if "tum-bolumler" in url:
35
35
  try:
36
36
  resp = await self.httpx.get(url)
37
- sel = Selector(resp.text)
37
+ sel = HTMLParser(resp.text)
38
38
 
39
39
  for item in sel.css("div.col-span-3 a"):
40
- name = item.css("h2::text").get()
41
- ep_info = item.css("div.opacity-80::text").get()
42
- href = item.css("::attr(href)").get()
43
- poster = item.css("div.image img::attr(src)").get()
40
+ name_el = item.css_first("h2")
41
+ ep_el = item.css_first("div.opacity-80")
42
+ img_el = item.css_first("div.image img")
43
+
44
+ name = name_el.text(strip=True) if name_el else None
45
+ ep_info = ep_el.text(strip=True) if ep_el else None
46
+ href = item.attrs.get("href")
47
+ poster = img_el.attrs.get("src") if img_el else None
44
48
 
45
49
  if name and href:
46
50
  title = f"{name} - {ep_info}" if ep_info else name
@@ -53,7 +57,7 @@ class SelcukFlix(PluginBase):
53
57
  category = category,
54
58
  title = title,
55
59
  url = final_url,
56
- poster = self.fix_url(poster)
60
+ poster = self.fix_url(poster) if poster else None
57
61
  ))
58
62
  except Exception:
59
63
  pass
@@ -184,9 +188,13 @@ class SelcukFlix(PluginBase):
184
188
 
185
189
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
186
190
  resp = await self.httpx.get(url)
187
- sel = Selector(resp.text)
191
+ sel = HTMLParser(resp.text)
192
+
193
+ next_data_el = sel.css_first("script#__NEXT_DATA__")
194
+ if not next_data_el:
195
+ return None
188
196
 
189
- next_data = sel.css("script#__NEXT_DATA__::text").get()
197
+ next_data = next_data_el.text(strip=True)
190
198
  if not next_data:
191
199
  return None
192
200
 
@@ -258,9 +266,13 @@ class SelcukFlix(PluginBase):
258
266
 
259
267
  async def load_links(self, url: str) -> list[ExtractResult]:
260
268
  resp = await self.httpx.get(url)
261
- sel = Selector(resp.text)
269
+ sel = HTMLParser(resp.text)
262
270
 
263
- next_data = sel.css("script#__NEXT_DATA__::text").get()
271
+ next_data_el = sel.css_first("script#__NEXT_DATA__")
272
+ if not next_data_el:
273
+ return []
274
+
275
+ next_data = next_data_el.text(strip=True)
264
276
  if not next_data:
265
277
  return []
266
278
 
@@ -300,8 +312,9 @@ class SelcukFlix(PluginBase):
300
312
  source_content = res[0].get("source_content") or res[0].get("sourceContent")
301
313
 
302
314
  if source_content:
303
- iframe_sel = Selector(source_content)
304
- iframe_src = iframe_sel.css("iframe::attr(src)").get()
315
+ iframe_sel = HTMLParser(source_content)
316
+ iframe_el = iframe_sel.css_first("iframe")
317
+ iframe_src = iframe_el.attrs.get("src") if iframe_el else None
305
318
  if iframe_src:
306
319
  iframe_src = self.fix_url(iframe_src)
307
320
  # Hotlinger domain değişimi (Kotlin referansı)
@@ -1,7 +1,7 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult
4
- from parsel import Selector
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult
4
+ from selectolax.parser import HTMLParser
5
5
  import re, json
6
6
 
7
7
  class SetFilmIzle(PluginBase):
@@ -53,18 +53,22 @@ class SetFilmIzle(PluginBase):
53
53
 
54
54
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
55
55
  istek = self.cloudscraper.get(url)
56
- secici = Selector(istek.text)
56
+ secici = HTMLParser(istek.text)
57
57
 
58
58
  results = []
59
59
  for item in secici.css("div.items article"):
60
- title = item.css("h2::text").get()
61
- href = item.css("a::attr(href)").get()
62
- poster = item.css("img::attr(data-src)").get()
60
+ title_el = item.css_first("h2")
61
+ link_el = item.css_first("a")
62
+ img_el = item.css_first("img")
63
+
64
+ title = title_el.text(strip=True) if title_el else None
65
+ href = link_el.attrs.get("href") if link_el else None
66
+ poster = img_el.attrs.get("data-src") if img_el else None
63
67
 
64
68
  if title and href:
65
69
  results.append(MainPageResult(
66
70
  category = category,
67
- title = title.strip(),
71
+ title = title,
68
72
  url = self.fix_url(href),
69
73
  poster = self.fix_url(poster) if poster else None
70
74
  ))
@@ -95,17 +99,21 @@ class SetFilmIzle(PluginBase):
95
99
  except:
96
100
  return []
97
101
 
98
- secici = Selector(text=html)
102
+ secici = HTMLParser(html)
99
103
  results = []
100
104
 
101
105
  for item in secici.css("div.items article"):
102
- title = item.css("h2::text").get()
103
- href = item.css("a::attr(href)").get()
104
- poster = item.css("img::attr(data-src)").get()
106
+ title_el = item.css_first("h2")
107
+ link_el = item.css_first("a")
108
+ img_el = item.css_first("img")
109
+
110
+ title = title_el.text(strip=True) if title_el else None
111
+ href = link_el.attrs.get("href") if link_el else None
112
+ poster = img_el.attrs.get("data-src") if img_el else None
105
113
 
106
114
  if title and href:
107
115
  results.append(SearchResult(
108
- title = title.strip(),
116
+ title = title,
109
117
  url = self.fix_url(href),
110
118
  poster = self.fix_url(poster) if poster else None
111
119
  ))
@@ -114,50 +122,73 @@ class SetFilmIzle(PluginBase):
114
122
 
115
123
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
116
124
  istek = await self.httpx.get(url)
117
- secici = Selector(istek.text)
118
-
119
- raw_title = secici.css("h1::text").get() or ""
120
- title = re.sub(r"\s*izle.*$", "", raw_title, flags=re.IGNORECASE).strip()
121
- poster = secici.css("div.poster img::attr(src)").get()
122
- description = secici.css("div.wp-content p::text").get()
123
- year = secici.css("div.extra span.C a::text").get()
124
- if year:
125
- year_match = re.search(r"\d{4}", year)
125
+ secici = HTMLParser(istek.text)
126
+ html_text = istek.text
127
+
128
+ title_el = secici.css_first("h1") or secici.css_first(".titles h1")
129
+ raw_title = title_el.text(strip=True) if title_el else ""
130
+ if not raw_title:
131
+ # Alternatif title yeri
132
+ title_meta = secici.css_first("meta[property='og:title']")
133
+ raw_title = title_meta.attrs.get("content", "") if title_meta else ""
134
+
135
+ title = re.sub(r"\s*izle.*$", "", raw_title, flags=re.IGNORECASE).strip()
136
+
137
+ poster_el = secici.css_first("div.poster img")
138
+ poster = poster_el.attrs.get("src") if poster_el else None
139
+
140
+ desc_el = secici.css_first("div.wp-content p")
141
+ description = desc_el.text(strip=True) if desc_el else None
142
+
143
+ year_el = secici.css_first("div.extra span.C a")
144
+ year = None
145
+ if year_el:
146
+ year_text = year_el.text(strip=True)
147
+ year_match = re.search(r"\d{4}", year_text)
126
148
  year = year_match.group() if year_match else None
127
- tags = [a.css("::text").get().strip() for a in secici.css("div.sgeneros a") if a.css("::text").get()]
128
- duration = secici.css("span.runtime::text").get()
129
- if duration:
130
- dur_match = re.search(r"\d+", duration)
149
+
150
+ tags = [a.text(strip=True) for a in secici.css("div.sgeneros a") if a.text(strip=True)]
151
+
152
+ duration_el = secici.css_first("span.runtime")
153
+ duration = None
154
+ if duration_el:
155
+ duration_text = duration_el.text(strip=True)
156
+ dur_match = re.search(r"\d+", duration_text)
131
157
  duration = int(dur_match.group()) if dur_match else None
132
158
 
133
- actors = [span.css("::text").get().strip() for span in secici.css("span.valor a > span") if span.css("::text").get()]
159
+ actors = [span.text(strip=True) for span in secici.css("span.valor a > span") if span.text(strip=True)]
134
160
 
135
- trailer_match = re.search(r'embed/([^?]*)\?rel', istek.text)
161
+ trailer_match = re.search(r'embed/([^?]*)\?rel', html_text)
136
162
  trailer = f"https://www.youtube.com/embed/{trailer_match.group(1)}" if trailer_match else None
137
163
 
138
164
  # Dizi mi film mi kontrol et
139
165
  is_series = "/dizi/" in url
140
166
 
141
167
  if is_series:
142
- year_elem = secici.css("a[href*='/yil/']::text").get()
143
- if year_elem:
168
+ year_link_el = secici.css_first("a[href*='/yil/']")
169
+ if year_link_el:
170
+ year_elem = year_link_el.text(strip=True)
144
171
  year_match = re.search(r"\d{4}", year_elem)
145
172
  year = year_match.group() if year_match else year
146
173
 
147
- dur_elem = secici.css("div#info span:contains('Dakika')::text").get()
148
- if dur_elem:
149
- dur_match = re.search(r"\d+", dur_elem)
150
- duration = int(dur_match.group()) if dur_match else duration
174
+ # Duration from info section
175
+ for span in secici.css("div#info span"):
176
+ span_text = span.text(strip=True) if span.text() else ""
177
+ if "Dakika" in span_text:
178
+ dur_match = re.search(r"\d+", span_text)
179
+ duration = int(dur_match.group()) if dur_match else duration
180
+ break
151
181
 
152
182
  episodes = []
153
183
  for ep_item in secici.css("div#episodes ul.episodios li"):
154
- ep_href = ep_item.css("h4.episodiotitle a::attr(href)").get()
155
- ep_name = ep_item.css("h4.episodiotitle a::text").get()
184
+ ep_title_el = ep_item.css_first("h4.episodiotitle a")
185
+ ep_href = ep_title_el.attrs.get("href") if ep_title_el else None
186
+ ep_name = ep_title_el.text(strip=True) if ep_title_el else None
156
187
 
157
188
  if not ep_href or not ep_name:
158
189
  continue
159
190
 
160
- ep_detail = ep_name.strip()
191
+ ep_detail = ep_name
161
192
  season_match = re.search(r"(\d+)\.\s*Sezon", ep_detail)
162
193
  episode_match = re.search(r"Sezon\s+(\d+)\.\s*Bölüm", ep_detail)
163
194
 
@@ -167,7 +198,7 @@ class SetFilmIzle(PluginBase):
167
198
  episodes.append(Episode(
168
199
  season = ep_season,
169
200
  episode = ep_episode,
170
- title = ep_name.strip(),
201
+ title = ep_name,
171
202
  url = self.fix_url(ep_href)
172
203
  ))
173
204
 
@@ -175,7 +206,7 @@ class SetFilmIzle(PluginBase):
175
206
  url = url,
176
207
  poster = self.fix_url(poster) if poster else None,
177
208
  title = title,
178
- description = description.strip() if description else None,
209
+ description = description,
179
210
  tags = tags,
180
211
  year = year,
181
212
  duration = duration,
@@ -187,7 +218,7 @@ class SetFilmIzle(PluginBase):
187
218
  url = url,
188
219
  poster = self.fix_url(poster) if poster else None,
189
220
  title = title,
190
- description = description.strip() if description else None,
221
+ description = description,
191
222
  tags = tags,
192
223
  year = year,
193
224
  duration = duration,
@@ -196,7 +227,7 @@ class SetFilmIzle(PluginBase):
196
227
 
197
228
  async def load_links(self, url: str) -> list[ExtractResult]:
198
229
  istek = await self.httpx.get(url)
199
- secici = Selector(istek.text)
230
+ secici = HTMLParser(istek.text)
200
231
 
201
232
  nonce = self._get_nonce("video_nonce", referer=url)
202
233
 
@@ -209,9 +240,9 @@ class SetFilmIzle(PluginBase):
209
240
 
210
241
  links = []
211
242
  for player in secici.css("nav.player a"):
212
- source_id = player.css("::attr(data-post-id)").get()
213
- player_name = player.css("::attr(data-player-name)").get()
214
- part_key = player.css("::attr(data-part-key)").get()
243
+ source_id = player.attrs.get("data-post-id")
244
+ player_name = player.attrs.get("data-player-name")
245
+ part_key = player.attrs.get("data-part-key")
215
246
 
216
247
  if not source_id or "event" in source_id or source_id == "":
217
248
  continue