KekikStream 2.3.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.
Files changed (85) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +3 -2
  2. KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
  3. KekikStream/Core/HTMLHelper.py +120 -49
  4. KekikStream/Core/Plugin/PluginBase.py +30 -12
  5. KekikStream/Core/Plugin/PluginLoader.py +12 -14
  6. KekikStream/Core/Plugin/PluginManager.py +2 -2
  7. KekikStream/Core/Plugin/PluginModels.py +0 -3
  8. KekikStream/Extractors/Abstream.py +27 -0
  9. KekikStream/Extractors/CloseLoad.py +30 -54
  10. KekikStream/Extractors/ContentX.py +27 -72
  11. KekikStream/Extractors/DonilasPlay.py +33 -77
  12. KekikStream/Extractors/DzenRu.py +10 -24
  13. KekikStream/Extractors/ExPlay.py +20 -38
  14. KekikStream/Extractors/Filemoon.py +21 -46
  15. KekikStream/Extractors/HDMomPlayer.py +30 -0
  16. KekikStream/Extractors/HDPlayerSystem.py +13 -31
  17. KekikStream/Extractors/HotStream.py +27 -0
  18. KekikStream/Extractors/JFVid.py +3 -24
  19. KekikStream/Extractors/JetTv.py +21 -34
  20. KekikStream/Extractors/JetV.py +55 -0
  21. KekikStream/Extractors/MailRu.py +11 -29
  22. KekikStream/Extractors/MixPlayHD.py +15 -28
  23. KekikStream/Extractors/MixTiger.py +17 -40
  24. KekikStream/Extractors/MolyStream.py +17 -21
  25. KekikStream/Extractors/Odnoklassniki.py +40 -104
  26. KekikStream/Extractors/PeaceMakerst.py +18 -45
  27. KekikStream/Extractors/PixelDrain.py +8 -16
  28. KekikStream/Extractors/PlayerFilmIzle.py +22 -41
  29. KekikStream/Extractors/RapidVid.py +21 -35
  30. KekikStream/Extractors/SetPlay.py +18 -43
  31. KekikStream/Extractors/SibNet.py +7 -17
  32. KekikStream/Extractors/Sobreatsesuyp.py +23 -45
  33. KekikStream/Extractors/TRsTX.py +23 -53
  34. KekikStream/Extractors/TurboImgz.py +7 -14
  35. KekikStream/Extractors/VCTPlay.py +10 -28
  36. KekikStream/Extractors/Veev.py +145 -0
  37. KekikStream/Extractors/VidBiz.py +62 -0
  38. KekikStream/Extractors/VidHide.py +58 -30
  39. KekikStream/Extractors/VidMoly.py +65 -99
  40. KekikStream/Extractors/VidMoxy.py +16 -27
  41. KekikStream/Extractors/VidPapi.py +24 -54
  42. KekikStream/Extractors/VideoSeyred.py +19 -40
  43. KekikStream/Extractors/Videostr.py +58 -0
  44. KekikStream/Extractors/Vidoza.py +18 -0
  45. KekikStream/Extractors/Vtbe.py +38 -0
  46. KekikStream/Extractors/YTDLP.py +2 -2
  47. KekikStream/Extractors/YildizKisaFilm.py +13 -31
  48. KekikStream/Extractors/Zeus.py +61 -0
  49. KekikStream/Plugins/BelgeselX.py +97 -77
  50. KekikStream/Plugins/DiziBox.py +28 -45
  51. KekikStream/Plugins/DiziMom.py +179 -0
  52. KekikStream/Plugins/DiziPal.py +95 -161
  53. KekikStream/Plugins/DiziYou.py +51 -147
  54. KekikStream/Plugins/Dizilla.py +40 -61
  55. KekikStream/Plugins/FilmBip.py +90 -39
  56. KekikStream/Plugins/FilmEkseni.py +199 -0
  57. KekikStream/Plugins/FilmMakinesi.py +72 -73
  58. KekikStream/Plugins/FilmModu.py +25 -35
  59. KekikStream/Plugins/Filmatek.py +184 -0
  60. KekikStream/Plugins/FilmciBaba.py +155 -0
  61. KekikStream/Plugins/FullHDFilmizlesene.py +16 -37
  62. KekikStream/Plugins/HDFilm.py +243 -0
  63. KekikStream/Plugins/HDFilmCehennemi.py +242 -189
  64. KekikStream/Plugins/JetFilmizle.py +101 -69
  65. KekikStream/Plugins/KultFilmler.py +138 -104
  66. KekikStream/Plugins/RecTV.py +52 -73
  67. KekikStream/Plugins/RoketDizi.py +18 -27
  68. KekikStream/Plugins/SelcukFlix.py +30 -48
  69. KekikStream/Plugins/SetFilmIzle.py +76 -104
  70. KekikStream/Plugins/SezonlukDizi.py +90 -94
  71. KekikStream/Plugins/Sinefy.py +195 -167
  72. KekikStream/Plugins/SinemaCX.py +148 -78
  73. KekikStream/Plugins/Sinezy.py +29 -31
  74. KekikStream/Plugins/SuperFilmGeldi.py +12 -17
  75. KekikStream/Plugins/UgurFilm.py +85 -38
  76. KekikStream/Plugins/Watch32.py +160 -0
  77. KekikStream/Plugins/YabanciDizi.py +176 -211
  78. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/METADATA +1 -1
  79. kekikstream-2.5.3.dist-info/RECORD +99 -0
  80. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/WHEEL +1 -1
  81. KekikStream/Plugins/FullHDFilm.py +0 -249
  82. kekikstream-2.3.9.dist-info/RECORD +0 -84
  83. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/entry_points.txt +0 -0
  84. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/licenses/LICENSE +0 -0
  85. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/top_level.txt +0 -0
@@ -11,55 +11,36 @@ class PlayerFilmIzle(ExtractorBase):
11
11
  return "filmizle.in" in url or "fireplayer" in url.lower()
12
12
 
13
13
  async def extract(self, url: str, referer: str = None) -> ExtractResult:
14
- # Kotlin tarafında referer mainUrl olarak zorlanmış
15
- ext_ref = self.main_url
16
- self.httpx.headers.update({"Referer": ext_ref})
14
+ ref = referer or self.main_url
15
+ self.httpx.headers.update({"Referer": ref})
17
16
 
18
- istek = await self.httpx.get(url)
19
- video_req = istek.text
17
+ resp = await self.httpx.get(url)
18
+ sel = HTMLHelper(resp.text)
20
19
 
21
20
  subtitles = []
22
- hp = HTMLHelper(video_req)
23
- sub_yakala = hp.regex_first(r'(?i)playerjsSubtitle = "([^"]*)"')
24
- if sub_yakala:
25
- # Format örneği: [dil]url
26
- if "]" in sub_yakala:
27
- sub_lang_raw, sub_url = sub_yakala.split("]", 1)
28
- sub_lang = sub_lang_raw.replace("[", "")
29
- subtitles.append(Subtitle(name=sub_lang, url=sub_url))
21
+ if raw_subs := sel.regex_first(r'playerjsSubtitle\s*=\s*"([^"]*)"'):
22
+ for lang, link in HTMLHelper(raw_subs).regex_all(r'\[(.*?)\](https?://[^\s\",]+)'):
23
+ subtitles.append(Subtitle(name=lang.strip(), url=link.strip()))
30
24
 
31
- # Packed script varsa unpack et
32
- unpacked = Packer.unpack(video_req) if Packer.detect_packed(video_req) else video_req
33
-
34
- # Data yakalama: FirePlayer("DATA", ...) formatından
35
- data_val = HTMLHelper(unpacked).regex_first(r'(?i)FirePlayer\s*\(\s*["\']([a-f0-9]+)["\']')
25
+ content = Packer.unpack(resp.text) if Packer.detect_packed(resp.text) else resp.text
26
+ data_val = HTMLHelper(content).regex_first(r'FirePlayer\s*\(\s*["\']([a-f0-9]+)["\']')
36
27
 
37
28
  if not data_val:
