KekikStream 1.7.2__tar.gz → 1.8.1__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 (91) hide show
  1. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Extractor/ExtractorBase.py +23 -6
  2. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Extractor/ExtractorModels.py +5 -7
  3. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Media/MediaHandler.py +16 -20
  4. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Media/MediaManager.py +0 -3
  5. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Plugin/PluginBase.py +29 -8
  6. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Plugin/PluginModels.py +25 -26
  7. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/CloseLoad.py +3 -4
  8. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/ContentX.py +4 -6
  9. kekikstream-1.8.1/KekikStream/Extractors/DzenRu.py +38 -0
  10. kekikstream-1.8.1/KekikStream/Extractors/ExPlay.py +53 -0
  11. kekikstream-1.8.1/KekikStream/Extractors/FirePlayer.py +60 -0
  12. kekikstream-1.8.1/KekikStream/Extractors/HDPlayerSystem.py +41 -0
  13. kekikstream-1.8.1/KekikStream/Extractors/JetTv.py +45 -0
  14. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/MailRu.py +3 -4
  15. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/MixPlayHD.py +2 -3
  16. kekikstream-1.8.1/KekikStream/Extractors/MixTiger.py +57 -0
  17. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/MolyStream.py +5 -5
  18. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/Odnoklassniki.py +7 -7
  19. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/PeaceMakerst.py +4 -5
  20. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/PixelDrain.py +1 -2
  21. kekikstream-1.8.1/KekikStream/Extractors/PlayerFilmIzle.py +62 -0
  22. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/RapidVid.py +2 -3
  23. kekikstream-1.8.1/KekikStream/Extractors/SetPlay.py +57 -0
  24. kekikstream-1.8.1/KekikStream/Extractors/SetPrime.py +45 -0
  25. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/SibNet.py +2 -3
  26. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/Sobreatsesuyp.py +4 -5
  27. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/TRsTX.py +4 -5
  28. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/TauVideo.py +2 -3
  29. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/TurboImgz.py +2 -3
  30. kekikstream-1.8.1/KekikStream/Extractors/TurkeyPlayer.py +34 -0
  31. kekikstream-1.8.1/KekikStream/Extractors/VidHide.py +72 -0
  32. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/VidMoly.py +4 -5
  33. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/VidMoxy.py +2 -3
  34. kekikstream-1.8.1/KekikStream/Extractors/VidPapi.py +89 -0
  35. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/VideoSeyred.py +3 -4
  36. kekikstream-1.8.1/KekikStream/Extractors/YildizKisaFilm.py +41 -0
  37. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/DiziBox.py +16 -16
  38. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/DiziPal.py +15 -15
  39. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/DiziYou.py +7 -7
  40. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/Dizilla.py +10 -6
  41. kekikstream-1.8.1/KekikStream/Plugins/FilmBip.py +145 -0
  42. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/FilmMakinesi.py +3 -3
  43. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/FilmModu.py +9 -9
  44. kekikstream-1.8.1/KekikStream/Plugins/FullHDFilm.py +164 -0
  45. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/FullHDFilmizlesene.py +3 -3
  46. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/HDFilmCehennemi.py +6 -3
  47. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/JetFilmizle.py +16 -9
  48. kekikstream-1.8.1/KekikStream/Plugins/KultFilmler.py +219 -0
  49. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/RecTV.py +17 -17
  50. kekikstream-1.8.1/KekikStream/Plugins/RoketDizi.py +181 -0
  51. kekikstream-1.8.1/KekikStream/Plugins/SelcukFlix.py +216 -0
  52. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/SezonlukDizi.py +11 -8
  53. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/SineWix.py +7 -7
  54. kekikstream-1.8.1/KekikStream/Plugins/Sinefy.py +214 -0
  55. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/SinemaCX.py +13 -13
  56. kekikstream-1.8.1/KekikStream/Plugins/Sinezy.py +99 -0
  57. kekikstream-1.8.1/KekikStream/Plugins/SuperFilmGeldi.py +121 -0
  58. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/UgurFilm.py +6 -6
  59. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/__init__.py +34 -24
  60. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/requirements.txt +2 -2
  61. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/PKG-INFO +3 -2
  62. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/SOURCES.txt +21 -0
  63. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/requires.txt +1 -0
  64. {kekikstream-1.7.2 → kekikstream-1.8.1}/PKG-INFO +3 -2
  65. {kekikstream-1.7.2 → kekikstream-1.8.1}/setup.py +3 -2
  66. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/CLI/__init__.py +0 -0
  67. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/CLI/pypi_kontrol.py +0 -0
  68. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
  69. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
  70. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
  71. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Plugin/PluginManager.py +0 -0
  72. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/UI/UIManager.py +0 -0
  73. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/__init__.py +0 -0
  74. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/FourCX.py +0 -0
  75. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/FourPichive.py +0 -0
  76. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/FourPlayRu.py +0 -0
  77. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/HDStreamAble.py +0 -0
  78. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/Hotlinger.py +0 -0
  79. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/OkRuHTTP.py +0 -0
  80. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/OkRuSSL.py +0 -0
  81. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/Pichive.py +0 -0
  82. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/PlayRu.py +0 -0
  83. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/VidMolyMe.py +0 -0
  84. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/__main__.py +0 -0
  85. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/dependency_links.txt +0 -0
  86. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/entry_points.txt +0 -0
  87. {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/top_level.txt +0 -0
  88. {kekikstream-1.7.2 → kekikstream-1.8.1}/LICENSE +0 -0
  89. {kekikstream-1.7.2 → kekikstream-1.8.1}/MANIFEST.in +0 -0
  90. {kekikstream-1.7.2 → kekikstream-1.8.1}/README.md +0 -0
  91. {kekikstream-1.7.2 → kekikstream-1.8.1}/setup.cfg +0 -0
@@ -3,6 +3,7 @@
3
3
  from abc import ABC, abstractmethod
4
4
  from curl_cffi import AsyncSession
5
5
  from cloudscraper import CloudScraper
6
+ from httpx import AsyncClient
6
7
  from typing import Optional
7
8
  from .ExtractorModels import ExtractResult
8
9
  from urllib.parse import urljoin
@@ -11,13 +12,27 @@ class ExtractorBase(ABC):
11
12
  # Çıkarıcının temel özellikleri
12
13
  name = "Extractor"
13
14
  main_url = ""
15
+ requires_cffi = False
14
16
 
15
17
  def __init__(self):
16
- # HTTP istekleri için oturum oluştur
17
- self.cffi = AsyncSession(impersonate="firefox135")
18
+ # cloudscraper - for bypassing Cloudflare
18
19
  self.cloudscraper = CloudScraper()
19
- self.cffi.cookies.update(self.cloudscraper.cookies)
20
- self.cffi.headers.update({"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 15.7; rv:135.0) Gecko/20100101 Firefox/135.0"})
20
+
21
+ # httpx - lightweight and safe for most HTTP requests
22
+ self.httpx = AsyncClient(
23
+ timeout = 3,
24
+ follow_redirects = True,
25
+ )
26
+ self.httpx.headers.update(self.cloudscraper.headers)
27
+ self.httpx.cookies.update(self.cloudscraper.cookies)
28
+
29
+ # curl_cffi - only initialize if needed for anti-bot bypass
30
+ self.cffi = None
31
+
32
+ if self.requires_cffi:
33
+ self.cffi = AsyncSession(impersonate="firefox135")
34
+ self.cffi.cookies.update(self.cloudscraper.cookies)
35
+ self.cffi.headers.update({"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 15.7; rv:135.0) Gecko/20100101 Firefox/135.0"})
21
36
 
22
37
  def can_handle_url(self, url: str) -> bool:
23
38
  # URL'nin bu çıkarıcı tarafından işlenip işlenemeyeceğini kontrol et
@@ -29,8 +44,10 @@ class ExtractorBase(ABC):
29
44
  pass
30
45
 
31
46
  async def close(self):
32
- # HTTP oturumunu güvenli bir şekilde kapat
33
- await self.cffi.close()
47
+ """Close both HTTP clients if they exist."""
48
+ await self.httpx.aclose()
49
+ if self.cffi:
50
+ await self.cffi.close()
34
51
 
35
52
  def fix_url(self, url: str) -> str:
36
53
  # Eksik URL'leri düzelt ve tam URL formatına çevir
@@ -1,8 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
3
  from pydantic import BaseModel
4
- from typing import List, Optional
5
-
6
4
 
7
5
  class Subtitle(BaseModel):
8
6
  """Altyazı modeli."""
@@ -12,8 +10,8 @@ class Subtitle(BaseModel):
12
10
 
13
11
  class ExtractResult(BaseModel):
14
12
  """Extractor'ın döndürmesi gereken sonuç modeli."""
15
- name : str
16
- url : str
17
- referer : str
18
- headers : Optional[dict] = {}
19
- subtitles : List[Subtitle] = []
13
+ name : str
14
+ url : str
15
+ referer : str | None = None
16
+ user_agent : str | None = None
17
+ subtitles : list[Subtitle] = []
@@ -5,33 +5,29 @@ from ..Extractor.ExtractorModels import ExtractResult
5
5
  import subprocess, os
6
6
 
7
7
  class MediaHandler:
8
- def __init__(self, title: str = "KekikStream", headers: dict = None):
9
- # Varsayılan HTTP başlıklarını ayarla
10
- if headers is None:
11
- headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)"}
12
-
13
- self.headers = headers
8
+ def __init__(self, title: str = "KekikStream"):
14
9
  self.title = title
