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,8 +1,8 @@
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, ExtractResult
4
- from parsel import Selector
5
- from Kekik.Sifreleme import StringCodec
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult
4
+ from selectolax.parser import HTMLParser
5
+ from Kekik.Sifreleme import StringCodec
6
6
  import json, re
7
7
 
8
8
  class FullHDFilmizlesene(PluginBase):
@@ -42,55 +42,102 @@ class FullHDFilmizlesene(PluginBase):
42
42
 
43
43
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
44
44
  istek = self.cloudscraper.get(f"{url}{page}")
45
- secici = Selector(istek.text)
46
-
47
- return [
48
- MainPageResult(
49
- category = category,
50
- title = veri.css("span.film-title::text").get(),
51
- url = self.fix_url(veri.css("a::attr(href)").get()),
52
- poster = self.fix_url(veri.css("img::attr(data-src)").get()),
53
- )
54
- for veri in secici.css("li.film")
55
- ]
45
+ secici = HTMLParser(istek.text)
46
+
47
+ results = []
48
+ for veri in secici.css("li.film"):
49
+ title_el = veri.css_first("span.film-title")
50
+ link_el = veri.css_first("a")
51
+ img_el = veri.css_first("img")
52
+
53
+ title = title_el.text(strip=True) if title_el else None
54
+ href = link_el.attrs.get("href") if link_el else None
55
+ poster = img_el.attrs.get("data-src") if img_el else None
56
+
57
+ if title and href:
58
+ results.append(MainPageResult(
59
+ category = category,
60
+ title = title,
61
+ url = self.fix_url(href),
62
+ poster = self.fix_url(poster) if poster else None,
63
+ ))
64
+
65
+ return results
56
66
 
57
67
  async def search(self, query: str) -> list[SearchResult]:
58
68
  istek = await self.httpx.get(f"{self.main_url}/arama/{query}")
59
- secici = Selector(istek.text)
69
+ secici = HTMLParser(istek.text)
60
70
 
61
71
  results = []
62
72
  for film in secici.css("li.film"):
63
- title = film.css("span.film-title::text").get()
64
- href = film.css("a::attr(href)").get()
65
- poster = film.css("img::attr(data-src)").get()
73
+ title_el = film.css_first("span.film-title")
74
+ link_el = film.css_first("a")
75
+ img_el = film.css_first("img")
76
+
77
+ title = title_el.text(strip=True) if title_el else None
78
+ href = link_el.attrs.get("href") if link_el else None
79
+ poster = img_el.attrs.get("data-src") if img_el else None
66
80
 
67
81
  if title and href:
68
- results.append(
69
- SearchResult(
70
- title = title.strip(),
71
- url = self.fix_url(href.strip()),
72
- poster = self.fix_url(poster.strip()) if poster else None,
73
- )
74
- )
82
+ results.append(SearchResult(
83
+ title = title,
84
+ url = self.fix_url(href),
85
+ poster = self.fix_url(poster) if poster else None,
86
+ ))
75
87
 
76
88
  return results
77
89
 
78
90
  async def load_item(self, url: str) -> MovieInfo:
79
91
  istek = await self.httpx.get(url)
