KekikStream 0.7.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (59) hide show
  1. KekikStream/CLI/__init__.py +4 -0
  2. KekikStream/CLI/pypi_kontrol.py +30 -0
  3. KekikStream/Core/ExtractorBase.py +42 -0
  4. KekikStream/Core/ExtractorLoader.py +82 -0
  5. KekikStream/Core/ExtractorModels.py +19 -0
  6. KekikStream/Core/MediaHandler.py +142 -0
  7. KekikStream/Core/PluginBase.py +80 -0
  8. KekikStream/Core/PluginLoader.py +61 -0
  9. KekikStream/Core/PluginModels.py +63 -0
  10. KekikStream/Core/__init__.py +9 -0
  11. KekikStream/Extractors/CloseLoad.py +31 -0
  12. KekikStream/Extractors/ContentX.py +80 -0
  13. KekikStream/Extractors/FourCX.py +7 -0
  14. KekikStream/Extractors/FourPichive.py +7 -0
  15. KekikStream/Extractors/FourPlayRu.py +7 -0
  16. KekikStream/Extractors/HDStreamAble.py +7 -0
  17. KekikStream/Extractors/Hotlinger.py +7 -0
  18. KekikStream/Extractors/MailRu.py +40 -0
  19. KekikStream/Extractors/MixPlayHD.py +42 -0
  20. KekikStream/Extractors/Odnoklassniki.py +106 -0
  21. KekikStream/Extractors/OkRuHTTP.py +7 -0
  22. KekikStream/Extractors/OkRuSSL.py +7 -0
  23. KekikStream/Extractors/PeaceMakerst.py +57 -0
  24. KekikStream/Extractors/Pichive.py +7 -0
  25. KekikStream/Extractors/PixelDrain.py +28 -0
  26. KekikStream/Extractors/PlayRu.py +7 -0
  27. KekikStream/Extractors/RapidVid.py +60 -0
  28. KekikStream/Extractors/SibNet.py +29 -0
  29. KekikStream/Extractors/Sobreatsesuyp.py +59 -0
  30. KekikStream/Extractors/TRsTX.py +67 -0
  31. KekikStream/Extractors/TauVideo.py +34 -0
  32. KekikStream/Extractors/TurboImgz.py +25 -0
  33. KekikStream/Extractors/VidMoly.py +85 -0
  34. KekikStream/Extractors/VidMoxy.py +50 -0
  35. KekikStream/Extractors/VideoSeyred.py +47 -0
  36. KekikStream/Managers/ExtractorManager.py +27 -0
  37. KekikStream/Managers/MediaManager.py +19 -0
  38. KekikStream/Managers/PluginManager.py +19 -0
  39. KekikStream/Managers/UIManager.py +49 -0
  40. KekikStream/Managers/__init__.py +6 -0
  41. KekikStream/Plugins/DiziBox.py +143 -0
  42. KekikStream/Plugins/DiziYou.py +127 -0
  43. KekikStream/Plugins/Dizilla.py +106 -0
  44. KekikStream/Plugins/FilmMakinesi.py +65 -0
  45. KekikStream/Plugins/FullHDFilmizlesene.py +78 -0
  46. KekikStream/Plugins/JetFilmizle.py +92 -0
  47. KekikStream/Plugins/RecTV.py +113 -0
  48. KekikStream/Plugins/SezonlukDizi.py +108 -0
  49. KekikStream/Plugins/SineWix.py +108 -0
  50. KekikStream/Plugins/UgurFilm.py +75 -0
  51. KekikStream/__init__.py +255 -0
  52. KekikStream/__main__.py +6 -0
  53. KekikStream/requirements.txt +10 -0
  54. KekikStream-0.7.1.dist-info/LICENSE +674 -0
  55. KekikStream-0.7.1.dist-info/METADATA +94 -0
  56. KekikStream-0.7.1.dist-info/RECORD +59 -0
  57. KekikStream-0.7.1.dist-info/WHEEL +5 -0
  58. KekikStream-0.7.1.dist-info/entry_points.txt +2 -0
  59. KekikStream-0.7.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,4 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from Kekik.cli import konsol, cikis_yap, hata_salla, log_salla, hata_yakala, bellek_temizle, temizle
