KekikStream 2.0.9__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Extractor/ExtractorBase.py +7 -2
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Plugin/PluginBase.py +61 -13
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/CloseLoad.py +17 -2
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/ContentX.py +6 -2
- kekikstream-2.2.0/KekikStream/Extractors/Filemoon.py +78 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/MixTiger.py +5 -5
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/RapidVid.py +15 -5
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/SetPlay.py +6 -3
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VidHide.py +11 -2
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/BelgeselX.py +7 -15
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/DiziBox.py +8 -12
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/DiziPal.py +11 -22
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/DiziYou.py +9 -17
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/Dizilla.py +4 -7
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/FilmBip.py +5 -8
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/FilmMakinesi.py +30 -20
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/FilmModu.py +8 -16
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/FullHDFilm.py +83 -27
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/FullHDFilmizlesene.py +5 -7
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/HDFilmCehennemi.py +85 -84
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/JetFilmizle.py +28 -13
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/KultFilmler.py +12 -14
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/RecTV.py +16 -24
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/RoketDizi.py +6 -9
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SelcukFlix.py +71 -51
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SetFilmIzle.py +24 -31
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SezonlukDizi.py +22 -9
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SineWix.py +13 -21
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/Sinefy.py +6 -6
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SinemaCX.py +34 -33
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/Sinezy.py +5 -8
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/SuperFilmGeldi.py +33 -30
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Plugins/UgurFilm.py +6 -8
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/__init__.py +17 -36
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/PKG-INFO +6 -3
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/SOURCES.txt +1 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/PKG-INFO +6 -3
- {kekikstream-2.0.9 → kekikstream-2.2.0}/README.md +5 -2
- {kekikstream-2.0.9 → kekikstream-2.2.0}/setup.py +1 -1
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/CLI/__init__.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/CLI/pypi_kontrol.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Extractor/ExtractorModels.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Extractor/YTDLPCache.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Media/MediaHandler.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Media/MediaManager.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Plugin/PluginManager.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/Plugin/PluginModels.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/UI/UIManager.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Core/__init__.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/DonilasPlay.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/DzenRu.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/ExPlay.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/HDPlayerSystem.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/JetTv.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/MailRu.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/MixPlayHD.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/MolyStream.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/Odnoklassniki.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/PeaceMakerst.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/PixelDrain.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/PlayerFilmIzle.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/SetPrime.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/SibNet.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/Sobreatsesuyp.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/TRsTX.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/TauVideo.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/TurboImgz.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/TurkeyPlayer.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VCTPlay.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VidMoly.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VidMoxy.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VidPapi.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/VideoSeyred.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/YTDLP.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/Extractors/YildizKisaFilm.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/__main__.py +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream/requirements.txt +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/dependency_links.txt +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/entry_points.txt +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/requires.txt +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/KekikStream.egg-info/top_level.txt +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/LICENSE +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/MANIFEST.in +0 -0
- {kekikstream-2.0.9 → kekikstream-2.2.0}/setup.cfg +0 -0
|
@@ -5,7 +5,7 @@ from cloudscraper import CloudScraper
|
|
|
5
5
|
from httpx import AsyncClient
|
|
6
6
|
from typing import Optional
|
|
7
7
|
from .ExtractorModels import ExtractResult
|
|
8
|
-
from urllib.parse import urljoin
|
|
8
|
+
from urllib.parse import urljoin, urlparse
|
|
9
9
|
|
|
10
10
|
class ExtractorBase(ABC):
|
|
11
11
|
# Çıkarıcının temel özellikleri
|
|
@@ -29,6 +29,11 @@ class ExtractorBase(ABC):
|
|
|
29
29
|
# URL'nin bu çıkarıcı tarafından işlenip işlenemeyeceğini kontrol et
|
|
30
30
|
return self.main_url in url
|
|
31
31
|
|
|
32
|
+
def get_base_url(self, url: str) -> str:
|
|
33
|
+
"""URL'den base URL'i çıkar (scheme + netloc)"""
|
|
34
|
+
parsed = urlparse(url)
|
|
35
|
+
return f"{parsed.scheme}://{parsed.netloc}"
|
|
36
|
+
|
|
32
37
|
@abstractmethod
|
|
33
38
|
async def extract(self, url: str, referer: Optional[str] = None) -> ExtractResult:
|
|
34
39
|
# Alt sınıflar tarafından uygulanacak medya çıkarma fonksiyonu
|
|
@@ -46,4 +51,4 @@ class ExtractorBase(ABC):
|
|
|
46
51
|
if url.startswith("http") or url.startswith("{\""):
|
|
47
52
|
return url
|
|
48
53
|
|
|
49
|
-
return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
|
|
54
|
+
return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
+
from ...CLI import konsol
|
|
3
4
|
from abc import ABC, abstractmethod
|
|
4
5
|
from cloudscraper import CloudScraper
|
|
5
6
|
from httpx import AsyncClient
|
|
6
|
-
from .PluginModels import MainPageResult, SearchResult, MovieInfo
|
|
7
|
+
from .PluginModels import MainPageResult, SearchResult, MovieInfo, SeriesInfo
|
|
7
8
|
from ..Media.MediaHandler import MediaHandler
|
|
8
9
|
from ..Extractor.ExtractorManager import ExtractorManager
|
|
10
|
+
from ..Extractor.ExtractorModels import ExtractResult
|
|
9
11
|
from urllib.parse import urljoin
|
|
10
12
|
import re
|
|
11
13
|
|
|
@@ -53,31 +55,31 @@ class PluginBase(ABC):
|
|
|
53
55
|
pass
|
|
54
56
|
|
|
55
57
|
@abstractmethod
|
|
56
|
-
async def load_item(self, url: str) -> MovieInfo:
|
|
58
|
+
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
57
59
|
"""Bir medya öğesi hakkında detaylı bilgi döndürür."""
|
|
58
60
|
pass
|
|
59
61
|
|
|
60
62
|
@abstractmethod
|
|
61
|
-
async def load_links(self, url: str) -> list[
|
|
63
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
62
64
|
"""
|
|
63
65
|
Bir medya öğesi için oynatma bağlantılarını döndürür.
|
|
64
|
-
|
|
66
|
+
|
|
65
67
|
Args:
|
|
66
68
|
url: Medya URL'si
|
|
67
|
-
|
|
69
|
+
|
|
68
70
|
Returns:
|
|
69
|
-
|
|
71
|
+
ExtractResult listesi, her biri şu alanları içerir:
|
|
70
72
|
- url (str, zorunlu): Video URL'si
|
|
71
73
|
- name (str, zorunlu): Gösterim adı (tüm bilgileri içerir)
|
|
72
74
|
- referer (str, opsiyonel): Referer header
|
|
73
|
-
- subtitles (list, opsiyonel): Altyazı listesi
|
|
74
|
-
|
|
75
|
+
- subtitles (list[Subtitle], opsiyonel): Altyazı listesi
|
|
76
|
+
|
|
75
77
|
Example:
|
|
76
78
|
[
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
ExtractResult(
|
|
80
|
+
url="https://example.com/video.m3u8",
|
|
81
|
+
name="HDFilmCehennemi | 1080p TR Dublaj"
|
|
82
|
+
)
|
|
81
83
|
]
|
|
82
84
|
"""
|
|
83
85
|
pass
|
|
@@ -95,6 +97,40 @@ class PluginBase(ABC):
|
|
|
95
97
|
|
|
96
98
|
return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
|
|
97
99
|
|
|
100
|
+
async def extract(self, url: str, referer: str = None, prefix: str | None = None) -> ExtractResult | None:
|
|
101
|
+
"""
|
|
102
|
+
Extractor ile video URL'sini çıkarır.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
url: Iframe veya video URL'si
|
|
106
|
+
referer: Referer header (varsayılan: plugin main_url)
|
|
107
|
+
prefix: İsmin başına eklenecek opsiyonel etiket (örn: "Türkçe Dublaj")
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
ExtractResult: Extractor sonucu (name prefix ile birleştirilmiş) veya None
|
|
111
|
+
|
|
112
|
+
Extractor bulunamadığında veya hata oluştuğunda uyarı verir.
|
|
113
|
+
"""
|
|
114
|
+
if referer is None:
|
|
115
|
+
referer = f"{self.main_url}/"
|
|
116
|
+
|
|
117
|
+
extractor = self.ex_manager.find_extractor(url)
|
|
118
|
+
if not extractor:
|
|
119
|
+
konsol.log(f"[magenta][?] {self.name} » Extractor bulunamadı: {url}")
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
data = await extractor.extract(url, referer=referer)
|
|
124
|
+
|
|
125
|
+
# prefix varsa name'e ekle
|
|
126
|
+
if prefix and data.name:
|
|
127
|
+
data.name = f"{prefix} | {data.name}"
|
|
128
|
+
|
|
129
|
+
return data
|
|
130
|
+
except Exception as hata:
|
|
131
|
+
konsol.log(f"[red][!] {self.name} » Extractor hatası ({extractor.name}): {hata}")
|
|
132
|
+
return None
|
|
133
|
+
|
|
98
134
|
@staticmethod
|
|
99
135
|
def clean_title(title: str) -> str:
|
|
100
136
|
suffixes = [
|
|
@@ -119,4 +155,16 @@ class PluginBase(ABC):
|
|
|
119
155
|
for suffix in suffixes:
|
|
120
156
|
cleaned_title = re.sub(f"{re.escape(suffix)}.*$", "", cleaned_title, flags=re.IGNORECASE).strip()
|
|
121
157
|
|
|
122
|
-
return cleaned_title
|
|
158
|
+
return cleaned_title
|
|
159
|
+
|
|
160
|
+
async def play(self, **kwargs):
|
|
161
|
+
"""
|
|
162
|
+
Varsayılan oynatma metodu.
|
|
163
|
+
Tüm pluginlerde ortak kullanılır.
|
|
164
|
+
"""
|
|
165
|
+
extract_result = ExtractResult(**kwargs)
|
|
166
|
+
self.media_handler.title = kwargs.get("name")
|
|
167
|
+
if self.name not in self.media_handler.title:
|
|
168
|
+
self.media_handler.title = f"{self.name} | {self.media_handler.title}"
|
|
169
|
+
|
|
170
|
+
self.media_handler.play_media(extract_result)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
4
|
from Kekik.Sifreleme import Packer, StreamDecoder
|
|
5
|
+
from parsel import Selector
|
|
5
6
|
import re
|
|
6
7
|
|
|
7
8
|
class CloseLoadExtractor(ExtractorBase):
|
|
@@ -15,12 +16,26 @@ class CloseLoadExtractor(ExtractorBase):
|
|
|
15
16
|
istek = await self.httpx.get(url)
|
|
16
17
|
istek.raise_for_status()
|
|
17
18
|
|
|
19
|
+
# Video URL'sini çıkar
|
|
18
20
|
eval_func = re.compile(r'\s*(eval\(function[\s\S].*)\s*').findall(istek.text)[0]
|
|
19
21
|
m3u_link = StreamDecoder.extract_stream_url(Packer.unpack(eval_func))
|
|
20
22
|
|
|
23
|
+
# Subtitle'ları parse et (Kotlin referansı: track elementleri)
|
|
24
|
+
subtitles = []
|
|
25
|
+
secici = Selector(istek.text)
|
|
26
|
+
for track in secici.css("track"):
|
|
27
|
+
raw_src = track.css("::attr(src)").get() or ""
|
|
28
|
+
raw_src = raw_src.strip()
|
|
29
|
+
label = track.css("::attr(label)").get() or track.css("::attr(srclang)").get() or "Altyazı"
|
|
30
|
+
|
|
31
|
+
if raw_src:
|
|
32
|
+
full_url = raw_src if raw_src.startswith("http") else f"{self.main_url}{raw_src}"
|
|
33
|
+
subtitles.append(Subtitle(name=label, url=full_url))
|
|
34
|
+
|
|
21
35
|
return ExtractResult(
|
|
22
36
|
name = self.name,
|
|
23
37
|
url = m3u_link,
|
|
24
38
|
referer = self.main_url,
|
|
25
|
-
subtitles =
|
|
39
|
+
subtitles = subtitles
|
|
26
40
|
)
|
|
41
|
+
|
|
@@ -24,6 +24,9 @@ class ContentX(ExtractorBase):
|
|
|
24
24
|
if referer:
|
|
25
25
|
self.httpx.headers.update({"Referer": referer})
|
|
26
26
|
|
|
27
|
+
# Dinamik base URL kullan
|
|
28
|
+
base_url = self.get_base_url(url)
|
|
29
|
+
|
|
27
30
|
istek = await self.httpx.get(url)
|
|
28
31
|
istek.raise_for_status()
|
|
29
32
|
i_source = istek.text
|
|
@@ -52,7 +55,8 @@ class ContentX(ExtractorBase):
|
|
|
52
55
|
)
|
|
53
56
|
)
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
# base_url kullan (contentx.me yerine)
|
|
59
|
+
vid_source_request = await self.httpx.get(f"{base_url}/source2.php?v={i_extract_value}", headers={"Referer": referer or base_url})
|
|
56
60
|
vid_source_request.raise_for_status()
|
|
57
61
|
|
|
58
62
|
vid_source = vid_source_request.text
|
|
@@ -72,7 +76,7 @@ class ContentX(ExtractorBase):
|
|
|
72
76
|
|
|
73
77
|
if i_dublaj := re.search(r',\"([^"]+)\",\"Türkçe"', i_source):
|
|
74
78
|
dublaj_value = i_dublaj[1]
|
|
75
|
-
dublaj_source_request = await self.httpx.get(f"{
|
|
79
|
+
dublaj_source_request = await self.httpx.get(f"{base_url}/source2.php?v={dublaj_value}", headers={"Referer": referer or base_url})
|
|
76
80
|
dublaj_source_request.raise_for_status()
|
|
77
81
|
|
|
78
82
|
dublaj_source = dublaj_source_request.text
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
+
from Kekik.Sifreleme import Packer
|
|
5
|
+
from parsel import Selector
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
class Filemoon(ExtractorBase):
|
|
9
|
+
name = "Filemoon"
|
|
10
|
+
main_url = "https://filemoon.to"
|
|
11
|
+
|
|
12
|
+
# Filemoon'un farklı domainlerini destekle
|
|
13
|
+
supported_domains = [
|
|
14
|
+
"filemoon.to",
|
|
15
|
+
"filemoon.in",
|
|
16
|
+
"filemoon.sx",
|
|
17
|
+
"filemoon.nl",
|
|
18
|
+
"filemoon.com"
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
def can_handle_url(self, url: str) -> bool:
|
|
22
|
+
return any(domain in url for domain in self.supported_domains)
|
|
23
|
+
|
|
24
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
25
|
+
headers = {
|
|
26
|
+
"Referer" : url,
|
|
27
|
+
"Sec-Fetch-Dest" : "iframe",
|
|
28
|
+
"Sec-Fetch-Mode" : "navigate",
|
|
29
|
+
"Sec-Fetch-Site" : "cross-site",
|
|
30
|
+
}
|
|
31
|
+
self.httpx.headers.update(headers)
|
|
32
|
+
|
|
33
|
+
# İlk sayfayı al
|
|
34
|
+
istek = await self.httpx.get(url)
|
|
35
|
+
response = istek.text
|
|
36
|
+
secici = Selector(response)
|
|
37
|
+
|
|
38
|
+
# Eğer iframe varsa, iframe'e git
|
|
39
|
+
iframe_src = secici.css("iframe::attr(src)").get()
|
|
40
|
+
if iframe_src:
|
|
41
|
+
iframe_url = self.fix_url(iframe_src)
|
|
42
|
+
self.httpx.headers.update({
|
|
43
|
+
"Accept-Language" : "en-US,en;q=0.5",
|
|
44
|
+
"Sec-Fetch-Dest" : "iframe"
|
|
45
|
+
})
|
|
46
|
+
istek = await self.httpx.get(iframe_url)
|
|
47
|
+
response = istek.text
|
|
48
|
+
|
|
49
|
+
# Packed script'i bul ve unpack et
|
|
50
|
+
m3u8_url = None
|
|
51
|
+
|
|
52
|
+
if Packer.detect_packed(response):
|
|
53
|
+
try:
|
|
54
|
+
unpacked = Packer.unpack(response)
|
|
55
|
+
# sources:[{file:"..." pattern'ını ara
|
|
56
|
+
if match := re.search(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"', unpacked):
|
|
57
|
+
m3u8_url = match.group(1)
|
|
58
|
+
elif match := re.search(r'file:\s*"([^"]*?\.m3u8[^"]*)"', unpacked):
|
|
59
|
+
m3u8_url = match.group(1)
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
# Fallback: Doğrudan response'ta ara
|
|
64
|
+
if not m3u8_url:
|
|
65
|
+
if match := re.search(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"', response):
|
|
66
|
+
m3u8_url = match.group(1)
|
|
67
|
+
elif match := re.search(r'file:\s*"([^"]*?\.m3u8[^"]*)"', response):
|
|
68
|
+
m3u8_url = match.group(1)
|
|
69
|
+
|
|
70
|
+
if not m3u8_url:
|
|
71
|
+
raise ValueError(f"Filemoon: Video URL bulunamadı. {url}")
|
|
72
|
+
|
|
73
|
+
return ExtractResult(
|
|
74
|
+
name = self.name,
|
|
75
|
+
url = self.fix_url(m3u8_url),
|
|
76
|
+
referer = f"{self.main_url}/",
|
|
77
|
+
subtitles = []
|
|
78
|
+
)
|
|
@@ -37,16 +37,15 @@ class MixTiger(ExtractorBase):
|
|
|
37
37
|
if not m3u_link:
|
|
38
38
|
raise ValueError("Video URL not found in response")
|
|
39
39
|
|
|
40
|
-
# Recursive extraction check
|
|
40
|
+
# Recursive extraction check - başka extractor kullanılabilir mi?
|
|
41
41
|
try:
|
|
42
42
|
from KekikStream.Core.Extractor.ExtractorManager import ExtractorManager
|
|
43
|
-
# Import inside method to avoid circular dependency
|
|
44
43
|
manager = ExtractorManager()
|
|
45
44
|
if nested_extractor := manager.find_extractor(m3u_link):
|
|
46
|
-
#
|
|
47
|
-
return await nested_extractor.extract(m3u_link, referer=
|
|
45
|
+
# Nested extractor ile çıkar
|
|
46
|
+
return await nested_extractor.extract(m3u_link, referer=ext_ref)
|
|
48
47
|
except Exception:
|
|
49
|
-
#
|
|
48
|
+
# Recursive extraction başarısız olursa standart sonucu döndür
|
|
50
49
|
pass
|
|
51
50
|
|
|
52
51
|
return ExtractResult(
|
|
@@ -55,3 +54,4 @@ class MixTiger(ExtractorBase):
|
|
|
55
54
|
referer = None if "disk.yandex" in m3u_link else ext_ref,
|
|
56
55
|
subtitles = []
|
|
57
56
|
)
|
|
57
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
|
-
from Kekik.Sifreleme import Packer, HexCodec
|
|
4
|
+
from Kekik.Sifreleme import Packer, HexCodec, StreamDecoder
|
|
5
5
|
import re, base64
|
|
6
6
|
|
|
7
7
|
class RapidVid(ExtractorBase):
|
|
@@ -39,15 +39,25 @@ class RapidVid(ExtractorBase):
|
|
|
39
39
|
subtitles.append(Subtitle(name=decoded_lang, url=sub_url.replace("\\", "")))
|
|
40
40
|
|
|
41
41
|
try:
|
|
42
|
+
decoded_url = None
|
|
43
|
+
|
|
44
|
+
# Method 1: file": "..." pattern (HexCodec)
|
|
42
45
|
if extracted_value := re.search(r'file": "(.*)",', istek.text):
|
|
43
46
|
escaped_hex = extracted_value[1]
|
|
44
47
|
decoded_url = HexCodec.decode(escaped_hex)
|
|
45
|
-
else:
|
|
46
|
-
av_encoded = re.search(r"av\('([^']+)'\)", istek.text)
|
|
47
|
-
if not av_encoded:
|
|
48
|
-
raise ValueError("AV encoding not found.")
|
|
49
48
|
|
|
49
|
+
# Method 2: av('...') pattern
|
|
50
|
+
elif av_encoded := re.search(r"av\('([^']+)'\)", istek.text):
|
|
50
51
|
decoded_url = self.decode_secret(av_encoded[1])
|
|
52
|
+
|
|
53
|
+
# Method 3: Packed script with dc_* function (StreamDecoder)
|
|
54
|
+
elif Packer.detect_packed(istek.text):
|
|
55
|
+
unpacked = Packer.unpack(istek.text)
|
|
56
|
+
decoded_url = StreamDecoder.extract_stream_url(unpacked)
|
|
57
|
+
|
|
58
|
+
if not decoded_url:
|
|
59
|
+
raise ValueError("No valid video URL pattern found.")
|
|
60
|
+
|
|
51
61
|
except Exception as hata:
|
|
52
62
|
raise RuntimeError(f"Extraction failed: {hata}") from hata
|
|
53
63
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
+
from urllib.parse import urlparse, parse_qs
|
|
4
5
|
import re
|
|
5
6
|
|
|
6
7
|
class SetPlay(ExtractorBase):
|
|
@@ -19,6 +20,9 @@ class SetPlay(ExtractorBase):
|
|
|
19
20
|
if referer:
|
|
20
21
|
self.httpx.headers.update({"Referer": referer})
|
|
21
22
|
|
|
23
|
+
# Dinamik base URL kullan
|
|
24
|
+
base_url = self.get_base_url(url)
|
|
25
|
+
|
|
22
26
|
istek = await self.httpx.get(url)
|
|
23
27
|
istek.raise_for_status()
|
|
24
28
|
|
|
@@ -39,7 +43,6 @@ class SetPlay(ExtractorBase):
|
|
|
39
43
|
title_base = title_match[1].split(".")[-1] if title_match else "Unknown"
|
|
40
44
|
|
|
41
45
|
# partKey logic
|
|
42
|
-
from urllib.parse import urlparse, parse_qs
|
|
43
46
|
parsed = urlparse(url)
|
|
44
47
|
params = parse_qs(parsed.query)
|
|
45
48
|
part_key = params.get("partKey", [""])[0]
|
|
@@ -52,8 +55,8 @@ class SetPlay(ExtractorBase):
|
|
|
52
55
|
else:
|
|
53
56
|
name_suffix = title_base
|
|
54
57
|
|
|
55
|
-
# M3U8 link oluştur
|
|
56
|
-
m3u_link = f"{
|
|
58
|
+
# M3U8 link oluştur - base_url kullan (main_url yerine)
|
|
59
|
+
m3u_link = f"{base_url}{video_url}?s={video_server}"
|
|
57
60
|
|
|
58
61
|
return ExtractResult(
|
|
59
62
|
name = f"{self.name} - {name_suffix}",
|
|
@@ -9,6 +9,12 @@ class VidHide(ExtractorBase):
|
|
|
9
9
|
name = "VidHide"
|
|
10
10
|
main_url = "https://vidhidepro.com"
|
|
11
11
|
|
|
12
|
+
# Birden fazla domain destekle
|
|
13
|
+
supported_domains = ["vidhidepro.com", "vidhide.com", "rubyvidhub.com"]
|
|
14
|
+
|
|
15
|
+
def can_handle_url(self, url: str) -> bool:
|
|
16
|
+
return any(domain in url for domain in self.supported_domains)
|
|
17
|
+
|
|
12
18
|
def get_embed_url(self, url: str) -> str:
|
|
13
19
|
if "/d/" in url:
|
|
14
20
|
return url.replace("/d/", "/v/")
|
|
@@ -20,6 +26,9 @@ class VidHide(ExtractorBase):
|
|
|
20
26
|
return url.replace("/f/", "/v/")
|
|
21
27
|
|
|
22
28
|
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
29
|
+
# Dinamik base URL kullan
|
|
30
|
+
base_url = self.get_base_url(url)
|
|
31
|
+
|
|
23
32
|
if referer:
|
|
24
33
|
self.httpx.headers.update({"Referer": referer})
|
|
25
34
|
|
|
@@ -27,7 +36,7 @@ class VidHide(ExtractorBase):
|
|
|
27
36
|
"Sec-Fetch-Dest" : "empty",
|
|
28
37
|
"Sec-Fetch-Mode" : "cors",
|
|
29
38
|
"Sec-Fetch-Site" : "cross-site",
|
|
30
|
-
"Origin" :
|
|
39
|
+
"Origin" : base_url,
|
|
31
40
|
})
|
|
32
41
|
|
|
33
42
|
embed_url = self.get_embed_url(url)
|
|
@@ -66,7 +75,7 @@ class VidHide(ExtractorBase):
|
|
|
66
75
|
return ExtractResult(
|
|
67
76
|
name = self.name,
|
|
68
77
|
url = self.fix_url(m3u8_url),
|
|
69
|
-
referer = f"{
|
|
78
|
+
referer = f"{base_url}/",
|
|
70
79
|
user_agent = self.httpx.headers.get("User-Agent", ""),
|
|
71
80
|
subtitles = []
|
|
72
81
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
|
|
4
4
|
from parsel import Selector
|
|
5
5
|
import re
|
|
6
6
|
|
|
@@ -164,7 +164,7 @@ class BelgeselX(PluginBase):
|
|
|
164
164
|
episodes = episodes
|
|
165
165
|
)
|
|
166
166
|
|
|
167
|
-
async def load_links(self, url: str) -> list[
|
|
167
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
168
168
|
istek = await self.httpx.get(url)
|
|
169
169
|
text = istek.text
|
|
170
170
|
|
|
@@ -187,18 +187,10 @@ class BelgeselX(PluginBase):
|
|
|
187
187
|
source_name = "Google" if quality == "FULL" else self.name
|
|
188
188
|
quality_str = "1080p" if quality == "FULL" else quality
|
|
189
189
|
|
|
190
|
-
links.append(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
190
|
+
links.append(ExtractResult(
|
|
191
|
+
url = video_url,
|
|
192
|
+
name = f"{source_name} | {quality_str}",
|
|
193
|
+
referer = url
|
|
194
|
+
))
|
|
195
195
|
|
|
196
196
|
return links
|
|
197
|
-
|
|
198
|
-
async def play(self, **kwargs):
|
|
199
|
-
extract_result = ExtractResult(**kwargs)
|
|
200
|
-
self.media_handler.title = kwargs.get("name")
|
|
201
|
-
if self.name not in self.media_handler.title:
|
|
202
|
-
self.media_handler.title = f"{self.name} | {self.media_handler.title}"
|
|
203
|
-
|
|
204
|
-
self.media_handler.play_media(extract_result)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
|
|
4
4
|
from Kekik.Sifreleme import CryptoJS
|
|
5
5
|
from parsel import Selector
|
|
6
6
|
import re, urllib.parse, base64, contextlib, asyncio, time
|
|
@@ -174,7 +174,7 @@ class DiziBox(PluginBase):
|
|
|
174
174
|
|
|
175
175
|
return results
|
|
176
176
|
|
|
177
|
-
async def load_links(self, url: str) -> list[
|
|
177
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
178
178
|
istek = await self.httpx.get(url)
|
|
179
179
|
secici = Selector(istek.text)
|
|
180
180
|
|
|
@@ -182,11 +182,9 @@ class DiziBox(PluginBase):
|
|
|
182
182
|
if main_iframe := secici.css("div#video-area iframe::attr(src)").get():
|
|
183
183
|
if decoded := await self._iframe_decode(self.name, main_iframe, url):
|
|
184
184
|
for iframe in decoded:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
"name" : f"{extractor.name if extractor else 'Main Player'}"
|
|
189
|
-
})
|
|
185
|
+
data = await self.extract(iframe)
|
|
186
|
+
if data:
|
|
187
|
+
results.append(data)
|
|
190
188
|
|
|
191
189
|
for alternatif in secici.css("div.video-toolbar option[value]"):
|
|
192
190
|
alt_name = alternatif.css("::text").get()
|
|
@@ -203,10 +201,8 @@ class DiziBox(PluginBase):
|
|
|
203
201
|
if alt_iframe := alt_secici.css("div#video-area iframe::attr(src)").get():
|
|
204
202
|
if decoded := await self._iframe_decode(alt_name, alt_iframe, url):
|
|
205
203
|
for iframe in decoded:
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
"name" : f"{extractor.name if extractor else alt_name}"
|
|
210
|
-
})
|
|
204
|
+
data = await self.extract(iframe, prefix=alt_name)
|
|
205
|
+
if data:
|
|
206
|
+
results.append(data)
|
|
211
207
|
|
|
212
208
|
return results
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode,
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, Subtitle, ExtractResult
|
|
4
4
|
from parsel import Selector
|
|
5
5
|
import re
|
|
6
6
|
|
|
@@ -177,7 +177,7 @@ class DiziPal(PluginBase):
|
|
|
177
177
|
duration = duration,
|
|
178
178
|
)
|
|
179
179
|
|
|
180
|
-
async def load_links(self, url: str) -> list[
|
|
180
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
181
181
|
# Reset headers to get HTML response
|
|
182
182
|
self.httpx.headers.update({
|
|
183
183
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
@@ -220,27 +220,16 @@ class DiziPal(PluginBase):
|
|
|
220
220
|
sub_url = sub_text.replace(f"[{lang}]", "")
|
|
221
221
|
subtitles.append(Subtitle(name=lang, url=self.fix_url(sub_url)))
|
|
222
222
|
|
|
223
|
-
results.append(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
223
|
+
results.append(ExtractResult(
|
|
224
|
+
name = self.name,
|
|
225
|
+
url = m3u_link,
|
|
226
|
+
referer = f"{self.main_url}/",
|
|
227
|
+
subtitles = subtitles
|
|
228
|
+
))
|
|
229
229
|
else:
|
|
230
230
|
# Extractor'a yönlendir
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
"url" : iframe,
|
|
235
|
-
"referer" : f"{self.main_url}/",
|
|
236
|
-
})
|
|
231
|
+
data = await self.extract(iframe)
|
|
232
|
+
if data:
|
|
233
|
+
results.append(data)
|
|
237
234
|
|
|
238
235
|
return results
|
|
239
|
-
|
|
240
|
-
async def play(self, **kwargs):
|
|
241
|
-
extract_result = ExtractResult(**kwargs)
|
|
242
|
-
self.media_handler.title = kwargs.get("name")
|
|
243
|
-
if self.name not in self.media_handler.title:
|
|
244
|
-
self.media_handler.title = f"{self.name} | {self.media_handler.title}"
|
|
245
|
-
|
|
246
|
-
self.media_handler.play_media(extract_result)
|
|
@@ -123,7 +123,7 @@ class DiziYou(PluginBase):
|
|
|
123
123
|
actors = actors
|
|
124
124
|
)
|
|
125
125
|
|
|
126
|
-
async def load_links(self, url: str) -> list[
|
|
126
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
127
127
|
istek = await self.httpx.get(url)
|
|
128
128
|
secici = Selector(istek.text)
|
|
129
129
|
|
|
@@ -179,19 +179,11 @@ class DiziYou(PluginBase):
|
|
|
179
179
|
|
|
180
180
|
results = []
|
|
181
181
|
for stream in stream_urls:
|
|
182
|
-
results.append(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
return results
|
|
190
|
-
|
|
191
|
-
async def play(self, **kwargs):
|
|
192
|
-
extract_result = ExtractResult(**kwargs)
|
|
193
|
-
self.media_handler.title = kwargs.get("name")
|
|
194
|
-
if self.name not in self.media_handler.title:
|
|
195
|
-
self.media_handler.title = f"{self.name} | {self.media_handler.title}"
|
|
196
|
-
|
|
197
|
-
self.media_handler.play_media(extract_result)
|
|
182
|
+
results.append(ExtractResult(
|
|
183
|
+
url = stream.get("url"),
|
|
184
|
+
name = f"{stream.get('dil')}",
|
|
185
|
+
referer = url,
|
|
186
|
+
subtitles = subtitles
|
|
187
|
+
))
|
|
188
|
+
|
|
189
|
+
return results
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
|
|
4
4
|
from parsel import Selector
|
|
5
5
|
from json import loads
|
|
6
6
|
from urllib.parse import urlparse, urlunparse
|
|
@@ -170,7 +170,7 @@ class Dizilla(PluginBase):
|
|
|
170
170
|
actors = actors
|
|
171
171
|
)
|
|
172
172
|
|
|
173
|
-
async def load_links(self, url: str) -> list[
|
|
173
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
174
174
|
istek = await self.httpx.get(url)
|
|
175
175
|
secici = Selector(istek.text)
|
|
176
176
|
|
|
@@ -196,8 +196,5 @@ class Dizilla(PluginBase):
|
|
|
196
196
|
if not iframe_url:
|
|
197
197
|
return []
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
return [
|
|
201
|
-
"url" : iframe_url,
|
|
202
|
-
"name" : f"{extractor.name if extractor else 'Main Player'} | {first_result.get('language_name', 'Unknown')}",
|
|
203
|
-
}]
|
|
199
|
+
data = await self.extract(iframe_url, prefix=first_result.get('language_name', 'Unknown'))
|
|
200
|
+
return [data] if data else []
|