KekikStream 2.3.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.
Files changed (85) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +3 -2
  2. KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
  3. KekikStream/Core/HTMLHelper.py +120 -49
  4. KekikStream/Core/Plugin/PluginBase.py +30 -12
  5. KekikStream/Core/Plugin/PluginLoader.py +12 -14
  6. KekikStream/Core/Plugin/PluginManager.py +2 -2
  7. KekikStream/Core/Plugin/PluginModels.py +0 -3
  8. KekikStream/Extractors/Abstream.py +27 -0
  9. KekikStream/Extractors/CloseLoad.py +30 -54
  10. KekikStream/Extractors/ContentX.py +27 -72
  11. KekikStream/Extractors/DonilasPlay.py +33 -77
  12. KekikStream/Extractors/DzenRu.py +10 -24
  13. KekikStream/Extractors/ExPlay.py +20 -38
  14. KekikStream/Extractors/Filemoon.py +21 -46
  15. KekikStream/Extractors/HDMomPlayer.py +30 -0
  16. KekikStream/Extractors/HDPlayerSystem.py +13 -31
  17. KekikStream/Extractors/HotStream.py +27 -0
  18. KekikStream/Extractors/JFVid.py +3 -24
  19. KekikStream/Extractors/JetTv.py +21 -34
  20. KekikStream/Extractors/JetV.py +55 -0
  21. KekikStream/Extractors/MailRu.py +11 -29
  22. KekikStream/Extractors/MixPlayHD.py +15 -28
  23. KekikStream/Extractors/MixTiger.py +17 -40
  24. KekikStream/Extractors/MolyStream.py +17 -21
  25. KekikStream/Extractors/Odnoklassniki.py +40 -104
  26. KekikStream/Extractors/PeaceMakerst.py +18 -45
  27. KekikStream/Extractors/PixelDrain.py +8 -16
  28. KekikStream/Extractors/PlayerFilmIzle.py +22 -41
  29. KekikStream/Extractors/RapidVid.py +21 -35
  30. KekikStream/Extractors/SetPlay.py +18 -43
  31. KekikStream/Extractors/SibNet.py +7 -17
  32. KekikStream/Extractors/Sobreatsesuyp.py +23 -45
  33. KekikStream/Extractors/TRsTX.py +23 -53
  34. KekikStream/Extractors/TurboImgz.py +7 -14
  35. KekikStream/Extractors/VCTPlay.py +10 -28
  36. KekikStream/Extractors/Veev.py +145 -0
  37. KekikStream/Extractors/VidBiz.py +62 -0
  38. KekikStream/Extractors/VidHide.py +58 -30
  39. KekikStream/Extractors/VidMoly.py +65 -99
  40. KekikStream/Extractors/VidMoxy.py +16 -27
  41. KekikStream/Extractors/VidPapi.py +24 -54
  42. KekikStream/Extractors/VideoSeyred.py +19 -40
  43. KekikStream/Extractors/Videostr.py +58 -0
  44. KekikStream/Extractors/Vidoza.py +18 -0
  45. KekikStream/Extractors/Vtbe.py +38 -0
  46. KekikStream/Extractors/YTDLP.py +2 -2
  47. KekikStream/Extractors/YildizKisaFilm.py +13 -31
  48. KekikStream/Extractors/Zeus.py +61 -0
  49. KekikStream/Plugins/BelgeselX.py +97 -77
  50. KekikStream/Plugins/DiziBox.py +28 -45
  51. KekikStream/Plugins/DiziMom.py +179 -0
  52. KekikStream/Plugins/DiziPal.py +95 -161
  53. KekikStream/Plugins/DiziYou.py +51 -147
  54. KekikStream/Plugins/Dizilla.py +40 -61
  55. KekikStream/Plugins/FilmBip.py +90 -39
  56. KekikStream/Plugins/FilmEkseni.py +199 -0
  57. KekikStream/Plugins/FilmMakinesi.py +72 -73
  58. KekikStream/Plugins/FilmModu.py +25 -35
  59. KekikStream/Plugins/Filmatek.py +184 -0
  60. KekikStream/Plugins/FilmciBaba.py +155 -0
  61. KekikStream/Plugins/FullHDFilmizlesene.py +16 -37
  62. KekikStream/Plugins/HDFilm.py +243 -0
  63. KekikStream/Plugins/HDFilmCehennemi.py +242 -189
  64. KekikStream/Plugins/JetFilmizle.py +101 -69
  65. KekikStream/Plugins/KultFilmler.py +138 -104
  66. KekikStream/Plugins/RecTV.py +52 -73
  67. KekikStream/Plugins/RoketDizi.py +18 -27
  68. KekikStream/Plugins/SelcukFlix.py +30 -48
  69. KekikStream/Plugins/SetFilmIzle.py +76 -104
  70. KekikStream/Plugins/SezonlukDizi.py +90 -94
  71. KekikStream/Plugins/Sinefy.py +195 -167
  72. KekikStream/Plugins/SinemaCX.py +148 -78
  73. KekikStream/Plugins/Sinezy.py +29 -31
  74. KekikStream/Plugins/SuperFilmGeldi.py +12 -17
  75. KekikStream/Plugins/UgurFilm.py +85 -38
  76. KekikStream/Plugins/Watch32.py +160 -0
  77. KekikStream/Plugins/YabanciDizi.py +176 -211
  78. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/METADATA +1 -1
  79. kekikstream-2.5.3.dist-info/RECORD +99 -0
  80. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/WHEEL +1 -1
  81. KekikStream/Plugins/FullHDFilm.py +0 -249
  82. kekikstream-2.3.9.dist-info/RECORD +0 -84
  83. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/entry_points.txt +0 -0
  84. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/licenses/LICENSE +0 -0
  85. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.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, MovieInfo, ExtractResult, HTMLHelper
