KekikStream 2.4.2__tar.gz → 2.4.3__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.4.2 → kekikstream-2.4.3}/KekikStream/Core/Extractor/ExtractorBase.py +3 -2
- kekikstream-2.4.3/KekikStream/Core/HTMLHelper.py +228 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/Plugin/PluginBase.py +3 -2
- kekikstream-2.4.3/KekikStream/Extractors/CloseLoad.py +52 -0
- kekikstream-2.4.3/KekikStream/Extractors/ContentX.py +54 -0
- kekikstream-2.4.3/KekikStream/Extractors/DonilasPlay.py +42 -0
- kekikstream-2.4.3/KekikStream/Extractors/DzenRu.py +24 -0
- kekikstream-2.4.3/KekikStream/Extractors/ExPlay.py +35 -0
- kekikstream-2.4.3/KekikStream/Extractors/Filemoon.py +63 -0
- kekikstream-2.4.3/KekikStream/Extractors/HDMomPlayer.py +30 -0
- kekikstream-2.4.3/KekikStream/Extractors/HDPlayerSystem.py +23 -0
- kekikstream-2.4.3/KekikStream/Extractors/HotStream.py +27 -0
- kekikstream-2.4.3/KekikStream/Extractors/JFVid.py +19 -0
- kekikstream-2.4.3/KekikStream/Extractors/JetTv.py +32 -0
- kekikstream-2.4.3/KekikStream/Extractors/MailRu.py +20 -0
- kekikstream-2.4.3/KekikStream/Extractors/MixPlayHD.py +28 -0
- kekikstream-2.4.3/KekikStream/Extractors/MixTiger.py +34 -0
- kekikstream-2.4.3/KekikStream/Extractors/MolyStream.py +38 -0
- kekikstream-2.4.3/KekikStream/Extractors/Odnoklassniki.py +41 -0
- kekikstream-2.4.3/KekikStream/Extractors/PeaceMakerst.py +36 -0
- kekikstream-2.4.3/KekikStream/Extractors/PixelDrain.py +20 -0
- kekikstream-2.4.3/KekikStream/Extractors/PlayerFilmIzle.py +46 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Extractors/RapidVid.py +21 -35
- kekikstream-2.4.3/KekikStream/Extractors/SetPlay.py +41 -0
- kekikstream-2.4.3/KekikStream/Extractors/SibNet.py +17 -0
- kekikstream-2.4.3/KekikStream/Extractors/Sobreatsesuyp.py +37 -0
- kekikstream-2.4.3/KekikStream/Extractors/TRsTX.py +37 -0
- kekikstream-2.4.3/KekikStream/Extractors/TurboImgz.py +17 -0
- kekikstream-2.4.3/KekikStream/Extractors/VCTPlay.py +23 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Extractors/VidHide.py +10 -31
- kekikstream-2.4.3/KekikStream/Extractors/VidMoly.py +98 -0
- kekikstream-2.4.3/KekikStream/Extractors/VidMoxy.py +37 -0
- kekikstream-2.4.3/KekikStream/Extractors/VidPapi.py +57 -0
- kekikstream-2.4.3/KekikStream/Extractors/VideoSeyred.py +32 -0
- kekikstream-2.4.3/KekikStream/Extractors/Videostr.py +58 -0
- kekikstream-2.4.3/KekikStream/Extractors/Vidoza.py +18 -0
- kekikstream-2.4.3/KekikStream/Extractors/YildizKisaFilm.py +23 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/BelgeselX.py +63 -69
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/DiziBox.py +16 -36
- kekikstream-2.4.3/KekikStream/Plugins/DiziMom.py +155 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/DiziPal.py +26 -75
- kekikstream-2.4.3/KekikStream/Plugins/DiziYou.py +135 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/Dizilla.py +18 -44
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/FilmBip.py +10 -24
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/FilmEkseni.py +12 -32
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/FilmMakinesi.py +24 -77
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/FilmModu.py +11 -18
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/Filmatek.py +13 -39
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/Full4kizle.py +33 -133
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/FullHDFilm.py +23 -93
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/FullHDFilmizlesene.py +10 -29
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/HDFilmCehennemi.py +27 -66
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/JetFilmizle.py +19 -20
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/KultFilmler.py +16 -50
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/RecTV.py +47 -85
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/SelcukFlix.py +29 -47
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/SetFilmIzle.py +28 -84
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/SezonlukDizi.py +27 -59
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/Sinefy.py +37 -100
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/SinemaCX.py +12 -18
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/Sinezy.py +11 -12
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/SuperFilmGeldi.py +8 -13
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/UgurFilm.py +14 -14
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/Watch32.py +42 -74
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/YabanciDizi.py +33 -87
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream.egg-info/PKG-INFO +1 -1
- {kekikstream-2.4.2 → kekikstream-2.4.3}/PKG-INFO +1 -1
- {kekikstream-2.4.2 → kekikstream-2.4.3}/setup.py +1 -1
- kekikstream-2.4.2/KekikStream/Core/HTMLHelper.py +0 -134
- kekikstream-2.4.2/KekikStream/Extractors/CloseLoad.py +0 -76
- kekikstream-2.4.2/KekikStream/Extractors/ContentX.py +0 -99
- kekikstream-2.4.2/KekikStream/Extractors/DonilasPlay.py +0 -86
- kekikstream-2.4.2/KekikStream/Extractors/DzenRu.py +0 -38
- kekikstream-2.4.2/KekikStream/Extractors/ExPlay.py +0 -53
- kekikstream-2.4.2/KekikStream/Extractors/Filemoon.py +0 -89
- kekikstream-2.4.2/KekikStream/Extractors/HDMomPlayer.py +0 -62
- kekikstream-2.4.2/KekikStream/Extractors/HDPlayerSystem.py +0 -41
- kekikstream-2.4.2/KekikStream/Extractors/HotStream.py +0 -45
- kekikstream-2.4.2/KekikStream/Extractors/JFVid.py +0 -40
- kekikstream-2.4.2/KekikStream/Extractors/JetTv.py +0 -45
- kekikstream-2.4.2/KekikStream/Extractors/MailRu.py +0 -38
- kekikstream-2.4.2/KekikStream/Extractors/MixPlayHD.py +0 -41
- kekikstream-2.4.2/KekikStream/Extractors/MixTiger.py +0 -57
- kekikstream-2.4.2/KekikStream/Extractors/MolyStream.py +0 -42
- kekikstream-2.4.2/KekikStream/Extractors/Odnoklassniki.py +0 -117
- kekikstream-2.4.2/KekikStream/Extractors/PeaceMakerst.py +0 -63
- kekikstream-2.4.2/KekikStream/Extractors/PixelDrain.py +0 -28
- kekikstream-2.4.2/KekikStream/Extractors/PlayerFilmIzle.py +0 -65
- kekikstream-2.4.2/KekikStream/Extractors/SetPlay.py +0 -66
- kekikstream-2.4.2/KekikStream/Extractors/SibNet.py +0 -27
- kekikstream-2.4.2/KekikStream/Extractors/Sobreatsesuyp.py +0 -59
- kekikstream-2.4.2/KekikStream/Extractors/TRsTX.py +0 -67
- kekikstream-2.4.2/KekikStream/Extractors/TurboImgz.py +0 -24
- kekikstream-2.4.2/KekikStream/Extractors/VCTPlay.py +0 -41
- kekikstream-2.4.2/KekikStream/Extractors/VidMoly.py +0 -132
- kekikstream-2.4.2/KekikStream/Extractors/VidMoxy.py +0 -48
- kekikstream-2.4.2/KekikStream/Extractors/VidPapi.py +0 -87
- kekikstream-2.4.2/KekikStream/Extractors/VideoSeyred.py +0 -53
- kekikstream-2.4.2/KekikStream/Extractors/Videostr.py +0 -115
- kekikstream-2.4.2/KekikStream/Extractors/Vidoza.py +0 -25
- kekikstream-2.4.2/KekikStream/Extractors/YildizKisaFilm.py +0 -41
- kekikstream-2.4.2/KekikStream/Plugins/DiziMom.py +0 -247
- kekikstream-2.4.2/KekikStream/Plugins/DiziYou.py +0 -243
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/CLI/__init__.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/CLI/pypi_kontrol.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/Extractor/ExtractorModels.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/Extractor/YTDLPCache.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/Media/MediaHandler.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/Media/MediaManager.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/Plugin/PluginManager.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/Plugin/PluginModels.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/UI/UIManager.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Core/__init__.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Extractors/SetPrime.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Extractors/TauVideo.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Extractors/TurkeyPlayer.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Extractors/YTDLP.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/RoketDizi.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/Plugins/SineWix.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/__init__.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/__main__.py +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream/requirements.txt +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream.egg-info/SOURCES.txt +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream.egg-info/dependency_links.txt +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream.egg-info/entry_points.txt +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream.egg-info/requires.txt +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/KekikStream.egg-info/top_level.txt +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/LICENSE +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/MANIFEST.in +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/README.md +0 -0
- {kekikstream-2.4.2 → kekikstream-2.4.3}/setup.cfg +0 -0
|
@@ -49,6 +49,7 @@ class ExtractorBase(ABC):
|
|
|
49
49
|
return ""
|
|
50
50
|
|
|
51
51
|
if url.startswith("http") or url.startswith("{\""):
|
|
52
|
-
return url
|
|
52
|
+
return url.replace("\\", "")
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
url = f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
|
|
55
|
+
return url.replace("\\", "")
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from selectolax.parser import HTMLParser, Node
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class HTMLHelper:
|
|
10
|
+
"""
|
|
11
|
+
Selectolax ile HTML parsing işlemlerini temiz, kısa ve okunabilir hale getiren yardımcı sınıf.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, html: str):
|
|
15
|
+
self.html = html
|
|
16
|
+
self.parser = HTMLParser(html)
|
|
17
|
+
|
|
18
|
+
# ========================
|
|
19
|
+
# SELECTOR (CSS) İŞLEMLERİ
|
|
20
|
+
# ========================
|
|
21
|
+
|
|
22
|
+
def _root(self, element: Node | None) -> Node | HTMLParser:
|
|
23
|
+
"""İşlem yapılacak temel elementi döndürür."""
|
|
24
|
+
return element if element is not None else self.parser
|
|
25
|
+
|
|
26
|
+
def select(self, selector: str, element: Node | None = None) -> list[Node]:
|
|
27
|
+
"""CSS selector ile tüm eşleşen elementleri döndür."""
|
|
28
|
+
return self._root(element).css(selector)
|
|
29
|
+
|
|
30
|
+
def select_first(self, selector: str | None, element: Node | None = None) -> Node | None:
|
|
31
|
+
"""CSS selector ile ilk eşleşen elementi döndür."""
|
|
32
|
+
if not selector:
|
|
33
|
+
return element
|
|
34
|
+
return self._root(element).css_first(selector)
|
|
35
|
+
|
|
36
|
+
def select_text(self, selector: str | None = None, element: Node | None = None, strip: bool = True) -> str | None:
|
|
37
|
+
"""CSS selector ile element bul ve text içeriğini döndür."""
|
|
38
|
+
el = self.select_first(selector, element)
|
|
39
|
+
if not el:
|
|
40
|
+
return None
|
|
41
|
+
val = el.text(strip=strip)
|
|
42
|
+
return val or None
|
|
43
|
+
|
|
44
|
+
def select_texts(self, selector: str, element: Node | None = None, strip: bool = True) -> list[str]:
|
|
45
|
+
"""CSS selector ile tüm eşleşen elementlerin text içeriklerini döndür."""
|
|
46
|
+
out: list[str] = []
|
|
47
|
+
for el in self.select(selector, element):
|
|
48
|
+
txt = el.text(strip=strip)
|
|
49
|
+
if txt:
|
|
50
|
+
out.append(txt)
|
|
51
|
+
return out
|
|
52
|
+
|
|
53
|
+
def select_attr(self, selector: str | None, attr: str, element: Node | None = None) -> str | None:
|
|
54
|
+
"""CSS selector ile element bul ve attribute değerini döndür."""
|
|
55
|
+
el = self.select_first(selector, element)
|
|
56
|
+
return el.attrs.get(attr) if el else None
|
|
57
|
+
|
|
58
|
+
def select_attrs(self, selector: str, attr: str, element: Node | None = None) -> list[str]:
|
|
59
|
+
"""CSS selector ile tüm eşleşen elementlerin attribute değerlerini döndür."""
|
|
60
|
+
out: list[str] = []
|
|
61
|
+
for el in self.select(selector, element):
|
|
62
|
+
val = el.attrs.get(attr)
|
|
63
|
+
if val:
|
|
64
|
+
out.append(val)
|
|
65
|
+
return out
|
|
66
|
+
|
|
67
|
+
def select_poster(self, selector: str = "img", element: Node | None = None) -> str | None:
|
|
68
|
+
"""Poster URL'sini çıkar. Önce data-src, sonra src dener."""
|
|
69
|
+
el = self.select_first(selector, element)
|
|
70
|
+
if not el:
|
|
71
|
+
return None
|
|
72
|
+
return el.attrs.get("data-src") or el.attrs.get("src")
|
|
73
|
+
|
|
74
|
+
def select_direct_text(self, selector: str, element: Node | None = None, strip: bool = True) -> str | None:
|
|
75
|
+
"""
|
|
76
|
+
Elementin yalnızca "kendi" düz metnini döndürür (child elementlerin text'ini katmadan).
|
|
77
|
+
Selectolax sürüm farklarına göre deep=False dene, yoksa node sibling-walk ile fallback yapar.
|
|
78
|
+
"""
|
|
79
|
+
el = self.select_first(selector, element)
|
|
80
|
+
if not el:
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
# 1) Bazı sürümlerde var: sadece direct text
|
|
84
|
+
try:
|
|
85
|
+
val = el.text(strip=strip, deep=False) # type: ignore[call-arg]
|
|
86
|
+
return val or None
|
|
87
|
+
except TypeError:
|
|
88
|
+
pass # deep parametresi yok, fallback'e geç
|
|
89
|
+
|
|
90
|
+
# 2) Fallback: direct children'ı el.child + next ile dolaş
|
|
91
|
+
parts: list[str] = []
|
|
92
|
+
ch = el.child
|
|
93
|
+
while ch is not None:
|
|
94
|
+
if ch.tag == "-text":
|
|
95
|
+
t = ch.text(strip=strip)
|
|
96
|
+
if t:
|
|
97
|
+
parts.append(t)
|
|
98
|
+
elif ch.tag == "br":
|
|
99
|
+
parts.append("\n")
|
|
100
|
+
ch = ch.next
|
|
101
|
+
|
|
102
|
+
out = "".join(parts).strip()
|
|
103
|
+
return out or None
|
|
104
|
+
|
|
105
|
+
# ========================
|
|
106
|
+
# META (LABEL -> VALUE) İŞLEMLERİ
|
|
107
|
+
# ========================
|
|
108
|
+
|
|
109
|
+
def meta_value(self, label: str, container_selector: str | None = None, strip: bool = True) -> str | None:
|
|
110
|
+
"""
|
|
111
|
+
Herhangi bir container içinde: LABEL metnini içeren bir elementten SONRA gelen metni döndürür.
|
|
112
|
+
label örn: "Oyuncular", "Yapım Yılı", "IMDB"
|
|
113
|
+
"""
|
|
114
|
+
needle = label.casefold()
|
|
115
|
+
|
|
116
|
+
# Belirli bir container varsa içinde ara, yoksa tüm dökümanda
|
|
117
|
+
targets = self.select(container_selector) if container_selector else [self.parser.body]
|
|
118
|
+
|
|
119
|
+
for root in targets:
|
|
120
|
+
if not root: continue
|
|
121
|
+
|
|
122
|
+
# Kalın/vurgulu elementlerde (span, strong, b, label, dt) label'ı ara
|
|
123
|
+
for label_el in self.select("span, strong, b, label, dt", root):
|
|
124
|
+
txt = (label_el.text(strip=True) or "").casefold()
|
|
125
|
+
if needle not in txt:
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
# 1) Elementin kendi içindeki text'te LABEL: VALUE formatı olabilir
|
|
129
|
+
# "Oyuncular: Brad Pitt" gibi. LABEL: sonrasını al.
|
|
130
|
+
full_txt = label_el.text(strip=strip)
|
|
131
|
+
if ":" in full_txt and needle in full_txt.split(":")[0].casefold():
|
|
132
|
+
val = full_txt.split(":", 1)[1].strip()
|
|
133
|
+
if val: return val
|
|
134
|
+
|
|
135
|
+
# 2) Label sonrası gelen ilk text node'u veya element'i al
|
|
136
|
+
curr = label_el.next
|
|
137
|
+
while curr:
|
|
138
|
+
if curr.tag == "-text":
|
|
139
|
+
val = curr.text(strip=strip).strip(" :")
|
|
140
|
+
if val: return val
|
|
141
|
+
elif curr.tag != "br":
|
|
142
|
+
val = curr.text(strip=strip).strip(" :")
|
|
143
|
+
if val: return val
|
|
144
|
+
else: # <br> gördüysek satır bitmiştir
|
|
145
|
+
break
|
|
146
|
+
curr = curr.next
|
|
147
|
+
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
def meta_list(self, label: str, container_selector: str | None = None, sep: str = ",") -> list[str]:
|
|
151
|
+
"""meta_value(...) çıktısını veya label'ın ebeveynindeki linkleri listeye döndürür."""
|
|
152
|
+
needle = label.casefold()
|
|
153
|
+
targets = self.select(container_selector) if container_selector else [self.parser.body]
|
|
154
|
+
|
|
155
|
+
for root in targets:
|
|
156
|
+
if not root: continue
|
|
157
|
+
for label_el in self.select("span, strong, b, label, dt", root):
|
|
158
|
+
if needle in (label_el.text(strip=True) or "").casefold():
|
|
159
|
+
# Eğer elementin ebeveyninde linkler varsa (Kutucuklu yapı), onları al
|
|
160
|
+
links = self.select_texts("a", label_el.parent)
|
|
161
|
+
if links: return links
|
|
162
|
+
|
|
163
|
+
# Yoksa düz metin olarak meta_value mantığıyla al
|
|
164
|
+
raw = self.meta_value(label, container_selector=container_selector)
|
|
165
|
+
if not raw: return []
|
|
166
|
+
return [x.strip() for x in raw.split(sep) if x.strip()]
|
|
167
|
+
|
|
168
|
+
return []
|
|
169
|
+
|
|
170
|
+
# ========================
|
|
171
|
+
# REGEX İŞLEMLERİ
|
|
172
|
+
# ========================
|
|
173
|
+
|
|
174
|
+
def _regex_source(self, target: str | int | None) -> str:
|
|
175
|
+
"""Regex için kaynak metni döndürür."""
|
|
176
|
+
return target if isinstance(target, str) else self.html
|
|
177
|
+
|
|
178
|
+
def _regex_flags(self, target: str | int | None, flags: int) -> int:
|
|
179
|
+
"""Regex flags değerini döndürür."""
|
|
180
|
+
return target if isinstance(target, int) else flags
|
|
181
|
+
|
|
182
|
+
def regex_first(self, pattern: str, target: str | int | None = None, flags: int = 0, group: int | None = 1) -> str | tuple | None:
|
|
183
|
+
"""Regex ile arama yap, istenen grubu döndür (group=None ise tüm grupları tuple olarak döndür)."""
|
|
184
|
+
match = re.search(pattern, self._regex_source(target), self._regex_flags(target, flags))
|
|
185
|
+
if not match:
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
if group is None:
|
|
189
|
+
return match.groups()
|
|
190
|
+
|
|
191
|
+
last_idx = match.lastindex or 0
|
|
192
|
+
return match.group(group) if last_idx >= group else match.group(0)
|
|
193
|
+
|
|
194
|
+
def regex_all(self, pattern: str, target: str | int | None = None, flags: int = 0) -> list[str]:
|
|
195
|
+
"""Regex ile tüm eşleşmeleri döndür."""
|
|
196
|
+
return re.findall(pattern, self._regex_source(target), self._regex_flags(target, flags))
|
|
197
|
+
|
|
198
|
+
def regex_replace(self, pattern: str, repl: str, target: str | int | None = None, flags: int = 0) -> str:
|
|
199
|
+
"""Regex ile replace yap."""
|
|
200
|
+
return re.sub(pattern, repl, self._regex_source(target), flags=self._regex_flags(target, flags))
|
|
201
|
+
|
|
202
|
+
# ========================
|
|
203
|
+
# ÖZEL AYIKLAYICILAR
|
|
204
|
+
# ========================
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def extract_season_episode(text: str) -> tuple[int | None, int | None]:
|
|
208
|
+
"""Metin içinden sezon ve bölüm numarasını çıkar."""
|
|
209
|
+
if m := re.search(r"[Ss](\d+)[Ee](\d+)", text):
|
|
210
|
+
return int(m.group(1)), int(m.group(2))
|
|
211
|
+
|
|
212
|
+
s = re.search(r"(\d+)\.\s*[Ss]ezon|[Ss]ezon[- ]?(\d+)|-(\d+)-sezon|S(\d+)|(\d+)\.[Ss]", text, re.I)
|
|
213
|
+
e = re.search(r"(\d+)\.\s*[Bb][öo]l[üu]m|[Bb][öo]l[üu]m[- ]?(\d+)|-(\d+)-bolum|[Ee](\d+)", text, re.I)
|
|
214
|
+
|
|
215
|
+
s_val = next((int(g) for g in s.groups() if g), None) if s else None
|
|
216
|
+
e_val = next((int(g) for g in e.groups() if g), None) if e else None
|
|
217
|
+
|
|
218
|
+
return s_val, e_val
|
|
219
|
+
|
|
220
|
+
def extract_year(self, *selectors: str, pattern: str = r"(\d{4})") -> int | None:
|
|
221
|
+
"""Birden fazla selector veya regex ile yıl bilgisini çıkar."""
|
|
222
|
+
for selector in selectors:
|
|
223
|
+
if text := self.select_text(selector):
|
|
224
|
+
if m := re.search(r"(\d{4})", text):
|
|
225
|
+
return int(m.group(1))
|
|
226
|
+
|
|
227
|
+
val = self.regex_first(pattern)
|
|
228
|
+
return int(val) if val and val.isdigit() else None
|
|
@@ -101,9 +101,10 @@ class PluginBase(ABC):
|
|
|
101
101
|
return ""
|
|
102
102
|
|
|
103
103
|
if url.startswith("http") or url.startswith("{\""):
|
|
104
|
-
return url
|
|
104
|
+
return url.replace("\\", "")
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
url = f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
|
|
107
|
+
return url.replace("\\", "")
|
|
107
108
|
|
|
108
109
|
async def extract(self, url: str, referer: str = None, prefix: str | None = None) -> ExtractResult | None:
|
|
109
110
|
"""
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
4
|
+
from Kekik.Sifreleme import Packer, StreamDecoder
|
|
5
|
+
import json, contextlib
|
|
6
|
+
|
|
7
|
+
class CloseLoad(ExtractorBase):
|
|
8
|
+
name = "CloseLoad"
|
|
9
|
+
main_url = "https://closeload.filmmakinesi.to"
|
|
10
|
+
|
|
11
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
12
|
+
self.httpx.headers.update({
|
|
13
|
+
"Referer" : referer or self.main_url,
|
|
14
|
+
"Origin" : self.main_url
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
resp = await self.httpx.get(url)
|
|
18
|
+
sel = HTMLHelper(resp.text)
|
|
19
|
+
|
|
20
|
+
# 1. JSON-LD'den Dene
|
|
21
|
+
m3u8_url = None
|
|
22
|
+
for script in sel.select("script[type='application/ld+json']"):
|
|
23
|
+
with contextlib.suppress(Exception):
|
|
24
|
+
data = json.loads(script.text(strip=True))
|
|
25
|
+
if content_url := data.get("contentUrl"):
|
|
26
|
+
if content_url.startswith("http"):
|
|
27
|
+
m3u8_url = content_url
|
|
28
|
+
break
|
|
29
|
+
|
|
30
|
+
# 2. Packed Script Fallback
|
|
31
|
+
if not m3u8_url:
|
|
32
|
+
if packed := sel.regex_first(r"(eval\(function\(p,a,c,k,e,d\).+?)\s*</script>"):
|
|
33
|
+
m3u8_url = StreamDecoder.extract_stream_url(Packer.unpack(packed))
|
|
34
|
+
|
|
35
|
+
if not m3u8_url:
|
|
36
|
+
raise ValueError(f"CloseLoad: Video URL bulunamadı. {url}")
|
|
37
|
+
|
|
38
|
+
subtitles = []
|
|
39
|
+
for track in sel.select("track"):
|
|
40
|
+
src = track.attrs.get("src")
|
|
41
|
+
if src:
|
|
42
|
+
subtitles.append(Subtitle(
|
|
43
|
+
name = track.attrs.get("label") or track.attrs.get("srclang") or "Altyazı",
|
|
44
|
+
url = self.fix_url(src)
|
|
45
|
+
))
|
|
46
|
+
|
|
47
|
+
return ExtractResult(
|
|
48
|
+
name = self.name,
|
|
49
|
+
url = m3u8_url,
|
|
50
|
+
referer = self.main_url,
|
|
51
|
+
subtitles = subtitles
|
|
52
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
|
|
4
|
+
|
|
5
|
+
class ContentX(ExtractorBase):
|
|
6
|
+
name = "ContentX"
|
|
7
|
+
main_url = "https://contentx.me"
|
|
8
|
+
|
|
9
|
+
# Birden fazla domain destekle
|
|
10
|
+
supported_domains = [
|
|
11
|
+
"contentx.me", "four.contentx.me",
|
|
12
|
+
"dplayer82.site", "sn.dplayer82.site", "four.dplayer82.site", "org.dplayer82.site",
|
|
13
|
+
"dplayer74.site", "sn.dplayer74.site",
|
|
14
|
+
"hotlinger.com", "sn.hotlinger.com",
|
|
15
|
+
"playru.net", "four.playru.net",
|
|
16
|
+
"pichive.online", "four.pichive.online", "pichive.me", "four.pichive.me"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
def can_handle_url(self, url: str) -> bool:
|
|
20
|
+
return any(domain in url for domain in self.supported_domains)
|
|
21
|
+
|
|
22
|
+
async def extract(self, url: str, referer: str = None) -> list[ExtractResult] | ExtractResult:
|
|
23
|
+
ref = referer or self.get_base_url(url)
|
|
24
|
+
self.httpx.headers.update({"Referer": ref})
|
|
25
|
+
|
|
26
|
+
resp = await self.httpx.get(url)
|
|
27
|
+
sel = HTMLHelper(resp.text)
|
|
28
|
+
|
|
29
|
+
v_id = sel.regex_first(r"window\.openPlayer\('([^']+)'")
|
|
30
|
+
if not v_id:
|
|
31
|
+
raise ValueError(f"ContentX: ID bulunamadı. {url}")
|
|
32
|
+
|
|
33
|
+
subtitles = []
|
|
34
|
+
for s_url, s_lang in sel.regex_all(r'"file":"([^\"]+)","label":"([^\"]+)"'):
|
|
35
|
+
decoded_lang = s_lang.encode().decode('unicode_escape')
|
|
36
|
+
subtitles.append(Subtitle(name=decoded_lang, url=self.fix_url(s_url.replace("\\", ""))))
|
|
37
|
+
|
|
38
|
+
results = []
|
|
39
|
+
# Base m3u8
|
|
40
|
+
vid_resp = await self.httpx.get(f"{self.get_base_url(url)}/source2.php?v={v_id}", headers={"Referer": url})
|
|
41
|
+
if m3u8_link := HTMLHelper(vid_resp.text).regex_first(r'file":"([^\"]+)"'):
|
|
42
|
+
m3u8_link = m3u8_link.replace("\\", "").replace("/m.php", "/master.m3u8")
|
|
43
|
+
results.append(ExtractResult(name=self.name, url=m3u8_link, referer=url, subtitles=subtitles))
|
|
44
|
+
|
|
45
|
+
# Dublaj Kontrolü
|
|
46
|
+
if dub_id := sel.regex_first(r'["\']([^"\']+)["\'],["\']Türkçe["\']'):
|
|
47
|
+
dub_resp = await self.httpx.get(f"{self.get_base_url(url)}/source2.php?v={dub_id}", headers={"Referer": url})
|
|
48
|
+
if dub_link := HTMLHelper(dub_resp.text).regex_first(r'file":"([^\"]+)"'):
|
|
49
|
+
results.append(ExtractResult(name=f"{self.name} Türkçe Dublaj", url=dub_link.replace("\\", ""), referer=url))
|
|
50
|
+
|
|
51
|
+
if not results:
|
|
52
|
+
raise ValueError(f"ContentX: Video linki bulunamadı. {url}")
|
|
53
|
+
|
|
54
|
+
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, Subtitle, HTMLHelper
|
|
4
|
+
from Kekik.Sifreleme import AESManager
|
|
5
|
+
import json, contextlib
|
|
6
|
+
|
|
7
|
+
class DonilasPlay(ExtractorBase):
|
|
8
|
+
name = "DonilasPlay"
|
|
9
|
+
main_url = "https://donilasplay.com"
|
|
10
|
+
|
|
11
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
12
|
+
self.httpx.headers.update({"Referer": referer or url})
|
|
13
|
+
|
|
14
|
+
resp = await self.httpx.get(url)
|
|
15
|
+
sel = HTMLHelper(resp.text)
|
|
16
|
+
|
|
17
|
+
m3u8_url = None
|
|
18
|
+
subtitles = []
|
|
19
|
+
|
|
20
|
+
# 1. bePlayer (AES)
|
|
21
|
+
if be_match := sel.regex_first(r"bePlayer\('([^']+)',\s*'(\{[^}]+\})'\);", group=None):
|
|
22
|
+
pass_val, data_val = be_match
|
|
23
|
+
with contextlib.suppress(Exception):
|
|
24
|
+
data = json.loads(AESManager.decrypt(data_val, pass_val))
|
|
25
|
+
m3u8_url = data.get("video_location")
|
|
26
|
+
for sub in data.get("strSubtitles", []):
|
|
27
|
+
if "Forced" not in sub.get("label", ""):
|
|
28
|
+
subtitles.append(Subtitle(name=sub.get("label"), url=self.fix_url(sub.get("file"))))
|
|
29
|
+
|
|
30
|
+
# 2. Fallback
|
|
31
|
+
if not m3u8_url:
|
|
32
|
+
m3u8_url = sel.regex_first(r'file:"([^"]+)"')
|
|
33
|
+
if tracks_match := sel.regex_first(r'tracks:\[([^\]]+)'):
|
|
34
|
+
with contextlib.suppress(Exception):
|
|
35
|
+
for track in json.loads(f"[{tracks_match}]"):
|
|
36
|
+
if "Forced" not in track.get("label", ""):
|
|
37
|
+
subtitles.append(Subtitle(name=track.get("label"), url=self.fix_url(track.get("file"))))
|
|
38
|
+
|
|
39
|
+
if not m3u8_url:
|
|
40
|
+
raise ValueError(f"DonilasPlay: Video linki bulunamadı. {url}")
|
|
41
|
+
|
|
42
|
+
return ExtractResult(name=self.name, url=m3u8_url, referer=url, subtitles=subtitles)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
4
|
+
|
|
5
|
+
class DzenRu(ExtractorBase):
|
|
6
|
+
name = "DzenRu"
|
|
7
|
+
main_url = "https://dzen.ru"
|
|
8
|
+
|
|
9
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
10
|
+
video_key = url.split("/")[-1]
|
|
11
|
+
v_url = f"{self.main_url}/embed/{video_key}"
|
|
12
|
+
|
|
13
|
+
if referer:
|
|
14
|
+
self.httpx.headers.update({"Referer": referer})
|
|
15
|
+
|
|
16
|
+
resp = await self.httpx.get(v_url)
|
|
17
|
+
sel = HTMLHelper(resp.text)
|
|
18
|
+
|
|
19
|
+
# Benzersiz okcdn.ru linklerini bul ve en yüksek kaliteyi (genelde sonuncu) seç
|
|
20
|
+
links = sel.regex_all(r'https://vd\d+\.okcdn\.ru/\?[^"\'\\\s]+')
|
|
21
|
+
if not links:
|
|
22
|
+
raise ValueError(f"DzenRu: Video linki bulunamadı. {url}")
|
|
23
|
+
|
|
24
|
+
return ExtractResult(name=self.name, url=list(set(links))[-1], referer=self.main_url)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
4
|
+
from urllib.parse import urlparse, parse_qs
|
|
5
|
+
|
|
6
|
+
class ExPlay(ExtractorBase):
|
|
7
|
+
name = "ExPlay"
|
|
8
|
+
main_url = "https://explay.store"
|
|
9
|
+
|
|
10
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
11
|
+
self.httpx.headers.update({"Referer": referer or url})
|
|
12
|
+
|
|
13
|
+
# Clean URL from partKey for initial request
|
|
14
|
+
clean_url = url.split("?")[0]
|
|
15
|
+
resp = await self.httpx.get(clean_url)
|
|
16
|
+
sel = HTMLHelper(resp.text)
|
|
17
|
+
|
|
18
|
+
v_url = sel.regex_first(r'videoUrl":"([^",]+)"')
|
|
19
|
+
v_srv = sel.regex_first(r'videoServer":"([^",]+)"')
|
|
20
|
+
if not v_url or not v_srv:
|
|
21
|
+
raise ValueError(f"ExPlay: Video url/server bulunamadı. {url}")
|
|
22
|
+
|
|
23
|
+
params = parse_qs(urlparse(url).query)
|
|
24
|
+
part_key = params.get("partKey", [""])[0]
|
|
25
|
+
|
|
26
|
+
suffix = part_key or "Bilinmiyor"
|
|
27
|
+
if not part_key:
|
|
28
|
+
title = sel.regex_first(r'title":"([^",]+)"')
|
|
29
|
+
if title: suffix = title.split(".")[-1]
|
|
30
|
+
|
|
31
|
+
return ExtractResult(
|
|
32
|
+
name = f"{self.name} - {suffix}",
|
|
33
|
+
url = f"{self.main_url}{v_url.replace('\\', '')}?s={v_srv}",
|
|
34
|
+
referer = clean_url
|
|
35
|
+
)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
4
|
+
from Kekik.Sifreleme import Packer
|
|
5
|
+
|
|
6
|
+
class Filemoon(ExtractorBase):
|
|
7
|
+
name = "Filemoon"
|
|
8
|
+
main_url = "https://filemoon.to"
|
|
9
|
+
|
|
10
|
+
# Filemoon'un farklı domainlerini destekle
|
|
11
|
+
supported_domains = [
|
|
12
|
+
"filemoon.to",
|
|
13
|
+
"filemoon.in",
|
|
14
|
+
"filemoon.sx",
|
|
15
|
+
"filemoon.nl",
|
|
16
|
+
"filemoon.com"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
def can_handle_url(self, url: str) -> bool:
|
|
20
|
+
return any(domain in url for domain in self.supported_domains)
|
|
21
|
+
|
|
22
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
23
|
+
default_headers = {
|
|
24
|
+
"Referer" : url,
|
|
25
|
+
"Sec-Fetch-Dest" : "iframe",
|
|
26
|
+
"Sec-Fetch-Mode" : "navigate",
|
|
27
|
+
"Sec-Fetch-Site" : "cross-site",
|
|
28
|
+
"User-Agent" : "Mozilla/5.0 (X11; Linux x86_64; rv:137.0) Gecko/20100101 Firefox/137.0"
|
|
29
|
+
}
|
|
30
|
+
self.httpx.headers.update(default_headers)
|
|
31
|
+
|
|
32
|
+
# İlk sayfayı al
|
|
33
|
+
istek = await self.httpx.get(url)
|
|
34
|
+
secici = HTMLHelper(istek.text)
|
|
35
|
+
|
|
36
|
+
# Eğer iframe varsa, iframe'e git
|
|
37
|
+
iframe_src = secici.select_attr("iframe", "src")
|
|
38
|
+
m3u8_url = None
|
|
39
|
+
|
|
40
|
+
if iframe_src:
|
|
41
|
+
url = self.fix_url(iframe_src)
|
|
42
|
+
istek = await self.httpx.get(url)
|
|
43
|
+
secici = HTMLHelper(istek.text)
|
|
44
|
+
|
|
45
|
+
# script p,a,c,k,e,d içinde ara
|
|
46
|
+
script_data = secici.regex_first(r"(eval\(function\(p,a,c,k,e,d\).+?)\s*</script>")
|
|
47
|
+
if script_data:
|
|
48
|
+
unpacked = Packer.unpack(script_data)
|
|
49
|
+
m3u8_url = HTMLHelper(unpacked).regex_first(r'sources:\[\{file:"(.*?)"')
|
|
50
|
+
|
|
51
|
+
if not m3u8_url:
|
|
52
|
+
# Fallback
|
|
53
|
+
m3u8_url = secici.regex_first(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"') or secici.regex_first(r'file:\s*"([^\"]*?\.m3u8[^"]*)"')
|
|
54
|
+
|
|
55
|
+
if not m3u8_url:
|
|
56
|
+
raise ValueError(f"Filemoon: Video URL bulunamadı. {url}")
|
|
57
|
+
|
|
58
|
+
return ExtractResult(
|
|
59
|
+
name = self.name,
|
|
60
|
+
url = self.fix_url(m3u8_url),
|
|
61
|
+
referer = f"{self.get_base_url(url)}/",
|
|
62
|
+
user_agent = default_headers["User-Agent"]
|
|
63
|
+
)
|
|
@@ -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, HTMLHelper
|
|
4
|
+
from Kekik.Sifreleme import AESManager
|
|
5
|
+
import contextlib, json
|
|
6
|
+
|
|
7
|
+
class HDMomPlayer(ExtractorBase):
|
|
8
|
+
name = "HDMomPlayer"
|
|
9
|
+
main_url = "https://hdmomplayer.com"
|
|
10
|
+
|
|
11
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
12
|
+
self.httpx.headers.update({"Referer": referer or url})
|
|
13
|
+
|
|
14
|
+
resp = await self.httpx.get(url)
|
|
15
|
+
sel = HTMLHelper(resp.text)
|
|
16
|
+
|
|
17
|
+
m3u8_url = None
|
|
18
|
+
if match := sel.regex_first(r"bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);", group=None):
|
|
19
|
+
pass_val, data_val = match
|
|
20
|
+
with contextlib.suppress(Exception):
|
|
21
|
+
decrypted = AESManager.decrypt(data_val, pass_val)
|
|
22
|
+
m3u8_url = HTMLHelper(decrypted).regex_first(r'video_location":"([^"]+)"')
|
|
23
|
+
|
|
24
|
+
if not m3u8_url:
|
|
25
|
+
m3u8_url = sel.regex_first(r'file:"([^"]+)"')
|
|
26
|
+
|
|
27
|
+
if not m3u8_url:
|
|
28
|
+
raise ValueError(f"HDMomPlayer: Video linki bulunamadı. {url}")
|
|
29
|
+
|
|
30
|
+
return ExtractResult(name=self.name, url=self.fix_url(m3u8_url), referer=url)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
+
|
|
5
|
+
class HDPlayerSystem(ExtractorBase):
|
|
6
|
+
name = "HDPlayerSystem"
|
|
7
|
+
main_url = "https://hdplayersystem.com"
|
|
8
|
+
|
|
9
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
10
|
+
ref = referer or self.main_url
|
|
11
|
+
v_id = url.split("video/")[-1] if "video/" in url else url.split("?data=")[-1]
|
|
12
|
+
|
|
13
|
+
resp = await self.httpx.post(
|
|
14
|
+
f"{self.main_url}/player/index.php?data={v_id}&do=getVideo",
|
|
15
|
+
data = {"hash": v_id, "r": ref},
|
|
16
|
+
headers = {"Referer": ref, "X-Requested-With": "XMLHttpRequest"}
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
m3u8_url = resp.json().get("securedLink")
|
|
20
|
+
if not m3u8_url:
|
|
21
|
+
raise ValueError(f"HDPlayerSystem: Video URL bulunamadı. {url}")
|
|
22
|
+
|
|
23
|
+
return ExtractResult(name=self.name, url=m3u8_url, referer=url)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
4
|
+
from Kekik.Sifreleme import AESManager
|
|
5
|
+
import contextlib
|
|
6
|
+
|
|
7
|
+
class HotStream(ExtractorBase):
|
|
8
|
+
name = "HotStream"
|
|
9
|
+
main_url = "https://hotstream.club"
|
|
10
|
+
|
|
11
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
12
|
+
self.httpx.headers.update({"Referer": referer or url})
|
|
13
|
+
|
|
14
|
+
resp = await self.httpx.get(url)
|
|
15
|
+
sel = HTMLHelper(resp.text)
|
|
16
|
+
|
|
17
|
+
m3u8_url = None
|
|
18
|
+
if match := sel.regex_first(r"bePlayer\('([^']+)',\s*'(\{[^']+\})'\)", group=None):
|
|
19
|
+
pass_val, data_val = match
|
|
20
|
+
with contextlib.suppress(Exception):
|
|
21
|
+
decrypted = AESManager.decrypt(data_val, pass_val)
|
|
22
|
+
m3u8_url = HTMLHelper(decrypted).regex_first(r'"video_location":"([^"]+)"')
|
|
23
|
+
|
|
24
|
+
if not m3u8_url:
|
|
25
|
+
raise ValueError(f"HotStream: Video linki bulunamadı. {url}")
|
|
26
|
+
|
|
27
|
+
return ExtractResult(name=self.name, url=self.fix_url(m3u8_url), referer=url)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
+
|
|
5
|
+
class JFVid(ExtractorBase):
|
|
6
|
+
name = "JFVid"
|
|
7
|
+
main_url = "https://jfvid.com"
|
|
8
|
+
|
|
9
|
+
# Birden fazla domain destekle
|
|
10
|
+
supported_domains = ["jfvid.com"]
|
|
11
|
+
|
|
12
|
+
def can_handle_url(self, url: str) -> bool:
|
|
13
|
+
return any(domain in url for domain in self.supported_domains)
|
|
14
|
+
|
|
15
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
16
|
+
base_url = self.get_base_url(url)
|
|
17
|
+
v_id = url.split("/play/")[-1] if "/play/" in url else url.split("/stream/")[-1]
|
|
18
|
+
|
|
19
|
+
return ExtractResult(name=self.name, url=f"{base_url}/stream/{v_id}", referer=referer or base_url)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
4
|
+
import contextlib
|
|
5
|
+
|
|
6
|
+
class JetTv(ExtractorBase):
|
|
7
|
+
name = "JetTv"
|
|
8
|
+
main_url = "https://jetv.xyz"
|
|
9
|
+
|
|
10
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
11
|
+
resp = await self.httpx.get(url)
|
|
12
|
+
sel = HTMLHelper(resp.text)
|
|
13
|
+
|
|
14
|
+
m3u8_url = None
|
|
15
|
+
final_ref = self.main_url
|
|
16
|
+
|
|
17
|
+
if "id=" in url:
|
|
18
|
+
v_id = url.split("id=")[-1]
|
|
19
|
+
with contextlib.suppress(Exception):
|
|
20
|
+
api_resp = await self.httpx.get(f"{self.main_url}/apollo/get_video.php?id={v_id}", headers={"Referer": url})
|
|
21
|
+
data = api_resp.json()
|
|
22
|
+
if data.get("success"):
|
|
23
|
+
m3u8_url = data.get("masterUrl")
|
|
24
|
+
final_ref = data.get("referrerUrl") or final_ref
|
|
25
|
+
|
|
26
|
+
if not m3u8_url:
|
|
27
|
+
m3u8_url = sel.regex_first(r"(?i)file: '([^']*)'")
|
|
28
|
+
|
|
29
|
+
if not m3u8_url:
|
|
30
|
+
raise ValueError(f"JetTv: Video URL bulunamadı. {url}")
|
|
31
|
+
|
|
32
|
+
return ExtractResult(name=self.name, url=m3u8_url, referer=final_ref)
|