KekikStream 2.3.3__py3-none-any.whl → 2.3.5__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 (61) hide show
  1. KekikStream/Core/HTMLHelper.py +134 -0
  2. KekikStream/Core/Plugin/PluginBase.py +12 -2
  3. KekikStream/Core/__init__.py +2 -0
  4. KekikStream/Extractors/CloseLoad.py +12 -13
  5. KekikStream/Extractors/ContentX.py +33 -31
  6. KekikStream/Extractors/DonilasPlay.py +10 -10
  7. KekikStream/Extractors/DzenRu.py +3 -3
  8. KekikStream/Extractors/ExPlay.py +10 -10
  9. KekikStream/Extractors/Filemoon.py +11 -16
  10. KekikStream/Extractors/JetTv.py +4 -4
  11. KekikStream/Extractors/MixPlayHD.py +10 -11
  12. KekikStream/Extractors/MolyStream.py +15 -9
  13. KekikStream/Extractors/Odnoklassniki.py +4 -4
  14. KekikStream/Extractors/PeaceMakerst.py +3 -3
  15. KekikStream/Extractors/PixelDrain.py +6 -5
  16. KekikStream/Extractors/PlayerFilmIzle.py +6 -10
  17. KekikStream/Extractors/RapidVid.py +8 -7
  18. KekikStream/Extractors/SetPlay.py +10 -10
  19. KekikStream/Extractors/SetPrime.py +3 -6
  20. KekikStream/Extractors/SibNet.py +4 -5
  21. KekikStream/Extractors/Sobreatsesuyp.py +5 -5
  22. KekikStream/Extractors/TRsTX.py +5 -5
  23. KekikStream/Extractors/TurboImgz.py +3 -4
  24. KekikStream/Extractors/TurkeyPlayer.py +5 -5
  25. KekikStream/Extractors/VidHide.py +4 -7
  26. KekikStream/Extractors/VidMoly.py +24 -25
  27. KekikStream/Extractors/VidMoxy.py +8 -9
  28. KekikStream/Extractors/VidPapi.py +5 -7
  29. KekikStream/Extractors/VideoSeyred.py +3 -3
  30. KekikStream/Plugins/BelgeselX.py +40 -51
  31. KekikStream/Plugins/DiziBox.py +53 -81
  32. KekikStream/Plugins/DiziPal.py +41 -74
  33. KekikStream/Plugins/DiziWatch.py +217 -0
  34. KekikStream/Plugins/DiziYou.py +95 -88
  35. KekikStream/Plugins/Dizilla.py +54 -72
  36. KekikStream/Plugins/FilmBip.py +24 -49
  37. KekikStream/Plugins/FilmMakinesi.py +35 -52
  38. KekikStream/Plugins/FilmModu.py +27 -41
  39. KekikStream/Plugins/FullHDFilm.py +50 -72
  40. KekikStream/Plugins/FullHDFilmizlesene.py +35 -51
  41. KekikStream/Plugins/HDFilmCehennemi.py +48 -62
  42. KekikStream/Plugins/JetFilmizle.py +32 -50
  43. KekikStream/Plugins/KultFilmler.py +42 -67
  44. KekikStream/Plugins/RecTV.py +7 -4
  45. KekikStream/Plugins/RoketDizi.py +30 -50
  46. KekikStream/Plugins/SelcukFlix.py +15 -29
  47. KekikStream/Plugins/SetFilmIzle.py +41 -70
  48. KekikStream/Plugins/SezonlukDizi.py +47 -65
  49. KekikStream/Plugins/Sinefy.py +39 -50
  50. KekikStream/Plugins/SinemaCX.py +31 -55
  51. KekikStream/Plugins/Sinezy.py +27 -54
  52. KekikStream/Plugins/SuperFilmGeldi.py +25 -44
  53. KekikStream/Plugins/UgurFilm.py +23 -48
  54. KekikStream/Plugins/YabanciDizi.py +274 -0
  55. {kekikstream-2.3.3.dist-info → kekikstream-2.3.5.dist-info}/METADATA +1 -1
  56. kekikstream-2.3.5.dist-info/RECORD +85 -0
  57. kekikstream-2.3.3.dist-info/RECORD +0 -82
  58. {kekikstream-2.3.3.dist-info → kekikstream-2.3.5.dist-info}/WHEEL +0 -0
  59. {kekikstream-2.3.3.dist-info → kekikstream-2.3.5.dist-info}/entry_points.txt +0 -0
  60. {kekikstream-2.3.3.dist-info → kekikstream-2.3.5.dist-info}/licenses/LICENSE +0 -0
  61. {kekikstream-2.3.3.dist-info → kekikstream-2.3.5.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,10 @@
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
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult, HTMLHelper
5
4
  from json import loads
6
5
  from urllib.parse import urlparse, urlunparse
7
6
  from Crypto.Cipher import AES
8
7
  from base64 import b64decode
9
- import re
10
8
 
11
9
  class Dizilla(PluginBase):
12
10
  name = "Dizilla"
@@ -52,45 +50,26 @@ class Dizilla(PluginBase):
52
50
  ])
53
51
  else:
54
52
  istek = await self.httpx.get(url.replace("SAYFA", str(page)))
55
- secici = HTMLParser(istek.text)
53
+ secici = HTMLHelper(istek.text)
56
54
 
57
- for veri in secici.css("div.tab-content > div.grid a"):
58
- h2_el = veri.css_first("h2")
59
- name = h2_el.text(strip=True) if h2_el else None
60
-
61
- # opacity-80 div'den episode bilgisi - normalize-space yerine doğrudan text
62
- opacity_el = veri.css_first("div[class*='opacity-80']")
63
- ep_name = opacity_el.text(strip=True) if opacity_el else None
64
- if not ep_name:
55
+ # Genel olarak dizi sayfalarına giden linkleri al
56
+ for veri in secici.select('a[href*="/dizi/"]'):
57
+ href = secici.select_attr('a', 'href', veri)
58
+ title = secici.select_text(None, veri)
59
+ if not href or not title:
65
60
  continue
66
61
 
67
- ep_name = ep_name.replace(". Sezon", "x").replace(". Bölüm", "").replace("x ", "x")
68
- title = f"{name} - {ep_name}"
69
-
70
- href = veri.attrs.get("href")
71
- ep_req = await self.httpx.get(self.fix_url(href))
72
- ep_secici = HTMLParser(ep_req.text)
73
-
74
- # nav li'leri alıp 3. elemana erişme (nth-of-type yerine)
75
- nav_lis = ep_secici.css("nav li")
76
- if len(nav_lis) >= 3:
77
- link_el = nav_lis[2].css_first("a")
78
- href = link_el.attrs.get("href") if link_el else None
79
- else:
80
- href = None
81
-
82
- poster_el = ep_secici.css_first("img.imgt")
83
- poster = poster_el.attrs.get("src") if poster_el else None
84
-
85
- if href:
86
- ana_sayfa.append(
87
- MainPageResult(
88
- category = category,
89
- title = title,
90
- url = self.fix_url(href),
91
- poster = self.fix_url(poster) if poster else None
92
- )
93
- )
62
+ # Detay sayfasından poster vb. bilgileri al
63
+ ep_req = await self.httpx.get(self.fix_url(href))
64
+ ep_secici = HTMLHelper(ep_req.text)
65
+ poster = ep_secici.select_attr('img.imgt', 'src') or ep_secici.select_attr('img', 'src')
66
+
67
+ ana_sayfa.append(MainPageResult(
68
+ category = category,
69
+ title = title,
70
+ url = self.fix_url(href),
71
+ poster = self.fix_url(poster) if poster else None
72
+ ))
94
73
 
95
74
  return ana_sayfa
96
75
 
@@ -124,9 +103,10 @@ class Dizilla(PluginBase):
124
103
  # -> https://images.macellan.online/...
125
104
  if "cdn.ampproject.org" in url:
126
105
  # /i/s/ veya /ii/s/ gibi AMP prefix'lerinden sonraki kısmı al
127
- match = re.search(r"cdn\.ampproject\.org/[^/]+/s/(.+)$", url)
106
+ helper = HTMLHelper(url)
107
+ match = helper.regex_first(r"cdn\.ampproject\.org/[^/]+/s/(.+)$")
128
108
  if match:
129
- return f"https://{match.group(1)}"
109
+ return f"https://{match}"
130
110
  return url
131
111
 
132
112
  async def search(self, query: str) -> list[SearchResult]:
@@ -155,64 +135,64 @@ class Dizilla(PluginBase):
155
135
 
156
136
  async def load_item(self, url: str) -> SeriesInfo:
157
137
  istek = await self.httpx.get(url)
158
- secici = HTMLParser(istek.text)
138
+ secici = HTMLHelper(istek.text)
159
139
 
160
- title = secici.css_first("div.poster.poster h2")
161
- title = title.text(strip=True) if title else None
140
+ title = secici.select_text("div.poster.poster h2")
162
141
  if not title:
163
142
  return None
164
143
 
165
- poster_el = secici.css_first("div.w-full.page-top.relative img")
166
- poster = self.fix_url(poster_el.attrs.get("src")) if poster_el else None
144
+ poster = secici.select_attr("div.w-full.page-top.relative img", "src")
145
+ poster = self.fix_url(poster) if poster else None
167
146
 
168
147
  # Year extraction (Kotlin: [1] index for w-fit min-w-fit)
169
- info_boxes = secici.css("div.w-fit.min-w-fit")
148
+ info_boxes = secici.select("div.w-fit.min-w-fit")
170
149
  year = None
171
150
  if len(info_boxes) > 1:
172
- year_el = info_boxes[1].css_first("span.text-sm.opacity-60")
173
- if year_el:
174
- year_text = year_el.text(strip=True)
151
+ year_text = secici.select_text("span.text-sm.opacity-60", info_boxes[1])
152
+ if year_text:
175
153
  year = year_text.split(" ")[-1] if " " in year_text else year_text
176
154
 
177
- description_el = secici.css_first("div.mt-2.text-sm")
178
- description = description_el.text(strip=True) if description_el else None
155
+ description = secici.select_text("div.mt-2.text-sm")
179
156
 
180
- tags_el = secici.css_first("div.poster.poster h3")
181
- tags = [t.strip() for t in tags_el.text(strip=True).split(",")] if tags_el else []
157
+ tags_text = secici.select_text("div.poster.poster h3")
158
+ tags = [t.strip() for t in tags_text.split(",")] if tags_text else []
182
159
 
183
- actors = [h5.text(strip=True) for h5 in secici.css("div.global-box h5")]
160
+ actors = secici.select_all_text("div.global-box h5")
184
161
 
185
162
  episodeses = []
186
163
  # Seasons links iteration
187
- season_links = secici.css("div.flex.items-center.flex-wrap.gap-2.mb-4 a")
164
+ season_links = secici.select("div.flex.items-center.flex-wrap.gap-2.mb-4 a")
188
165
  for sezon in season_links:
189
- sezon_href = self.fix_url(sezon.attrs.get("href"))
166
+ sezon_href = secici.select_attr("a", "href", sezon)
167
+ sezon_href = self.fix_url(sezon_href)
190
168
  sezon_req = await self.httpx.get(sezon_href)
191
169
 
192
170
  season_num = None
193
171
  try:
194
172
  # URL'den sezon numarasını çek: ...-N-sezon formatı
195
- season_match = re.search(r"-(\d+)-sezon", sezon_href)
173
+ season_match = secici.regex_first(r"-(\d+)-sezon", sezon_href)
196
174
  if season_match:
197
- season_num = int(season_match.group(1))
175
+ season_num = int(season_match)
198
176
  except:
199
177
  pass
200
178
 
201
- sezon_secici = HTMLParser(sezon_req.text)
202
- for bolum in sezon_secici.css("div.episodes div.cursor-pointer"):
179
+ sezon_secici = HTMLHelper(sezon_req.text)
180
+ for bolum in sezon_secici.select("div.episodes div.cursor-pointer"):
203
181
  # Kotlin: bolum.select("a").last()
204
- links = bolum.css("a")
182
+ links = sezon_secici.select("a", bolum)
205
183
  if not links:
206
184
  continue
207
185
 
208
186
  ep_link = links[-1]
209
- ep_name = ep_link.text(strip=True)
210
- ep_href = self.fix_url(ep_link.attrs.get("href"))
187
+ ep_name = sezon_secici.select_text("a", ep_link)
188
+ ep_href = sezon_secici.select_attr("a", "href", ep_link)
189
+ ep_href = self.fix_url(ep_href)
211
190
 
212
191
  # Episode number (first link's text usually)
213
192
  ep_num = None
214
193
  try:
215
- ep_num = int(links[0].text(strip=True))
194
+ ep_num_text = sezon_secici.select_text("a", links[0])
195
+ ep_num = int(ep_num_text) if ep_num_text else None
216
196
  except:
217
197
  pass
218
198
 
@@ -236,13 +216,13 @@ class Dizilla(PluginBase):
236
216
 
237
217
  async def load_links(self, url: str) -> list[ExtractResult]:
238
218
  istek = await self.httpx.get(url)
239
- secici = HTMLParser(istek.text)
219
+ secici = HTMLHelper(istek.text)
240
220
 
241
- next_data_el = secici.css_first("script#__NEXT_DATA__")
242
- if not next_data_el:
221
+ next_data_text = secici.select_text("script#__NEXT_DATA__")
222
+ if not next_data_text:
243
223
  return []
244
224
 
245
- next_data = loads(next_data_el.text(strip=True))
225
+ next_data = loads(next_data_text)
246
226
  secure_data = next_data.get("props", {}).get("pageProps", {}).get("secureData", {})
247
227
  decrypted = await self.decrypt_response(secure_data)
248
228
  results = decrypted.get("RelatedResults", {}).get("getEpisodeSources", {}).get("result", [])
@@ -258,8 +238,8 @@ class Dizilla(PluginBase):
258
238
  cleaned_source = source_content.replace('"', '').replace('\\', '')
259
239
 
260
240
  # Parse cleaned HTML
261
- iframe_el = HTMLParser(cleaned_source).css_first("iframe")
262
- iframe_src = iframe_el.attrs.get("src") if iframe_el else None
241
+ iframe_secici = HTMLHelper(cleaned_source)
242
+ iframe_src = iframe_secici.select_attr("iframe", "src")
263
243
 
264
244
  # Referer check (matching Kotlin: loadExtractor(iframe, "${mainUrl}/", ...))
265
245
  iframe_url = self.fix_url(iframe_src) if iframe_src else None
@@ -268,4 +248,6 @@ class Dizilla(PluginBase):
268
248
  return []
269
249
 
270
250
  data = await self.extract(iframe_url, referer=f"{self.main_url}/", prefix=first_result.get('language_name', 'Unknown'))
271
- return [data] if data else []
251
+ if not data:
252
+ return []
253
+ return data if isinstance(data, list) else [data]
@@ -1,8 +1,6 @@
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 selectolax.parser import HTMLParser
5
- import re
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
6
4
 
7
5
  class FilmBip(PluginBase):
8
6
  name = "FilmBip"
@@ -37,16 +35,13 @@ class FilmBip(PluginBase):
37
35
  page_url = page_url.rstrip("/")
38
36
 
39
37
  istek = await self.httpx.get(page_url)
40
- secici = HTMLParser(istek.text)
38
+ secici = HTMLHelper(istek.text)
41
39
 
42
40
  results = []
43
- for veri in secici.css("div.poster-long"):
44
- img = veri.css_first("a.block img.lazy")
45
- link_el = veri.css_first("a.block")
46
-
47
- title = img.attrs.get("alt") if img else None
48
- href = link_el.attrs.get("href") if link_el else None
49
- poster = (img.attrs.get("data-src") or img.attrs.get("src")) if img else None
41
+ for veri in secici.select("div.poster-long"):
42
+ title = secici.select_attr("a.block img.lazy", "alt", veri)
43
+ href = secici.select_attr("a.block", "href", veri)
44
+ poster = secici.select_poster("a.block img.lazy", veri)
50
45
 
51
46
  if title and href:
52
47
  results.append(MainPageResult(
@@ -80,17 +75,13 @@ class FilmBip(PluginBase):
80
75
  except Exception:
81
76
  return []
82
77
 
83
- secici = HTMLParser(html_content)
78
+ secici = HTMLHelper(html_content)
84
79
 
85
80
  results = []
86
- for veri in secici.css("li"):
87
- link_el = veri.css_first("a.block.truncate")
88
- href_el = veri.css_first("a")
89
- img_el = veri.css_first("img.lazy")
90
-
91
- title = link_el.text(strip=True) if link_el else None
92
- href = href_el.attrs.get("href") if href_el else None
93
- poster = img_el.attrs.get("data-src") if img_el else None
81
+ for veri in secici.select("li"):
82
+ title = secici.select_text("a.block.truncate", veri)
83
+ href = secici.select_attr("a", "href", veri)
84
+ poster = secici.select_attr("img.lazy", "data-src", veri)
94
85
 
95
86
  if title and href:
96
87
  results.append(SearchResult(
@@ -103,42 +94,27 @@ class FilmBip(PluginBase):
103
94
 
104
95
  async def load_item(self, url: str) -> MovieInfo:
105
96
  istek = await self.httpx.get(url)
106
- secici = HTMLParser(istek.text)
97
+ secici = HTMLHelper(istek.text)
107
98
  html_text = istek.text
108
99
 
109
- title_el = secici.css_first("div.page-title h1")
110
- title = title_el.text(strip=True) if title_el else ""
100
+ title = secici.select_text("div.page-title h1") or ""
111
101
 
112
- og_image = secici.css_first("meta[property='og:image']")
113
- poster = og_image.attrs.get("content") if og_image else None
102
+ poster = secici.select_attr("meta[property='og:image']", "content")
114
103
 
115
- trailer_el = secici.css_first("div.series-profile-trailer")
116
- trailer = trailer_el.attrs.get("data-yt") if trailer_el else None
104
+ trailer = secici.select_attr("div.series-profile-trailer", "data-yt")
117
105
 
118
- desc_el = secici.css_first("div.series-profile-infos-in.article p")
119
- if not desc_el:
120
- desc_el = secici.css_first("div.series-profile-summary p")
121
- description = desc_el.text(strip=True) if desc_el else None
106
+ description = secici.select_text("div.series-profile-infos-in.article p") or secici.select_text("div.series-profile-summary p")
122
107
 
123
- tags = [a.text(strip=True) for a in secici.css("div.series-profile-type.tv-show-profile-type a") if a.text(strip=True)]
108
+ tags = secici.select_all_text("div.series-profile-type.tv-show-profile-type a")
124
109
 
125
110
  # XPath yerine regex kullanarak yıl, süre vs. çıkarma
126
- year = None
127
- year_match = re.search(r'Yapım yılı.*?<p[^>]*>(\d{4})</p>', html_text, re.IGNORECASE | re.DOTALL)
128
- if year_match:
129
- year = year_match.group(1)
111
+ year = secici.regex_first(r'(?i)Yapım yılı.*?<p[^>]*>(\d{4})</p>', secici.html)
130
112
 
131
- duration = None
132
- duration_match = re.search(r'Süre.*?<p[^>]*>(\d+)', html_text, re.IGNORECASE | re.DOTALL)
133
- if duration_match:
134
- duration = duration_match.group(1)
113
+ duration = secici.regex_first(r'(?i)Süre.*?<p[^>]*>(\d+)', secici.html)
135
114
 
136
- rating = None
137
- rating_match = re.search(r'IMDB Puanı.*?<span[^>]*>([0-9.]+)</span>', html_text, re.IGNORECASE | re.DOTALL)
138
- if rating_match:
139
- rating = rating_match.group(1)
115
+ rating = secici.regex_first(r'(?i)IMDB Puanı.*?<span[^>]*>([0-9.]+)</span>', secici.html)
140
116
 
141
- actors = [img.attrs.get("alt") for img in secici.css("div.series-profile-cast ul li a img") if img.attrs.get("alt")]
117
+ actors = [img.attrs.get("alt") for img in secici.select("div.series-profile-cast ul li a img") if img.attrs.get("alt")]
142
118
 
143
119
  return MovieInfo(
144
120
  url = url,
@@ -154,13 +130,12 @@ class FilmBip(PluginBase):
154
130
 
155
131
  async def load_links(self, url: str) -> list[ExtractResult]:
156
132
  istek = await self.httpx.get(url)
157
- secici = HTMLParser(istek.text)
133
+ secici = HTMLHelper(istek.text)
158
134
 
159
135
  results = []
160
136
 
161
- for player in secici.css("div#tv-spoox2"):
162
- iframe_el = player.css_first("iframe")
163
- iframe = iframe_el.attrs.get("src") if iframe_el else None
137
+ for player in secici.select("div#tv-spoox2"):
138
+ iframe = secici.select_attr("iframe", "src", player)
164
139
 
165
140
  if iframe:
166
141
  iframe = self.fix_url(iframe)
@@ -1,8 +1,6 @@
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 selectolax.parser import HTMLParser
5
- import re
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, HTMLHelper
6
4
 
7
5
  class FilmMakinesi(PluginBase):
8
6
  name = "FilmMakinesi"
@@ -37,17 +35,13 @@ class FilmMakinesi(PluginBase):
37
35
 
38
36
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
39
37
  istek = self.cloudscraper.get(f"{url}{'' if page == 1 else f'page/{page}/'}")
40
- secici = HTMLParser(istek.text)
38
+ secici = HTMLHelper(istek.text)
41
39
 
42
40
  results = []
43
- for veri in secici.css("div.item-relative"):
44
- title_el = veri.css_first("div.title")
45
- link_el = veri.css_first("a")
46
- img_el = veri.css_first("img")
47
-
48
- title = title_el.text(strip=True) if title_el else None
49
- href = link_el.attrs.get("href") if link_el else None
50
- poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
41
+ for veri in secici.select("div.item-relative"):
42
+ title = secici.select_text("div.title", veri)
43
+ href = secici.select_attr("a", "href", veri)
44
+ poster = secici.select_poster("img", veri)
51
45
 
52
46
  if title and href:
53
47
  results.append(MainPageResult(
@@ -61,17 +55,13 @@ class FilmMakinesi(PluginBase):
61
55
 
62
56
  async def search(self, query: str) -> list[SearchResult]:
63
57
  istek = await self.httpx.get(f"{self.main_url}/arama/?s={query}")
64
- secici = HTMLParser(istek.text)
58
+ secici = HTMLHelper(istek.text)
65
59
 
66
60
  results = []
67
- for article in secici.css("div.item-relative"):
68
- title_el = article.css_first("div.title")
69
- link_el = article.css_first("a")
70
- img_el = article.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
61
+ for article in secici.select("div.item-relative"):
62
+ title = secici.select_text("div.title", article)
63
+ href = secici.select_attr("a", "href", article)
64
+ poster = secici.select_poster("img", article)
75
65
 
76
66
  if title and href:
77
67
  results.append(SearchResult(
@@ -84,48 +74,42 @@ class FilmMakinesi(PluginBase):
84
74
 
85
75
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
86
76
  istek = await self.httpx.get(url)
87
- secici = HTMLParser(istek.text)
77
+ secici = HTMLHelper(istek.text)
88
78
 
89
- title_el = secici.css_first("h1.title")
90
- title = title_el.text(strip=True) if title_el else ""
79
+ title = secici.select_text("h1.title") or ""
91
80
 
92
- poster_el = secici.css_first("img.cover-img")
93
- poster = poster_el.attrs.get("src", "").strip() if poster_el else ""
81
+ poster = secici.select_attr("img.cover-img", "src") or ""
82
+ poster = poster.strip()
94
83
 
95
- desc_el = secici.css_first("div.info-description p")
96
- description = desc_el.text(strip=True) if desc_el else ""
84
+ description = secici.select_text("div.info-description p") or ""
97
85
 
98
- rating_el = secici.css_first("div.score")
99
- rating = None
100
- if rating_el:
101
- rating_text = rating_el.text(strip=True)
102
- if rating_text:
103
- rating = rating_text.split()[0]
86
+ rating_text = secici.select_text("div.score") or ""
87
+ rating = None
88
+ if rating_text:
89
+ rating = rating_text.split()[0]
104
90
 
105
- year_el = secici.css_first("span.date a")
106
- year = year_el.text(strip=True) if year_el else ""
91
+ year = secici.select_text("span.date a") or ""
107
92
 
108
- actors = [el.text(strip=True) for el in secici.css("div.cast-name") if el.text(strip=True)]
109
- tags = [el.text(strip=True) for el in secici.css("div.genre a") if el.text(strip=True)]
93
+ actors = secici.select_all_text("div.cast-name")
94
+ tags = secici.select_all_text("div.genre a")
110
95
 
111
- duration_el = secici.css_first("div.time")
112
- duration = None
113
- if duration_el:
114
- duration_text = duration_el.text(strip=True)
96
+ duration = None
97
+ duration_text = secici.select_text("div.time") or None
98
+ if duration_text:
115
99
  parts = duration_text.split()
116
100
  if len(parts) > 1:
117
101
  duration = parts[1].strip()
118
102
 
119
103
  # Dizi mi kontrol et - sezon/bölüm linkleri var mı?
120
104
  episodes = []
121
- all_links = secici.css("a[href]")
105
+ all_links = secici.select("a[href]")
122
106
  for link in all_links:
123
107
  href = link.attrs.get("href", "")
124
- match = re.search(r"/sezon-(\d+)/bolum-(\d+)", href)
125
- if match:
126
- season_no = int(match.group(1))
127
- ep_no = int(match.group(2))
128
-
108
+ pairs = HTMLHelper(href).regex_all(r"/sezon-(\d+)/bolum-(\d+)")
109
+ if pairs:
110
+ season_no = int(pairs[0][0])
111
+ ep_no = int(pairs[0][1])
112
+
129
113
  # Bölüm başlığını çıkar - text'ten gerçek ismi al
130
114
  # Format: "22 Eylül 2014 / 44 dk /1. Sezon / 1. BölümPilot"
131
115
  full_text = link.text(strip=True)
@@ -184,12 +168,12 @@ class FilmMakinesi(PluginBase):
184
168
 
185
169
  async def load_links(self, url: str) -> list[ExtractResult]:
186
170
  istek = await self.httpx.get(url)
187
- secici = HTMLParser(istek.text)
171
+ secici = HTMLHelper(istek.text)
188
172
 
189
173
  response = []
190
174
 
191
175
  # Video parts linklerini ve etiketlerini al
192
- for link in secici.css("div.video-parts a[data-video_url]"):
176
+ for link in secici.select("div.video-parts a[data-video_url]"):
193
177
  video_url = link.attrs.get("data-video_url")
194
178
  label = link.text(strip=True) if link.text(strip=True) else ""
195
179
 
@@ -200,8 +184,7 @@ class FilmMakinesi(PluginBase):
200
184
 
201
185
  # Eğer video-parts yoksa iframe kullan
202
186
  if not response:
203
- iframe_el = secici.css_first("iframe")
204
- iframe_src = iframe_el.attrs.get("data-src") if iframe_el else None
187
+ iframe_src = secici.select_attr("iframe", "data-src")
205
188
  if iframe_src:
206
189
  data = await self.extract(iframe_src)
207
190
  if data:
@@ -1,8 +1,6 @@
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, Subtitle, ExtractResult
4
- from selectolax.parser import HTMLParser
5
- import re
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Subtitle, ExtractResult, HTMLHelper
6
4
 
7
5
  class FilmModu(PluginBase):
8
6
  name = "FilmModu"
@@ -42,16 +40,13 @@ class FilmModu(PluginBase):
42
40
 
43
41
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
44
42
  istek = await self.httpx.get(url.replace("SAYFA", str(page)))
45
- secici = HTMLParser(istek.text)
43
+ secici = HTMLHelper(istek.text)
46
44
 
47
45
  results = []
48
- for veri in secici.css("div.movie"):
49
- link_el = veri.css_first("a")
50
- img_el = veri.css_first("picture img")
51
-
52
- title = link_el.text(strip=True) if link_el else None
53
- href = link_el.attrs.get("href") if link_el else None
54
- poster = img_el.attrs.get("data-src") if img_el else None
46
+ for veri in secici.select("div.movie"):
47
+ title = secici.select_text("a", veri)
48
+ href = secici.select_attr("a", "href", veri)
49
+ poster = secici.select_attr("picture img", "data-src", veri)
55
50
 
56
51
  if title and href:
57
52
  results.append(MainPageResult(
@@ -65,16 +60,13 @@ class FilmModu(PluginBase):
65
60
 
66
61
  async def search(self, query: str) -> list[SearchResult]:
67
62
  istek = await self.httpx.get(f"{self.main_url}/film-ara?term={query}")
68
- secici = HTMLParser(istek.text)
63
+ secici = HTMLHelper(istek.text)
69
64
 
70
65
  results = []
71
- for veri in secici.css("div.movie"):
72
- link_el = veri.css_first("a")
73
- img_el = veri.css_first("picture img")
74
-
75
- title = link_el.text(strip=True) if link_el else None
76
- href = link_el.attrs.get("href") if link_el else None
77
- poster = img_el.attrs.get("data-src") if img_el else None
66
+ for veri in secici.select("div.movie"):
67
+ title = secici.select_text("a", veri)
68
+ href = secici.select_attr("a", "href", veri)
69
+ poster = secici.select_attr("picture img", "data-src", veri)
78
70
 
79
71
  if title and href:
80
72
  results.append(SearchResult(
@@ -87,31 +79,25 @@ class FilmModu(PluginBase):
87
79
 
88
80
  async def load_item(self, url: str) -> MovieInfo:
89
81
  istek = await self.httpx.get(url)
90
- secici = HTMLParser(istek.text)
91
-
92
- org_title_el = secici.css_first("div.titles h1")
93
- alt_title_el = secici.css_first("div.titles h2")
82
+ secici = HTMLHelper(istek.text)
94
83
 
95
- org_title = org_title_el.text(strip=True) if org_title_el else ""
96
- alt_title = alt_title_el.text(strip=True) if alt_title_el else ""
84
+ org_title = secici.select_text("div.titles h1") or ""
85
+ alt_title = secici.select_text("div.titles h2") or ""
97
86
  title = f"{org_title} - {alt_title}" if alt_title else org_title
98
87
 
99
- poster_el = secici.css_first("img.img-responsive")
100
- poster = poster_el.attrs.get("src") if poster_el else None
88
+ poster = secici.select_attr("img.img-responsive", "src") if secici.select_attr("img.img-responsive", "src") else None
101
89
 
102
- desc_el = secici.css_first("p[itemprop='description']")
103
- description = desc_el.text(strip=True) if desc_el else None
90
+ description = secici.select_text("p[itemprop='description']") or None
104
91
 
105
- tags = [a.text(strip=True) for a in secici.css("a[href*='film-tur/']") if a.text(strip=True)]
92
+ tags = secici.select_all_text("a[href*='film-tur/']")
106
93
 
107
- year_el = secici.css_first("span[itemprop='dateCreated']")
108
- year = year_el.text(strip=True) if year_el else None
94
+ year = secici.select_text("span[itemprop='dateCreated']") or None
109
95
 
110
96
  actors = []
111
- for a in secici.css("a[itemprop='actor']"):
112
- span_el = a.css_first("span")
113
- if span_el and span_el.text(strip=True):
114
- actors.append(span_el.text(strip=True))
97
+ for a in secici.select("a[itemprop='actor']"):
98
+ name = secici.select_text("span", a)
99
+ if name:
100
+ actors.append(name)
115
101
 
116
102
  return MovieInfo(
117
103
  url = url,
@@ -125,9 +111,9 @@ class FilmModu(PluginBase):
125
111
 
126
112
  async def load_links(self, url: str) -> list[ExtractResult]:
127
113
  istek = await self.httpx.get(url)
128
- secici = HTMLParser(istek.text)
114
+ secici = HTMLHelper(istek.text)
129
115
 
130
- alternates = secici.css("div.alternates a")
116
+ alternates = secici.select("div.alternates a")
131
117
  if not alternates:
132
118
  return []
133
119
 
@@ -144,14 +130,14 @@ class FilmModu(PluginBase):
144
130
  alt_istek = await self.httpx.get(alt_link)
145
131
  alt_text = alt_istek.text
146
132
 
147
- vid_id = re.search(r"var videoId = '(.*)'", alt_text)
148
- vid_type = re.search(r"var videoType = '(.*)'", alt_text)
133
+ vid_id = HTMLHelper(alt_text).regex_first(r"var videoId = '([^']*)'")
134
+ vid_type = HTMLHelper(alt_text).regex_first(r"var videoType = '([^']*)'")
149
135
 
150
136
  if not vid_id or not vid_type:
151
137
  continue
152
138
 
153
139
  source_istek = await self.httpx.get(
154
- f"{self.main_url}/get-source?movie_id={vid_id[1]}&type={vid_type[1]}"
140
+ f"{self.main_url}/get-source?movie_id={vid_id}&type={vid_type}"
155
141
  )
156
142
  source_data = source_istek.json()
157
143