KekikStream 2.0.9__tar.gz → 2.2.0__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 (87) hide show
  1. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Extractor/ExtractorBase.py +7 -2
  2. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Plugin/PluginBase.py +61 -13
  3. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/CloseLoad.py +17 -2
  4. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/ContentX.py +6 -2
  5. kekikstream-2.2.0/KekikStream/Extractors/Filemoon.py +78 -0
  6. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/MixTiger.py +5 -5
  7. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/RapidVid.py +15 -5
  8. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/SetPlay.py +6 -3
  9. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VidHide.py +11 -2
  10. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/BelgeselX.py +7 -15
  11. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/DiziBox.py +8 -12
  12. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/DiziPal.py +11 -22
  13. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/DiziYou.py +9 -17
  14. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/Dizilla.py +4 -7
  15. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/FilmBip.py +5 -8
  16. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/FilmMakinesi.py +30 -20
  17. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/FilmModu.py +8 -16
  18. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/FullHDFilm.py +83 -27
  19. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/FullHDFilmizlesene.py +5 -7
  20. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/HDFilmCehennemi.py +85 -84
  21. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/JetFilmizle.py +28 -13
  22. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/KultFilmler.py +12 -14
  23. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/RecTV.py +16 -24
  24. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/RoketDizi.py +6 -9
  25. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SelcukFlix.py +71 -51
  26. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SetFilmIzle.py +24 -31
  27. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SezonlukDizi.py +22 -9
  28. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SineWix.py +13 -21
  29. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/Sinefy.py +6 -6
  30. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SinemaCX.py +34 -33
  31. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/Sinezy.py +5 -8
  32. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SuperFilmGeldi.py +33 -30
  33. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/UgurFilm.py +6 -8
  34. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/__init__.py +17 -36
  35. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/PKG-INFO +6 -3
  36. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/SOURCES.txt +1 -0
  37. {kekikstream-2.0.9 → kekikstream-2.2.0}/PKG-INFO +6 -3
  38. {kekikstream-2.0.9 → kekikstream-2.2.0}/README.md +5 -2
  39. {kekikstream-2.0.9 → kekikstream-2.2.0}/setup.py +1 -1
  40. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/CLI/__init__.py +0 -0
  41. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/CLI/pypi_kontrol.py +0 -0
  42. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
  43. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
  44. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Extractor/ExtractorModels.py +0 -0
  45. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Extractor/YTDLPCache.py +0 -0
  46. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Media/MediaHandler.py +0 -0
  47. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Media/MediaManager.py +0 -0
  48. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
  49. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Plugin/PluginManager.py +0 -0
  50. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Plugin/PluginModels.py +0 -0
  51. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/UI/UIManager.py +0 -0
  52. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/__init__.py +0 -0
  53. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/DonilasPlay.py +0 -0
  54. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/DzenRu.py +0 -0
  55. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/ExPlay.py +0 -0
  56. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/HDPlayerSystem.py +0 -0
  57. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/JetTv.py +0 -0
  58. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/MailRu.py +0 -0
  59. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/MixPlayHD.py +0 -0
  60. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/MolyStream.py +0 -0
  61. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/Odnoklassniki.py +0 -0
  62. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/PeaceMakerst.py +0 -0
  63. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/PixelDrain.py +0 -0
  64. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/PlayerFilmIzle.py +0 -0
  65. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/SetPrime.py +0 -0
  66. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/SibNet.py +0 -0
  67. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/Sobreatsesuyp.py +0 -0
  68. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/TRsTX.py +0 -0
  69. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/TauVideo.py +0 -0
  70. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/TurboImgz.py +0 -0
  71. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/TurkeyPlayer.py +0 -0
  72. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VCTPlay.py +0 -0
  73. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VidMoly.py +0 -0
  74. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VidMoxy.py +0 -0
  75. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VidPapi.py +0 -0
  76. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VideoSeyred.py +0 -0
  77. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/YTDLP.py +0 -0
  78. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/YildizKisaFilm.py +0 -0
  79. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/__main__.py +0 -0
  80. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/requirements.txt +0 -0
  81. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/dependency_links.txt +0 -0
  82. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/entry_points.txt +0 -0
  83. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/requires.txt +0 -0
  84. {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/top_level.txt +0 -0
  85. {kekikstream-2.0.9 → kekikstream-2.2.0}/LICENSE +0 -0
  86. {kekikstream-2.0.9 → kekikstream-2.2.0}/MANIFEST.in +0 -0
  87. {kekikstream-2.0.9 → kekikstream-2.2.0}/setup.cfg +0 -0
@@ -5,7 +5,7 @@ from cloudscraper import CloudScraper
5
5
  from httpx import AsyncClient
6
6
  from typing import Optional
7
7
  from .ExtractorModels import ExtractResult
8
- from urllib.parse import urljoin
8
+ from urllib.parse import urljoin, urlparse
9
9
 
10
10
  class ExtractorBase(ABC):
11
11
  # Çıkarıcının temel özellikleri
@@ -29,6 +29,11 @@ class ExtractorBase(ABC):
29
29
  # URL'nin bu çıkarıcı tarafından işlenip işlenemeyeceğini kontrol et
30
30
  return self.main_url in url
31
31
 
32
+ def get_base_url(self, url: str) -> str:
33
+ """URL'den base URL'i çıkar (scheme + netloc)"""
34
+ parsed = urlparse(url)
35
+ return f"{parsed.scheme}://{parsed.netloc}"
36
+
32
37
  @abstractmethod
33
38
  async def extract(self, url: str, referer: Optional[str] = None) -> ExtractResult:
34
39
  # Alt sınıflar tarafından uygulanacak medya çıkarma fonksiyonu
@@ -46,4 +51,4 @@ class ExtractorBase(ABC):
46
51
  if url.startswith("http") or url.startswith("{\""):
47
52
  return url
48
53
 
49
- return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
54
+ return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
@@ -1,11 +1,13 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
+ from ...CLI import konsol
3
4
  from abc import ABC, abstractmethod
4
5
  from cloudscraper import CloudScraper
5
6
  from httpx import AsyncClient
6
- from .PluginModels import MainPageResult, SearchResult, MovieInfo
7
+ from .PluginModels import MainPageResult, SearchResult, MovieInfo, SeriesInfo
7
8
  from ..Media.MediaHandler import MediaHandler
8
9
  from ..Extractor.ExtractorManager import ExtractorManager
10
+ from ..Extractor.ExtractorModels import ExtractResult
9
11
  from urllib.parse import urljoin
10
12
  import re
11
13
 
@@ -53,31 +55,31 @@ class PluginBase(ABC):
53
55
  pass
54
56
 
55
57
  @abstractmethod
56
- async def load_item(self, url: str) -> MovieInfo:
58
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
57
59
  """Bir medya öğesi hakkında detaylı bilgi döndürür."""
58
60
  pass
59
61
 
60
62
  @abstractmethod
61
- async def load_links(self, url: str) -> list[dict]:
63
+ async def load_links(self, url: str) -> list[ExtractResult]:
62
64
  """
63
65
  Bir medya öğesi için oynatma bağlantılarını döndürür.
64
-
66
+
65
67
  Args:
66
68
  url: Medya URL'si
67
-
69
+
68
70
  Returns:
69
- Dictionary listesi, her biri şu alanları içerir:
71
+ ExtractResult listesi, her biri şu alanları içerir:
70
72
  - url (str, zorunlu): Video URL'si
71
73
  - name (str, zorunlu): Gösterim adı (tüm bilgileri içerir)
72
74
  - referer (str, opsiyonel): Referer header
73
- - subtitles (list, opsiyonel): Altyazı listesi
74
-
75
+ - subtitles (list[Subtitle], opsiyonel): Altyazı listesi
76
+
75
77
  Example:
76
78
  [
77
- {
78
- "url": "https://example.com/video.m3u8",
79
- "name": "HDFilmCehennemi | 1080p TR Dublaj"
80
- }
79
+ ExtractResult(
80
+ url="https://example.com/video.m3u8",
81
+ name="HDFilmCehennemi | 1080p TR Dublaj"
82
+ )
81
83
  ]
82
84
  """
83
85
  pass
@@ -95,6 +97,40 @@ class PluginBase(ABC):
95
97
 
96
98
  return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
97
99
 
100
+ async def extract(self, url: str, referer: str = None, prefix: str | None = None) -> ExtractResult | None:
101
+ """
102
+ Extractor ile video URL'sini çıkarır.
103
+
104
+ Args:
105
+ url: Iframe veya video URL'si
106
+ referer: Referer header (varsayılan: plugin main_url)
107
+ prefix: İsmin başına eklenecek opsiyonel etiket (örn: "Türkçe Dublaj")
108
+
109
+ Returns:
110
+ ExtractResult: Extractor sonucu (name prefix ile birleştirilmiş) veya None
111
+
112
+ Extractor bulunamadığında veya hata oluştuğunda uyarı verir.
113
+ """
114
+ if referer is None:
115
+ referer = f"{self.main_url}/"
116
+
117
+ extractor = self.ex_manager.find_extractor(url)
118
+ if not extractor:
119
+ konsol.log(f"[magenta][?] {self.name} » Extractor bulunamadı: {url}")
120
+ return None
121
+
122
+ try:
123
+ data = await extractor.extract(url, referer=referer)
124
+
125
+ # prefix varsa name'e ekle
126
+ if prefix and data.name:
127
+ data.name = f"{prefix} | {data.name}"
128
+
129
+ return data
130
+ except Exception as hata:
131
+ konsol.log(f"[red][!] {self.name} » Extractor hatası ({extractor.name}): {hata}")
132
+ return None
133
+
98
134
  @staticmethod
99
135
  def clean_title(title: str) -> str:
100
136
  suffixes = [
@@ -119,4 +155,16 @@ class PluginBase(ABC):
119
155
  for suffix in suffixes:
120
156
  cleaned_title = re.sub(f"{re.escape(suffix)}.*$", "", cleaned_title, flags=re.IGNORECASE).strip()
121
157
 
122
- return cleaned_title
158
+ return cleaned_title
159
+
160
+ async def play(self, **kwargs):
161
+ """
162
+ Varsayılan oynatma metodu.
163
+ Tüm pluginlerde ortak kullanılır.
164
+ """
165
+ extract_result = ExtractResult(**kwargs)
166
+ self.media_handler.title = kwargs.get("name")
167
+ if self.name not in self.media_handler.title:
168
+ self.media_handler.title = f"{self.name} | {self.media_handler.title}"
169
+
170
+ self.media_handler.play_media(extract_result)
@@ -1,7 +1,8 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import ExtractorBase, ExtractResult
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
4
  from Kekik.Sifreleme import Packer, StreamDecoder
5
+ from parsel import Selector
5
6
  import re
6
7
 
7
8
  class CloseLoadExtractor(ExtractorBase):
@@ -15,12 +16,26 @@ class CloseLoadExtractor(ExtractorBase):
15
16
  istek = await self.httpx.get(url)
16
17
  istek.raise_for_status()
17
18
 
19
+ # Video URL'sini çıkar
18
20
  eval_func = re.compile(r'\s*(eval\(function[\s\S].*)\s*').findall(istek.text)[0]
19
21
  m3u_link = StreamDecoder.extract_stream_url(Packer.unpack(eval_func))
20
22
 
23
+ # Subtitle'ları parse et (Kotlin referansı: track elementleri)
24
+ subtitles = []
25
+ secici = Selector(istek.text)
26
+ for track in secici.css("track"):
27
+ raw_src = track.css("::attr(src)").get() or ""
28
+ raw_src = raw_src.strip()
29
+ label = track.css("::attr(label)").get() or track.css("::attr(srclang)").get() 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
+
21
35
  return ExtractResult(
22
36
  name = self.name,
23
37
  url = m3u_link,
24
38
  referer = self.main_url,
25
- subtitles = []
39
+ subtitles = subtitles
26
40
  )
41
+
@@ -24,6 +24,9 @@ class ContentX(ExtractorBase):
24
24
  if referer:
25
25
  self.httpx.headers.update({"Referer": referer})
26
26
 
27
+ # Dinamik base URL kullan
28
+ base_url = self.get_base_url(url)
29
+
27
30
  istek = await self.httpx.get(url)
28
31
  istek.raise_for_status()
29
32
  i_source = istek.text
@@ -52,7 +55,8 @@ class ContentX(ExtractorBase):
52
55
  )
53
56
  )
