KekikStream 2.2.3__py3-none-any.whl → 2.2.6__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 (34) hide show
  1. KekikStream/Extractors/CloseLoad.py +7 -8
  2. KekikStream/Extractors/Filemoon.py +7 -6
  3. KekikStream/Extractors/MolyStream.py +6 -5
  4. KekikStream/Extractors/VidHide.py +0 -1
  5. KekikStream/Extractors/VidMoly.py +17 -9
  6. KekikStream/Plugins/BelgeselX.py +39 -20
  7. KekikStream/Plugins/DiziBox.py +115 -59
  8. KekikStream/Plugins/DiziPal.py +87 -40
  9. KekikStream/Plugins/DiziYou.py +105 -64
  10. KekikStream/Plugins/Dizilla.py +57 -28
  11. KekikStream/Plugins/FilmBip.py +60 -31
  12. KekikStream/Plugins/FilmMakinesi.py +75 -51
  13. KekikStream/Plugins/FilmModu.py +73 -36
  14. KekikStream/Plugins/FullHDFilm.py +82 -48
  15. KekikStream/Plugins/FullHDFilmizlesene.py +94 -39
  16. KekikStream/Plugins/HDFilmCehennemi.py +79 -54
  17. KekikStream/Plugins/JetFilmizle.py +78 -50
  18. KekikStream/Plugins/KultFilmler.py +64 -34
  19. KekikStream/Plugins/RoketDizi.py +43 -26
  20. KekikStream/Plugins/SelcukFlix.py +27 -14
  21. KekikStream/Plugins/SetFilmIzle.py +74 -43
  22. KekikStream/Plugins/SezonlukDizi.py +102 -46
  23. KekikStream/Plugins/Sinefy.py +130 -101
  24. KekikStream/Plugins/SinemaCX.py +82 -37
  25. KekikStream/Plugins/Sinezy.py +61 -47
  26. KekikStream/Plugins/SuperFilmGeldi.py +72 -36
  27. KekikStream/Plugins/UgurFilm.py +72 -34
  28. KekikStream/requirements.txt +1 -1
  29. {kekikstream-2.2.3.dist-info → kekikstream-2.2.6.dist-info}/METADATA +3 -3
  30. {kekikstream-2.2.3.dist-info → kekikstream-2.2.6.dist-info}/RECORD +34 -34
  31. {kekikstream-2.2.3.dist-info → kekikstream-2.2.6.dist-info}/WHEEL +0 -0
  32. {kekikstream-2.2.3.dist-info → kekikstream-2.2.6.dist-info}/entry_points.txt +0 -0
  33. {kekikstream-2.2.3.dist-info → kekikstream-2.2.6.dist-info}/licenses/LICENSE +0 -0
  34. {kekikstream-2.2.3.dist-info → kekikstream-2.2.6.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
4
- from parsel import Selector
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
4
+ from selectolax.parser import HTMLParser
5
5
  import re
6
6
 
7
7
  class SezonlukDizi(PluginBase):
@@ -41,73 +41,125 @@ class SezonlukDizi(PluginBase):
41
41
 
42
42
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
43
43
  istek = await self.httpx.get(f"{url}{page}")
44
- secici = Selector(istek.text)
45
-
46
- return [
47
- MainPageResult(
48
- category = category,
49
- title = veri.css("div.description::text").get(),
50
- url = self.fix_url(veri.css("::attr(href)").get()),
51
- poster = self.fix_url(veri.css("img::attr(data-src)").get()),
52
- )
53
- for veri in secici.css("div.afis a") if veri.css("div.description::text").get()
54
- ]
44
+ secici = HTMLParser(istek.text)
45
+
46
+ results = []
47
+ for veri in secici.css("div.afis a"):
48
+ desc_el = veri.css_first("div.description")
49
+ img_el = veri.css_first("img")
50
+
51
+ title = desc_el.text(strip=True) if desc_el else None
52
+ href = veri.attrs.get("href")
53
+ poster = img_el.attrs.get("data-src") if img_el else None
54
+
55
+ if title and href:
56
+ results.append(MainPageResult(
57
+ category = category,
58
+ title = title,
59
+ url = self.fix_url(href),
60
+ poster = self.fix_url(poster) if poster else None,
61
+ ))
62
+
63
+ return results
55
64
 
56
65
  async def search(self, query: str) -> list[SearchResult]:
57
66
  istek = await self.httpx.get(f"{self.main_url}/diziler.asp?adi={query}")
58
- secici = Selector(istek.text)
67
+ secici = HTMLParser(istek.text)
59
68
 
60
- return [
61
- SearchResult(
62
- title = afis.css("div.description::text").get().strip(),
63
- url = self.fix_url(afis.attrib.get("href")),
64
- poster = self.fix_url(afis.css("img::attr(data-src)").get()),
65
- )
66
- for afis in secici.css("div.afis a.column")
67
- ]
69
+ results = []
70
+ for afis in secici.css("div.afis a.column"):
71
+ desc_el = afis.css_first("div.description")
72
+ img_el = afis.css_first("img")
73
+
74
+ title = desc_el.text(strip=True) if desc_el else None
75
+ href = afis.attrs.get("href")
76
+ poster = img_el.attrs.get("data-src") if img_el else None
77
+
78
+ if title and href:
79
+ results.append(SearchResult(
80
+ title = title,
81
+ url = self.fix_url(href),
82
+ poster = self.fix_url(poster) if poster else None,
83
+ ))
84
+
85
+ return results
68
86
 
69
87
  async def load_item(self, url: str) -> SeriesInfo:
70
88
  istek = await self.httpx.get(url)
71
- secici = Selector(istek.text)
89
+ secici = HTMLParser(istek.text)
90
+
91
+ title_el = secici.css_first("div.header")
92
+ title = title_el.text(strip=True) if title_el else ""
93
+
94
+ poster_el = secici.css_first("div.image img")
95
+ poster = poster_el.attrs.get("data-src", "").strip() if poster_el else ""
96
+
97
+ # year: re_first yerine re.search
98
+ year_el = secici.css_first("div.extra span")
99
+ year_text = year_el.text(strip=True) if year_el else ""
100
+ year_match = re.search(r"(\d{4})", year_text)
101
+ year = year_match.group(1) if year_match else None
72
102
 
73
- title = secici.css("div.header::text").get().strip()
74
- poster = self.fix_url(secici.css("div.image img::attr(data-src)").get().strip())
75
- year = secici.css("div.extra span::text").re_first(r"(\d{4})")
76
- description = secici.xpath("normalize-space(//span[@id='tartismayorum-konu'])").get()
77
- tags = secici.css("div.labels a[href*='tur']::text").getall()
78
- rating = secici.css("div.dizipuani a div::text").re_first(r"[\d.,]+")
79
- actors = []
103
+ # xpath normalized-space yerine doğrudan ID ile element bulup text al
104
+ desc_el = secici.css_first("span#tartismayorum-konu")
105
+ description = desc_el.text(strip=True) if desc_el else ""
106
+
107
+ tags = [a.text(strip=True) for a in secici.css("div.labels a[href*='tur']") if a.text(strip=True)]
108
+
109
+ # rating: re_first yerine re.search
110
+ rating_el = secici.css_first("div.dizipuani a div")
111
+ rating_text = rating_el.text(strip=True) if rating_el else ""
112
+ rating_match = re.search(r"[\d.,]+", rating_text)
113
+ rating = rating_match.group() if rating_match else None
114
+
115
+ actors = []
80
116
 
81
117
  actors_istek = await self.httpx.get(f"{self.main_url}/oyuncular/{url.split('/')[-1]}")
82
- actors_secici = Selector(actors_istek.text)
83
- actors = [
84
- actor.css("div.header::text").get().strip()
85
- for actor in actors_secici.css("div.doubling div.ui")
86
- ]
118
+ actors_secici = HTMLParser(actors_istek.text)
119
+ for actor in actors_secici.css("div.doubling div.ui"):
120
+ header_el = actor.css_first("div.header")
121
+ if header_el and header_el.text(strip=True):
122
+ actors.append(header_el.text(strip=True))
87
123
 
88
124
  episodes_istek = await self.httpx.get(f"{self.main_url}/bolumler/{url.split('/')[-1]}")
89
- episodes_secici = Selector(episodes_istek.text)
125
+ episodes_secici = HTMLParser(episodes_istek.text)
90
126
  episodes = []
91
127
 
92
128
  for sezon in episodes_secici.css("table.unstackable"):
93
129
  for bolum in sezon.css("tbody tr"):
94
- ep_name = bolum.css("td:nth-of-type(4) a::text").get().strip()
95
- ep_href = self.fix_url(bolum.css("td:nth-of-type(4) a::attr(href)").get())
96
- ep_episode = bolum.css("td:nth-of-type(3) a::text").re_first(r"(\d+)")
97
- ep_season = bolum.css("td:nth-of-type(2)::text").re_first(r"(\d+)")
130
+ # td:nth-of-type selectolax'ta desteklenmiyor, alternatif yol: tüm td'leri alıp indexle
131
+ tds = bolum.css("td")
132
+ if len(tds) < 4:
133
+ continue
134
+
135
+ # 4. td'den isim ve href
136
+ ep_name_el = tds[3].css_first("a")
137
+ ep_name = ep_name_el.text(strip=True) if ep_name_el else None
138
+ ep_href = ep_name_el.attrs.get("href") if ep_name_el else None
139
+
140
+ # 3. td'den episode (re_first yerine re.search)
141
+ ep_episode_el = tds[2].css_first("a")
142
+ ep_episode_text = ep_episode_el.text(strip=True) if ep_episode_el else ""
143
+ ep_episode_match = re.search(r"(\d+)", ep_episode_text)
144
+ ep_episode = ep_episode_match.group(1) if ep_episode_match else None
145
+
146
+ # 2. td'den season (re_first yerine re.search)
147
+ ep_season_text = tds[1].text(strip=True) if tds[1] else ""
148
+ ep_season_match = re.search(r"(\d+)", ep_season_text)
149
+ ep_season = ep_season_match.group(1) if ep_season_match else None
98
150
 
99
151
  if ep_name and ep_href:
100
152
  episode = Episode(
101
153
  season = ep_season,
102
154
  episode = ep_episode,
103
155
  title = ep_name,
104
- url = ep_href,
156
+ url = self.fix_url(ep_href),
105
157
  )
106
158
  episodes.append(episode)
107
159
 
108
160
  return SeriesInfo(
109
161
  url = url,
110
- poster = poster,
162
+ poster = self.fix_url(poster) if poster else None,
111
163
  title = title,
112
164
  description = description,
113
165
  tags = tags,
@@ -133,9 +185,10 @@ class SezonlukDizi(PluginBase):
133
185
 
134
186
  async def load_links(self, url: str) -> list[ExtractResult]:
135
187
  istek = await self.httpx.get(url)
136
- secici = Selector(istek.text)
188
+ secici = HTMLParser(istek.text)
137
189
 
138
- bid = secici.css("div#dilsec::attr(data-id)").get()
190
+ dilsec_el = secici.css_first("div#dilsec")
191
+ bid = dilsec_el.attrs.get("data-id") if dilsec_el else None
139
192
  if not bid:
140
193
  return []
141
194
 
@@ -162,9 +215,12 @@ class SezonlukDizi(PluginBase):
162
215
  headers = {"X-Requested-With": "XMLHttpRequest"},
163
216
  data = {"id": veri.get("id")},
164
217
  )
165
- secici = Selector(veri_response.text)
218
+ veri_secici = HTMLParser(veri_response.text)
219
+
220
+ iframe_el = veri_secici.css_first("iframe")
221
+ iframe = iframe_el.attrs.get("src") if iframe_el else None
166
222
 
167
- if iframe := secici.css("iframe::attr(src)").get():
223
+ if iframe:
168
224
  if "link.asp" in iframe:
169
225
  continue
170
226
 
@@ -1,7 +1,7 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, MovieInfo, ExtractResult
4
- from parsel import Selector
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, MovieInfo, ExtractResult
4
+ from selectolax.parser import HTMLParser
5
5
  import re, json, urllib.parse
6
6
 
7
7
  class Sinefy(PluginBase):
@@ -37,31 +37,35 @@ class Sinefy(PluginBase):
37
37
 
38
38
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
39
39
  if "page/" in url:
40
- full_url = f"{url}{page}"
40
+ full_url = f"{url}{page}"
41
41
  elif "en-yenifilmler" in url or "netflix" in url:
42
- full_url = f"{url}/{page}"
42
+ full_url = f"{url}/{page}"
43
43
  else:
44
- full_url = f"{url}&page={page}"
44
+ full_url = f"{url}&page={page}"
45
45
 
46
46
  resp = await self.httpx.get(full_url)
47
- sel = Selector(resp.text)
47
+ sel = HTMLParser(resp.text)
48
48
 
49
49
  results = []
50
- # Kotlin: div.poster-with-subject, div.dark-segment div.poster-md.poster
51
50
  for item in sel.css("div.poster-with-subject, div.dark-segment div.poster-md.poster"):
52
- title = item.css("h2::text").get()
53
- href = item.css("a::attr(href)").get()
54
- poster = item.css("img::attr(data-srcset)").get()
55
- if poster:
56
- poster = poster.split(",")[0].split(" ")[0]
57
-
58
- if title and href:
59
- results.append(MainPageResult(
60
- category = category,
61
- title = title,
62
- url = self.fix_url(href),
63
- poster = self.fix_url(poster)
64
- ))
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
58
+
59
+ if poster:
60
+ poster = poster.split(",")[0].split(" ")[0]
61
+
62
+ if title and href:
63
+ results.append(MainPageResult(
64
+ category = category,
65
+ title = title,
66
+ url = self.fix_url(href),
67
+ poster = self.fix_url(poster) if poster else None
68
+ ))
65
69
 
66
70
  return results
67
71
 
@@ -71,13 +75,18 @@ class Sinefy(PluginBase):
71
75
  c_value = "MTc0NzI2OTAwMDU3ZTEwYmZjMDViNWFmOWIwZDViODg0MjU4MjA1ZmYxOThmZTYwMDdjMWQzMzliNzY5NzFlZmViMzRhMGVmNjgwODU3MGIyZA=="
72
76
 
73
77
  try:
74
- resp = await self.httpx.get(self.main_url)
75
- sel = Selector(resp.text)
76
- cke = sel.css("input[name='cKey']::attr(value)").get()
77
- cval = sel.css("input[name='cValue']::attr(value)").get()
78
- if cke and cval:
79
- c_key = cke
80
- c_value = cval
78
+ 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']")
83
+
84
+ cke = cke_el.attrs.get("value") if cke_el else None
85
+ cval = cval_el.attrs.get("value") if cval_el else None
86
+
87
+ if cke and cval:
88
+ c_key = cke
89
+ c_value = cval
81
90
 
82
91
  except Exception:
83
92
  pass
@@ -111,23 +120,23 @@ class Sinefy(PluginBase):
111
120
  res_array = data.get("data", {}).get("result", [])
112
121
 
113
122
  if not res_array:
114
- # Fallback manual parsing ?
115
- pass
123
+ # Fallback manual parsing ?
124
+ pass
116
125
 
117
126
  for item in res_array:
118
- name = item.get("object_name")
119
- slug = item.get("used_slug")
120
- poster = item.get("object_poster_url")
121
-
122
- if name and slug:
123
- if "cdn.ampproject.org" in poster:
124
- poster = "https://images.macellan.online/images/movie/poster/180/275/80/" + poster.split("/")[-1]
125
-
126
- results.append(SearchResult(
127
- title=name,
128
- url=self.fix_url(slug),
129
- poster=self.fix_url(poster)
130
- ))
127
+ name = item.get("object_name")
128
+ slug = item.get("used_slug")
129
+ poster = item.get("object_poster_url")
130
+
131
+ if name and slug:
132
+ if "cdn.ampproject.org" in poster:
133
+ poster = "https://images.macellan.online/images/movie/poster/180/275/80/" + poster.split("/")[-1]
134
+
135
+ results.append(SearchResult(
136
+ title=name,
137
+ url=self.fix_url(slug),
138
+ poster=self.fix_url(poster) if poster else None
139
+ ))
131
140
  return results
132
141
 
133
142
  except Exception:
@@ -136,74 +145,85 @@ class Sinefy(PluginBase):
136
145
 
137
146
  async def load_item(self, url: str) -> SeriesInfo:
138
147
  resp = await self.httpx.get(url)
139
- sel = Selector(resp.text)
148
+ sel = HTMLParser(resp.text)
140
149
 
141
- title = sel.css("h1::text").get()
142
- poster_info = sel.css("div.ui.items img::attr(data-srcset)").get()
150
+ title_el = sel.css_first("h1")
151
+ title = title_el.text(strip=True) if title_el else None
152
+
153
+ img_el = sel.css_first("div.ui.items img")
154
+ poster_info = img_el.attrs.get("data-srcset") if img_el else None
143
155
  poster = None
144
156
  if poster_info:
145
- # take 1x
146
- parts = str(poster_info).split(",")
147
- for p in parts:
148
- if "1x" in p:
149
- poster = p.strip().split(" ")[0]
150
- break
157
+ # take 1x
158
+ parts = str(poster_info).split(",")
159
+ for p in parts:
160
+ if "1x" in p:
161
+ poster = p.strip().split(" ")[0]
162
+ break
151
163
 
152
- description = sel.css("p#tv-series-desc::text").get()
153
- tags = sel.css("div.item.categories a::text").getall()
154
- rating = sel.css("span.color-imdb::text").get()
155
- actors = sel.css("div.content h5::text").getall()
156
- year = sel.css("span.item.year::text").get() # Year bilgisi eklendi
164
+ desc_el = sel.css_first("p#tv-series-desc")
165
+ description = desc_el.text(strip=True) if desc_el else None
166
+
167
+ tags = [a.text(strip=True) for a in sel.css("div.item.categories a") if a.text(strip=True)]
168
+
169
+ rating_el = sel.css_first("span.color-imdb")
170
+ rating = rating_el.text(strip=True) if rating_el else None
171
+
172
+ actors = [h5.text(strip=True) for h5 in sel.css("div.content h5") if h5.text(strip=True)]
173
+
174
+ year_el = sel.css_first("span.item.year")
175
+ year = year_el.text(strip=True) if year_el else None
157
176
 
158
177
  episodes = []
159
178
  season_elements = sel.css("section.episodes-box")
160
179
 
161
180
  if season_elements:
162
- # Get season links
163
- season_links = []
164
- menu = sel.css("div.ui.vertical.fluid.tabular.menu a")
165
- for link in menu:
166
- href = link.css("::attr(href)").get()
167
- if href:
168
- season_links.append(self.fix_url(href))
169
-
170
- for s_url in season_links:
171
- target_url = s_url if "/bolum-" in s_url else f"{s_url}/bolum-1"
172
-
173
- try:
174
- s_resp = await self.httpx.get(target_url)
175
- s_sel = Selector(s_resp.text)
176
- ep_links = s_sel.css("div.ui.list.celled a.item")
177
-
178
- current_season_no = 1
179
- match = re.search(r"sezon-(\d+)", target_url)
180
- if match:
181
- current_season_no = int(match.group(1))
182
-
183
- for ep_link in ep_links:
184
- href = ep_link.css("::attr(href)").get()
185
- name = ep_link.css("div.content div.header::text").get()
186
-
187
- if href:
188
- ep_no = 0
189
- match_ep = re.search(r"bolum-(\d+)", href)
190
- if match_ep:
191
- ep_no = int(match_ep.group(1))
192
-
193
- episodes.append(Episode(
194
- season = current_season_no,
195
- episode = ep_no,
196
- title = name.strip() if name else "",
197
- url = self.fix_url(href)
198
- ))
199
- except Exception:
200
- pass
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))
188
+
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")
196
+
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))
201
+
202
+ for ep_link in ep_links:
203
+ 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 ""
206
+
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
+
213
+ episodes.append(Episode(
214
+ season = current_season_no,
215
+ episode = ep_no,
216
+ title = name,
217
+ url = self.fix_url(href)
218
+ ))
219
+ except Exception:
220
+ pass
201
221
 
