KekikStream 2.2.9__py3-none-any.whl → 2.5.3__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.

Potentially problematic release.


This version of KekikStream might be problematic. Click here for more details.

Files changed (88) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +3 -2
  2. KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
  3. KekikStream/Core/HTMLHelper.py +205 -0
  4. KekikStream/Core/Plugin/PluginBase.py +48 -12
  5. KekikStream/Core/Plugin/PluginLoader.py +13 -14
  6. KekikStream/Core/Plugin/PluginManager.py +2 -2
  7. KekikStream/Core/Plugin/PluginModels.py +0 -3
  8. KekikStream/Core/__init__.py +2 -0
  9. KekikStream/Extractors/Abstream.py +27 -0
  10. KekikStream/Extractors/CloseLoad.py +31 -56
  11. KekikStream/Extractors/ContentX.py +28 -71
  12. KekikStream/Extractors/DonilasPlay.py +34 -78
  13. KekikStream/Extractors/DzenRu.py +11 -25
  14. KekikStream/Extractors/ExPlay.py +20 -38
  15. KekikStream/Extractors/Filemoon.py +23 -53
  16. KekikStream/Extractors/HDMomPlayer.py +30 -0
  17. KekikStream/Extractors/HDPlayerSystem.py +13 -31
  18. KekikStream/Extractors/HotStream.py +27 -0
  19. KekikStream/Extractors/JFVid.py +3 -24
  20. KekikStream/Extractors/JetTv.py +21 -34
  21. KekikStream/Extractors/JetV.py +55 -0
  22. KekikStream/Extractors/MailRu.py +11 -29
  23. KekikStream/Extractors/MixPlayHD.py +17 -31
  24. KekikStream/Extractors/MixTiger.py +17 -40
  25. KekikStream/Extractors/MolyStream.py +25 -22
  26. KekikStream/Extractors/Odnoklassniki.py +41 -105
  27. KekikStream/Extractors/PeaceMakerst.py +20 -47
  28. KekikStream/Extractors/PixelDrain.py +9 -16
  29. KekikStream/Extractors/PlayerFilmIzle.py +23 -46
  30. KekikStream/Extractors/RapidVid.py +23 -36
  31. KekikStream/Extractors/SetPlay.py +19 -44
  32. KekikStream/Extractors/SetPrime.py +3 -6
  33. KekikStream/Extractors/SibNet.py +8 -19
  34. KekikStream/Extractors/Sobreatsesuyp.py +25 -47
  35. KekikStream/Extractors/TRsTX.py +25 -55
  36. KekikStream/Extractors/TurboImgz.py +8 -16
  37. KekikStream/Extractors/TurkeyPlayer.py +5 -5
  38. KekikStream/Extractors/VCTPlay.py +10 -28
  39. KekikStream/Extractors/Veev.py +145 -0
  40. KekikStream/Extractors/VidBiz.py +62 -0
  41. KekikStream/Extractors/VidHide.py +59 -34
  42. KekikStream/Extractors/VidMoly.py +67 -89
  43. KekikStream/Extractors/VidMoxy.py +17 -29
  44. KekikStream/Extractors/VidPapi.py +26 -58
  45. KekikStream/Extractors/VideoSeyred.py +21 -42
  46. KekikStream/Extractors/Videostr.py +58 -0
  47. KekikStream/Extractors/Vidoza.py +18 -0
  48. KekikStream/Extractors/Vtbe.py +38 -0
  49. KekikStream/Extractors/YTDLP.py +2 -2
  50. KekikStream/Extractors/YildizKisaFilm.py +13 -31
  51. KekikStream/Extractors/Zeus.py +61 -0
  52. KekikStream/Plugins/BelgeselX.py +108 -99
  53. KekikStream/Plugins/DiziBox.py +61 -106
  54. KekikStream/Plugins/DiziMom.py +179 -0
  55. KekikStream/Plugins/DiziPal.py +104 -192
  56. KekikStream/Plugins/DiziYou.py +66 -149
  57. KekikStream/Plugins/Dizilla.py +93 -126
  58. KekikStream/Plugins/FilmBip.py +102 -72
  59. KekikStream/Plugins/FilmEkseni.py +199 -0
  60. KekikStream/Plugins/FilmMakinesi.py +101 -64
  61. KekikStream/Plugins/FilmModu.py +35 -59
  62. KekikStream/Plugins/Filmatek.py +184 -0
  63. KekikStream/Plugins/FilmciBaba.py +155 -0
  64. KekikStream/Plugins/FullHDFilmizlesene.py +32 -78
  65. KekikStream/Plugins/HDFilm.py +243 -0
  66. KekikStream/Plugins/HDFilmCehennemi.py +261 -222
  67. KekikStream/Plugins/JetFilmizle.py +117 -98
  68. KekikStream/Plugins/KultFilmler.py +153 -143
  69. KekikStream/Plugins/RecTV.py +53 -49
  70. KekikStream/Plugins/RoketDizi.py +92 -123
  71. KekikStream/Plugins/SelcukFlix.py +86 -95
  72. KekikStream/Plugins/SetFilmIzle.py +105 -143
  73. KekikStream/Plugins/SezonlukDizi.py +106 -128
  74. KekikStream/Plugins/Sinefy.py +194 -166
  75. KekikStream/Plugins/SinemaCX.py +159 -113
  76. KekikStream/Plugins/Sinezy.py +44 -73
  77. KekikStream/Plugins/SuperFilmGeldi.py +28 -52
  78. KekikStream/Plugins/UgurFilm.py +94 -72
  79. KekikStream/Plugins/Watch32.py +160 -0
  80. KekikStream/Plugins/YabanciDizi.py +250 -0
  81. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/METADATA +1 -1
  82. kekikstream-2.5.3.dist-info/RECORD +99 -0
  83. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/WHEEL +1 -1
  84. KekikStream/Plugins/FullHDFilm.py +0 -254
  85. kekikstream-2.2.9.dist-info/RECORD +0 -82
  86. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/entry_points.txt +0 -0
  87. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/licenses/LICENSE +0 -0
  88. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.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, 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, contextlib, asyncio