10
+ self.headers = {}
15
11
 
16
12
  def play_media(self, extract_data: ExtractResult):
17
- # Referer varsa headers'a ekle
18
- if extract_data.referer:
19
- self.headers.update({"Referer": extract_data.referer})
13
+ # user-agent ekle (varsayılan veya extract_data'dan)
14
+ user_agent = extract_data.user_agent or "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)"
15
+ self.headers["user-agent"] = user_agent
20
16
 
21
- # ExtractResult'tan gelen headers'ları ekle
22
- if extract_data.headers:
23
- self.headers.update(extract_data.headers)
17
+ # referer ekle
18
+ if extract_data.referer:
19
+ self.headers["referer"] = extract_data.referer
24
20
 
25
21
  # Google Drive gibi özel durumlar için yt-dlp kullan
26
- if self.headers.get("User-Agent") in ["googleusercontent", "Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0"]:
22
+ if user_agent in ["googleusercontent", "Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0"]:
27
23
  return self.play_with_ytdlp(extract_data)
28
24
 
29
25
  # İşletim sistemine göre oynatıcı seç
30
26
  if subprocess.check_output(['uname', '-o']).strip() == b'Android':
31
27
  return self.play_with_android_mxplayer(extract_data)
32
28
 
33
- # Cookie veya alt yazılar varsa mpv kullan
34
- if "Cookie" in self.headers or extract_data.subtitles:
29
+ # Alt yazılar varsa mpv kullan
30
+ if extract_data.subtitles:
35
31
  return self.play_with_mpv(extract_data)
