KekikStream 2.2.9__py3-none-any.whl → 2.3.9__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.
Potentially problematic release.
This version of KekikStream might be problematic. Click here for more details.
- KekikStream/Core/HTMLHelper.py +134 -0
- KekikStream/Core/Plugin/PluginBase.py +22 -4
- KekikStream/Core/Plugin/PluginLoader.py +3 -2
- KekikStream/Core/Plugin/PluginManager.py +2 -2
- KekikStream/Core/__init__.py +2 -0
- KekikStream/Extractors/CloseLoad.py +12 -13
- KekikStream/Extractors/ContentX.py +33 -31
- KekikStream/Extractors/DonilasPlay.py +10 -10
- KekikStream/Extractors/DzenRu.py +3 -3
- KekikStream/Extractors/ExPlay.py +10 -10
- KekikStream/Extractors/Filemoon.py +11 -16
- KekikStream/Extractors/JetTv.py +4 -4
- KekikStream/Extractors/MixPlayHD.py +10 -11
- KekikStream/Extractors/MolyStream.py +16 -9
- KekikStream/Extractors/Odnoklassniki.py +4 -4
- KekikStream/Extractors/PeaceMakerst.py +3 -3
- KekikStream/Extractors/PixelDrain.py +6 -5
- KekikStream/Extractors/PlayerFilmIzle.py +6 -10
- KekikStream/Extractors/RapidVid.py +8 -7
- KekikStream/Extractors/SetPlay.py +10 -10
- KekikStream/Extractors/SetPrime.py +3 -6
- KekikStream/Extractors/SibNet.py +4 -5
- KekikStream/Extractors/Sobreatsesuyp.py +5 -5
- KekikStream/Extractors/TRsTX.py +5 -5
- KekikStream/Extractors/TurboImgz.py +3 -4
- KekikStream/Extractors/TurkeyPlayer.py +5 -5
- KekikStream/Extractors/VidHide.py +4 -7
- KekikStream/Extractors/VidMoly.py +37 -25
- KekikStream/Extractors/VidMoxy.py +8 -9
- KekikStream/Extractors/VidPapi.py +5 -7
- KekikStream/Extractors/VideoSeyred.py +3 -3
- KekikStream/Plugins/BelgeselX.py +40 -51
- KekikStream/Plugins/DiziBox.py +53 -81
- KekikStream/Plugins/DiziPal.py +50 -72
- KekikStream/Plugins/DiziYou.py +96 -83
- KekikStream/Plugins/Dizilla.py +95 -107
- KekikStream/Plugins/FilmBip.py +29 -50
- KekikStream/Plugins/FilmMakinesi.py +84 -46
- KekikStream/Plugins/FilmModu.py +27 -41
- KekikStream/Plugins/FullHDFilm.py +57 -62
- KekikStream/Plugins/FullHDFilmizlesene.py +32 -57
- KekikStream/Plugins/HDFilmCehennemi.py +51 -65
- KekikStream/Plugins/JetFilmizle.py +38 -51
- KekikStream/Plugins/KultFilmler.py +43 -67
- KekikStream/Plugins/RecTV.py +34 -9
- KekikStream/Plugins/RoketDizi.py +89 -111
- KekikStream/Plugins/SelcukFlix.py +102 -93
- KekikStream/Plugins/SetFilmIzle.py +65 -75
- KekikStream/Plugins/SezonlukDizi.py +47 -65
- KekikStream/Plugins/Sinefy.py +70 -70
- KekikStream/Plugins/SinemaCX.py +31 -55
- KekikStream/Plugins/Sinezy.py +27 -54
- KekikStream/Plugins/SuperFilmGeldi.py +25 -44
- KekikStream/Plugins/UgurFilm.py +23 -48
- KekikStream/Plugins/YabanciDizi.py +285 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/METADATA +1 -1
- kekikstream-2.3.9.dist-info/RECORD +84 -0
- kekikstream-2.2.9.dist-info/RECORD +0 -82
- {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/WHEEL +0 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from selectolax.parser import HTMLParser, Node
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HTMLHelper:
|
|
8
|
+
"""
|
|
9
|
+
Selectolax ile HTML parsing işlemlerini temiz, kısa ve okunabilir hale getiren yardımcı sınıf.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, html: str):
|
|
13
|
+
self.parser = HTMLParser(html)
|
|
14
|
+
self.html = html
|
|
15
|
+
|
|
16
|
+
# ========================
|
|
17
|
+
# TEMEL SELECTOR İŞLEMLERİ
|
|
18
|
+
# ========================
|
|
19
|
+
|
|
20
|
+
def _target(self, element: Node | None) -> Node | HTMLParser:
|
|
21
|
+
"""İşlem yapılacak temel elementi döndürür."""
|
|
22
|
+
return element if element is not None else self.parser
|
|
23
|
+
|
|
24
|
+
def select(self, selector: str, element: Node | None = None) -> list[Node]:
|
|
25
|
+
"""CSS selector ile tüm eşleşen elementleri döndür."""
|
|
26
|
+
return self._target(element).css(selector)
|
|
27
|
+
|
|
28
|
+
def select_first(self, selector: str | None, element: Node | None = None) -> Node | None:
|
|
29
|
+
"""CSS selector ile ilk eşleşen elementi döndür."""
|
|
30
|
+
if not selector:
|
|
31
|
+
return element
|
|
32
|
+
|
|
33
|
+
return self._target(element).css_first(selector)
|
|
34
|
+
|
|
35
|
+
def select_text(self, selector: str | None = None, element: Node | None = None, strip: bool = True) -> str | None:
|
|
36
|
+
"""CSS selector ile element bul ve text içeriğini döndür."""
|
|
37
|
+
el = self.select_first(selector, element)
|
|
38
|
+
if not el:
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
val = el.text(strip=strip)
|
|
42
|
+
return val if val else None
|
|
43
|
+
|
|
44
|
+
def select_attr(self, selector: str | None, attr: str, element: Node | None = None) -> str | None:
|
|
45
|
+
"""CSS selector ile element bul ve attribute değerini döndür."""
|
|
46
|
+
el = self.select_first(selector, element)
|
|
47
|
+
return el.attrs.get(attr) if el else None
|
|
48
|
+
|
|
49
|
+
def select_all_text(self, selector: str, element: Node | None = None, strip: bool = True) -> list[str]:
|
|
50
|
+
"""CSS selector ile tüm eşleşen elementlerin text içeriklerini döndür."""
|
|
51
|
+
return [
|
|
52
|
+
txt for el in self.select(selector, element)
|
|
53
|
+
if (txt := el.text(strip=strip))
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
def select_all_attr(self, selector: str, attr: str, element: Node | None = None) -> list[str]:
|
|
57
|
+
"""CSS selector ile tüm eşleşen elementlerin attribute değerlerini döndür."""
|
|
58
|
+
return [
|
|
59
|
+
val for el in self.select(selector, element)
|
|
60
|
+
if (val := el.attrs.get(attr))
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
# ----------------------------------------------
|
|
64
|
+
|
|
65
|
+
def select_poster(self, selector: str = "img", element: Node | None = None) -> str | None:
|
|
66
|
+
"""Poster URL'sini çıkar. Önce data-src, sonra src dener."""
|
|
67
|
+
el = self.select_first(selector, element)
|
|
68
|
+
if not el:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
return el.attrs.get("data-src") or el.attrs.get("src")
|
|
72
|
+
|
|
73
|
+
# ========================
|
|
74
|
+
# REGEX İŞLEMLERİ
|
|
75
|
+
# ========================
|
|
76
|
+
|
|
77
|
+
def _source(self, target: str | int | None) -> str:
|
|
78
|
+
"""Regex için kaynak metni döndürür."""
|
|
79
|
+
return target if isinstance(target, str) else self.html
|
|
80
|
+
|
|
81
|
+
def _flags(self, target: str | int | None, flags: int) -> int:
|
|
82
|
+
"""Regex flags değerini döndürür."""
|
|
83
|
+
return target if isinstance(target, int) else flags
|
|
84
|
+
|
|
85
|
+
def regex_first(self, pattern: str, target: str | int | None = None, flags: int = 0) -> str | None:
|
|
86
|
+
"""Regex ile arama yap, ilk grubu döndür (grup yoksa tamamını)."""
|
|
87
|
+
match = re.search(pattern, self._source(target), self._flags(target, flags))
|
|
88
|
+
if not match:
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
return match.group(1)
|
|
93
|
+
except IndexError:
|
|
94
|
+
return match.group(0)
|
|
95
|
+
|
|
96
|
+
def regex_all(self, pattern: str, target: str | int | None = None, flags: int = 0) -> list[str]:
|
|
97
|
+
"""Regex ile tüm eşleşmeleri döndür."""
|
|
98
|
+
return re.findall(pattern, self._source(target), self._flags(target, flags))
|
|
99
|
+
|
|
100
|
+
def regex_replace(self, pattern: str, repl: str, target: str | int | None = None, flags: int = 0) -> str:
|
|
101
|
+
"""Regex ile replace yap."""
|
|
102
|
+
return re.sub(pattern, repl, self._source(target), flags)
|
|
103
|
+
|
|
104
|
+
# ========================
|
|
105
|
+
# ÖZEL AYIKLAYICILAR
|
|
106
|
+
# ========================
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def extract_season_episode(text: str) -> tuple[int | None, int | None]:
|
|
110
|
+
"""Metin içinden sezon ve bölüm numarasını çıkar."""
|
|
111
|
+
# S01E05 formatı
|
|
112
|
+
if m := re.search(r"[Ss](\d+)[Ee](\d+)", text):
|
|
113
|
+
return int(m.group(1)), int(m.group(2))
|
|
114
|
+
|
|
115
|
+
# Ayrı ayrı ara
|
|
116
|
+
s = re.search(r"(\d+)\.\s*[Ss]ezon|[Ss]ezon[- ]?(\d+)|-(\d+)-sezon", text, re.I)
|
|
117
|
+
e = re.search(r"(\d+)\.\s*[Bb]ölüm|[Bb]olum[- ]?(\d+)|-(\d+)-bolum|[Ee](\d+)", text, re.I)
|
|
118
|
+
|
|
119
|
+
# İlk bulunan grubu al (None değilse)
|
|
120
|
+
s_val = next((int(g) for g in s.groups() if g), None) if s else None
|
|
121
|
+
e_val = next((int(g) for g in e.groups() if g), None) if e else None
|
|
122
|
+
|
|
123
|
+
return s_val, e_val
|
|
124
|
+
|
|
125
|
+
def extract_year(self, *selectors: str, pattern: str = r"(\d{4})") -> int | None:
|
|
126
|
+
"""Birden fazla selector veya regex ile yıl bilgisini çıkar."""
|
|
127
|
+
for selector in selectors:
|
|
128
|
+
if text := self.select_text(selector):
|
|
129
|
+
if m := re.search(r"(\d{4})", text):
|
|
130
|
+
return int(m.group(1))
|
|
131
|
+
|
|
132
|
+
val = self.regex_first(pattern)
|
|
133
|
+
return int(val) if val and val.isdigit() else None
|
|
134
|
+
|
|
@@ -25,14 +25,22 @@ class PluginBase(ABC):
|
|
|
25
25
|
self.main_page = {url.replace(self.main_url, new_url): category for url, category in self.main_page.items()}
|
|
26
26
|
self.main_url = new_url
|
|
27
27
|
|
|
28
|
-
def __init__(self):
|
|
28
|
+
def __init__(self, proxy: str | dict | None = None):
|
|
29
29
|
# cloudscraper - for bypassing Cloudflare
|
|
30
30
|
self.cloudscraper = CloudScraper()
|
|
31
|
+
if proxy:
|
|
32
|
+
self.cloudscraper.proxies = proxy if isinstance(proxy, dict) else {"http": proxy, "https": proxy}
|
|
33
|
+
|
|
34
|
+
# Convert dict proxy to string for httpx if necessary
|
|
35
|
+
httpx_proxy = proxy
|
|
36
|
+
if isinstance(proxy, dict):
|
|
37
|
+
httpx_proxy = proxy.get("https") or proxy.get("http")
|
|
31
38
|
|
|
32
39
|
# httpx - lightweight and safe for most HTTP requests
|
|
33
40
|
self.httpx = AsyncClient(
|
|
34
41
|
timeout = 3,
|
|
35
|
-
follow_redirects = True
|
|
42
|
+
follow_redirects = True,
|
|
43
|
+
proxy = httpx_proxy
|
|
36
44
|
)
|
|
37
45
|
self.httpx.headers.update(self.cloudscraper.headers)
|
|
38
46
|
self.httpx.cookies.update(self.cloudscraper.cookies)
|
|
@@ -122,7 +130,14 @@ class PluginBase(ABC):
|
|
|
122
130
|
try:
|
|
123
131
|
data = await extractor.extract(url, referer=referer)
|
|
124
132
|
|
|
125
|
-
#
|
|
133
|
+
# Liste ise her bir öğe için prefix ekle
|
|
134
|
+
if isinstance(data, list):
|
|
135
|
+
for item in data:
|
|
136
|
+
if prefix and item.name:
|
|
137
|
+
item.name = f"{prefix} | {item.name}"
|
|
138
|
+
return data
|
|
139
|
+
|
|
140
|
+
# Tekil öğe ise
|
|
126
141
|
if prefix and data.name:
|
|
127
142
|
data.name = f"{prefix} | {data.name}"
|
|
128
143
|
|
|
@@ -132,7 +147,10 @@ class PluginBase(ABC):
|
|
|
132
147
|
return None
|
|
133
148
|
|
|
134
149
|
@staticmethod
|
|
135
|
-
def clean_title(title: str) -> str:
|
|
150
|
+
def clean_title(title: str | None) -> str | None:
|
|
151
|
+
if not title:
|
|
152
|
+
return None
|
|
153
|
+
|
|
136
154
|
suffixes = [
|
|
137
155
|
" izle",
|
|
138
156
|
" full film",
|
|
@@ -6,8 +6,9 @@ from pathlib import Path
|
|
|
6
6
|
import os, importlib.util, traceback
|
|
7
7
|
|
|
8
8
|
class PluginLoader:
|
|
9
|
-
def __init__(self, plugins_dir: str):
|
|
9
|
+
def __init__(self, plugins_dir: str, proxy: str | dict | None = None):
|
|
10
10
|
# Yerel ve global eklenti dizinlerini ayarla
|
|
11
|
+
self.proxy = proxy
|
|
11
12
|
self.local_plugins_dir = Path(plugins_dir).resolve()
|
|
12
13
|
self.global_plugins_dir = Path(__file__).parent.parent.parent / plugins_dir
|
|
13
14
|
|
|
@@ -70,7 +71,7 @@ class PluginLoader:
|
|
|
70
71
|
obj = getattr(module, attr)
|
|
71
72
|
if isinstance(obj, type) and issubclass(obj, PluginBase) and obj is not PluginBase:
|
|
72
73
|
# konsol.log(f"[yellow]Yüklenen sınıf\t\t: {module_name}.{obj.__name__} ({obj.__module__}.{obj.__name__})[/yellow]")
|
|
73
|
-
return obj()
|
|
74
|
+
return obj(proxy=self.proxy)
|
|
74
75
|
|
|
75
76
|
except Exception as hata:
|
|
76
77
|
konsol.print(f"[red][!] Eklenti yüklenirken hata oluştu: {module_name}\nHata: {hata}")
|
|
@@ -4,9 +4,9 @@ from .PluginLoader import PluginLoader
|
|
|
4
4
|
from .PluginBase import PluginBase
|
|
5
5
|
|
|
6
6
|
class PluginManager:
|
|
7
|
-
def __init__(self, plugin_dir="Plugins"):
|
|
7
|
+
def __init__(self, plugin_dir="Plugins", proxy: str | dict | None = None):
|
|
8
8
|
# Eklenti yükleyiciyi başlat ve tüm eklentileri yükle
|
|
9
|
-
self.plugin_loader = PluginLoader(plugin_dir)
|
|
9
|
+
self.plugin_loader = PluginLoader(plugin_dir, proxy=proxy)
|
|
10
10
|
self.plugins = self.plugin_loader.load_all()
|
|
11
11
|
|
|
12
12
|
def get_plugin_names(self):
|
KekikStream/Core/__init__.py
CHANGED
|
@@ -1,9 +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, Subtitle
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
4
4
|
from Kekik.Sifreleme import Packer, StreamDecoder
|
|
5
|
-
|
|
6
|
-
import re, json
|
|
5
|
+
import json
|
|
7
6
|
|
|
8
7
|
class CloseLoadExtractor(ExtractorBase):
|
|
9
8
|
name = "CloseLoad"
|
|
@@ -11,8 +10,8 @@ class CloseLoadExtractor(ExtractorBase):
|
|
|
11
10
|
|
|
12
11
|
def _extract_from_json_ld(self, html: str) -> str | None:
|
|
13
12
|
"""JSON-LD script tag'inden contentUrl'i çıkar (Kotlin versiyonundaki gibi)"""
|
|
14
|
-
secici =
|
|
15
|
-
for script in secici.
|
|
13
|
+
secici = HTMLHelper(html)
|
|
14
|
+
for script in secici.select("script[type='application/ld+json']"):
|
|
16
15
|
try:
|
|
17
16
|
data = json.loads(script.text(strip=True))
|
|
18
17
|
if content_url := data.get("contentUrl"):
|
|
@@ -20,17 +19,17 @@ class CloseLoadExtractor(ExtractorBase):
|
|
|
20
19
|
return content_url
|
|
21
20
|
except (json.JSONDecodeError, TypeError):
|
|
22
21
|
# Regex ile contentUrl'i çıkarmayı dene
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
if content_url := secici.regex_first(r'"contentUrl"\s*:\s*"([^\"]+)"', script.text()):
|
|
23
|
+
if content_url.startswith("http"):
|
|
24
|
+
return content_url
|
|
26
25
|
return None
|
|
27
26
|
|
|
28
27
|
def _extract_from_packed(self, html: str) -> str | None:
|
|
29
28
|
"""Packed JavaScript'ten video URL'sini çıkar (fallback)"""
|
|
30
29
|
try:
|
|
31
|
-
|
|
32
|
-
if
|
|
33
|
-
return StreamDecoder.extract_stream_url(Packer.unpack(
|
|
30
|
+
packed = HTMLHelper(html).regex_all(r'\s*(eval\(function[\s\S].*)')
|
|
31
|
+
if packed:
|
|
32
|
+
return StreamDecoder.extract_stream_url(Packer.unpack(packed[0]))
|
|
34
33
|
except Exception:
|
|
35
34
|
pass
|
|
36
35
|
return None
|
|
@@ -59,8 +58,8 @@ class CloseLoadExtractor(ExtractorBase):
|
|
|
59
58
|
|
|
60
59
|
# Subtitle'ları parse et (Kotlin referansı: track elementleri)
|
|
61
60
|
subtitles = []
|
|
62
|
-
secici =
|
|
63
|
-
for track in secici.
|
|
61
|
+
secici = HTMLHelper(istek.text)
|
|
62
|
+
for track in secici.select("track"):
|
|
64
63
|
raw_src = track.attrs.get("src") or ""
|
|
65
64
|
raw_src = raw_src.strip()
|
|
66
65
|
label = track.attrs.get("label") or track.attrs.get("srclang") or "Altyazı"
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
|
-
import re
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
5
4
|
|
|
6
5
|
class ContentX(ExtractorBase):
|
|
7
6
|
name = "ContentX"
|
|
@@ -31,15 +30,13 @@ class ContentX(ExtractorBase):
|
|
|
31
30
|
istek.raise_for_status()
|
|
32
31
|
i_source = istek.text
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
if not
|
|
33
|
+
i_extract_value = HTMLHelper(i_source).regex_first(r"window\.openPlayer\('([^']+)'")
|
|
34
|
+
if not i_extract_value:
|
|
36
35
|
raise ValueError("i_extract is null")
|
|
37
|
-
i_extract_value = i_extract[1]
|
|
38
36
|
|
|
39
37
|
subtitles = []
|
|
40
38
|
sub_urls = set()
|
|
41
|
-
for
|
|
42
|
-
sub_url, sub_lang = match.groups()
|
|
39
|
+
for sub_url, sub_lang in HTMLHelper(i_source).regex_all(r'"file":"([^\"]+)","label":"([^\"]+)"'):
|
|
43
40
|
|
|
44
41
|
if sub_url in sub_urls:
|
|
45
42
|
continue
|
|
@@ -50,8 +47,12 @@ class ContentX(ExtractorBase):
|
|
|
50
47
|
name = sub_lang.replace("\\u0131", "ı")
|
|
51
48
|
.replace("\\u0130", "İ")
|
|
52
49
|
.replace("\\u00fc", "ü")
|
|
53
|
-
.replace("\\u00e7", "ç")
|
|
54
|
-
|
|
50
|
+
.replace("\\u00e7", "ç")
|
|
51
|
+
.replace("\\u011f", "ğ")
|
|
52
|
+
.replace("\\u015f", "ş")
|
|
53
|
+
.replace("\\u011e", "Ğ")
|
|
54
|
+
.replace("\\u015e", "Ş"),
|
|
55
|
+
url = self.fix_url(sub_url.replace("\\/", "/").replace("\\", ""))
|
|
55
56
|
)
|
|
56
57
|
)
|
|
57
58
|
|
|
@@ -60,11 +61,11 @@ class ContentX(ExtractorBase):
|
|
|
60
61
|
vid_source_request.raise_for_status()
|
|
61
62
|
|
|
62
63
|
vid_source = vid_source_request.text
|
|
63
|
-
|
|
64
|
-
if not
|
|
64
|
+
m3u_link = HTMLHelper(vid_source).regex_first(r'file":"([^\"]+)"')
|
|
65
|
+
if not m3u_link:
|
|
65
66
|
raise ValueError("vidExtract is null")
|
|
66
67
|
|
|
67
|
-
m3u_link =
|
|
68
|
+
m3u_link = m3u_link.replace("\\", "").replace("/m.php", "/master.m3u8")
|
|
68
69
|
results = [
|
|
69
70
|
ExtractResult(
|
|
70
71
|
name = self.name,
|
|
@@ -74,24 +75,25 @@ class ContentX(ExtractorBase):
|
|
|
74
75
|
)
|
|
75
76
|
]
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
dublaj_value = HTMLHelper(i_source).regex_first(r'["\']([^"\']+)["\'],["\']Türkçe["\']')
|
|
79
|
+
if dublaj_value:
|
|
80
|
+
try:
|
|
81
|
+
dublaj_source_request = await self.httpx.get(f"{base_url}/source2.php?v={dublaj_value}", headers={"Referer": referer or base_url})
|
|
82
|
+
dublaj_source_request.raise_for_status()
|
|
83
|
+
|
|
84
|
+
dublaj_source = dublaj_source_request.text
|
|
85
|
+
dublaj_link = HTMLHelper(dublaj_source).regex_first(r'file":"([^\"]+)"')
|
|
86
|
+
if dublaj_link:
|
|
87
|
+
dublaj_link = dublaj_link.replace("\\", "")
|
|
88
|
+
results.append(
|
|
89
|
+
ExtractResult(
|
|
90
|
+
name = f"{self.name} Türkçe Dublaj",
|
|
91
|
+
url = dublaj_link,
|
|
92
|
+
referer = url,
|
|
93
|
+
subtitles = []
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
except Exception:
|
|
97
|
+
pass
|
|
96
98
|
|
|
97
99
|
return results[0] if len(results) == 1 else results
|
|
@@ -1,8 +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, Subtitle
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
4
4
|
from Kekik.Sifreleme import AESManager
|
|
5
|
-
import
|
|
5
|
+
import json
|
|
6
6
|
|
|
7
7
|
class DonilasPlay(ExtractorBase):
|
|
8
8
|
name = "DonilasPlay"
|
|
@@ -20,10 +20,10 @@ class DonilasPlay(ExtractorBase):
|
|
|
20
20
|
subtitles = []
|
|
21
21
|
|
|
22
22
|
# bePlayer pattern
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
be_player_data =
|
|
23
|
+
hp = HTMLHelper(i_source)
|
|
24
|
+
be_player_matches = hp.regex_all(r"bePlayer\('([^']+)',\s*'(\{[^}]+\})'\);")
|
|
25
|
+
if be_player_matches:
|
|
26
|
+
be_player_pass, be_player_data = be_player_matches[0]
|
|
27
27
|
|
|
28
28
|
try:
|
|
29
29
|
# AES decrypt
|
|
@@ -54,15 +54,15 @@ class DonilasPlay(ExtractorBase):
|
|
|
54
54
|
|
|
55
55
|
# Fallback: file pattern
|
|
56
56
|
if not m3u_link:
|
|
57
|
-
file_match =
|
|
57
|
+
file_match = hp.regex_first(r'file:"([^"]+)"')
|
|
58
58
|
if file_match:
|
|
59
|
-
m3u_link = file_match
|
|
59
|
+
m3u_link = file_match
|
|
60
60
|
|
|
61
61
|
# tracks pattern for subtitles
|
|
62
|
-
tracks_match =
|
|
62
|
+
tracks_match = hp.regex_first(r'tracks:\[([^\]]+)')
|
|
63
63
|
if tracks_match:
|
|
64
64
|
try:
|
|
65
|
-
tracks_str = f"[{tracks_match
|
|
65
|
+
tracks_str = f"[{tracks_match}]"
|
|
66
66
|
tracks = json.loads(tracks_str)
|
|
67
67
|
for track in tracks:
|
|
68
68
|
file_url = track.get("file")
|
KekikStream/Extractors/DzenRu.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
-
import re
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
5
4
|
|
|
6
5
|
class DzenRu(ExtractorBase):
|
|
7
6
|
name = "DzenRu"
|
|
@@ -18,7 +17,8 @@ class DzenRu(ExtractorBase):
|
|
|
18
17
|
istek.raise_for_status()
|
|
19
18
|
|
|
20
19
|
# okcdn.ru linklerini bul
|
|
21
|
-
|
|
20
|
+
hp = HTMLHelper(istek.text)
|
|
21
|
+
matches = hp.regex_all(r'https://vd\d+\.okcdn\.ru/\?[^"\'\\\s]+')
|
|
22
22
|
|
|
23
23
|
if not matches:
|
|
24
24
|
raise ValueError("DzenRu video link not found")
|
KekikStream/Extractors/ExPlay.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
|
-
import re
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
5
4
|
from urllib.parse import urlparse, parse_qs
|
|
6
5
|
|
|
7
6
|
class ExPlay(ExtractorBase):
|
|
@@ -23,21 +22,22 @@ class ExPlay(ExtractorBase):
|
|
|
23
22
|
istek = await self.httpx.get(clean_url)
|
|
24
23
|
istek.raise_for_status()
|
|
25
24
|
|
|
25
|
+
hp = HTMLHelper(istek.text)
|
|
26
|
+
|
|
26
27
|
# videoUrl çıkar
|
|
27
|
-
|
|
28
|
-
if not
|
|
28
|
+
video_url = hp.regex_first(r'videoUrl":"([^",]+)"')
|
|
29
|
+
if not video_url:
|
|
29
30
|
raise ValueError("videoUrl not found")
|
|
30
|
-
video_url =
|
|
31
|
+
video_url = video_url.replace("\\", "")
|
|
31
32
|
|
|
32
33
|
# videoServer çıkar
|
|
33
|
-
|
|
34
|
-
if not
|
|
34
|
+
video_server = hp.regex_first(r'videoServer":"([^",]+)"')
|
|
35
|
+
if not video_server:
|
|
35
36
|
raise ValueError("videoServer not found")
|
|
36
|
-
video_server = video_server_match[1]
|
|
37
37
|
|
|
38
38
|
# title çıkar
|
|
39
|
-
|
|
40
|
-
title =
|
|
39
|
+
title = hp.regex_first(r'title":"([^",]+)"')
|
|
40
|
+
title = title.split(".")[-1] if title else "Unknown"
|
|
41
41
|
|
|
42
42
|
if part_key and "turkce" in part_key.lower():
|
|
43
43
|
title = part_key # Or nicer formatting like SetPlay
|
|
@@ -1,9 +1,7 @@
|
|
|
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, HTMLHelper
|
|
4
4
|
from Kekik.Sifreleme import Packer
|
|
5
|
-
from selectolax.parser import HTMLParser
|
|
6
|
-
import re
|
|
7
5
|
|
|
8
6
|
class Filemoon(ExtractorBase):
|
|
9
7
|
name = "Filemoon"
|
|
@@ -34,11 +32,10 @@ class Filemoon(ExtractorBase):
|
|
|
34
32
|
# İlk sayfayı al
|
|
35
33
|
istek = await self.httpx.get(url)
|
|
36
34
|
response = istek.text
|
|
37
|
-
secici =
|
|
35
|
+
secici = HTMLHelper(response)
|
|
38
36
|
|
|
39
37
|
# Eğer iframe varsa, iframe'e git
|
|
40
|
-
|
|
41
|
-
iframe_src = iframe_el.attrs.get("src") if iframe_el else None
|
|
38
|
+
iframe_src = secici.select_attr("iframe", "src")
|
|
42
39
|
|
|
43
40
|
m3u8_url = None
|
|
44
41
|
|
|
@@ -52,8 +49,8 @@ class Filemoon(ExtractorBase):
|
|
|
52
49
|
|
|
53
50
|
if script_data:
|
|
54
51
|
unpacked = Packer.unpack(script_data)
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
unpacked_sec = HTMLHelper(unpacked)
|
|
53
|
+
m3u8_url = unpacked_sec.regex_first(r'sources:\[\{file:"(.*?)"')
|
|
57
54
|
else:
|
|
58
55
|
# Iframe varsa devam et
|
|
59
56
|
iframe_url = self.fix_url(iframe_src)
|
|
@@ -62,25 +59,23 @@ class Filemoon(ExtractorBase):
|
|
|
62
59
|
|
|
63
60
|
istek = await self.httpx.get(iframe_url, headers=iframe_headers)
|
|
64
61
|
response = istek.text
|
|
65
|
-
secici =
|
|
62
|
+
secici = HTMLHelper(response)
|
|
66
63
|
|
|
67
64
|
script_data = ""
|
|
68
|
-
for script in secici.
|
|
65
|
+
for script in secici.select("script"):
|
|
69
66
|
if "function(p,a,c,k,e,d)" in script.text():
|
|
70
67
|
script_data = script.text()
|
|
71
68
|
break
|
|
72
69
|
|
|
73
70
|
if script_data:
|
|
74
71
|
unpacked = Packer.unpack(script_data)
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
unpacked_sec = HTMLHelper(unpacked)
|
|
73
|
+
m3u8_url = unpacked_sec.regex_first(r'sources:\[\{file:"(.*?)"')
|
|
77
74
|
|
|
78
75
|
if not m3u8_url:
|
|
79
76
|
# Son çare: Normal response içinde ara
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
elif match := re.search(r'file:\s*"([^"]*?\.m3u8[^"]*)"', response):
|
|
83
|
-
m3u8_url = match.group(1)
|
|
77
|
+
resp_sec = HTMLHelper(response)
|
|
78
|
+
m3u8_url = resp_sec.regex_first(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"') or resp_sec.regex_first(r'file:\s*"([^\"]*?\.m3u8[^"]*)"')
|
|
84
79
|
|
|
85
80
|
if not m3u8_url:
|
|
86
81
|
raise ValueError(f"Filemoon: Video URL bulunamadı. {url}")
|
KekikStream/Extractors/JetTv.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
|
-
import
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
4
|
+
import json
|
|
5
5
|
|
|
6
6
|
class JetTv(ExtractorBase):
|
|
7
7
|
name = "JetTv"
|
|
@@ -31,8 +31,8 @@ class JetTv(ExtractorBase):
|
|
|
31
31
|
|
|
32
32
|
# 2. Yöntem: Regex Fallback
|
|
33
33
|
if not master_url:
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
hp = HTMLHelper(document)
|
|
35
|
+
master_url = hp.regex_first(r"(?i)file: '([^']*)'") or master_url
|
|
36
36
|
|
|
37
37
|
if not master_url:
|
|
38
38
|
raise ValueError(f"JetTv: Video kaynağı bulunamadı. {url}")
|
|
@@ -1,8 +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, HTMLHelper
|
|
4
4
|
from Kekik.Sifreleme import AESManager
|
|
5
|
-
import
|
|
5
|
+
import json
|
|
6
6
|
|
|
7
7
|
class MixPlayHD(ExtractorBase):
|
|
8
8
|
name = "MixPlayHD"
|
|
@@ -15,12 +15,12 @@ class MixPlayHD(ExtractorBase):
|
|
|
15
15
|
istek = await self.httpx.get(url)
|
|
16
16
|
istek.raise_for_status()
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
hp = HTMLHelper(istek.text)
|
|
19
|
+
be_player_matches = hp.regex_all(r"bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);")
|
|
20
|
+
if not be_player_matches:
|
|
20
21
|
raise ValueError("bePlayer not found in the response.")
|
|
21
22
|
|
|
22
|
-
be_player_pass =
|
|
23
|
-
be_player_data = be_player_match[2]
|
|
23
|
+
be_player_pass, be_player_data = be_player_matches[0]
|
|
24
24
|
|
|
25
25
|
try:
|
|
26
26
|
decrypted_data = AESManager.decrypt(be_player_data, be_player_pass).replace("\\", "")
|
|
@@ -28,13 +28,12 @@ class MixPlayHD(ExtractorBase):
|
|
|
28
28
|
except Exception as hata:
|
|
29
29
|
raise RuntimeError(f"Decryption failed: {hata}") from hata
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
):
|
|
31
|
+
client_str = decrypted_json.get("schedule", {}).get("client", "")
|
|
32
|
+
video_url = HTMLHelper(client_str).regex_first(r'"video_location":"([^"]+)"')
|
|
33
|
+
if video_url:
|
|
35
34
|
return ExtractResult(
|
|
36
35
|
name = self.name,
|
|
37
|
-
url =
|
|
36
|
+
url = video_url,
|
|
38
37
|
referer = self.main_url,
|
|
39
38
|
subtitles = []
|
|
40
39
|
)
|