6
5
 
7
6
  class Sinefy(PluginBase):
8
7
  name = "Sinefy"
@@ -43,18 +42,14 @@ class Sinefy(PluginBase):
43
42
  else:
44
43
  full_url = f"{url}&page={page}"
45
44
 
46
- resp = await self.httpx.get(full_url)
47
- sel = HTMLParser(resp.text)
45
+ istek = await self.httpx.get(full_url)
46
+ secici = HTMLHelper(istek.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 secici.select("div.poster-with-subject, div.dark-segment div.poster-md.poster"):
50
+ title = secici.select_text("h2", item)
51
+ href = secici.select_attr("a", "href", item)
52
+ poster = secici.select_attr("img", "data-srcset", item)
58
53
 
59
54
  if poster:
60
55
  poster = poster.split(",")[0].split(" ")[0]
@@ -64,7 +59,7 @@ class Sinefy(PluginBase):
64
59
  category = category,
65
60
  title = title,
66
61
  url = self.fix_url(href),
67
- poster = self.fix_url(poster) if poster else None
62
+ poster = self.fix_url(poster)
68
63
  ))
69
64
 
70
65
  return results
@@ -74,197 +69,230 @@ class Sinefy(PluginBase):
74
69
  c_key = "ca1d4a53d0f4761a949b85e51e18f096"
75
70
  c_value = "MTc0NzI2OTAwMDU3ZTEwYmZjMDViNWFmOWIwZDViODg0MjU4MjA1ZmYxOThmZTYwMDdjMWQzMzliNzY5NzFlZmViMzRhMGVmNjgwODU3MGIyZA=="
76
71
 
