KekikStream 0.0.1__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 +3 -0
- KekikStream/Core/ExtractorBase.py +18 -0
- KekikStream/Core/ExtractorLoader.py +61 -0
- KekikStream/Core/ExtractorModels.py +17 -0
- KekikStream/Core/MediaHandler.py +58 -0
- KekikStream/Core/PluginBase.py +42 -0
- KekikStream/Core/PluginLoader.py +63 -0
- KekikStream/Core/PluginModels.py +32 -0
- KekikStream/Core/__init__.py +9 -0
- KekikStream/Extractors/CloseLoad.py +30 -0
- KekikStream/Extractors/MailRu.py +40 -0
- KekikStream/Managers/ExtractorManager.py +27 -0
- KekikStream/Managers/MediaManager.py +19 -0
- KekikStream/Managers/PluginManager.py +18 -0
- KekikStream/Managers/UIManager.py +32 -0
- KekikStream/Managers/__init__.py +6 -0
- KekikStream/Plugins/FilmMakinesi.py +65 -0
- KekikStream/Plugins/UgurFilm.py +75 -0
- KekikStream/__init__.py +114 -0
- KekikStream/__main__.py +6 -0
- KekikStream/requirements.txt +9 -0
- KekikStream-0.0.1.dist-info/LICENSE +674 -0
- KekikStream-0.0.1.dist-info/METADATA +66 -0
- KekikStream-0.0.1.dist-info/RECORD +27 -0
- KekikStream-0.0.1.dist-info/WHEEL +5 -0
- KekikStream-0.0.1.dist-info/entry_points.txt +2 -0
- KekikStream-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
2
|
+
|
3
|
+
from abc import ABC, abstractmethod
|
4
|
+
from typing import Optional
|
5
|
+
from .ExtractorModels import ExtractResult
|
6
|
+
|
7
|
+
class ExtractorBase(ABC):
|
8
|
+
name = "Extractor"
|
9
|
+
main_url = ""
|
10
|
+
|
11
|
+
def can_handle_url(self, url: str) -> bool:
|
12
|
+
"""URL'nin bu extractor tarafından işlenip işlenemeyeceğini kontrol eder."""
|
13
|
+
return self.main_url in url
|
14
|
+
|
15
|
+
@abstractmethod
|
16
|
+
async def extract(self, url: str, referer: Optional[str] = None) -> ExtractResult:
|
17
|
+
"""Bir URL'den medya bilgilerini çıkarır."""
|
18
|
+
pass
|
@@ -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 .ExtractorBase import ExtractorBase
|
5
|
+
from pathlib import Path
|
6
|
+
import importlib.util
|
7
|
+
import os
|
8
|
+
|
9
|
+
class ExtractorLoader:
|
10
|
+
def __init__(self, extractors_dir: str):
|
11
|
+
self.local_extractors_dir = Path(extractors_dir)
|
12
|
+
self.global_extractors_dir = Path(__file__).parent.parent / extractors_dir
|
13
|
+
if not self.local_extractors_dir.exists() and not self.global_extractors_dir.exists():
|
14
|
+
konsol.log(f"[red][!] Extractor dizini bulunamadı: {self.extractors_dir}[/red]")
|
15
|
+
cikis_yap(False)
|
16
|
+
|
17
|
+
def load_all(self) -> list[ExtractorBase]:
|
18
|
+
extractors = []
|
19
|
+
|
20
|
+
if self.global_extractors_dir.exists():
|
21
|
+
konsol.log(f"[green][*] Global Extractor dizininden yükleniyor: {self.global_extractors_dir}[/green]")
|
22
|
+
extractors.extend(self._load_from_directory(self.global_extractors_dir))
|
23
|
+
|
24
|
+
if self.local_extractors_dir.exists():
|
25
|
+
konsol.log(f"[green][*] Yerel Extractor dizininden yükleniyor: {self.local_extractors_dir}[/green]")
|
26
|
+
extractors.extend(self._load_from_directory(self.local_extractors_dir))
|
27
|
+
|
28
|
+
if not extractors:
|
29
|
+
konsol.print("[yellow][!] Yüklenecek bir Extractor bulunamadı![/yellow]")
|
30
|
+
|
31
|
+
return extractors
|
32
|
+
|
33
|
+
def _load_from_directory(self, directory: Path) -> list[ExtractorBase]:
|
34
|
+
extractors = []
|
35
|
+
for file in os.listdir(directory):
|
36
|
+
if file.endswith(".py") and not file.startswith("__"):
|
37
|
+
module_name = file[:-3]
|
38
|
+
if extractor := self._load_extractor(directory, module_name):
|
39
|
+
extractors.append(extractor)
|
40
|
+
|
41
|
+
return extractors
|
42
|
+
|
43
|
+
def _load_extractor(self, directory: Path, module_name: str):
|
44
|
+
try:
|
45
|
+
path = directory / f"{module_name}.py"
|
46
|
+
spec = importlib.util.spec_from_file_location(module_name, path)
|
47
|
+
if not spec or not spec.loader:
|
48
|
+
return None
|
49
|
+
|
50
|
+
module = importlib.util.module_from_spec(spec)
|
51
|
+
spec.loader.exec_module(module)
|
52
|
+
|
53
|
+
for attr in dir(module):
|
54
|
+
obj = getattr(module, attr)
|
55
|
+
if isinstance(obj, type) and issubclass(obj, ExtractorBase) and obj is not ExtractorBase:
|
56
|
+
return obj
|
57
|
+
|
58
|
+
except Exception as hata:
|
59
|
+
konsol.print(f"[red][!] Extractor yüklenirken hata oluştu: {module_name}\nHata: {hata}")
|
60
|
+
|
61
|
+
return None
|
@@ -0,0 +1,17 @@
|
|
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
|
+
class Subtitle(BaseModel):
|
7
|
+
"""Altyazı modeli."""
|
8
|
+
name : str
|
9
|
+
url : str
|
10
|
+
|
11
|
+
class ExtractResult(BaseModel):
|
12
|
+
"""Extractor'ın döndürmesi gereken sonuç modeli."""
|
13
|
+
name : str
|
14
|
+
url : str
|
15
|
+
referer : str
|
16
|
+
subtitles : List[Subtitle] = []
|
17
|
+
headers : Optional[dict] = {}
|
@@ -0,0 +1,58 @@
|
|
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
|
6
|
+
|
7
|
+
class MediaHandler:
|
8
|
+
def __init__(self, title: str = "KekikStream", headers: dict = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)"}):
|
9
|
+
self.headers = headers
|
10
|
+
self.title = title
|
11
|
+
|
12
|
+
def play_with_vlc(self, extract_data: ExtractResult):
|
13
|
+
try:
|
14
|
+
if "Cookie" in self.headers:
|
15
|
+
self.play_with_mpv(extract_data)
|
16
|
+
return
|
17
|
+
|
18
|
+
vlc_command = ["vlc"]
|
19
|
+
|
20
|
+
if self.title:
|
21
|
+
vlc_command.append(f"--meta-title={self.title}")
|
22
|
+
vlc_command.append(f"--input-title-format={self.title}")
|
23
|
+
|
24
|
+
if "User-Agent" in self.headers:
|
25
|
+
vlc_command.append(f"--http-user-agent={self.headers.get('User-Agent')}")
|
26
|
+
|
27
|
+
if "Referer" in self.headers:
|
28
|
+
vlc_command.append(f"--http-referrer={self.headers.get('Referer')}")
|
29
|
+
|
30
|
+
vlc_command.append(extract_data.url)
|
31
|
+
subprocess.run(vlc_command, check=True)
|
32
|
+
except subprocess.CalledProcessError as e:
|
33
|
+
konsol.print(f"[red]VLC oynatma hatası: {e}[/red]")
|
34
|
+
except FileNotFoundError:
|
35
|
+
konsol.print("[red]VLC bulunamadı! VLC kurulu olduğundan emin olun.[/red]")
|
36
|
+
|
37
|
+
def play_with_mpv(self, extract_data: ExtractResult):
|
38
|
+
try:
|
39
|
+
mpv_command = ["mpv"]
|
40
|
+
|
41
|
+
if self.title:
|
42
|
+
mpv_command.append(f"--title={self.title}")
|
43
|
+
|
44
|
+
if "User-Agent" in self.headers:
|
45
|
+
mpv_command.append(f"--http-header-fields=User-Agent: {self.headers.get('User-Agent')}")
|
46
|
+
|
47
|
+
if "Referer" in self.headers:
|
48
|
+
mpv_command.append(f"--http-header-fields=Referer: {self.headers.get('Referer')}")
|
49
|
+
|
50
|
+
if "Cookie" in self.headers:
|
51
|
+
mpv_command.append(f"--http-header-fields=Cookie: {self.headers.get('Cookie')}")
|
52
|
+
|
53
|
+
mpv_command.append(extract_data.url)
|
54
|
+
subprocess.run(mpv_command, check=True)
|
55
|
+
except subprocess.CalledProcessError as e:
|
56
|
+
konsol.print(f"[red]mpv oynatma hatası: {e}[/red]")
|
57
|
+
except FileNotFoundError:
|
58
|
+
konsol.print("[red]mpv bulunamadı! mpv kurulu olduğundan emin olun.[/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 .PluginModels import SearchResult, MovieInfo
|
6
|
+
|
7
|
+
class PluginBase(ABC):
|
8
|
+
name = "Plugin"
|
9
|
+
main_url = "https://example.com"
|
10
|
+
|
11
|
+
def __init__(self):
|
12
|
+
self.oturum = AsyncClient(
|
13
|
+
headers = {
|
14
|
+
"User-Agent" : "keyiflerolsun/KekikStream",
|
15
|
+
"Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
16
|
+
},
|
17
|
+
timeout = Timeout(10.0),
|
18
|
+
)
|
19
|
+
|
20
|
+
@abstractmethod
|
21
|
+
async def search(self, query: str) -> list[SearchResult]:
|
22
|
+
"""Kullanıcı arama sorgusuna göre sonuç döndürür."""
|
23
|
+
pass
|
24
|
+
|
25
|
+
@abstractmethod
|
26
|
+
async def load_item(self, url: str) -> MovieInfo:
|
27
|
+
"""Bir medya öğesi hakkında detaylı bilgi döndürür."""
|
28
|
+
pass
|
29
|
+
|
30
|
+
@abstractmethod
|
31
|
+
async def load_links(self, url: str) -> list[str]:
|
32
|
+
"""Bir medya öğesi için oynatma bağlantılarını döndürür."""
|
33
|
+
pass
|
34
|
+
|
35
|
+
async def close(self):
|
36
|
+
await self.oturum.aclose()
|
37
|
+
|
38
|
+
def fix_url(self, partial_url: str):
|
39
|
+
if partial_url.startswith("http"):
|
40
|
+
return partial_url
|
41
|
+
|
42
|
+
return self.main_url.rstrip("/") + "/" + partial_url.lstrip("/")
|
@@ -0,0 +1,63 @@
|
|
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 importlib.util
|
7
|
+
import os
|
8
|
+
import traceback
|
9
|
+
|
10
|
+
class PluginLoader:
|
11
|
+
def __init__(self, plugins_dir: str):
|
12
|
+
self.local_plugins_dir = Path(plugins_dir).resolve()
|
13
|
+
self.global_plugins_dir = Path(__file__).parent.parent / plugins_dir
|
14
|
+
if not self.local_plugins_dir.exists() and not self.global_plugins_dir.exists():
|
15
|
+
konsol.log(f"[red][!] Extractor dizini bulunamadı: {self.plugins_dir}[/red]")
|
16
|
+
cikis_yap(False)
|
17
|
+
|
18
|
+
def load_all(self) -> dict[str, PluginBase]:
|
19
|
+
plugins = {}
|
20
|
+
|
21
|
+
if self.global_plugins_dir.exists():
|
22
|
+
konsol.log(f"[green][*] Global Plugin dizininden yükleniyor: {self.global_plugins_dir}[/green]")
|
23
|
+
plugins.update(self._load_from_directory(self.global_plugins_dir))
|
24
|
+
|
25
|
+
if self.local_plugins_dir.exists():
|
26
|
+
konsol.log(f"[green][*] Yerel Plugin dizininden yükleniyor: {self.local_plugins_dir}[/green]")
|
27
|
+
plugins.update(self._load_from_directory(self.local_plugins_dir))
|
28
|
+
|
29
|
+
if not plugins:
|
30
|
+
konsol.print("[yellow][!] Yüklenecek bir Plugin bulunamadı![/yellow]")
|
31
|
+
|
32
|
+
return plugins
|
33
|
+
|
34
|
+
def _load_from_directory(self, directory: Path) -> dict[str, PluginBase]:
|
35
|
+
plugins = {}
|
36
|
+
for file in os.listdir(directory):
|
37
|
+
if file.endswith(".py") and not file.startswith("__"):
|
38
|
+
module_name = file[:-3]
|
39
|
+
if plugin := self._load_plugin(directory, module_name):
|
40
|
+
plugins[module_name] = plugin
|
41
|
+
|
42
|
+
return plugins
|
43
|
+
|
44
|
+
def _load_plugin(self, directory: Path, module_name: str):
|
45
|
+
try:
|
46
|
+
path = directory / f"{module_name}.py"
|
47
|
+
spec = importlib.util.spec_from_file_location(module_name, path)
|
48
|
+
if not spec or not spec.loader:
|
49
|
+
raise ImportError(f"Spec oluşturulamadı: {module_name}")
|
50
|
+
|
51
|
+
module = importlib.util.module_from_spec(spec)
|
52
|
+
spec.loader.exec_module(module)
|
53
|
+
|
54
|
+
for attr in dir(module):
|
55
|
+
obj = getattr(module, attr)
|
56
|
+
if isinstance(obj, type) and issubclass(obj, PluginBase) and obj is not PluginBase:
|
57
|
+
return obj()
|
58
|
+
|
59
|
+
except Exception as hata:
|
60
|
+
konsol.print(f"[red][!] Plugin yüklenirken hata oluştu: {module_name}\nHata: {hata}")
|
61
|
+
konsol.print(f"[dim]{traceback.format_exc()}[/dim]")
|
62
|
+
|
63
|
+
return None
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
2
|
+
|
3
|
+
from pydantic import BaseModel, validator
|
4
|
+
from typing import 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
|
+
@validator("tags", pre=True)
|
27
|
+
def convert_tags(cls, value):
|
28
|
+
return ", ".join(value) if isinstance(value, list) else value
|
29
|
+
|
30
|
+
@validator("actors", pre=True)
|
31
|
+
def convert_actors(cls, value):
|
32
|
+
return ", ".join(value) if isinstance(value, list) 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
|
6
|
+
from .ExtractorBase import ExtractorBase
|
7
|
+
from .ExtractorLoader import ExtractorLoader
|
8
|
+
from .ExtractorModels import ExtractResult
|
9
|
+
from .MediaHandler import MediaHandler
|
@@ -0,0 +1,30 @@
|
|
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 httpx import AsyncClient
|
6
|
+
from base64 import b64decode
|
7
|
+
|
8
|
+
class CloseLoadExtractor(ExtractorBase):
|
9
|
+
name = "CloseLoad"
|
10
|
+
main_url = "https://closeload.filmmakinesi.de"
|
11
|
+
|
12
|
+
async def extract(self, url, referer=None) -> ExtractResult:
|
13
|
+
"""Bir URL'den medya bilgilerini çıkarır."""
|
14
|
+
async with AsyncClient() as client:
|
15
|
+
response = await client.get(url, headers={"Referer": referer} if referer else {})
|
16
|
+
response.raise_for_status()
|
17
|
+
|
18
|
+
selector = Selector(response.text)
|
19
|
+
atob = selector.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
|
+
return ExtractResult(
|
26
|
+
name = self.name,
|
27
|
+
url = m3u_link,
|
28
|
+
referer = self.main_url,
|
29
|
+
subtitles = []
|
30
|
+
)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
2
|
+
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult
|
4
|
+
from httpx import AsyncClient
|
5
|
+
import json
|
6
|
+
|
7
|
+
class MailRuExtractor(ExtractorBase):
|
8
|
+
name = "MailRu"
|
9
|
+
main_url = "https://my.mail.ru"
|
10
|
+
|
11
|
+
async def extract(self, url, referer=None) -> ExtractResult:
|
12
|
+
"""Mail.Ru URL'sinden medya bilgilerini çıkarır."""
|
13
|
+
vid_id = url.split("video/embed/")[-1].strip()
|
14
|
+
video_meta_url = f"{self.main_url}/+/video/meta/{vid_id}"
|
15
|
+
|
16
|
+
async with AsyncClient() as client:
|
17
|
+
response = await client.get(video_meta_url, headers={"Referer": url} if referer else {})
|
18
|
+
response.raise_for_status()
|
19
|
+
|
20
|
+
video_key = response.cookies.get("video_key")
|
21
|
+
if not video_key:
|
22
|
+
raise ValueError("Video key bulunamadı.")
|
23
|
+
|
24
|
+
video_data = json.loads(response.text)
|
25
|
+
videos = video_data.get("videos", [])
|
26
|
+
if not videos:
|
27
|
+
raise ValueError("Videolar bulunamadı.")
|
28
|
+
|
29
|
+
video = videos[0]
|
30
|
+
video_url = video["url"]
|
31
|
+
if video_url.startswith("//"):
|
32
|
+
video_url = f"https:{video_url}"
|
33
|
+
|
34
|
+
return ExtractResult(
|
35
|
+
name = self.name,
|
36
|
+
url = video_url,
|
37
|
+
referer = self.main_url,
|
38
|
+
subtitles = [],
|
39
|
+
headers = {"Cookie": f"video_key={video_key}"}
|
40
|
+
)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
2
|
+
|
3
|
+
from ..Core import ExtractorLoader, ExtractorBase
|
4
|
+
|
5
|
+
class ExtractorManager:
|
6
|
+
def __init__(self, extractor_dir="Extractors"):
|
7
|
+
self.extractor_loader = ExtractorLoader(extractor_dir)
|
8
|
+
self.extractors = self.extractor_loader.load_all()
|
9
|
+
|
10
|
+
def find_extractor(self, link):
|
11
|
+
for extractor_cls in self.extractors:
|
12
|
+
extractor:ExtractorBase = extractor_cls()
|
13
|
+
if extractor.can_handle_url(link):
|
14
|
+
return extractor
|
15
|
+
|
16
|
+
return None
|
17
|
+
|
18
|
+
def map_links_to_extractors(self, links):
|
19
|
+
mapping = {}
|
20
|
+
for link in links:
|
21
|
+
for extractor_cls in self.extractors:
|
22
|
+
extractor:ExtractorBase = extractor_cls()
|
23
|
+
if extractor.can_handle_url(link):
|
24
|
+
mapping[link] = extractor.name
|
25
|
+
break
|
26
|
+
|
27
|
+
return mapping
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
2
|
+
|
3
|
+
from ..Core import MediaHandler
|
4
|
+
|
5
|
+
class MediaManager:
|
6
|
+
def __init__(self):
|
7
|
+
self.media_handler = MediaHandler()
|
8
|
+
|
9
|
+
def set_title(self, title):
|
10
|
+
self.media_handler.title = title
|
11
|
+
|
12
|
+
def get_title(self):
|
13
|
+
return self.media_handler.title
|
14
|
+
|
15
|
+
def set_headers(self, headers):
|
16
|
+
self.media_handler.headers.update(headers)
|
17
|
+
|
18
|
+
def play_media(self, extract_data):
|
19
|
+
self.media_handler.play_with_vlc(extract_data)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
2
|
+
|
3
|
+
from ..Core import PluginLoader
|
4
|
+
|
5
|
+
class PluginManager:
|
6
|
+
def __init__(self, plugin_dir="Plugins"):
|
7
|
+
self.plugin_loader = PluginLoader(plugin_dir)
|
8
|
+
self.plugins = self.plugin_loader.load_all()
|
9
|
+
|
10
|
+
def get_plugin_names(self):
|
11
|
+
return list(self.plugins.keys())
|
12
|
+
|
13
|
+
def select_plugin(self, plugin_name):
|
14
|
+
return self.plugins.get(plugin_name)
|
15
|
+
|
16
|
+
async def close_plugins(self):
|
17
|
+
for plugin in self.plugins.values():
|
18
|
+
await plugin.close()
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
2
|
+
|
3
|
+
from ..CLI import konsol
|
4
|
+
from rich.panel import Panel
|
5
|
+
from rich.table import Table
|
6
|
+
from InquirerPy import inquirer
|
7
|
+
import os
|
8
|
+
|
9
|
+
class UIManager:
|
10
|
+
@staticmethod
|
11
|
+
def clear_console():
|
12
|
+
os.system("cls" if os.name == "nt" else "clear")
|
13
|
+
|
14
|
+
@staticmethod
|
15
|
+
async def select_from_list(message, choices):
|
16
|
+
return await inquirer.select(message=message, choices=choices).execute_async()
|
17
|
+
|
18
|
+
@staticmethod
|
19
|
+
async def prompt_text(message):
|
20
|
+
return await inquirer.text(message=message).execute_async()
|
21
|
+
|
22
|
+
@staticmethod
|
23
|
+
def display_media_info(plugin_name, media_info):
|
24
|
+
table = Table(show_header=False, box=None)
|
25
|
+
table.add_column(justify="right", style="cyan", no_wrap=True)
|
26
|
+
table.add_column(style="magenta")
|
27
|
+
|
28
|
+
for key, value in media_info.dict().items():
|
29
|
+
if value:
|
30
|
+
table.add_row(f"[bold cyan]{key.capitalize()}[/bold cyan]", str(value))
|
31
|
+
|
32
|
+
konsol.print(Panel(table, title=f"[bold green]{plugin_name}[/bold green]", expand=False))
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
2
|
+
|
3
|
+
from KekikStream.Core import PluginBase, SearchResult, MovieInfo
|
4
|
+
from parsel import Selector
|
5
|
+
|
6
|
+
class FilmMakinesi(PluginBase):
|
7
|
+
name = "FilmMakinesi"
|
8
|
+
main_url = "https://filmmakinesi.de"
|
9
|
+
|
10
|
+
async def search(self, query: str) -> list[SearchResult]:
|
11
|
+
response = await self.oturum.get(f"{self.main_url}/?s={query}")
|
12
|
+
selector = Selector(response.text)
|
13
|
+
|
14
|
+
results = []
|
15
|
+
for article in selector.css("section#film_posts article"):
|
16
|
+
title = article.css("h6 a::text").get()
|
17
|
+
href = article.css("h6 a::attr(href)").get()
|
18
|
+
poster = article.css("img::attr(data-src)").get() or article.css("img::attr(src)").get()
|
19
|
+
|
20
|
+
if title and href:
|
21
|
+
results.append(
|
22
|
+
SearchResult(
|
23
|
+
title = title.strip(),
|
24
|
+
url = self.fix_url(href.strip()),
|
25
|
+
poster = self.fix_url(poster.strip()) if poster else None,
|
26
|
+
)
|
27
|
+
)
|
28
|
+
|
29
|
+
return results
|
30
|
+
|
31
|
+
async def load_item(self, url: str) -> MovieInfo:
|
32
|
+
response = await self.oturum.get(url)
|
33
|
+
selector = Selector(response.text)
|
34
|
+
|
35
|
+
title = selector.css("h1.single_h1 a::text").get(default="").strip()
|
36
|
+
poster = selector.css("[property='og:image']::attr(content)").get(default="").strip()
|
37
|
+
description = selector.css("section#film_single article p:last-of-type::text").get(default="").strip()
|
38
|
+
tags = selector.css("dt:contains('Tür:') + dd a::text").get(default="").strip()
|
39
|
+
rating = selector.css("dt:contains('IMDB Puanı:') + dd::text").get(default="").strip()
|
40
|
+
year = selector.css("dt:contains('Yapım Yılı:') + dd a::text").get(default="").strip()
|
41
|
+
actors = selector.css("dt:contains('Oyuncular:') + dd::text").get(default="").strip()
|
42
|
+
duration = selector.css("dt:contains('Film Süresi:') + dd time::attr(datetime)").get(default="").strip()
|
43
|
+
|
44
|
+
duration_minutes = 0
|
45
|
+
if duration and duration.startswith("PT") and duration.endswith("M"):
|
46
|
+
duration_minutes = int(duration[2:-1])
|
47
|
+
|
48
|
+
return MovieInfo(
|
49
|
+
url = url,
|
50
|
+
poster = self.fix_url(poster),
|
51
|
+
title = title,
|
52
|
+
description = description,
|
53
|
+
tags = tags,
|
54
|
+
rating = rating,
|
55
|
+
year = year,
|
56
|
+
actors = actors,
|
57
|
+
duration = duration_minutes
|
58
|
+
)
|
59
|
+
|
60
|
+
async def load_links(self, url: str) -> list[str]:
|
61
|
+
response = await self.oturum.get(url)
|
62
|
+
selector = Selector(response.text)
|
63
|
+
|
64
|
+
iframe_src = selector.css("div.player-div iframe::attr(src)").get() or selector.css("div.player-div iframe::attr(data-src)").get()
|
65
|
+
return [self.fix_url(iframe_src)] if iframe_src else []
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
2
|
+
|
3
|
+
from KekikStream.Core import PluginBase, SearchResult, MovieInfo
|
4
|
+
from parsel import Selector
|
5
|
+
|
6
|
+
class UgurFilm(PluginBase):
|
7
|
+
name = "UgurFilm"
|
8
|
+
main_url = "https://ugurfilm8.com"
|
9
|
+
|
10
|
+
async def search(self, query: str) -> list[SearchResult]:
|
11
|
+
response = await self.oturum.get(f"{self.main_url}/?s={query}")
|
12
|
+
selector = Selector(response.text)
|
13
|
+
|
14
|
+
results = []
|
15
|
+
for article in selector.css("div.icerik div"):
|
16
|
+
title = article.css("span:nth-child(1)::text").get()
|
17
|
+
href = article.css("a::attr(href)").get()
|
18
|
+
poster = article.css("img::attr(src)").get()
|
19
|
+
|
20
|
+
if title and href:
|
21
|
+
results.append(
|
22
|
+
SearchResult(
|
23
|
+
title = title.strip(),
|
24
|
+
url = self.fix_url(href.strip()),
|
25
|
+
poster = self.fix_url(poster.strip()) if poster else None,
|
26
|
+
)
|
27
|
+
)
|
28
|
+
|
29
|
+
return results
|
30
|
+
|
31
|
+
async def load_item(self, url: str) -> MovieInfo:
|
32
|
+
response = await self.oturum.get(url)
|
33
|
+
selector = Selector(response.text)
|
34
|
+
|
35
|
+
title = selector.css("div.bilgi h2::text").get(default="").strip()
|
36
|
+
poster = selector.css("div.resim img::attr(src)").get(default="").strip()
|
37
|
+
description = selector.css("div.slayt-aciklama::text").get(default="").strip()
|
38
|
+
tags = selector.css("p.tur a[href*='/category/']::text").getall()
|
39
|
+
year = selector.css("a[href*='/yil/']::text").re_first(r"\d+")
|
40
|
+
actors = [actor.css("span::text").get() for actor in selector.css("li.oyuncu-k")]
|
41
|
+
|
42
|
+
return MovieInfo(
|
43
|
+
url = self.fix_url(url),
|
44
|
+
poster = self.fix_url(poster),
|
45
|
+
title = title,
|
46
|
+
description = description,
|
47
|
+
tags = tags,
|
48
|
+
year = year,
|
49
|
+
actors = actors,
|
50
|
+
)
|
51
|
+
|
52
|
+
async def load_links(self, url: str) -> list[str]:
|
53
|
+
response = await self.oturum.get(url)
|
54
|
+
selector = Selector(response.text)
|
55
|
+
results = []
|
56
|
+
|
57
|
+
for part_link in selector.css("li.parttab a::attr(href)").getall():
|
58
|
+
sub_response = await self.oturum.get(part_link)
|
59
|
+
sub_selector = Selector(sub_response.text)
|
60
|
+
|
61
|
+
iframe = sub_selector.css("div#vast iframe::attr(src)").get()
|
62
|
+
if iframe and self.main_url in iframe:
|
63
|
+
post_data = {
|
64
|
+
"vid" : iframe.split("vid=")[-1],
|
65
|
+
"alternative" : "default",
|
66
|
+
"ord" : "1",
|
67
|
+
}
|
68
|
+
player_response = await self.oturum.post(
|
69
|
+
url = f"{self.main_url}/player/ajax_sources.php",
|
70
|
+
data = post_data
|
71
|
+
)
|
72
|
+
iframe = player_response.json().get("iframe")
|
73
|
+
results.append(iframe)
|
74
|
+
|
75
|
+
return results
|