4
- import json, urllib.parse
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, MovieInfo, ExtractResult, HTMLHelper
4
+ import json, contextlib, asyncio
5
5
 
6
6
  class Sinefy(PluginBase):
7
7
  name = "Sinefy"
@@ -42,14 +42,14 @@ class Sinefy(PluginBase):
42
42
  else:
43
43
  full_url = f"{url}&page={page}"
44
44
 
45
- resp = await self.httpx.get(full_url)
46
- sel = HTMLHelper(resp.text)
45
+ istek = await self.httpx.get(full_url)
46
+ secici = HTMLHelper(istek.text)
47
47
 
48
48
  results = []
49
- for item in sel.select("div.poster-with-subject, div.dark-segment div.poster-md.poster"):
50
- title = sel.select_text("h2", item)
51
- href = sel.select_attr("a", "href", item)
52
- poster = sel.select_attr("img", "data-srcset", item)
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)
53
53
 
54
54
  if poster:
55
55
  poster = poster.split(",")[0].split(" ")[0]
@@ -59,7 +59,7 @@ class Sinefy(PluginBase):
59
59
  category = category,
60
60
  title = title,
61
61
  url = self.fix_url(href),
62
- poster = self.fix_url(poster) if poster else None
62
+ poster = self.fix_url(poster)
63
63
  ))
64
64
 
65
65
  return results
@@ -69,202 +69,230 @@ class Sinefy(PluginBase):
69
69
  c_key = "ca1d4a53d0f4761a949b85e51e18f096"
70
70
  c_value = "MTc0NzI2OTAwMDU3ZTEwYmZjMDViNWFmOWIwZDViODg0MjU4MjA1ZmYxOThmZTYwMDdjMWQzMzliNzY5NzFlZmViMzRhMGVmNjgwODU3MGIyZA=="
71
71
 
72
- try:
73
- resp = await self.httpx.get(self.main_url)
74
- sel = HTMLHelper(resp.text)
72
+ with contextlib.suppress(Exception):
73
+ istek = await self.httpx.get(self.main_url)
74
+ secici = HTMLHelper(istek.text)
75
75
 
76
- cke = sel.select_attr("input[name='cKey']", "value")
77
- cval = sel.select_attr("input[name='cValue']", "value")
76
+ cke = secici.select_attr("input[name='cKey']", "value")
77
+ cval = secici.select_attr("input[name='cValue']", "value")
78
78
 
79
79
  if cke and cval:
80
80
  c_key = cke
81
81
  c_value = cval
82
82
 
83
- except Exception:
84
- 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
+ )
85
97
 
86
- post_url = f"{self.main_url}/bg/searchcontent"
87
- data = {
88
- "cKey" : c_key,
89
- "cValue" : c_value,
90
- "searchTerm" : query
91
- }
92
-
93
- headers = {
94
- "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0",
95
- "Accept" : "application/json, text/javascript, */*; q=0.01",
96
- "X-Requested-With" : "XMLHttpRequest",
97
- "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8"
98
- }
99
-
100
- response = await self.httpx.post(post_url, data=data, headers=headers)
101
-
102
- try:
98
+ with contextlib.suppress(Exception):
103
99
  # Extract JSON data from response (might contain garbage chars at start)
104
100
  raw = response.text
105
101
  json_start = raw.find('{')
106
102
  if json_start != -1:
107
103
  clean_json = raw[json_start:]
108
- data = json.loads(clean_json)
109
-
104
+ data = json.loads(clean_json)
105
+
110
106
  results = []
111
107
  # Result array is in data['data']['result']
112
108
  res_array = data.get("data", {}).get("result", [])
113
-
109
+
114
110
  if not res_array:
115
111
  # Fallback manual parsing ?
116
112
  pass
117
113
 
118
114
  for item in res_array:
119
- name = item.get("object_name")
120
- slug = item.get("used_slug")
115
+ name = item.get("object_name")
116
+ slug = item.get("used_slug")
121
117
  poster = item.get("object_poster_url")
122
-
118
+
123
119
  if name and slug:
124
120
  if "cdn.ampproject.org" in poster:
125
121
  poster = "https://images.macellan.online/images/movie/poster/180/275/80/" + poster.split("/")[-1]
126
-
122
+
127
123
  results.append(SearchResult(
128
- title=name,
129
- url=self.fix_url(slug),
130
- poster=self.fix_url(poster) if poster else None
124
+ title = name,
125
+ url = self.fix_url(slug),
126
+ poster = self.fix_url(poster)
131
127
  ))