77
- try:
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']")
72
+ with contextlib.suppress(Exception):
73
+ istek = await self.httpx.get(self.main_url)
74
+ secici = HTMLHelper(istek.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 = secici.select_attr("input[name='cKey']", "value")
77
+ cval = secici.select_attr("input[name='cValue']", "value")
86
78
 
87
79
  if cke and cval:
88
80
  c_key = cke
89
81
  c_value = cval
90
82
 
91
- except Exception:
92
- pass
83
+ response = await self.httpx.post(
84
+ url = f"{self.main_url}/bg/searchcontent",
85
+ headers = {
86
+ "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0",
87
+ "Accept" : "application/json, text/javascript, */*; q=0.01",
88
+ "X-Requested-With" : "XMLHttpRequest",
89
+ "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8"
90
+ },
91
+ data = {
92
+ "cKey" : c_key,
93
+ "cValue" : c_value,
94
+ "searchTerm" : query
95
+ }
96
+ )
93
97
 
94
- post_url = f"{self.main_url}/bg/searchcontent"
95
- data = {
96
- "cKey" : c_key,
97
- "cValue" : c_value,
98
- "searchTerm" : query
99
- }
100
-
101
- headers = {
102
- "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0",
103
- "Accept" : "application/json, text/javascript, */*; q=0.01",
104
- "X-Requested-With" : "XMLHttpRequest",
105
- "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8"
106
- }
107
-
108
- response = await self.httpx.post(post_url, data=data, headers=headers)
109
-
110
- try:
98
+ with contextlib.suppress(Exception):
111
99
  # Extract JSON data from response (might contain garbage chars at start)
112
100
  raw = response.text
113
101
  json_start = raw.find('{')
114
102
  if json_start != -1:
115
103
  clean_json = raw[json_start:]
116
- data = json.loads(clean_json)
117
-
104
+ data = json.loads(clean_json)
105
+
118
106
  results = []
119
107
  # Result array is in data['data']['result']
120
108
  res_array = data.get("data", {}).get("result", [])
121
-
109
+
122
110
  if not res_array:
123
111
  # Fallback manual parsing ?
124
112
  pass
125
113
 
126
114
  for item in res_array:
127
- name = item.get("object_name")
128
- slug = item.get("used_slug")
115
+ name = item.get("object_name")
116
+ slug = item.get("used_slug")
129
117
  poster = item.get("object_poster_url")
130
-
118
+
131
119
  if name and slug:
132
120
  if "cdn.ampproject.org" in poster:
133
121
  poster = "https://images.macellan.online/images/movie/poster/180/275/80/" + poster.split("/")[-1]
134
-
122
+
135
123
  results.append(SearchResult(
136
- title=name,
137
- url=self.fix_url(slug),
138
- poster=self.fix_url(poster) if poster else None
124
+ title = name,
125
+ url = self.fix_url(slug),
126
+ poster = self.fix_url(poster)
139
127
  ))
140
128
  return results
141
129
 
142
- except Exception:
143
- pass
144
130
  return []
145
131
 
146
- async def load_item(self, url: str) -> SeriesInfo:
147
- resp = await self.httpx.get(url)
148
- sel = HTMLParser(resp.text)
132
+ async def load_item(self, url: str) -> SeriesInfo | MovieInfo:
133
+ istek = await self.httpx.get(url)
134
+ secici = HTMLHelper(istek.text)
149
135
 
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
155
- poster = None
156
- if poster_info:
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
163
-
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)]
136
+ title = secici.select_direct_text("h1")
137
+ poster_attr = secici.select_attr("img.series-profile-thumb", "data-srcset") or secici.select_attr("img.series-profile-thumb", "srcset")
138
+ if poster_attr:
139
+ # "url 1x, url 2x" -> en sondakini (en yüksek kalite) al
140
+ poster = poster_attr.split(",")[-1].strip().split(" ")[0]
141
+ else:
142
+ poster = secici.select_poster("img.series-profile-thumb")
168
143
 
