KekikStream 1.4.4__py3-none-any.whl → 2.0.2__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 (81) hide show
  1. KekikStream/CLI/pypi_kontrol.py +6 -6
  2. KekikStream/Core/Extractor/ExtractorBase.py +13 -12
  3. KekikStream/Core/Extractor/ExtractorLoader.py +25 -17
  4. KekikStream/Core/Extractor/ExtractorManager.py +53 -9
  5. KekikStream/Core/Extractor/ExtractorModels.py +5 -7
  6. KekikStream/Core/Extractor/YTDLPCache.py +35 -0
  7. KekikStream/Core/Media/MediaHandler.py +52 -31
  8. KekikStream/Core/Media/MediaManager.py +0 -3
  9. KekikStream/Core/Plugin/PluginBase.py +47 -21
  10. KekikStream/Core/Plugin/PluginLoader.py +11 -7
  11. KekikStream/Core/Plugin/PluginModels.py +25 -25
  12. KekikStream/Core/__init__.py +1 -0
  13. KekikStream/Extractors/CloseLoad.py +6 -26
  14. KekikStream/Extractors/ContentX_.py +40 -0
  15. KekikStream/Extractors/DzenRu.py +38 -0
  16. KekikStream/Extractors/ExPlay.py +53 -0
  17. KekikStream/Extractors/FirePlayer.py +60 -0
  18. KekikStream/Extractors/HDPlayerSystem.py +41 -0
  19. KekikStream/Extractors/JetTv.py +45 -0
  20. KekikStream/Extractors/MailRu.py +2 -4
  21. KekikStream/Extractors/MixTiger.py +57 -0
  22. KekikStream/Extractors/MolyStream.py +25 -7
  23. KekikStream/Extractors/Odnoklassniki.py +16 -11
  24. KekikStream/Extractors/{OkRuHTTP.py → Odnoklassniki_.py} +5 -1
  25. KekikStream/Extractors/{HDStreamAble.py → PeaceMakerst_.py} +1 -1
  26. KekikStream/Extractors/PixelDrain.py +0 -1
  27. KekikStream/Extractors/PlayerFilmIzle.py +62 -0
  28. KekikStream/Extractors/RapidVid.py +30 -13
  29. KekikStream/Extractors/RapidVid_.py +7 -0
  30. KekikStream/Extractors/SetPlay.py +57 -0
  31. KekikStream/Extractors/SetPrime.py +45 -0
  32. KekikStream/Extractors/SibNet.py +0 -1
  33. KekikStream/Extractors/TurkeyPlayer.py +34 -0
  34. KekikStream/Extractors/VidHide.py +72 -0
  35. KekikStream/Extractors/VidMoly.py +20 -19
  36. KekikStream/Extractors/{VidMolyMe.py → VidMoly_.py} +1 -1
  37. KekikStream/Extractors/VidMoxy.py +0 -1
  38. KekikStream/Extractors/VidPapi.py +89 -0
  39. KekikStream/Extractors/YTDLP.py +177 -0
  40. KekikStream/Extractors/YildizKisaFilm.py +41 -0
  41. KekikStream/Plugins/DiziBox.py +28 -16
  42. KekikStream/Plugins/DiziPal.py +246 -0
  43. KekikStream/Plugins/DiziYou.py +58 -31
  44. KekikStream/Plugins/Dizilla.py +97 -68
  45. KekikStream/Plugins/FilmBip.py +145 -0
  46. KekikStream/Plugins/FilmMakinesi.py +61 -52
  47. KekikStream/Plugins/FilmModu.py +138 -0
  48. KekikStream/Plugins/FullHDFilm.py +164 -0
  49. KekikStream/Plugins/FullHDFilmizlesene.py +38 -37
  50. KekikStream/Plugins/HDFilmCehennemi.py +44 -54
  51. KekikStream/Plugins/JetFilmizle.py +68 -42
  52. KekikStream/Plugins/KultFilmler.py +219 -0
  53. KekikStream/Plugins/RecTV.py +41 -37
  54. KekikStream/Plugins/RoketDizi.py +232 -0
  55. KekikStream/Plugins/SelcukFlix.py +309 -0
  56. KekikStream/Plugins/SezonlukDizi.py +16 -14
  57. KekikStream/Plugins/SineWix.py +39 -30
  58. KekikStream/Plugins/Sinefy.py +238 -0
  59. KekikStream/Plugins/SinemaCX.py +157 -0
  60. KekikStream/Plugins/Sinezy.py +146 -0
  61. KekikStream/Plugins/SuperFilmGeldi.py +121 -0
  62. KekikStream/Plugins/UgurFilm.py +10 -10
  63. KekikStream/__init__.py +296 -319
  64. KekikStream/requirements.txt +3 -4
  65. kekikstream-2.0.2.dist-info/METADATA +309 -0
  66. kekikstream-2.0.2.dist-info/RECORD +82 -0
  67. {kekikstream-1.4.4.dist-info → kekikstream-2.0.2.dist-info}/WHEEL +1 -1
  68. KekikStream/Extractors/FourCX.py +0 -7
  69. KekikStream/Extractors/FourPichive.py +0 -7
  70. KekikStream/Extractors/FourPlayRu.py +0 -7
  71. KekikStream/Extractors/Hotlinger.py +0 -7
  72. KekikStream/Extractors/OkRuSSL.py +0 -7
  73. KekikStream/Extractors/Pichive.py +0 -7
  74. KekikStream/Extractors/PlayRu.py +0 -7
  75. KekikStream/Helpers/Unpack.py +0 -75
  76. KekikStream/Plugins/Shorten.py +0 -225
  77. kekikstream-1.4.4.dist-info/METADATA +0 -108
  78. kekikstream-1.4.4.dist-info/RECORD +0 -63
  79. {kekikstream-1.4.4.dist-info → kekikstream-2.0.2.dist-info}/entry_points.txt +0 -0
  80. {kekikstream-1.4.4.dist-info → kekikstream-2.0.2.dist-info/licenses}/LICENSE +0 -0
  81. {kekikstream-1.4.4.dist-info → kekikstream-2.0.2.dist-info}/top_level.txt +0 -0