4
+ from .pypi_kontrol import pypi_kontrol_guncelle
@@ -0,0 +1,30 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from . import konsol
4
+ from rich.panel import Panel
5
+ from pkg_resources import get_distribution
6
+ from requests import get
7
+ from subprocess import check_call
8
+ import sys
9
+
10
+ def pypi_kontrol_guncelle(paket_adi: str):
11
+ try:
12
+ konsol.print(f"[bold cyan] {paket_adi} Güncellemesi kontrol ediliyor...[/bold cyan]")
13
+ mevcut_surum = get_distribution(paket_adi).version
14
+ konsol.print(Panel(f"[cyan]Yüklü sürüm:[/cyan] [bold yellow]{mevcut_surum}[/bold yellow]"))
15
+
16
+ istek = get(f"https://pypi.org/pypi/{paket_adi}/json")
17
+ if istek.status_code == 200:
18
+ son_surum = istek.json()["info"]["version"]
19
+ konsol.print(Panel(f"[cyan]En son sürüm:[/cyan] [bold green]{son_surum}[/bold green]"))
20
+
21
+ if mevcut_surum != son_surum:
22
+ konsol.print(f"[bold red]{paket_adi} güncelleniyor...[/bold red]")
23
+ check_call([sys.executable, "-m", "pip", "install", "--upgrade", paket_adi, "--break-system-packages"])
24
+ konsol.print(f"[bold green]{paket_adi} güncellendi![/bold green]")
25
+ else:
26
+ konsol.print(f"[bold green]{paket_adi} zaten güncel.[/bold green]")
27
+ else:
28
+ konsol.print("[bold red]PyPI'ye erişilemiyor. Güncelleme kontrolü atlanıyor.[/bold red]")
29
+ except Exception as hata:
30
+ konsol.print(f"[bold red]Güncelleme kontrolü sırasında hata oluştu: {hata}[/bold red]")
@@ -0,0 +1,42 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from abc import ABC, abstractmethod
4
+ from httpx import AsyncClient, Timeout
5
+ from cloudscraper import CloudScraper
6
+ from typing import Optional
7
+ from .ExtractorModels import ExtractResult
8
+
9
+ class ExtractorBase(ABC):
10
+ name = "Extractor"
11
+ main_url = ""
12
+
13
+ def __init__(self):
14
+ self.oturum = AsyncClient(
15
+ headers = {
16
+ "User-Agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)",
17
+ "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
18
+ },
19
+ timeout = Timeout(10.0)
20
+ )
21
+ self.cloudscraper = CloudScraper()
22
+
23
+ def can_handle_url(self, url: str) -> bool:
24
+ """URL'nin bu extractor tarafından işlenip işlenemeyeceğini kontrol eder."""
25
+ return self.main_url in url
26
+
27
+ @abstractmethod
28
+ async def extract(self, url: str, referer: Optional[str] = None) -> ExtractResult:
29
+ """Bir URL'den medya bilgilerini çıkarır."""
30
+ pass
31
+
32
+ async def close(self):
33
+ await self.oturum.aclose()
34
+
35
+ def fix_url(self, url: str) -> str:
36
+ if not url:
37
+ return ""
38
+
39
+ if url.startswith("http") or url.startswith("{\""):
40
+ return url
41
+
42
+ return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
@@ -0,0 +1,82 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from ..CLI import konsol, cikis_yap
4
+ from .ExtractorBase import ExtractorBase
5
+ from pathlib import Path
6
+ import os, importlib.util
7
+
8
+ class ExtractorLoader:
9
+ def __init__(self, extractors_dir: str):
10
+ self.local_extractors_dir = Path(extractors_dir)
11
+ self.global_extractors_dir = Path(__file__).parent.parent / extractors_dir
12
+ if not self.local_extractors_dir.exists() and not self.global_extractors_dir.exists():
13
+ konsol.log(f"[red][!] Extractor dizini bulunamadı: {self.global_extractors_dir}[/red]")
14
+ cikis_yap(False)
15
+
16
+ def load_all(self) -> list[ExtractorBase]:
17
+ extractors = []
18
+
19
+ # Global Extractor'ları yükle
20
+ if self.global_extractors_dir.exists():
21
+ konsol.log(f"[green][*] Global Extractor dizininden yükleniyor: {self.global_extractors_dir}[/green]")
22
+ global_extractors = self._load_from_directory(self.global_extractors_dir)
23
+ konsol.log(f"[green]Global Extractor'lar: {[e.__name__ for e in global_extractors]}[/green]")
24
+ extractors.extend(global_extractors)
25
+
26
+ # Yerel Extractor'ları yükle
27
+ if self.local_extractors_dir.exists():
28
+ konsol.log(f"[green][*] Yerel Extractor dizininden yükleniyor: {self.local_extractors_dir}[/green]")
29
+ local_extractors = self._load_from_directory(self.local_extractors_dir)
30
+ konsol.log(f"[green]Yerel Extractor'lar: {[e.__name__ for e in local_extractors]}[/green]")
31
+ extractors.extend(local_extractors)
32
+
33
+ # Benzersizliği sağlama (modül adı + sınıf adı bazında)
34
+ unique_extractors = []
35
+ seen = set()
36
+ for ext in extractors:
37
+ identifier = f"{ext.__module__}.{ext.__name__}"
38
+ if identifier not in seen:
39
+ unique_extractors.append(ext)
40
+ seen.add(identifier)
41
+
42
+ konsol.log(f"[blue]Sonuç Extractor'lar: {[e.__name__ for e in unique_extractors]}[/blue]")
43
+
44
+ if not unique_extractors:
45
+ konsol.log("[yellow][!] Yüklenecek bir Extractor bulunamadı![/yellow]")
46
+
47
+ return unique_extractors
48
+
49
+ def _load_from_directory(self, directory: Path) -> list[ExtractorBase]:
50
+ extractors = []
51
+ for file in os.listdir(directory):
52
+ if file.endswith(".py") and not file.startswith("__"):
53
+ module_name = file[:-3]
54
+ konsol.log(f"[cyan]Modül yükleniyor: {module_name}[/cyan]")
55
+ if extractor := self._load_extractor(directory, module_name):
56
+ konsol.log(f"[magenta]Extractor bulundu: {extractor.__name__}[/magenta]")
57
+ extractors.append(extractor)
58
+
59
+ konsol.log(f"[yellow]{directory} dizininden yüklenen Extractor'lar: {[e.__name__ for e in extractors]}[/yellow]")
60
+ return extractors
61
+
62
+ def _load_extractor(self, directory: Path, module_name: str):
63
+ try:
64
+ path = directory / f"{module_name}.py"
65
+ spec = importlib.util.spec_from_file_location(module_name, path)
66
+ if not spec or not spec.loader:
67
+ return None
68
+
69
+ module = importlib.util.module_from_spec(spec)
70
+ spec.loader.exec_module(module)
71
+
72
+ # Yalnızca doğru modülden gelen ExtractorBase sınıflarını yükle
73
+ for attr in dir(module):
74
+ obj = getattr(module, attr)
75
+ if obj.__module__ == module_name and isinstance(obj, type) and issubclass(obj, ExtractorBase) and obj is not ExtractorBase:
76
+ konsol.log(f"[green]Yüklenen sınıf: {module_name}.{obj.__name__} ({obj.__module__}.{obj.__name__})[/green]")
77
+ return obj
78
+
79
+ except Exception as hata:
80
+ konsol.log(f"[red][!] Extractor yüklenirken hata oluştu: {module_name}\nHata: {hata}")
81
+
82
+ return None
@@ -0,0 +1,19 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from pydantic import BaseModel
4
+ from typing import List, Optional
5
+
6
+
7
+ class Subtitle(BaseModel):
8
+ """Altyazı modeli."""
9
+ name : str
10
+ url : str
11
+
12
+
13
+ class ExtractResult(BaseModel):
14
+ """Extractor'ın döndürmesi gereken sonuç modeli."""
15
+ name : str
16
+ url : str
17
+ referer : str
18
+ subtitles : List[Subtitle] = []
19
+ headers : Optional[dict] = {}
@@ -0,0 +1,142 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from ..CLI import konsol
4
+ from .ExtractorModels import ExtractResult
5
+ import subprocess, os
6
+
7
+ class MediaHandler:
8
+ def __init__(self, title: str = "KekikStream", headers: dict = None):
9
+ if headers is None:
10
+ headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)"}
11
+
12
+ self.headers = headers
13
+ self.title = title
14
+
15
+ def play_media(self, extract_data: ExtractResult):
16
+ if self.headers.get("User-Agent") == "googleusercontent":
17
+ return self.play_with_ytdlp(extract_data)
18
+
19
+ if subprocess.check_output(['uname', '-o']).strip() == b'Android':
20
+ return self.play_with_android_mxplayer(extract_data)
21
+
22
+ if "Cookie" in self.headers or extract_data.subtitles:
23
+ return self.play_with_mpv(extract_data)
24
+
25
+ return self.play_with_vlc(extract_data)
26
+
27
+ def play_with_vlc(self, extract_data: ExtractResult):
28
+ try:
29
+ vlc_command = ["vlc", "--quiet"]
30
+
31
+ if self.title:
32
+ vlc_command.extend([
33
+ f"--meta-title={self.title}",
34
+ f"--input-title-format={self.title}"
35
+ ])
36
+
37
+ if "User-Agent" in self.headers:
38
+ vlc_command.append(f"--http-user-agent={self.headers.get('User-Agent')}")
39
+
40
+ if "Referer" in self.headers:
41
+ vlc_command.append(f"--http-referrer={self.headers.get('Referer')}")
42
+
43
+ vlc_command.extend(
44
+ f"--sub-file={subtitle.url}" for subtitle in extract_data.subtitles
45
+ )
46
+ vlc_command.append(extract_data.url)
47
+
48
+ with open(os.devnull, "w") as devnull:
49
+ subprocess.run(vlc_command, stdout=devnull, stderr=devnull, check=True)
50
+
51
+ except subprocess.CalledProcessError as hata:
52
+ konsol.print(f"[red]VLC oynatma hatası: {hata}[/red]")
53
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
54
+ except FileNotFoundError:
55
+ konsol.print("[red]VLC bulunamadı! VLC kurulu olduğundan emin olun.[/red]")
56
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
57
+
58
+ def play_with_mpv(self, extract_data: ExtractResult):
59
+ try:
60
+ mpv_command = ["mpv", "--really-quiet"]
61
+
62
+ if self.title:
63
+ mpv_command.append(f"--force-media-title={self.title}")
64
+
65
+ for key, value in self.headers.items():
66
+ mpv_command.append(f"--http-header-fields={key}: {value}")
67
+
68
+ mpv_command.extend(
69
+ f"--sub-file={subtitle.url}" for subtitle in extract_data.subtitles
70
+ )
71
+ mpv_command.append(extract_data.url)
72
+
73
+ with open(os.devnull, "w") as devnull:
74
+ subprocess.run(mpv_command, stdout=devnull, stderr=devnull, check=True)
75
+
76
+ except subprocess.CalledProcessError as hata:
77
+ konsol.print(f"[red]mpv oynatma hatası: {hata}[/red]")
78
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
79
+ except FileNotFoundError:
80
+ konsol.print("[red]mpv bulunamadı! mpv kurulu olduğundan emin olun.[/red]")
81
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
82
+
83
+ def play_with_ytdlp(self, extract_data: ExtractResult):
84
+ try:
85
+ ytdlp_command = ["yt-dlp", "--quiet", "--no-warnings", "--downloader", "ffmpeg", "--hls-use-mpegts"]
86
+
87
+ for key, value in self.headers.items():
88
+ ytdlp_command.extend(["--add-header", f"{key}: {value}"])
89
+
90
+ ytdlp_command.extend([
91
+ "-o", "-",
92
+ extract_data.url
93
+ ])
94
+
95
+ mpv_command = ["mpv", "--really-quiet", "-"]
96
+
97
+ if self.title:
98
+ mpv_command.append(f"--force-media-title={self.title}")
99
+
100
+ mpv_command.extend(
101
+ f"--sub-file={subtitle.url}" for subtitle in extract_data.subtitles
102
+ )
103
+
104
+ with subprocess.Popen(ytdlp_command, stdout=subprocess.PIPE) as ytdlp_proc:
105
+ subprocess.run(mpv_command, stdin=ytdlp_proc.stdout, check=True)
106
+
107
+ except subprocess.CalledProcessError as hata:
108
+ print(f"[red]Oynatma hatası: {hata}[/red]")
109
+ print({"title": self.title, "url": extract_data.url, "headers": self.headers})
110
+ except FileNotFoundError:
111
+ print("[red]yt-dlp veya mpv bulunamadı! Kurulumlarından emin olun.[/red]")
112
+ print({"title": self.title, "url": extract_data.url, "headers": self.headers})
113
+
114
+ def play_with_android_mxplayer(self, extract_data: ExtractResult):
115
+ paketler = [
116
+ "com.mxtech.videoplayer.ad/.ActivityScreen", # Free sürüm
117
+ "com.mxtech.videoplayer.pro/.ActivityScreen" # Pro sürüm
118
+ ]
119
+
120
+ for paket in paketler:
121
+ try:
122
+ android_command = [
123
+ "am", "start",
124
+ "-a", "android.intent.action.VIEW",
125
+ "-d", extract_data.url,
126
+ "-n", paket
127
+ ]
128
+
129
+ if self.title:
130
+ android_command.extend(["--es", "title", self.title])
131
+
132
+ with open(os.devnull, "w") as devnull:
133
+ subprocess.run(android_command, stdout=devnull, stderr=devnull, check=True)
134
+
135
+ return
136
+
137
+ except subprocess.CalledProcessError as hata:
138
+ konsol.print(f"[red]{paket} oynatma hatası: {hata}[/red]")
139
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
140
+ except FileNotFoundError:
141
+ konsol.print(f"Paket: {paket}, Hata: MX Player kurulu değil")
142
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
@@ -0,0 +1,80 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from abc import ABC, abstractmethod
4
+ from httpx import AsyncClient, Timeout
5
+ from cloudscraper import CloudScraper
6
+ from .PluginModels import SearchResult, MovieInfo
7
+ from .MediaHandler import MediaHandler
8
+ from urllib.parse import urljoin
9
+ import re
10
+
11
+ class PluginBase(ABC):
12
+ name = "Plugin"
13
+ main_url = "https://example.com"
14
+ _data = {}
15
+
16
+ def __init__(self):
17
+ self.oturum = AsyncClient(
18
+ headers = {
19
+ "User-Agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)",
20
+ "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
21
+ },
22
+ timeout = Timeout(10.0),
23
+ )
24
+ self.media_handler = MediaHandler()
25
+ self.cloudscraper = CloudScraper()
26
+ self.oturum.headers.update(self.cloudscraper.headers)
27
+ self.oturum.cookies.update(self.cloudscraper.cookies)
28
+
29
+ @abstractmethod
30
+ async def search(self, query: str) -> list[SearchResult]:
31
+ """Kullanıcı arama sorgusuna göre sonuç döndürür."""
32
+ pass
33
+
34
+ @abstractmethod
35
+ async def load_item(self, url: str) -> MovieInfo:
36
+ """Bir medya öğesi hakkında detaylı bilgi döndürür."""
37
+ pass
38
+
39
+ @abstractmethod
40
+ async def load_links(self, url: str) -> list[str]:
41
+ """Bir medya öğesi için oynatma bağlantılarını döndürür."""
42
+ pass
43
+
44
+ async def close(self):
45
+ await self.oturum.aclose()
46
+
47
+ def fix_url(self, url: str) -> str:
48
+ if not url:
49
+ return ""
50
+
51
+ if url.startswith("http") or url.startswith("{\""):
52
+ return url
53
+
54
+ return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
55
+
56
+ @staticmethod
57
+ def clean_title(title: str) -> str:
58
+ suffixes = [
59
+ " izle",
60
+ " full film",
61
+ " filmini full",
62
+ " full türkçe",
63
+ " alt yazılı",
64
+ " altyazılı",
65
+ " tr dublaj",
66
+ " hd türkçe",
67
+ " türkçe dublaj",
68
+ " yeşilçam ",
69
+ " erotik fil",
70
+ " türkçe",
71
+ " yerli",
72
+ " tüekçe dublaj",
73
+ ]
74
+
75
+ cleaned_title = title.strip()
76
+
77
+ for suffix in suffixes:
78
+ cleaned_title = re.sub(f"{re.escape(suffix)}.*$", "", cleaned_title, flags=re.IGNORECASE).strip()
79
+
80
+ return cleaned_title
@@ -0,0 +1,61 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from ..CLI import konsol, cikis_yap
4
+ from .PluginBase import PluginBase
5
+ from pathlib import Path
6
+ import os, importlib.util, traceback
7
+
8
+ class PluginLoader:
9
+ def __init__(self, plugins_dir: str):
10
+ self.local_plugins_dir = Path(plugins_dir).resolve()
11
+ self.global_plugins_dir = Path(__file__).parent.parent / plugins_dir
12
+ if not self.local_plugins_dir.exists() and not self.global_plugins_dir.exists():
13
+ konsol.log(f"[red][!] Extractor dizini bulunamadı: {self.plugins_dir}[/red]")
14
+ cikis_yap(False)
15
+
16
+ def load_all(self) -> dict[str, PluginBase]:
17
+ plugins = {}
18
+
19
+ if self.global_plugins_dir.exists():
20
+ konsol.log(f"[green][*] Global Plugin dizininden yükleniyor: {self.global_plugins_dir}[/green]")
21
+ plugins |= self._load_from_directory(self.global_plugins_dir)
22
+
23
+ if self.local_plugins_dir.exists():
24
+ konsol.log(f"[green][*] Yerel Plugin dizininden yükleniyor: {self.local_plugins_dir}[/green]")
25
+ plugins |= self._load_from_directory(self.local_plugins_dir)
26
+
27
+ if not plugins:
28
+ konsol.print("[yellow][!] Yüklenecek bir Plugin bulunamadı![/yellow]")
29
+
30
+ return dict(sorted(plugins.items()))
31
+
32
+ def _load_from_directory(self, directory: Path) -> dict[str, PluginBase]:
33
+ plugins = {}
34
+ for file in os.listdir(directory):
35
+ if file.endswith(".py") and not file.startswith("__"):
36
+ module_name = file[:-3]
37
+ if plugin := self._load_plugin(directory, module_name):
38
+ plugins[module_name] = plugin
39
+
40
+ return plugins
41
+
42
+ def _load_plugin(self, directory: Path, module_name: str):
43
+ try:
44
+ path = directory / f"{module_name}.py"
45
+ spec = importlib.util.spec_from_file_location(module_name, path)
46
+ if not spec or not spec.loader:
47
+ raise ImportError(f"Spec oluşturulamadı: {module_name}")
48
+
49
+ module = importlib.util.module_from_spec(spec)
50
+ spec.loader.exec_module(module)
51
+
52
+ for attr in dir(module):
53
+ obj = getattr(module, attr)
54
+ if isinstance(obj, type) and issubclass(obj, PluginBase) and obj is not PluginBase:
55
+ return obj()
56
+
57
+ except Exception as hata:
58
+ konsol.print(f"[red][!] Plugin yüklenirken hata oluştu: {module_name}\nHata: {hata}")
59
+ konsol.print(f"[dim]{traceback.format_exc()}[/dim]")
60
+
61
+ return None
@@ -0,0 +1,63 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from pydantic import BaseModel, field_validator
4
+ from typing import List, Optional
5
+
6
+
7
+ class SearchResult(BaseModel):
8
+ """Arama sonucunda dönecek veri modeli."""
9
+ title : str
10
+ url : str
11
+ poster : Optional[str] = None
12
+
13
+
14
+ class MovieInfo(BaseModel):
15
+ """Bir medya öğesinin bilgilerini tutan model."""
16
+ url : str
17
+ poster : Optional[str] = None
18
+ title : Optional[str] = None
19
+ description : Optional[str] = None
20
+ tags : Optional[str] = None
21
+ rating : Optional[str] = None
22
+ year : Optional[str] = None
23
+ actors : Optional[str] = None
24
+ duration : Optional[int] = None
25
+
26
+ @field_validator("tags", "actors", mode="before")
27
+ @classmethod
28
+ def convert_lists(cls, value):
29
+ return ", ".join(value) if isinstance(value, list) else value
30
+
31
+ @field_validator("rating", "year", mode="before")
32
+ @classmethod
33
+ def ensure_string(cls, value):
34
+ return str(value) if value is not None else value
35
+
36
+
37
+ class Episode(BaseModel):
38
+ season : Optional[int] = None
39
+ episode : Optional[int] = None
40
+ title : Optional[str] = None
41
+ url : Optional[str] = None
42
+
43
+
44
+ class SeriesInfo(BaseModel):
45
+ url : Optional[str] = None
46
+ poster : Optional[str] = None
47
+ title : Optional[str] = None
48
+ description : Optional[str] = None
49
+ tags : Optional[str] = None
50
+ rating : Optional[str] = None
51
+ year : Optional[str] = None
52
+ actors : Optional[str] = None
53
+ episodes : Optional[List[Episode]] = None
54
+
55
+ @field_validator("tags", "actors", mode="before")
56
+ @classmethod
57
+ def convert_lists(cls, value):
58
+ return ", ".join(value) if isinstance(value, list) else value
59
+
60
+ @field_validator("rating", "year", mode="before")
61
+ @classmethod
62
+ def ensure_string(cls, value):
63
+ return str(value) if value is not None else value
@@ -0,0 +1,9 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from .PluginBase import PluginBase
4
+ from .PluginLoader import PluginLoader
5
+ from .PluginModels import SearchResult, MovieInfo, Episode, SeriesInfo
6
+ from .ExtractorBase import ExtractorBase
7
+ from .ExtractorLoader import ExtractorLoader
8
+ from .ExtractorModels import ExtractResult, Subtitle
9
+ from .MediaHandler import MediaHandler
@@ -0,0 +1,31 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult
4
+ from parsel import Selector
5
+ from base64 import b64decode
6
+
7
+ class CloseLoadExtractor(ExtractorBase):
8
+ name = "CloseLoad"
9
+ main_url = "https://closeload.filmmakinesi.de"
10
+
11
+ async def extract(self, url, referer=None) -> ExtractResult:
12
+ if referer:
13
+ self.oturum.headers.update({"Referer": referer})
14
+
15
+ istek = await self.oturum.get(url)
16
+ istek.raise_for_status()
17
+
18
+ secici = Selector(istek.text)
19
+ atob = secici.re(r"aHR0[0-9a-zA-Z+/=]*")
20
+ if not atob:
21
+ raise ValueError("Base64 kodu bulunamadı.")
22
+
23
+ m3u_link = b64decode(f"{atob[0]}===").decode("utf-8")
24
+
25
+ await self.close()
26
+ return ExtractResult(
27
+ name = self.name,
28
+ url = m3u_link,
29
+ referer = self.main_url,
30
+ subtitles = []
31
+ )
@@ -0,0 +1,80 @@
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 ContentX(ExtractorBase):
7
+ name = "ContentX"
8
+ main_url = "https://contentx.me"
9
+
10
+ async def extract(self, url, referer=None) -> list[ExtractResult]:
11
+ if referer:
12
+ self.oturum.headers.update({"Referer": referer})
13
+
14
+ istek = await self.oturum.get(url)
15
+ istek.raise_for_status()
16
+ i_source = istek.text
17
+
18
+ i_extract = re.search(r"window\.openPlayer\('([^']+)'", i_source)
19
+ if not i_extract:
20
+ raise ValueError("i_extract is null")
21
+ i_extract_value = i_extract[1]
22
+
23
+ subtitles = []
24
+ sub_urls = set()
25
+ for match in re.finditer(r'"file":"([^"]+)","label":"([^"]+)"', i_source):
26
+ sub_url, sub_lang = match.groups()
27
+
28
+ if sub_url in sub_urls:
29
+ continue
30
+
31
+ sub_urls.add(sub_url)
32
+ subtitles.append(
33
+ Subtitle(
34
+ name = sub_lang.replace("\\u0131", "ı")
35
+ .replace("\\u0130", "İ")
36
+ .replace("\\u00fc", "ü")
37
+ .replace("\\u00e7", "ç"),
38
+ url = self.fix_url(sub_url.replace("\\", ""))
39
+ )
40
+ )
41
+
42
+ vid_source_request = await self.oturum.get(f"{self.main_url}/source2.php?v={i_extract_value}", headers={"Referer": referer or self.main_url})
43
+ vid_source_request.raise_for_status()
44
+
45
+ vid_source = vid_source_request.text
46
+ vid_extract = re.search(r'file":"([^"]+)"', vid_source)
47
+ if not vid_extract:
48
+ raise ValueError("vidExtract is null")
49
+
50
+ m3u_link = vid_extract[1].replace("\\", "")
51
+ results = [
52
+ ExtractResult(
53
+ name = self.name,
54
+ url = m3u_link,
55
+ referer = url,
56
+ subtitles = subtitles
57
+ )
58
+ ]
59
+
60
+ if i_dublaj := re.search(r',\"([^"]+)\",\"Türkçe"', i_source):
61
+ dublaj_value = i_dublaj[1]
62
+ dublaj_source_request = await self.oturum.get(f"{self.main_url}/source2.php?v={dublaj_value}", headers={"Referer": referer or self.main_url})
63
+ dublaj_source_request.raise_for_status()
64
+
65
+ dublaj_source = dublaj_source_request.text
66
+ dublaj_extract = re.search(r'file":"([^"]+)"', dublaj_source)
67
+ if not dublaj_extract:
68
+ raise ValueError("dublajExtract is null")
69
+
70
+ dublaj_link = dublaj_extract[1].replace("\\", "")
71
+ results.append(
72
+ ExtractResult(
73
+ name = f"{self.name} Türkçe Dublaj",
74
+ url = dublaj_link,
75
+ referer = url,
76
+ subtitles = []
77
+ )
78
+ )
79
+
80
+ return results[0] if len(results) == 1 else results
@@ -0,0 +1,7 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Extractors.ContentX import ContentX
4
+
5
+ class FourCX(ContentX):
6
+ name = "FourCX"
7
+ main_url = "https://four.contentx.me"
@@ -0,0 +1,7 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Extractors.ContentX import ContentX
4
+
5
+ class FourPichive(ContentX):
6
+ name = "FourPichive"
7
+ main_url = "https://four.pichive.online"