169
- rating_el = sel.css_first("span.color-imdb")
170
- rating = rating_el.text(strip=True) if rating_el else None
144
+ description = secici.select_text("p#tv-series-desc")
145
+ tags = secici.select_texts("div.item.categories a")
146
+ rating = secici.select_text("span.color-imdb")
147
+ actors = secici.select_texts("div.content h5")
148
+ year = secici.extract_year("div.truncate")
149
+ duration = secici.regex_first(r"(\d+)", secici.select_text(".media-meta td:last-child"))
150
+ if duration == year or int(duration) < 40:
151
+ duration = None
171
152
 
172
- actors = [h5.text(strip=True) for h5 in sel.css("div.content h5") if h5.text(strip=True)]
153
+ common_info = {
154
+ "url" : url,
155
+ "poster" : self.fix_url(poster),
156
+ "title" : title,
157
+ "description" : description,
158
+ "tags" : tags,
159
+ "rating" : rating,
160
+ "year" : year,
161
+ "actors" : actors,
162
+ "duration" : duration
163
+ }
173
164
 
174
- year_el = sel.css_first("span.item.year")
175
- year = year_el.text(strip=True) if year_el else None
176
-
177
165
  episodes = []
178
- season_elements = sel.css("section.episodes-box")
179
-
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:
166
+ for tab in secici.select("div.ui.tab"):
167
+ for link in secici.select("a[href*='bolum']", tab):
185
168
  href = link.attrs.get("href")
186
169
  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