36
32
 
37
33
  return self.play_with_vlc(extract_data) or self.play_with_mpv(extract_data)
@@ -48,11 +44,11 @@ class MediaHandler:
48
44
  f"--input-title-format={self.title}"
49
45
  ])
50
46
 
51
- if "User-Agent" in self.headers:
52
- vlc_command.append(f"--http-user-agent={self.headers.get('User-Agent')}")
47
+ if "user-agent" in self.headers:
48
+ vlc_command.append(f"--http-user-agent={self.headers.get('user-agent')}")
53
49
 
54
- if "Referer" in self.headers:
55
- vlc_command.append(f"--http-referrer={self.headers.get('Referer')}")
50
+ if "referer" in self.headers:
51
+ vlc_command.append(f"--http-referrer={self.headers.get('referer')}")
56
52
 
57
53
  vlc_command.extend(
58
54
  f"--sub-file={subtitle.url}" for subtitle in extract_data.subtitles
@@ -162,4 +158,4 @@ class MediaHandler:
162
158
  konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
163
159
  except FileNotFoundError:
164
160
  konsol.print(f"Paket: {paket}, Hata: MX Player kurulu değil")
165
- konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
161
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
@@ -12,8 +12,5 @@ class MediaManager:
12
12
  def get_title(self):
13
13
  return self.media_handler.title
14
14
 
15
- def set_headers(self, headers):
16
- self.media_handler.headers.update(headers)
17
-
18
15
  def play_media(self, extract_data):
19
16
  self.media_handler.play_media(extract_data)
@@ -3,6 +3,7 @@
3
3
  from abc import ABC, abstractmethod
4
4
  from curl_cffi import AsyncSession
5
5
  from cloudscraper import CloudScraper
6
+ from httpx import AsyncClient
6
7
  from .PluginModels import MainPageResult, SearchResult, MovieInfo
7
8
  from ..Media.MediaHandler import MediaHandler
8
9
  from ..Extractor.ExtractorManager import ExtractorManager
@@ -16,6 +17,8 @@ class PluginBase(ABC):
16
17
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
17
18
  description = "No description provided."
18
19
 
20
+ requires_cffi = False
21
+
19
22
  main_page = {}
20
23
 
21
24
  async def url_update(self, new_url: str):
@@ -24,17 +27,32 @@ class PluginBase(ABC):
24
27
  self.main_url = new_url
25
28
 
26
29
  def __init__(self):
27
- self.cffi = AsyncSession(impersonate="firefox135")
30
+ # cloudscraper - for bypassing Cloudflare
28
31
  self.cloudscraper = CloudScraper()
29
- self.cffi.cookies.update(self.cloudscraper.cookies)
30
- self.cffi.headers.update({"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 15.7; rv:135.0) Gecko/20100101 Firefox/135.0"})
32
+
33
+ # httpx - lightweight and safe for most HTTP requests
34
+ self.httpx = AsyncClient(
35
+ timeout = 3,
36
+ follow_redirects = True,
37
+ )
38
+ self.httpx.headers.update(self.cloudscraper.headers)
39
+ self.httpx.cookies.update(self.cloudscraper.cookies)
40
+
41
+ # curl_cffi - only initialize if needed for anti-bot bypass
42
+ self.cffi = None
43
+
44
+ if self.requires_cffi:
45
+ self.cffi = AsyncSession(impersonate="firefox135")
46
+ self.cffi.cookies.update(self.cloudscraper.cookies)
47
+ self.cffi.headers.update({"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 15.7; rv:135.0) Gecko/20100101 Firefox/135.0"})
48
+
31
49
  self.media_handler = MediaHandler()
32
50
  self.ex_manager = ExtractorManager()
33
51
 
34
- # @abstractmethod
35
- # async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
36
- # """Ana sayfadaki popüler içerikleri döndürür."""
37
- # pass
52
+ @abstractmethod
53
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
54
+ """Ana sayfadaki popüler içerikleri döndürür."""
55
+ pass
38
56
 
39
57
  @abstractmethod
40
58
  async def search(self, query: str) -> list[SearchResult]:
@@ -72,7 +90,10 @@ class PluginBase(ABC):
72
90
  pass
73
91
 
74
92
  async def close(self):
75
- await self.cffi.close()
93
+ """Close both HTTP clients if they exist."""
94
+ await self.httpx.aclose()
95
+ if self.cffi:
96
+ await self.cffi.close()
76
97
 
77
98
  def fix_url(self, url: str) -> str:
78
99
  if not url:
@@ -1,34 +1,33 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
3
  from pydantic import BaseModel, field_validator, model_validator
4
- from typing import List, Optional
5
4
 
6
5
  class MainPageResult(BaseModel):
7
6
  """Ana sayfa sonucunda dönecek veri modeli."""
8
7
  category : str
9
8
  title : str
10
9
  url : str
11
- poster : Optional[str] = None
10
+ poster : str | None = None
12
11
 
13
12
 
14
13
  class SearchResult(BaseModel):
15
14
  """Arama sonucunda dönecek veri modeli."""
16
15
  title : str
17
16
  url : str
18
- poster : Optional[str] = None
17
+ poster : str | None = None
19
18
 
20
19
 
21
20
  class MovieInfo(BaseModel):
22
21
  """Bir medya öğesinin bilgilerini tutan model."""
23
22
  url : str
24
- poster : Optional[str] = None
25
- title : Optional[str] = None
26
- description : Optional[str] = None
27
- tags : Optional[str] = None
28
- rating : Optional[str] = None
29
- year : Optional[str] = None
30
- actors : Optional[str] = None
31
- duration : Optional[int] = None
23
+ poster : str | None = None
24
+ title : str | None = None
25
+ description : str | None = None
26
+ tags : str | None = None
27
+ rating : str | None = None
28
+ year : str | None = None
29
+ actors : str | None = None
30
+ duration : int | None = None
32
31
 
33
32
  @field_validator("tags", "actors", mode="before")
34
33
  @classmethod
@@ -42,10 +41,10 @@ class MovieInfo(BaseModel):
42
41
 
43
42
 
44
43
  class Episode(BaseModel):
45
- season : Optional[int] = None
46
- episode : Optional[int] = None
47
- title : Optional[str] = None
48
- url : Optional[str] = None
44
+ season : int | None = None
45
+ episode : int | None = None
46
+ title : str | None = None
47
+ url : str | None = None
49
48
 
50
49
  @model_validator(mode="after")
51
50
  def check_title(self) -> "Episode":
@@ -58,16 +57,16 @@ class Episode(BaseModel):
58
57
  return self
59
58
 
60
59
  class SeriesInfo(BaseModel):
61
- url : Optional[str] = None
62
- poster : Optional[str] = None
63
- title : Optional[str] = None
64
- description : Optional[str] = None
65
- tags : Optional[str] = None
66
- rating : Optional[str] = None
67
- year : Optional[str] = None
68
- actors : Optional[str] = None
69
- duration : Optional[int] = None
70
- episodes : Optional[List[Episode]] = None
60
+ url : str | None = None
61
+ poster : str | None = None
62
+ title : str | None = None
63
+ description : str | None = None
64
+ tags : str | None = None
65
+ rating : str | None = None
66
+ year : str | None = None
67
+ actors : str | None = None
68
+ duration : int | None = None
69
+ episodes : list[Episode] | None = None
71
70
 
72
71
  @field_validator("tags", "actors", mode="before")
73
72
  @classmethod
@@ -77,4 +76,4 @@ class SeriesInfo(BaseModel):
77
76
  @field_validator("rating", "year", mode="before")
78
77
  @classmethod
79
78
  def ensure_string(cls, value):
80
- return str(value) if value is not None else value
79
+ return str(value) if value is not None else value
@@ -10,9 +10,9 @@ class CloseLoadExtractor(ExtractorBase):
10
10
 
11
11
  async def extract(self, url, referer=None) -> ExtractResult:
12
12
  if referer:
13
- self.cffi.headers.update({"Referer": referer})
13
+ self.httpx.headers.update({"Referer": referer})
14
14
 
15
- istek = await self.cffi.get(url)
15
+ istek = await self.httpx.get(url)
16
16
  istek.raise_for_status()
17
17
 
18
18
  eval_func = re.compile(r'\s*(eval\(function[\s\S].*)\s*').findall(istek.text)[0]
@@ -22,6 +22,5 @@ class CloseLoadExtractor(ExtractorBase):
22
22
  name = self.name,
23
23
  url = m3u_link,
24
24
  referer = self.main_url,
25
- headers = {},
26
25
  subtitles = []
27
- )
26
+ )
@@ -9,9 +9,9 @@ class ContentX(ExtractorBase):
9
9
 