80
- secici = Selector(istek.text)
81
-
82
- title = secici.xpath("normalize-space(//div[@class='izle-titles'])").get().strip()
83
- poster = secici.css("div img::attr(data-src)").get().strip()
84
- description = secici.css("div.ozet-ic p::text").get().strip()
85
- tags = secici.css("a[rel='category tag']::text").getall()
86
- rating = secici.xpath("normalize-space(//div[@class='puanx-puan'])").get().split()[-1]
87
- year = secici.css("div.dd a.category::text").get().strip().split()[0]
88
- actors = secici.css("div.film-info ul li:nth-child(2) a > span::text").getall()
89
- duration = secici.css("span.sure::text").get("0 Dakika").split()[0]
92
+ secici = HTMLParser(istek.text)
93
+ html_text = istek.text
94
+
95
+ # Title: normalize-space yerine doğrudan class ile
96
+ title_el = secici.css_first("div.izle-titles")
97
+ title = title_el.text(strip=True) if title_el else ""
98
+
99
+ img_el = secici.css_first("div img[data-src]")
100
+ poster = img_el.attrs.get("data-src", "").strip() if img_el else ""
101
+
102
+ desc_el = secici.css_first("div.ozet-ic p")
103
+ description = desc_el.text(strip=True) if desc_el else ""
104
+
105
+ tags = [a.text(strip=True) for a in secici.css("a[rel='category tag']") if a.text(strip=True)]
106
+
107
+ # Rating: normalize-space yerine doğrudan class ile ve son kelimeyi al
108
+ rating_el = secici.css_first("div.puanx-puan")
109
+ rating = None
110
+ if rating_el:
111
+ rating_text = rating_el.text(strip=True)
112
+ if rating_text:
113
+ parts = rating_text.split()
114
+ rating = parts[-1] if parts else None
115
+
116
+ # Year: ilk yıl formatında değer
117
+ year_el = secici.css_first("div.dd a.category")
118
+ year = None
119
+ if year_el:
120
+ year_text = year_el.text(strip=True)
121
+ if year_text:
122
+ parts = year_text.split()
123
+ year = parts[0] if parts else None
124
+
125
+ # Actors: nth-child yerine tüm li'leri alıp 2. index
126
+ lis = secici.css("div.film-info ul li")
127
+ actors = []
128
+ if len(lis) >= 2:
129
+ actors = [a.text(strip=True) for a in lis[1].css("a > span") if a.text(strip=True)]
130
+
131
+ duration_el = secici.css_first("span.sure")
132
+ duration = "0"
133
+ if duration_el:
134
+ duration_text = duration_el.text(strip=True)
135
+ duration_parts = duration_text.split()
136
+ duration = duration_parts[0] if duration_parts else "0"
90
137
 