38
- raise ValueError("PlayerFilmIzle: Data bulunamadı")
39
-
40
- url_post = f"{self.main_url}/player/index.php?data={data_val}&do=getVideo"
41
-
42
- post_headers = {
43
- "Referer": ext_ref,
44
- "X-Requested-With": "XMLHttpRequest"
45
- }
46
-
47
- # Kotlin'de post data: "hash" -> data, "r" -> ""
48
- post_data = {"hash": data_val, "r": ""}
49
-
50
- response = await self.httpx.post(url_post, data=post_data, headers=post_headers)
51
- get_url = response.text.replace("\\", "")
52
-
53
- m3u8_url = ""
54
- m3u8_url = HTMLHelper(get_url).regex_first(r'(?i)"securedLink":"([^\\"]*)"') or m3u8_url
29
+ raise ValueError(f"PlayerFilmIzle: Data bulunamadı. {url}")
55
30
 
31
+ resp_vid = await self.httpx.post(
32
+ f"{self.main_url}/player/index.php?data={data_val}&do=getVideo",
33
+ data = {"hash": data_val, "r": ""},
34
+ headers = {"X-Requested-With": "XMLHttpRequest"}
35
+ )
36
+
37
+ m3u8_url = HTMLHelper(resp_vid.text).regex_first(r'"securedLink":"([^"]+)"')
56
38
  if not m3u8_url:
57
- raise ValueError("PlayerFilmIzle: M3U8 linki bulunamadı")
39
+ raise ValueError(f"PlayerFilmIzle: Video URL bulunamadı. {url}")
58
40
 
59
41
  return ExtractResult(
60
- name = self.name,
61
- url = m3u8_url,
62
- referer = ext_ref,
63
- user_agent = self.httpx.headers.get("User-Agent", None),
64
- subtitles = subtitles
42
+ name = self.name,
43
+ url = m3u8_url.replace("\\", ""),
44
+ referer = ref,
45
+ subtitles = subtitles
65
46
  )
@@ -14,57 +14,43 @@ class RapidVid(ExtractorBase):
14
14
  def can_handle_url(self, url: str) -> bool:
15
15
  return any(domain in url for domain in self.supported_domains)
16
16
 
17
- async def extract(self, url, referer=None) -> ExtractResult:
17
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
18
18
  if referer:
19
19
  self.httpx.headers.update({"Referer": referer})
20
20
 
21
- istek = await self.httpx.get(url)
22
- istek.raise_for_status()
21
+ resp = await self.httpx.get(url)
22
+ sel = HTMLHelper(resp.text)
23
23
 
24
- subtitles = []
25
- hp = HTMLHelper(istek.text)
26
- subtitle_matches = hp.regex_all(r'captions\",\"file\":\"([^\"]+)\",\"label\":\"([^\"]+)\"')
27
- seen_subtitles = set()
28
-
29
- for sub_url, sub_lang in subtitle_matches:
30
- if sub_url in seen_subtitles:
31
- continue
32
-
33
- seen_subtitles.add(sub_url)
34
- decoded_lang = (
35
- sub_lang.replace("\\u0131", "ı")
36
- .replace("\\u0130", "İ")
37
- .replace("\\u00fc", "ü")
38
- .replace("\\u00e7", "ç")
39
- )
40
- subtitles.append(Subtitle(name=decoded_lang, url=sub_url.replace("\\", "")))
24
+ subtitles = []
25
+ for s_url, s_lang in sel.regex_all(r'captions","file":"([^\"]+)","label":"([^\"]+)"'):
26
+ decoded_lang = s_lang.encode().decode('unicode_escape')
27
+ subtitles.append(Subtitle(name=decoded_lang, url=s_url.replace("\\", "")))
41
28
 
42
29
  try:
43
- decoded_url = None
30
+ video_url = None
44
31
 
45
- # Method 1: file": "..." pattern (HexCodec)
46
- if extracted_value := hp.regex_first(r'file": "(.*)",'):
47
- escaped_hex = extracted_value
48
- decoded_url = HexCodec.decode(escaped_hex)
32
+ # Method 1: HexCodec pattern
33
+ if hex_data := sel.regex_first(r'file": "(.*)",'):
34
+ video_url = HexCodec.decode(hex_data)
49
35
 