10
10
  async def extract(self, url, referer=None) -> list[ExtractResult]:
11
11
  if referer:
12
- self.cffi.headers.update({"Referer": referer})
12
+ self.httpx.headers.update({"Referer": referer})
13
13
 
14
- istek = await self.cffi.get(url)
14
+ istek = await self.httpx.get(url)
15
15
  istek.raise_for_status()
16
16
  i_source = istek.text
17
17
 
@@ -39,7 +39,7 @@ class ContentX(ExtractorBase):
39
39
  )
40
40
  )
41
41
 
42
- vid_source_request = await self.cffi.get(f"{self.main_url}/source2.php?v={i_extract_value}", headers={"Referer": referer or self.main_url})
42
+ vid_source_request = await self.httpx.get(f"{self.main_url}/source2.php?v={i_extract_value}", headers={"Referer": referer or self.main_url})
43
43
  vid_source_request.raise_for_status()
44
44
 
45
45
  vid_source = vid_source_request.text
@@ -53,14 +53,13 @@ class ContentX(ExtractorBase):
53
53
  name = self.name,
54
54
  url = m3u_link,
55
55
  referer = url,
56
- headers = {},
57
56
  subtitles = subtitles
58
57
  )
59
58
  ]
60
59
 
61
60
  if i_dublaj := re.search(r',\"([^"]+)\",\"Türkçe"', i_source):