221
-
170
+ s, e = secici.extract_season_episode(href)
171
+ name = secici.select_text("div.content div.header", link) or link.text(strip=True)
172
+ episodes.append(Episode(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
173
+
222
174
  if episodes:
223
- return SeriesInfo(
224
- title = title,
225
- url = url,
226
- poster = self.fix_url(poster) if poster else None,
227
- description = description,
228
- rating = rating,
229
- tags = tags,
230
- actors = actors,
231
- year = year,
232
- episodes = episodes
233
- )
234
- else:
235
- return MovieInfo(
236
- title = title,
237
- url = url,
238
- poster = self.fix_url(poster) if poster else None,
239
- description = description,
240
- rating = rating,
241
- tags = tags,
242
- actors = actors,
243
- year = year
244
- )
175
+ return SeriesInfo(**common_info, episodes=episodes)
245
176
 
246
- async def load_links(self, url: str) -> list[ExtractResult]:
247
- resp = await self.httpx.get(url)
248
- sel = HTMLParser(resp.text)
249
-
250
- iframe_el = sel.css_first("iframe")
251
- iframe = iframe_el.attrs.get("src") if iframe_el else None
177
+ return MovieInfo(**common_info)
252
178
 
253
- if not iframe:
254
- return []
255
-
256
- iframe_url = self.fix_url(iframe)
257
-
258
- # Try to extract actual video URL, fallback to raw iframe if fails
179
+ def _find_iframe(self, secici: HTMLHelper) -> str | None:
180
+ """Sayfa kaynağındaki video iframe adresini bulur."""
181
+ src = secici.select_attr("iframe", "src") or \
182
+ secici.select_attr("iframe", "data-src") or \
183
+ secici.regex_first(r'<iframe[^>]+src="([^"]+)"')
184
+ return self.fix_url(src) if src else None
185
+
186
+ async def _process_source(self, source: dict, subtitles: list) -> list[ExtractResult]:
187
+ """Tekil bir kaynağı işleyip sonucu döndürür."""
188
+ target_url = source["url"]
189
+ name = source["name"]
190
+
191
+ # Eğer direkt iframe değilse (Sayfa linki ise), önce iframe'i bul
192
+ if not source.get("is_main"):
193
+ try:
194
+ resp = await self.httpx.get(target_url)
195
+ temp_sel = HTMLHelper(resp.text)
196
+
197
+ if not (iframe_url := self._find_iframe(temp_sel)):
198
+ return []
199
+
200
+ target_url = iframe_url
201
+
202
+ # Tab (Dil Seçeneği) ise, gittiğimiz sayfadaki aktif player ismini ekle
203
+ if source.get("is_tab"):
204
+ p_name = temp_sel.select_text("div.alternatives-for-this div.playeritems.active") or "PUB"
205
+ name = f"{name} | {p_name}"
206
+ except Exception:
207
+ return []
208
+
209
+ # Linki Extract Et
259
210
  try:
260
- result = await self.extract(iframe_url)
261
- if result:
262
- return [result] if not isinstance(result, list) else result
211
+ extracted = await self.extract(target_url, referer=self.main_url)
212
+ if not extracted:
213
+ return []
214
+
215
+ items = extracted if isinstance(extracted, list) else [extracted]
216
+
217
+ # Sonuçları işle (İsim ver, altyazı ekle)
218
+ copy_subtitles = list(subtitles) # Her item için kopyasını kullan
219
+ for item in items:
220
+ item.name = name
221
+ if copy_subtitles:
222
+ if not item.subtitles:
223
+ item.subtitles = copy_subtitles
224
+ else:
225
+ item.subtitles.extend(copy_subtitles)
226
+
227
+ return items
263
228
  except Exception:
264
- pass
265
-
266
- # Fallback: return raw iframe URL
267
- return [ExtractResult(
268
- url = iframe_url,
269
- name = "Sinefy Player"
270
- )]
229
+ return []
230
+
231
+ async def load_links(self, url: str) -> list[ExtractResult]:
232
+ istek = await self.httpx.get(url)
233
+ secici = HTMLHelper(istek.text)
234
+
235
+ # 1. Altyazıları Topla
236
+ subtitles = []
237
+ for track in secici.select("track"):
238
+ if track.attrs.get("kind") in ("subtitles", "captions"):
239
+ if src := track.attrs.get("src"):
240
+ lang = track.attrs.get("label") or track.attrs.get("srclang") or "Altyazı"
241
+ subtitles.append(self.new_subtitle(src, lang))
242
+
243
+ sources = []
244
+
245
+ # Aktif Sayfa Bilgileri
246
+ active_tab_name = secici.select_text("div#series-tabs a.active") or "Sinefy"
247
+ active_player = secici.select_text("div.alternatives-for-this div.playeritems.active") or "PUB"
248
+
249
+ # A) Ana Video (Main Iframe)
250
+ if main_iframe := self._find_iframe(secici):
251
+ sources.append({
252
+ "url" : main_iframe,
253
+ "name" : f"{active_tab_name} | {active_player}",
254
+ "is_main" : True,
255
+ "is_tab" : False
256
+ })
257
+
258
+ # B) Alternatif Playerlar (Mevcut Sayfa Player Butonları)
259
+ for btn in secici.select("div.alternatives-for-this div.playeritems:not(.active) a"):
260
+ if href := btn.attrs.get("href"):
261
+ if "javascript" not in href:
262
+ sources.append({
263
+ "url" : self.fix_url(href),
264
+ "name" : f"{active_tab_name} | {btn.text(strip=True)}",
265
+ "is_main" : False,
266
+ "is_tab" : False
267
+ })
268
+
269
+ # C) Diğer Dil Seçenekleri (Tabs - Sekmeler)
270
+ for tab in secici.select("div#series-tabs a:not(.active)"):
271
+ if href := tab.attrs.get("href"):
272
+ sources.append({
273
+ "url" : self.fix_url(href),
274
+ "name" : tab.text(strip=True),
275
+ "is_main" : False,
276
+ "is_tab" : True
277
+ })
278
+
279
+ # 2. Kaynakları Paralel İşle
280
+ tasks = [self._process_source(src, subtitles) for src in sources]
281
+ results_groups = await asyncio.gather(*tasks)
282
+
283
+ # 3. Sonuçları Birleştir
284
+ final_results = []
285
+ for group in results_groups:
286
+ if group:
287
+ final_results.extend(group)
288
+
289
+ # 4. Duplicate Temizle (URL + İsim Kombinasyonu)
290
+ unique_results = []
291
+ seen = set()
292
+ for res in final_results:
293
+ key = (res.url, res.name)
294
+ if res.url and key not in seen:
295
+ unique_results.append(res)
296
+ seen.add(key)
297
+
298
+ return unique_results