50
36
  # Method 2: av('...') pattern
51
- elif av_encoded := hp.regex_first(r"av\('([^']+)'\)"):
52
- decoded_url = self.decode_secret(av_encoded)
37
+ elif av_data := sel.regex_first(r"av\('([^']+)'\)"):
38
+ video_url = self.decode_secret(av_data)
53
39
 
54
- # Method 3: Packed script with dc_* function (StreamDecoder)
55
- elif Packer.detect_packed(istek.text):
56
- unpacked = Packer.unpack(istek.text)
57
- decoded_url = StreamDecoder.extract_stream_url(unpacked)
40
+ # Method 3: Packed dc_*
41
+ elif Packer.detect_packed(resp.text):
42
+ unpacked = Packer.unpack(resp.text)
43
+ video_url = StreamDecoder.extract_stream_url(unpacked)
58
44
 
59
- if not decoded_url:
60
- raise ValueError("No valid video URL pattern found.")
45
+ if not video_url:
46
+ raise ValueError(f"RapidVid: Video URL bulunamadı. {url}")
61
47
 
62
48
  except Exception as hata:
63
- raise RuntimeError(f"Extraction failed: {hata}") from hata
49
+ raise RuntimeError(f"RapidVid: Extraction failed: {hata}") from hata
64
50
 
65
51
  return ExtractResult(
66
52
  name = self.name,
67
- url = decoded_url,
53
+ url = video_url,
68
54
  referer = self.main_url,
69
55
  subtitles = subtitles
70
56
  )
@@ -7,60 +7,35 @@ class SetPlay(ExtractorBase):
7
7
  name = "SetPlay"
8
8
  main_url = "https://setplay.shop"
9
9
 
10
- # Birden fazla domain destekle
11
10
  supported_domains = ["setplay.cfd", "setplay.shop", "setplay.site"]
12
11
 
13
12
  def can_handle_url(self, url: str) -> bool:
14
13
  return any(domain in url for domain in self.supported_domains)
15
14
 
16
- async def extract(self, url, referer=None) -> ExtractResult:
17
- ext_ref = referer or ""
18
-
19
- if referer:
20
- self.httpx.headers.update({"Referer": referer})
21
-
22
- # Dinamik base URL kullan
15
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
16
+ self.httpx.headers.update({"Referer": referer or url})
23
17
  base_url = self.get_base_url(url)
24
18
 
25
- istek = await self.httpx.get(url)
26
- istek.raise_for_status()
27
-
28
- hp = HTMLHelper(istek.text)
19
+ resp = await self.httpx.get(url)
20
+ sel = HTMLHelper(resp.text)
29
21
 
30
- # videoUrl çıkar
31
- video_url = hp.regex_first(r'videoUrl":"([^",]+)"')
32
- if not video_url:
33
- raise ValueError("videoUrl not found")
34
- video_url = video_url.replace("\\", "")
22
+ v_url = sel.regex_first(r'videoUrl":"([^",]+)"')
23
+ v_srv = sel.regex_first(r'videoServer":"([^",]+)"')
24
+ if not v_url or not v_srv:
25
+ raise ValueError(f"SetPlay: Video url/server bulunamadı. {url}")
35
26
 
36
- # videoServer çıkar
37
- video_server = hp.regex_first(r'videoServer":"([^",]+)"')
38
- if not video_server:
39
- raise ValueError("videoServer not found")
40
-
41
- # title çıkar (opsiyonel)
42
- title_base = hp.regex_first(r'title":"([^",]+)"')
43
- title_base = title_base.split(".")[-1] if title_base else "Unknown"
44
-
45
- # partKey logic
46
- parsed = urlparse(url)
47
- params = parse_qs(parsed.query)
48
- part_key = params.get("partKey", [""])[0]
27
+ params = parse_qs(urlparse(url).query)
28
+ part_key = params.get("partKey", [""])[0].lower()
49
29
 