202
222
  if episodes:
203
223
  return SeriesInfo(
204
224
  title = title,
205
225
  url = url,
206
- poster = self.fix_url(poster),
226
+ poster = self.fix_url(poster) if poster else None,
207
227
  description = description,
208
228
  rating = rating,
209
229
  tags = tags,
@@ -215,7 +235,7 @@ class Sinefy(PluginBase):
215
235
  return MovieInfo(
216
236
  title = title,
217
237
  url = url,
218
- poster = self.fix_url(poster),
238
+ poster = self.fix_url(poster) if poster else None,
219
239
  description = description,
220
240
  rating = rating,
221
241
  tags = tags,
@@ -225,16 +245,25 @@ class Sinefy(PluginBase):
225
245
 
226
246
  async def load_links(self, url: str) -> list[ExtractResult]:
227
247
  resp = await self.httpx.get(url)
228
- sel = Selector(resp.text)
248
+ sel = HTMLParser(resp.text)
229
249
 
230
- iframe = sel.css("iframe::attr(src)").get()
250
+ iframe_el = sel.css_first("iframe")
251
+ iframe = iframe_el.attrs.get("src") if iframe_el else None
252
+
231
253
  if not iframe:
232
254
  return []
233
255
 
234
256
  iframe_url = self.fix_url(iframe)
235
257
 
236
- # Always return iframe (matching Kotlin - no extractor check)
237
- # loadExtractor in Kotlin handles extraction internally
258
+ # Try to extract actual video URL, fallback to raw iframe if fails
259
+ try:
260
+ result = await self.extract(iframe_url)
261
+ if result:
262
+ return [result] if not isinstance(result, list) else result
263
+ except Exception:
264
+ pass
265
+
266
+ # Fallback: return raw iframe URL
238
267
  return [ExtractResult(
239
268
  url = iframe_url,
240
269
  name = "Sinefy Player"