@@ -1,16 +1,18 @@
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, MovieInfo, ExtractResult, Subtitle
4
- from parsel import Selector
5
- from KekikStream.Helpers.Unpack import unpack
6
- import random, string, re, base64
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, Subtitle
4
+ from parsel import Selector
5
+ from Kekik.Sifreleme import Packer, StreamDecoder
6
+ import random, string, re
7
7
 
8
8
  class HDFilmCehennemi(PluginBase):
9
9
  name = "HDFilmCehennemi"
10
10
  language = "tr"
11
- main_url = "https://www.hdfilmcehennemi.nl"
11
+ main_url = "https://www.hdfilmcehennemi.ws"
12
12
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
13
13
  description = "Türkiye'nin en hızlı hd film izleme sitesi"
14
+
15
+
14
16
 
15
17
  main_page = {
16
18
  f"{main_url}" : "Yeni Eklenen Filmler",
@@ -29,9 +31,8 @@ class HDFilmCehennemi(PluginBase):
29
31
  f"{main_url}/tur/romantik-filmleri-izle-1" : "Romantik Filmleri"
30
32
  }
31
33
 
32
- @kekik_cache(ttl=60*60)
33
34
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
34
- istek = await self.httpx.get(f"{url}")
35
+ istek = await self.httpx.get(f"{url}", follow_redirects=True)
35
36
  secici = Selector(istek.text)
36
37
 
37
38
  return [
@@ -44,10 +45,9 @@ class HDFilmCehennemi(PluginBase):
44
45
  for veri in secici.css("div.section-content a.poster")
45
46
  ]
46
47
 
47
- @kekik_cache(ttl=60*60)
48
48
  async def search(self, query: str) -> list[SearchResult]:
49
49
  istek = await self.httpx.get(
50
- url = f"{self.main_url}/search?q={query}",
50
+ url = f"{self.main_url}/search/?q={query}",
51
51
  headers = {
52
52
  "Referer" : f"{self.main_url}/",
53
53
  "X-Requested-With" : "fetch",
@@ -73,7 +73,6 @@ class HDFilmCehennemi(PluginBase):
73
73
 
74
74
  return results
75
75
 
76
- @kekik_cache(ttl=60*60)
77
76
  async def load_item(self, url: str) -> MovieInfo:
78
77
  istek = await self.httpx.get(url, headers = {"Referer": f"{self.main_url}/"})
79
78
  secici = Selector(istek.text)
@@ -96,7 +95,7 @@ class HDFilmCehennemi(PluginBase):
96
95
  return MovieInfo(
97
96
  url = url,
98
97
  poster = self.fix_url(poster),
99
- title = title,
98
+ title = self.clean_title(title),
100
99
  description = description,
101
100
  tags = tags,
102
101
  rating = rating,
@@ -108,8 +107,9 @@ class HDFilmCehennemi(PluginBase):
108
107
  def generate_random_cookie(self):
109
108
  return "".join(random.choices(string.ascii_letters + string.digits, k=16))
110
109
 
111
- @kekik_cache(ttl=15*60)
112
- async def cehennempass(self, video_id: str):
110
+ async def cehennempass(self, video_id: str) -> list[dict]:
111
+ results = []
112
+
113
113
  istek = await self.httpx.post(
114
114
  url = "https://cehennempass.pw/process_quality_selection.php",
115
115
  headers = {
@@ -120,15 +120,12 @@ class HDFilmCehennemi(PluginBase):
120
120
  },
121
121
  data = {"video_id": video_id, "selected_quality": "low"},
122
122
  )
123
-
124
- video_url = istek.json().get("download_link")
125
-
126
- self._data[self.fix_url(video_url)] = {
127
- "ext_name" : f"{self.name} | Düşük Kalite",
128
- "name" : "Düşük Kalite",
129
- "referer" : f"https://cehennempass.pw/download/{video_id}",
130
- "subtitles" : []
131
- }
123
+ if video_url := istek.json().get("download_link"):
124
+ results.append({
125
+ "url" : self.fix_url(video_url),
126
+ "name" : "Düşük Kalite",
127
+ "referer" : f"https://cehennempass.pw/download/{video_id}"
128
+ })
132
129
 
133
130
  istek = await self.httpx.post(
134
131
  url = "https://cehennempass.pw/process_quality_selection.php",
@@ -140,19 +137,15 @@ class HDFilmCehennemi(PluginBase):
140
137
  },
141
138
  data = {"video_id": video_id, "selected_quality": "high"},
142
139
  )
140
+ if video_url := istek.json().get("download_link"):
141
+ results.append({
142
+ "url" : self.fix_url(video_url),
143
+ "name" : "Yüksek Kalite",
144
+ "referer" : f"https://cehennempass.pw/download/{video_id}"
145
+ })
143
146
 
144
- video_url = istek.json().get("download_link")
145
-
146
- self._data[self.fix_url(video_url)] = {
147
- "ext_name" : f"{self.name} | Yüksek Kalite",
148
- "name" : "Yüksek Kalite",
149
- "referer" : f"https://cehennempass.pw/download/{video_id}",
150
- "subtitles" : []
151
- }
152
-
153
- return None
147
+ return results
154
148
 
155
- @kekik_cache(ttl=15*60)
156
149
  async def invoke_local_source(self, iframe: str, source: str, url: str):
157
150
  self.httpx.headers.update({"Referer": f"{self.main_url}/"})
158
151
  istek = await self.httpx.get(iframe)
@@ -160,12 +153,10 @@ class HDFilmCehennemi(PluginBase):
160
153
  try:
161
154
  eval_func = re.compile(r'\s*(eval\(function[\s\S].*)\s*').findall(istek.text)[0]
162
155
  except Exception:
163
- await self.cehennempass(iframe.split("/")[-1])
164
- return None, None
156
+ return await self.cehennempass(iframe.split("/")[-1])
165
157
 
166
- unpacked = unpack(eval_func)
167
- b64_url = re.search(r'file_link=\"(.*)\"\;', unpacked)[1]
168
- video_url = base64.b64decode(b64_url).decode("utf-8")
158
+ unpacked = Packer.unpack(eval_func)
159
+ video_url = StreamDecoder.extract_stream_url(unpacked)
169
160
 
170
161
  subtitles = []
171
162
  try:
@@ -178,20 +169,18 @@ class HDFilmCehennemi(PluginBase):
178
169
  except Exception:
179
170
  pass
180
171
 
181
- data = {
182
- "ext_name" : f"{self.name} | {source}",
183
- "name" : f"{self.name} | {source}",
172
+ return [{
173
+ "url" : video_url,
174
+ "name" : source,
184
175
  "referer" : url,
185
176
  "subtitles" : subtitles
186
- }
187
-
188
- return video_url, data
177
+ }]
189
178
 
190
- @kekik_cache(ttl=15*60)
191
- async def load_links(self, url: str) -> list[str]:
179
+ async def load_links(self, url: str) -> list[dict]:
192
180
  istek = await self.httpx.get(url)
193
181
  secici = Selector(istek.text)
194
182
 
183
+ results = []
195
184
  for alternatif in secici.css("div.alternative-links"):
196
185
  lang_code = alternatif.css("::attr(data-lang)").get().upper()
197
186
 
@@ -208,23 +197,24 @@ class HDFilmCehennemi(PluginBase):
208
197
  },
209
198
  )
