KekikStream 0.7.1__tar.gz
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.
- kekikstream-0.7.1/KekikStream/CLI/__init__.py +4 -0
- kekikstream-0.7.1/KekikStream/CLI/pypi_kontrol.py +30 -0
- kekikstream-0.7.1/KekikStream/Core/ExtractorBase.py +42 -0
- kekikstream-0.7.1/KekikStream/Core/ExtractorLoader.py +82 -0
- kekikstream-0.7.1/KekikStream/Core/ExtractorModels.py +19 -0
- kekikstream-0.7.1/KekikStream/Core/MediaHandler.py +142 -0
- kekikstream-0.7.1/KekikStream/Core/PluginBase.py +80 -0
- kekikstream-0.7.1/KekikStream/Core/PluginLoader.py +61 -0
- kekikstream-0.7.1/KekikStream/Core/PluginModels.py +63 -0
- kekikstream-0.7.1/KekikStream/Core/__init__.py +9 -0
- kekikstream-0.7.1/KekikStream/Extractors/CloseLoad.py +31 -0
- kekikstream-0.7.1/KekikStream/Extractors/ContentX.py +80 -0
- kekikstream-0.7.1/KekikStream/Extractors/FourCX.py +7 -0
- kekikstream-0.7.1/KekikStream/Extractors/FourPichive.py +7 -0
- kekikstream-0.7.1/KekikStream/Extractors/FourPlayRu.py +7 -0
- kekikstream-0.7.1/KekikStream/Extractors/HDStreamAble.py +7 -0
- kekikstream-0.7.1/KekikStream/Extractors/Hotlinger.py +7 -0
- kekikstream-0.7.1/KekikStream/Extractors/MailRu.py +40 -0
- kekikstream-0.7.1/KekikStream/Extractors/MixPlayHD.py +42 -0
- kekikstream-0.7.1/KekikStream/Extractors/Odnoklassniki.py +106 -0
- kekikstream-0.7.1/KekikStream/Extractors/OkRuHTTP.py +7 -0
- kekikstream-0.7.1/KekikStream/Extractors/OkRuSSL.py +7 -0
- kekikstream-0.7.1/KekikStream/Extractors/PeaceMakerst.py +57 -0
- kekikstream-0.7.1/KekikStream/Extractors/Pichive.py +7 -0
- kekikstream-0.7.1/KekikStream/Extractors/PixelDrain.py +28 -0
- kekikstream-0.7.1/KekikStream/Extractors/PlayRu.py +7 -0
- kekikstream-0.7.1/KekikStream/Extractors/RapidVid.py +60 -0
- kekikstream-0.7.1/KekikStream/Extractors/SibNet.py +29 -0
- kekikstream-0.7.1/KekikStream/Extractors/Sobreatsesuyp.py +59 -0
- kekikstream-0.7.1/KekikStream/Extractors/TRsTX.py +67 -0
- kekikstream-0.7.1/KekikStream/Extractors/TauVideo.py +34 -0
- kekikstream-0.7.1/KekikStream/Extractors/TurboImgz.py +25 -0
- kekikstream-0.7.1/KekikStream/Extractors/VidMoly.py +85 -0
- kekikstream-0.7.1/KekikStream/Extractors/VidMoxy.py +50 -0
- kekikstream-0.7.1/KekikStream/Extractors/VideoSeyred.py +47 -0
- kekikstream-0.7.1/KekikStream/Managers/ExtractorManager.py +27 -0
- kekikstream-0.7.1/KekikStream/Managers/MediaManager.py +19 -0
- kekikstream-0.7.1/KekikStream/Managers/PluginManager.py +19 -0
- kekikstream-0.7.1/KekikStream/Managers/UIManager.py +49 -0
- kekikstream-0.7.1/KekikStream/Managers/__init__.py +6 -0
- kekikstream-0.7.1/KekikStream/Plugins/DiziBox.py +143 -0
- kekikstream-0.7.1/KekikStream/Plugins/DiziYou.py +127 -0
- kekikstream-0.7.1/KekikStream/Plugins/Dizilla.py +106 -0
- kekikstream-0.7.1/KekikStream/Plugins/FilmMakinesi.py +65 -0
- kekikstream-0.7.1/KekikStream/Plugins/FullHDFilmizlesene.py +78 -0
- kekikstream-0.7.1/KekikStream/Plugins/JetFilmizle.py +92 -0
- kekikstream-0.7.1/KekikStream/Plugins/RecTV.py +113 -0
- kekikstream-0.7.1/KekikStream/Plugins/SezonlukDizi.py +108 -0
- kekikstream-0.7.1/KekikStream/Plugins/SineWix.py +108 -0
- kekikstream-0.7.1/KekikStream/Plugins/UgurFilm.py +75 -0
- kekikstream-0.7.1/KekikStream/__init__.py +255 -0
- kekikstream-0.7.1/KekikStream/__main__.py +6 -0
- kekikstream-0.7.1/KekikStream/requirements.txt +10 -0
- kekikstream-0.7.1/KekikStream.egg-info/PKG-INFO +94 -0
- kekikstream-0.7.1/KekikStream.egg-info/SOURCES.txt +63 -0
- kekikstream-0.7.1/KekikStream.egg-info/dependency_links.txt +1 -0
- kekikstream-0.7.1/KekikStream.egg-info/entry_points.txt +2 -0
- kekikstream-0.7.1/KekikStream.egg-info/requires.txt +8 -0
- kekikstream-0.7.1/KekikStream.egg-info/top_level.txt +1 -0
- kekikstream-0.7.1/LICENSE +674 -0
- kekikstream-0.7.1/MANIFEST.in +1 -0
- kekikstream-0.7.1/PKG-INFO +94 -0
- kekikstream-0.7.1/README.md +59 -0
- kekikstream-0.7.1/setup.cfg +4 -0
- kekikstream-0.7.1/setup.py +49 -0
@@ -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
|