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,13 +1,13 @@
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
 
7
7
  class DiziYou(PluginBase):
8
8
  name = "DiziYou"
9
9
  language = "tr"
10
- main_url = "https://www.diziyou2.com"
10
+ main_url = "https://www.diziyou.one"
11
11
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
12
  description = "Diziyou en kaliteli Türkçe dublaj ve altyazılı yabancı dizi izleme sitesidir. Güncel ve efsanevi dizileri 1080p Full HD kalitede izlemek için hemen tıkla!"
13
13
 
@@ -29,7 +29,6 @@ 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
33
  istek = await self.httpx.get(f"{url.replace('SAYFA', str(page))}")
35
34
  secici = Selector(istek.text)
@@ -44,7 +43,6 @@ 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
47
  istek = await self.httpx.get(f"{self.main_url}/?s={query}")
50
48
  secici = Selector(istek.text)
@@ -53,33 +51,53 @@ class DiziYou(PluginBase):
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
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
- description = secici.css("div.diziyou_desc::text").get().strip()
77
+ description = secici.css("div.diziyou_desc::text").get()
78
+ if description:
79
+ description = description.strip()
70
80
  tags = secici.css("div.genres a::text").getall()
71
81
  rating = secici.xpath("//span[contains(., 'IMDB')]/following-sibling::text()[1]").get()
72
82
  _actors = secici.xpath("//span[contains(., 'Oyuncular')]/following-sibling::text()[1]").get()
73
83
  actors = [actor.strip() for actor in _actors.split(",")] if _actors else []
74
84
 
75
85
  episodes = []
76
- for it in secici.css("div.bolumust"):
77
- ep_name = it.css("div.baslik::text").get().strip()
78
- ep_href = it.xpath("ancestor::a/@href").get()
79
- 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:
80
96
  continue
81
97
 
82
- 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
83
101
 
84
102
  ep_episode = re.search(r"(\d+)\. Bölüm", ep_name)[1]
85
103
  ep_season = re.search(r"(\d+)\. Sezon", ep_name)[1]
@@ -105,14 +123,23 @@ class DiziYou(PluginBase):
105
123
  actors = actors
106
124
  )
107
125
 
108
- @kekik_cache(ttl=15*60)
109
- async def load_links(self, url: str) -> list[str]:
126
+ async def load_links(self, url: str) -> list[dict]:
110
127
  istek = await self.httpx.get(url)
111
128
  secici = Selector(istek.text)
112
129
 
113
- item_title = secici.css("div.title h1::text").get()
114
- ep_name = secici.css("div#bolum-ismi::text").get().strip()
115
- 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", "")
116
143
 
117
144
  subtitles = []
118
145
  stream_urls = []
@@ -128,7 +155,7 @@ class DiziYou(PluginBase):
128
155
  url = self.fix_url(f"{self.main_url.replace('www', 'storage')}/subtitles/{item_id}/tr.vtt"),
129
156
  ))
130
157
  veri = {
131
- "dil": "Orjinal Dil",
158
+ "dil": "Orjinal Dil (TR Altyazı)",
132
159
  "url": f"{self.main_url.replace('www', 'storage')}/episodes/{item_id}/play.m3u8"
133
160
  }
134
161
  if veri not in stream_urls:
@@ -139,31 +166,31 @@ class DiziYou(PluginBase):
139
166
  url = self.fix_url(f"{self.main_url.replace('www', 'storage')}/subtitles/{item_id}/en.vtt"),
140
167
  ))
141
168
  veri = {
142
- "dil": "Orjinal Dil",
169
+ "dil": "Orjinal Dil (EN Altyazı)",
143
170
  "url": f"{self.main_url.replace('www', 'storage')}/episodes/{item_id}/play.m3u8"
144
171
  }
145
172
  if veri not in stream_urls:
146
173
  stream_urls.append(veri)
147
174
  case "turkceDublaj":
148
175
  stream_urls.append({
149
- "dil": "Dublaj",
176
+ "dil": "Türkçe Dublaj",
150
177
  "url": f"{self.main_url.replace('www', 'storage')}/episodes/{item_id}_tr/play.m3u8"
151
178
  })
152
179
 
153
-
180
+ results = []
154
181
  for stream in stream_urls:
155
- self._data[stream.get("url")] = {
156
- "ext_name" : f"{self.name} | {stream.get('dil')}",
157
- "name" : f"{self.name} | {stream.get('dil')} | {item_title} - {ep_name}",
182
+ results.append({
183
+ "url" : stream.get("url"),
184
+ "name" : f"{stream.get('dil')}",
158
185
  "referer" : url,
159
186
  "subtitles" : subtitles
160
- }
187
+ })
161
188
 
162
- return [stream.get("url") for stream in stream_urls]
189
+ return results
163
190
 
164
- async def play(self, name: str, url: str, referer: str, subtitles: list[Subtitle]):
165
- extract_result = ExtractResult(name=name, url=url, referer=referer, subtitles=subtitles)
166
- self.media_handler.title = name
191
+ async def play(self, **kwargs):
192
+ extract_result = ExtractResult(**kwargs)
193
+ self.media_handler.title = kwargs.get("name")
167
194
  if self.name not in self.media_handler.title:
168
195
  self.media_handler.title = f"{self.name} | {self.media_handler.title}"
169
196
 
@@ -1,58 +1,71 @@
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
4
4
  from parsel import Selector
5
5
  from json import loads
6
6
  from urllib.parse import urlparse, urlunparse
7
+ from Crypto.Cipher import AES
8
+ from base64 import b64decode
7
9
 
8
10
  class Dizilla(PluginBase):
9
11
  name = "Dizilla"
10
12
  language = "tr"
11
- main_url = "https://dizilla.club"
13
+ main_url = "https://dizilla40.com"
12
14
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
13
- description = "Dizilla tüm yabancı dizileri ücretsiz olarak Türkçe Dublaj ve altyazılı seçenekleri ile 1080P kalite izleyebileceğiniz yeni nesil yabancı dizi izleme siteniz."
15
+ description = "1080p yabancı dizi izle. Türkçe altyazılı veya dublaj seçenekleriyle 1080p çözünürlükte yabancı dizilere anında ulaş. Popüler dizileri kesintisiz izle."
14
16
 
15
17
  main_page = {
16
- f"{main_url}/tum-bolumler" : "Altyazılı Bölümler",
17
- f"{main_url}/dublaj-bolumler" : "Dublaj Bölümler",
18
- f"{main_url}/dizi-turu/aile" : "Aile",
19
- f"{main_url}/dizi-turu/aksiyon" : "Aksiyon",
20
- f"{main_url}/dizi-turu/bilim-kurgu" : "Bilim Kurgu",
21
- f"{main_url}/dizi-turu/romantik" : "Romantik",
22
- f"{main_url}/dizi-turu/komedi" : "Komedi"
18
+ f"{main_url}/tum-bolumler" : "Altyazılı Bölümler",
19
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=15&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Aile",
20
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=9&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Aksiyon",
21
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=17&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Animasyon",
22
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=5&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Bilim Kurgu",
23
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=2&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Dram",
24
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=12&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Fantastik",
25
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=18&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Gerilim",
26
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=3&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Gizem",
27
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=4&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Komedi",
28
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=8&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Korku",
29
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=24&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Macera",
30
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=7&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Romantik",
31
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=26&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Savaş",
32
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=1&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Suç",
33
+ f"{main_url}/api/bg/findSeries?releaseYearStart=1900&releaseYearEnd=2050&imdbPointMin=0&imdbPointMax=10&categoryIdsComma=11&countryIdsComma=&orderType=date_desc&languageId=-1&currentPage=SAYFA&currentPageCount=24&queryStr=&categorySlugsComma=&countryCodesComma=" : "Western",
23
34
  }
24
35
 
25
- @kekik_cache(ttl=60*60)
26
36
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
27
- istek = await self.httpx.get(url)
28
- secici = Selector(istek.text)
29
-
30
37
  ana_sayfa = []
31
38
 
32
- if "dizi-turu" in url:
39
+ if "api/bg" in url:
40
+ istek = await self.httpx.post(url.replace("SAYFA", str(page)))
41
+ decrypted = await self.decrypt_response(istek.json().get("response"))
42
+ veriler = decrypted.get("result", [])
33
43
  ana_sayfa.extend([
34
44
  MainPageResult(
35
45
  category = category,
36
- title = veri.css("h2::text").get(),
37
- url = self.fix_url(veri.css("::attr(href)").get()),
38
- poster = self.fix_url(veri.css("img::attr(src)").get() or veri.css("img::attr(data-src)").get())
46
+ title = veri.get("original_title"),
47
+ url = self.fix_url(f"{self.main_url}/{veri.get('used_slug')}"),
48
+ poster = self.fix_url(veri.get("object_poster_url")),
39
49
  )
40
- for veri in secici.css("div.grid-cols-3 a")
50
+ for veri in veriler
41
51
  ])
42
52
  else:
43
- for veri in secici.css("div.grid a"):
53
+ istek = await self.httpx.get(url.replace("SAYFA", str(page)))
54
+ secici = Selector(istek.text)
55
+
56
+ for veri in secici.css("div.tab-content > div.grid a"):
44
57
  name = veri.css("h2::text").get()
45
- ep_name = veri.css("div.opacity-80::text").get()
58
+ ep_name = veri.xpath("normalize-space(//div[contains(@class, 'opacity-80')])").get()
46
59
  if not ep_name:
47
60
  continue
48
61
 
49
62
  ep_name = ep_name.replace(". Sezon", "x").replace(". Bölüm", "").replace("x ", "x")
50
63
  title = f"{name} - {ep_name}"
51
64
 
52
- ep_req = await self.httpx.get(veri.css("::attr(href)").get())
65
+ ep_req = await self.httpx.get(self.fix_url(veri.css("::attr(href)").get()))
53
66
  ep_secici = Selector(ep_req.text)
54
- href = self.fix_url(ep_secici.css("a.relative::attr(href)").get())
55
- poster = self.fix_url(ep_secici.css("img.imgt::attr(onerror)").get().split("= '")[-1].split("';")[0])
67
+ href = self.fix_url(ep_secici.css("nav li:nth-of-type(3) a::attr(href)").get())
68
+ poster = self.fix_url(ep_secici.css("img.imgt::attr(src)").get())
56
69
 
57
70
  ana_sayfa.append(
58
71
  MainPageResult(
@@ -65,32 +78,31 @@ class Dizilla(PluginBase):
65
78
 
66
79
  return ana_sayfa
67
80
 
68
- @kekik_cache(ttl=60*60)
81
+ async def decrypt_response(self, response: str) -> dict:
82
+ # 32 bytes key
83
+ key = "9bYMCNQiWsXIYFWYAu7EkdsSbmGBTyUI".encode("utf-8")
84
+
85
+ # IV = 16 bytes of zero
86
+ iv = bytes([0] * 16)
87
+
88
+ # Base64 decode
89
+ encrypted_bytes = b64decode(response)
90
+
91
+ # AES/CBC/PKCS5Padding
92
+ cipher = AES.new(key, AES.MODE_CBC, iv)
93
+ decrypted = cipher.decrypt(encrypted_bytes)
94
+
95
+ # PKCS5/PKCS7 padding remove
96
+ pad_len = decrypted[-1]
97
+ decrypted = decrypted[:-pad_len]
98
+
99
+ # JSON decode
100
+ return loads(decrypted.decode("utf-8"))
101
+
69
102
  async def search(self, query: str) -> list[SearchResult]:
70
- ilk_istek = await self.httpx.get(self.main_url)
71
- ilk_secici = Selector(ilk_istek.text)
72
- cKey = ilk_secici.css("input[name='cKey']::attr(value)").get()
73
- cValue = ilk_secici.css("input[name='cValue']::attr(value)").get()
74
-
75
- self.httpx.headers.update({
76
- "Accept" : "application/json, text/javascript, */*; q=0.01",
77
- "X-Requested-With" : "XMLHttpRequest",
78
- "Referer" : f"{self.main_url}/"
79
- })
80
- self.httpx.cookies.update({
81
- "showAllDaFull" : "true",
82
- "PHPSESSID" : ilk_istek.cookies.get("PHPSESSID"),
83
- })
84
-
85
- arama_istek = await self.httpx.post(
86
- url = f"{self.main_url}/bg/searchcontent",
87
- data = {
88
- "cKey" : cKey,
89
- "cValue" : cValue,
90
- "searchterm" : query
91
- }
92
- )
93
- arama_veri = arama_istek.json().get("data", {}).get("result", [])
103
+ arama_istek = await self.httpx.post(f"{self.main_url}/api/bg/searchcontent?searchterm={query}")
104
+ decrypted = await self.decrypt_response(arama_istek.json().get("response"))
105
+ arama_veri = decrypted.get("result", [])
94
106
 
95
107
  return [
96
108
  SearchResult(
@@ -101,7 +113,6 @@ class Dizilla(PluginBase):
101
113
  for veri in arama_veri
102
114
  ]
103
115
 
104
- @kekik_cache(ttl=60*60)
105
116
  async def url_base_degis(self, eski_url:str, yeni_base:str) -> str:
106
117
  parsed_url = urlparse(eski_url)
107
118
  parsed_yeni_base = urlparse(yeni_base)
@@ -112,27 +123,34 @@ class Dizilla(PluginBase):
112
123
 
113
124
  return urlunparse(yeni_url)
114
125
 
115
- @kekik_cache(ttl=60*60)
116
126
  async def load_item(self, url: str) -> SeriesInfo:
117
127
  istek = await self.httpx.get(url)
118
128
  secici = Selector(istek.text)
119
129
  veri = loads(secici.xpath("//script[@type='application/ld+json']/text()").getall()[-1])
120
130
 
121
- title = veri.get("name")
131
+ title = veri.get("name")
122
132
  if alt_title := veri.get("alternateName"):
123
133
  title += f" - ({alt_title})"
124
134
 
125
135
  poster = self.fix_url(veri.get("image"))
126
136
  description = veri.get("description")
127
137
  year = veri.get("datePublished").split("-")[0]
128
- tags = []
129
- rating = veri.get("aggregateRating", {}).get("ratingValue")
130
- actors = [actor.get("name") for actor in veri.get("actor", []) if actor.get("name")]
138
+
139
+ # Tags extraction from page content (h3 tag)
140
+ tags_raw = secici.css("div.poster.poster h3::text").get()
141
+ tags = [t.strip() for t in tags_raw.split(",")] if tags_raw else []
142
+
143
+ rating = veri.get("aggregateRating", {}).get("ratingValue")
144
+ actors = [actor.get("name") for actor in veri.get("actor", []) if actor.get("name")]
131
145
 
132
146
  bolumler = []
133
147
  sezonlar = veri.get("containsSeason") if isinstance(veri.get("containsSeason"), list) else [veri.get("containsSeason")]
134
148
  for sezon in sezonlar:
135
- for bolum in sezon.get("episode"):
149
+ episodes = sezon.get("episode")
150
+ if isinstance(episodes, dict):
151
+ episodes = [episodes]
152
+
153
+ for bolum in episodes:
136
154
  bolumler.append(Episode(
137
155
  season = sezon.get("seasonNumber"),
138
156
  episode = bolum.get("episodeNumber"),
@@ -152,15 +170,26 @@ class Dizilla(PluginBase):
152
170
  actors = actors
153
171
  )
154
172
 
155
- @kekik_cache(ttl=15*60)
156
- async def load_links(self, url: str) -> list[str]:
157
- istek = await self.httpx.get(url)
158
- secici = Selector(istek.text)
159
-
160
- iframes = [self.fix_url(secici.css("div#playerLsDizilla iframe::attr(src)").get())]
161
- for alternatif in secici.css("a[href*='player']"):
162
- alt_istek = await self.httpx.get(self.fix_url(alternatif.css("::attr(href)").get()))
163
- alt_secici = Selector(alt_istek.text)
164
- iframes.append(self.fix_url(alt_secici.css("div#playerLsDizilla iframe::attr(src)").get()))
165
-
166
- return iframes
173
+ async def load_links(self, url: str) -> list[dict]:
174
+ istek = await self.httpx.get(url)
175
+ secici = Selector(istek.text)
176
+
177
+ next_data = loads(secici.css("script#__NEXT_DATA__::text").get())
178
+ secure_data = next_data.get("props", {}).get("pageProps", {}).get("secureData", {})
179
+ decrypted = await self.decrypt_response(secure_data)
180
+ results = decrypted.get("RelatedResults", {}).get("getEpisodeSources", {}).get("result", [])
181
+
182
+ links = []
183
+ for result in results:
184
+ iframe_src = Selector(result.get("source_content")).css("iframe::attr(src)").get()
185
+ iframe_url = self.fix_url(iframe_src)
186
+ if not iframe_url:
187
+ continue
188
+
189
+ extractor = self.ex_manager.find_extractor(iframe_url)
190
+ links.append({
191
+ "url" : iframe_url,
192
+ "name" : f"{extractor.name if extractor else 'Main Player'} | {result.get('language_name')}",
193
+ })
194
+
195
+ return links
@@ -0,0 +1,145 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo
4
+ from parsel import Selector
5
+
6
+ class FilmBip(PluginBase):
7
+ name = "FilmBip"
8
+ language = "tr"
9
+ main_url = "https://filmbip.com"
10
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
11
+ description = "FilmBip adlı film sitemizde Full HD film izle. Yerli ve yabancı filmleri Türkçe dublaj veya altyazılı şekilde 1080p yüksek kalite film izle"
12
+
13
+ main_page = {
14
+ f"{main_url}/filmler/SAYFA" : "Yeni Filmler",
15
+ f"{main_url}/film/tur/aile/SAYFA" : "Aile",
16
+ f"{main_url}/film/tur/aksiyon/SAYFA" : "Aksiyon",
17
+ f"{main_url}/film/tur/belgesel/SAYFA" : "Belgesel",
18
+ f"{main_url}/film/tur/bilim-kurgu/SAYFA" : "Bilim Kurgu",
19
+ f"{main_url}/film/tur/dram/SAYFA" : "Dram",
20
+ f"{main_url}/film/tur/fantastik/SAYFA" : "Fantastik",
21
+ f"{main_url}/film/tur/gerilim/SAYFA" : "Gerilim",
22
+ f"{main_url}/film/tur/gizem/SAYFA" : "Gizem",
23
+ f"{main_url}/film/tur/komedi/SAYFA" : "Komedi",
24
+ f"{main_url}/film/tur/korku/SAYFA" : "Korku",
25
+ f"{main_url}/film/tur/macera/SAYFA" : "Macera",
26
+ f"{main_url}/film/tur/muzik/SAYFA" : "Müzik",
27
+ f"{main_url}/film/tur/romantik/SAYFA" : "Romantik",
28
+ f"{main_url}/film/tur/savas/SAYFA" : "Savaş",
29
+ f"{main_url}/film/tur/suc/SAYFA" : "Suç",
30
+ f"{main_url}/film/tur/tarih/SAYFA" : "Tarih",
31
+ f"{main_url}/film/tur/vahsi-bati/SAYFA" : "Western",
32
+ }
33
+
34
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
35
+ page_url = url.replace("SAYFA", "") if page == 1 else url.replace("SAYFA", str(page))
36
+ page_url = page_url.rstrip("/")
37
+
38
+ istek = await self.httpx.get(page_url)
39
+ secici = Selector(istek.text)
40
+
41
+ results = []
42
+ for veri in secici.css("div.poster-long"):
43
+ img = veri.css("a.block img.lazy")
44
+ title = img.css("::attr(alt)").get()
45
+ href = self.fix_url(veri.css("a.block::attr(href)").get())
46
+ poster = self.fix_url(img.css("::attr(data-src)").get() or img.css("::attr(src)").get())
47
+
48
+ if title and href:
49
+ results.append(MainPageResult(
50
+ category = category,
51
+ title = title,
52
+ url = href,
53
+ poster = poster,
54
+ ))
55
+
56
+ return results
57
+
58
+ async def search(self, query: str) -> list[SearchResult]:
59
+ istek = await self.httpx.post(
60
+ url = f"{self.main_url}/search",
61
+ headers = {
62
+ "Accept" : "application/json, text/javascript, */*; q=0.01",
63
+ "X-Requested-With" : "XMLHttpRequest",
64
+ "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8",
65
+ "Origin" : self.main_url,
66
+ "Referer" : f"{self.main_url}/"
67
+ },
68
+ data = {"query": query}
69
+ )
70
+
71
+ try:
72
+ json_data = istek.json()
73
+ if not json_data.get("success"):
74
+ return []
75
+
76
+ html_content = json_data.get("theme", "")
77
+ except Exception:
78
+ return []
79
+
80
+ secici = Selector(text=html_content)
81
+
82
+ results = []
83
+ for veri in secici.css("li"):
84
+ title = veri.css("a.block.truncate::text").get()
85
+ href = self.fix_url(veri.css("a::attr(href)").get())
86
+ poster = self.fix_url(veri.css("img.lazy::attr(data-src)").get())
87
+
88
+ if title and href:
89
+ results.append(SearchResult(
90
+ title = title.strip(),
91
+ url = href,
92
+ poster = poster,
93
+ ))
94
+
95
+ return results
96
+
97
+ async def load_item(self, url: str) -> MovieInfo:
98
+ istek = await self.httpx.get(url)
99
+ secici = Selector(istek.text)
100
+
101
+ title = secici.css("div.page-title h1::text").get()
102
+ poster = self.fix_url(secici.css("meta[property='og:image']::attr(content)").get())
103
+ trailer = secici.css("div.series-profile-trailer::attr(data-yt)").get()
104
+ description = secici.css("div.series-profile-infos-in.article p::text").get() or \
105
+ secici.css("div.series-profile-summary p::text").get()
106
+
107
+ tags = secici.css("div.series-profile-type.tv-show-profile-type a::text").getall()
108
+
109
+ # XPath ile yıl, süre ve puan
110
+ year = secici.xpath("//li[span[contains(text(), 'Yapım yılı')]]/p/text()").re_first(r"(\d{4})")
111
+ duration = secici.xpath("//li[span[contains(text(), 'Süre')]]/p/text()").re_first(r"(\d+)")
112
+ rating = secici.xpath("//li[span[contains(text(), 'IMDB Puanı')]]/p/span/text()").get()
113
+
114
+ actors = secici.css("div.series-profile-cast ul li a img::attr(alt)").getall()
115
+
116
+ return MovieInfo(
117
+ url = url,
118
+ poster = poster,
119
+ title = self.clean_title(title) if title else "",
120
+ description = description.strip() if description else None,
121
+ tags = tags,
122
+ year = year,
123
+ rating = rating,
124
+ duration = int(duration) if duration else None,
125
+ actors = actors,
126
+ )
127
+
128
+ async def load_links(self, url: str) -> list[dict]:
129
+ istek = await self.httpx.get(url)
130
+ secici = Selector(istek.text)
131
+
132
+ results = []
133
+
134
+ for player in secici.css("div#tv-spoox2"):
135
+ iframe = self.fix_url(player.css("iframe::attr(src)").get())
136
+
137
+ if iframe:
138
+ extractor = self.ex_manager.find_extractor(iframe)
139
+ results.append({
140
+ "name" : extractor.name if extractor else "Player",
141
+ "url" : iframe,
142
+ "referer" : f"{self.main_url}/"
143
+ })
144
+
145
+ return results