50
- name_suffix = ""
51
- if "turkcedublaj" in part_key.lower():
52
- name_suffix = "Dublaj"
53
- elif "turkcealtyazi" in part_key.lower():
54
- name_suffix = "Altyazı"
30
+ suffix = "Bilinmiyor"
31
+ if "turkcedublaj" in part_key: suffix = "Dublaj"
32
+ elif "turkcealtyazi" in part_key: suffix = "Altyazı"
55
33
  else:
56
- name_suffix = title_base
57
-
58
- # M3U8 link oluştur - base_url kullan (main_url yerine)
59
- m3u_link = f"{base_url}{video_url}?s={video_server}"
34
+ title = sel.regex_first(r'title":"([^",]+)"')
35
+ if title: suffix = title.split(".")[-1]
60
36
 
61
37
  return ExtractResult(
62
- name = f"{self.name} - {name_suffix}",
63
- url = m3u_link,
64
- referer = url,
65
- subtitles = []
38
+ name = f"{self.name} - {suffix}",
39
+ url = f"{base_url}{v_url.replace('\\', '')}?s={v_srv}",
40
+ referer = url
66
41
  )
@@ -6,22 +6,12 @@ class SibNet(ExtractorBase):
6
6
  name = "SibNet"
7
7
  main_url = "https://video.sibnet.ru"
8
8
 
9
- async def extract(self, url, referer=None) -> ExtractResult:
10
- if referer:
11
- self.httpx.headers.update({"Referer": referer})
9
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
10
+ self.httpx.headers.update({"Referer": referer or url})
12
11
 
13
- response = await self.httpx.get(url)
14
- response.raise_for_status()
12
+ resp = await self.httpx.get(url)
13
+ path = HTMLHelper(resp.text).regex_first(r'player\.src\(\[\{src: "([^\"]+)"')
14
+ if not path:
15
+ raise ValueError(f"SibNet: Video yolu bulunamadı. {url}")
15
16
 
16
- m3u_suffix = HTMLHelper(response.text).regex_first(r'player\.src\(\[\{src: "([^\"]+)"')
17
- if not m3u_suffix:
18
- raise ValueError("m3u bağlantısı bulunamadı.")
19
-
20
- m3u_link = f"{self.main_url}{m3u_suffix}"
21
-
22
- return ExtractResult(
23
- name = self.name,
24
- url = m3u_link,
25
- referer = url,
26
- subtitles = []
27
- )
17
+ return ExtractResult(name=self.name, url=f"{self.main_url}{path}", referer=url)
@@ -7,53 +7,31 @@ class Sobreatsesuyp(ExtractorBase):
7
7
  name = "Sobreatsesuyp"
8
8
  main_url = "https://sobreatsesuyp.com"
9
9
 
10
- async def extract(self, url, referer=None) -> ExtractResult:
11
- if referer:
12
- self.httpx.headers.update({"Referer": referer})
10
+ async def extract(self, url: str, referer: str = None) -> list[ExtractResult] | ExtractResult:
11
+ ref = referer or self.main_url
12
+ self.httpx.headers.update({"Referer": ref})
13
13
 
14
- istek = await self.httpx.get(url)
15
- istek.raise_for_status()
14
+ resp = await self.httpx.get(url)
15
+ path = HTMLHelper(resp.text).regex_first(r'file":"([^\"]+)')
16
+ if not path:
17
+ raise ValueError(f"Sobreatsesuyp: File path bulunamadı. {url}")
16
18
 
17
- file_path = HTMLHelper(istek.text).regex_first(r'file":"([^\"]+)')
18
- if not file_path:
19
- raise ValueError("File not found in response.")
19
+ post_resp = await self.httpx.post(f"{self.main_url}/{path.replace('\\', '')}")
20
+ data_list = post_resp.json()[1:] if isinstance(post_resp.json(), list) else []
20
21
 