54
57
 
55
- vid_source_request = await self.httpx.get(f"{self.main_url}/source2.php?v={i_extract_value}", headers={"Referer": referer or self.main_url})
58
+ # base_url kullan (contentx.me yerine)
59
+ vid_source_request = await self.httpx.get(f"{base_url}/source2.php?v={i_extract_value}", headers={"Referer": referer or base_url})
56
60
  vid_source_request.raise_for_status()
57
61
 
58
62
  vid_source = vid_source_request.text
@@ -72,7 +76,7 @@ class ContentX(ExtractorBase):
72
76
 
73
77
  if i_dublaj := re.search(r',\"([^"]+)\",\"Türkçe"', i_source):
74
78
  dublaj_value = i_dublaj[1]
75
- dublaj_source_request = await self.httpx.get(f"{self.main_url}/source2.php?v={dublaj_value}", headers={"Referer": referer or self.main_url})
79
+ dublaj_source_request = await self.httpx.get(f"{base_url}/source2.php?v={dublaj_value}", headers={"Referer": referer or base_url})
76
80
  dublaj_source_request.raise_for_status()
77
81
 
78
82
  dublaj_source = dublaj_source_request.text
@@ -0,0 +1,78 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult
4
+ from Kekik.Sifreleme import Packer
5
+ from parsel import Selector
6
+ import re
7
+
8
+ class Filemoon(ExtractorBase):
9
+ name = "Filemoon"
10
+ main_url = "https://filemoon.to"
11
+
12
+ # Filemoon'un farklı domainlerini destekle
13
+ supported_domains = [
14
+ "filemoon.to",
15
+ "filemoon.in",
16
+ "filemoon.sx",
17
+ "filemoon.nl",
18
+ "filemoon.com"
19
+ ]
20
+
21
+ def can_handle_url(self, url: str) -> bool:
22
+ return any(domain in url for domain in self.supported_domains)
23
+
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",
30
+ }
31
+ self.httpx.headers.update(headers)
32
+
33
+ # İlk sayfayı al
34
+ istek = await self.httpx.get(url)
35
+ response = istek.text
36
+ secici = Selector(response)
37
+
38
+ # Eğer iframe varsa, iframe'e git
39
+ iframe_src = secici.css("iframe::attr(src)").get()
40
+ if iframe_src:
41
+ iframe_url = self.fix_url(iframe_src)
42
+ self.httpx.headers.update({
43
+ "Accept-Language" : "en-US,en;q=0.5",
44
+ "Sec-Fetch-Dest" : "iframe"
45
+ })
46
+ istek = await self.httpx.get(iframe_url)
47
+ response = istek.text
48
+
49
+ # Packed script'i bul ve unpack et
50
+ m3u8_url = None
51
+
52
+ if Packer.detect_packed(response):
53
+ try:
54
+ unpacked = Packer.unpack(response)
55
+ # sources:[{file:"..." pattern'ını ara
56
+ if match := re.search(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"', unpacked):
57
+ m3u8_url = match.group(1)
58
+ elif match := re.search(r'file:\s*"([^"]*?\.m3u8[^"]*)"', unpacked):
59
+ m3u8_url = match.group(1)
60
+ except Exception:
61
+ pass
62
+
63
+ # Fallback: Doğrudan response'ta ara
64
+ if not m3u8_url:
65
+ if match := re.search(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"', response):
66
+ m3u8_url = match.group(1)
67
+ elif match := re.search(r'file:\s*"([^"]*?\.m3u8[^"]*)"', response):
68
+ m3u8_url = match.group(1)
69
+
70
+ if not m3u8_url:
71
+ raise ValueError(f"Filemoon: Video URL bulunamadı. {url}")
72
+
73
+ return ExtractResult(
74
+ name = self.name,
75
+ url = self.fix_url(m3u8_url),
76
+ referer = f"{self.main_url}/",
77
+ subtitles = []
78
+ )
@@ -37,16 +37,15 @@ class MixTiger(ExtractorBase):
37
37
  if not m3u_link:
38
38
  raise ValueError("Video URL not found in response")
39
39
 
40
- # Recursive extraction check
40
+ # Recursive extraction check - başka extractor kullanılabilir mi?
41
41
  try:
42
42
  from KekikStream.Core.Extractor.ExtractorManager import ExtractorManager
43
- # Import inside method to avoid circular dependency
44
43
  manager = ExtractorManager()
45
44
  if nested_extractor := manager.find_extractor(m3u_link):
46
- # Use recursive extraction
47
- return await nested_extractor.extract(m3u_link, referer=final_referer)
45
+ # Nested extractor ile çıkar
46
+ return await nested_extractor.extract(m3u_link, referer=ext_ref)
48
47
  except Exception:
49
- # If recursion fails, fallback to standard result
48
+ # Recursive extraction başarısız olursa standart sonucu döndür
50
49
  pass
51
50
 
52
51
  return ExtractResult(
@@ -55,3 +54,4 @@ class MixTiger(ExtractorBase):
55
54
  referer = None if "disk.yandex" in m3u_link else ext_ref,
56
55
  subtitles = []
57
56
  )
57
+
@@ -1,7 +1,7 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
3
  from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
- from Kekik.Sifreleme import Packer, HexCodec
4
+ from Kekik.Sifreleme import Packer, HexCodec, StreamDecoder
5
5
  import re, base64
6
6
 
7
7
  class RapidVid(ExtractorBase):
@@ -39,15 +39,25 @@ class RapidVid(ExtractorBase):
39
39
  subtitles.append(Subtitle(name=decoded_lang, url=sub_url.replace("\\", "")))
40
40
 
41
41
  try:
42
+ decoded_url = None
43
+
44
+ # Method 1: file": "..." pattern (HexCodec)
42
45
  if extracted_value := re.search(r'file": "(.*)",', istek.text):
43
46
  escaped_hex = extracted_value[1]
44
47
  decoded_url = HexCodec.decode(escaped_hex)
45
- else:
46
- av_encoded = re.search(r"av\('([^']+)'\)", istek.text)
47
- if not av_encoded:
48
- raise ValueError("AV encoding not found.")
49
48
 
49
+ # Method 2: av('...') pattern
50
+ elif av_encoded := re.search(r"av\('([^']+)'\)", istek.text):
50
51
  decoded_url = self.decode_secret(av_encoded[1])
52
+
53
+ # Method 3: Packed script with dc_* function (StreamDecoder)
54
+ elif Packer.detect_packed(istek.text):
55
+ unpacked = Packer.unpack(istek.text)
56
+ decoded_url = StreamDecoder.extract_stream_url(unpacked)
57
+
58
+ if not decoded_url:
59
+ raise ValueError("No valid video URL pattern found.")
60
+
51
61
  except Exception as hata:
52
62
  raise RuntimeError(f"Extraction failed: {hata}") from hata
53
63
 
@@ -1,6 +1,7 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
3
  from KekikStream.Core import ExtractorBase, ExtractResult
4
+ from urllib.parse import urlparse, parse_qs
4
5
  import re
5
6
 
6
7
  class SetPlay(ExtractorBase):
@@ -19,6 +20,9 @@ class SetPlay(ExtractorBase):
19
20
  if referer:
20
21
  self.httpx.headers.update({"Referer": referer})
21
22
 
23
+ # Dinamik base URL kullan
24
+ base_url = self.get_base_url(url)
25
+
22
26
  istek = await self.httpx.get(url)
23
27
  istek.raise_for_status()
24
28
 
@@ -39,7 +43,6 @@ class SetPlay(ExtractorBase):
39
43
  title_base = title_match[1].split(".")[-1] if title_match else "Unknown"
40
44
 
41
45
  # partKey logic
42
- from urllib.parse import urlparse, parse_qs
43
46
  parsed = urlparse(url)
44
47
  params = parse_qs(parsed.query)
45
48
  part_key = params.get("partKey", [""])[0]
@@ -52,8 +55,8 @@ class SetPlay(ExtractorBase):
52
55
  else:
53
56
  name_suffix = title_base
54
57
 
55
- # M3U8 link oluştur
56
- m3u_link = f"{self.main_url}{video_url}?s={video_server}"
58
+ # M3U8 link oluştur - base_url kullan (main_url yerine)
59
+ m3u_link = f"{base_url}{video_url}?s={video_server}"
57
60
 
58
61
  return ExtractResult(
59
62
  name = f"{self.name} - {name_suffix}",
@@ -9,6 +9,12 @@ class VidHide(ExtractorBase):
9
9
  name = "VidHide"
10
10
  main_url = "https://vidhidepro.com"
11
11
 
12
+ # Birden fazla domain destekle
13
+ supported_domains = ["vidhidepro.com", "vidhide.com", "rubyvidhub.com"]
14
+
15
+ def can_handle_url(self, url: str) -> bool:
16
+ return any(domain in url for domain in self.supported_domains)
17
+
12
18
  def get_embed_url(self, url: str) -> str:
13
19
  if "/d/" in url:
14
20
  return url.replace("/d/", "/v/")
@@ -20,6 +26,9 @@ class VidHide(ExtractorBase):
20
26
  return url.replace("/f/", "/v/")
21
27
 
22
28
  async def extract(self, url: str, referer: str = None) -> ExtractResult:
29
+ # Dinamik base URL kullan
30
+ base_url = self.get_base_url(url)
31
+
23
32
  if referer:
24
33
  self.httpx.headers.update({"Referer": referer})
25
34
 
@@ -27,7 +36,7 @@ class VidHide(ExtractorBase):
27
36
  "Sec-Fetch-Dest" : "empty",
28
37
  "Sec-Fetch-Mode" : "cors",
29
38
  "Sec-Fetch-Site" : "cross-site",
30
- "Origin" : self.main_url,
39
+ "Origin" : base_url,
31
40
  })
32
41
 
33
42
  embed_url = self.get_embed_url(url)
@@ -66,7 +75,7 @@ class VidHide(ExtractorBase):
66
75
  return ExtractResult(
67
76
  name = self.name,
68
77
  url = self.fix_url(m3u8_url),
69
- referer = f"{self.main_url}/",
78
+ referer = f"{base_url}/",
70
79
  user_agent = self.httpx.headers.get("User-Agent", ""),
71
80
  subtitles = []
72
81
  )
@@ -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 PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
4
4
  from parsel import Selector
5
5
  import re
6
6
 
@@ -164,7 +164,7 @@ class BelgeselX(PluginBase):
164
164
  episodes = episodes
165
165
  )
166
166
 
167
- async def load_links(self, url: str) -> list[dict]:
167
+ async def load_links(self, url: str) -> list[ExtractResult]:
168
168
  istek = await self.httpx.get(url)
169
169
  text = istek.text
170
170
 
@@ -187,18 +187,10 @@ class BelgeselX(PluginBase):
187
187
  source_name = "Google" if quality == "FULL" else self.name
188
188
  quality_str = "1080p" if quality == "FULL" else quality
189
189
 
190
- links.append({
191
- "url" : video_url,
192
- "name" : f"{source_name} | {quality_str}",
193
- "referer" : url
194
- })
190
+ links.append(ExtractResult(
191
+ url = video_url,
192
+ name = f"{source_name} | {quality_str}",
193
+ referer = url
194
+ ))
195
195
 
196
196
  return links
197
-
198
- async def play(self, **kwargs):
199
- extract_result = ExtractResult(**kwargs)
200
- self.media_handler.title = kwargs.get("name")
201
- if self.name not in self.media_handler.title:
202
- self.media_handler.title = f"{self.name} | {self.media_handler.title}"
203
-
204
- self.media_handler.play_media(extract_result)
@@ -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 PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
4
4
  from Kekik.Sifreleme import CryptoJS
5
5
  from parsel import Selector