210
199
 
211
- match = re.search(r'data-src=\\"([^"]+)', api_get.text)
200
+ match = re.search(r'data-src=\\\"([^"]+)', api_get.text)
212
201
  iframe = match[1].replace("\\", "") if match else None
213
202
 
214
203
  if iframe and "?rapidrame_id=" in iframe:
215
204
  iframe = f"{self.main_url}/playerr/{iframe.split('?rapidrame_id=')[1]}"
216
205
 
217
- video_url, data = await self.invoke_local_source(iframe, source, url)
218
- if not video_url:
206
+ video_data_list = await self.invoke_local_source(iframe, source, url)
207
+ if not video_data_list:
219
208
  continue
220
209
 
221
- self._data[video_url] = data
210
+ for video_data in video_data_list:
211
+ results.append(video_data)
222
212
 
223
- return list(self._data.keys())
213
+ return results
224
214
 
225
- async def play(self, name: str, url: str, referer: str, subtitles: list[Subtitle]):
226
- extract_result = ExtractResult(name=name, url=url, referer=referer, subtitles=subtitles)
227
- self.media_handler.title = name
215
+ async def play(self, **kwargs):
216
+ extract_result = ExtractResult(**kwargs)
217
+ self.media_handler.title = kwargs.get("name")
228
218
  if self.name not in self.media_handler.title:
229
219
  self.media_handler.title = f"{self.name} | {self.media_handler.title}"
230
220
 
@@ -1,12 +1,12 @@
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, MovieInfo
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo
4
4
  from parsel import Selector
5
5
 
6
6
  class JetFilmizle(PluginBase):
7
7
  name = "JetFilmizle"
8
8
  language = "tr"
9
- main_url = "https://jetfilmizle.io"
9
+ main_url = "https://jetfilmizle.website"
10
10
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
11
11
  description = "Binlerce Film İzleme Seçeneğiyle En İyi Film İzleme Sitesi"
12
12
 
@@ -19,9 +19,8 @@ class JetFilmizle(PluginBase):
19
19
  f"{main_url}/kategoriler/yesilcam-filmleri-izlee/page/" : "Yeşilçam Filmleri"
20
20
  }
21
21
 
22
- @kekik_cache(ttl=60*60)
23
22
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
24
- istek = await self.httpx.get(f"{url}{page}")
23
+ istek = await self.httpx.get(f"{url}{page}", follow_redirects=True)
25
24
  secici = Selector(istek.text)
