KekikStream 2.2.9__py3-none-any.whl → 2.3.9__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 (62) hide show
  1. KekikStream/Core/HTMLHelper.py +134 -0
  2. KekikStream/Core/Plugin/PluginBase.py +22 -4
  3. KekikStream/Core/Plugin/PluginLoader.py +3 -2
  4. KekikStream/Core/Plugin/PluginManager.py +2 -2
  5. KekikStream/Core/__init__.py +2 -0
  6. KekikStream/Extractors/CloseLoad.py +12 -13
  7. KekikStream/Extractors/ContentX.py +33 -31
  8. KekikStream/Extractors/DonilasPlay.py +10 -10
  9. KekikStream/Extractors/DzenRu.py +3 -3
  10. KekikStream/Extractors/ExPlay.py +10 -10
  11. KekikStream/Extractors/Filemoon.py +11 -16
  12. KekikStream/Extractors/JetTv.py +4 -4
  13. KekikStream/Extractors/MixPlayHD.py +10 -11
  14. KekikStream/Extractors/MolyStream.py +16 -9
  15. KekikStream/Extractors/Odnoklassniki.py +4 -4
  16. KekikStream/Extractors/PeaceMakerst.py +3 -3
  17. KekikStream/Extractors/PixelDrain.py +6 -5
  18. KekikStream/Extractors/PlayerFilmIzle.py +6 -10
  19. KekikStream/Extractors/RapidVid.py +8 -7
  20. KekikStream/Extractors/SetPlay.py +10 -10
  21. KekikStream/Extractors/SetPrime.py +3 -6
  22. KekikStream/Extractors/SibNet.py +4 -5
  23. KekikStream/Extractors/Sobreatsesuyp.py +5 -5
  24. KekikStream/Extractors/TRsTX.py +5 -5
  25. KekikStream/Extractors/TurboImgz.py +3 -4
  26. KekikStream/Extractors/TurkeyPlayer.py +5 -5
  27. KekikStream/Extractors/VidHide.py +4 -7
  28. KekikStream/Extractors/VidMoly.py +37 -25
  29. KekikStream/Extractors/VidMoxy.py +8 -9
  30. KekikStream/Extractors/VidPapi.py +5 -7
  31. KekikStream/Extractors/VideoSeyred.py +3 -3
  32. KekikStream/Plugins/BelgeselX.py +40 -51
  33. KekikStream/Plugins/DiziBox.py +53 -81
  34. KekikStream/Plugins/DiziPal.py +50 -72
  35. KekikStream/Plugins/DiziYou.py +96 -83
  36. KekikStream/Plugins/Dizilla.py +95 -107
  37. KekikStream/Plugins/FilmBip.py +29 -50
  38. KekikStream/Plugins/FilmMakinesi.py +84 -46
  39. KekikStream/Plugins/FilmModu.py +27 -41
  40. KekikStream/Plugins/FullHDFilm.py +57 -62
  41. KekikStream/Plugins/FullHDFilmizlesene.py +32 -57
  42. KekikStream/Plugins/HDFilmCehennemi.py +51 -65
  43. KekikStream/Plugins/JetFilmizle.py +38 -51
  44. KekikStream/Plugins/KultFilmler.py +43 -67
  45. KekikStream/Plugins/RecTV.py +34 -9
  46. KekikStream/Plugins/RoketDizi.py +89 -111
  47. KekikStream/Plugins/SelcukFlix.py +102 -93
  48. KekikStream/Plugins/SetFilmIzle.py +65 -75
  49. KekikStream/Plugins/SezonlukDizi.py +47 -65
  50. KekikStream/Plugins/Sinefy.py +70 -70
  51. KekikStream/Plugins/SinemaCX.py +31 -55
  52. KekikStream/Plugins/Sinezy.py +27 -54
  53. KekikStream/Plugins/SuperFilmGeldi.py +25 -44
  54. KekikStream/Plugins/UgurFilm.py +23 -48
  55. KekikStream/Plugins/YabanciDizi.py +285 -0
  56. {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/METADATA +1 -1
  57. kekikstream-2.3.9.dist-info/RECORD +84 -0
  58. kekikstream-2.2.9.dist-info/RECORD +0 -82
  59. {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/WHEEL +0 -0
  60. {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/entry_points.txt +0 -0
  61. {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/licenses/LICENSE +0 -0
  62. {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/top_level.txt +0 -0
@@ -1,8 +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
4
- from selectolax.parser import HTMLParser
5
- import re, asyncio
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult, HTMLHelper
4
+ import asyncio
6
5
 
7
6
  class SezonlukDizi(PluginBase):
8
7
  name = "SezonlukDizi"
@@ -41,26 +40,24 @@ class SezonlukDizi(PluginBase):
41
40
 
42
41
  async def _get_asp_data(self) -> dict:
43
42
  js_req = await self.httpx.get(f"{self.main_url}/js/site.min.js")
44
- alt_match = re.search(r"dataAlternatif(.*?)\.asp", js_req.text)
45
- embed_match = re.search(r"dataEmbed(.*?)\.asp", js_req.text)
43
+ js = HTMLHelper(js_req.text)
44
+ alt = js.regex_first(r"dataAlternatif(.*?)\.asp")
45
+ emb = js.regex_first(r"dataEmbed(.*?)\.asp")
46
46
 
47
47
  return {
48
- "alternatif": alt_match.group(1) if alt_match else "",
49
- "embed": embed_match.group(1) if embed_match else ""
48
+ "alternatif": alt or "",
49
+ "embed": emb or ""
50
50
  }
51
51
 
52
52
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
53
53
  istek = await self.httpx.get(f"{url}{page}")
54
- secici = HTMLParser(istek.text)
54
+ secici = HTMLHelper(istek.text)
55
55
 
56
56
  results = []
57
- for veri in secici.css("div.afis a"):
58
- desc_el = veri.css_first("div.description")
59
- img_el = veri.css_first("img")
60
-
61
- title = desc_el.text(strip=True) if desc_el else None
62
- href = veri.attrs.get("href")
63
- poster = img_el.attrs.get("data-src") if img_el else None
57
+ for veri in secici.select("div.afis a"):
58
+ title = secici.select_text("div.description", veri)
59
+ href = secici.select_attr("a", "href", veri)
60
+ poster = secici.select_attr("img", "data-src", veri)
64
61
 
65
62
  if title and href:
66
63
  results.append(MainPageResult(
@@ -74,16 +71,13 @@ class SezonlukDizi(PluginBase):
74
71
 
75
72
  async def search(self, query: str) -> list[SearchResult]:
76
73
  istek = await self.httpx.get(f"{self.main_url}/diziler.asp?adi={query}")
77
- secici = HTMLParser(istek.text)
74
+ secici = HTMLHelper(istek.text)
78
75
 
79
76
  results = []
80
- for afis in secici.css("div.afis a"):
81
- desc_el = afis.css_first("div.description")
82
- img_el = afis.css_first("img")
83
-
84
- title = desc_el.text(strip=True) if desc_el else None
85
- href = afis.attrs.get("href")
86
- poster = img_el.attrs.get("data-src") if img_el else None
77
+ for afis in secici.select("div.afis a"):
78
+ title = secici.select_text("div.description", afis)
79
+ href = secici.select_attr("a", "href", afis)
80
+ poster = secici.select_attr("img", "data-src", afis)
87
81
 
88
82
  if title and href:
89
83
  results.append(SearchResult(
@@ -96,67 +90,56 @@ class SezonlukDizi(PluginBase):
96
90
 
97
91
  async def load_item(self, url: str) -> SeriesInfo:
98
92
  istek = await self.httpx.get(url)
99
- secici = HTMLParser(istek.text)
93
+ secici = HTMLHelper(istek.text)
100
94
 
101
- title_el = secici.css_first("div.header")
102
- title = title_el.text(strip=True) if title_el else ""
95
+ title = secici.select_text("div.header") or ""
103
96
 
104
- poster_el = secici.css_first("div.image img")
105
- poster = poster_el.attrs.get("data-src", "").strip() if poster_el else ""
97
+ poster = secici.select_attr("div.image img", "data-src") or ""
106
98
 
107
99
  # year: re_first yerine re.search
108
- year_el = secici.css_first("div.extra span")
109
- year_text = year_el.text(strip=True) if year_el else ""
110
- year_match = re.search(r"(\d{4})", year_text)
111
- year = year_match.group(1) if year_match else None
100
+ year_text = secici.select_text("div.extra span") or ""
101
+ year = secici.regex_first(r"(\d{4})", year_text)
112
102
 
113
103
  # xpath normalized-space yerine doğrudan ID ile element bulup text al
114
- desc_el = secici.css_first("span#tartismayorum-konu")
115
- description = desc_el.text(strip=True) if desc_el else ""
104
+ description = secici.select_text("span#tartismayorum-konu") or ""
116
105
 
117
- tags = [a.text(strip=True) for a in secici.css("div.labels a[href*='tur']") if a.text(strip=True)]
106
+ tags = [a.text(strip=True) for a in secici.select("div.labels a[href*='tur']") if a.text(strip=True)]
118
107
 
119
- # rating: re_first yerine re.search
120
- rating_el = secici.css_first("div.dizipuani a div")
121
- rating_text = rating_el.text(strip=True) if rating_el else ""
122
- rating_match = re.search(r"[\d.,]+", rating_text)
123
- rating = rating_match.group() if rating_match else None
108
+ # rating: regex ile çıkar
109
+ rating_text = secici.select_text("div.dizipuani a div") or ""
110
+ rating = secici.regex_first(r"[\d.,]+", rating_text)
124
111
 
125
112
  actors = []
126
113
 
127
114
  actors_istek = await self.httpx.get(f"{self.main_url}/oyuncular/{url.split('/')[-1]}")
128
- actors_secici = HTMLParser(actors_istek.text)
129
- for actor in actors_secici.css("div.doubling div.ui"):
130
- header_el = actor.css_first("div.header")
131
- if header_el and header_el.text(strip=True):
132
- actors.append(header_el.text(strip=True))
115
+ actors_secici = HTMLHelper(actors_istek.text)
116
+ for actor in actors_secici.select("div.doubling div.ui"):
117
+ header_text = actors_secici.select_text("div.header", actor)
118
+ if header_text:
119
+ actors.append(header_text)
133
120
 
134
121
  episodes_istek = await self.httpx.get(f"{self.main_url}/bolumler/{url.split('/')[-1]}")
135
- episodes_secici = HTMLParser(episodes_istek.text)
122
+ episodes_secici = HTMLHelper(episodes_istek.text)
136
123
  episodes = []
137
124
 
138
- for sezon in episodes_secici.css("table.unstackable"):
139
- for bolum in sezon.css("tbody tr"):
125
+ for sezon in episodes_secici.select("table.unstackable"):
126
+ for bolum in episodes_secici.select("tbody tr", sezon):
140
127
  # td:nth-of-type selectolax'ta desteklenmiyor, alternatif yol: tüm td'leri alıp indexle
141
- tds = bolum.css("td")
128
+ tds = episodes_secici.select("td", bolum)
142
129
  if len(tds) < 4:
143
130
  continue
144
131
 
145
132
  # 4. td'den isim ve href
146
- ep_name_el = tds[3].css_first("a")
147
- ep_name = ep_name_el.text(strip=True) if ep_name_el else None
148
- ep_href = ep_name_el.attrs.get("href") if ep_name_el else None
133
+ ep_name = episodes_secici.select_text("a", tds[3])
134
+ ep_href = episodes_secici.select_attr("a", "href", tds[3])
149
135
 
150
- # 3. td'den episode (re_first yerine re.search)
151
- ep_episode_el = tds[2].css_first("a")
152
- ep_episode_text = ep_episode_el.text(strip=True) if ep_episode_el else ""
153
- ep_episode_match = re.search(r"(\d+)", ep_episode_text)
154
- ep_episode = ep_episode_match.group(1) if ep_episode_match else None
136
+ # 3. td'den episode (re_first yerine regex)
137
+ ep_episode_text = episodes_secici.select_text("a", tds[2]) or ""
138
+ ep_episode = episodes_secici.regex_first(r"(\d+)", ep_episode_text)
155
139
 
156
- # 2. td'den season (re_first yerine re.search)
140
+ # 2. td'den season (re_first yerine regex)
157
141
  ep_season_text = tds[1].text(strip=True) if tds[1] else ""
158
- ep_season_match = re.search(r"(\d+)", ep_season_text)
159
- ep_season = ep_season_match.group(1) if ep_season_match else None
142
+ ep_season = secici.regex_first(r"(\d+)", ep_season_text)
160
143
 
161
144
  if ep_name and ep_href:
162
145
  episode = Episode(
@@ -181,10 +164,10 @@ class SezonlukDizi(PluginBase):
181
164
 
182
165
  async def load_links(self, url: str) -> list[ExtractResult]:
183
166
  istek = await self.httpx.get(url)
184
- secici = HTMLParser(istek.text)
167
+ secici = HTMLHelper(istek.text)
185
168
  asp_data = await self._get_asp_data()
186
169
 
187
- bid = secici.css_first("div#dilsec").attrs.get("data-id") if secici.css_first("div#dilsec") else None
170
+ bid = secici.select_attr("div#dilsec", "data-id")
188
171
  if not bid:
189
172
  return []
190
173
 
@@ -199,9 +182,8 @@ class SezonlukDizi(PluginBase):
199
182
  headers = {"X-Requested-With": "XMLHttpRequest"},
200
183
  data = {"id": str(veri.get("id"))}
201
184
  )
202
- embed_secici = HTMLParser(embed_resp.text)
203
- iframe_el = embed_secici.css_first("iframe")
204
- iframe_src = iframe_el.attrs.get("src") if iframe_el else None
185
+ embed_secici = HTMLHelper(embed_resp.text)
186
+ iframe_src = embed_secici.select_attr("iframe", "src")
205
187
 
206
188
  if iframe_src:
207
189
  if "link.asp" in iframe_src:
@@ -1,8 +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, MovieInfo, ExtractResult
4
- from selectolax.parser import HTMLParser
5
- import re, json, urllib.parse
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, MovieInfo, ExtractResult, HTMLHelper
4
+ import json, urllib.parse
6
5
 
7
6
  class Sinefy(PluginBase):
8
7
  name = "Sinefy"
@@ -44,17 +43,13 @@ class Sinefy(PluginBase):
44
43
  full_url = f"{url}&page={page}"
45
44
 
46
45
  resp = await self.httpx.get(full_url)
47
- sel = HTMLParser(resp.text)
46
+ sel = HTMLHelper(resp.text)
48
47
 
49
48
  results = []
50
- for item in sel.css("div.poster-with-subject, div.dark-segment div.poster-md.poster"):
51
- h2_el = item.css_first("h2")
52
- link_el = item.css_first("a")
53
- img_el = item.css_first("img")
54
-
55
- title = h2_el.text(strip=True) if h2_el else None
56
- href = link_el.attrs.get("href") if link_el else None
57
- poster = img_el.attrs.get("data-srcset") if img_el else None
49
+ for item in sel.select("div.poster-with-subject, div.dark-segment div.poster-md.poster"):
50
+ title = sel.select_text("h2", item)
51
+ href = sel.select_attr("a", "href", item)
52
+ poster = sel.select_attr("img", "data-srcset", item)
58
53
 
59
54
  if poster:
60
55
  poster = poster.split(",")[0].split(" ")[0]
@@ -76,13 +71,10 @@ class Sinefy(PluginBase):
76
71
 
77
72
  try:
78
73
  resp = await self.httpx.get(self.main_url)
79
- sel = HTMLParser(resp.text)
80
-
81
- cke_el = sel.css_first("input[name='cKey']")
82
- cval_el = sel.css_first("input[name='cValue']")
74
+ sel = HTMLHelper(resp.text)
83
75
 
84
- cke = cke_el.attrs.get("value") if cke_el else None
85
- cval = cval_el.attrs.get("value") if cval_el else None
76
+ cke = sel.select_attr("input[name='cKey']", "value")
77
+ cval = sel.select_attr("input[name='cValue']", "value")
86
78
 
87
79
  if cke and cval:
88
80
  c_key = cke
@@ -145,79 +137,88 @@ class Sinefy(PluginBase):
145
137
 
146
138
  async def load_item(self, url: str) -> SeriesInfo:
147
139
  resp = await self.httpx.get(url)
148
- sel = HTMLParser(resp.text)
140
+ sel = HTMLHelper(resp.text)
149
141
 
150
- title_el = sel.css_first("h1")
151
- title = title_el.text(strip=True) if title_el else None
142
+ title = sel.select_text("h1")
152
143
 
153
- img_el = sel.css_first("div.ui.items img")
154
- poster_info = img_el.attrs.get("data-srcset") if img_el else None
155
- poster = None
144
+ poster_info = sel.select_attr("div.ui.items img", "data-srcset")
145
+ poster = None
156
146
  if poster_info:
157
- # take 1x
158
147
  parts = str(poster_info).split(",")
159
148
  for p in parts:
160
149
  if "1x" in p:
161
150
  poster = p.strip().split(" ")[0]
162
151
  break
163
-
164
- desc_el = sel.css_first("p#tv-series-desc")
165
- description = desc_el.text(strip=True) if desc_el else None
166
152
 
167
- tags = [a.text(strip=True) for a in sel.css("div.item.categories a") if a.text(strip=True)]
153
+ description = sel.select_text("p#tv-series-desc")
168
154
 
169
- rating_el = sel.css_first("span.color-imdb")
170
- rating = rating_el.text(strip=True) if rating_el else None
155
+ tags = [a.text(strip=True) for a in sel.select("div.item.categories a") if a.text(strip=True)]
171
156
 
172
- actors = [h5.text(strip=True) for h5 in sel.css("div.content h5") if h5.text(strip=True)]
157
+ rating = sel.select_text("span.color-imdb")
173
158
 
174
- year_el = sel.css_first("span.item.year")
175
- year = year_el.text(strip=True) if year_el else None
159
+ actors = [h5.text(strip=True) for h5 in sel.select("div.content h5") if h5.text(strip=True)]
160
+
161
+ year = sel.select_text("span.item.year")
162
+ if not year and title:
163
+ # Try to extract year from title like "Movie Name(2024)"
164
+ year_match = sel.regex_first(r"\((\d{4})\)", title)
165
+ if year_match:
166
+ year = year_match
176
167
 
177
168
  episodes = []
178
- season_elements = sel.css("section.episodes-box")
169
+ episodes_box_list = sel.select("section.episodes-box")
179
170
 
180
- if season_elements:
181
- # Get season links
182
- season_links = []
183
- menu = sel.css("div.ui.vertical.fluid.tabular.menu a")
184
- for link in menu:
185
- href = link.attrs.get("href")
186
- if href:
187
- season_links.append(self.fix_url(href))
171
+ if episodes_box_list:
172
+ episodes_box = episodes_box_list[0]
173
+ # Sezon menüsünden sezon linklerini al
174
+ season_menu = sel.select("div.ui.vertical.fluid.tabular.menu a.item", episodes_box)
188
175
 
189
- for s_url in season_links:
190
- target_url = s_url if "/bolum-" in s_url else f"{s_url}/bolum-1"
191
-
192
- try:
193
- s_resp = await self.httpx.get(target_url)
194
- s_sel = HTMLParser(s_resp.text)
195
- ep_links = s_sel.css("div.ui.list.celled a.item")
176
+ # Sezon tab içeriklerini al
177
+ season_tabs = sel.select("div.ui.tab", episodes_box)
178
+
179
+ # Eğer birden fazla sezon varsa, her sezon tab'ından bölümleri çek
180
+ if season_tabs:
181
+ for idx, season_tab in enumerate(season_tabs):
182
+ # Sezon numarasını belirle
183
+ current_season_no = idx + 1
184
+
185
+ # Menüden sezon numarasını almaya çalış
186
+ if idx < len(season_menu):
187
+ menu_href = season_menu[idx].attrs.get("href", "")
188
+ match = sel.regex_first(r"sezon-(\d+)", menu_href)
189
+ if match:
190
+ current_season_no = int(match)
196
191
 
197
- current_season_no = 1
198
- match = re.search(r"sezon-(\d+)", target_url)
199
- if match:
200
- current_season_no = int(match.group(1))
192
+ # Bu sezon tab'ından bölüm linklerini çek
193
+ ep_links = sel.select("a[href*='bolum']", season_tab)
201
194
 
195
+ seen_urls = set()
202
196
  for ep_link in ep_links:
203
197
  href = ep_link.attrs.get("href")
204
- name_el = ep_link.css_first("div.content div.header")
205
- name = name_el.text(strip=True) if name_el else ""
198
+ if not href or href in seen_urls:
199
+ continue
200
+ seen_urls.add(href)
201
+
202
+ # Bölüm numarasını URL'den çıkar
203
+ ep_no = 0
204
+ match_ep = sel.regex_first(r"bolum-(\d+)", href)
205
+ if match_ep:
206
+ ep_no = int(match_ep)
206
207
 
207
- if href:
208
- ep_no = 0
209
- match_ep = re.search(r"bolum-(\d+)", href)
210
- if match_ep:
211
- ep_no = int(match_ep.group(1))
212
-
208
+ # Bölüm başlığını çıkar (önce title attribute, sonra text)
209
+ name = ep_link.attrs.get("title", "")
210
+ if not name:
211
+ name = sel.select_text("div.content div.header", ep_link)
212
+ if not name:
213
+ name = ep_link.text(strip=True)
214
+
215
+ if href and ep_no > 0:
213
216
  episodes.append(Episode(
214
- season = current_season_no,
217
+ season = current_season_no,
215
218
  episode = ep_no,
216
- title = name,
217
- url = self.fix_url(href)
219
+ title = name.strip() if name else f"{ep_no}. Bölüm",
220
+ url = self.fix_url(href)
218
221
  ))
219
- except Exception:
220
- pass
221
222
 
222
223
  if episodes:
223
224
  return SeriesInfo(
@@ -245,10 +246,9 @@ class Sinefy(PluginBase):
245
246
 
246
247
  async def load_links(self, url: str) -> list[ExtractResult]:
247
248
  resp = await self.httpx.get(url)
248
- sel = HTMLParser(resp.text)
249
+ sel = HTMLHelper(resp.text)
249
250
 
250
- iframe_el = sel.css_first("iframe")
251
- iframe = iframe_el.attrs.get("src") if iframe_el else None
251
+ iframe = sel.select_attr("iframe", "src")
252
252
 
253
253
  if not iframe:
254
254
  return []
@@ -1,8 +1,7 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
3
  from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Subtitle, ExtractResult
4
- from selectolax.parser import HTMLParser
5
- import re
4
+ from KekikStream.Core.HTMLHelper import HTMLHelper
6
5
 
7
6
  class SinemaCX(PluginBase):
8
7
  name = "SinemaCX"
@@ -39,23 +38,16 @@ class SinemaCX(PluginBase):
39
38
 
40
39
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
41
40
  istek = await self.httpx.get(url.replace("SAYFA", str(page)))
42
- secici = HTMLParser(istek.text)
41
+ secici = HTMLHelper(istek.text)
43
42
 
44
43
  results = []
45
- for veri in secici.css("div.son div.frag-k, div.icerik div.frag-k"):
46
- span_el = veri.css_first("div.yanac span")
47
- if not span_el:
48
- continue
49
-
50
- title = span_el.text(strip=True)
44
+ for veri in secici.select("div.son div.frag-k, div.icerik div.frag-k"):
45
+ title = secici.select_text("div.yanac span", veri)
51
46
  if not title:
52
47
  continue
53
48
 
54
- link_el = veri.css_first("div.yanac a")
55
- img_el = veri.css_first("a.resim img")
56
-
57
- href = link_el.attrs.get("href") if link_el else None
58
- poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
49
+ href = secici.select_attr("div.yanac a", "href", veri)
50
+ poster = secici.select_attr("a.resim img", "data-src", veri) or secici.select_attr("a.resim img", "src", veri)
59
51
 
60
52
  results.append(MainPageResult(
61
53
  category = category,
@@ -68,23 +60,16 @@ class SinemaCX(PluginBase):
68
60
 
69
61
  async def search(self, query: str) -> list[SearchResult]:
70
62
  istek = await self.httpx.get(f"{self.main_url}/?s={query}")
71
- secici = HTMLParser(istek.text)
63
+ secici = HTMLHelper(istek.text)
72
64
 
73
65
  results = []
74
- for veri in secici.css("div.icerik div.frag-k"):
75
- span_el = veri.css_first("div.yanac span")
76
- if not span_el:
77
- continue
78
-
79
- title = span_el.text(strip=True)
66
+ for veri in secici.select("div.icerik div.frag-k"):
67
+ title = secici.select_text("div.yanac span", veri)
80
68
  if not title:
81
69
  continue
82
70
 
83
- link_el = veri.css_first("div.yanac a")
84
- img_el = veri.css_first("a.resim img")
85
-
86
- href = link_el.attrs.get("href") if link_el else None
87
- poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
71
+ href = secici.select_attr("div.yanac a", "href", veri)
72
+ poster = secici.select_attr("a.resim img", "data-src", veri) or secici.select_attr("a.resim img", "src", veri)
88
73
 
89
74
  results.append(SearchResult(
90
75
  title = title,
@@ -96,29 +81,21 @@ class SinemaCX(PluginBase):
96
81
 
97
82
  async def load_item(self, url: str) -> MovieInfo:
98
83
  istek = await self.httpx.get(url)
99
- secici = HTMLParser(istek.text)
84
+ secici = HTMLHelper(istek.text)
100
85
 
101
- duration_match = re.search(r"Süre:.*?(\d+)\s*Dakika", istek.text)
86
+ duration_match = secici.regex_first(r"Süre:.*?(\d+)\s*Dakika")
102
87
 
103
- desc_el = secici.css_first("div.ackl div.scroll-liste")
104
- description = desc_el.text(strip=True) if desc_el else None
88
+ description = secici.select_text("div.ackl div.scroll-liste")
105
89
 
106
- link_el = secici.css_first("link[rel='image_src']")
107
- poster = link_el.attrs.get("href") if link_el else None
90
+ poster = secici.select_attr("link[rel='image_src']", "href")
108
91
 
109
- title_el = secici.css_first("div.f-bilgi h1")
110
- title = title_el.text(strip=True) if title_el else None
92
+ title = secici.select_text("div.f-bilgi h1")
111
93
 
112
- tags = [a.text(strip=True) for a in secici.css("div.f-bilgi div.tur a") if a.text(strip=True)]
94
+ tags = secici.select_all_text("div.f-bilgi div.tur a")
113
95
 
114
- year_el = secici.css_first("div.f-bilgi ul.detay a[href*='yapim']")
115
- year = year_el.text(strip=True) if year_el else None
96
+ year = secici.select_text("div.f-bilgi ul.detay a[href*='yapim']")
116
97
 
117
- actors = []
118
- for li in secici.css("li.oync li.oyuncu-k"):
119
- isim_el = li.css_first("span.isim")
120
- if isim_el and isim_el.text(strip=True):
121
- actors.append(isim_el.text(strip=True))
98
+ actors = secici.select_all_text("li.oync li.oyuncu-k span.isim")
122
99
 
123
100
  return MovieInfo(
124
101
  url = url,
@@ -128,14 +105,14 @@ class SinemaCX(PluginBase):
128
105
  tags = tags,
129
106
  year = year,
130
107
  actors = actors,
131
- duration = int(duration_match[1]) if duration_match else None,
108
+ duration = int(duration_match) if duration_match else None,
132
109
  )
133
110
 
134
111
  async def load_links(self, url: str) -> list[ExtractResult]:
135
112
  istek = await self.httpx.get(url)
136
- secici = HTMLParser(istek.text)
113
+ secici = HTMLHelper(istek.text)
137
114
 
138
- iframe_list = [iframe.attrs.get("data-vsrc") for iframe in secici.css("iframe") if iframe.attrs.get("data-vsrc")]
115
+ iframe_list = secici.select_all_attr("iframe", "data-vsrc")
139
116
 
140
117
  # Sadece fragman varsa /2/ sayfasından dene
141
118
  has_only_trailer = all(
@@ -146,8 +123,9 @@ class SinemaCX(PluginBase):
146
123
  if has_only_trailer:
147
124
  alt_url = url.rstrip("/") + "/2/"
148
125
  alt_istek = await self.httpx.get(alt_url)
149
- alt_sec = HTMLParser(alt_istek.text)
150
- iframe_list = [iframe.attrs.get("data-vsrc") for iframe in alt_sec.css("iframe") if iframe.attrs.get("data-vsrc")]
126
+ alt_istek = await self.httpx.get(alt_url)
127
+ alt_sec = HTMLHelper(alt_istek.text)
128
+ iframe_list = alt_sec.select_all_attr("iframe", "data-vsrc")
151
129
 
152
130
  if not iframe_list:
153
131
  return []
@@ -164,17 +142,15 @@ class SinemaCX(PluginBase):
164
142
  iframe_text = iframe_istek.text
165
143
 
166
144
  subtitles = []
167
- sub_match = re.search(r'playerjsSubtitle\s*=\s*"(.+?)"', iframe_text)
168
- if sub_match:
169
- sub_section = sub_match[1]
170
- for sub in re.finditer(r'\[(.*?)](https?://[^\s",]+)', sub_section):
171
- subtitles.append(Subtitle(name=sub[1], url=self.fix_url(sub[2])))
145
+ sub_section = HTMLHelper(iframe_text).regex_first(r'playerjsSubtitle\s*=\s*"(.+?)"')
146
+ if sub_section:
147
+ for lang, link in HTMLHelper(sub_section).regex_all(r'\[(.*?)](https?://[^\s\",]+)'):
148
+ subtitles.append(Subtitle(name=lang, url=self.fix_url(link)))
172
149
 
173
150
  # player.filmizle.in kontrolü
174
151
  if "player.filmizle.in" in iframe.lower():
175
- base_match = re.search(r"https?://([^/]+)", iframe)
176
- if base_match:
177
- base_url = base_match[1]
152
+ base_url = HTMLHelper(iframe).regex_first(r"https?://([^/]+)")
153
+ if base_url:
178
154
  vid_id = iframe.split("/")[-1]
179
155
 
180
156
  self.httpx.headers.update({"X-Requested-With": "XMLHttpRequest"})