91
138
  return MovieInfo(
92
139
  url = url,
93
- poster = self.fix_url(poster),
140
+ poster = self.fix_url(poster) if poster else None,
94
141
  title = title,
95
142
  description = description,
96
143
  tags = tags,
@@ -102,10 +149,18 @@ class FullHDFilmizlesene(PluginBase):
102
149
 
103
150
  async def load_links(self, url: str) -> list[ExtractResult]:
104
151
  istek = await self.httpx.get(url)
105
- secici = Selector(istek.text)
152
+ secici = HTMLParser(istek.text)
153
+ html_text = istek.text
154
+
155
+ # İlk script'i al (xpath (//script)[1] yerine)
156
+ scripts = secici.css("script")
157
+ script_content = scripts[0].text() if scripts else ""
158
+
159
+ scx_match = re.search(r"scx = (.*?);", script_content)
160
+ if not scx_match:
161
+ return []
106
162
 
107
- script = secici.xpath("(//script)[1]").get()
108
- scx_data = json.loads(re.findall(r"scx = (.*?);", script)[0])
163
+ scx_data = json.loads(scx_match.group(1))
109
164
  scx_keys = list(scx_data.keys())
110
165
 
111
166
  link_list = []
@@ -1,8 +1,8 @@
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, Subtitle, ExtractResult
4
- from parsel import Selector
5
- from Kekik.Sifreleme import Packer, StreamDecoder
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, Subtitle, ExtractResult
4
+ from selectolax.parser import HTMLParser
5
+ from Kekik.Sifreleme import Packer, StreamDecoder
6
6
  import random, string, re
7
7
 
8
8
  class HDFilmCehennemi(PluginBase):
@@ -31,17 +31,26 @@ class HDFilmCehennemi(PluginBase):
31
31
 
32
32
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
33
33
  istek = await self.httpx.get(f"{url}", follow_redirects=True)
34
- secici = Selector(istek.text)
35
-
36
- return [
37
- MainPageResult(
38
- category = category,
39
- title = veri.css("strong.poster-title::text").get(),
40
- url = self.fix_url(veri.css("::attr(href)").get()),
41
- poster = self.fix_url(veri.css("img::attr(data-src)").get()),
42
- )
43
- for veri in secici.css("div.section-content a.poster")
44
- ]
34
+ secici = HTMLParser(istek.text)
35
+
36
+ results = []
37
+ for veri in secici.css("div.section-content a.poster"):
38
+ title_el = veri.css_first("strong.poster-title")
39
+ img_el = veri.css_first("img")
40
+
41
+ title = title_el.text(strip=True) if title_el else None
42
+ href = veri.attrs.get("href")
43
+ poster = img_el.attrs.get("data-src") if img_el else None
44
+
45
+ if title and href:
46
+ results.append(MainPageResult(
47
+ category = category,
48
+ title = title,
49
+ url = self.fix_url(href),
50
+ poster = self.fix_url(poster) if poster else None,
51
+ ))
52
+
53
+ return results
45
54
 
46
55
  async def search(self, query: str) -> list[SearchResult]:
47
56
  istek = await self.httpx.get(
@@ -54,57 +63,69 @@ class HDFilmCehennemi(PluginBase):
54
63
  )
55
64
 
56
65
  results = []
57
- for veri in istek.json().get("results"):
58
- secici = Selector(veri)
59
- title = secici.css("h4.title::text").get()
60
- href = secici.css("a::attr(href)").get()
61
- poster = secici.css("img::attr(data-src)").get() or secici.css("img::attr(src)").get()
66
+ for veri in istek.json().get("results", []):
67
+ secici = HTMLParser(veri)
68
+ title_el = secici.css_first("h4.title")
69
+ link_el = secici.css_first("a")
70
+ img_el = secici.css_first("img")
71
+
72
+ title = title_el.text(strip=True) if title_el else None
73
+ href = link_el.attrs.get("href") if link_el else None
74
+ poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
62
75
 
63
76
  if title and href:
64
- results.append(
65
- SearchResult(
66
- title = title.strip(),
67
- url = self.fix_url(href.strip()),
68
- poster = self.fix_url(poster.strip()) if poster else None,
69
- )
70
- )
77
+ results.append(SearchResult(
78
+ title = title,
79
+ url = self.fix_url(href),
80
+ poster = self.fix_url(poster) if poster else None,
81
+ ))
71
82
 
72
83
  return results
73
84
 
74
85
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
75
86
  istek = await self.httpx.get(url, headers = {"Referer": f"{self.main_url}/"})
76
- secici = Selector(istek.text)
77
-
78
- title = secici.css("h1.section-title::text").get()
79
- title = title.strip() if title else ""
80
- poster = secici.css("aside.post-info-poster img.lazyload::attr(data-src)").get() or ""
81
- poster = poster.strip() if poster else ""
82
- description = secici.css("article.post-info-content > p::text").get() or ""
83
- description = description.strip() if description else ""
84
- tags = secici.css("div.post-info-genres a::text").getall()
85
- rating = secici.css("div.post-info-imdb-rating span::text").get() or ""
86
- rating = rating.strip() if rating else ""
87
- year = secici.css("div.post-info-year-country a::text").get() or ""
88
- year = year.strip() if year else ""
89
- actors = secici.css("div.post-info-cast a > strong::text").getall()
90
- duration = secici.css("div.post-info-duration::text").get() or "0"
91
- duration = duration.replace("dakika", "").strip()
87
+ secici = HTMLParser(istek.text)
88
+
89
+ title_el = secici.css_first("h1.section-title")
90
+ title = title_el.text(strip=True) if title_el else ""
91
+
92
+ poster_el = secici.css_first("aside.post-info-poster img.lazyload")
93
+ poster = poster_el.attrs.get("data-src", "").strip() if poster_el else ""
94
+
95
+ desc_el = secici.css_first("article.post-info-content > p")
96
+ description = desc_el.text(strip=True) if desc_el else ""
97
+
98
+ tags = [a.text(strip=True) for a in secici.css("div.post-info-genres a") if a.text(strip=True)]
99
+
100
+ rating_el = secici.css_first("div.post-info-imdb-rating span")
101
+ rating = rating_el.text(strip=True) if rating_el else ""
102
+
103
+ year_el = secici.css_first("div.post-info-year-country a")
104
+ year = year_el.text(strip=True) if year_el else ""
105
+
106
+ actors = [a.text(strip=True) for a in secici.css("div.post-info-cast a > strong") if a.text(strip=True)]
107
+
108
+ duration_el = secici.css_first("div.post-info-duration")
109
+ duration_str = duration_el.text(strip=True) if duration_el else "0"
110
+ duration_str = duration_str.replace("dakika", "").strip()
92
111
 
93
112
  try:
94
- duration_minutes = int(re.search(r'\d+', duration).group()) if re.search(r'\d+', duration) else 0
113
+ duration_match = re.search(r'\d+', duration_str)
114
+ duration_minutes = int(duration_match.group()) if duration_match else 0
95
115
  except Exception:
96
116
  duration_minutes = 0
97
117
 
98
118
  # Dizi mi film mi kontrol et (Kotlin referansı: div.seasons kontrolü)
99
- is_series = len(secici.css("div.seasons").getall()) > 0
119
+ is_series = len(secici.css("div.seasons")) > 0
100
120
 
101
121
  if is_series:
102
122
  episodes = []
103
123
  for ep in secici.css("div.seasons-tab-content a"):
104
- ep_name = ep.css("h4::text").get()
105
- ep_href = ep.css("::attr(href)").get()
124
+ ep_name_el = ep.css_first("h4")
125
+ ep_name = ep_name_el.text(strip=True) if ep_name_el else None
126
+ ep_href = ep.attrs.get("href")
127
+
106
128
  if ep_name and ep_href:
107
- ep_name = ep_name.strip()
108
129
  # Regex ile sezon ve bölüm numarası çıkar
109
130
  ep_match = re.search(r'(\d+)\.\s*Bölüm', ep_name)
110
131
  sz_match = re.search(r'(\d+)\.\s*Sezon', ep_name)
@@ -120,7 +141,7 @@ class HDFilmCehennemi(PluginBase):
120
141
 
121
142
  return SeriesInfo(
122
143
  url = url,
123
- poster = self.fix_url(poster),
144
+ poster = self.fix_url(poster) if poster else None,
124
145
  title = self.clean_title(title),
125
146
  description = description,
126
147
  tags = tags,
@@ -132,7 +153,7 @@ class HDFilmCehennemi(PluginBase):
132
153
  else:
133
154
  return MovieInfo(
134
155
  url = url,
135
- poster = self.fix_url(poster),
156
+ poster = self.fix_url(poster) if poster else None,
136
157
  title = self.clean_title(title),
137
158
  description = description,
138
159
  tags = tags,
@@ -207,7 +228,7 @@ class HDFilmCehennemi(PluginBase):
207
228
  return ""
208
229
 
209
230
  # 4) Özel decoder ile çöz
210
- return StreamDecoder._brute_force(parts)
231
+ return StreamDecoder.extract_stream_url(unpacked)
211
232
 
212
233
  async def invoke_local_source(self, iframe: str, source: str, url: str):
213
234
  self.httpx.headers.update({
@@ -255,15 +276,19 @@ class HDFilmCehennemi(PluginBase):
255
276
 
256
277
  async def load_links(self, url: str) -> list[ExtractResult]:
257
278
  istek = await self.httpx.get(url)
258
- secici = Selector(istek.text)
279
+ secici = HTMLParser(istek.text)
259
280
 
260
281
  results = []
261
282
  for alternatif in secici.css("div.alternative-links"):
262
- lang_code = alternatif.css("::attr(data-lang)").get().upper()
283
+ lang_code = alternatif.attrs.get("data-lang", "").upper()
263
284
 
264
285
  for link in alternatif.css("button.alternative-link"):
265
- source = f"{link.css('::text').get().replace('(HDrip Xbet)', '').strip()} {lang_code}"
266
- video_id = link.css("::attr(data-video)").get()
286
+ source_text = link.text(strip=True).replace('(HDrip Xbet)', '').strip()
287
+ source = f"{source_text} {lang_code}"
288
+ video_id = link.attrs.get("data-video")
289
+
290
+ if not video_id:
291
+ continue
267
292
 
268
293
  api_get = await self.httpx.get(
269
294
  url = f"{self.main_url}/video/{video_id}/",
@@ -1,7 +1,8 @@
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, ExtractResult
4
- from parsel import Selector
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult
4
+ from selectolax.parser import HTMLParser
5
+ import re
5
6
 
6
7
  class JetFilmizle(PluginBase):
7
8
  name = "JetFilmizle"
@@ -16,22 +17,56 @@ class JetFilmizle(PluginBase):
16
17
  f"{main_url}/editorun-secimi/page/" : "Editörün Seçimi",
17
18
  f"{main_url}/turk-film-izle/page/" : "Türk Filmleri",
18
19
  f"{main_url}/cizgi-filmler-izle/page/" : "Çizgi Filmler",
19
- f"{main_url}/kategoriler/yesilcam-filmleri-izlee/page/" : "Yeşilçam Filmleri"
20
+ f"{main_url}/kategoriler/yesilcam-filmleri-izlee/page/" : "Yeşilçam Filmleri",
21
+ f"{main_url}/film-turu/aile-filmleri-izle/page/" : "Aile Filmleri",
22
+ f"{main_url}/film-turu/aksiyon-filmleri/page/" : "Aksiyon Filmleri",
23
+ f"{main_url}/film-turu/animasyon-filmler-izle/page/" : "Animasyon Filmleri",
24
+ f"{main_url}/film-turu/bilim-kurgu-filmler/page/" : "Bilim Kurgu Filmleri",
25
+ f"{main_url}/film-turu/dram-filmleri-izle/page/" : "Dram Filmleri",
26
+ f"{main_url}/film-turu/fantastik-filmleri-izle/page/" : "Fantastik Filmler",
27
+ f"{main_url}/film-turu/gerilim-filmleri/page/" : "Gerilim Filmleri",
28
+ f"{main_url}/film-turu/gizem-filmleri/page/" : "Gizem Filmleri",
29
+ f"{main_url}/film-turu/komedi-film-full-izle/page/" : "Komedi Filmleri",
30
+ f"{main_url}/film-turu/korku-filmleri-izle/page/" : "Korku Filmleri",
31
+ f"{main_url}/film-turu/macera-filmleri/page/" : "Macera Filmleri",
32
+ f"{main_url}/film-turu/muzikal/page/" : "Müzikal Filmler",
33
+ f"{main_url}/film-turu/polisiye/page/" : "Polisiye Filmler",
34
+ f"{main_url}/film-turu/romantik-film-izle/page/" : "Romantik Filmler",
35
+ f"{main_url}/film-turu/savas-filmi-izle/page/" : "Savaş Filmleri",
36
+ f"{main_url}/film-turu/spor/page/" : "Spor Filmleri",
37
+ f"{main_url}/film-turu/suc-filmleri/page/" : "Suç Filmleri",
38
+ f"{main_url}/film-turu/tarihi-filmler/page/" : "Tarihi Filmleri",
20
39
  }
21
40
 
22
41
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
23
42
  istek = await self.httpx.get(f"{url}{page}", follow_redirects=True)
24
- secici = Selector(istek.text)
25
-
26
- return [
27
- MainPageResult(
28
- category = category,
29
- title = self.clean_title(veri.css("h2 a::text, h3 a::text, h4 a::text, h5 a::text, h6 a::text").get()),
30
- url = self.fix_url(veri.css("a::attr(href)").get()),
31
- poster = self.fix_url(veri.css("img::attr(data-src)").get() or veri.css("img::attr(src)").get()),
32
- )
33
- for veri in secici.css("article.movie") if veri.css("h2 a::text, h3 a::text, h4 a::text, h5 a::text, h6 a::text").get()
34
- ]
43
+ secici = HTMLParser(istek.text)
44
+
45
+ results = []
46
+ for veri in secici.css("article.movie"):
47
+ # h2-h6 içindeki a linki
48
+ title_link = None
49
+ for h_tag in ["h2", "h3", "h4", "h5", "h6"]:
50
+ title_link = veri.css_first(f"{h_tag} a")
51
+ if title_link:
52
+ break
53
+
54
+ link_el = veri.css_first("a")
55
+ img_el = veri.css_first("img")
56
+
57
+ title = self.clean_title(title_link.text(strip=True)) if title_link else None
58
+ href = link_el.attrs.get("href") if link_el else None
59
+ poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
60
+
61
+ if title and href:
62
+ results.append(MainPageResult(
63
+ category = category,
64
+ title = title,
65
+ url = self.fix_url(href),
66
+ poster = self.fix_url(poster) if poster else None,
67
+ ))
68
+
69
+ return results
35
70
 
36
71
  async def search(self, query: str) -> list[SearchResult]:
37
72
  istek = await self.httpx.post(
@@ -39,55 +74,66 @@ class JetFilmizle(PluginBase):
39
74
  data = {"s": query},
40
75
  headers = {"Referer": f"{self.main_url}/"}
41
76
  )
42
- secici = Selector(istek.text)
77
+ secici = HTMLParser(istek.text)
43
78
 
44
79
  results = []
45
80
  for article in secici.css("article.movie"):
46
- title = self.clean_title(article.css("h2 a::text, h3 a::text, h4 a::text, h5 a::text, h6 a::text").get())
47
- href = article.css("a::attr(href)").get()
48
- poster = article.css("img::attr(data-src)").get() or article.css("img::attr(src)").get()
81
+ # h2-h6 içindeki a linki
82
+ title_link = None
83
+ for h_tag in ["h2", "h3", "h4", "h5", "h6"]:
84
+ title_link = article.css_first(f"{h_tag} a")
85
+ if title_link:
86
+ break
87
+
88
+ link_el = article.css_first("a")
89
+ img_el = article.css_first("img")
90
+
91
+ title = self.clean_title(title_link.text(strip=True)) if title_link else None
92
+ href = link_el.attrs.get("href") if link_el else None
93
+ poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
49
94
 
50
95
  if title and href:
51
- results.append(
52
- SearchResult(
53
- title = title.strip(),
54
- url = self.fix_url(href.strip()),
55
- poster = self.fix_url(poster.strip()) if poster else None,
56
- )
57
- )
96
+ results.append(SearchResult(
97
+ title = title,
98
+ url = self.fix_url(href),
99
+ poster = self.fix_url(poster) if poster else None,
100
+ ))
58
101
 
59
102
  return results
60
103
 
61
104
  async def load_item(self, url: str) -> MovieInfo:
62
105
  istek = await self.httpx.get(url)
63
- secici = Selector(istek.text)
106
+ secici = HTMLParser(istek.text)
107
+ html_text = istek.text
64
108
 
65
- title = self.clean_title(secici.css("div.movie-exp-title::text").get())
66
- poster_raw = secici.css("section.movie-exp img::attr(data-src), section.movie-exp img::attr(src)").get()
67
- poster = poster_raw.strip() if poster_raw else None
68
-
69
- desc_raw = secici.css("section.movie-exp p.aciklama::text").get()
70
- description = desc_raw.strip() if desc_raw else None
109
+ title_el = secici.css_first("div.movie-exp-title")
110
+ title = self.clean_title(title_el.text(strip=True)) if title_el else None
111
+
112
+ img_el = secici.css_first("section.movie-exp img")
113
+ poster_raw = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
114
+ poster = poster_raw.strip() if poster_raw else None
71
115
 
72
- tags = secici.css("section.movie-exp div.catss a::text").getall()
116
+ desc_el = secici.css_first("section.movie-exp p.aciklama")
117
+ description = desc_el.text(strip=True) if desc_el else None
73
118
 
74
- rating_raw = secici.css("section.movie-exp div.imdb_puan span::text").get()
75
- rating = rating_raw.strip() if rating_raw else None
119
+ tags = [a.text(strip=True) for a in secici.css("section.movie-exp div.catss a") if a.text(strip=True)]
76
120
 
121
+ rating_el = secici.css_first("section.movie-exp div.imdb_puan span")
122
+ rating = rating_el.text(strip=True) if rating_el else None
77
123
 
78
- # Year - div.yap içinde 4 haneli sayı ara
79
- year_div = secici.xpath("//div[@class='yap' and (contains(., 'Vizyon') or contains(., 'Yapım'))]/text()").get()
124
+ # Year - div.yap içinde 4 haneli sayı ara (xpath yerine regex)
80
125
  year = None
81
- if year_div:
82
- year_match = re.search(r'(\d{4})', year_div.strip())
126
+ yap_match = re.search(r'<div class="yap"[^>]*>([^<]*(?:Vizyon|Yapım)[^<]*)</div>', html_text, re.IGNORECASE)
127
+ if yap_match:
128
+ year_match = re.search(r'(\d{4})', yap_match.group(1))
83
129
  if year_match:
84
130
  year = year_match.group(1)
85
131
 
86
- actors = secici.css("div[itemprop='actor'] a span::text").getall()
132
+ actors = [a.text(strip=True) for a in secici.css("div[itemprop='actor'] a span") if a.text(strip=True)]
87
133
 
88
134
  return MovieInfo(
89
135
  url = url,
90
- poster = self.fix_url(poster),
136
+ poster = self.fix_url(poster) if poster else None,
91
137
  title = title,
92
138
  description = description,
93
139
  tags = tags,
@@ -98,15 +144,15 @@ class JetFilmizle(PluginBase):
98
144
 
99
145
  async def load_links(self, url: str) -> list[ExtractResult]:
100
146
  istek = await self.httpx.get(url)
101
- secici = Selector(istek.text)
147
+ secici = HTMLParser(istek.text)
102
148
 
103
149
  results = []
104
150
 
105
151
  # 1) Ana iframe'leri kontrol et
106
152
  for iframe in secici.css("iframe"):
107
- src = (iframe.css("::attr(src)").get() or
108
- iframe.css("::attr(data-src)").get() or
109
- iframe.css("::attr(data-lazy-src)").get())
153
+ src = (iframe.attrs.get("src") or
154
+ iframe.attrs.get("data-src") or
155
+ iframe.attrs.get("data-lazy-src"))
110
156
 
111
157
  if src and src != "about:blank":
112
158
  iframe_url = self.fix_url(src)
@@ -117,9 +163,10 @@ class JetFilmizle(PluginBase):
117
163
  # 2) Sayfa numaralarından linkleri topla (Fragman hariç)
118
164
  page_links = []
119
165
  for link in secici.css("a.post-page-numbers"):
120
- isim = link.css("span::text").get() or ""
166
+ span_el = link.css_first("span")
167
+ isim = span_el.text(strip=True) if span_el else ""
121
168
  if isim != "Fragman":
122
- href = link.css("::attr(href)").get()
169
+ href = link.attrs.get("href")
123
170
  if href:
124
171
  page_links.append((self.fix_url(href), isim))
125
172
 
@@ -127,12 +174,12 @@ class JetFilmizle(PluginBase):
127
174
  for page_url, isim in page_links:
128
175
  try:
129
176
  page_resp = await self.httpx.get(page_url)
130
- page_sel = Selector(page_resp.text)
177
+ page_sel = HTMLParser(page_resp.text)
131
178
 
132
179
  for iframe in page_sel.css("div#movie iframe"):
133
- src = (iframe.css("::attr(src)").get() or
134
- iframe.css("::attr(data-src)").get() or
135
- iframe.css("::attr(data-lazy-src)").get())
180
+ src = (iframe.attrs.get("src") or
181
+ iframe.attrs.get("data-src") or
182
+ iframe.attrs.get("data-lazy-src"))
136
183
 
137
184
  if src and src != "about:blank":
138
185
  iframe_url = self.fix_url(src)