62
61
  dublaj_value = i_dublaj[1]
63
- dublaj_source_request = await self.cffi.get(f"{self.main_url}/source2.php?v={dublaj_value}", headers={"Referer": referer or self.main_url})
62
+ dublaj_source_request = await self.httpx.get(f"{self.main_url}/source2.php?v={dublaj_value}", headers={"Referer": referer or self.main_url})
64
63
  dublaj_source_request.raise_for_status()
65
64
 
66
65
  dublaj_source = dublaj_source_request.text
@@ -74,7 +73,6 @@ class ContentX(ExtractorBase):
74
73
  name = f"{self.name} Türkçe Dublaj",
75
74
  url = dublaj_link,
76
75
  referer = url,
77
- headers = {},
78
76
  subtitles = []
79
77
  )
80
78
  )
@@ -0,0 +1,38 @@
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 DzenRu(ExtractorBase):
7
+ name = "DzenRu"
8
+ main_url = "https://dzen.ru"
9
+
10
+ async def extract(self, url, referer=None) -> ExtractResult:
11
+ video_key = url.split("/")[-1]
12
+ video_url = f"{self.main_url}/embed/{video_key}"
13
+
14
+ if referer:
15
+ self.httpx.headers.update({"Referer": referer})
16
+
17
+ istek = await self.httpx.get(video_url)
18
+ istek.raise_for_status()
19
+
20
+ # okcdn.ru linklerini bul
21
+ matches = re.findall(r'https://vd\d+\.okcdn\.ru/\?[^"\'\\\s]+', istek.text)
22
+
23
+ if not matches:
24
+ raise ValueError("DzenRu video link not found")
25
+
26
+ # Benzersiz linkleri al, son kaliteyi kullan
27
+ unique_links = list(set(matches))
28
+ best_link = unique_links[-1] if unique_links else None
29
+
30
+ if not best_link:
31
+ raise ValueError("No valid video URL found")
32
+
33
+ return ExtractResult(
34
+ name = self.name,
35
+ url = best_link,
36
+ referer = self.main_url,
37
+ subtitles = []
38
+ )
@@ -0,0 +1,53 @@
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
+ from urllib.parse import urlparse, parse_qs
6
+
7
+ class ExPlay(ExtractorBase):
8
+ name = "ExPlay"
9
+ main_url = "https://explay.store"
10
+
11
+ async def extract(self, url, referer=None) -> ExtractResult:
12
+ ext_ref = referer or ""
13
+
14
+ # URL parsing for partKey
15
+ parsed = urlparse(url)
16
+ params = parse_qs(parsed.query)
17
+ part_key = params.get("partKey", [""])[0]
18
+ clean_url = url.split("?partKey=")[0]
19
+
20
+ if referer:
21
+ self.httpx.headers.update({"Referer": referer})
22
+
23
+ istek = await self.httpx.get(clean_url)
24
+ istek.raise_for_status()
25
+
26
+ # videoUrl çıkar
27
+ video_url_match = re.search(r'videoUrl":"([^",]+)"', istek.text)
28
+ if not video_url_match:
29
+ raise ValueError("videoUrl not found")
30
+ video_url = video_url_match[1].replace("\\", "")
31
+
32
+ # videoServer çıkar
33
+ video_server_match = re.search(r'videoServer":"([^",]+)"', istek.text)
34
+ if not video_server_match:
35
+ raise ValueError("videoServer not found")
36
+ video_server = video_server_match[1]
37
+
38
+ # title çıkar
39
+ title_match = re.search(r'title":"([^",]+)"', istek.text)
40
+ title = title_match[1].split(".")[-1] if title_match else "Unknown"
41
+
42
+ if part_key and "turkce" in part_key.lower():
43
+ title = part_key # Or nicer formatting like SetPlay
44
+
45
+ # M3U8 link oluştur
46
+ m3u_link = f"{self.main_url}{video_url}?s={video_server}"
47
+
48
+ return ExtractResult(
49
+ name = f"{self.name} - {title}",
50
+ url = m3u_link,
51
+ referer = clean_url,
52
+ subtitles = []
53
+ )
@@ -0,0 +1,60 @@
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
+ import re
6
+
7
+ class FirePlayer(ExtractorBase):
8
+ name = "FirePlayer"
9
+ main_url = "https://Player.filmizle.in"
10
+
11
+ def can_handle_url(self, url: str) -> bool:
12
+ return "filmizle.in" in url or "fireplayer" in url.lower()
13
+
14
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
15
+ if not referer:
16
+ referer = "https://sinezy.site/"
17
+
18
+ headers = {
19
+ "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",
20
+ "Referer": referer
21
+ }
22
+
23
+ istek = await self.httpx.get(url, headers=headers)
24
+
25
+ # Unpack usage similar to VidMoxy / suggestion
26
+ # Find the packed code block
27
+ match = re.search(r'(eval\(function\(p,a,c,k,e,d\)[\s\S]+?)\s*</script>', istek.text)
28
+ if match:
29
+ packed_code = match.group(1)
30
+ unpacked = Packer.unpack(packed_code)
31
+ else:
32
+ unpacked = istek.text
33
+
34
+ # Normalize escaped slashes
35
+ unpacked = unpacked.replace(r"\/", "/")
36
+
37
+ video_url = None
38
+
39
+ # Look for .mp4 or .m3u8 urls directly first
40
+ url_match = re.search(r'(https?://[^"\'\s]+\.(?:mp4|m3u8))', unpacked)
41
+ if url_match:
42
+ video_url = url_match.group(1)
43
+
44
+ if not video_url:
45
+ # Fallback: find all 'file': '...' and pick best
46
+ files = re.findall(r'file\s*:\s*["\']([^"\']+)["\']', unpacked)
47
+ for f in files:
48
+ if f.strip() and not f.endswith(".jpg") and not f.endswith(".png") and not f.endswith(".vtt"):
49
+ video_url = f
50
+ break
51
+
52
+ if not video_url:
53
+ raise ValueError("Could not find video URL in unpacked content")
54
+
55
+ return ExtractResult(
56
+ name = self.name,
57
+ url = video_url,
58
+ referer = url,
59
+ user_agent = headers.get("User-Agent", "")
60
+ )
@@ -0,0 +1,41 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult
4
+
5
+ class HDPlayerSystem(ExtractorBase):
6
+ name = "HDPlayerSystem"
7
+ main_url = "https://hdplayersystem.com"
8
+
9
+ async def extract(self, url, referer=None) -> ExtractResult:
10
+ ext_ref = referer or ""
11
+
12
+ if "video/" in url:
13
+ vid_id = url.split("video/")[-1]
14
+ else:
15
+ vid_id = url.split("?data=")[-1]
16
+
17
+ post_url = f"{self.main_url}/player/index.php?data={vid_id}&do=getVideo"
18
+
19
+ response = await self.httpx.post(
20
+ url = post_url,
21
+ data = {"hash": vid_id, "r": ext_ref},
22
+ headers = {
23
+ "Referer" : ext_ref,
24
+ "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8",
25
+ "X-Requested-With" : "XMLHttpRequest"
26
+ }
27
+ )
28
+ response.raise_for_status()
29
+
30
+ video_data = response.json()
31
+ m3u_link = video_data.get("securedLink")
32
+
33
+ if not m3u_link:
34
+ raise ValueError("securedLink not found in response")
35
+
36
+ return ExtractResult(
37
+ name = self.name,
38
+ url = m3u_link,
39
+ referer = url,
40
+ subtitles = []
41
+ )
@@ -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, Subtitle
4
+ import re, json
5
+
6
+ class JetTv(ExtractorBase):
7
+ name = "JetTv"
8
+ main_url = "https://jetv.xyz"
9
+
10
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
11
+ istek = await self.httpx.get(url)
12
+ document = istek.text
13
+
14
+ # 1. Yöntem: API üzerinden alma
15
+ master_url = ""
16
+ final_ref = f"{self.main_url}/"
17
+
18
+ if "id=" in url:
19
+ vid_id = url.split("id=")[-1]
20
+ api_url = f"https://jetv.xyz/apollo/get_video.php?id={vid_id}"
21
+ try:
22
+ # Referer olarak video sayfasının kendisi gönderilmeli
23
+ api_resp = await self.httpx.get(api_url, headers={"Referer": url})
24
+ api_json = api_resp.json()
25
+
26
+ if api_json.get("success"):
27
+ master_url = api_json.get("masterUrl", "")
28
+ final_ref = api_json.get("referrerUrl") or final_ref
29
+ except Exception:
30
+ pass
31
+
32
+ # 2. Yöntem: Regex Fallback
33
+ if not master_url:
34
+ if match := re.search(r"file: '([^']*)'", document, re.IGNORE_CASE):
35
+ master_url = match.group(1)
36
+
37
+ if not master_url:
38
+ raise ValueError(f"JetTv: Video kaynağı bulunamadı. {url}")
39
+
40
+ return ExtractResult(
41
+ name = self.name,
42
+ url = master_url,
43
+ referer = final_ref,
44
+ subtitles = []
45
+ )
@@ -11,9 +11,9 @@ class MailRuExtractor(ExtractorBase):
11
11
  video_meta_url = f"{self.main_url}/+/video/meta/{vid_id}"