6
6
  import re, urllib.parse, base64, contextlib, asyncio, time
@@ -174,7 +174,7 @@ class DiziBox(PluginBase):
174
174
 
175
175
  return results
176
176
 
177
- async def load_links(self, url: str) -> list[dict]:
177
+ async def load_links(self, url: str) -> list[ExtractResult]:
178
178
  istek = await self.httpx.get(url)
179
179
  secici = Selector(istek.text)
180
180
 
@@ -182,11 +182,9 @@ class DiziBox(PluginBase):
182
182
  if main_iframe := secici.css("div#video-area iframe::attr(src)").get():
183
183
  if decoded := await self._iframe_decode(self.name, main_iframe, url):
184
184
  for iframe in decoded:
185
- extractor = self.ex_manager.find_extractor(iframe)
186
- results.append({
187
- "url" : iframe,
188
- "name" : f"{extractor.name if extractor else 'Main Player'}"
189
- })
185
+ data = await self.extract(iframe)
186
+ if data:
187
+ results.append(data)
190
188
 
191
189
  for alternatif in secici.css("div.video-toolbar option[value]"):
192
190
  alt_name = alternatif.css("::text").get()
@@ -203,10 +201,8 @@ class DiziBox(PluginBase):
203
201
  if alt_iframe := alt_secici.css("div#video-area iframe::attr(src)").get():
