KekikStream 2.0.2__py3-none-any.whl → 2.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- KekikStream/Core/Extractor/ExtractorBase.py +7 -2
- KekikStream/Core/Plugin/PluginBase.py +61 -13
- KekikStream/Extractors/CloseLoad.py +17 -2
- KekikStream/Extractors/ContentX.py +19 -2
- KekikStream/Extractors/DonilasPlay.py +86 -0
- KekikStream/Extractors/Filemoon.py +78 -0
- KekikStream/Extractors/MixTiger.py +5 -5
- KekikStream/Extractors/Odnoklassniki.py +6 -0
- KekikStream/Extractors/PeaceMakerst.py +6 -0
- KekikStream/Extractors/PlayerFilmIzle.py +8 -5
- KekikStream/Extractors/RapidVid.py +21 -5
- KekikStream/Extractors/SetPlay.py +13 -4
- KekikStream/Extractors/VCTPlay.py +41 -0
- KekikStream/Extractors/VidHide.py +11 -2
- KekikStream/Extractors/VidMoly.py +52 -30
- KekikStream/Extractors/YTDLP.py +88 -54
- KekikStream/Plugins/BelgeselX.py +196 -0
- KekikStream/Plugins/DiziBox.py +8 -12
- KekikStream/Plugins/DiziPal.py +11 -22
- KekikStream/Plugins/DiziYou.py +9 -17
- KekikStream/Plugins/Dizilla.py +19 -14
- KekikStream/Plugins/FilmBip.py +5 -8
- KekikStream/Plugins/FilmMakinesi.py +31 -21
- KekikStream/Plugins/FilmModu.py +14 -18
- KekikStream/Plugins/FullHDFilm.py +83 -27
- KekikStream/Plugins/FullHDFilmizlesene.py +6 -8
- KekikStream/Plugins/HDFilmCehennemi.py +133 -57
- KekikStream/Plugins/JetFilmizle.py +29 -14
- KekikStream/Plugins/KultFilmler.py +13 -15
- KekikStream/Plugins/RecTV.py +16 -24
- KekikStream/Plugins/RoketDizi.py +22 -32
- KekikStream/Plugins/SelcukFlix.py +99 -80
- KekikStream/Plugins/SetFilmIzle.py +252 -0
- KekikStream/Plugins/SezonlukDizi.py +43 -9
- KekikStream/Plugins/SineWix.py +13 -21
- KekikStream/Plugins/Sinefy.py +13 -10
- KekikStream/Plugins/SinemaCX.py +35 -38
- KekikStream/Plugins/Sinezy.py +5 -8
- KekikStream/Plugins/SuperFilmGeldi.py +36 -27
- KekikStream/Plugins/UgurFilm.py +7 -9
- KekikStream/__init__.py +17 -36
- {kekikstream-2.0.2.dist-info → kekikstream-2.2.0.dist-info}/METADATA +6 -3
- kekikstream-2.2.0.dist-info/RECORD +81 -0
- KekikStream/Extractors/ContentX_.py +0 -40
- KekikStream/Extractors/FirePlayer.py +0 -60
- KekikStream/Extractors/Odnoklassniki_.py +0 -11
- KekikStream/Extractors/PeaceMakerst_.py +0 -7
- KekikStream/Extractors/RapidVid_.py +0 -7
- KekikStream/Extractors/VidMoly_.py +0 -7
- kekikstream-2.0.2.dist-info/RECORD +0 -82
- {kekikstream-2.0.2.dist-info → kekikstream-2.2.0.dist-info}/WHEEL +0 -0
- {kekikstream-2.0.2.dist-info → kekikstream-2.2.0.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.0.2.dist-info → kekikstream-2.2.0.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.0.2.dist-info → kekikstream-2.2.0.dist-info}/top_level.txt +0 -0
|
@@ -9,6 +9,12 @@ class VidMoly(ExtractorBase):
|
|
|
9
9
|
name = "VidMoly"
|
|
10
10
|
main_url = "https://vidmoly.to"
|
|
11
11
|
|
|
12
|
+
# Birden fazla domain destekle
|
|
13
|
+
supported_domains = ["vidmoly.to", "vidmoly.me", "vidmoly.net"]
|
|
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
|
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
13
19
|
if referer:
|
|
14
20
|
self.httpx.headers.update({"Referer": referer})
|
|
@@ -17,11 +23,11 @@ class VidMoly(ExtractorBase):
|
|
|
17
23
|
"Sec-Fetch-Dest" : "iframe",
|
|
18
24
|
})
|
|
19
25
|
|
|
20
|
-
if
|
|
21
|
-
self.main_url = self.main_url.replace(".me", ".net")
|
|
26
|
+
if ".me" in url:
|
|
22
27
|
url = url.replace(".me", ".net")
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
# VidMoly bazen redirect ediyor, takip et
|
|
30
|
+
response = await self.httpx.get(url, follow_redirects=True)
|
|
25
31
|
if "Select number" in response.text:
|
|
26
32
|
secici = Selector(response.text)
|
|
27
33
|
response = await self.httpx.post(
|
|
@@ -33,21 +39,10 @@ class VidMoly(ExtractorBase):
|
|
|
33
39
|
"ts" : secici.css("input[name='ts']::attr(value)").get(),
|
|
34
40
|
"nonce" : secici.css("input[name='nonce']::attr(value)").get(),
|
|
35
41
|
"ctok" : secici.css("input[name='ctok']::attr(value)").get()
|
|
36
|
-
}
|
|
42
|
+
},
|
|
43
|
+
follow_redirects=True
|
|
37
44
|
)
|
|
38
45
|
|
|
39
|
-
script_match = re.search(r"sources:\s*\[(.*?)\],", response.text, re.DOTALL)
|
|
40
|
-
script_content = script_match[1] if script_match else None
|
|
41
|
-
|
|
42
|
-
if not script_content:
|
|
43
|
-
raise ValueError("Gerekli script bulunamadı.")
|
|
44
|
-
|
|
45
|
-
# Video kaynaklarını ayrıştır
|
|
46
|
-
video_data = self._add_marks(script_content, "file")
|
|
47
|
-
try:
|
|
48
|
-
video_sources = json.loads(f"[{video_data}]")
|
|
49
|
-
except json.JSONDecodeError as hata:
|
|
50
|
-
raise ValueError("Video kaynakları ayrıştırılamadı.") from hata
|
|
51
46
|
|
|
52
47
|
# Altyazı kaynaklarını ayrıştır
|
|
53
48
|
subtitles = []
|
|
@@ -66,22 +61,49 @@ class VidMoly(ExtractorBase):
|
|
|
66
61
|
for sub in subtitle_sources
|
|
67
62
|
if sub.get("kind") == "captions"
|
|
68
63
|
]
|
|
69
|
-
# İlk video kaynağını al
|
|
70
|
-
video_url = None
|
|
71
|
-
for source in video_sources:
|
|
72
|
-
if file_url := source.get("file"):
|
|
73
|
-
video_url = file_url
|
|
74
|
-
break
|
|
75
64
|
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
script_match = re.search(r"sources:\s*\[(.*?)\],", response.text, re.DOTALL)
|
|
66
|
+
if script_match:
|
|
67
|
+
script_content = script_match[1]
|
|
68
|
+
# Video kaynaklarını ayrıştır
|
|
69
|
+
video_data = self._add_marks(script_content, "file")
|
|
70
|
+
try:
|
|
71
|
+
video_sources = json.loads(f"[{video_data}]")
|
|
72
|
+
# İlk video kaynağını al
|
|
73
|
+
for source in video_sources:
|
|
74
|
+
if file_url := source.get("file"):
|
|
75
|
+
return ExtractResult(
|
|
76
|
+
name = self.name,
|
|
77
|
+
url = file_url,
|
|
78
|
+
referer = self.main_url,
|
|
79
|
+
subtitles = subtitles
|
|
80
|
+
)
|
|
81
|
+
except json.JSONDecodeError:
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
# Fallback: Doğrudan file regex ile ara (Kotlin mantığı)
|
|
85
|
+
# file:"..." veya file: "..."
|
|
86
|
+
if file_match := re.search(r'file\s*:\s*["\']([^"\']+\.m3u8[^"\']*)["\']', response.text):
|
|
87
|
+
return ExtractResult(
|
|
88
|
+
name = self.name,
|
|
89
|
+
url = file_match.group(1),
|
|
90
|
+
referer = self.main_url,
|
|
91
|
+
subtitles = subtitles
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Fallback 2: Herhangi bir file (m3u8 olma şartı olmadan ama tercihen)
|
|
95
|
+
if file_match := re.search(r'file\s*:\s*["\']([^"\']+)["\']', response.text):
|
|
96
|
+
url_candidate = file_match.group(1)
|
|
97
|
+
# Resim dosyalarını hariç tut
|
|
98
|
+
if not url_candidate.endswith(('.jpg', '.png', '.jpeg')):
|
|
99
|
+
return ExtractResult(
|
|
100
|
+
name = self.name,
|
|
101
|
+
url = url_candidate,
|
|
102
|
+
referer = self.main_url,
|
|
103
|
+
subtitles = subtitles
|
|
104
|
+
)
|
|
78
105
|
|
|
79
|
-
|
|
80
|
-
name = self.name,
|
|
81
|
-
url = video_url,
|
|
82
|
-
referer = self.main_url,
|
|
83
|
-
subtitles = subtitles
|
|
84
|
-
)
|
|
106
|
+
raise ValueError("Video URL bulunamadı.")
|
|
85
107
|
|
|
86
108
|
def _add_marks(self, text: str, field: str) -> str:
|
|
87
109
|
"""
|
KekikStream/Extractors/YTDLP.py
CHANGED
|
@@ -10,6 +10,86 @@ class YTDLP(ExtractorBase):
|
|
|
10
10
|
|
|
11
11
|
_FAST_DOMAIN_RE = None # compiled mega-regex (host üstünden)
|
|
12
12
|
|
|
13
|
+
_POPULAR_TLDS = {
|
|
14
|
+
"com", "net", "org", "tv", "io", "co", "me", "ly", "ru", "fr", "de", "es", "it",
|
|
15
|
+
"nl", "be", "ch", "at", "uk", "ca", "au", "jp", "kr", "cn", "in", "br", "mx",
|
|
16
|
+
"ar", "tr", "gov", "edu", "mil", "int", "info", "biz", "name", "pro", "aero",
|
|
17
|
+
"coop", "museum", "onion"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# 1. Literal TLD Regex: youtube\.com, vimeo\.com
|
|
21
|
+
# sorted by reverse length to prevent partial matches (e.g. 'co' matching 'com')
|
|
22
|
+
_LITERAL_TLD_RE = re.compile(
|
|
23
|
+
rf"([a-z0-9][-a-z0-9]*(?:\\\.[-a-z0-9]+)*\\\.(?:{'|'.join(sorted(_POPULAR_TLDS, key=len, reverse=True))}))",
|
|
24
|
+
re.IGNORECASE
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# 2. Regex TLD Regex: dailymotion\.[a-z]{2,3}
|
|
28
|
+
_REGEX_TLD_RE = re.compile(
|
|
29
|
+
r"([a-z0-9][-a-z0-9]*)\\\.\[a-z\]\{?\d*,?\d*\}?",
|
|
30
|
+
re.IGNORECASE
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# 3. Alternation TLD Regex: \.(?:com|net|org)
|
|
34
|
+
_ALT_TLD_RE = re.compile(
|
|
35
|
+
r"\\\.\(\?:([a-z|]+)\)",
|
|
36
|
+
re.IGNORECASE
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Kelime yakalayıcı (domain bulmak için)
|
|
40
|
+
_DOMAIN_WORD_RE = re.compile(
|
|
41
|
+
r"([a-z0-9][-a-z0-9]*)",
|
|
42
|
+
re.IGNORECASE
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def _extract_literal_domains(cls, valid_url: str) -> set[str]:
|
|
47
|
+
"""Pattern 1: Literal TLD domainlerini (youtube.com) çıkarır."""
|
|
48
|
+
return {
|
|
49
|
+
m.replace(r"\.", ".").lower()
|
|
50
|
+
for m in cls._LITERAL_TLD_RE.findall(valid_url)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def _extract_regex_tld_domains(cls, valid_url: str) -> set[str]:
|
|
55
|
+
"""Pattern 2: Regex TLD domainlerini (dailymotion.[...]) çıkarır ve popüler TLD'lerle birleştirir."""
|
|
56
|
+
domains = set()
|
|
57
|
+
for base in cls._REGEX_TLD_RE.findall(valid_url):
|
|
58
|
+
base_domain = base.lower()
|
|
59
|
+
for tld in cls._POPULAR_TLDS:
|
|
60
|
+
domains.add(f"{base_domain}.{tld}")
|
|
61
|
+
return domains
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def _extract_alternation_domains(cls, valid_url: str) -> set[str]:
|
|
65
|
+
"""Pattern 3: Alternation TLD domainlerini (pornhub.(?:com|net)) çıkarır."""
|
|
66
|
+
domains = set()
|
|
67
|
+
for m in cls._ALT_TLD_RE.finditer(valid_url):
|
|
68
|
+
tlds = m.group(1).split("|")
|
|
69
|
+
start = m.start()
|
|
70
|
+
|
|
71
|
+
# Geriye doğru git ve domain'i bul
|
|
72
|
+
before = valid_url[:start]
|
|
73
|
+
|
|
74
|
+
# 1. Named Groups (?P<name> temizle
|
|
75
|
+
before = re.sub(r"\(\?P<[^>]+>", "", before)
|
|
76
|
+
|
|
77
|
+
# 2. Simple Non-Capturing Groups (?:xxx)? temizle (sadece alphanumeric ve escape)
|
|
78
|
+
before = re.sub(r"\(\?:[a-z0-9-]+\)\??", "", before)
|
|
79
|
+
|
|
80
|
+
# Son domain-like kelimeyi al
|
|
81
|
+
words = cls._DOMAIN_WORD_RE.findall(before)
|
|
82
|
+
if not words:
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
base = words[-1].lower()
|
|
86
|
+
for tld in tlds:
|
|
87
|
+
tld = tld.strip().lower()
|
|
88
|
+
if tld and len(tld) <= 6:
|
|
89
|
+
domains.add(f"{base}.{tld}")
|
|
90
|
+
|
|
91
|
+
return domains
|
|
92
|
+
|
|
13
93
|
@classmethod
|
|
14
94
|
def _init_fast_domain_regex(cls):
|
|
15
95
|
"""
|
|
@@ -19,44 +99,31 @@ class YTDLP(ExtractorBase):
|
|
|
19
99
|
return
|
|
20
100
|
|
|
21
101
|
domains = set()
|
|
22
|
-
|
|
23
|
-
# Merkezi cache'den extractorları al
|
|
24
102
|
extractors = get_ytdlp_extractors()
|
|
25
103
|
|
|
26
|
-
# yt-dlp extractor'larının _VALID_URL regex'lerinden domain yakala
|
|
27
|
-
# Regex metinlerinde domainler genelde "\." şeklinde geçer.
|
|
28
|
-
domain_pat = re.compile(r"(?:[a-z0-9-]+\\\.)+[a-z]{2,}", re.IGNORECASE)
|
|
29
|
-
|
|
30
104
|
for ie in extractors:
|
|
31
105
|
valid = getattr(ie, "_VALID_URL", None)
|
|
32
106
|
if not valid or not isinstance(valid, str):
|
|
33
107
|
continue
|
|
34
108
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# Çok agresif/şüpheli şeyleri elemek istersen burada filtre koyabilirsin
|
|
39
|
-
# (genelde gerek kalmıyor)
|
|
40
|
-
domains.add(d)
|
|
109
|
+
domains |= cls._extract_literal_domains(valid)
|
|
110
|
+
domains |= cls._extract_regex_tld_domains(valid)
|
|
111
|
+
domains |= cls._extract_alternation_domains(valid)
|
|
41
112
|
|
|
42
113
|
# Hiç domain çıkmazsa (çok uç durum) fallback: boş regex
|
|
43
114
|
if not domains:
|
|
44
115
|
cls._FAST_DOMAIN_RE = re.compile(r"$^") # hiçbir şeye match etmez
|
|
45
116
|
return
|
|
46
117
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
joined = "|".join(sorted(re.escape(d) for d in domains))
|
|
50
|
-
pattern = rf"(?:^|.*\.)(?:{joined})$"
|
|
51
|
-
cls._FAST_DOMAIN_RE = re.compile(pattern, re.IGNORECASE)
|
|
118
|
+
joined = "|".join(re.escape(d) for d in sorted(domains))
|
|
119
|
+
cls._FAST_DOMAIN_RE = re.compile(rf"(?:^|.*\.)(?:{joined})$", re.IGNORECASE)
|
|
52
120
|
|
|
53
121
|
def __init__(self):
|
|
54
122
|
self.__class__._init_fast_domain_regex()
|
|
55
123
|
|
|
56
124
|
def can_handle_url(self, url: str) -> bool:
|
|
57
125
|
"""
|
|
58
|
-
Fast-path: URL host'unu tek mega-regex ile kontrol et
|
|
59
|
-
Slow-path: gerekirse mevcut extract_info tabanlı kontrolün
|
|
126
|
+
Fast-path: URL host'unu tek mega-regex ile kontrol et
|
|
60
127
|
"""
|
|
61
128
|
# URL parse + host al
|
|
62
129
|
try:
|
|
@@ -77,41 +144,8 @@ class YTDLP(ExtractorBase):
|
|
|
77
144
|
if host and self.__class__._FAST_DOMAIN_RE.search(host):
|
|
78
145
|
return True
|
|
79
146
|
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
# stderr'ı geçici olarak kapat (hata mesajlarını gizle)
|
|
83
|
-
old_stderr = sys.stderr
|
|
84
|
-
sys.stderr = open(os.devnull, "w")
|
|
85
|
-
|
|
86
|
-
try:
|
|
87
|
-
ydl_opts = {
|
|
88
|
-
"simulate" : True, # Download yok, sadece tespit
|
|
89
|
-
"quiet" : True, # Log kirliliği yok
|
|
90
|
-
"no_warnings" : True, # Uyarı mesajları yok
|
|
91
|
-
"extract_flat" : True, # Minimal işlem
|
|
92
|
-
"no_check_certificates" : True,
|
|
93
|
-
"ignoreerrors" : True, # Hataları yoksay
|
|
94
|
-
"socket_timeout" : 3,
|
|
95
|
-
"retries" : 1
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
|
99
|
-
# URL'yi işleyebiliyor mu kontrol et
|
|
100
|
-
info = ydl.extract_info(url, download=False, process=False)
|
|
101
|
-
|
|
102
|
-
# Generic extractor ise atla
|
|
103
|
-
if info and info.get("extractor_key") != "Generic":
|
|
104
|
-
return True
|
|
105
|
-
|
|
106
|
-
return False
|
|
107
|
-
finally:
|
|
108
|
-
# stderr'ı geri yükle
|
|
109
|
-
sys.stderr.close()
|
|
110
|
-
sys.stderr = old_stderr
|
|
111
|
-
|
|
112
|
-
except Exception:
|
|
113
|
-
# yt-dlp işleyemezse False döndür
|
|
114
|
-
return False
|
|
147
|
+
# yt-dlp işleyemezse False döndür
|
|
148
|
+
return False
|
|
115
149
|
|
|
116
150
|
async def extract(self, url: str, referer: str | None = None) -> ExtractResult:
|
|
117
151
|
ydl_opts = {
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
|
|
4
|
+
from parsel import Selector
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
class BelgeselX(PluginBase):
|
|
8
|
+
name = "BelgeselX"
|
|
9
|
+
language = "tr"
|
|
10
|
+
main_url = "https://belgeselx.com"
|
|
11
|
+
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
12
|
+
description = "2022 yılında son çıkan belgeselleri belgeselx.com'da izle. En yeni belgeseller, türkçe altyazılı yada dublaj olarak 1080p kalitesinde hd belgesel izle."
|
|
13
|
+
|
|
14
|
+
main_page = {
|
|
15
|
+
f"{main_url}/konu/turk-tarihi-belgeselleri&page=" : "Türk Tarihi",
|
|
16
|
+
f"{main_url}/konu/tarih-belgeselleri&page=" : "Tarih",
|
|
17
|
+
f"{main_url}/konu/seyehat-belgeselleri&page=" : "Seyahat",
|
|
18
|
+
f"{main_url}/konu/seri-belgeseller&page=" : "Seri",
|
|
19
|
+
f"{main_url}/konu/savas-belgeselleri&page=" : "Savaş",
|
|
20
|
+
f"{main_url}/konu/sanat-belgeselleri&page=" : "Sanat",
|
|
21
|
+
f"{main_url}/konu/psikoloji-belgeselleri&page=" : "Psikoloji",
|
|
22
|
+
f"{main_url}/konu/polisiye-belgeselleri&page=" : "Polisiye",
|
|
23
|
+
f"{main_url}/konu/otomobil-belgeselleri&page=" : "Otomobil",
|
|
24
|
+
f"{main_url}/konu/nazi-belgeselleri&page=" : "Nazi",
|
|
25
|
+
f"{main_url}/konu/muhendislik-belgeselleri&page=" : "Mühendislik",
|
|
26
|
+
f"{main_url}/konu/kultur-din-belgeselleri&page=" : "Kültür Din",
|
|
27
|
+
f"{main_url}/konu/kozmik-belgeseller&page=" : "Kozmik",
|
|
28
|
+
f"{main_url}/konu/hayvan-belgeselleri&page=" : "Hayvan",
|
|
29
|
+
f"{main_url}/konu/eski-tarih-belgeselleri&page=" : "Eski Tarih",
|
|
30
|
+
f"{main_url}/konu/egitim-belgeselleri&page=" : "Eğitim",
|
|
31
|
+
f"{main_url}/konu/dunya-belgeselleri&page=" : "Dünya",
|
|
32
|
+
f"{main_url}/konu/doga-belgeselleri&page=" : "Doğa",
|
|
33
|
+
f"{main_url}/konu/bilim-belgeselleri&page=" : "Bilim"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def _to_title_case(text: str) -> str:
|
|
38
|
+
"""Türkçe için title case dönüşümü."""
|
|
39
|
+
return " ".join(
|
|
40
|
+
word.lower().replace("i", "İ").capitalize() if word.lower().startswith("i") else word.capitalize()
|
|
41
|
+
for word in text.split()
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
45
|
+
istek = self.cloudscraper.get(f"{url}{page}")
|
|
46
|
+
secici = Selector(istek.text)
|
|
47
|
+
|
|
48
|
+
results = []
|
|
49
|
+
for item in secici.css("div.gen-movie-contain > div.gen-info-contain > div.gen-movie-info"):
|
|
50
|
+
title = item.css("div.gen-movie-info > h3 a::text").get()
|
|
51
|
+
href = item.css("div.gen-movie-info > h3 a::attr(href)").get()
|
|
52
|
+
parent = item.xpath("../../..")
|
|
53
|
+
poster = parent.css("div.gen-movie-img > img::attr(src)").get()
|
|
54
|
+
|
|
55
|
+
if title and href:
|
|
56
|
+
results.append(MainPageResult(
|
|
57
|
+
category = category,
|
|
58
|
+
title = self._to_title_case(title.strip()),
|
|
59
|
+
url = self.fix_url(href),
|
|
60
|
+
poster = self.fix_url(poster) if poster else None
|
|
61
|
+
))
|
|
62
|
+
|
|
63
|
+
return results
|
|
64
|
+
|
|
65
|
+
async def search(self, query: str) -> list[SearchResult]:
|
|
66
|
+
# Google Custom Search API kullanıyor
|
|
67
|
+
cx = "016376594590146270301:iwmy65ijgrm"
|
|
68
|
+
|
|
69
|
+
token_resp = self.cloudscraper.get(f"https://cse.google.com/cse.js?cx={cx}")
|
|
70
|
+
token_text = token_resp.text
|
|
71
|
+
|
|
72
|
+
cse_lib_match = re.search(r'cselibVersion": "(.*)"', token_text)
|
|
73
|
+
cse_tok_match = re.search(r'cse_token": "(.*)"', token_text)
|
|
74
|
+
|
|
75
|
+
if not cse_lib_match or not cse_tok_match:
|
|
76
|
+
return []
|
|
77
|
+
|
|
78
|
+
cse_lib = cse_lib_match.group(1)
|
|
79
|
+
cse_tok = cse_tok_match.group(1)
|
|
80
|
+
|
|
81
|
+
search_url = (
|
|
82
|
+
f"https://cse.google.com/cse/element/v1?"
|
|
83
|
+
f"rsz=filtered_cse&num=100&hl=tr&source=gcsc&cselibv={cse_lib}&cx={cx}"
|
|
84
|
+
f"&q={query}&safe=off&cse_tok={cse_tok}&sort=&exp=cc%2Capo&oq={query}"
|
|
85
|
+
f"&callback=google.search.cse.api9969&rurl=https%3A%2F%2Fbelgeselx.com%2F"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
resp = self.cloudscraper.get(search_url)
|
|
89
|
+
resp_text = resp.text
|
|
90
|
+
|
|
91
|
+
titles = re.findall(r'"titleNoFormatting": "(.*?)"', resp_text)
|
|
92
|
+
urls = re.findall(r'"url": "(.*?)"', resp_text)
|
|
93
|
+
images = re.findall(r'"ogImage": "(.*?)"', resp_text)
|
|
94
|
+
|
|
95
|
+
results = []
|
|
96
|
+
for i, title in enumerate(titles):
|
|
97
|
+
url_val = urls[i] if i < len(urls) else None
|
|
98
|
+
poster = images[i] if i < len(images) else None
|
|
99
|
+
|
|
100
|
+
if not url_val or "diziresimleri" not in url_val:
|
|
101
|
+
# URL'den belgesel linkini oluştur
|
|
102
|
+
if poster and "diziresimleri" in poster:
|
|
103
|
+
file_name = poster.rsplit("/", 1)[-1]
|
|
104
|
+
file_name = re.sub(r"\.(jpe?g|png|webp)$", "", file_name)
|
|
105
|
+
url_val = f"{self.main_url}/belgeseldizi/{file_name}"
|
|
106
|
+
else:
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
clean_title = title.split("İzle")[0].strip()
|
|
110
|
+
results.append(SearchResult(
|
|
111
|
+
title = self._to_title_case(clean_title),
|
|
112
|
+
url = url_val,
|
|
113
|
+
poster = poster
|
|
114
|
+
))
|
|
115
|
+
|
|
116
|
+
return results
|
|
117
|
+
|
|
118
|
+
async def load_item(self, url: str) -> SeriesInfo:
|
|
119
|
+
istek = await self.httpx.get(url)
|
|
120
|
+
secici = Selector(istek.text)
|
|
121
|
+
|
|
122
|
+
title = secici.css("h2.gen-title::text").get()
|
|
123
|
+
poster = secici.css("div.gen-tv-show-top img::attr(src)").get()
|
|
124
|
+
description = secici.css("div.gen-single-tv-show-info p::text").get()
|
|
125
|
+
|
|
126
|
+
tags = []
|
|
127
|
+
for tag_link in secici.css("div.gen-socail-share a[href*='belgeselkanali']"):
|
|
128
|
+
tag_href = tag_link.css("::attr(href)").get()
|
|
129
|
+
if tag_href:
|
|
130
|
+
tag_name = tag_href.rsplit("/", 1)[-1].replace("-", " ")
|
|
131
|
+
tags.append(self._to_title_case(tag_name))
|
|
132
|
+
|
|
133
|
+
episodes = []
|
|
134
|
+
counter = 0
|
|
135
|
+
for ep_item in secici.css("div.gen-movie-contain"):
|
|
136
|
+
ep_name = ep_item.css("div.gen-movie-info h3 a::text").get()
|
|
137
|
+
ep_href = ep_item.css("div.gen-movie-info h3 a::attr(href)").get()
|
|
138
|
+
|
|
139
|
+
if not ep_name or not ep_href:
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
season_text = ep_item.css("div.gen-single-meta-holder ul li::text").get() or ""
|
|
143
|
+
episode_match = re.search(r"Bölüm (\d+)", season_text)
|
|
144
|
+
season_match = re.search(r"Sezon (\d+)", season_text)
|
|
145
|
+
|
|
146
|
+
ep_episode = int(episode_match.group(1)) if episode_match else counter
|
|
147
|
+
ep_season = int(season_match.group(1)) if season_match else 1
|
|
148
|
+
|
|
149
|
+
counter += 1
|
|
150
|
+
|
|
151
|
+
episodes.append(Episode(
|
|
152
|
+
season = ep_season,
|
|
153
|
+
episode = ep_episode,
|
|
154
|
+
title = ep_name.strip(),
|
|
155
|
+
url = self.fix_url(ep_href)
|
|
156
|
+
))
|
|
157
|
+
|
|
158
|
+
return SeriesInfo(
|
|
159
|
+
url = url,
|
|
160
|
+
poster = self.fix_url(poster) if poster else None,
|
|
161
|
+
title = self._to_title_case(title.strip()) if title else None,
|
|
162
|
+
description = description.strip() if description else None,
|
|
163
|
+
tags = tags,
|
|
164
|
+
episodes = episodes
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
168
|
+
istek = await self.httpx.get(url)
|
|
169
|
+
text = istek.text
|
|
170
|
+
|
|
171
|
+
# fnc_addWatch div'inden data-episode ID'sini al
|
|
172
|
+
ep_match = re.search(r'<div[^>]*class=["\'][^"\']*fnc_addWatch[^"\']*["\'][^>]*data-episode=["\'](\d+)["\']', text)
|
|
173
|
+
if not ep_match:
|
|
174
|
+
return []
|
|
175
|
+
|
|
176
|
+
episode_id = ep_match.group(1)
|
|
177
|
+
iframe_url = f"{self.main_url}/video/data/new4.php?id={episode_id}"
|
|
178
|
+
|
|
179
|
+
iframe_resp = await self.httpx.get(iframe_url, headers={"Referer": url})
|
|
180
|
+
iframe_text = iframe_resp.text
|
|
181
|
+
|
|
182
|
+
links = []
|
|
183
|
+
for match in re.finditer(r'file:"([^"]+)", label: "([^"]+)"', iframe_text):
|
|
184
|
+
video_url = match.group(1)
|
|
185
|
+
quality = match.group(2)
|
|
186
|
+
|
|
187
|
+
source_name = "Google" if quality == "FULL" else self.name
|
|
188
|
+
quality_str = "1080p" if quality == "FULL" else quality
|
|
189
|
+
|
|
190
|
+
links.append(ExtractResult(
|
|
191
|
+
url = video_url,
|
|
192
|
+
name = f"{source_name} | {quality_str}",
|
|
193
|
+
referer = url
|
|
194
|
+
))
|
|
195
|
+
|
|
196
|
+
return links
|
KekikStream/Plugins/DiziBox.py
CHANGED
|
@@ -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
|
KekikStream/Plugins/DiziPal.py
CHANGED
|
@@ -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)
|
KekikStream/Plugins/DiziYou.py
CHANGED
|
@@ -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
|