KekikStream 2.2.9__py3-none-any.whl → 2.5.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of KekikStream might be problematic. Click here for more details.

Files changed (88) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +3 -2
  2. KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
  3. KekikStream/Core/HTMLHelper.py +205 -0
  4. KekikStream/Core/Plugin/PluginBase.py +48 -12
  5. KekikStream/Core/Plugin/PluginLoader.py +13 -14
  6. KekikStream/Core/Plugin/PluginManager.py +2 -2
  7. KekikStream/Core/Plugin/PluginModels.py +0 -3
  8. KekikStream/Core/__init__.py +2 -0
  9. KekikStream/Extractors/Abstream.py +27 -0
  10. KekikStream/Extractors/CloseLoad.py +31 -56
  11. KekikStream/Extractors/ContentX.py +28 -71
  12. KekikStream/Extractors/DonilasPlay.py +34 -78
  13. KekikStream/Extractors/DzenRu.py +11 -25
  14. KekikStream/Extractors/ExPlay.py +20 -38
  15. KekikStream/Extractors/Filemoon.py +23 -53
  16. KekikStream/Extractors/HDMomPlayer.py +30 -0
  17. KekikStream/Extractors/HDPlayerSystem.py +13 -31
  18. KekikStream/Extractors/HotStream.py +27 -0
  19. KekikStream/Extractors/JFVid.py +3 -24
  20. KekikStream/Extractors/JetTv.py +21 -34
  21. KekikStream/Extractors/JetV.py +55 -0
  22. KekikStream/Extractors/MailRu.py +11 -29
  23. KekikStream/Extractors/MixPlayHD.py +17 -31
  24. KekikStream/Extractors/MixTiger.py +17 -40
  25. KekikStream/Extractors/MolyStream.py +25 -22
  26. KekikStream/Extractors/Odnoklassniki.py +41 -105
  27. KekikStream/Extractors/PeaceMakerst.py +20 -47
  28. KekikStream/Extractors/PixelDrain.py +9 -16
  29. KekikStream/Extractors/PlayerFilmIzle.py +23 -46
  30. KekikStream/Extractors/RapidVid.py +23 -36
  31. KekikStream/Extractors/SetPlay.py +19 -44
  32. KekikStream/Extractors/SetPrime.py +3 -6
  33. KekikStream/Extractors/SibNet.py +8 -19
  34. KekikStream/Extractors/Sobreatsesuyp.py +25 -47
  35. KekikStream/Extractors/TRsTX.py +25 -55
  36. KekikStream/Extractors/TurboImgz.py +8 -16
  37. KekikStream/Extractors/TurkeyPlayer.py +5 -5
  38. KekikStream/Extractors/VCTPlay.py +10 -28
  39. KekikStream/Extractors/Veev.py +145 -0
  40. KekikStream/Extractors/VidBiz.py +62 -0
  41. KekikStream/Extractors/VidHide.py +59 -34
  42. KekikStream/Extractors/VidMoly.py +67 -89
  43. KekikStream/Extractors/VidMoxy.py +17 -29
  44. KekikStream/Extractors/VidPapi.py +26 -58
  45. KekikStream/Extractors/VideoSeyred.py +21 -42
  46. KekikStream/Extractors/Videostr.py +58 -0
  47. KekikStream/Extractors/Vidoza.py +18 -0
  48. KekikStream/Extractors/Vtbe.py +38 -0
  49. KekikStream/Extractors/YTDLP.py +2 -2
  50. KekikStream/Extractors/YildizKisaFilm.py +13 -31
  51. KekikStream/Extractors/Zeus.py +61 -0
  52. KekikStream/Plugins/BelgeselX.py +108 -99
  53. KekikStream/Plugins/DiziBox.py +61 -106
  54. KekikStream/Plugins/DiziMom.py +179 -0
  55. KekikStream/Plugins/DiziPal.py +104 -192
  56. KekikStream/Plugins/DiziYou.py +66 -149
  57. KekikStream/Plugins/Dizilla.py +93 -126
  58. KekikStream/Plugins/FilmBip.py +102 -72
  59. KekikStream/Plugins/FilmEkseni.py +199 -0
  60. KekikStream/Plugins/FilmMakinesi.py +101 -64
  61. KekikStream/Plugins/FilmModu.py +35 -59
  62. KekikStream/Plugins/Filmatek.py +184 -0
  63. KekikStream/Plugins/FilmciBaba.py +155 -0
  64. KekikStream/Plugins/FullHDFilmizlesene.py +32 -78
  65. KekikStream/Plugins/HDFilm.py +243 -0
  66. KekikStream/Plugins/HDFilmCehennemi.py +261 -222
  67. KekikStream/Plugins/JetFilmizle.py +117 -98
  68. KekikStream/Plugins/KultFilmler.py +153 -143
  69. KekikStream/Plugins/RecTV.py +53 -49
  70. KekikStream/Plugins/RoketDizi.py +92 -123
  71. KekikStream/Plugins/SelcukFlix.py +86 -95
  72. KekikStream/Plugins/SetFilmIzle.py +105 -143
  73. KekikStream/Plugins/SezonlukDizi.py +106 -128
  74. KekikStream/Plugins/Sinefy.py +194 -166
  75. KekikStream/Plugins/SinemaCX.py +159 -113
  76. KekikStream/Plugins/Sinezy.py +44 -73
  77. KekikStream/Plugins/SuperFilmGeldi.py +28 -52
  78. KekikStream/Plugins/UgurFilm.py +94 -72
  79. KekikStream/Plugins/Watch32.py +160 -0
  80. KekikStream/Plugins/YabanciDizi.py +250 -0
  81. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/METADATA +1 -1
  82. kekikstream-2.5.3.dist-info/RECORD +99 -0
  83. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/WHEEL +1 -1
  84. KekikStream/Plugins/FullHDFilm.py +0 -254
  85. kekikstream-2.2.9.dist-info/RECORD +0 -82
  86. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/entry_points.txt +0 -0
  87. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/licenses/LICENSE +0 -0
  88. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,145 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
4
+ from contextlib import suppress
5
+
6
+ class Veev(ExtractorBase):
7
+ name = "Veev"
8
+ main_url = "https://veev.to"
9
+
10
+ supported_domains = ["veev.to", "kinoger.to", "poophq.com", "doods.pro", "dood.so", "dood.li", "dood.wf", "dood.cx", "dood.sh", "dood.watch", "dood.pm", "dood.to", "dood.ws"]
11
+
12
+ def can_handle_url(self, url: str) -> bool:
13
+ return any(domain in url for domain in self.supported_domains)
14
+
15
+ def veev_decode(self, encoded: str) -> str:
16
+ if not encoded:
17
+ return ""
18
+
19
+ result = []
20
+ # Python dictionary key integer, value string
21
+ # Başlangıçta 0-255 ascii karakterleri
22
+ lut = {i: chr(i) for i in range(256)}
23
+ n = 256
24
+
25
+ c = encoded[0]
26
+ result.append(c)
27
+
28
+ for char in encoded[1:]:
29
+ code = ord(char)
30
+ if code < 256:
31
+ nc = char
32
+ else:
33
+ nc = lut.get(code, c + c[0])
34
+
35
+ result.append(nc)
36
+ lut[n] = c + nc[0]
37
+ n += 1
38
+ c = nc
39
+
40
+ return "".join(result)
41
+
42
+ def build_array(self, encoded: str) -> list[list[int]]:
43
+ result = []
44
+ iterator = iter(encoded)
45
+
46
+ def next_int_or_zero():
47
+ try:
48
+ char = next(iterator)
49
+ if char.isdigit():
50
+ return int(char)
51
+ return 0
52
+ except StopIteration:
53
+ return 0
54
+
55
+ count = next_int_or_zero()
56
+ while count != 0:
57
+ row = []
58
+ for _ in range(count):
59
+ row.append(next_int_or_zero())
60
+ result.append(list(reversed(row)))
61
+ count = next_int_or_zero()
62
+
63
+ return result
64
+
65
+ def decode_url(self, encoded: str, rules: list[int]) -> str:
66
+ text = encoded
67
+ for r in rules:
68
+ if r == 1:
69
+ text = text[::-1]
70
+
71
+ # Hex decode
72
+ with suppress(Exception):
73
+ # remove optional whitespace just in case
74
+ clean_hex = "".join(text.split())
75
+ arr = bytes.fromhex(clean_hex)
76
+ # utf-8 decode, replace errors
77
+ text = arr.decode('utf-8', errors='replace')
78
+
79
+ text = text.replace("dXRmOA==", "")
80
+
81
+ return text
82
+
83
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
84
+ # URL'den ID çıkar
85
+ # https://veev.to/e/lla8v3k6arev
86
+ video_id = url.split("/")[-1]
87
+
88
+ # Sayfayı al
89
+ # Referer lazım mı? Genelde lazım olabilir.
90
+ page_url = f"{self.main_url}/e/{video_id}"
91
+ resp = await self.httpx.get(page_url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"})
92
+ html = resp.text
93
+
94
+ # Regex ile şifreli stringleri bul
95
+ # Regex: [.\s'](?:fc|_vvto\[[^]]*)(?:['\]]*)?\s*[:=]\s*['"]([^'"]+)
96
+ # Python regex için düzenleme gerekebilir.
97
+ found_values = HTMLHelper(html).regex_all(r"[.\s'](?:fc|_vvto\[[^]]*)(?:['\]]*)?\s*[:=]\s*['\"]([^'\"]+)")
98
+
99
+ if not found_values:
100
+ raise ValueError(f"Veev: Token bulunamadı. {url}")
101
+
102
+ # Kotlin kodunda sondan başlayıp deniyor (reversed)
103
+ for f in reversed(found_values):
104
+ try:
105
+ ch = self.veev_decode(f)
106
+ if ch == f:
107
+ continue # Decode olmadıysa geç
108
+
109
+ # API Call
110
+ dl_url = f"{self.main_url}/dl?op=player_api&cmd=gi&file_code={video_id}&r={self.main_url}&ch={ch}&ie=1"
111
+ api_resp = await self.httpx.get(dl_url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"})
112
+
113
+ data = api_resp.json()
114
+ file_obj = data.get("file")
115
+ if not file_obj or file_obj.get("file_status") != "OK":
116
+ continue
117
+
118
+ dv = file_obj.get("dv")
119
+ # dv json string içinde s key'inde olabilir (Kotlin: getString("s"))
120
+ # Ancak api yanıtını görmeden emin olamayız, json yapısına göre file->dv bir string mi object mi?
121
+ # Kotlin: file.getJSONArray("dv").getJSONObject(0).getString("s")
122
+ # Demek ki dv bir array
123
+
124
+ encoded_dv = None
125
+ if isinstance(dv, list) and len(dv) > 0:
126
+ if isinstance(dv[0], dict):
127
+ encoded_dv = dv[0].get("s")
128
+
129
+ if not encoded_dv:
130
+ continue
131
+
132
+ # Decode
133
+ # rules = buildArray(ch)[0]
134
+ rules = self.build_array(ch)[0]
135
+
136
+ final_url = self.decode_url(self.veev_decode(encoded_dv), rules)
137
+
138
+ if final_url.startswith("http"):
139
+ return ExtractResult(name=self.name, url=self.fix_url(final_url), referer=self.main_url)
140
+
141
+ except Exception as e:
142
+ # print(f"Veev Error: {e}")
143
+ continue
144
+
145
+ raise ValueError(f"Veev: Video URL'si çözülemedi. {url}")
@@ -0,0 +1,62 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
4
+ from Kekik.Sifreleme import Packer
5
+
6
+ class VidBiz(ExtractorBase):
7
+ name = "VidBiz"
8
+ main_url = "https://videolar.biz"
9
+
10
+ async def extract(self, url: str, referer: str = None) -> list[ExtractResult]:
11
+ istek = await self.httpx.get(url, headers={"Referer": referer} if referer else None)
12
+ text = istek.text
13
+
14
+ # Eval script bul (kaken içeriyor olmalı)
15
+ eval_script = HTMLHelper(text).regex_first(r'(eval\(function[\s\S]+?)<\/script>') or \
16
+ HTMLHelper(text).regex_first(r'(eval\(function[\s\S]+)')
17
+ if not eval_script:
18
+ raise ValueError(f"VidBiz: Packed script bulunamadı. {url}")
19
+
20
+ unpacked = ""
21
+ try:
22
+ unpacked = Packer.unpack(eval_script)
23
+ except:
24
+ raise ValueError("VidBiz: Unpack hatası")
25
+
26
+ # window.kaken="..."
27
+ kaken = HTMLHelper(unpacked).regex_first(r'window\.kaken\s*=\s*"([^"]+)"')
28
+ if not kaken:
29
+ raise ValueError("VidBiz: Kaken token bulunamadı")
30
+
31
+ # API POST
32
+ # Content-Type: text/plain önemli olabilir
33
+ resp = await self.httpx.post(
34
+ url = "https://s2.videolar.biz/api/",
35
+ content = kaken, # data yerine content=raw string
36
+ headers = {"Content-Type": "text/plain", "Referer": url}
37
+ )
38
+
39
+ try:
40
+ data = resp.json()
41
+ except:
42
+ raise ValueError("VidBiz: API yanıtı JSON değil")
43
+
44
+ if data.get("status") != "ok":
45
+ raise ValueError(f"VidBiz: API hatası {data}")
46
+
47
+ results = []
48
+ for source in data.get("sources", []):
49
+ file_url = source.get("file")
50
+ label = source.get("label", "Unknown")
51
+
52
+ if not file_url:
53
+ continue
54
+
55
+ results.append(ExtractResult(
56
+ name = f"{self.name} | {label}",
57
+ url = self.fix_url(file_url),
58
+ referer = url,
59
+ user_agent = self.httpx.headers.get("User-Agent", "")
60
+ ))
61
+
62
+ return results
@@ -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 ExtractorBase, ExtractResult, Subtitle
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
4
4
  from Kekik.Sifreleme import Packer
5
5
  import re
6
6
 
@@ -9,7 +9,16 @@ class VidHide(ExtractorBase):
9
9
  main_url = "https://vidhidepro.com"
10
10
 
11
11
  # Birden fazla domain destekle
12
- supported_domains = ["vidhidepro.com", "vidhide.com", "rubyvidhub.com"]
12
+ supported_domains = [
13
+ "vidhidepro.com", "vidhide.com", "rubyvidhub.com",
14
+ "vidhidevip.com", "vidhideplus.com", "vidhidepre.com",
15
+ "movearnpre.com", "oneupload.to",
16
+ "filelions.live", "filelions.online", "filelions.to",
17
+ "kinoger.be",
18
+ "smoothpre.com",
19
+ "dhtpre.com",
20
+ "peytonepre.com"
21
+ ]
13
22
 
14
23
  def can_handle_url(self, url: str) -> bool:
15
24
  return any(domain in url for domain in self.supported_domains)
@@ -21,53 +30,70 @@ class VidHide(ExtractorBase):
21
30
  return url.replace("/download/", "/v/")
22
31
  elif "/file/" in url:
23
32
  return url.replace("/file/", "/v/")
33
+ elif "/embed/" in url:
34
+ return url.replace("/embed/", "/v/")
24
35
  else:
25
36
  return url.replace("/f/", "/v/")
26
37
 
27
38
  async def extract(self, url: str, referer: str = None) -> ExtractResult:
28
- # Dinamik base URL kullan
29
39
  base_url = self.get_base_url(url)
30
-
31
- if referer:
32
- self.httpx.headers.update({"Referer": referer})
33
-
34
40
  self.httpx.headers.update({
35
- "Sec-Fetch-Dest" : "empty",
36
- "Sec-Fetch-Mode" : "cors",
37
- "Sec-Fetch-Site" : "cross-site",
38
- "Origin" : base_url,
41
+ "Referer" : referer or base_url,
42
+ "Origin" : base_url,
39
43
  })
40
44
 
41
45
  embed_url = self.get_embed_url(url)
42
- istek = await self.httpx.get(embed_url)
43
- response = istek.text
46
+ istek = await self.httpx.get(embed_url, follow_redirects=True)
47
+ text = istek.text
48
+
49
+ # Silinmiş dosya kontrolü
50
+ if "File is no longer available" in text or "File Not Found" in text:
51
+ raise ValueError(f"VidHide: Video silinmiş. {url}")
52
+
53
+ # JS Redirect Kontrolü (OneUpload vb.)
54
+ if js_redirect := HTMLHelper(text).regex_first(r"window\.location\.replace\(['\"]([^'\"]+)['\"]\)") or \
55
+ HTMLHelper(text).regex_first(r"window\.location\.href\s*=\s*['\"]([^'\"]+)['\"]"):
56
+ # Redirect url'i al
57
+ target_url = js_redirect
58
+ # Bazen path relative olabilir ama genelde full url
59
+ if not target_url.startswith("http"):
60
+ # urljoin gerekebilir ama şimdilik doğrudan deneyelim veya fix_url
61
+ target_url = self.fix_url(target_url) # fix_url base'e göre düzeltebilir mi? ExtractorBase.fix_url genelde şema ekler.
62
+ pass
63
+
64
+ # Yeniden istek at
65
+ istek = await self.httpx.get(target_url, headers={"Referer": embed_url}, follow_redirects=True)
66
+ text = istek.text
44
67
 
45
- script = None
46
- if "eval(function" in response:
68
+ sel = HTMLHelper(text)
69
+
70
+ unpacked = ""
71
+ # Eval script bul (regex ile daha sağlam)
72
+ if eval_match := sel.regex_first(r'(eval\s*\(\s*function[\s\S]+?)<\/script>'):
47
73
  try:
48
- unpacked = Packer.unpack(response)
74
+ unpacked = Packer.unpack(eval_match)
49
75
  if "var links" in unpacked:
50
- script = unpacked.split("var links")[1]
51
- else:
52
- script = unpacked
53
- except Exception:
76
+ unpacked = unpacked.split("var links")[1]
77
+ except:
54
78
  pass
55
79
 
56
- if not script:
57
- if matches := re.search(r'sources:\s*(\[.*?\])', response, re.DOTALL):
58
- script = matches.group(1)
80
+ content = unpacked or text
59
81
 
60
- m3u8_url = None
61
- if script:
62
- # m3u8 urls could be prefixed by 'file:', 'hls2:' or 'hls4:', so we just match ':'
63
- if match := re.search(r':\s*"([^"]*?m3u8[^"]*?)"', script):
64
- m3u8_url = match.group(1)
82
+ # Regex: Kotlin mantığı (: "url")
83
+ # Ayrıca sources: [...] mantığını da ekle
84
+ m3u8_url = HTMLHelper(content).regex_first(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"')
65
85
 
66
86
  if not m3u8_url:
67
- # Fallback direct search in response if unpacking failed or structure changed
68
- if match := re.search(r'file:"(.*?\.m3u8.*?)"', response):
69
- m3u8_url = match.group(1)
70
-
87
+ # Genel arama (hls:, file: vb.)
88
+ # Kotlin Regex: :\s*"(.*?m3u8.*?)"
89
+ match = HTMLHelper(content).regex_first(r':\s*["\']([^"\']+\.m3u8[^"\']*)["\']')
90
+ if match:
91
+ m3u8_url = match
92
+
93
+ if not m3u8_url:
94
+ # Son şans: herhangi bir m3u8 linki
95
+ m3u8_url = HTMLHelper(content).regex_first(r'["\']([^"\']+\.m3u8[^"\']*)["\']')
96
+
71
97
  if not m3u8_url:
72
98
  raise ValueError(f"VidHide: Video URL bulunamadı. {url}")
73
99
 
@@ -75,6 +101,5 @@ class VidHide(ExtractorBase):
75
101
  name = self.name,
76
102
  url = self.fix_url(m3u8_url),
77
103
  referer = f"{base_url}/",
78
- user_agent = self.httpx.headers.get("User-Agent", ""),
79
- subtitles = []
104
+ user_agent = self.httpx.headers.get("User-Agent", "")
80
105
  )
@@ -1,8 +1,7 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
  # ! https://github.com/recloudstream/cloudstream/blob/master/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidmoly.kt
3
3
 
4
- from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
5
- from selectolax.parser import HTMLParser
4
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
6
5
  import re, contextlib, json
7
6
 
8
7
  class VidMoly(ExtractorBase):
@@ -10,7 +9,7 @@ class VidMoly(ExtractorBase):
10
9
  main_url = "https://vidmoly.to"
11
10
 
12
11
  # Birden fazla domain destekle
13
- supported_domains = ["vidmoly.to", "vidmoly.me", "vidmoly.net"]
12
+ supported_domains = ["vidmoly.to", "vidmoly.me", "vidmoly.net", "vidmoly.biz"]
14
13
 
15
14
  def can_handle_url(self, url: str) -> bool:
16
15
  return any(domain in url for domain in self.supported_domains)
@@ -19,102 +18,81 @@ class VidMoly(ExtractorBase):
19
18
  if referer:
20
19
  self.httpx.headers.update({"Referer": referer})
21
20
 
22
- self.httpx.headers.update({
23
- "Sec-Fetch-Dest" : "iframe",
24
- })
25
-
26
- if ".me" in url:
27
- url = url.replace(".me", ".net")
28
-
29
- # VidMoly bazen redirect ediyor, takip et
30
- response = await self.httpx.get(url, follow_redirects=True)
31
- if "Select number" in response.text:
32
- secici = HTMLParser(response.text)
33
-
34
- op_el = secici.css_first("input[name='op']")
35
- file_code_el = secici.css_first("input[name='file_code']")
36
- answer_el = secici.css_first("div.vhint b")
37
- ts_el = secici.css_first("input[name='ts']")
38
- nonce_el = secici.css_first("input[name='nonce']")
39
- ctok_el = secici.css_first("input[name='ctok']")
40
-
41
- response = await self.httpx.post(
42
- url = url,
43
- data = {
44
- "op" : op_el.attrs.get("value") if op_el else None,
45
- "file_code" : file_code_el.attrs.get("value") if file_code_el else None,
46
- "answer" : answer_el.text(strip=True) if answer_el else None,
47
- "ts" : ts_el.attrs.get("value") if ts_el else None,
48
- "nonce" : nonce_el.attrs.get("value") if nonce_el else None,
49
- "ctok" : ctok_el.attrs.get("value") if ctok_el else None
50
- },
51
- follow_redirects=True
52
- )
53
-
21
+ self.httpx.headers.update({"Sec-Fetch-Dest" : "iframe"})
22
+
23
+ # Domain normalleştirme
24
+ url = url.replace(".me", ".net").replace(".to", ".net")
25
+
26
+ resp = await self.httpx.get(url, follow_redirects=True)
27
+ sel = HTMLHelper(resp.text)
28
+
29
+ # "Select number" kontrolü (Bot koruması)
30
+ if "Select number" in resp.text:
31
+ op_val = sel.select_attr("input[name='op']", "value")
32
+ file_code_val = sel.select_attr("input[name='file_code']", "value")
33
+ answer_val = sel.select_text("div.vhint b")
34
+ ts_val = sel.select_attr("input[name='ts']", "value")
35
+ nonce_val = sel.select_attr("input[name='nonce']", "value")
36
+ ctok_val = sel.select_attr("input[name='ctok']", "value")
37
+
38
+ resp = await self.httpx.post(url, data={
39
+ "op" : op_val,
40
+ "file_code" : file_code_val,
41
+ "answer" : answer_val,
42
+ "ts" : ts_val,
43
+ "nonce" : nonce_val,
44
+ "ctok" : ctok_val
45
+ }, follow_redirects=True)
46
+ sel = HTMLHelper(resp.text)
54
47
 
55
48
  # Altyazı kaynaklarını ayrıştır
56
49
  subtitles = []
57
- if subtitle_match := re.search(r"tracks:\s*\[(.*?)\]", response.text, re.DOTALL):
58
- subtitle_data = self._add_marks(subtitle_match[1], "file")
59
- subtitle_data = self._add_marks(subtitle_data, "label")
60
- subtitle_data = self._add_marks(subtitle_data, "kind")
50
+ if sub_str := sel.regex_first(r"(?s)tracks:\s*\[(.*?)\]"):
51
+ sub_data = self._add_marks(sub_str, "file")
52
+ sub_data = self._add_marks(sub_data, "label")
53
+ sub_data = self._add_marks(sub_data, "kind")
61
54
 
62
55
  with contextlib.suppress(json.JSONDecodeError):
63
- subtitle_sources = json.loads(f"[{subtitle_data}]")
56
+ sub_sources = json.loads(f"[{sub_data}]")
64
57
  subtitles = [
65
- Subtitle(
66
- name = sub.get("label"),
67
- url = self.fix_url(sub.get("file")),
68
- )
69
- for sub in subtitle_sources
70
- if sub.get("kind") == "captions"
58
+ Subtitle(name=sub.get("label"), url=self.fix_url(sub.get("file")))
59
+ for sub in sub_sources if sub.get("kind") == "captions"
71
60
  ]
72
61
 
73
- script_match = re.search(r"sources:\s*\[(.*?)\],", response.text, re.DOTALL)
74
- if script_match:
75
- script_content = script_match[1]
76
- # Video kaynaklarını ayrıştır
77
- video_data = self._add_marks(script_content, "file")
78
- try:
79
- video_sources = json.loads(f"[{video_data}]")
80
- # İlk video kaynağını al
81
- for source in video_sources:
82
- if file_url := source.get("file"):
83
- return ExtractResult(
84
- name = self.name,
85
- url = file_url,
86
- referer = self.main_url,
87
- subtitles = subtitles
88
- )
89
- except json.JSONDecodeError:
90
- pass
91
-
92
- # Fallback: Doğrudan file regex ile ara (Kotlin mantığı)
93
- # file:"..." veya file: "..."
94
- if file_match := re.search(r'file\s*:\s*["\']([^"\']+\.m3u8[^"\']*)["\']', response.text):
95
- return ExtractResult(
96
- name = self.name,
97
- url = file_match.group(1),
98
- referer = self.main_url,
99
- subtitles = subtitles
100
- )
101
-
102
- # Fallback 2: Herhangi bir file (m3u8 olma şartı olmadan ama tercihen)
103
- if file_match := re.search(r'file\s*:\s*["\']([^"\']+)["\']', response.text):
104
- url_candidate = file_match.group(1)
105
- # Resim dosyalarını hariç tut
106
- if not url_candidate.endswith(('.jpg', '.png', '.jpeg')):
107
- return ExtractResult(
108
- name = self.name,
109
- url = url_candidate,
110
- referer = self.main_url,
111
- subtitles = subtitles
112
- )
113
-
114
- raise ValueError("Video URL bulunamadı.")
62
+ # Video URL Bulma
63
+ video_url = None
64
+ if "#EXTM3U" in resp.text:
65
+ for line in resp.text.splitlines():
66
+ if line.strip().startswith("http"):
67
+ video_url = line.strip().replace('"', '').replace("'", "")
68
+ break
69
+
70
+ if not video_url:
71
+ if src_str := sel.regex_first(r"(?s)sources:\s*\[(.*?)\],"):
72
+ vid_data = self._add_marks(src_str, "file")
73
+ with contextlib.suppress(json.JSONDecodeError):
74
+ vid_sources = json.loads(f"[{vid_data}]")
75
+ for source in vid_sources:
76
+ if source.get("file"):
77
+ video_url = source.get("file")
78
+ break
79
+
80
+ if not video_url:
81
+ video_url = sel.regex_first(r'file\s*:\s*["\']([^"\']+\.m3u8[^"\']*)["\']') or \
82
+ sel.regex_first(r'file\s*:\s*["\']([^"\']+\.mp4[^"\']*)["\']')
83
+
84
+ if not video_url:
85
+ raise ValueError(f"VidMoly: Video URL bulunamadı. {url}")
86
+
87
+ return ExtractResult(
88
+ name = self.name,
89
+ url = video_url,
90
+ referer = f"{self.get_base_url(url)}/",
91
+ subtitles = subtitles
92
+ )
115
93
 
116
94
  def _add_marks(self, text: str, field: str) -> str:
117
95
  """
118
96
  Verilen alanı çift tırnak içine alır.
119
97
  """
120
- return re.sub(rf"\"?{field}\"?", f"\"{field}\"", text)
98
+ return HTMLHelper(text).regex_replace(rf"\"?{field}\"?", f"\"{field}\"")
@@ -1,49 +1,37 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
4
4
  from Kekik.Sifreleme import Packer, HexCodec
5
- import re
6
5
 
7
6
  class VidMoxy(ExtractorBase):
8
7
  name = "VidMoxy"
9
8
  main_url = "https://vidmoxy.com"
10
9
 
11
- async def extract(self, url, referer=None) -> ExtractResult:
10
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
12
11
  if referer:
13
12
  self.httpx.headers.update({"Referer": referer})
14
13
 
15
- istek = await self.httpx.get(url)
16
- istek.raise_for_status()
14
+ resp = await self.httpx.get(url)
15
+ sel = HTMLHelper(resp.text)
17
16
 
18
- subtitles = []
19
- subtitle_matches = re.findall(r'captions","file":"([^"]+)","label":"([^"]+)"', istek.text)
20
- seen_subtitles = set()
17
+ subtitles = []
18
+ for s_url, s_lang in sel.regex_all(r'captions","file":"([^\"]+)","label":"([^\"]+)"'):
19
+ decoded_lang = s_lang.encode().decode('unicode_escape')
20
+ subtitles.append(Subtitle(name=decoded_lang, url=s_url.replace("\\", "")))
21
21
 
22
- for sub_url, sub_lang in subtitle_matches:
23
- if sub_url in seen_subtitles:
24
- continue
22
+ hex_data = sel.regex_first(r'file": "(.*)",')
23
+ if not hex_data:
24
+ eval_data = sel.regex_first(r'\};\s*(eval\(function[\s\S]*?)var played = \d+;')
25
+ if eval_data:
26
+ unpacked = Packer.unpack(Packer.unpack(eval_data))
27
+ hex_data = HTMLHelper(unpacked).regex_first(r'file":"(.*)","label')
25
28
 
26
- seen_subtitles.add(sub_url)
27
- decoded_lang = (
28
- sub_lang.replace("\\u0131", "ı")
29
- .replace("\\u0130", "İ")
30
- .replace("\\u00fc", "ü")
31
- .replace("\\u00e7", "ç")
32
- )
33
- subtitles.append(Subtitle(name=decoded_lang, url=sub_url.replace("\\", "")))
34
-
35
- try:
36
- escaped_hex = re.findall(r'file": "(.*)",', istek.text)[0]
37
- except Exception:
38
- eval_jwsetup = re.compile(r'\};\s*(eval\(function[\s\S]*?)var played = \d+;').findall(istek.text)[0]
39
- jwsetup = Packer.unpack(Packer.unpack(eval_jwsetup))
40
- escaped_hex = re.findall(r'file":"(.*)","label', jwsetup)[0]
41
-
42
- m3u_link = HexCodec.decode(escaped_hex)
29
+ if not hex_data:
30
+ raise ValueError(f"VidMoxy: Hex data bulunamadı. {url}")
43
31
 
44
32
  return ExtractResult(
45
33
  name = self.name,
46
- url = m3u_link,
34
+ url = HexCodec.decode(hex_data),
47
35
  referer = self.main_url,
48
36
  subtitles = subtitles
49
37
  )