26
25
 
27
26
  return [
@@ -31,10 +30,9 @@ class JetFilmizle(PluginBase):
31
30
  url = self.fix_url(veri.css("a::attr(href)").get()),
32
31
  poster = self.fix_url(veri.css("img::attr(data-src)").get() or veri.css("img::attr(src)").get()),
33
32
  )
34
- for veri in secici.css("article.movie")
33
+ for veri in secici.css("article.movie") if veri.css("h2 a::text, h3 a::text, h4 a::text, h5 a::text, h6 a::text").get()
35
34
  ]
36
35
 
37
- @kekik_cache(ttl=60*60)
38
36
  async def search(self, query: str) -> list[SearchResult]:
39
37
  istek = await self.httpx.post(
40
38
  url = f"{self.main_url}/filmara.php",
@@ -60,18 +58,31 @@ class JetFilmizle(PluginBase):
60
58
 
61
59
  return results
62
60
 
63
- @kekik_cache(ttl=60*60)
64
61
  async def load_item(self, url: str) -> MovieInfo:
65
62
  istek = await self.httpx.get(url)
66
63
  secici = Selector(istek.text)
67
64
 
68
65
  title = self.clean_title(secici.css("div.movie-exp-title::text").get())
69
- poster = secici.css("section.movie-exp img::attr(data-src), section.movie-exp img::attr(src)").get().strip()
70
- description = secici.css("section.movie-exp p.aciklama::text").get().strip()
66
+ poster_raw = secici.css("section.movie-exp img::attr(data-src), section.movie-exp img::attr(src)").get()
67
+ poster = poster_raw.strip() if poster_raw else None
68
+
69
+ desc_raw = secici.css("section.movie-exp p.aciklama::text").get()
70
+ description = desc_raw.strip() if desc_raw else None
71
+
71
72
  tags = secici.css("section.movie-exp div.catss a::text").getall()
72
- rating = secici.css("section.movie-exp div.imdb_puan span::text").get().strip()
73
- year = secici.xpath("//div[@class='yap' and (contains(., 'Vizyon') or contains(., 'Yapım'))]/text()").get()
74
- year = year.strip() if year else None
73
+
74
+ rating_raw = secici.css("section.movie-exp div.imdb_puan span::text").get()
75
+ rating = rating_raw.strip() if rating_raw else None
76
+
77
+
78
+ # Year - div.yap içinde 4 haneli sayı ara
79
+ year_div = secici.xpath("//div[@class='yap' and (contains(., 'Vizyon') or contains(., 'Yapım'))]/text()").get()
80
+ year = None
81
+ if year_div:
82
+ year_match = re.search(r'(\d{4})', year_div.strip())
83
+ if year_match:
84
+ year = year_match.group(1)
85
+
75
86
  actors = secici.css("div[itemprop='actor'] a span::text").getall()
76
87
 
77
88
  return MovieInfo(
@@ -85,39 +96,54 @@ class JetFilmizle(PluginBase):
85
96
  actors = actors
86
97
  )
87
98
 
88
- @kekik_cache(ttl=15*60)
89
- async def load_links(self, url: str) -> list[str]:
99
+ async def load_links(self, url: str) -> list[dict]:
90
100
  istek = await self.httpx.get(url)
91
101
  secici = Selector(istek.text)
92
102
 
93
- iframes = []
94
- if main_iframe := secici.css("div#movie iframe::attr(data-src), div#movie iframe::attr(data), div#movie iframe::attr(src)").get():
95
- iframes.append(self.fix_url(main_iframe))
103
+ results = []
96
104
 
97
- for part in secici.css("div.film_part a"):
98
- part_href = part.attrib.get("href")
99
- if not part_href:
105
+ # 1) Ana iframe'leri kontrol et
106
+ for iframe in secici.css("iframe"):
107
+ src = (iframe.css("::attr(src)").get() or
108
+ iframe.css("::attr(data-src)").get() or
109
+ iframe.css("::attr(data-lazy-src)").get())
110
+
111
+ if src and src != "about:blank":
112
+ iframe_url = self.fix_url(src)
113
+ extractor = self.ex_manager.find_extractor(iframe_url)
114
+ results.append({
115
+ "url": iframe_url,
116
+ "name": extractor.name if extractor else "Ana Player"
117
+ })
118
+
119
+ # 2) Sayfa numaralarından linkleri topla (Fragman hariç)
120
+ page_links = []
121
+ for link in secici.css("a.post-page-numbers"):
122
+ isim = link.css("span::text").get() or ""
123
+ if isim != "Fragman":
124
+ href = link.css("::attr(href)").get()
125
+ if href:
126
+ page_links.append((self.fix_url(href), isim))
127
+
128
+ # 3) Her sayfa linkindeki iframe'leri bul
129
+ for page_url, isim in page_links:
130
+ try:
131
+ page_resp = await self.httpx.get(page_url)
132
+ page_sel = Selector(page_resp.text)
133
+
134
+ for iframe in page_sel.css("div#movie iframe"):
135
+ src = (iframe.css("::attr(src)").get() or
136
+ iframe.css("::attr(data-src)").get() or
137
+ iframe.css("::attr(data-lazy-src)").get())
138
+
139
+ if src and src != "about:blank":
140
+ iframe_url = self.fix_url(src)
141
+ extractor = self.ex_manager.find_extractor(iframe_url)
142
+ results.append({
143
+ "url": iframe_url,
144
+ "name": f"{extractor.name if extractor else 'Player'} | {isim}"
145
+ })
146
+ except Exception:
100
147
  continue
101
148
 
102
- part_istek = await self.httpx.get(part_href)
103
- part_secici = Selector(part_istek.text)
104
-
105
- if iframe := part_secici.css("div#movie iframe::attr(data-src), div#movie iframe::attr(data), div#movie iframe::attr(src)").get():
106
- iframes.append(self.fix_url(iframe))
107
- else:
108
- for link in part_secici.css("div#movie p a"):
109
- if download_link := link.attrib.get("href"):
110
- iframes.append(self.fix_url(download_link))
111
-
112
- processed_iframes = []
113
- for iframe in iframes:
114
- if "jetv.xyz" in iframe:
115
- jetv_istek = await self.httpx.get(iframe)
116
- jetv_secici = Selector(jetv_istek.text)
117
-
118
- if jetv_iframe := jetv_secici.css("iframe::attr(src)").get():
119
- processed_iframes.append(self.fix_url(jetv_iframe))
120
- else:
121
- processed_iframes.append(iframe)
122
-
123
- return processed_iframes
149
+ return results
@@ -0,0 +1,219 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, Subtitle
4
+ from parsel import Selector
5
+ import re, base64
6
+
7
+ class KultFilmler(PluginBase):
8
+ name = "KultFilmler"
9
+ language = "tr"
10
+ main_url = "https://kultfilmler.net"
11
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
+ description = "Kült film ve dizi izleme sitesi."
13
+
14
+ main_page = {
15
+ f"{main_url}/category/aile-filmleri-izle" : "Aile",
16
+ f"{main_url}/category/aksiyon-filmleri-izle" : "Aksiyon",
17
+ f"{main_url}/category/animasyon-filmleri-izle" : "Animasyon",
18
+ f"{main_url}/category/belgesel-izle" : "Belgesel",
19
+ f"{main_url}/category/bilim-kurgu-filmleri-izle": "Bilim Kurgu",
20
+ f"{main_url}/category/biyografi-filmleri-izle" : "Biyografi",
21
+ f"{main_url}/category/dram-filmleri-izle" : "Dram",
22
+ f"{main_url}/category/fantastik-filmleri-izle" : "Fantastik",
23
+ f"{main_url}/category/gerilim-filmleri-izle" : "Gerilim",
24
+ f"{main_url}/category/gizem-filmleri-izle" : "Gizem",
25
+ f"{main_url}/category/kara-filmleri-izle" : "Kara Film",
26
+ f"{main_url}/category/kisa-film-izle" : "Kısa Metraj",
27
+ f"{main_url}/category/komedi-filmleri-izle" : "Komedi",
28
+ f"{main_url}/category/korku-filmleri-izle" : "Korku",
29
+ f"{main_url}/category/macera-filmleri-izle" : "Macera",
30
+ f"{main_url}/category/muzik-filmleri-izle" : "Müzik",
31
+ f"{main_url}/category/polisiye-filmleri-izle" : "Polisiye",
32
+ f"{main_url}/category/romantik-filmleri-izle" : "Romantik",
33
+ f"{main_url}/category/savas-filmleri-izle" : "Savaş",
34
+ f"{main_url}/category/suc-filmleri-izle" : "Suç",
35
+ f"{main_url}/category/tarih-filmleri-izle" : "Tarih",
36
+ f"{main_url}/category/yerli-filmleri-izle" : "Yerli",
37
+ }
38
+
39
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
40
+ istek = await self.httpx.get(url)
41
+ secici = Selector(istek.text)
42
+
43
+ results = []
44
+ for veri in secici.css("div.col-md-12 div.movie-box"):
45
+ title = veri.css("div.img img::attr(alt)").get()
46
+ href = self.fix_url(veri.css("a::attr(href)").get())
47
+ poster = self.fix_url(veri.css("div.img img::attr(src)").get())
48
+
49
+ if title and href:
50
+ results.append(MainPageResult(
51
+ category = category,
52
+ title = title,
53
+ url = href,
54
+ poster = poster,
55
+ ))
56
+
57
+ return results
58
+
59
+ async def search(self, query: str) -> list[SearchResult]:
60
+ istek = await self.httpx.get(f"{self.main_url}?s={query}")
61
+ secici = Selector(istek.text)
62
+
63
+ results = []
64
+ for veri in secici.css("div.movie-box"):
65
+ title = veri.css("div.img img::attr(alt)").get()
66
+ href = self.fix_url(veri.css("a::attr(href)").get())
67
+ poster = self.fix_url(veri.css("div.img img::attr(src)").get())
68
+
69
+ if title and href:
70
+ results.append(SearchResult(
71
+ title = title,
72
+ url = href,
73
+ poster = poster,
74
+ ))
75
+
76
+ return results
77
+
78
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
79
+ istek = await self.httpx.get(url)
80
+ secici = Selector(istek.text)
81
+
82
+ title = secici.css("div.film-bilgileri img::attr(alt)").get() or secici.css("[property='og:title']::attr(content)").get()
83
+ poster = self.fix_url(secici.css("[property='og:image']::attr(content)").get())
84
+ description = secici.css("div.description::text").get()
85
+ tags = secici.css("ul.post-categories a::text").getall()
86
+ # HTML analizine göre güncellenen alanlar
87
+ year = secici.css("li.release span a::text").get()
88
+ duration = secici.css("li.time span::text").re_first(r"(\d+)")
89
+ rating = secici.css("div.imdb-count::text").get()
90
+ actors = secici.css("div.actors a::text").getall()
91
+ if rating:
92
+ rating = rating.strip()
93
+
94
+ # Dizi mi kontrol et
95
+ if "/dizi/" in url:
96
+ episodes = []
97
+ for bolum in secici.css("div.episode-box"):
98
+ ep_href = self.fix_url(bolum.css("div.name a::attr(href)").get())
99
+ ssn_detail = bolum.css("span.episodetitle::text").get() or ""
100
+ ep_detail = bolum.css("span.episodetitle b::text").get() or ""
101
+ ep_name = f"{ssn_detail} - {ep_detail}"
102
+
103
+ if ep_href:
104
+ ep_season = re.search(r"(\d+)\.", ssn_detail)
105
+ ep_episode = re.search(r"(\d+)\.", ep_detail)
106
+
107
+ episodes.append(Episode(
108
+ season = int(ep_season[1]) if ep_season else 1,
109
+ episode = int(ep_episode[1]) if ep_episode else 1,
110
+ title = ep_name.strip(" -"),
111
+ url = ep_href,
112
+ ))
113
+
114
+ return SeriesInfo(
115
+ url = url,
116
+ poster = poster,
117
+ title = self.clean_title(title) if title else "",
118
+ description = description,
119
+ tags = tags,
120
+ year = year,
121
+ actors = actors,
122
+ rating = rating,
123
+ episodes = episodes,
124
+ )
125
+
126
+ return MovieInfo(
127
+ url = url,
128
+ poster = poster,
129
+ title = self.clean_title(title) if title else "",
130
+ description = description,
131
+ tags = tags,
132
+ year = year,
133
+ rating = rating,
134
+ actors = actors,
135
+ duration = int(duration) if duration else None,
136
+ )
137
+
138
+ def _get_iframe(self, source_code: str) -> str:
139
+ """Base64 kodlu iframe'i çözümle"""
140
+ atob_match = re.search(r"PHA\+[0-9a-zA-Z+/=]*", source_code)
141
+ if not atob_match:
142
+ return ""
143
+
144
+ atob = atob_match.group()
145
+
146
+ # Padding düzelt
147
+ padding = 4 - len(atob) % 4
148
+ if padding < 4:
149
+ atob = atob + "=" * padding
150
+
151
+ try:
152
+ decoded = base64.b64decode(atob).decode("utf-8")
153
+ secici = Selector(text=decoded)
154
+ return self.fix_url(secici.css("iframe::attr(src)").get()) or ""
155
+ except Exception:
156
+ return ""
157
+
158
+ def _extract_subtitle_url(self, source_code: str) -> str | None:
159
+ """Altyazı URL'sini çıkar"""
160
+ match = re.search(r"(https?://[^\s\"]+\.srt)", source_code)
161
+ return match[1] if match else None
162
+
163
+ async def load_links(self, url: str) -> list[dict]:
164
+ istek = await self.httpx.get(url)
165
+ secici = Selector(istek.text)
166
+
167
+ iframes = set()
168
+
169
+ # Ana iframe
170
+ main_frame = self._get_iframe(istek.text)
171
+ if main_frame:
172
+ iframes.add(main_frame)
173
+
174
+ # Alternatif player'lar
175
+ for player in secici.css("div.container#player"):
176
+ alt_iframe = self.fix_url(player.css("iframe::attr(src)").get())
177
+ if alt_iframe:
178
+ alt_istek = await self.httpx.get(alt_iframe)
179
+ alt_frame = self._get_iframe(alt_istek.text)
180
+ if alt_frame:
181
+ iframes.add(alt_frame)
182
+
183
+ results = []
184
+
185
+ for iframe in iframes:
186
+ subtitles = []
187
+
188
+ # VidMoly özel işleme
189
+ if "vidmoly" in iframe:
190
+ headers = {
191
+ "User-Agent" : "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36",
192
+ "Sec-Fetch-Dest" : "iframe"
193
+ }
194
+ iframe_istek = await self.httpx.get(iframe, headers=headers)
195
+ m3u_match = re.search(r'file:"([^"]+)"', iframe_istek.text)
196
+
197
+ if m3u_match:
198
+ results.append({
199
+ "name" : "VidMoly",
200
+ "url" : m3u_match[1],
201
+ "referer" : self.main_url,
202
+ "subtitles" : []
203
+ })
204
+ continue
205
+
206
+ # Altyazı çıkar
207
+ subtitle_url = self._extract_subtitle_url(url)
208
+ if subtitle_url:
209
+ subtitles.append(Subtitle(name="Türkçe", url=subtitle_url))
210
+
211
+ extractor = self.ex_manager.find_extractor(iframe)
212
+ results.append({
213
+ "name" : extractor.name if extractor else "Player",
214
+ "url" : iframe,
215
+ "referer" : f"{self.main_url}/",
216
+ "subtitles" : subtitles
217
+ })
218
+
219
+ return results