132
128
  return results
133
129
 
134
- except Exception:
135
- pass
136
130
  return []
137
131
 
138
- async def load_item(self, url: str) -> SeriesInfo:
139
- resp = await self.httpx.get(url)
140
- sel = HTMLHelper(resp.text)
132
+ async def load_item(self, url: str) -> SeriesInfo | MovieInfo:
133
+ istek = await self.httpx.get(url)
134
+ secici = HTMLHelper(istek.text)
141
135
 
142
- title = sel.select_text("h1")
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")
143
143
 
144
- poster_info = sel.select_attr("div.ui.items img", "data-srcset")
145
- poster = None
146
- if poster_info:
147
- parts = str(poster_info).split(",")
148
- for p in parts:
149
- if "1x" in p:
150
- poster = p.strip().split(" ")[0]
151
- break
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
152
152
 
153
- description = sel.select_text("p#tv-series-desc")
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
+ }
154
164
 
155
- tags = [a.text(strip=True) for a in sel.select("div.item.categories a") if a.text(strip=True)]
165
+ episodes = []
166
+ for tab in secici.select("div.ui.tab"):
167
+ for link in secici.select("a[href*='bolum']", tab):
168
+ href = link.attrs.get("href")
169
+ if href:
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)))
156
173
 
157
- rating = sel.select_text("span.color-imdb")
174
+ if episodes:
175
+ return SeriesInfo(**common_info, episodes=episodes)
158
176
 
159
- actors = [h5.text(strip=True) for h5 in sel.select("div.content h5") if h5.text(strip=True)]
177
+ return MovieInfo(**common_info)
160
178
 
161
- year = sel.select_text("span.item.year")
162
- if not year and title:
163
- # Try to extract year from title like "Movie Name(2024)"
164
- year_match = sel.regex_first(r"\((\d{4})\)", title)
165
- if year_match:
166
- year = year_match
167
-
168
- episodes = []
169
- episodes_box_list = sel.select("section.episodes-box")
170
-
171
- if episodes_box_list:
172
- episodes_box = episodes_box_list[0]
173
- # Sezon menüsünden sezon linklerini al
174
- season_menu = sel.select("div.ui.vertical.fluid.tabular.menu a.item", episodes_box)
175
-
176
- # Sezon tab içeriklerini al
177
- season_tabs = sel.select("div.ui.tab", episodes_box)
178
-
179
- # Eğer birden fazla sezon varsa, her sezon tab'ından bölümleri çek
180
- if season_tabs:
181
- for idx, season_tab in enumerate(season_tabs):
182
- # Sezon numarasını belirle
183
- current_season_no = idx + 1
184
-
185
- # Menüden sezon numarasını almaya çalış
186
- if idx < len(season_menu):
187
- menu_href = season_menu[idx].attrs.get("href", "")
188
- match = sel.regex_first(r"sezon-(\d+)", menu_href)
189
- if match:
190
- current_season_no = int(match)
191
-
192
- # Bu sezon tab'ından bölüm linklerini çek
193
- ep_links = sel.select("a[href*='bolum']", season_tab)
194
-
195
- seen_urls = set()
196
- for ep_link in ep_links:
197
- href = ep_link.attrs.get("href")
198
- if not href or href in seen_urls:
199
- continue
200
- seen_urls.add(href)
201
-
202
- # Bölüm numarasını URL'den çıkar
203
- ep_no = 0
204
- match_ep = sel.regex_first(r"bolum-(\d+)", href)
205
- if match_ep:
206
- ep_no = int(match_ep)
207
-
208
- # Bölüm başlığını çıkar (önce title attribute, sonra text)
209
- name = ep_link.attrs.get("title", "")
210
- if not name:
211
- name = sel.select_text("div.content div.header", ep_link)
212
- if not name:
213
- name = ep_link.text(strip=True)
214
-
215
- if href and ep_no > 0:
216
- episodes.append(Episode(
217
- season = current_season_no,
218
- episode = ep_no,
219
- title = name.strip() if name else f"{ep_no}. Bölüm",
220
- url = self.fix_url(href)
221
- ))
222
-
223
- if episodes:
224
- return SeriesInfo(
225
- title = title,
226
- url = url,
227
- poster = self.fix_url(poster) if poster else None,
228
- description = description,
229
- rating = rating,
230
- tags = tags,
231
- actors = actors,
232
- year = year,
233
- episodes = episodes
234
- )
235
- else:
236
- return MovieInfo(
237
- title = title,
238
- url = url,
239
- poster = self.fix_url(poster) if poster else None,
240
- description = description,
241
- rating = rating,
242
- tags = tags,
243
- actors = actors,
244
- year = year
245
- )
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
246
185
 
247
- async def load_links(self, url: str) -> list[ExtractResult]:
248
- resp = await self.httpx.get(url)
249
- sel = HTMLHelper(resp.text)
250
-
251
- iframe = sel.select_attr("iframe", "src")
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"]
252
190
 
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
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