KekikStream 1.7.1__py3-none-any.whl → 2.2.0__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 (88) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +20 -9
  2. KekikStream/Core/Extractor/ExtractorLoader.py +25 -17
  3. KekikStream/Core/Extractor/ExtractorManager.py +53 -9
  4. KekikStream/Core/Extractor/ExtractorModels.py +5 -7
  5. KekikStream/Core/Extractor/YTDLPCache.py +35 -0
  6. KekikStream/Core/Media/MediaHandler.py +44 -26
  7. KekikStream/Core/Media/MediaManager.py +0 -3
  8. KekikStream/Core/Plugin/PluginBase.py +82 -22
  9. KekikStream/Core/Plugin/PluginLoader.py +11 -7
  10. KekikStream/Core/Plugin/PluginModels.py +25 -26
  11. KekikStream/Core/__init__.py +1 -0
  12. KekikStream/Extractors/CloseLoad.py +21 -7
  13. KekikStream/Extractors/ContentX.py +21 -6
  14. KekikStream/Extractors/DonilasPlay.py +86 -0
  15. KekikStream/Extractors/DzenRu.py +38 -0
  16. KekikStream/Extractors/ExPlay.py +53 -0
  17. KekikStream/Extractors/Filemoon.py +78 -0
  18. KekikStream/Extractors/HDPlayerSystem.py +41 -0
  19. KekikStream/Extractors/JetTv.py +45 -0
  20. KekikStream/Extractors/MailRu.py +3 -4
  21. KekikStream/Extractors/MixPlayHD.py +2 -3
  22. KekikStream/Extractors/MixTiger.py +57 -0
  23. KekikStream/Extractors/MolyStream.py +5 -5
  24. KekikStream/Extractors/Odnoklassniki.py +13 -7
  25. KekikStream/Extractors/PeaceMakerst.py +10 -5
  26. KekikStream/Extractors/PixelDrain.py +1 -2
  27. KekikStream/Extractors/PlayerFilmIzle.py +65 -0
  28. KekikStream/Extractors/RapidVid.py +23 -8
  29. KekikStream/Extractors/SetPlay.py +66 -0
  30. KekikStream/Extractors/SetPrime.py +45 -0
  31. KekikStream/Extractors/SibNet.py +2 -3
  32. KekikStream/Extractors/Sobreatsesuyp.py +4 -5
  33. KekikStream/Extractors/TRsTX.py +4 -5
  34. KekikStream/Extractors/TauVideo.py +2 -3
  35. KekikStream/Extractors/TurboImgz.py +2 -3
  36. KekikStream/Extractors/TurkeyPlayer.py +34 -0
  37. KekikStream/Extractors/VCTPlay.py +41 -0
  38. KekikStream/Extractors/VidHide.py +81 -0
  39. KekikStream/Extractors/VidMoly.py +55 -34
  40. KekikStream/Extractors/VidMoxy.py +2 -3
  41. KekikStream/Extractors/VidPapi.py +89 -0
  42. KekikStream/Extractors/VideoSeyred.py +3 -4
  43. KekikStream/Extractors/YTDLP.py +211 -0
  44. KekikStream/Extractors/YildizKisaFilm.py +41 -0
  45. KekikStream/Plugins/BelgeselX.py +196 -0
  46. KekikStream/Plugins/DiziBox.py +25 -34
  47. KekikStream/Plugins/DiziPal.py +24 -35
  48. KekikStream/Plugins/DiziYou.py +54 -37
  49. KekikStream/Plugins/Dizilla.py +66 -46
  50. KekikStream/Plugins/FilmBip.py +142 -0
  51. KekikStream/Plugins/FilmMakinesi.py +36 -28
  52. KekikStream/Plugins/FilmModu.py +20 -24
  53. KekikStream/Plugins/FullHDFilm.py +220 -0
  54. KekikStream/Plugins/FullHDFilmizlesene.py +9 -15
  55. KekikStream/Plugins/HDFilmCehennemi.py +141 -69
  56. KekikStream/Plugins/JetFilmizle.py +85 -52
  57. KekikStream/Plugins/KultFilmler.py +217 -0
  58. KekikStream/Plugins/RecTV.py +22 -34
  59. KekikStream/Plugins/RoketDizi.py +222 -0
  60. KekikStream/Plugins/SelcukFlix.py +328 -0
  61. KekikStream/Plugins/SetFilmIzle.py +252 -0
  62. KekikStream/Plugins/SezonlukDizi.py +54 -21
  63. KekikStream/Plugins/SineWix.py +17 -29
  64. KekikStream/Plugins/Sinefy.py +241 -0
  65. KekikStream/Plugins/SinemaCX.py +154 -0
  66. KekikStream/Plugins/Sinezy.py +143 -0
  67. KekikStream/Plugins/SuperFilmGeldi.py +130 -0
  68. KekikStream/Plugins/UgurFilm.py +13 -19
  69. KekikStream/__init__.py +47 -56
  70. KekikStream/requirements.txt +3 -4
  71. kekikstream-2.2.0.dist-info/METADATA +312 -0
  72. kekikstream-2.2.0.dist-info/RECORD +81 -0
  73. KekikStream/Extractors/FourCX.py +0 -7
  74. KekikStream/Extractors/FourPichive.py +0 -7
  75. KekikStream/Extractors/FourPlayRu.py +0 -7
  76. KekikStream/Extractors/HDStreamAble.py +0 -7
  77. KekikStream/Extractors/Hotlinger.py +0 -7
  78. KekikStream/Extractors/OkRuHTTP.py +0 -7
  79. KekikStream/Extractors/OkRuSSL.py +0 -7
  80. KekikStream/Extractors/Pichive.py +0 -7
  81. KekikStream/Extractors/PlayRu.py +0 -7
  82. KekikStream/Extractors/VidMolyMe.py +0 -7
  83. kekikstream-1.7.1.dist-info/METADATA +0 -109
  84. kekikstream-1.7.1.dist-info/RECORD +0 -63
  85. {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/WHEEL +0 -0
  86. {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/entry_points.txt +0 -0
  87. {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/licenses/LICENSE +0 -0
  88. {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,196 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
4
+ from parsel import Selector
5
+ import re
6
+
7
+ class BelgeselX(PluginBase):
8
+ name = "BelgeselX"
9
+ language = "tr"
10
+ main_url = "https://belgeselx.com"
11
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
+ description = "2022 yılında son çıkan belgeselleri belgeselx.com'da izle. En yeni belgeseller, türkçe altyazılı yada dublaj olarak 1080p kalitesinde hd belgesel izle."
13
+
14
+ main_page = {
15
+ f"{main_url}/konu/turk-tarihi-belgeselleri&page=" : "Türk Tarihi",
16
+ f"{main_url}/konu/tarih-belgeselleri&page=" : "Tarih",
17
+ f"{main_url}/konu/seyehat-belgeselleri&page=" : "Seyahat",
18
+ f"{main_url}/konu/seri-belgeseller&page=" : "Seri",
19
+ f"{main_url}/konu/savas-belgeselleri&page=" : "Savaş",
20
+ f"{main_url}/konu/sanat-belgeselleri&page=" : "Sanat",
21
+ f"{main_url}/konu/psikoloji-belgeselleri&page=" : "Psikoloji",
22
+ f"{main_url}/konu/polisiye-belgeselleri&page=" : "Polisiye",
23
+ f"{main_url}/konu/otomobil-belgeselleri&page=" : "Otomobil",
24
+ f"{main_url}/konu/nazi-belgeselleri&page=" : "Nazi",
25
+ f"{main_url}/konu/muhendislik-belgeselleri&page=" : "Mühendislik",
26
+ f"{main_url}/konu/kultur-din-belgeselleri&page=" : "Kültür Din",
27
+ f"{main_url}/konu/kozmik-belgeseller&page=" : "Kozmik",
28
+ f"{main_url}/konu/hayvan-belgeselleri&page=" : "Hayvan",
29
+ f"{main_url}/konu/eski-tarih-belgeselleri&page=" : "Eski Tarih",
30
+ f"{main_url}/konu/egitim-belgeselleri&page=" : "Eğitim",
31
+ f"{main_url}/konu/dunya-belgeselleri&page=" : "Dünya",
32
+ f"{main_url}/konu/doga-belgeselleri&page=" : "Doğa",
33
+ f"{main_url}/konu/bilim-belgeselleri&page=" : "Bilim"
34
+ }
35
+
36
+ @staticmethod
37
+ def _to_title_case(text: str) -> str:
38
+ """Türkçe için title case dönüşümü."""
39
+ return " ".join(
40
+ word.lower().replace("i", "İ").capitalize() if word.lower().startswith("i") else word.capitalize()
41
+ for word in text.split()
42
+ )
43
+
44
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
45
+ istek = self.cloudscraper.get(f"{url}{page}")
46
+ secici = Selector(istek.text)
47
+
48
+ results = []
49
+ for item in secici.css("div.gen-movie-contain > div.gen-info-contain > div.gen-movie-info"):
50
+ title = item.css("div.gen-movie-info > h3 a::text").get()
51
+ href = item.css("div.gen-movie-info > h3 a::attr(href)").get()
52
+ parent = item.xpath("../../..")
53
+ poster = parent.css("div.gen-movie-img > img::attr(src)").get()
54
+
55
+ if title and href:
56
+ results.append(MainPageResult(
57
+ category = category,
58
+ title = self._to_title_case(title.strip()),
59
+ url = self.fix_url(href),
60
+ poster = self.fix_url(poster) if poster else None
61
+ ))
62
+
63
+ return results
64
+
65
+ async def search(self, query: str) -> list[SearchResult]:
66
+ # Google Custom Search API kullanıyor
67
+ cx = "016376594590146270301:iwmy65ijgrm"
68
+
69
+ token_resp = self.cloudscraper.get(f"https://cse.google.com/cse.js?cx={cx}")
70
+ token_text = token_resp.text
71
+
72
+ cse_lib_match = re.search(r'cselibVersion": "(.*)"', token_text)
73
+ cse_tok_match = re.search(r'cse_token": "(.*)"', token_text)
74
+
75
+ if not cse_lib_match or not cse_tok_match:
76
+ return []
77
+
78
+ cse_lib = cse_lib_match.group(1)
79
+ cse_tok = cse_tok_match.group(1)
80
+
81
+ search_url = (
82
+ f"https://cse.google.com/cse/element/v1?"
83
+ f"rsz=filtered_cse&num=100&hl=tr&source=gcsc&cselibv={cse_lib}&cx={cx}"
84
+ f"&q={query}&safe=off&cse_tok={cse_tok}&sort=&exp=cc%2Capo&oq={query}"
85
+ f"&callback=google.search.cse.api9969&rurl=https%3A%2F%2Fbelgeselx.com%2F"
86
+ )
87
+
88
+ resp = self.cloudscraper.get(search_url)
89
+ resp_text = resp.text
90
+
91
+ titles = re.findall(r'"titleNoFormatting": "(.*?)"', resp_text)
92
+ urls = re.findall(r'"url": "(.*?)"', resp_text)
93
+ images = re.findall(r'"ogImage": "(.*?)"', resp_text)
94
+
95
+ results = []
96
+ for i, title in enumerate(titles):
97
+ url_val = urls[i] if i < len(urls) else None
98
+ poster = images[i] if i < len(images) else None
99
+
100
+ if not url_val or "diziresimleri" not in url_val:
101
+ # URL'den belgesel linkini oluştur
102
+ if poster and "diziresimleri" in poster:
103
+ file_name = poster.rsplit("/", 1)[-1]
104
+ file_name = re.sub(r"\.(jpe?g|png|webp)$", "", file_name)
105
+ url_val = f"{self.main_url}/belgeseldizi/{file_name}"
106
+ else:
107
+ continue
108
+
109
+ clean_title = title.split("İzle")[0].strip()
110
+ results.append(SearchResult(
111
+ title = self._to_title_case(clean_title),
112
+ url = url_val,
113
+ poster = poster
114
+ ))
115
+
116
+ return results
117
+
118
+ async def load_item(self, url: str) -> SeriesInfo:
119
+ istek = await self.httpx.get(url)
120
+ secici = Selector(istek.text)
121
+
122
+ title = secici.css("h2.gen-title::text").get()
123
+ poster = secici.css("div.gen-tv-show-top img::attr(src)").get()
124
+ description = secici.css("div.gen-single-tv-show-info p::text").get()
125
+
126
+ tags = []
127
+ for tag_link in secici.css("div.gen-socail-share a[href*='belgeselkanali']"):
128
+ tag_href = tag_link.css("::attr(href)").get()
129
+ if tag_href:
130
+ tag_name = tag_href.rsplit("/", 1)[-1].replace("-", " ")
131
+ tags.append(self._to_title_case(tag_name))
132
+
133
+ episodes = []
134
+ counter = 0
135
+ for ep_item in secici.css("div.gen-movie-contain"):
136
+ ep_name = ep_item.css("div.gen-movie-info h3 a::text").get()
137
+ ep_href = ep_item.css("div.gen-movie-info h3 a::attr(href)").get()
138
+
139
+ if not ep_name or not ep_href:
140
+ continue
141
+
142
+ season_text = ep_item.css("div.gen-single-meta-holder ul li::text").get() or ""
143
+ episode_match = re.search(r"Bölüm (\d+)", season_text)
144
+ season_match = re.search(r"Sezon (\d+)", season_text)
145
+
146
+ ep_episode = int(episode_match.group(1)) if episode_match else counter
147
+ ep_season = int(season_match.group(1)) if season_match else 1
148
+
149
+ counter += 1
150
+
151
+ episodes.append(Episode(
152
+ season = ep_season,
153
+ episode = ep_episode,
154
+ title = ep_name.strip(),
155
+ url = self.fix_url(ep_href)
156
+ ))
157
+
158
+ return SeriesInfo(
159
+ url = url,
160
+ poster = self.fix_url(poster) if poster else None,
161
+ title = self._to_title_case(title.strip()) if title else None,
162
+ description = description.strip() if description else None,
163
+ tags = tags,
164
+ episodes = episodes
165
+ )
166
+
167
+ async def load_links(self, url: str) -> list[ExtractResult]:
168
+ istek = await self.httpx.get(url)
169
+ text = istek.text
170
+
171
+ # fnc_addWatch div'inden data-episode ID'sini al
172
+ ep_match = re.search(r'<div[^>]*class=["\'][^"\']*fnc_addWatch[^"\']*["\'][^>]*data-episode=["\'](\d+)["\']', text)
173
+ if not ep_match:
174
+ return []
175
+
176
+ episode_id = ep_match.group(1)
177
+ iframe_url = f"{self.main_url}/video/data/new4.php?id={episode_id}"
178
+
179
+ iframe_resp = await self.httpx.get(iframe_url, headers={"Referer": url})
180
+ iframe_text = iframe_resp.text
181
+
182
+ links = []
183
+ for match in re.finditer(r'file:"([^"]+)", label: "([^"]+)"', iframe_text):
184
+ video_url = match.group(1)
185
+ quality = match.group(2)
186
+
187
+ source_name = "Google" if quality == "FULL" else self.name
188
+ quality_str = "1080p" if quality == "FULL" else quality
189
+
190
+ links.append(ExtractResult(
191
+ url = video_url,
192
+ name = f"{source_name} | {quality_str}",
193
+ referer = url
194
+ ))
195
+
196
+ return links
@@ -1,6 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import kekik_cache, PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
4
4
  from Kekik.Sifreleme import CryptoJS
5
5
  from parsel import Selector
6
6
  import re, urllib.parse, base64, contextlib, asyncio, time
@@ -40,15 +40,14 @@ class DiziBox(PluginBase):
40
40
  f"{main_url}/dizi-arsivi/page/SAYFA/?tur[0]=yarisma&yil&imdb" : "Yarışma"
41
41
  }
42
42
 
43
- #@kekik_cache(ttl=60*60)
44
43
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
45
- self.cffi.cookies.update({
44
+ self.httpx.cookies.update({
46
45
  "isTrustedUser" : "true",
47
46
  "dbxu" : str(time.time() * 1000).split(".")[0]
48
47
  })
49
- istek = await self.cffi.get(
48
+ istek = await self.httpx.get(
50
49
  url = f"{url.replace('SAYFA', str(page))}",
51
- allow_redirects = True
50
+ follow_redirects = True
52
51
  )
53
52
  secici = Selector(istek.text)
54
53
 
@@ -62,13 +61,12 @@ class DiziBox(PluginBase):
62
61
  for veri in secici.css("article.detailed-article")
63
62
  ]
64
63
 
65
- #@kekik_cache(ttl=60*60)
66
64
  async def search(self, query: str) -> list[SearchResult]:
67
- self.cffi.cookies.update({
65
+ self.httpx.cookies.update({
68
66
  "isTrustedUser" : "true",
69
67
  "dbxu" : str(time.time() * 1000).split(".")[0]
70
68
  })
71
- istek = await self.cffi.get(f"{self.main_url}/?s={query}")
69
+ istek = await self.httpx.get(f"{self.main_url}/?s={query}")
72
70
  secici = Selector(istek.text)
73
71
 
74
72
  return [
@@ -80,9 +78,8 @@ class DiziBox(PluginBase):
80
78
  for item in secici.css("article.detailed-article")
81
79
  ]
82
80
 
83
- #@kekik_cache(ttl=60*60)
84
81
  async def load_item(self, url: str) -> SeriesInfo:
85
- istek = await self.cffi.get(url)
82
+ istek = await self.httpx.get(url)
86
83
  secici = Selector(istek.text)
87
84
 
88
85
  title = secici.css("div.tv-overview h1 a::text").get()
@@ -96,7 +93,7 @@ class DiziBox(PluginBase):
96
93
  episodes = []
97
94
  for sezon_link in secici.css("div#seasons-list a::attr(href)").getall():
98
95
  sezon_url = self.fix_url(sezon_link)
99
- sezon_istek = await self.cffi.get(sezon_url)
96
+ sezon_istek = await self.httpx.get(sezon_url)
100
97
  sezon_secici = Selector(sezon_istek.text)
101
98
 
102
99
  for bolum in sezon_secici.css("article.grid-box"):
@@ -127,12 +124,11 @@ class DiziBox(PluginBase):
127
124
  actors = actors,
128
125
  )
129
126
 
130
- #@kekik_cache(ttl=60*60)
131
127
  async def _iframe_decode(self, name:str, iframe_link:str, referer:str) -> list[str]:
132
128
  results = []
133
129
 
134
- self.cffi.headers.update({"Referer": referer})
135
- self.cffi.cookies.update({
130
+ self.httpx.headers.update({"Referer": referer})
131
+ self.httpx.cookies.update({
136
132
  "isTrustedUser" : "true",
137
133
  "dbxu" : str(time.time() * 1000).split(".")[0]
138
134
  })
@@ -140,12 +136,12 @@ class DiziBox(PluginBase):
140
136
  if "/player/king/king.php" in iframe_link:
141
137
  iframe_link = iframe_link.replace("king.php?v=", "king.php?wmode=opaque&v=")
142
138
 
143
- istek = await self.cffi.get(iframe_link)
139
+ istek = await self.httpx.get(iframe_link)
144
140
  secici = Selector(istek.text)
145
141
  iframe = secici.css("div#Player iframe::attr(src)").get()
146
142
 
147
- self.cffi.headers.update({"Referer": self.main_url})
148
- istek = await self.cffi.get(iframe)
143
+ self.httpx.headers.update({"Referer": self.main_url})
144
+ istek = await self.httpx.get(iframe)
149
145
 
150
146
  crypt_data = re.search(r"CryptoJS\.AES\.decrypt\(\"(.*)\",\"", istek.text)[1]
151
147
  crypt_pass = re.search(r"\",\"(.*)\"\);", istek.text)[1]
@@ -161,7 +157,7 @@ class DiziBox(PluginBase):
161
157
  while True:
162
158
  await asyncio.sleep(.3)
163
159
  with contextlib.suppress(Exception):
164
- istek = await self.cffi.get(iframe_link)
160
+ istek = await self.httpx.get(iframe_link)
165
161
 
166
162
  if atob_data := re.search(r"unescape\(\"(.*)\"\)", istek.text):
167
163
  decoded_atob = urllib.parse.unquote(atob_data[1])
@@ -178,20 +174,17 @@ class DiziBox(PluginBase):
178
174
 
179
175
  return results
180
176
 
181
- #@kekik_cache(ttl=15*60)
182
- async def load_links(self, url: str) -> list[dict]:
183
- istek = await self.cffi.get(url)
177
+ async def load_links(self, url: str) -> list[ExtractResult]:
178
+ istek = await self.httpx.get(url)
184
179
  secici = Selector(istek.text)
185
180
 
186
181
  results = []
187
182
  if main_iframe := secici.css("div#video-area iframe::attr(src)").get():
188
183
  if decoded := await self._iframe_decode(self.name, main_iframe, url):
189
184
  for iframe in decoded:
190
- extractor = self.ex_manager.find_extractor(iframe)
191
- results.append({
192
- "url" : iframe,
193
- "name" : f"{extractor.name if extractor else 'Main Player'}"
194
- })
185
+ data = await self.extract(iframe)
186
+ if data:
187
+ results.append(data)
195
188
 
196
189
  for alternatif in secici.css("div.video-toolbar option[value]"):
197
190
  alt_name = alternatif.css("::text").get()
@@ -200,18 +193,16 @@ class DiziBox(PluginBase):
200
193
  if not alt_link:
201
194
  continue
202
195
 
203
- self.cffi.headers.update({"Referer": url})
204
- alt_istek = await self.cffi.get(alt_link)
196
+ self.httpx.headers.update({"Referer": url})
197
+ alt_istek = await self.httpx.get(alt_link)
205
198
  alt_istek.raise_for_status()
206
199
 
207
200
  alt_secici = Selector(alt_istek.text)
208
201
  if alt_iframe := alt_secici.css("div#video-area iframe::attr(src)").get():
209
202
  if decoded := await self._iframe_decode(alt_name, alt_iframe, url):
210
203
  for iframe in decoded:
211
- extractor = self.ex_manager.find_extractor(iframe)
212
- results.append({
213
- "url" : iframe,
214
- "name" : f"{extractor.name if extractor else alt_name}"
215
- })
204
+ data = await self.extract(iframe, prefix=alt_name)
205
+ if data:
206
+ results.append(data)
216
207
 
217
- return results
208
+ return results
@@ -1,15 +1,15 @@
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, Subtitle
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, Subtitle, ExtractResult
4
4
  from parsel import Selector
5
5
  import re
6
6
 
7
7
  class DiziPal(PluginBase):
8
8
  name = "DiziPal"
9
9
  language = "tr"
10
- main_url = "https://dizipal1222.com"
10
+ main_url = "https://dizipal1223.com"
11
11
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
- description = "Yabancı Dizi ve Film izle."
12
+ description = "dizipal güncel, dizipal yeni ve gerçek adresi. dizipal en yeni dizi ve filmleri güvenli ve hızlı şekilde sunar."
13
13
 
14
14
  main_page = {
15
15
  f"{main_url}/diziler/son-bolumler" : "Son Bölümler",
@@ -26,7 +26,7 @@ class DiziPal(PluginBase):
26
26
  }
27
27
 
28
28
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
29
- istek = await self.cffi.get(url)
29
+ istek = await self.httpx.get(url)
30
30
  secici = Selector(istek.text)
31
31
 
32
32
  results = []
@@ -67,12 +67,12 @@ class DiziPal(PluginBase):
67
67
  return results
68
68
 
69
69
  async def search(self, query: str) -> list[SearchResult]:
70
- self.cffi.headers.update({
70
+ self.httpx.headers.update({
71
71
  "Accept" : "application/json, text/javascript, */*; q=0.01",
72
72
  "X-Requested-With" : "XMLHttpRequest"
73
73
  })
74
74
 
75
- istek = await self.cffi.post(
75
+ istek = await self.httpx.post(
76
76
  url = f"{self.main_url}/api/search-autocomplete",
77
77
  data = {"query": query}
78
78
  )
@@ -106,12 +106,12 @@ class DiziPal(PluginBase):
106
106
 
107
107
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
108
108
  # Reset headers to get HTML response
109
- self.cffi.headers.update({
109
+ self.httpx.headers.update({
110
110
  "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
111
111
  })
112
- self.cffi.headers.pop("X-Requested-With", None)
112
+ self.httpx.headers.pop("X-Requested-With", None)
113
113
 
114
- istek = await self.cffi.get(url)
114
+ istek = await self.httpx.get(url)
115
115
  secici = Selector(text=istek.text, type="html")
116
116
 
117
117
  poster = self.fix_url(secici.css("meta[property='og:image']::attr(content)").get())
@@ -177,14 +177,14 @@ class DiziPal(PluginBase):
177
177
  duration = duration,
178
178
  )
179
179
 
180
- async def load_links(self, url: str) -> list[dict]:
180
+ async def load_links(self, url: str) -> list[ExtractResult]:
181
181
  # Reset headers to get HTML response
182
- self.cffi.headers.update({
182
+ self.httpx.headers.update({
183
183
  "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
184
184
  })
185
- self.cffi.headers.pop("X-Requested-With", None)
185
+ self.httpx.headers.pop("X-Requested-With", None)
186
186
 
187
- istek = await self.cffi.get(url)
187
+ istek = await self.httpx.get(url)
188
188
  secici = Selector(istek.text)
189
189
 
190
190
  iframe = secici.css(".series-player-container iframe::attr(src)").get()
@@ -196,8 +196,8 @@ class DiziPal(PluginBase):
196
196
 
197
197
  results = []
198
198
 
199
- self.cffi.headers.update({"Referer": f"{self.main_url}/"})
200
- i_istek = await self.cffi.get(iframe)
199
+ self.httpx.headers.update({"Referer": f"{self.main_url}/"})
200
+ i_istek = await self.httpx.get(iframe)
201
201
  i_text = i_istek.text
202
202
 
203
203
  # m3u link çıkar
@@ -220,27 +220,16 @@ class DiziPal(PluginBase):
220
220
  sub_url = sub_text.replace(f"[{lang}]", "")
221
221
  subtitles.append(Subtitle(name=lang, url=self.fix_url(sub_url)))
222
222
 
223
- results.append({
224
- "name" : self.name,
225
- "url" : m3u_link,
226
- "referer" : f"{self.main_url}/",
227
- "subtitles" : subtitles
228
- })
223
+ results.append(ExtractResult(
224
+ name = self.name,
225
+ url = m3u_link,
226
+ referer = f"{self.main_url}/",
227
+ subtitles = subtitles
228
+ ))
229
229
  else:
230
230
  # Extractor'a yönlendir
231
- extractor = self.ex_manager.find_extractor(iframe)
232
- results.append({
233
- "name" : f"{extractor.name if extractor else self.name}",
234
- "url" : iframe,
235
- "referer" : f"{self.main_url}/",
236
- })
231
+ data = await self.extract(iframe)
232
+ if data:
233
+ results.append(data)
237
234
 
238
235
  return results
239
-
240
- async def play(self, name: str, url: str, referer: str, subtitles: list[Subtitle]):
241
- extract_result = ExtractResult(name=name, url=url, referer=referer, subtitles=subtitles)
242
- self.media_handler.title = name
243
- if self.name not in self.media_handler.title:
244
- self.media_handler.title = f"{self.name} | {self.media_handler.title}"
245
-
246
- self.media_handler.play_media(extract_result)
@@ -1,6 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import kekik_cache, PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, Subtitle, ExtractResult
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, Subtitle, ExtractResult
4
4
  from parsel import Selector
5
5
  import re
6
6
 
@@ -29,9 +29,8 @@ class DiziYou(PluginBase):
29
29
  f"{main_url}/dizi-arsivi/page/SAYFA/?tur=Vah%C5%9Fi+Bat%C4%B1" : "Vahşi Batı"
30
30
  }
31
31
 
32
- #@kekik_cache(ttl=60*60)
33
32
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
34
- istek = await self.cffi.get(f"{url.replace('SAYFA', str(page))}")
33
+ istek = await self.httpx.get(f"{url.replace('SAYFA', str(page))}")
35
34
  secici = Selector(istek.text)
36
35
 
37
36
  return [
@@ -44,27 +43,36 @@ class DiziYou(PluginBase):
44
43
  for veri in secici.css("div.single-item")
45
44
  ]
46
45
 
47
- #@kekik_cache(ttl=60*60)
48
46
  async def search(self, query: str) -> list[SearchResult]:
49
- istek = await self.cffi.get(f"{self.main_url}/?s={query}")
47
+ istek = await self.httpx.get(f"{self.main_url}/?s={query}")
50
48
  secici = Selector(istek.text)
51
49
 
52
50
  return [
53
51
  SearchResult(
54
52
  title = afis.css("div#categorytitle a::text").get().strip(),
55
53
  url = self.fix_url(afis.css("div#categorytitle a::attr(href)").get()),
56
- poster = self.fix_url(afis.css("img::attr(src)").get()),
54
+ poster = self.fix_url(afis.css("img::attr(src)").get() or afis.css("img::attr(data-src)").get())
57
55
  )
58
56
  for afis in secici.css("div.incontent div#list-series")
59
57
  ]
60
58
 
61
- #@kekik_cache(ttl=60*60)
62
59
  async def load_item(self, url: str) -> SeriesInfo:
63
- istek = await self.cffi.get(url)
60
+ istek = await self.httpx.get(url)
64
61
  secici = Selector(istek.text)
65
62
 
66
- title = secici.css("h1::text").get().strip()
67
- poster = self.fix_url(secici.css("div.category_image img::attr(src)").get().strip())
63
+ # Title - div.title h1 içinde
64
+ title_raw = secici.css("div.title h1::text").get()
65
+ title = title_raw.strip() if title_raw else ""
66
+
67
+ # Fallback: Eğer title boşsa URL'den çıkar (telif kısıtlaması olan sayfalar için)
68
+ if not title:
69
+ # URL'den slug'ı al: https://www.diziyou.one/jasmine/ -> jasmine -> Jasmine
70
+ slug = url.rstrip('/').split('/')[-1]
71
+ title = slug.replace('-', ' ').title()
72
+
73
+ # Poster
74
+ poster_raw = secici.css("div.category_image img::attr(src)").get()
75
+ poster = self.fix_url(poster_raw) if poster_raw else ""
68
76
  year = secici.xpath("//span[contains(., 'Yapım Yılı')]/following-sibling::text()[1]").get()
69
77
  description = secici.css("div.diziyou_desc::text").get()
70
78
  if description:
@@ -75,13 +83,21 @@ class DiziYou(PluginBase):
75
83
  actors = [actor.strip() for actor in _actors.split(",")] if _actors else []
76
84
 
77
85
  episodes = []
78
- for it in secici.css("div.bolumust"):
79
- ep_name = it.css("div.baslik::text").get().strip()
80
- ep_href = it.xpath("ancestor::a/@href").get()
81
- if not ep_name or not ep_href:
86
+ # Episodes - bolumust her bölüm için bir <a> içinde
87
+ # :has() parsel'de çalışmıyor, XPath kullanıyoruz
88
+ for link in secici.xpath('//a[div[@class="bolumust"]]'):
89
+ ep_name_raw = link.css("div.baslik::text").get()
90
+ if not ep_name_raw:
91
+ continue
92
+ ep_name = ep_name_raw.strip()
93
+
94
+ ep_href = self.fix_url(link.css("::attr(href)").get())
95
+ if not ep_href:
82
96
  continue
83
97
 
84
- ep_name_clean = it.css("div.bolumismi::text").get().strip().replace("(", "").replace(")", "").strip() if it.css("div.bolumismi::text").get() else ep_name
98
+ # Bölüm ismi varsa al
99
+ ep_name_raw_clean = link.css("div.bolumismi::text").get()
100
+ ep_name_clean = ep_name_raw_clean.strip().replace("(", "").replace(")", "").strip() if ep_name_raw_clean else ep_name
85
101
 
86
102
  ep_episode = re.search(r"(\d+)\. Bölüm", ep_name)[1]
87
103
  ep_season = re.search(r"(\d+)\. Sezon", ep_name)[1]
@@ -107,14 +123,23 @@ class DiziYou(PluginBase):
107
123
  actors = actors
108
124
  )
109
125
 
110
- #@kekik_cache(ttl=15*60)
111
- async def load_links(self, url: str) -> list[dict]:
112
- istek = await self.cffi.get(url)
126
+ async def load_links(self, url: str) -> list[ExtractResult]:
127
+ istek = await self.httpx.get(url)
113
128
  secici = Selector(istek.text)
114
129
 
115
- item_title = secici.css("div.title h1::text").get()
116
- ep_name = secici.css("div#bolum-ismi::text").get().strip()
117
- item_id = secici.css("iframe#diziyouPlayer::attr(src)").get().split("/")[-1].replace(".html", "")
130
+ # Title ve episode name - None kontrolü ekle
131
+ item_title_raw = secici.css("div.title h1::text").get()
132
+ item_title = item_title_raw.strip() if item_title_raw else ""
133
+
134
+ ep_name_raw = secici.css("div#bolum-ismi::text").get()
135
+ ep_name = ep_name_raw.strip() if ep_name_raw else ""
136
+
137
+ # Player src'den item_id çıkar
138
+ player_src = secici.css("iframe#diziyouPlayer::attr(src)").get()
139
+ if not player_src:
140
+ return [] # Player bulunamadıysa boş liste döndür
141
+
142
+ item_id = player_src.split("/")[-1].replace(".html", "")
118
143
 
119
144
  subtitles = []
120
145
  stream_urls = []
@@ -154,19 +179,11 @@ class DiziYou(PluginBase):
154
179
 
155
180
  results = []
156
181
  for stream in stream_urls:
157
- results.append({
158
- "url" : stream.get("url"),
159
- "name" : f"{stream.get('dil')}",
160
- "referer" : url,
161
- "subtitles" : subtitles
162
- })
163
-
164
- return results
165
-
166
- async def play(self, name: str, url: str, referer: str, subtitles: list[Subtitle]):
167
- extract_result = ExtractResult(name=name, url=url, referer=referer, subtitles=subtitles)
168
- self.media_handler.title = name
169
- if self.name not in self.media_handler.title:
170
- self.media_handler.title = f"{self.name} | {self.media_handler.title}"
171
-
172
- self.media_handler.play_media(extract_result)
182
+ results.append(ExtractResult(
183
+ url = stream.get("url"),
184
+ name = f"{stream.get('dil')}",
185
+ referer = url,
186
+ subtitles = subtitles
187
+ ))
188
+
189
+ return results