12
12
 
13
13
  if referer:
14
- self.cffi.headers.update({"Referer": referer})
14
+ self.httpx.headers.update({"Referer": referer})
15
15
 
16
- istek = await self.cffi.get(video_meta_url)
16
+ istek = await self.httpx.get(video_meta_url)
17
17
  istek.raise_for_status()
18
18
 
19
19
  video_key = istek.cookies.get("video_key")
@@ -34,6 +34,5 @@ class MailRuExtractor(ExtractorBase):
34
34
  name = self.name,
35
35
  url = video_url,
36
36
  referer = self.main_url,
37
- headers = {"Cookie": f"video_key={video_key}"},
38
37
  subtitles = []
39
- )
38
+ )
@@ -10,9 +10,9 @@ class MixPlayHD(ExtractorBase):
10
10
 
11
11
  async def extract(self, url, referer=None) -> ExtractResult:
12
12
  if referer:
13
- self.cffi.headers.update({"Referer": referer})
13
+ self.httpx.headers.update({"Referer": referer})
14
14
 
15
- istek = await self.cffi.get(url)
15
+ istek = await self.httpx.get(url)
16
16
  istek.raise_for_status()
17
17
 
18
18
  be_player_match = re.search(r"bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);", istek.text)
@@ -36,7 +36,6 @@ class MixPlayHD(ExtractorBase):
36
36
  name = self.name,
37
37
  url = video_url_match[1],
38
38
  referer = self.main_url,
39
- headers = {},
40
39
  subtitles = []
41
40
  )
42
41
  else: