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
@@ -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, Subtitle
4
+ import re, json
5
+
6
+ class PlayerFilmIzle(ExtractorBase):
7
+ name = "PlayerFilmIzle"
8
+ main_url = "https://player.filmizle.in"
9
+
10
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
11
+ # Kotlin tarafında referer mainUrl olarak zorlanmış
12
+ ext_ref = self.main_url
13
+ self.httpx.headers.update({"Referer": ext_ref})
14
+
15
+ istek = await self.httpx.get(url)
16
+ video_req = istek.text
17
+
18
+ subtitles = []
19
+ if sub_match := re.search(r'playerjsSubtitle = "([^"]*)"', video_req, re.IGNORECASE):
20
+ sub_yakala = sub_match.group(1)
21
+ # Format örneği: [dil]url
22
+ # Kotlin kodunda: subYakala.substringAfter("]") -> url
23
+ # subYakala.substringBefore("]").removePrefix("[") -> lang
24
+ if "]" in sub_yakala:
25
+ sub_lang_raw, sub_url = sub_yakala.split("]", 1)
26
+ sub_lang = sub_lang_raw.replace("[", "")
27
+ subtitles.append(Subtitle(name=sub_lang, url=sub_url))
28
+
29
+ # Data yakalama: FirePlayer|DATA|...
30
+ data_match = re.search(r'FirePlayer\|([^|]+)\|', video_req, re.IGNORECASE)
31
+ data_val = data_match.group(1) if data_match else None
32
+
33
+ if not data_val:
34
+ raise ValueError("PlayerFilmIzle: Data bulunamadı")
35
+
36
+ url_post = f"{self.main_url}/player/index.php?data={data_val}&do=getVideo"
37
+
38
+ post_headers = {
39
+ "Referer": ext_ref,
40
+ "X-Requested-With": "XMLHttpRequest"
41
+ }
42
+
43
+ # Kotlin'de post data: "hash" -> data, "r" -> ""
44
+ post_data = {"hash": data_val, "r": ""}
45
+
46
+ response = await self.httpx.post(url_post, data=post_data, headers=post_headers)
47
+ get_url = response.text.replace("\\", "")
48
+
49
+ m3u8_url = ""
50
+ if url_yakala := re.search(r'"securedLink":"([^"]*)"', get_url, re.IGNORECASE):
51
+ m3u8_url = url_yakala.group(1)
52
+
53
+ if not m3u8_url:
54
+ raise ValueError("PlayerFilmIzle: M3U8 linki bulunamadı")
55
+
56
+ return ExtractResult(
57
+ name = self.name,
58
+ url = m3u8_url,
59
+ referer = ext_ref,
60
+ user_agent = self.httpx.headers.get("User-Agent", None),
61
+ subtitles = subtitles
62
+ )
@@ -2,7 +2,7 @@
2
2
 
3
3
  from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
4
  from Kekik.Sifreleme import Packer, HexCodec
5
- import re
5
+ import re, base64
6
6
 
7
7
  class RapidVid(ExtractorBase):
8
8
  name = "RapidVid"
@@ -37,24 +37,41 @@ class RapidVid(ExtractorBase):
37
37
  escaped_hex = extracted_value[1]
38
38
  decoded_url = HexCodec.decode(escaped_hex)
39
39
  else:
40
- eval_jwsetup = re.search(r'\};\s*(eval\(function[\s\S]*?)var played = \d+;', istek.text)
41
- if not eval_jwsetup:
42
- raise ValueError("JWPlayer setup not found.")
40
+ av_encoded = re.search(r"av\('([^']+)'\)", istek.text)
41
+ if not av_encoded:
42
+ raise ValueError("AV encoding not found.")
43
43
 
44
- unpacked_jwsetup = Packer.unpack(Packer.unpack(eval_jwsetup[1]))
45
- extracted_value = re.search(r'file":"(.*)","label', unpacked_jwsetup)
46
- if not extracted_value:
47
- raise ValueError("File URL not found in unpacked JWPlayer setup.")
48
-
49
- escaped_hex = extracted_value[1].replace("\\\\x", "")
50
- decoded_url = bytes.fromhex(escaped_hex).decode("utf-8")
44
+ decoded_url = self.decode_secret(av_encoded[1])
51
45
  except Exception as hata:
52
46
  raise RuntimeError(f"Extraction failed: {hata}") from hata
53
47
 
54
- await self.close()
55
48
  return ExtractResult(
56
49
  name = self.name,
57
50
  url = decoded_url,
58
51
  referer = self.main_url,
59
52
  subtitles = subtitles
60
- )
53
+ )
54
+
55
+ def decode_secret(self, encoded_string: str) -> str:
56
+ # 1. Base64 ile şifrelenmiş string ters çevrilmiş, önce geri çeviriyoruz
57
+ reversed_input = encoded_string[::-1]
58
+
59
+ # 2. İlk base64 çözme işlemi
60
+ decoded_once = base64.b64decode(reversed_input).decode("utf-8")
61
+
62
+ decrypted_chars = []
63
+ key = "K9L"
64
+
65
+ # 3. Key'e göre karakter kaydırma geri alınıyor
66
+ for index, encoded_char in enumerate(decoded_once):
67
+ key_char = key[index % len(key)]
68
+ offset = (ord(key_char) % 5) + 1 # Her karakter için dinamik offset
69
+
70
+ original_char_code = ord(encoded_char) - offset
71
+ decrypted_chars.append(chr(original_char_code))
72
+
73
+ # 4. Karakterleri birleştirip ikinci base64 çözme işlemini yapıyoruz
74
+ intermediate_string = "".join(decrypted_chars)
75
+ final_decoded_bytes = base64.b64decode(intermediate_string)
76
+
77
+ return final_decoded_bytes.decode("utf-8")
@@ -0,0 +1,7 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Extractors.RapidVid import RapidVid
4
+
5
+ class RapidFilm(RapidVid):
6
+ name = "RapidFilm"
7
+ main_url = "https://rapid.filmmakinesi.to"
@@ -0,0 +1,57 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult
4
+ import re
5
+
6
+ class SetPlay(ExtractorBase):
7
+ name = "SetPlay"
8
+ main_url = "https://setplay.cfd"
9
+
10
+ async def extract(self, url, referer=None) -> ExtractResult:
11
+ ext_ref = referer or ""
12
+
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
+ # videoUrl çıkar
20
+ video_url_match = re.search(r'videoUrl":"([^",]+)"', istek.text)
21
+ if not video_url_match:
22
+ raise ValueError("videoUrl not found")
23
+ video_url = video_url_match[1].replace("\\", "")
24
+
25
+ # videoServer çıkar
26
+ video_server_match = re.search(r'videoServer":"([^",]+)"', istek.text)
27
+ if not video_server_match:
28
+ raise ValueError("videoServer not found")
29
+ video_server = video_server_match[1]
30
+
31
+ # title çıkar (opsiyonel)
32
+ title_match = re.search(r'title":"([^",]+)"', istek.text)
33
+ title_base = title_match[1].split(".")[-1] if title_match else "Unknown"
34
+
35
+ # partKey logic
36
+ from urllib.parse import urlparse, parse_qs
37
+ parsed = urlparse(url)
38
+ params = parse_qs(parsed.query)
39
+ part_key = params.get("partKey", [""])[0]
40
+
41
+ name_suffix = ""
42
+ if "turkcedublaj" in part_key.lower():
43
+ name_suffix = "Dublaj"
44
+ elif "turkcealtyazi" in part_key.lower():
45
+ name_suffix = "Altyazı"
46
+ else:
47
+ name_suffix = title_base
48
+
49
+ # M3U8 link oluştur
50
+ m3u_link = f"{self.main_url}{video_url}?s={video_server}"
51
+
52
+ return ExtractResult(
53
+ name = f"{self.name} - {name_suffix}",
54
+ url = m3u_link,
55
+ referer = url,
56
+ subtitles = []
57
+ )
@@ -0,0 +1,45 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult
4
+ from urllib.parse import urlparse, parse_qs
5
+ import re
6
+
7
+ class SetPrime(ExtractorBase):
8
+ name = "SetPrime"
9
+ main_url = "https://setplay.site"
10
+
11
+ async def extract(self, url, referer=None) -> ExtractResult:
12
+ # URL parsing
13
+ parsed = urlparse(url)
14
+ params = parse_qs(parsed.query)
15
+ part_key = params.get("partKey", [""])[0].upper()
16
+ clean_url = url.split("?partKey=")[0]
17
+
18
+ # POST URL: embed?i= -> embed/get?i=
19
+ post_url = clean_url.replace("embed?i=", "embed/get?i=")
20
+
21
+ response = await self.httpx.post(
22
+ url = post_url,
23
+ headers = {"Referer": clean_url}
24
+ )
25
+ response.raise_for_status()
26
+
27
+ # Links parse
28
+ links_match = re.search(r'Links":\["([^"\]]+)"', response.text)
29
+ if not links_match:
30
+ raise ValueError("Links not found in SetPrime response")
31
+
32
+ link_suffix = links_match.group(1)
33
+ if not link_suffix.startswith("/"):
34
+ raise ValueError("Links not valid (must start with /)")
35
+
36
+ m3u_link = f"{self.main_url}{link_suffix}"
37
+
38
+ display_name = f"{self.name} - {part_key}" if part_key else self.name
39
+
40
+ return ExtractResult(
41
+ name = display_name,
42
+ url = m3u_link,
43
+ referer = clean_url,
44
+ subtitles = []
45
+ )
@@ -20,7 +20,6 @@ class SibNet(ExtractorBase):
20
20
 
21
21
  m3u_link = f"{self.main_url}{match[1]}"
22
22
 
23
- await self.close()
24
23
  return ExtractResult(
25
24
  name = self.name,
26
25
  url = m3u_link,
@@ -0,0 +1,34 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
+ import re, json
5
+
6
+ class TurkeyPlayer(ExtractorBase):
7
+ name = "TurkeyPlayer"
8
+ main_url = "https://watch.turkeyplayer.com/"
9
+
10
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
11
+ if referer:
12
+ self.httpx.headers.update({"Referer": referer})
13
+
14
+ istek = await self.httpx.get(url)
15
+ page_content = istek.text
16
+
17
+ video_json_match = re.search(r'var\s+video\s*=\s*(\{.*?\});', page_content, re.DOTALL)
18
+ if not video_json_match:
19
+ raise ValueError("TurkeyPlayer: Video JSON bulunamadı")
20
+
21
+ video_data = json.loads(video_json_match.group(1))
22
+
23
+ video_id = video_data.get("id")
24
+ video_md5 = video_data.get("md5")
25
+
26
+ master_url = f"https://watch.turkeyplayer.com/m3u8/8/{video_md5}/master.txt?s=1&id={video_id}&cache=1"
27
+
28
+ return ExtractResult(
29
+ name = self.name,
30
+ url = master_url,
31
+ referer = referer or url,
32
+ user_agent = self.httpx.headers.get("User-Agent", ""),
33
+ subtitles = []
34
+ )
@@ -0,0 +1,72 @@
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
5
+ from parsel import Selector
6
+ import re
7
+
8
+ class VidHide(ExtractorBase):
9
+ name = "VidHide"
10
+ main_url = "https://vidhidepro.com"
11
+
12
+ def get_embed_url(self, url: str) -> str:
13
+ if "/d/" in url:
14
+ return url.replace("/d/", "/v/")
15
+ elif "/download/" in url:
16
+ return url.replace("/download/", "/v/")
17
+ elif "/file/" in url:
18
+ return url.replace("/file/", "/v/")
19
+ else:
20
+ return url.replace("/f/", "/v/")
21
+
22
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
23
+ if referer:
24
+ self.httpx.headers.update({"Referer": referer})
25
+
26
+ self.httpx.headers.update({
27
+ "Sec-Fetch-Dest" : "empty",
28
+ "Sec-Fetch-Mode" : "cors",
29
+ "Sec-Fetch-Site" : "cross-site",
30
+ "Origin" : self.main_url,
31
+ })
32
+
33
+ embed_url = self.get_embed_url(url)
34
+ istek = await self.httpx.get(embed_url)
35
+ response = istek.text
36
+
37
+ script = None
38
+ if "eval(function" in response:
39
+ try:
40
+ unpacked = Packer.unpack(response)
41
+ if "var links" in unpacked:
42
+ script = unpacked.split("var links")[1]
43
+ else:
44
+ script = unpacked
45
+ except Exception:
46
+ pass
47
+
48
+ if not script:
49
+ if matches := re.search(r'sources:\s*(\[.*?\])', response, re.DOTALL):
50
+ script = matches.group(1)
51
+
52
+ m3u8_url = None
53
+ if script:
54
+ # m3u8 urls could be prefixed by 'file:', 'hls2:' or 'hls4:', so we just match ':'
55
+ if match := re.search(r':\s*"([^"]*?m3u8[^"]*?)"', script):
56
+ m3u8_url = match.group(1)
57
+
58
+ if not m3u8_url:
59
+ # Fallback direct search in response if unpacking failed or structure changed
60
+ if match := re.search(r'file:"(.*?\.m3u8.*?)"', response):
61
+ m3u8_url = match.group(1)
62
+
63
+ if not m3u8_url:
64
+ raise ValueError(f"VidHide: Video URL bulunamadı. {url}")
65
+
66
+ return ExtractResult(
67
+ name = self.name,
68
+ url = self.fix_url(m3u8_url),
69
+ referer = f"{self.main_url}/",
70
+ user_agent = self.httpx.headers.get("User-Agent", ""),
71
+ subtitles = []
72
+ )
@@ -2,7 +2,8 @@
2
2
  # ! https://github.com/recloudstream/cloudstream/blob/master/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidmoly.kt
3
3
 
4
4
  from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
5
- import re, asyncio, contextlib, json
5
+ from parsel import Selector
6
+ import re, contextlib, json
6
7
 
7
8
  class VidMoly(ExtractorBase):
8
9
  name = "VidMoly"
@@ -13,29 +14,30 @@ class VidMoly(ExtractorBase):
13
14
  self.httpx.headers.update({"Referer": referer})
14
15
 
15
16
  self.httpx.headers.update({
16
- "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
17
17
  "Sec-Fetch-Dest" : "iframe",
18
18
  })
19
19
 
20
20
  if self.main_url.endswith(".me"):
21
- self.main_url = self.main_url.replace(".me", ".to")
22
- url = url.replace(".me", ".to")
21
+ self.main_url = self.main_url.replace(".me", ".net")
22
+ url = url.replace(".me", ".net")
23
23
 
24
- # Embed URL oluştur
25
- embed_url = url.replace("/w/", "/embed-") + "-920x360.html" if "/w/" in url else url
26
- script_content = None
27
- attempts = 0
24
+ response = await self.httpx.get(url)
25
+ if "Select number" in response.text:
26
+ secici = Selector(response.text)
27
+ response = await self.httpx.post(
28
+ url = url,
29
+ data = {
30
+ "op" : secici.css("input[name='op']::attr(value)").get(),
31
+ "file_code" : secici.css("input[name='file_code']::attr(value)").get(),
32
+ "answer" : secici.css("div.vhint b::text").get(),
33
+ "ts" : secici.css("input[name='ts']::attr(value)").get(),
34
+ "nonce" : secici.css("input[name='nonce']::attr(value)").get(),
35
+ "ctok" : secici.css("input[name='ctok']::attr(value)").get()
36
+ }
37
+ )
28
38
 
29
- # Script verisini almak için deneme yap
30
- while attempts < 10 and not script_content:
31
- attempts += 1
32
- response = await self.httpx.get(embed_url)
33
- response.raise_for_status()
34
-
35
- script_match = re.search(r"sources:\s*\[(.*?)\],", response.text, re.DOTALL)
36
- script_content = script_match[1] if script_match else None
37
- if not script_content:
38
- await asyncio.sleep(0.5)
39
+ script_match = re.search(r"sources:\s*\[(.*?)\],", response.text, re.DOTALL)
40
+ script_content = script_match[1] if script_match else None
39
41
 
40
42
  if not script_content:
41
43
  raise ValueError("Gerekli script bulunamadı.")
@@ -74,7 +76,6 @@ class VidMoly(ExtractorBase):
74
76
  if not video_url:
75
77
  raise ValueError("Video URL bulunamadı.")
76
78
 
77
- await self.close()
78
79
  return ExtractResult(
79
80
  name = self.name,
80
81
  url = video_url,
@@ -4,4 +4,4 @@ from KekikStream.Extractors.VidMoly import VidMoly
4
4
 
5
5
  class VidMolyMe(VidMoly):
6
6
  name = "VidMolyMe"
7
- main_url = "https://vidmoly.me"
7
+ main_url = "https://vidmoly.me"
@@ -41,7 +41,6 @@ class VidMoxy(ExtractorBase):
41
41
 
42
42
  m3u_link = HexCodec.decode(escaped_hex)
43
43
 
44
- await self.close()
45
44
  return ExtractResult(
46
45
  name = self.name,
47
46
  url = m3u_link,
@@ -0,0 +1,89 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
+ import re
5
+
6
+ class VidPapi(ExtractorBase):
7
+ name = "VidApi"
8
+ main_url = "https://vidpapi.xyz"
9
+
10
+ async def extract(self, url, referer=None) -> ExtractResult:
11
+ ext_ref = referer or ""
12
+
13
+ # URL parsing
14
+ if "video/" in url:
15
+ vid_id = url.split("video/")[-1]
16
+ else:
17
+ vid_id = url.split("?data=")[-1]
18
+
19
+ # 1. Altyazıları çek
20
+ sub_url = f"{self.main_url}/player/index.php?data={vid_id}"
21
+ sub_headers = {
22
+ "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8",
23
+ "X-Requested-With" : "XMLHttpRequest",
24
+ "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0",
25
+ "Referer" : ext_ref or "https://kultfilmler.pro/"
26
+ }
27
+
28
+ subtitles = []
29
+ try:
30
+ sub_istek = await self.httpx.post(
31
+ url = sub_url,
32
+ headers = sub_headers,
33
+ data = {"hash": vid_id, "r": "https://kultfilmler.pro/"}
34
+ )
35
+
36
+ subtitle_match = re.search(r'var playerjsSubtitle = "([^"]*)"', sub_istek.text, re.IGNORECASE)
37
+ if subtitle_match and subtitle_match.group(1):
38
+ raw_subs = subtitle_match.group(1)
39
+
40
+ found_subs = re.findall(r'\[(.*?)\](.*?)(?:,|$)', raw_subs)
41
+ for lang, sub_link in found_subs:
42
+ lang = lang.strip()
43
+ if "Türkçe" in lang:
44
+ lang_code = "tr"
45
+ lang_name = "Turkish"
46
+ elif "İngilizce" in lang:
47
+ lang_code = "en"
48
+ lang_name = "English"
49
+ else:
50
+ lang_code = lang[:2].lower()
51
+ lang_name = lang
52
+
53
+ subtitles.append(Subtitle(
54
+ name = lang_name,
55
+ url = sub_link.strip()
56
+ ))
57
+
58
+ except Exception as e:
59
+ pass
60
+
61
+ # 2. Videoyu çek
62
+ video_url = f"{self.main_url}/player/index.php?data={vid_id}&do=getVideo"
63
+ video_headers = sub_headers.copy()
64
+
65
+ response = await self.httpx.post(
66
+ url = video_url,
67
+ headers = video_headers,
68
+ data = {"hash": vid_id, "r": "https://kultfilmler.pro/"}
69
+ )
70
+ response.raise_for_status()
71
+
72
+ try:
73
+ video_data = response.json()
74
+ except Exception:
75
+ return None
76
+
77
+ stream_url = video_data.get("securedLink")
78
+ if not stream_url or not stream_url.strip():
79
+ stream_url = video_data.get("videoSource")
80
+
81
+ if not stream_url:
82
+ raise ValueError("No video link found in VidPapi response")
83
+
84
+ return ExtractResult(
85
+ name = self.name,
86
+ url = stream_url,
87
+ referer = ext_ref or self.main_url,
88
+ subtitles = subtitles
89
+ )