204
202
  if decoded := await self._iframe_decode(alt_name, alt_iframe, url):
205
203
  for iframe in decoded:
206
- extractor = self.ex_manager.find_extractor(iframe)
207
- results.append({
208
- "url" : iframe,
209
- "name" : f"{extractor.name if extractor else alt_name}"
210
- })
204
+ data = await self.extract(iframe, prefix=alt_name)
205
+ if data:
206
+ results.append(data)
211
207
 
212
208
  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 PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, Subtitle
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, Subtitle, ExtractResult
4
4
  from parsel import Selector
5
5
  import re
6
6
 
@@ -177,7 +177,7 @@ class DiziPal(PluginBase):
177
177
  duration = duration,
178
178
  )
179
179
 
180
- async def load_links(self, url: str) -> list[dict]:
180
+ async def load_links(self, url: str) -> list[ExtractResult]:
181
181
  # Reset headers to get HTML response
182
182
  self.httpx.headers.update({
183
183
  "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
@@ -220,27 +220,16 @@ class DiziPal(PluginBase):
220
220
  sub_url = sub_text.replace(f"[{lang}]", "")
221
221
  subtitles.append(Subtitle(name=lang, url=self.fix_url(sub_url)))
222
222
 
223
- results.append({
224
- "name" : self.name,
225
- "url" : m3u_link,
226
- "referer" : f"{self.main_url}/",
227
- "subtitles" : subtitles
228
- })
223
+ results.append(ExtractResult(
224
+ name = self.name,
225
+ url = m3u_link,
226
+ referer = f"{self.main_url}/",
227
+ subtitles = subtitles
228
+ ))
229
229
  else:
230
230
  # Extractor'a yönlendir
231
- extractor = self.ex_manager.find_extractor(iframe)
232
- results.append({
233
- "name" : f"{extractor.name if extractor else self.name}",
234
- "url" : iframe,
235
- "referer" : f"{self.main_url}/",
236
- })
231
+ data = await self.extract(iframe)
232
+ if data:
233
+ results.append(data)
237
234
 
238
235
  return results
239
-
240
- async def play(self, **kwargs):
241
- extract_result = ExtractResult(**kwargs)
242
- self.media_handler.title = kwargs.get("name")
243
- if self.name not in self.media_handler.title:
244
- self.media_handler.title = f"{self.name} | {self.media_handler.title}"
245
-
246
- self.media_handler.play_media(extract_result)
@@ -123,7 +123,7 @@ class DiziYou(PluginBase):
123
123
  actors = actors
124
124
  )
125
125
 
126
- async def load_links(self, url: str) -> list[dict]:
126
+ async def load_links(self, url: str) -> list[ExtractResult]:
127
127
  istek = await self.httpx.get(url)
128
128
  secici = Selector(istek.text)
129
129
 
@@ -179,19 +179,11 @@ class DiziYou(PluginBase):
179
179
 
180
180
  results = []
181
181
  for stream in stream_urls:
182
- results.append({
183
- "url" : stream.get("url"),
184
- "name" : f"{stream.get('dil')}",
185
- "referer" : url,
186
- "subtitles" : subtitles
187
- })
188
-
189
- return results
190
-
191
- async def play(self, **kwargs):
192
- extract_result = ExtractResult(**kwargs)
193
- self.media_handler.title = kwargs.get("name")
194
- if self.name not in self.media_handler.title:
195
- self.media_handler.title = f"{self.name} | {self.media_handler.title}"
196
-
197
- self.media_handler.play_media(extract_result)
182
+ results.append(ExtractResult(
183
+ url = stream.get("url"),
184
+ name = f"{stream.get('dil')}",
185
+ referer = url,
186
+ subtitles = subtitles
187
+ ))
188
+
189
+ 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 PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
4
4
  from parsel import Selector
5
5
  from json import loads
6
6
  from urllib.parse import urlparse, urlunparse
@@ -170,7 +170,7 @@ class Dizilla(PluginBase):
170
170
  actors = actors
171
171
  )
172
172
 
173
- async def load_links(self, url: str) -> list[dict]:
173
+ async def load_links(self, url: str) -> list[ExtractResult]:
174
174
  istek = await self.httpx.get(url)
175
175
  secici = Selector(istek.text)
176
176
 
@@ -196,8 +196,5 @@ class Dizilla(PluginBase):
196
196
  if not iframe_url:
197
197
  return []
198
198
 
199
- extractor = self.ex_manager.find_extractor(iframe_url)
200
- return [{
201
- "url" : iframe_url,
202
- "name" : f"{extractor.name if extractor else 'Main Player'} | {first_result.get('language_name', 'Unknown')}",
203
- }]
199
+ data = await self.extract(iframe_url, prefix=first_result.get('language_name', 'Unknown'))
200
+ return [data] if data else []