KekikStream 0.2.3__py3-none-any.whl → 0.5.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- KekikStream/CLI/__init__.py +1 -1
- KekikStream/CLI/pypi_kontrol.py +30 -0
- KekikStream/Core/ExtractorBase.py +13 -2
- KekikStream/Core/ExtractorLoader.py +31 -10
- KekikStream/Core/ExtractorModels.py +2 -0
- KekikStream/Core/MediaHandler.py +70 -30
- KekikStream/Core/PluginBase.py +6 -5
- KekikStream/Core/PluginLoader.py +3 -5
- KekikStream/Core/PluginModels.py +6 -16
- KekikStream/Extractors/ContentX.py +80 -0
- KekikStream/Extractors/FourCX.py +7 -0
- KekikStream/Extractors/FourPichive.py +7 -0
- KekikStream/Extractors/FourPlayRu.py +7 -0
- KekikStream/Extractors/HDStreamAble.py +7 -0
- KekikStream/Extractors/Hotlinger.py +7 -0
- KekikStream/Extractors/MixPlayHD.py +42 -0
- KekikStream/Extractors/Odnoklassniki.py +106 -0
- KekikStream/Extractors/OkRuHTTP.py +7 -0
- KekikStream/Extractors/OkRuSSL.py +7 -0
- KekikStream/Extractors/PeaceMakerst.py +57 -0
- KekikStream/Extractors/Pichive.py +7 -0
- KekikStream/Extractors/PixelDrain.py +1 -1
- KekikStream/Extractors/PlayRu.py +7 -0
- KekikStream/Extractors/RapidVid.py +9 -10
- KekikStream/Extractors/SibNet.py +1 -1
- KekikStream/Extractors/Sobreatsesuyp.py +5 -6
- KekikStream/Extractors/TRsTX.py +5 -6
- KekikStream/Extractors/TauVideo.py +11 -10
- KekikStream/Extractors/TurboImgz.py +9 -12
- KekikStream/Extractors/VidMoly.py +25 -33
- KekikStream/Extractors/VidMoxy.py +1 -1
- KekikStream/Extractors/VideoSeyred.py +47 -0
- KekikStream/Managers/MediaManager.py +1 -1
- KekikStream/Managers/UIManager.py +6 -2
- KekikStream/Plugins/DiziBox.py +138 -0
- KekikStream/Plugins/Dizilla.py +95 -0
- KekikStream/Plugins/FilmMakinesi.py +8 -8
- KekikStream/Plugins/FullHDFilmizlesene.py +5 -4
- KekikStream/Plugins/JetFilmizle.py +4 -8
- KekikStream/Plugins/RecTV.py +111 -0
- KekikStream/Plugins/SineWix.py +4 -1
- KekikStream/Plugins/UgurFilm.py +3 -3
- KekikStream/__init__.py +177 -158
- KekikStream/__main__.py +1 -1
- KekikStream/requirements.txt +2 -1
- {KekikStream-0.2.3.dist-info → KekikStream-0.5.3.dist-info}/METADATA +4 -3
- KekikStream-0.5.3.dist-info/RECORD +58 -0
- KekikStream/CLI/check_update.py +0 -33
- KekikStream-0.2.3.dist-info/RECORD +0 -41
- {KekikStream-0.2.3.dist-info → KekikStream-0.5.3.dist-info}/LICENSE +0 -0
- {KekikStream-0.2.3.dist-info → KekikStream-0.5.3.dist-info}/WHEEL +0 -0
- {KekikStream-0.2.3.dist-info → KekikStream-0.5.3.dist-info}/entry_points.txt +0 -0
- {KekikStream-0.2.3.dist-info → KekikStream-0.5.3.dist-info}/top_level.txt +0 -0
KekikStream/CLI/__init__.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
2
2
|
|
3
3
|
from Kekik.cli import konsol, cikis_yap, hata_salla, log_salla, hata_yakala, bellek_temizle, temizle
|
4
|
-
from .
|
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]")
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
from abc import ABC, abstractmethod
|
4
4
|
from httpx import AsyncClient, Timeout
|
5
|
+
from cloudscraper import CloudScraper
|
5
6
|
from typing import Optional
|
6
7
|
from .ExtractorModels import ExtractResult
|
7
8
|
|
@@ -15,8 +16,9 @@ class ExtractorBase(ABC):
|
|
15
16
|
"User-Agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)",
|
16
17
|
"Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
17
18
|
},
|
18
|
-
timeout = Timeout(10.0)
|
19
|
+
timeout = Timeout(10.0)
|
19
20
|
)
|
21
|
+
self.cloudscraper = CloudScraper()
|
20
22
|
|
21
23
|
def can_handle_url(self, url: str) -> bool:
|
22
24
|
"""URL'nin bu extractor tarafından işlenip işlenemeyeceğini kontrol eder."""
|
@@ -28,4 +30,13 @@ class ExtractorBase(ABC):
|
|
28
30
|
pass
|
29
31
|
|
30
32
|
async def close(self):
|
31
|
-
await self.oturum.aclose()
|
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)
|
@@ -3,41 +3,60 @@
|
|
3
3
|
from ..CLI import konsol, cikis_yap
|
4
4
|
from .ExtractorBase import ExtractorBase
|
5
5
|
from pathlib import Path
|
6
|
-
import importlib.util
|
7
|
-
import os
|
6
|
+
import os, importlib.util
|
8
7
|
|
9
8
|
class ExtractorLoader:
|
10
9
|
def __init__(self, extractors_dir: str):
|
11
10
|
self.local_extractors_dir = Path(extractors_dir)
|
12
11
|
self.global_extractors_dir = Path(__file__).parent.parent / extractors_dir
|
13
12
|
if not self.local_extractors_dir.exists() and not self.global_extractors_dir.exists():
|
14
|
-
konsol.log(f"[red][!] Extractor dizini bulunamadı: {self.
|
13
|
+
konsol.log(f"[red][!] Extractor dizini bulunamadı: {self.global_extractors_dir}[/red]")
|
15
14
|
cikis_yap(False)
|
16
15
|
|
17
16
|
def load_all(self) -> list[ExtractorBase]:
|
18
17
|
extractors = []
|
19
18
|
|
19
|
+
# Global Extractor'ları yükle
|
20
20
|
if self.global_extractors_dir.exists():
|
21
21
|
konsol.log(f"[green][*] Global Extractor dizininden yükleniyor: {self.global_extractors_dir}[/green]")
|
22
|
-
|
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)
|
23
25
|
|
26
|
+
# Yerel Extractor'ları yükle
|
24
27
|
if self.local_extractors_dir.exists():
|
25
28
|
konsol.log(f"[green][*] Yerel Extractor dizininden yükleniyor: {self.local_extractors_dir}[/green]")
|
26
|
-
|
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)
|
27
32
|
|
28
|
-
|
29
|
-
|
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)
|
30
41
|
|
31
|
-
|
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
|
32
48
|
|
33
49
|
def _load_from_directory(self, directory: Path) -> list[ExtractorBase]:
|
34
50
|
extractors = []
|
35
51
|
for file in os.listdir(directory):
|
36
52
|
if file.endswith(".py") and not file.startswith("__"):
|
37
53
|
module_name = file[:-3]
|
54
|
+
konsol.log(f"[cyan]Modül yükleniyor: {module_name}[/cyan]")
|
38
55
|
if extractor := self._load_extractor(directory, module_name):
|
56
|
+
konsol.log(f"[magenta]Extractor bulundu: {extractor.__name__}[/magenta]")
|
39
57
|
extractors.append(extractor)
|
40
58
|
|
59
|
+
konsol.log(f"[yellow]{directory} dizininden yüklenen Extractor'lar: {[e.__name__ for e in extractors]}[/yellow]")
|
41
60
|
return extractors
|
42
61
|
|
43
62
|
def _load_extractor(self, directory: Path, module_name: str):
|
@@ -50,12 +69,14 @@ class ExtractorLoader:
|
|
50
69
|
module = importlib.util.module_from_spec(spec)
|
51
70
|
spec.loader.exec_module(module)
|
52
71
|
|
72
|
+
# Yalnızca doğru modülden gelen ExtractorBase sınıflarını yükle
|
53
73
|
for attr in dir(module):
|
54
74
|
obj = getattr(module, attr)
|
55
|
-
if isinstance(obj, type) and issubclass(obj, ExtractorBase) and obj is not ExtractorBase:
|
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]")
|
56
77
|
return obj
|
57
78
|
|
58
79
|
except Exception as hata:
|
59
|
-
konsol.
|
80
|
+
konsol.log(f"[red][!] Extractor yüklenirken hata oluştu: {module_name}\nHata: {hata}")
|
60
81
|
|
61
82
|
return None
|
@@ -3,11 +3,13 @@
|
|
3
3
|
from pydantic import BaseModel
|
4
4
|
from typing import List, Optional
|
5
5
|
|
6
|
+
|
6
7
|
class Subtitle(BaseModel):
|
7
8
|
"""Altyazı modeli."""
|
8
9
|
name : str
|
9
10
|
url : str
|
10
11
|
|
12
|
+
|
11
13
|
class ExtractResult(BaseModel):
|
12
14
|
"""Extractor'ın döndürmesi gereken sonuç modeli."""
|
13
15
|
name : str
|
KekikStream/Core/MediaHandler.py
CHANGED
@@ -2,24 +2,34 @@
|
|
2
2
|
|
3
3
|
from ..CLI import konsol
|
4
4
|
from .ExtractorModels import ExtractResult
|
5
|
-
import subprocess
|
5
|
+
import subprocess, os
|
6
6
|
|
7
7
|
class MediaHandler:
|
8
|
-
def __init__(self, title: str = "KekikStream", headers: dict =
|
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
|
+
|
9
12
|
self.headers = headers
|
10
13
|
self.title = title
|
11
14
|
|
15
|
+
def play_media(self, extract_data: ExtractResult):
|
16
|
+
if subprocess.check_output(['uname', '-o']).strip() == b'Android':
|
17
|
+
return self.play_with_android_mxplayer(extract_data)
|
18
|
+
|
19
|
+
if "Cookie" in self.headers or extract_data.subtitles:
|
20
|
+
return self.play_with_mpv(extract_data)
|
21
|
+
|
22
|
+
return self.play_with_vlc(extract_data)
|
23
|
+
|
12
24
|
def play_with_vlc(self, extract_data: ExtractResult):
|
13
25
|
try:
|
14
|
-
|
15
|
-
self.play_with_mpv(extract_data)
|
16
|
-
return
|
17
|
-
|
18
|
-
vlc_command = ["vlc"]
|
26
|
+
vlc_command = ["vlc", "--quiet"]
|
19
27
|
|
20
28
|
if self.title:
|
21
|
-
vlc_command.
|
22
|
-
|
29
|
+
vlc_command.extend([
|
30
|
+
f"--meta-title={self.title}",
|
31
|
+
f"--input-title-format={self.title}"
|
32
|
+
])
|
23
33
|
|
24
34
|
if "User-Agent" in self.headers:
|
25
35
|
vlc_command.append(f"--http-user-agent={self.headers.get('User-Agent')}")
|
@@ -27,13 +37,16 @@ class MediaHandler:
|
|
27
37
|
if "Referer" in self.headers:
|
28
38
|
vlc_command.append(f"--http-referrer={self.headers.get('Referer')}")
|
29
39
|
|
30
|
-
|
31
|
-
|
32
|
-
|
40
|
+
vlc_command.extend(
|
41
|
+
f"--sub-file={subtitle.url}" for subtitle in extract_data.subtitles
|
42
|
+
)
|
33
43
|
vlc_command.append(extract_data.url)
|
34
|
-
|
35
|
-
|
36
|
-
|
44
|
+
|
45
|
+
with open(os.devnull, "w") as devnull:
|
46
|
+
subprocess.run(vlc_command, stdout=devnull, stderr=devnull, check=True)
|
47
|
+
|
48
|
+
except subprocess.CalledProcessError as hata:
|
49
|
+
konsol.print(f"[red]VLC oynatma hatası: {hata}[/red]")
|
37
50
|
konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
|
38
51
|
except FileNotFoundError:
|
39
52
|
konsol.print("[red]VLC bulunamadı! VLC kurulu olduğundan emin olun.[/red]")
|
@@ -41,28 +54,55 @@ class MediaHandler:
|
|
41
54
|
|
42
55
|
def play_with_mpv(self, extract_data: ExtractResult):
|
43
56
|
try:
|
44
|
-
mpv_command = ["mpv"]
|
57
|
+
mpv_command = ["mpv", "--really-quiet"]
|
45
58
|
|
46
59
|
if self.title:
|
47
|
-
mpv_command.append(f"--title={self.title}")
|
60
|
+
mpv_command.append(f"--force-media-title={self.title}")
|
48
61
|
|
49
|
-
|
50
|
-
mpv_command.append(f"--http-header-fields=
|
62
|
+
for key, value in self.headers.items():
|
63
|
+
mpv_command.append(f"--http-header-fields={key}: {value}")
|
51
64
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
mpv_command.append(f"--http-header-fields=Cookie: {self.headers.get('Cookie')}")
|
65
|
+
mpv_command.extend(
|
66
|
+
f"--sub-file={subtitle.url}" for subtitle in extract_data.subtitles
|
67
|
+
)
|
68
|
+
mpv_command.append(extract_data.url)
|
57
69
|
|
58
|
-
|
59
|
-
|
70
|
+
with open(os.devnull, "w") as devnull:
|
71
|
+
subprocess.run(mpv_command, stdout=devnull, stderr=devnull, check=True)
|
60
72
|
|
61
|
-
|
62
|
-
|
63
|
-
except subprocess.CalledProcessError as e:
|
64
|
-
konsol.print(f"[red]mpv oynatma hatası: {e}[/red]")
|
73
|
+
except subprocess.CalledProcessError as hata:
|
74
|
+
konsol.print(f"[red]mpv oynatma hatası: {hata}[/red]")
|
65
75
|
konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
|
66
76
|
except FileNotFoundError:
|
67
77
|
konsol.print("[red]mpv bulunamadı! mpv kurulu olduğundan emin olun.[/red]")
|
68
78
|
konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
|
79
|
+
|
80
|
+
def play_with_android_mxplayer(self, extract_data: ExtractResult):
|
81
|
+
paketler = [
|
82
|
+
"com.mxtech.videoplayer.ad/.ActivityScreen", # Free sürüm
|
83
|
+
"com.mxtech.videoplayer.pro/.ActivityScreen" # Pro sürüm
|
84
|
+
]
|
85
|
+
|
86
|
+
for paket in paketler:
|
87
|
+
try:
|
88
|
+
android_command = [
|
89
|
+
"am", "start",
|
90
|
+
"-a", "android.intent.action.VIEW",
|
91
|
+
"-d", extract_data.url,
|
92
|
+
"-n", paket
|
93
|
+
]
|
94
|
+
|
95
|
+
if self.title:
|
96
|
+
android_command.extend(["--es", "title", self.title])
|
97
|
+
|
98
|
+
with open(os.devnull, "w") as devnull:
|
99
|
+
subprocess.run(android_command, stdout=devnull, stderr=devnull, check=True)
|
100
|
+
|
101
|
+
return
|
102
|
+
|
103
|
+
except subprocess.CalledProcessError as hata:
|
104
|
+
konsol.print(f"[red]{paket} oynatma hatası: {hata}[/red]")
|
105
|
+
konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
|
106
|
+
except FileNotFoundError:
|
107
|
+
konsol.print(f"Paket: {paket}, Hata: MX Player kurulu değil")
|
108
|
+
konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
|
KekikStream/Core/PluginBase.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
from abc import ABC, abstractmethod
|
4
4
|
from httpx import AsyncClient, Timeout
|
5
|
+
from cloudscraper import CloudScraper
|
5
6
|
from .PluginModels import SearchResult, MovieInfo
|
6
7
|
from .MediaHandler import MediaHandler
|
7
8
|
from urllib.parse import urljoin
|
@@ -21,6 +22,9 @@ class PluginBase(ABC):
|
|
21
22
|
timeout = Timeout(10.0),
|
22
23
|
)
|
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)
|
24
28
|
|
25
29
|
@abstractmethod
|
26
30
|
async def search(self, query: str) -> list[SearchResult]:
|
@@ -43,14 +47,11 @@ class PluginBase(ABC):
|
|
43
47
|
def fix_url(self, url: str) -> str:
|
44
48
|
if not url:
|
45
49
|
return ""
|
46
|
-
|
50
|
+
|
47
51
|
if url.startswith("http") or url.startswith("{\""):
|
48
52
|
return url
|
49
53
|
|
50
|
-
if url.startswith("//")
|
51
|
-
return f"https:{url}"
|
52
|
-
|
53
|
-
return urljoin(self.main_url, url)
|
54
|
+
return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
|
54
55
|
|
55
56
|
@staticmethod
|
56
57
|
def clean_title(title: str) -> str:
|
KekikStream/Core/PluginLoader.py
CHANGED
@@ -3,9 +3,7 @@
|
|
3
3
|
from ..CLI import konsol, cikis_yap
|
4
4
|
from .PluginBase import PluginBase
|
5
5
|
from pathlib import Path
|
6
|
-
import importlib.util
|
7
|
-
import os
|
8
|
-
import traceback
|
6
|
+
import os, importlib.util, traceback
|
9
7
|
|
10
8
|
class PluginLoader:
|
11
9
|
def __init__(self, plugins_dir: str):
|
@@ -20,11 +18,11 @@ class PluginLoader:
|
|
20
18
|
|
21
19
|
if self.global_plugins_dir.exists():
|
22
20
|
konsol.log(f"[green][*] Global Plugin dizininden yükleniyor: {self.global_plugins_dir}[/green]")
|
23
|
-
plugins
|
21
|
+
plugins |= self._load_from_directory(self.global_plugins_dir)
|
24
22
|
|
25
23
|
if self.local_plugins_dir.exists():
|
26
24
|
konsol.log(f"[green][*] Yerel Plugin dizininden yükleniyor: {self.local_plugins_dir}[/green]")
|
27
|
-
plugins
|
25
|
+
plugins |= self._load_from_directory(self.local_plugins_dir)
|
28
26
|
|
29
27
|
if not plugins:
|
30
28
|
konsol.print("[yellow][!] Yüklenecek bir Plugin bulunamadı![/yellow]")
|
KekikStream/Core/PluginModels.py
CHANGED
@@ -23,17 +23,12 @@ class MovieInfo(BaseModel):
|
|
23
23
|
actors : Optional[str] = None
|
24
24
|
duration : Optional[int] = None
|
25
25
|
|
26
|
-
@field_validator("tags", mode="before")
|
26
|
+
@field_validator("tags", "actors", mode="before")
|
27
27
|
@classmethod
|
28
|
-
def
|
28
|
+
def convert_lists(cls, value):
|
29
29
|
return ", ".join(value) if isinstance(value, list) else value
|
30
30
|
|
31
|
-
@field_validator("
|
32
|
-
@classmethod
|
33
|
-
def convert_actors(cls, value):
|
34
|
-
return ", ".join(value) if isinstance(value, list) else value
|
35
|
-
|
36
|
-
@field_validator("rating", mode="before")
|
31
|
+
@field_validator("rating", "year", mode="before")
|
37
32
|
@classmethod
|
38
33
|
def ensure_string(cls, value):
|
39
34
|
return str(value) if value is not None else value
|
@@ -57,17 +52,12 @@ class SeriesInfo(BaseModel):
|
|
57
52
|
actors : Optional[str] = None
|
58
53
|
episodes : Optional[List[Episode]] = None
|
59
54
|
|
60
|
-
@field_validator("tags", mode="before")
|
61
|
-
@classmethod
|
62
|
-
def convert_tags(cls, value):
|
63
|
-
return ", ".join(value) if isinstance(value, list) else value
|
64
|
-
|
65
|
-
@field_validator("actors", mode="before")
|
55
|
+
@field_validator("tags", "actors", mode="before")
|
66
56
|
@classmethod
|
67
|
-
def
|
57
|
+
def convert_lists(cls, value):
|
68
58
|
return ", ".join(value) if isinstance(value, list) else value
|
69
59
|
|
70
|
-
@field_validator("rating", mode="before")
|
60
|
+
@field_validator("rating", "year", mode="before")
|
71
61
|
@classmethod
|
72
62
|
def ensure_string(cls, value):
|
73
63
|
return str(value) if value is not None else value
|
@@ -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,42 @@
|
|
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 AESManager
|
5
|
+
import re, json
|
6
|
+
|
7
|
+
class MixPlayHD(ExtractorBase):
|
8
|
+
name = "MixPlayHD"
|
9
|
+
main_url = "https://mixplayhd.com"
|
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
|
+
be_player_match = re.search(r"bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);", istek.text)
|
19
|
+
if not be_player_match:
|
20
|
+
raise ValueError("bePlayer not found in the response.")
|
21
|
+
|
22
|
+
be_player_pass = be_player_match[1]
|
23
|
+
be_player_data = be_player_match[2]
|
24
|
+
|
25
|
+
try:
|
26
|
+
decrypted_data = AESManager.decrypt(be_player_data, be_player_pass).replace("\\", "")
|
27
|
+
decrypted_json = json.loads(decrypted_data)
|
28
|
+
except Exception as hata:
|
29
|
+
raise RuntimeError(f"Decryption failed: {hata}") from hata
|
30
|
+
|
31
|
+
if video_url_match := re.search(
|
32
|
+
pattern = r'"video_location":"([^"]+)"',
|
33
|
+
string = decrypted_json.get("schedule", {}).get("client", ""),
|
34
|
+
):
|
35
|
+
return ExtractResult(
|
36
|
+
name = self.name,
|
37
|
+
url = video_url_match[1],
|
38
|
+
referer = self.main_url,
|
39
|
+
subtitles = []
|
40
|
+
)
|
41
|
+
else:
|
42
|
+
raise ValueError("M3U8 video URL not found in the decrypted data.")
|