KekikStream 2.3.3__py3-none-any.whl → 2.3.4__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 (59) hide show
  1. KekikStream/Core/HTMLHelper.py +134 -0
  2. KekikStream/Core/Plugin/PluginBase.py +4 -1
  3. KekikStream/Core/__init__.py +2 -0
  4. KekikStream/Extractors/CloseLoad.py +12 -13
  5. KekikStream/Extractors/ContentX.py +12 -15
  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 +5 -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/DiziYou.py +95 -88
  34. KekikStream/Plugins/Dizilla.py +51 -71
  35. KekikStream/Plugins/FilmBip.py +24 -49
  36. KekikStream/Plugins/FilmMakinesi.py +35 -52
  37. KekikStream/Plugins/FilmModu.py +27 -41
  38. KekikStream/Plugins/FullHDFilm.py +50 -72
  39. KekikStream/Plugins/FullHDFilmizlesene.py +35 -51
  40. KekikStream/Plugins/HDFilmCehennemi.py +48 -62
  41. KekikStream/Plugins/JetFilmizle.py +32 -50
  42. KekikStream/Plugins/KultFilmler.py +42 -67
  43. KekikStream/Plugins/RecTV.py +7 -4
  44. KekikStream/Plugins/RoketDizi.py +30 -50
  45. KekikStream/Plugins/SelcukFlix.py +15 -29
  46. KekikStream/Plugins/SetFilmIzle.py +41 -70
  47. KekikStream/Plugins/SezonlukDizi.py +47 -65
  48. KekikStream/Plugins/Sinefy.py +34 -50
  49. KekikStream/Plugins/SinemaCX.py +31 -55
  50. KekikStream/Plugins/Sinezy.py +27 -54
  51. KekikStream/Plugins/SuperFilmGeldi.py +25 -44
  52. KekikStream/Plugins/UgurFilm.py +23 -48
  53. {kekikstream-2.3.3.dist-info → kekikstream-2.3.4.dist-info}/METADATA +1 -1
  54. kekikstream-2.3.4.dist-info/RECORD +83 -0
  55. kekikstream-2.3.3.dist-info/RECORD +0 -82
  56. {kekikstream-2.3.3.dist-info → kekikstream-2.3.4.dist-info}/WHEEL +0 -0
  57. {kekikstream-2.3.3.dist-info → kekikstream-2.3.4.dist-info}/entry_points.txt +0 -0
  58. {kekikstream-2.3.3.dist-info → kekikstream-2.3.4.dist-info}/licenses/LICENSE +0 -0
  59. {kekikstream-2.3.3.dist-info → kekikstream-2.3.4.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, MovieInfo
4
- from selectolax.parser import HTMLParser
5
- import re, base64, json
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult, MovieInfo, HTMLHelper
4
+ import base64, json
6
5
 
7
6
  class RoketDizi(PluginBase):
8
7
  name = "RoketDizi"
@@ -24,19 +23,15 @@ class RoketDizi(PluginBase):
24
23
 
25
24
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
26
25
  istek = await self.httpx.get(f"{url}?&page={page}")
27
- secici = HTMLParser(istek.text)
26
+ secici = HTMLHelper(istek.text)
28
27
 
29
28
  results = []
30
29
 
31
30
  # 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
31
+ for item in secici.select("div.new-added-list > span"):
32
+ title = secici.select_text("span.line-clamp-1", item)
33
+ href = secici.select_attr("a", "href", item)
34
+ poster = secici.select_attr("img", "src", item)
40
35
 
41
36
  if title and href:
42
37
  results.append(MainPageResult(
@@ -92,58 +87,49 @@ class RoketDizi(PluginBase):
92
87
  async def load_item(self, url: str) -> SeriesInfo:
93
88
  # Note: Handling both Movie and Series logic in one, returning SeriesInfo generally or MovieInfo
94
89
  resp = await self.httpx.get(url)
95
- sel = HTMLParser(resp.text)
90
+ sel = HTMLHelper(resp.text)
96
91
  html_text = resp.text
97
92
 
98
- title_el = sel.css_first("h1.text-white")
99
- title = title_el.text(strip=True) if title_el else None
93
+ title = sel.select_text("h1.text-white")
100
94
 
101
- poster_el = sel.css_first("div.w-full.page-top img")
102
- poster = poster_el.attrs.get("src") if poster_el else None
95
+ poster = sel.select_attr("div.w-full.page-top img", "src")
103
96
 
104
- desc_el = sel.css_first("div.mt-2.text-sm")
105
- description = desc_el.text(strip=True) if desc_el else None
97
+ description = sel.select_text("div.mt-2.text-sm")
106
98
 
107
99
  # Tags - genre bilgileri (Detaylar bölümünde)
108
100
  tags = []
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
+ genre_text = sel.select_text("h3.text-white.opacity-90")
102
+ if genre_text:
103
+ tags = [t.strip() for t in genre_text.split(",")]
114
104
 
115
105
  # Rating
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
106
+ rating = sel.select_text("span.text-white.text-sm.font-bold")
118
107
 
119
108
  # Year ve Actors - Detaylar (Details) bölümünden
120
109
  year = None
121
110
  actors = []
122
111
 
123
112
  # Detaylar bölümündeki tüm flex-col div'leri al
124
- detail_items = sel.css("div.flex.flex-col")
113
+ detail_items = sel.select("div.flex.flex-col")
125
114
  for item in detail_items:
126
- label_el = item.css_first("span.text-base")
127
- value_el = item.css_first("span.text-sm.opacity-90")
115
+ label = sel.select_text("span.text-base", item)
116
+ value = sel.select_text("span.text-sm.opacity-90", item)
128
117
 
129
- label = label_el.text(strip=True) if label_el else None
130
- value = value_el.text(strip=True) if value_el else None
118
+ label = label if label else None
119
+ value = value if value else None
131
120
 
132
121
  if label and value:
133
122
  # Yayın tarihi (yıl)
134
123
  if label == "Yayın tarihi":
135
124
  # "16 Ekim 2018" formatından yılı çıkar
136
- year_match = re.search(r'\d{4}', value)
137
- if year_match:
138
- year = year_match.group()
139
-
125
+ year = HTMLHelper(value).regex_first(r'\d{4}')
140
126
  # Yaratıcılar veya Oyuncular
141
127
  elif label in ["Yaratıcılar", "Oyuncular"]:
142
128
  if value:
143
129
  actors.append(value)
144
130
 
145
131
  # Check urls for episodes
146
- all_urls = re.findall(r'"url":"([^"]*)"', html_text)
132
+ all_urls = HTMLHelper(html_text).regex_all(r'"url":"([^"]*)"')
147
133
  is_series = any("bolum-" in u for u in all_urls)
148
134
 
149
135
  episodes = []
@@ -152,11 +138,11 @@ class RoketDizi(PluginBase):
152
138
  episodes_dict = {}
153
139
  for u in all_urls:
154
140
  if "bolum" in u and u not in episodes_dict:
155
- season_match = re.search(r'/sezon-(\d+)', u)
156
- ep_match = re.search(r'/bolum-(\d+)', u)
141
+ season = HTMLHelper(u).regex_first(r'/sezon-(\d+)')
142
+ ep_num = HTMLHelper(u).regex_first(r'/bolum-(\d+)')
157
143
 
158
- season = int(season_match.group(1)) if season_match else 1
159
- episode_num = int(ep_match.group(1)) if ep_match else 1
144
+ season = int(season) if season else 1
145
+ episode_num = int(ep_num) if ep_num else 1
160
146
 
161
147
  # Key olarak (season, episode) tuple kullan
162
148
  key = (season, episode_num)
@@ -184,13 +170,9 @@ class RoketDizi(PluginBase):
184
170
 
185
171
  async def load_links(self, url: str) -> list[ExtractResult]:
186
172
  resp = await self.httpx.get(url)
187
- sel = HTMLParser(resp.text)
173
+ sel = HTMLHelper(resp.text)
188
174
 
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)
175
+ next_data = sel.select_text("script#__NEXT_DATA__")
194
176
  if not next_data:
195
177
  return []
196
178
 
@@ -208,11 +190,9 @@ class RoketDizi(PluginBase):
208
190
  source_content = source.get("source_content", "")
209
191
 
210
192
  # iframe URL'ini source_content'ten çıkar
211
- iframe_match = re.search(r'<iframe[^>]*src=["\']([^"\']*)["\']', source_content)
212
- if not iframe_match:
193
+ iframe_url = HTMLHelper(source_content).regex_first(r'<iframe[^>]*src=["\']([^"\']*)["\']')
194
+ if not iframe_url:
213
195
  continue
214
-
215
- iframe_url = iframe_match.group(1)
216
196
 
217
197
  # Fix URL protocol
218
198
  if not iframe_url.startswith("http"):
@@ -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, MovieInfo, SeriesInfo, Episode, ExtractResult
4
- from selectolax.parser import HTMLParser
5
- import re, base64, json, urllib.parse
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, HTMLHelper
4
+ import base64, json, urllib.parse
6
5
 
7
6
  class SelcukFlix(PluginBase):
8
7
  name = "SelcukFlix"
@@ -34,17 +33,13 @@ class SelcukFlix(PluginBase):
34
33
  if "tum-bolumler" in url:
35
34
  try:
36
35
  resp = await self.httpx.get(url)
37
- sel = HTMLParser(resp.text)
36
+ sel = HTMLHelper(resp.text)
38
37
 
39
- for item in sel.css("div.col-span-3 a"):
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
38
+ for item in sel.select("div.col-span-3 a"):
39
+ name = sel.select_text("h2", item)
40
+ ep_info = sel.select_text("div.opacity-80", item)
41
+ href = sel.select_attr("a", "href", item)
42
+ poster = sel.select_attr("div.image img", "src", item)
48
43
 
49
44
  if name and href:
50
45
  title = f"{name} - {ep_info}" if ep_info else name
@@ -188,14 +183,10 @@ class SelcukFlix(PluginBase):
188
183
 
189
184
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
190
185
  resp = await self.httpx.get(url)
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
186
+ sel = HTMLHelper(resp.text)
196
187
 
197
- next_data = next_data_el.text(strip=True)
198
- if not next_data:
188
+ next_data = sel.select_text("script#__NEXT_DATA__")
189
+ if not next_data:
199
190
  return None
200
191
 
201
192
  data = json.loads(next_data)
@@ -266,13 +257,9 @@ class SelcukFlix(PluginBase):
266
257
 
267
258
  async def load_links(self, url: str) -> list[ExtractResult]:
268
259
  resp = await self.httpx.get(url)
269
- sel = HTMLParser(resp.text)
260
+ sel = HTMLHelper(resp.text)
270
261
 
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)
262
+ next_data = sel.select_text("script#__NEXT_DATA__")
276
263
  if not next_data:
277
264
  return []
278
265
 
@@ -312,9 +299,8 @@ class SelcukFlix(PluginBase):
312
299
  source_content = res[0].get("source_content") or res[0].get("sourceContent")
313
300
 
314
301
  if source_content:
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
302
+ iframe_sel = HTMLHelper(source_content)
303
+ iframe_src = iframe_sel.select_attr("iframe", "src")
318
304
  if iframe_src:
319
305
  iframe_src = self.fix_url(iframe_src)
320
306
  # Hotlinger domain değişimi (Kotlin referansı)
@@ -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, MovieInfo, SeriesInfo, Episode, ExtractResult
4
- from selectolax.parser import HTMLParser
5
- import re, json, asyncio
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, HTMLHelper
4
+ import json, asyncio
6
5
 
7
6
  class SetFilmIzle(PluginBase):
8
7
  name = "SetFilmIzle"
@@ -53,17 +52,13 @@ class SetFilmIzle(PluginBase):
53
52
 
54
53
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
55
54
  istek = self.cloudscraper.get(url)
56
- secici = HTMLParser(istek.text)
55
+ secici = HTMLHelper(istek.text)
57
56
 
58
57
  results = []
59
- for item in secici.css("div.items article"):
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
58
+ for item in secici.select("div.items article"):
59
+ title = secici.select_text("h2", item)
60
+ href = secici.select_attr("a", "href", item)
61
+ poster = secici.select_attr("img", "data-src", item)
67
62
 
68
63
  if title and href:
69
64
  results.append(MainPageResult(
@@ -99,17 +94,13 @@ class SetFilmIzle(PluginBase):
99
94
  except:
100
95
  return []
101
96
 
102
- secici = HTMLParser(html)
97
+ secici = HTMLHelper(html)
103
98
  results = []
104
99
 
105
- for item in secici.css("div.items article"):
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
100
+ for item in secici.select("div.items article"):
101
+ title = secici.select_text("h2", item)
102
+ href = secici.select_attr("a", "href", item)
103
+ poster = secici.select_attr("img", "data-src", item)
113
104
 
114
105
  if title and href:
115
106
  results.append(SearchResult(
@@ -122,78 +113,59 @@ class SetFilmIzle(PluginBase):
122
113
 
123
114
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
124
115
  istek = await self.httpx.get(url)
125
- secici = HTMLParser(istek.text)
116
+ secici = HTMLHelper(istek.text)
126
117
  html_text = istek.text
127
118
 
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()
119
+ raw_title = secici.select_text("h1") or secici.select_text(".titles h1") or secici.select_attr("meta[property='og:title']", "content") or ""
120
+ title = HTMLHelper(raw_title).regex_replace(r"(?i)\s*izle.*$", "", flags=0).strip()
136
121
 
137
- poster_el = secici.css_first("div.poster img")
138
- poster = poster_el.attrs.get("src") if poster_el else None
122
+ poster = secici.select_attr("div.poster img", "src")
139
123
 
140
- desc_el = secici.css_first("div.wp-content p")
141
- description = desc_el.text(strip=True) if desc_el else None
124
+ description = secici.select_text("div.wp-content p")
142
125
 
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)
148
- year = year_match.group() if year_match else None
126
+ year_text = secici.select_text("div.extra span.C a")
127
+ year = secici.regex_first(r"\d{4}", year_text) if year_text else None
149
128
 
150
- tags = [a.text(strip=True) for a in secici.css("div.sgeneros a") if a.text(strip=True)]
129
+ tags = [a.text(strip=True) for a in secici.select("div.sgeneros a") if a.text(strip=True)]
151
130
 
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)
157
- duration = int(dur_match.group()) if dur_match else None
131
+ duration_text = secici.select_text("span.runtime")
132
+ duration = int(secici.regex_first(r"\d+", duration_text)) if duration_text and secici.regex_first(r"\d+", duration_text) else None
158
133
 
159
- actors = [span.text(strip=True) for span in secici.css("span.valor a > span") if span.text(strip=True)]
134
+ actors = [span.text(strip=True) for span in secici.select("span.valor a > span") if span.text(strip=True)]
160
135
 
161
- trailer_match = re.search(r'embed/([^?]*)\?rel', html_text)
162
- trailer = f"https://www.youtube.com/embed/{trailer_match.group(1)}" if trailer_match else None
136
+ trailer = None
137
+ if trailer_id := secici.regex_first(r'embed/([^?]*)\?rel', html_text):
138
+ trailer = f"https://www.youtube.com/embed/{trailer_id}"
163
139
 
164
140
  # Dizi mi film mi kontrol et
165
141
  is_series = "/dizi/" in url
166
142
 
167
143
  if is_series:
168
- year_link_el = secici.css_first("a[href*='/yil/']")
169
- if year_link_el:
170
- year_elem = year_link_el.text(strip=True)
171
- year_match = re.search(r"\d{4}", year_elem)
172
- year = year_match.group() if year_match else year
144
+ year_elem = secici.select_text("a[href*='/yil/']")
145
+ if year_elem:
146
+ year = secici.regex_first(r"\d{4}", year_elem) or year
173
147
 
174
148
  # Duration from info section
175
- for span in secici.css("div#info span"):
149
+ for span in secici.select("div#info span"):
176
150
  span_text = span.text(strip=True) if span.text() else ""
177
151
  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
152
+ duration = secici.regex_first(r"\d+", span_text) and int(secici.regex_first(r"\d+", span_text))
180
153
  break
181
154
 
182
155
  episodes = []
183
- for ep_item in secici.css("div#episodes ul.episodios li"):
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
+ for ep_item in secici.select("div#episodes ul.episodios li"):
157
+ ep_href = secici.select_attr("h4.episodiotitle a", "href", ep_item)
158
+ ep_name = secici.select_text("h4.episodiotitle a", ep_item)
187
159
 
188
160
  if not ep_href or not ep_name:
189
161
  continue
190
162
 
191
163
  ep_detail = ep_name
192
- season_match = re.search(r"(\d+)\.\s*Sezon", ep_detail)
193
- episode_match = re.search(r"Sezon\s+(\d+)\.\s*Bölüm", ep_detail)
164
+ ep_season = secici.regex_first(r"(\d+)\.\s*Sezon", ep_detail) or 1
165
+ ep_episode = secici.regex_first(r"Sezon\s+(\d+)\.\s*Bölüm", ep_detail)
194
166
 
195
- ep_season = int(season_match.group(1)) if season_match else 1
196
- ep_episode = int(episode_match.group(1)) if episode_match else None
167
+ ep_season = int(ep_season) if isinstance(ep_season, str) and ep_season.isdigit() else 1
168
+ ep_episode = int(ep_episode) if isinstance(ep_episode, str) and ep_episode.isdigit() else None
197
169
 
198
170
  episodes.append(Episode(
199
171
  season = ep_season,
@@ -201,7 +173,6 @@ class SetFilmIzle(PluginBase):
201
173
  title = ep_name,
202
174
  url = self.fix_url(ep_href)
203
175
  ))
204
-
205
176
  return SeriesInfo(
206
177
  url = url,
207
178
  poster = self.fix_url(poster) if poster else None,
@@ -227,9 +198,9 @@ class SetFilmIzle(PluginBase):
227
198
 
228
199
  async def load_links(self, url: str) -> list[ExtractResult]:
229
200
  istek = await self.httpx.get(url)
230
- secici = HTMLParser(istek.text)
201
+ secici = HTMLHelper(istek.text)
231
202
 
232
- nonce = secici.css_first("div#playex").attrs.get("data-nonce") if secici.css_first("div#playex") else ""
203
+ nonce = secici.select_attr("div#playex", "data-nonce") or ""
233
204
 
234
205
  # partKey to dil label mapping
235
206
  part_key_labels = {
@@ -279,7 +250,7 @@ class SetFilmIzle(PluginBase):
279
250
 
280
251
  return await self.extract(iframe_url, prefix=label if label else None)
281
252
 
282
- for player in secici.css("nav.player a"):
253
+ for player in secici.select("nav.player a"):
283
254
  tasks.append(fetch_and_extract(player))
284
255
 
285
256
  results = await asyncio.gather(*tasks)
@@ -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", veri)
79
+ href = secici.select_attr("a", "href", veri)
80
+ poster = secici.select_attr("img", "data-src", veri)
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: