KekikStream 2.2.7__tar.gz → 2.2.9__tar.gz

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 (89) hide show
  1. kekikstream-2.2.9/KekikStream/Extractors/CloseLoad.py +77 -0
  2. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/Filemoon.py +41 -26
  3. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/Dizilla.py +64 -37
  4. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/HDFilmCehennemi.py +31 -35
  5. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/SetFilmIzle.py +47 -44
  6. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/SezonlukDizi.py +51 -51
  7. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream.egg-info/PKG-INFO +1 -1
  8. {kekikstream-2.2.7 → kekikstream-2.2.9}/PKG-INFO +1 -1
  9. {kekikstream-2.2.7 → kekikstream-2.2.9}/setup.py +1 -1
  10. kekikstream-2.2.7/KekikStream/Extractors/CloseLoad.py +0 -40
  11. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/CLI/__init__.py +0 -0
  12. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/CLI/pypi_kontrol.py +0 -0
  13. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/Extractor/ExtractorBase.py +0 -0
  14. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
  15. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
  16. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/Extractor/ExtractorModels.py +0 -0
  17. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/Extractor/YTDLPCache.py +0 -0
  18. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/Media/MediaHandler.py +0 -0
  19. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/Media/MediaManager.py +0 -0
  20. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/Plugin/PluginBase.py +0 -0
  21. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
  22. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/Plugin/PluginManager.py +0 -0
  23. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/Plugin/PluginModels.py +0 -0
  24. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/UI/UIManager.py +0 -0
  25. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Core/__init__.py +0 -0
  26. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/ContentX.py +0 -0
  27. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/DonilasPlay.py +0 -0
  28. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/DzenRu.py +0 -0
  29. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/ExPlay.py +0 -0
  30. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/HDPlayerSystem.py +0 -0
  31. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/JFVid.py +0 -0
  32. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/JetTv.py +0 -0
  33. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/MailRu.py +0 -0
  34. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/MixPlayHD.py +0 -0
  35. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/MixTiger.py +0 -0
  36. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/MolyStream.py +0 -0
  37. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/Odnoklassniki.py +0 -0
  38. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/PeaceMakerst.py +0 -0
  39. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/PixelDrain.py +0 -0
  40. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/PlayerFilmIzle.py +0 -0
  41. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/RapidVid.py +0 -0
  42. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/SetPlay.py +0 -0
  43. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/SetPrime.py +0 -0
  44. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/SibNet.py +0 -0
  45. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/Sobreatsesuyp.py +0 -0
  46. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/TRsTX.py +0 -0
  47. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/TauVideo.py +0 -0
  48. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/TurboImgz.py +0 -0
  49. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/TurkeyPlayer.py +0 -0
  50. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/VCTPlay.py +0 -0
  51. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/VidHide.py +0 -0
  52. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/VidMoly.py +0 -0
  53. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/VidMoxy.py +0 -0
  54. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/VidPapi.py +0 -0
  55. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/VideoSeyred.py +0 -0
  56. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/YTDLP.py +0 -0
  57. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Extractors/YildizKisaFilm.py +0 -0
  58. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/BelgeselX.py +0 -0
  59. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/DiziBox.py +0 -0
  60. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/DiziPal.py +0 -0
  61. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/DiziYou.py +0 -0
  62. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/FilmBip.py +0 -0
  63. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/FilmMakinesi.py +0 -0
  64. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/FilmModu.py +0 -0
  65. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/FullHDFilm.py +0 -0
  66. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/FullHDFilmizlesene.py +0 -0
  67. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/JetFilmizle.py +0 -0
  68. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/KultFilmler.py +0 -0
  69. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/RecTV.py +0 -0
  70. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/RoketDizi.py +0 -0
  71. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/SelcukFlix.py +0 -0
  72. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/SineWix.py +0 -0
  73. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/Sinefy.py +0 -0
  74. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/SinemaCX.py +0 -0
  75. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/Sinezy.py +0 -0
  76. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/SuperFilmGeldi.py +0 -0
  77. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/Plugins/UgurFilm.py +0 -0
  78. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/__init__.py +0 -0
  79. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/__main__.py +0 -0
  80. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream/requirements.txt +0 -0
  81. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream.egg-info/SOURCES.txt +0 -0
  82. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream.egg-info/dependency_links.txt +0 -0
  83. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream.egg-info/entry_points.txt +0 -0
  84. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream.egg-info/requires.txt +0 -0
  85. {kekikstream-2.2.7 → kekikstream-2.2.9}/KekikStream.egg-info/top_level.txt +0 -0
  86. {kekikstream-2.2.7 → kekikstream-2.2.9}/LICENSE +0 -0
  87. {kekikstream-2.2.7 → kekikstream-2.2.9}/MANIFEST.in +0 -0
  88. {kekikstream-2.2.7 → kekikstream-2.2.9}/README.md +0 -0
  89. {kekikstream-2.2.7 → kekikstream-2.2.9}/setup.cfg +0 -0
@@ -0,0 +1,77 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
+ from Kekik.Sifreleme import Packer, StreamDecoder
5
+ from selectolax.parser import HTMLParser
6
+ import re, json
7
+
8
+ class CloseLoadExtractor(ExtractorBase):
9
+ name = "CloseLoad"
10
+ main_url = "https://closeload.filmmakinesi.to"
11
+
12
+ def _extract_from_json_ld(self, html: str) -> str | None:
13
+ """JSON-LD script tag'inden contentUrl'i çıkar (Kotlin versiyonundaki gibi)"""
14
+ secici = HTMLParser(html)
15
+ for script in secici.css("script[type='application/ld+json']"):
16
+ try:
17
+ data = json.loads(script.text(strip=True))
18
+ if content_url := data.get("contentUrl"):
19
+ if content_url.startswith("http"):
20
+ return content_url
21
+ except (json.JSONDecodeError, TypeError):
22
+ # Regex ile contentUrl'i çıkarmayı dene
23
+ match = re.search(r'"contentUrl"\s*:\s*"([^"]+)"', script.text())
24
+ if match and match.group(1).startswith("http"):
25
+ return match.group(1)
26
+ return None
27
+
28
+ def _extract_from_packed(self, html: str) -> str | None:
29
+ """Packed JavaScript'ten video URL'sini çıkar (fallback)"""
30
+ try:
31
+ eval_func = re.compile(r'\s*(eval\(function[\s\S].*)').findall(html)
32
+ if eval_func:
33
+ return StreamDecoder.extract_stream_url(Packer.unpack(eval_func[0]))
34
+ except Exception:
35
+ pass
36
+ return None
37
+
38
+ async def extract(self, url, referer=None) -> ExtractResult:
39
+ if referer:
40
+ self.httpx.headers.update({"Referer": referer})
41
+
42
+ self.httpx.headers.update({
43
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0",
44
+ "Origin": self.main_url
45
+ })
46
+
47
+ istek = await self.httpx.get(url)
48
+ istek.raise_for_status()
49
+
50
+ # Önce JSON-LD'den dene (daha güvenilir - Kotlin versiyonu gibi)
51
+ m3u_link = self._extract_from_json_ld(istek.text)
52
+
53
+ # Fallback: Packed JavaScript'ten çıkar
54
+ if not m3u_link:
55
+ m3u_link = self._extract_from_packed(istek.text)
56
+
57
+ if not m3u_link:
58
+ raise Exception("Video URL bulunamadı (ne JSON-LD ne de packed script'ten)")
59
+
60
+ # Subtitle'ları parse et (Kotlin referansı: track elementleri)
61
+ subtitles = []
62
+ secici = HTMLParser(istek.text)
63
+ for track in secici.css("track"):
64
+ raw_src = track.attrs.get("src") or ""
65
+ raw_src = raw_src.strip()
66
+ label = track.attrs.get("label") or track.attrs.get("srclang") or "Altyazı"
67
+
68
+ if raw_src:
69
+ full_url = raw_src if raw_src.startswith("http") else f"{self.main_url}{raw_src}"
70
+ subtitles.append(Subtitle(name=label, url=full_url))
71
+
72
+ return ExtractResult(
73
+ name = self.name,
74
+ url = m3u_link,
75
+ referer = self.main_url,
76
+ subtitles = subtitles
77
+ )
@@ -22,13 +22,14 @@ class Filemoon(ExtractorBase):
22
22
  return any(domain in url for domain in self.supported_domains)
23
23
 
24
24
  async def extract(self, url: str, referer: str = None) -> ExtractResult:
25
- headers = {
26
- "Referer" : url,
27
- "Sec-Fetch-Dest" : "iframe",
28
- "Sec-Fetch-Mode" : "navigate",
29
- "Sec-Fetch-Site" : "cross-site",
25
+ default_headers = {
26
+ "Referer" : url,
27
+ "Sec-Fetch-Dest" : "iframe",
28
+ "Sec-Fetch-Mode" : "navigate",
29
+ "Sec-Fetch-Site" : "cross-site",
30
+ "User-Agent" : "Mozilla/5.0 (X11; Linux x86_64; rv:137.0) Gecko/20100101 Firefox/137.0"
30
31
  }
31
- self.httpx.headers.update(headers)
32
+ self.httpx.headers.update(default_headers)
32
33
 
33
34
  # İlk sayfayı al
34
35
  istek = await self.httpx.get(url)
@@ -38,31 +39,44 @@ class Filemoon(ExtractorBase):
38
39
  # Eğer iframe varsa, iframe'e git
39
40
  iframe_el = secici.css_first("iframe")
40
41
  iframe_src = iframe_el.attrs.get("src") if iframe_el else None
41
- if iframe_src:
42
- iframe_url = self.fix_url(iframe_src)
43
- self.httpx.headers.update({
44
- "Accept-Language" : "en-US,en;q=0.5",
45
- "Sec-Fetch-Dest" : "iframe"
46
- })
47
- istek = await self.httpx.get(iframe_url)
48
- response = istek.text
49
-
50
- # Packed script'i bul ve unpack et
42
+
51
43
  m3u8_url = None
52
-
53
- if Packer.detect_packed(response):
54
- try:
55
- unpacked = Packer.unpack(response)
56
- # sources:[{file:"...» pattern'ını ara
57
- if match := re.search(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"', unpacked):
44
+
45
+ if not iframe_src:
46
+ # Fallback: Script içinde ara (Kotlin: selectFirst("script:containsData(function(p,a,c,k,e,d))"))
47
+ script_data = ""
48
+ for script in secici.css("script"):
49
+ if "function(p,a,c,k,e,d)" in script.text():
50
+ script_data = script.text()
51
+ break
52
+
53
+ if script_data:
54
+ unpacked = Packer.unpack(script_data)
55
+ if match := re.search(r'sources:\[\{file:"(.*?)"', unpacked):
58
56
  m3u8_url = match.group(1)
59
- elif match := re.search(r'file:\s*"([^"]*?\.m3u8[^"]*)"', unpacked):
57
+ else:
58
+ # Iframe varsa devam et
59
+ iframe_url = self.fix_url(iframe_src)
60
+ iframe_headers = default_headers.copy()
61
+ iframe_headers["Accept-Language"] = "en-US,en;q=0.5"
62
+
63
+ istek = await self.httpx.get(iframe_url, headers=iframe_headers)
64
+ response = istek.text
65
+ secici = HTMLParser(response)
66
+
67
+ script_data = ""
68
+ for script in secici.css("script"):
69
+ if "function(p,a,c,k,e,d)" in script.text():
70
+ script_data = script.text()
71
+ break
72
+
73
+ if script_data:
74
+ unpacked = Packer.unpack(script_data)
75
+ if match := re.search(r'sources:\[\{file:"(.*?)"', unpacked):
60
76
  m3u8_url = match.group(1)
61
- except Exception:
62
- pass
63
77
 
64
- # Fallback: Doğrudan response'ta ara
65
78
  if not m3u8_url:
79
+ # Son çare: Normal response içinde ara
66
80
  if match := re.search(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"', response):
67
81
  m3u8_url = match.group(1)
68
82
  elif match := re.search(r'file:\s*"([^"]*?\.m3u8[^"]*)"', response):
@@ -75,5 +89,6 @@ class Filemoon(ExtractorBase):
75
89
  name = self.name,
76
90
  url = self.fix_url(m3u8_url),
77
91
  referer = f"{self.main_url}/",
92
+ user_agent = default_headers["User-Agent"],
78
93
  subtitles = []
79
94
  )
@@ -142,44 +142,70 @@ class Dizilla(PluginBase):
142
142
  istek = await self.httpx.get(url)
143
143
  secici = HTMLParser(istek.text)
144
144
 
145
- # application/ld+json script'lerini al
146
- ld_json_scripts = secici.css("script[type='application/ld+json']")
147
- if not ld_json_scripts:
145
+ title = secici.css_first("div.poster.poster h2")
146
+ title = title.text(strip=True) if title else None
147
+ if not title:
148
148
  return None
149
149
 
150
- # Son script'i al
151
- last_script = ld_json_scripts[-1]
152
- veri = loads(last_script.text(strip=True))
150
+ poster_el = secici.css_first("div.w-full.page-top.relative img")
151
+ poster = self.fix_url(poster_el.attrs.get("src")) if poster_el else None
153
152
 
154
- title = veri.get("name")
155
- if alt_title := veri.get("alternateName"):
156
- title += f" - ({alt_title})"
153
+ # Year extraction (Kotlin: [1] index for w-fit min-w-fit)
154
+ info_boxes = secici.css("div.w-fit.min-w-fit")
155
+ year = None
156
+ if len(info_boxes) > 1:
157
+ year_el = info_boxes[1].css_first("span.text-sm.opacity-60")
158
+ if year_el:
159
+ year_text = year_el.text(strip=True)
160
+ year = year_text.split(" ")[-1] if " " in year_text else year_text
161
+
162
+ description_el = secici.css_first("div.mt-2.text-sm")
163
+ description = description_el.text(strip=True) if description_el else None
157
164
 
158
- poster = self.fix_url(veri.get("image"))
159
- description = veri.get("description")
160
- year = veri.get("datePublished").split("-")[0]
161
-
162
- # Tags extraction from page content (h3 tag)
163
165
  tags_el = secici.css_first("div.poster.poster h3")
164
- tags_raw = tags_el.text(strip=True) if tags_el else ""
165
- tags = [t.strip() for t in tags_raw.split(",")] if tags_raw else []
166
-
167
- rating = veri.get("aggregateRating", {}).get("ratingValue")
168
- actors = [actor.get("name") for actor in veri.get("actor", []) if actor.get("name")]
169
-
170
- bolumler = []
171
- sezonlar = veri.get("containsSeason") if isinstance(veri.get("containsSeason"), list) else [veri.get("containsSeason")]
172
- for sezon in sezonlar:
173
- episodes = sezon.get("episode")
174
- if isinstance(episodes, dict):
175
- episodes = [episodes]
166
+ tags = [t.strip() for t in tags_el.text(strip=True).split(",")] if tags_el else []
167
+
168
+ actors = [h5.text(strip=True) for h5 in secici.css("div.global-box h5")]
169
+
170
+ episodeses = []
171
+ # Seasons links iteration
172
+ season_links = secici.css("div.flex.items-center.flex-wrap.gap-2.mb-4 a")
173
+ for sezon in season_links:
174
+ sezon_href = self.fix_url(sezon.attrs.get("href"))
175
+ sezon_req = await self.httpx.get(sezon_href)
176
176
 
177
- for bolum in episodes:
178
- bolumler.append(Episode(
179
- season = sezon.get("seasonNumber"),
180
- episode = bolum.get("episodeNumber"),
181
- title = bolum.get("name"),
182
- url = await self.url_base_degis(bolum.get("url"), self.main_url),
177
+ season_num = None
178
+ try:
179
+ # URL'den sezon numarasını çek: ...-sezon-X
180
+ season_match = re.search(r"sezon-(\d+)", sezon_href)
181
+ if season_match:
182
+ season_num = int(season_match.group(1))
183
+ except:
184
+ pass
185
+
186
+ sezon_secici = HTMLParser(sezon_req.text)
187
+ for bolum in sezon_secici.css("div.episodes div.cursor-pointer"):
188
+ # Kotlin: bolum.select("a").last()
189
+ links = bolum.css("a")
190
+ if not links:
191
+ continue
192
+
193
+ ep_link = links[-1]
194
+ ep_name = ep_link.text(strip=True)
195
+ ep_href = self.fix_url(ep_link.attrs.get("href"))
196
+
197
+ # Episode number (first link's text usually)
198
+ ep_num = None
199
+ try:
200
+ ep_num = int(links[0].text(strip=True))
201
+ except:
202
+ pass
203
+
204
+ episodeses.append(Episode(
205
+ season = season_num,
206
+ episode = ep_num,
207
+ title = ep_name,
208
+ url = ep_href
183
209
  ))
184
210
 
185
211
  return SeriesInfo(
@@ -188,9 +214,8 @@ class Dizilla(PluginBase):
188
214
  title = title,
189
215
  description = description,
190
216
  tags = tags,
191
- rating = rating,
192
- year = year,
193
- episodes = bolumler,
217
+ year = str(year) if year else None,
218
+ episodes = episodeses,
194
219
  actors = actors
195
220
  )
196
221
 
@@ -212,7 +237,7 @@ class Dizilla(PluginBase):
212
237
 
213
238
  # Get first source (matching Kotlin)
214
239
  first_result = results[0]
215
- source_content = first_result.get("source_content", "")
240
+ source_content = str(first_result.get("source_content", ""))
216
241
 
217
242
  # Clean the source_content string (matching Kotlin: .replace("\"", "").replace("\\", ""))
218
243
  cleaned_source = source_content.replace('"', '').replace('\\', '')
@@ -220,10 +245,12 @@ class Dizilla(PluginBase):
220
245
  # Parse cleaned HTML
221
246
  iframe_el = HTMLParser(cleaned_source).css_first("iframe")
222
247
  iframe_src = iframe_el.attrs.get("src") if iframe_el else None
248
+
249
+ # Referer check (matching Kotlin: loadExtractor(iframe, "${mainUrl}/", ...))
223
250
  iframe_url = self.fix_url(iframe_src) if iframe_src else None
224
251
 
225
252
  if not iframe_url:
226
253
  return []
227
254
 
228
- data = await self.extract(iframe_url, prefix=first_result.get('language_name', 'Unknown'))
255
+ data = await self.extract(iframe_url, referer=f"{self.main_url}/", prefix=first_result.get('language_name', 'Unknown'))
229
256
  return [data] if data else []
@@ -205,30 +205,23 @@ class HDFilmCehennemi(PluginBase):
205
205
 
206
206
  return results
207
207
 
208
- def extract_hdch_url(self, unpacked: str) -> str:
209
- """HDFilmCehennemi unpacked script'ten video URL'sini çıkar"""
210
- # 1) Decode fonksiyonunun adını bul: function <NAME>(value_parts)
211
- match_fn = re.search(r'function\s+(\w+)\s*\(\s*value_parts\s*\)', unpacked)
212
- if not match_fn:
213
- return ""
214
-
215
- fn_name = match_fn.group(1)
216
-
217
- # 2) Bu fonksiyonun array ile çağrıldığı yeri bul: <NAME>([ ... ])
218
- array_call_regex = re.compile(rf'{re.escape(fn_name)}\(\s*\[(.*?)\]\s*\)', re.DOTALL)
219
- match_call = array_call_regex.search(unpacked)
220
- if not match_call:
221
- return ""
222
-
223
- array_body = match_call.group(1)
224
-
225
- # 3) Array içindeki string parçalarını topla
226
- parts = re.findall(r'["\']([^"\']+)["\']', array_body)
227
- if not parts:
228
- return ""
229
-
230
- # 4) Özel decoder ile çöz
231
- return StreamDecoder.extract_stream_url(unpacked)
208
+ def _extract_from_json_ld(self, html: str) -> str | None:
209
+ """JSON-LD script tag'inden contentUrl'i çıkar (Kotlin versiyonundaki gibi)"""
210
+ # Önce JSON-LD'den dene
211
+ json_ld_match = re.search(r'<script[^>]+type=["\']application/ld\+json["\'][^>]*>(.*?)</script>', html, re.DOTALL)
212
+ if json_ld_match:
213
+ try:
214
+ import json
215
+ data = json.loads(json_ld_match.group(1).strip())
216
+ if content_url := data.get("contentUrl"):
217
+ if content_url.startswith("http"):
218
+ return content_url
219
+ except Exception:
220
+ # Regex ile contentUrl'i çıkarmayı dene
221
+ match = re.search(r'"contentUrl"\s*:\s*"([^"]+)"', html)
222
+ if match and match.group(1).startswith("http"):
223
+ return match.group(1)
224
+ return None
232
225
 
233
226
  async def invoke_local_source(self, iframe: str, source: str, url: str):
234
227
  self.httpx.headers.update({
@@ -240,18 +233,21 @@ class HDFilmCehennemi(PluginBase):
240
233
  if not istek.text:
241
234
  return await self.cehennempass(iframe.split("/")[-1])
242
235
 
243
- # eval(function...) içeren packed script bul
244
- eval_match = re.search(r'(eval\(function[\s\S]+)', istek.text)
245
- if not eval_match:
246
- return await self.cehennempass(iframe.split("/")[-1])
236
+ # Önce JSON-LD'den dene (Kotlin versiyonu gibi - daha güvenilir)
237
+ video_url = self._extract_from_json_ld(istek.text)
247
238
 
248
- try:
249
- unpacked = Packer.unpack(eval_match.group(1))
250
- except Exception:
251
- return await self.cehennempass(iframe.split("/")[-1])
252
-
253
- # HDFilmCehennemi özel decoder ile video URL'sini çıkar
254
- video_url = self.extract_hdch_url(unpacked)
239
+ # Fallback: Packed JavaScript'ten çıkar
240
+ if not video_url:
241
+ # eval(function...) içeren packed script bul
242
+ eval_match = re.search(r'(eval\(function[\s\S]+)', istek.text)
243
+ if not eval_match:
244
+ return await self.cehennempass(iframe.split("/")[-1])
245
+
246
+ try:
247
+ unpacked = Packer.unpack(eval_match.group(1))
248
+ video_url = StreamDecoder.extract_stream_url(unpacked)
249
+ except Exception:
250
+ return await self.cehennempass(iframe.split("/")[-1])
255
251
 
256
252
  if not video_url:
257
253
  return await self.cehennempass(iframe.split("/")[-1])
@@ -2,7 +2,7 @@
2
2
 
3
3
  from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult
4
4
  from selectolax.parser import HTMLParser
5
- import re, json
5
+ import re, json, asyncio
6
6
 
7
7
  class SetFilmIzle(PluginBase):
8
8
  name = "SetFilmIzle"
@@ -229,7 +229,7 @@ class SetFilmIzle(PluginBase):
229
229
  istek = await self.httpx.get(url)
230
230
  secici = HTMLParser(istek.text)
231
231
 
232
- nonce = self._get_nonce("video_nonce", referer=url)
232
+ nonce = secici.css_first("div#playex").attrs.get("data-nonce") if secici.css_first("div#playex") else ""
233
233
 
234
234
  # partKey to dil label mapping
235
235
  part_key_labels = {
@@ -238,46 +238,49 @@ class SetFilmIzle(PluginBase):
238
238
  "orijinal" : "Orijinal"
239
239
  }
240
240
 
241
- links = []
241
+ semaphore = asyncio.Semaphore(5)
242
+ tasks = []
243
+
244
+ async def fetch_and_extract(player):
245
+ async with semaphore:
246
+ source_id = player.attrs.get("data-post-id")
247
+ player_name = player.attrs.get("data-player-name")
248
+ part_key = player.attrs.get("data-part-key")
249
+
250
+ if not source_id or "event" in source_id or source_id == "":
251
+ return None
252
+
253
+ try:
254
+ resp = self.cloudscraper.post(
255
+ f"{self.main_url}/wp-admin/admin-ajax.php",
256
+ headers = {"Referer": url},
257
+ data = {
258
+ "action" : "get_video_url",
259
+ "nonce" : nonce,
260
+ "post_id" : source_id,
261
+ "player_name" : player_name or "",
262
+ "part_key" : part_key or ""
263
+ }
264
+ )
265
+ data = resp.json()
266
+ except:
267
+ return None
268
+
269
+ iframe_url = data.get("data", {}).get("url")
270
+ if not iframe_url:
271
+ return None
272
+
273
+ if "setplay" not in iframe_url and part_key:
274
+ iframe_url = f"{iframe_url}?partKey={part_key}"
275
+
276
+ label = part_key_labels.get(part_key, "")
277
+ if not label and part_key:
278
+ label = part_key.replace("_", " ").title()
279
+
280
+ return await self.extract(iframe_url, prefix=label if label else None)
281
+
242
282
  for player in secici.css("nav.player a"):
243
- source_id = player.attrs.get("data-post-id")
244
- player_name = player.attrs.get("data-player-name")
245
- part_key = player.attrs.get("data-part-key")
246
-
247
- if not source_id or "event" in source_id or source_id == "":
248
- continue
249
-
250
- try:
251
- resp = self.cloudscraper.post(
252
- f"{self.main_url}/wp-admin/admin-ajax.php",
253
- headers = {"Referer": url},
254
- data = {
255
- "action" : "get_video_url",
256
- "nonce" : nonce,
257
- "post_id" : source_id,
258
- "player_name" : player_name or "",
259
- "part_key" : part_key or ""
260
- }
261
- )
262
- data = resp.json()
263
- except:
264
- continue
265
-
266
- iframe_url = data.get("data", {}).get("url")
267
- if not iframe_url:
268
- continue
269
-
270
- # SetPlay URL'si için part_key ekleme
271
- if "setplay" not in iframe_url and part_key:
272
- iframe_url = f"{iframe_url}?partKey={part_key}"
273
-
274
- # Dil etiketi oluştur
275
- label = part_key_labels.get(part_key, "")
276
- if not label and part_key:
277
- label = part_key.replace("_", " ").title()
278
-
279
- data = await self.extract(iframe_url, prefix=label if label else None)
280
- if data:
281
- links.append(data)
282
-
283
- return links
283
+ tasks.append(fetch_and_extract(player))
284
+
285
+ results = await asyncio.gather(*tasks)
286
+ return [r for r in results if r]
@@ -2,7 +2,7 @@
2
2
 
3
3
  from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
4
4
  from selectolax.parser import HTMLParser
5
- import re
5
+ import re, asyncio
6
6
 
7
7
  class SezonlukDizi(PluginBase):
8
8
  name = "SezonlukDizi"
@@ -39,6 +39,16 @@ class SezonlukDizi(PluginBase):
39
39
  f"{main_url}/diziler.asp?siralama_tipi=id&tur=western&s=" : "Western"
40
40
  }
41
41
 
42
+ async def _get_asp_data(self) -> dict:
43
+ js_req = await self.httpx.get(f"{self.main_url}/js/site.min.js")
44
+ alt_match = re.search(r"dataAlternatif(.*?)\.asp", js_req.text)
45
+ embed_match = re.search(r"dataEmbed(.*?)\.asp", js_req.text)
46
+
47
+ return {
48
+ "alternatif": alt_match.group(1) if alt_match else "",
49
+ "embed": embed_match.group(1) if embed_match else ""
50
+ }
51
+
42
52
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
43
53
  istek = await self.httpx.get(f"{url}{page}")
44
54
  secici = HTMLParser(istek.text)
@@ -67,7 +77,7 @@ class SezonlukDizi(PluginBase):
67
77
  secici = HTMLParser(istek.text)
68
78
 
69
79
  results = []
70
- for afis in secici.css("div.afis a.column"):
80
+ for afis in secici.css("div.afis a"):
71
81
  desc_el = afis.css_first("div.description")
72
82
  img_el = afis.css_first("img")
73
83
 
@@ -169,64 +179,54 @@ class SezonlukDizi(PluginBase):
169
179
  actors = actors
170
180
  )
171
181
 
172
- async def get_asp_data(self) -> tuple[str, str]:
173
- """Fetch dynamic ASP version numbers from site.min.js"""
174
- try:
175
- js_content = await self.httpx.get(f"{self.main_url}/js/site.min.js")
176
- alternatif_match = re.search(r'dataAlternatif(.*?)\.asp', js_content.text)
177
- embed_match = re.search(r'dataEmbed(.*?)\.asp', js_content.text)
178
-
179
- alternatif_ver = alternatif_match.group(1) if alternatif_match else "22"
180
- embed_ver = embed_match.group(1) if embed_match else "22"
181
-
182
- return (alternatif_ver, embed_ver)
183
- except Exception:
184
- return ("22", "22") # Fallback to default versions
185
-
186
182
  async def load_links(self, url: str) -> list[ExtractResult]:
187
183
  istek = await self.httpx.get(url)
188
184
  secici = HTMLParser(istek.text)
189
-
190
- dilsec_el = secici.css_first("div#dilsec")
191
- bid = dilsec_el.attrs.get("data-id") if dilsec_el else None
185
+ asp_data = await self._get_asp_data()
186
+
187
+ bid = secici.css_first("div#dilsec").attrs.get("data-id") if secici.css_first("div#dilsec") else None
192
188
  if not bid:
193
189
  return []
194
190
 
195
- # Get dynamic ASP versions
196
- alternatif_ver, embed_ver = await self.get_asp_data()
191
+ semaphore = asyncio.Semaphore(5)
192
+ tasks = []
197
193
 
198
- results = []
199
- for dil, label in [("1", "Altyazı"), ("0", "Dublaj")]:
200
- dil_istek = await self.httpx.post(
201
- url = f"{self.main_url}/ajax/dataAlternatif{alternatif_ver}.asp",
194
+ async def fetch_and_extract(veri, dil_etiketi):
195
+ async with semaphore:
196
+ try:
197
+ embed_resp = await self.httpx.post(
198
+ f"{self.main_url}/ajax/dataEmbed{asp_data['embed']}.asp",
199
+ headers = {"X-Requested-With": "XMLHttpRequest"},
200
+ data = {"id": str(veri.get("id"))}
201
+ )
202
+ embed_secici = HTMLParser(embed_resp.text)
203
+ iframe_el = embed_secici.css_first("iframe")
204
+ iframe_src = iframe_el.attrs.get("src") if iframe_el else None
205
+
206
+ if iframe_src:
207
+ if "link.asp" in iframe_src:
208
+ return None
209
+
210
+ iframe_url = self.fix_url(iframe_src)
211
+ return await self.extract(iframe_url, referer=f"{self.main_url}/", prefix=f"{dil_etiketi} - {veri.get('baslik')}")
212
+ except:
213
+ pass
214
+ return None
215
+
216
+ for dil_kodu, dil_etiketi in [("1", "Altyazı"), ("0", "Dublaj")]:
217
+ altyazi_resp = await self.httpx.post(
218
+ f"{self.main_url}/ajax/dataAlternatif{asp_data['alternatif']}.asp",
202
219
  headers = {"X-Requested-With": "XMLHttpRequest"},
203
- data = {"bid": bid, "dil": dil},
220
+ data = {"bid": bid, "dil": dil_kodu}
204
221
  )
205
-
222
+
206
223
  try:
207
- dil_json = dil_istek.json()
208
- except Exception:
224
+ data_json = altyazi_resp.json()
225
+ if data_json.get("status") == "success" and data_json.get("data"):
226
+ for veri in data_json["data"]:
227
+ tasks.append(fetch_and_extract(veri, dil_etiketi))
228
+ except:
209
229
  continue
210
230
 
211
- if dil_json.get("status") == "success":
212
- for idx, veri in enumerate(dil_json.get("data", [])):
213
- veri_response = await self.httpx.post(
214
- url = f"{self.main_url}/ajax/dataEmbed{embed_ver}.asp",
215
- headers = {"X-Requested-With": "XMLHttpRequest"},
216
- data = {"id": veri.get("id")},
217
- )
218
- veri_secici = HTMLParser(veri_response.text)
219
-
220
- iframe_el = veri_secici.css_first("iframe")
221
- iframe = iframe_el.attrs.get("src") if iframe_el else None
222
-
223
- if iframe:
224
- if "link.asp" in iframe:
225
- continue
226
-
227
- iframe_url = self.fix_url(iframe)
228
- data = await self.extract(iframe_url, prefix=label)
229
- if data:
230
- results.append(data)
231
-
232
- return results
231
+ results = await asyncio.gather(*tasks)
232
+ return [r for r in results if r]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: KekikStream
3
- Version: 2.2.7
3
+ Version: 2.2.9
4
4
  Summary: terminal üzerinden medya içeriği aramanızı ve VLC/MPV gibi popüler medya oynatıcılar aracılığıyla doğrudan izlemenizi sağlayan modüler ve genişletilebilir bir bıdı bıdı
5
5
  Home-page: https://github.com/keyiflerolsun/KekikStream
6
6
  Author: keyiflerolsun
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: KekikStream
3
- Version: 2.2.7
3
+ Version: 2.2.9
4
4
  Summary: terminal üzerinden medya içeriği aramanızı ve VLC/MPV gibi popüler medya oynatıcılar aracılığıyla doğrudan izlemenizi sağlayan modüler ve genişletilebilir bir bıdı bıdı
5
5
  Home-page: https://github.com/keyiflerolsun/KekikStream
6
6
  Author: keyiflerolsun
@@ -6,7 +6,7 @@ from io import open
6
6
  setup(
7
7
  # ? Genel Bilgiler
8
8
  name = "KekikStream",
9
- version = "2.2.7",
9
+ version = "2.2.9",
10
10
  url = "https://github.com/keyiflerolsun/KekikStream",
11
11
  description = "terminal üzerinden medya içeriği aramanızı ve VLC/MPV gibi popüler medya oynatıcılar aracılığıyla doğrudan izlemenizi sağlayan modüler ve genişletilebilir bir bıdı bıdı",
12
12
  keywords = ["KekikStream", "KekikAkademi", "keyiflerolsun"],
@@ -1,40 +0,0 @@
1
- # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
-
3
- from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
- from Kekik.Sifreleme import Packer, StreamDecoder
5
- from selectolax.parser import HTMLParser
6
- import re
7
-
8
- class CloseLoadExtractor(ExtractorBase):
9
- name = "CloseLoad"
10
- main_url = "https://closeload.filmmakinesi.to"
11
-
12
- async def extract(self, url, referer=None) -> ExtractResult:
13
- if referer:
14
- self.httpx.headers.update({"Referer": referer})
15
-
16
- istek = await self.httpx.get(url)
17
- istek.raise_for_status()
18
-
19
- # Video URL'sini çıkar
20
- eval_func = re.compile(r'\s*(eval\(function[\s\S].*)').findall(istek.text)[0]
21
- m3u_link = StreamDecoder.extract_stream_url(Packer.unpack(eval_func))
22
-
23
- # Subtitle'ları parse et (Kotlin referansı: track elementleri)
24
- subtitles = []
25
- secici = HTMLParser(istek.text)
26
- for track in secici.css("track"):
27
- raw_src = track.attrs.get("src") or ""
28
- raw_src = raw_src.strip()
29
- label = track.attrs.get("label") or track.attrs.get("srclang") or "Altyazı"
30
-
31
- if raw_src:
32
- full_url = raw_src if raw_src.startswith("http") else f"{self.main_url}{raw_src}"
33
- subtitles.append(Subtitle(name=label, url=full_url))
34
-
35
- return ExtractResult(
36
- name = self.name,
37
- url = m3u_link,
38
- referer = self.main_url,
39
- subtitles = subtitles
40
- )
File without changes
File without changes
File without changes
File without changes