21
- file_path = file_path.replace("\\", "")
22
- post_link = f"{self.main_url}/{file_path}"
23
-
24
- post_istek = await self.httpx.post(post_link)
25
- post_istek.raise_for_status()
26
-
27
- try:
28
- post_json = json.loads(post_istek.text)
29
- except json.JSONDecodeError as hata:
30
- raise ValueError("Failed to parse JSON response.") from hata
31
-
32
- video_data_list = post_json[1:] if isinstance(post_json, list) else []
33
-
34
- all_results = []
35
-
36
- for item in video_data_list:
22
+ results = []
23
+ for item in data_list:
37
24
  title = item.get("title")
38
25
  file = item.get("file")
39
-
40
- if not title or not file:
41
- continue
42
-
43
- playlist_url = f"{self.main_url}/playlist/{file.lstrip('/')}.txt"
44
- playlist_request = await self.httpx.post(playlist_url, headers={"Referer": referer or self.main_url})
45
- playlist_request.raise_for_status()
46
-
47
- all_results.append(
48
- ExtractResult(
49
- name = f"{self.name} - {title}",
50
- url = playlist_request.text,
51
- referer = self.main_url,
52
- subtitles = []
53
- )
54
- )
55
-
56
- if not all_results:
57
- raise ValueError("No videos found in response.")
58
-
59
- return all_results[0] if len(all_results) == 1 else all_results
26
+ if title and file:
27
+ playlist_resp = await self.httpx.post(f"{self.main_url}/playlist/{file.lstrip('/')}.txt")
28
+ results.append(ExtractResult(
29
+ name = f"{self.name} - {title}",
30
+ url = playlist_resp.text,
31
+ referer = self.main_url
32
+ ))
33
+
34
+ if not results:
35
+ raise ValueError(f"Sobreatsesuyp: Video bulunamadı. {url}")
36
+
37
+ return results[0] if len(results) == 1 else results
@@ -7,61 +7,31 @@ class TRsTX(ExtractorBase):
7
7
  name = "TRsTX"
8
8
  main_url = "https://trstx.org"
9
9
 
10
- async def extract(self, url, referer=None) -> list[ExtractResult]:
11
- if referer:
12
- self.httpx.headers.update({"Referer": referer})
10
+ async def extract(self, url: str, referer: str = None) -> list[ExtractResult] | ExtractResult:
11
+ ref = referer or self.main_url
12
+ self.httpx.headers.update({"Referer": ref})
13
13
 
14
- istek = await self.httpx.get(url)
15
- istek.raise_for_status()
14
+ resp = await self.httpx.get(url)
15
+ path = HTMLHelper(resp.text).regex_first(r'file":"([^\"]+)')
16
+ if not path:
17
+ raise ValueError(f"TRsTX: File path bulunamadı. {url}")
16
18
 
17
- file_path = HTMLHelper(istek.text).regex_first(r'file":"([^\"]+)')
18
- if not file_path:
19
- raise ValueError("File not found in response.")
19
+ post_resp = await self.httpx.post(f"{self.main_url}/{path.replace('\\', '')}")
20
+ data_list = post_resp.json()[1:] if isinstance(post_resp.json(), list) else []
20
21
 
21
- file_path = file_path.replace("\\", "")
22
- post_link = f"{self.main_url}/{file_path}"
23
-
24
- post_istek = await self.httpx.post(post_link)
25
- post_istek.raise_for_status()
26
-
27
- try:
28
- post_json = json.loads(post_istek.text)
29
- except json.JSONDecodeError as hata:
30
- raise ValueError("Failed to parse JSON response.") from hata
31
-
32
- video_data_list = post_json[1:] if isinstance(post_json, list) else []
33
-
34
- video_links = set()
35
- all_results = []
36
-
37
- for item in video_data_list:
22
+ results = []
23
+ for item in data_list:
38
24
  title = item.get("title")
39
25
  file = item.get("file")
40
-
41
- if not title or not file:
42
- continue
43
-
44
- playlist_url = f"{self.main_url}/playlist/{file.lstrip('/')}.txt"
45
- playlist_request = await self.httpx.post(playlist_url, headers={"Referer": referer or self.main_url})
46
- playlist_request.raise_for_status()
47
-
48
- video_data = playlist_request.text
49
-
50
- if video_data in video_links:
51
- continue
52
-
53
- video_links.add(video_data)
54
-
55
- all_results.append(
56
- ExtractResult(
57
- name = f"{self.name} - {title}",
58
- url = video_data,
59
- referer = self.main_url,
60
- subtitles = []
61
- )
62
- )
63
-
64
- if not all_results:
65
- raise ValueError("No videos found in response.")
66
-
67
- return all_results[0] if len(all_results) == 1 else all_results
26
+ if title and file:
27
+ playlist_resp = await self.httpx.post(f"{self.main_url}/playlist/{file.lstrip('/')}.txt")
28
+ results.append(ExtractResult(
29
+ name = f"{self.name} - {title}",
30
+ url = playlist_resp.text,
31
+ referer = self.main_url
32
+ ))
33
+
34
+ if not results:
35
+ raise ValueError(f"TRsTX: Video bulunamadı. {url}")
36
+
37
+ return results[0] if len(results) == 1 else results
@@ -6,19 +6,12 @@ class TurboImgz(ExtractorBase):
6
6
  name = "TurboImgz"
7
7
  main_url = "https://turbo.imgz.me"
8
8
 
9
- async def extract(self, url, referer=None) -> ExtractResult:
10
- if referer:
11
- self.httpx.headers.update({"Referer": referer})
9
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
10
+ self.httpx.headers.update({"Referer": referer or url})
12
11
 
13
- istek = await self.httpx.get(url)
14
- istek.raise_for_status()
12
+ resp = await self.httpx.get(url)
13
+ v_url = HTMLHelper(resp.text).regex_first(r'file: "(.*)",')
14
+ if not v_url:
15
+ raise ValueError(f"TurboImgz: Video bulunamadı. {url}")
15
16
 
16
- if video := HTMLHelper(istek.text).regex_first(r'file: "(.*)",'):
17
- return ExtractResult(
18
- name = self.name,
19
- url = video,
20
- referer = referer or self.main_url,
21
- subtitles = []
22
- )
23
- else:
24
- raise ValueError("File not found in response.")
17
+ return ExtractResult(name=self.name, url=v_url, referer=referer or self.main_url)
@@ -7,35 +7,17 @@ class VCTPlay(ExtractorBase):
7
7
  name = "VCTPlay"
8
8
  main_url = "https://vctplay.site"
9
9
 
10
- async def extract(self, url, referer=None) -> ExtractResult:
11
- if referer:
12
- self.httpx.headers.update({"Referer": referer})
10
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
11
+ v_id = url.split("/")[-1].split("?")[0]
12
+ params = parse_qs(urlparse(url).query)
13
+ part_key = params.get("partKey", [""])[0].lower()
13
14
 
14
- # URL'den video ID'sini çıkar
15
- # https://vctplay.site/video/2hjDGco5exdv -> 2hjDGco5exdv
16
- video_id = url.split("/")[-1]
17
- if "?" in video_id:
18
- video_id = video_id.split("?")[0]
19
-
20
- # Manifests URL oluştur
21
- master_url = f"{self.main_url}/manifests/{video_id}/master.txt"
22
-
23
- # partKey'den isim belirle
24
- parsed = urlparse(url)
25
- params = parse_qs(parsed.query)
26
- part_key = params.get("partKey", [""])[0]
27
-
28
- name_suffix = ""
29
- if "turkcedublaj" in part_key.lower():
30
- name_suffix = "Dublaj"
31
- elif "turkcealtyazi" in part_key.lower():
32
- name_suffix = "Altyazı"
33
-
34
- display_name = f"{self.name} - {name_suffix}" if name_suffix else self.name
15
+ suffix = ""
16
+ if "turkcedublaj" in part_key: suffix = "Dublaj"
17
+ elif "turkcealtyazi" in part_key: suffix = "Altyazı"
35
18
 
36
19
  return ExtractResult(
37
- name = display_name,
38
- url = master_url,
39
- referer = f"{self.main_url}/",
40
- subtitles = []
20
+ name = f"{self.name} - {suffix}" if suffix else self.name,
21
+ url = f"{self.main_url}/manifests/{v_id}/master.txt",
22
+ referer = f"{self.main_url}/"
41
23
  )
@@ -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}")