KekikStream 1.5.6__py3-none-any.whl → 1.5.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- KekikStream/CLI/pypi_kontrol.py +6 -6
- KekikStream/Core/Media/MediaHandler.py +9 -6
- KekikStream/Core/Plugin/PluginBase.py +30 -8
- KekikStream/Core/Plugin/PluginLoader.py +2 -2
- KekikStream/Extractors/CloseLoad.py +4 -23
- KekikStream/Extractors/VidMoly.py +5 -16
- KekikStream/Plugins/DiziBox.py +28 -13
- KekikStream/Plugins/DiziYou.py +15 -16
- KekikStream/Plugins/Dizilla.py +55 -41
- KekikStream/Plugins/FilmMakinesi.py +34 -27
- KekikStream/Plugins/FullHDFilmizlesene.py +16 -10
- KekikStream/Plugins/HDFilmCehennemi.py +23 -25
- KekikStream/Plugins/JetFilmizle.py +15 -7
- KekikStream/Plugins/RecTV.py +14 -17
- KekikStream/Plugins/SezonlukDizi.py +15 -12
- KekikStream/Plugins/Shorten.py +9 -5
- KekikStream/Plugins/UgurFilm.py +12 -8
- KekikStream/__init__.py +285 -318
- {kekikstream-1.5.6.dist-info → kekikstream-1.5.7.dist-info}/METADATA +3 -2
- {kekikstream-1.5.6.dist-info → kekikstream-1.5.7.dist-info}/RECORD +24 -25
- {kekikstream-1.5.6.dist-info → kekikstream-1.5.7.dist-info}/WHEEL +1 -1
- KekikStream/Plugins/SineWix.py +0 -161
- {kekikstream-1.5.6.dist-info → kekikstream-1.5.7.dist-info}/entry_points.txt +0 -0
- {kekikstream-1.5.6.dist-info → kekikstream-1.5.7.dist-info/licenses}/LICENSE +0 -0
- {kekikstream-1.5.6.dist-info → kekikstream-1.5.7.dist-info}/top_level.txt +0 -0
KekikStream/CLI/pypi_kontrol.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from .
|
|
4
|
-
from rich.panel
|
|
5
|
-
from
|
|
6
|
-
from requests
|
|
7
|
-
from subprocess
|
|
3
|
+
from . import konsol
|
|
4
|
+
from rich.panel import Panel
|
|
5
|
+
from importlib import metadata
|
|
6
|
+
from requests import get
|
|
7
|
+
from subprocess import check_call
|
|
8
8
|
import sys
|
|
9
9
|
|
|
10
10
|
def pypi_kontrol_guncelle(paket_adi: str):
|
|
11
11
|
try:
|
|
12
12
|
konsol.print(f"[bold cyan] {paket_adi} Güncellemesi kontrol ediliyor...[/bold cyan]")
|
|
13
|
-
mevcut_surum =
|
|
13
|
+
mevcut_surum = metadata.version(paket_adi)
|
|
14
14
|
konsol.print(Panel(f"[cyan]Yüklü sürüm:[/cyan] [bold yellow]{mevcut_surum}[/bold yellow]"))
|
|
15
15
|
|
|
16
16
|
istek = get(f"https://pypi.org/pypi/{paket_adi}/json")
|
|
@@ -34,11 +34,11 @@ class MediaHandler:
|
|
|
34
34
|
if "Cookie" in self.headers or extract_data.subtitles:
|
|
35
35
|
return self.play_with_mpv(extract_data)
|
|
36
36
|
|
|
37
|
-
return self.play_with_vlc(extract_data)
|
|
37
|
+
return self.play_with_vlc(extract_data) or self.play_with_mpv(extract_data)
|
|
38
38
|
|
|
39
39
|
def play_with_vlc(self, extract_data: ExtractResult):
|
|
40
40
|
konsol.log(f"[yellow][»] VLC ile Oynatılıyor : {extract_data.url}")
|
|
41
|
-
konsol.print(self.headers)
|
|
41
|
+
# konsol.print(self.headers)
|
|
42
42
|
try:
|
|
43
43
|
vlc_command = ["vlc", "--quiet"]
|
|
44
44
|
|
|
@@ -62,16 +62,19 @@ class MediaHandler:
|
|
|
62
62
|
with open(os.devnull, "w") as devnull:
|
|
63
63
|
subprocess.run(vlc_command, stdout=devnull, stderr=devnull, check=True)
|
|
64
64
|
|
|
65
|
+
return True
|
|
65
66
|
except subprocess.CalledProcessError as hata:
|
|
66
67
|
konsol.print(f"[red]VLC oynatma hatası: {hata}[/red]")
|
|
67
68
|
konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
|
|
69
|
+
return False
|
|
68
70
|
except FileNotFoundError:
|
|
69
71
|
konsol.print("[red]VLC bulunamadı! VLC kurulu olduğundan emin olun.[/red]")
|
|
70
|
-
konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
|
|
72
|
+
# konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
|
|
73
|
+
return False
|
|
71
74
|
|
|
72
75
|
def play_with_mpv(self, extract_data: ExtractResult):
|
|
73
76
|
konsol.log(f"[yellow][»] MPV ile Oynatılıyor : {extract_data.url}")
|
|
74
|
-
konsol.print(self.headers)
|
|
77
|
+
# konsol.print(self.headers)
|
|
75
78
|
try:
|
|
76
79
|
mpv_command = ["mpv"]
|
|
77
80
|
|
|
@@ -98,7 +101,7 @@ class MediaHandler:
|
|
|
98
101
|
|
|
99
102
|
def play_with_ytdlp(self, extract_data: ExtractResult):
|
|
100
103
|
konsol.log(f"[yellow][»] yt-dlp ile Oynatılıyor : {extract_data.url}")
|
|
101
|
-
konsol.print(self.headers)
|
|
104
|
+
# konsol.print(self.headers)
|
|
102
105
|
try:
|
|
103
106
|
ytdlp_command = ["yt-dlp", "--quiet", "--no-warnings"]
|
|
104
107
|
|
|
@@ -131,7 +134,7 @@ class MediaHandler:
|
|
|
131
134
|
|
|
132
135
|
def play_with_android_mxplayer(self, extract_data: ExtractResult):
|
|
133
136
|
konsol.log(f"[yellow][»] MxPlayer ile Oynatılıyor : {extract_data.url}")
|
|
134
|
-
konsol.print(self.headers)
|
|
137
|
+
# konsol.print(self.headers)
|
|
135
138
|
paketler = [
|
|
136
139
|
"com.mxtech.videoplayer.ad/.ActivityScreen", # Free sürüm
|
|
137
140
|
"com.mxtech.videoplayer.pro/.ActivityScreen" # Pro sürüm
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from abc
|
|
4
|
-
from httpx
|
|
5
|
-
from cloudscraper
|
|
6
|
-
from .PluginModels
|
|
7
|
-
from ..Media.MediaHandler
|
|
8
|
-
from
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from httpx import AsyncClient, Timeout
|
|
5
|
+
from cloudscraper import CloudScraper
|
|
6
|
+
from .PluginModels import MainPageResult, SearchResult, MovieInfo
|
|
7
|
+
from ..Media.MediaHandler import MediaHandler
|
|
8
|
+
from ..Extractor.ExtractorManager import ExtractorManager
|
|
9
|
+
from urllib.parse import urljoin
|
|
9
10
|
import re
|
|
10
11
|
|
|
11
12
|
class PluginBase(ABC):
|
|
@@ -34,6 +35,7 @@ class PluginBase(ABC):
|
|
|
34
35
|
)
|
|
35
36
|
self.media_handler = MediaHandler()
|
|
36
37
|
self.cloudscraper = CloudScraper()
|
|
38
|
+
self.ex_manager = ExtractorManager()
|
|
37
39
|
self.httpx.headers.update(self.cloudscraper.headers)
|
|
38
40
|
self.httpx.cookies.update(self.cloudscraper.cookies)
|
|
39
41
|
|
|
@@ -53,8 +55,28 @@ class PluginBase(ABC):
|
|
|
53
55
|
pass
|
|
54
56
|
|
|
55
57
|
@abstractmethod
|
|
56
|
-
async def load_links(self, url: str) -> list[
|
|
57
|
-
"""
|
|
58
|
+
async def load_links(self, url: str) -> list[dict]:
|
|
59
|
+
"""
|
|
60
|
+
Bir medya öğesi için oynatma bağlantılarını döndürür.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
url: Medya URL'si
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Dictionary listesi, her biri şu alanları içerir:
|
|
67
|
+
- url (str, zorunlu): Video URL'si
|
|
68
|
+
- name (str, zorunlu): Gösterim adı (tüm bilgileri içerir)
|
|
69
|
+
- referer (str, opsiyonel): Referer header
|
|
70
|
+
- subtitles (list, opsiyonel): Altyazı listesi
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
[
|
|
74
|
+
{
|
|
75
|
+
"url": "https://example.com/video.m3u8",
|
|
76
|
+
"name": "HDFilmCehennemi | 1080p TR Dublaj"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
"""
|
|
58
80
|
pass
|
|
59
81
|
|
|
60
82
|
async def close(self):
|
|
@@ -20,9 +20,9 @@ class PluginLoader:
|
|
|
20
20
|
plugins = {}
|
|
21
21
|
|
|
22
22
|
# Global eklentileri yükle
|
|
23
|
-
|
|
23
|
+
if self.global_plugins_dir.exists():
|
|
24
24
|
# konsol.log(f"[green][*] Global Eklenti dizininden yükleniyor: {self.global_plugins_dir}[/green]")
|
|
25
|
-
|
|
25
|
+
plugins |= self._load_from_directory(self.global_plugins_dir)
|
|
26
26
|
|
|
27
27
|
# Yerel eklentileri yükle
|
|
28
28
|
if self.local_plugins_dir.exists():
|
|
@@ -1,31 +1,12 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
-
from Kekik.Sifreleme import Packer
|
|
5
|
-
import re
|
|
6
|
-
|
|
7
|
-
def get_m3u_link(data: str) -> str:
|
|
8
|
-
first = base64.b64decode(data)
|
|
9
|
-
first_reversed = first[::-1]
|
|
10
|
-
|
|
11
|
-
second = base64.b64decode(first_reversed)
|
|
12
|
-
|
|
13
|
-
parts = second.decode('utf-8').split("|")
|
|
14
|
-
if len(parts) < 2:
|
|
15
|
-
raise ValueError("Decoded data has an unexpected format.")
|
|
16
|
-
|
|
17
|
-
return parts[1]
|
|
18
|
-
|
|
19
|
-
def extract_data(raw_script: str) -> str:
|
|
20
|
-
pattern = re.compile(r'return result\}var .*?=.*?\("(.*?)"\)')
|
|
21
|
-
if match := pattern.search(raw_script):
|
|
22
|
-
return match[1]
|
|
23
|
-
else:
|
|
24
|
-
raise Exception("data not found")
|
|
4
|
+
from Kekik.Sifreleme import Packer, StreamDecoder
|
|
5
|
+
import re
|
|
25
6
|
|
|
26
7
|
class CloseLoadExtractor(ExtractorBase):
|
|
27
8
|
name = "CloseLoad"
|
|
28
|
-
main_url = "https://closeload.filmmakinesi.
|
|
9
|
+
main_url = "https://closeload.filmmakinesi.sh"
|
|
29
10
|
|
|
30
11
|
async def extract(self, url, referer=None) -> ExtractResult:
|
|
31
12
|
if referer:
|
|
@@ -35,7 +16,7 @@ class CloseLoadExtractor(ExtractorBase):
|
|
|
35
16
|
istek.raise_for_status()
|
|
36
17
|
|
|
37
18
|
eval_func = re.compile(r'\s*(eval\(function[\s\S].*)\s*').findall(istek.text)[0]
|
|
38
|
-
m3u_link =
|
|
19
|
+
m3u_link = StreamDecoder.extract_stream_url(Packer.unpack(eval_func))
|
|
39
20
|
|
|
40
21
|
await self.close()
|
|
41
22
|
return ExtractResult(
|
|
@@ -18,24 +18,13 @@ class VidMoly(ExtractorBase):
|
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
if self.main_url.endswith(".me"):
|
|
21
|
-
self.main_url = self.main_url.replace(".me", ".
|
|
22
|
-
url
|
|
21
|
+
self.main_url = self.main_url.replace(".me", ".net")
|
|
22
|
+
url = url.replace(".me", ".net")
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
embed_url = url.replace("/w/", "/embed-") + "-920x360.html" if "/w/" in url else url
|
|
26
|
-
script_content = None
|
|
27
|
-
attempts = 0
|
|
24
|
+
response = await self.httpx.get(url)
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
attempts += 1
|
|
32
|
-
response = await self.httpx.get(embed_url)
|
|
33
|
-
response.raise_for_status()
|
|
34
|
-
|
|
35
|
-
script_match = re.search(r"sources:\s*\[(.*?)\],", response.text, re.DOTALL)
|
|
36
|
-
script_content = script_match[1] if script_match else None
|
|
37
|
-
if not script_content:
|
|
38
|
-
await asyncio.sleep(0.5)
|
|
26
|
+
script_match = re.search(r"sources:\s*\[(.*?)\],", response.text, re.DOTALL)
|
|
27
|
+
script_content = script_match[1] if script_match else None
|
|
39
28
|
|
|
40
29
|
if not script_content:
|
|
41
30
|
raise ValueError("Gerekli script bulunamadı.")
|
KekikStream/Plugins/DiziBox.py
CHANGED
|
@@ -40,7 +40,7 @@ class DiziBox(PluginBase):
|
|
|
40
40
|
f"{main_url}/dizi-arsivi/page/SAYFA/?tur[0]=yarisma&yil&imdb" : "Yarışma"
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
#@kekik_cache(ttl=60*60)
|
|
44
44
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
45
45
|
istek = await self.httpx.get(
|
|
46
46
|
url = f"{url.replace('SAYFA', str(page))}",
|
|
@@ -58,7 +58,7 @@ class DiziBox(PluginBase):
|
|
|
58
58
|
for veri in secici.css("article.detailed-article")
|
|
59
59
|
]
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
#@kekik_cache(ttl=60*60)
|
|
62
62
|
async def search(self, query: str) -> list[SearchResult]:
|
|
63
63
|
self.httpx.cookies.update({
|
|
64
64
|
"LockUser" : "true",
|
|
@@ -77,7 +77,7 @@ class DiziBox(PluginBase):
|
|
|
77
77
|
for item in secici.css("article.detailed-article")
|
|
78
78
|
]
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
#@kekik_cache(ttl=60*60)
|
|
81
81
|
async def load_item(self, url: str) -> SeriesInfo:
|
|
82
82
|
istek = await self.httpx.get(url)
|
|
83
83
|
secici = Selector(istek.text)
|
|
@@ -124,13 +124,19 @@ class DiziBox(PluginBase):
|
|
|
124
124
|
actors = actors,
|
|
125
125
|
)
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
#@kekik_cache(ttl=60*60)
|
|
128
128
|
async def _iframe_decode(self, name:str, iframe_link:str, referer:str) -> list[str]:
|
|
129
129
|
results = []
|
|
130
130
|
|
|
131
|
+
self.httpx.headers.update({"Referer": referer})
|
|
132
|
+
self.httpx.cookies.update({
|
|
133
|
+
"LockUser" : "true",
|
|
134
|
+
"isTrustedUser" : "true",
|
|
135
|
+
"dbxu" : "1722403730363"
|
|
136
|
+
})
|
|
137
|
+
|
|
131
138
|
if "/player/king/king.php" in iframe_link:
|
|
132
139
|
iframe_link = iframe_link.replace("king.php?v=", "king.php?wmode=opaque&v=")
|
|
133
|
-
self.httpx.headers.update({"Referer": referer})
|
|
134
140
|
|
|
135
141
|
istek = await self.httpx.get(iframe_link)
|
|
136
142
|
secici = Selector(istek.text)
|
|
@@ -150,7 +156,6 @@ class DiziBox(PluginBase):
|
|
|
150
156
|
|
|
151
157
|
elif "/player/moly/moly.php" in iframe_link:
|
|
152
158
|
iframe_link = iframe_link.replace("moly.php?h=", "moly.php?wmode=opaque&h=")
|
|
153
|
-
self.httpx.headers.update({"Referer": referer})
|
|
154
159
|
while True:
|
|
155
160
|
await asyncio.sleep(.3)
|
|
156
161
|
with contextlib.suppress(Exception):
|
|
@@ -171,15 +176,20 @@ class DiziBox(PluginBase):
|
|
|
171
176
|
|
|
172
177
|
return results
|
|
173
178
|
|
|
174
|
-
|
|
175
|
-
async def load_links(self, url: str) -> list[
|
|
179
|
+
#@kekik_cache(ttl=15*60)
|
|
180
|
+
async def load_links(self, url: str) -> list[dict]:
|
|
176
181
|
istek = await self.httpx.get(url)
|
|
177
182
|
secici = Selector(istek.text)
|
|
178
183
|
|
|
179
|
-
|
|
184
|
+
results = []
|
|
180
185
|
if main_iframe := secici.css("div#video-area iframe::attr(src)").get():
|
|
181
186
|
if decoded := await self._iframe_decode(self.name, main_iframe, url):
|
|
182
|
-
|
|
187
|
+
for iframe in decoded:
|
|
188
|
+
extractor = self.ex_manager.find_extractor(iframe)
|
|
189
|
+
results.append({
|
|
190
|
+
"url" : iframe,
|
|
191
|
+
"name" : f"{extractor.name if extractor else 'Main Player'}"
|
|
192
|
+
})
|
|
183
193
|
|
|
184
194
|
for alternatif in secici.css("div.video-toolbar option[value]"):
|
|
185
195
|
alt_name = alternatif.css("::text").get()
|
|
@@ -195,6 +205,11 @@ class DiziBox(PluginBase):
|
|
|
195
205
|
alt_secici = Selector(alt_istek.text)
|
|
196
206
|
if alt_iframe := alt_secici.css("div#video-area iframe::attr(src)").get():
|
|
197
207
|
if decoded := await self._iframe_decode(alt_name, alt_iframe, url):
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
208
|
+
for iframe in decoded:
|
|
209
|
+
extractor = self.ex_manager.find_extractor(iframe)
|
|
210
|
+
results.append({
|
|
211
|
+
"url" : iframe,
|
|
212
|
+
"name" : f"{extractor.name if extractor else alt_name}"
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
return results
|
KekikStream/Plugins/DiziYou.py
CHANGED
|
@@ -7,7 +7,7 @@ import re
|
|
|
7
7
|
class DiziYou(PluginBase):
|
|
8
8
|
name = "DiziYou"
|
|
9
9
|
language = "tr"
|
|
10
|
-
main_url = "https://www.
|
|
10
|
+
main_url = "https://www.diziyou.mx"
|
|
11
11
|
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
12
12
|
description = "Diziyou en kaliteli Türkçe dublaj ve altyazılı yabancı dizi izleme sitesidir. Güncel ve efsanevi dizileri 1080p Full HD kalitede izlemek için hemen tıkla!"
|
|
13
13
|
|
|
@@ -31,7 +31,7 @@ class DiziYou(PluginBase):
|
|
|
31
31
|
|
|
32
32
|
_data = {}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
#@kekik_cache(ttl=60*60)
|
|
35
35
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
36
36
|
istek = await self.httpx.get(f"{url.replace('SAYFA', str(page))}")
|
|
37
37
|
secici = Selector(istek.text)
|
|
@@ -46,7 +46,7 @@ class DiziYou(PluginBase):
|
|
|
46
46
|
for veri in secici.css("div.single-item")
|
|
47
47
|
]
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
#@kekik_cache(ttl=60*60)
|
|
50
50
|
async def search(self, query: str) -> list[SearchResult]:
|
|
51
51
|
istek = await self.httpx.get(f"{self.main_url}/?s={query}")
|
|
52
52
|
secici = Selector(istek.text)
|
|
@@ -60,7 +60,7 @@ class DiziYou(PluginBase):
|
|
|
60
60
|
for afis in secici.css("div.incontent div#list-series")
|
|
61
61
|
]
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
#@kekik_cache(ttl=60*60)
|
|
64
64
|
async def load_item(self, url: str) -> SeriesInfo:
|
|
65
65
|
istek = await self.httpx.get(url)
|
|
66
66
|
secici = Selector(istek.text)
|
|
@@ -107,8 +107,8 @@ class DiziYou(PluginBase):
|
|
|
107
107
|
actors = actors
|
|
108
108
|
)
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
async def load_links(self, url: str) -> list[
|
|
110
|
+
#@kekik_cache(ttl=15*60)
|
|
111
|
+
async def load_links(self, url: str) -> list[dict]:
|
|
112
112
|
istek = await self.httpx.get(url)
|
|
113
113
|
secici = Selector(istek.text)
|
|
114
114
|
|
|
@@ -130,7 +130,7 @@ class DiziYou(PluginBase):
|
|
|
130
130
|
url = self.fix_url(f"{self.main_url.replace('www', 'storage')}/subtitles/{item_id}/tr.vtt"),
|
|
131
131
|
))
|
|
132
132
|
veri = {
|
|
133
|
-
"dil": "Orjinal Dil",
|
|
133
|
+
"dil": "Orjinal Dil (TR Altyazı)",
|
|
134
134
|
"url": f"{self.main_url.replace('www', 'storage')}/episodes/{item_id}/play.m3u8"
|
|
135
135
|
}
|
|
136
136
|
if veri not in stream_urls:
|
|
@@ -141,28 +141,27 @@ class DiziYou(PluginBase):
|
|
|
141
141
|
url = self.fix_url(f"{self.main_url.replace('www', 'storage')}/subtitles/{item_id}/en.vtt"),
|
|
142
142
|
))
|
|
143
143
|
veri = {
|
|
144
|
-
"dil": "Orjinal Dil",
|
|
144
|
+
"dil": "Orjinal Dil (EN Altyazı)",
|
|
145
145
|
"url": f"{self.main_url.replace('www', 'storage')}/episodes/{item_id}/play.m3u8"
|
|
146
146
|
}
|
|
147
147
|
if veri not in stream_urls:
|
|
148
148
|
stream_urls.append(veri)
|
|
149
149
|
case "turkceDublaj":
|
|
150
150
|
stream_urls.append({
|
|
151
|
-
"dil": "Dublaj",
|
|
151
|
+
"dil": "Türkçe Dublaj",
|
|
152
152
|
"url": f"{self.main_url.replace('www', 'storage')}/episodes/{item_id}_tr/play.m3u8"
|
|
153
153
|
})
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
results = []
|
|
156
156
|
for stream in stream_urls:
|
|
157
|
-
|
|
158
|
-
"
|
|
159
|
-
"name" : f"{
|
|
157
|
+
results.append({
|
|
158
|
+
"url" : stream.get("url"),
|
|
159
|
+
"name" : f"{stream.get('dil')}",
|
|
160
160
|
"referer" : url,
|
|
161
|
-
"headers" : self.media_handler.headers,
|
|
162
161
|
"subtitles" : subtitles
|
|
163
|
-
}
|
|
162
|
+
})
|
|
164
163
|
|
|
165
|
-
return
|
|
164
|
+
return results
|
|
166
165
|
|
|
167
166
|
async def play(self, name: str, url: str, referer: str, subtitles: list[Subtitle]):
|
|
168
167
|
extract_result = ExtractResult(name=name, url=url, referer=referer, subtitles=subtitles)
|
KekikStream/Plugins/Dizilla.py
CHANGED
|
@@ -4,11 +4,13 @@ from KekikStream.Core import kekik_cache, PluginBase, MainPageResult, SearchResu
|
|
|
4
4
|
from parsel import Selector
|
|
5
5
|
from json import loads
|
|
6
6
|
from urllib.parse import urlparse, urlunparse
|
|
7
|
+
from Crypto.Cipher import AES
|
|
8
|
+
from base64 import b64decode
|
|
7
9
|
|
|
8
10
|
class Dizilla(PluginBase):
|
|
9
11
|
name = "Dizilla"
|
|
10
12
|
language = "tr"
|
|
11
|
-
main_url = "https://
|
|
13
|
+
main_url = "https://dizilla40.com"
|
|
12
14
|
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
13
15
|
description = "Dizilla tüm yabancı dizileri ücretsiz olarak Türkçe Dublaj ve altyazılı seçenekleri ile 1080P kalite izleyebileceğiniz yeni nesil yabancı dizi izleme siteniz."
|
|
14
16
|
|
|
@@ -22,7 +24,7 @@ class Dizilla(PluginBase):
|
|
|
22
24
|
f"{main_url}/dizi-turu/komedi" : "Komedi"
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
#@kekik_cache(ttl=60*60)
|
|
26
28
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
27
29
|
istek = await self.httpx.get(url)
|
|
28
30
|
secici = Selector(istek.text)
|
|
@@ -65,32 +67,32 @@ class Dizilla(PluginBase):
|
|
|
65
67
|
|
|
66
68
|
return ana_sayfa
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
async def decrypt_response(self, response: str) -> dict:
|
|
71
|
+
# 32 bytes key
|
|
72
|
+
key = "9bYMCNQiWsXIYFWYAu7EkdsSbmGBTyUI".encode("utf-8")
|
|
73
|
+
|
|
74
|
+
# IV = 16 bytes of zero
|
|
75
|
+
iv = bytes([0] * 16)
|
|
76
|
+
|
|
77
|
+
# Base64 decode
|
|
78
|
+
encrypted_bytes = b64decode(response)
|
|
79
|
+
|
|
80
|
+
# AES/CBC/PKCS5Padding
|
|
81
|
+
cipher = AES.new(key, AES.MODE_CBC, iv)
|
|
82
|
+
decrypted = cipher.decrypt(encrypted_bytes)
|
|
83
|
+
|
|
84
|
+
# PKCS5/PKCS7 padding remove
|
|
85
|
+
pad_len = decrypted[-1]
|
|
86
|
+
decrypted = decrypted[:-pad_len]
|
|
87
|
+
|
|
88
|
+
# JSON decode
|
|
89
|
+
return loads(decrypted.decode("utf-8"))
|
|
90
|
+
|
|
91
|
+
#@kekik_cache(ttl=60*60)
|
|
69
92
|
async def search(self, query: str) -> list[SearchResult]:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
cValue = ilk_secici.css("input[name='cValue']::attr(value)").get()
|
|
74
|
-
|
|
75
|
-
self.httpx.headers.update({
|
|
76
|
-
"Accept" : "application/json, text/javascript, */*; q=0.01",
|
|
77
|
-
"X-Requested-With" : "XMLHttpRequest",
|
|
78
|
-
"Referer" : f"{self.main_url}/"
|
|
79
|
-
})
|
|
80
|
-
self.httpx.cookies.update({
|
|
81
|
-
"showAllDaFull" : "true",
|
|
82
|
-
"PHPSESSID" : ilk_istek.cookies.get("PHPSESSID"),
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
arama_istek = await self.httpx.post(
|
|
86
|
-
url = f"{self.main_url}/bg/searchcontent",
|
|
87
|
-
data = {
|
|
88
|
-
"cKey" : cKey,
|
|
89
|
-
"cValue" : cValue,
|
|
90
|
-
"searchterm" : query
|
|
91
|
-
}
|
|
92
|
-
)
|
|
93
|
-
arama_veri = arama_istek.json().get("data", {}).get("result", [])
|
|
93
|
+
arama_istek = await self.httpx.post(f"{self.main_url}/api/bg/searchcontent?searchterm={query}")
|
|
94
|
+
decrypted = await self.decrypt_response(arama_istek.json().get("response"))
|
|
95
|
+
arama_veri = decrypted.get("result", [])
|
|
94
96
|
|
|
95
97
|
return [
|
|
96
98
|
SearchResult(
|
|
@@ -101,7 +103,7 @@ class Dizilla(PluginBase):
|
|
|
101
103
|
for veri in arama_veri
|
|
102
104
|
]
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
#@kekik_cache(ttl=60*60)
|
|
105
107
|
async def url_base_degis(self, eski_url:str, yeni_base:str) -> str:
|
|
106
108
|
parsed_url = urlparse(eski_url)
|
|
107
109
|
parsed_yeni_base = urlparse(yeni_base)
|
|
@@ -112,7 +114,7 @@ class Dizilla(PluginBase):
|
|
|
112
114
|
|
|
113
115
|
return urlunparse(yeni_url)
|
|
114
116
|
|
|
115
|
-
|
|
117
|
+
#@kekik_cache(ttl=60*60)
|
|
116
118
|
async def load_item(self, url: str) -> SeriesInfo:
|
|
117
119
|
istek = await self.httpx.get(url)
|
|
118
120
|
secici = Selector(istek.text)
|
|
@@ -152,15 +154,27 @@ class Dizilla(PluginBase):
|
|
|
152
154
|
actors = actors
|
|
153
155
|
)
|
|
154
156
|
|
|
155
|
-
|
|
156
|
-
async def load_links(self, url: str) -> list[
|
|
157
|
-
istek
|
|
158
|
-
secici
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
157
|
+
#@kekik_cache(ttl=15*60)
|
|
158
|
+
async def load_links(self, url: str) -> list[dict]:
|
|
159
|
+
istek = await self.httpx.get(url)
|
|
160
|
+
secici = Selector(istek.text)
|
|
161
|
+
|
|
162
|
+
next_data = loads(secici.css("script#__NEXT_DATA__::text").get())
|
|
163
|
+
secure_data = next_data.get("props", {}).get("pageProps", {}).get("secureData", {})
|
|
164
|
+
decrypted = await self.decrypt_response(secure_data)
|
|
165
|
+
results = decrypted.get("RelatedResults", {}).get("getEpisodeSources", {}).get("result", [])
|
|
166
|
+
|
|
167
|
+
links = []
|
|
168
|
+
for result in results:
|
|
169
|
+
iframe_src = Selector(result.get("source_content")).css("iframe::attr(src)").get()
|
|
170
|
+
iframe_url = self.fix_url(iframe_src)
|
|
171
|
+
if not iframe_url:
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
extractor = self.ex_manager.find_extractor(iframe_url)
|
|
175
|
+
links.append({
|
|
176
|
+
"url" : iframe_url,
|
|
177
|
+
"name" : f"{extractor.name if extractor else 'Main Player'} | {result.get('language_name')}",
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
return links
|
|
@@ -6,7 +6,7 @@ from parsel import Selector
|
|
|
6
6
|
class FilmMakinesi(PluginBase):
|
|
7
7
|
name = "FilmMakinesi"
|
|
8
8
|
language = "tr"
|
|
9
|
-
main_url = "https://filmmakinesi.
|
|
9
|
+
main_url = "https://filmmakinesi.sh"
|
|
10
10
|
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
11
11
|
description = "Film Makinesi, en yeni ve en güncel filmleri sitemizde full HD kalite farkı ile izleyebilirsiniz. HD film izle denildiğinde akla gelen en kaliteli film izleme sitesi."
|
|
12
12
|
|
|
@@ -34,7 +34,7 @@ class FilmMakinesi(PluginBase):
|
|
|
34
34
|
f"{main_url}/film-izle/spor/page/" : "Spor"
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
#@kekik_cache(ttl=60*60)
|
|
38
38
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
39
39
|
istek = self.cloudscraper.get(f"{url}{page}")
|
|
40
40
|
secici = Selector(istek.text)
|
|
@@ -51,15 +51,15 @@ class FilmMakinesi(PluginBase):
|
|
|
51
51
|
for veri in veriler
|
|
52
52
|
]
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
#@kekik_cache(ttl=60*60)
|
|
55
55
|
async def search(self, query: str) -> list[SearchResult]:
|
|
56
|
-
istek = await self.httpx.get(f"{self.main_url}/?s={query}")
|
|
56
|
+
istek = await self.httpx.get(f"{self.main_url}/arama/?s={query}")
|
|
57
57
|
secici = Selector(istek.text)
|
|
58
58
|
|
|
59
59
|
results = []
|
|
60
|
-
for article in secici.css("
|
|
61
|
-
title = article.css("
|
|
62
|
-
href = article.css("
|
|
60
|
+
for article in secici.css("div.item-relative"):
|
|
61
|
+
title = article.css("div.title::text").get()
|
|
62
|
+
href = article.css("a::attr(href)").get()
|
|
63
63
|
poster = article.css("img::attr(data-src)").get() or article.css("img::attr(src)").get()
|
|
64
64
|
|
|
65
65
|
if title and href:
|
|
@@ -73,40 +73,47 @@ class FilmMakinesi(PluginBase):
|
|
|
73
73
|
|
|
74
74
|
return results
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
#@kekik_cache(ttl=60*60)
|
|
77
77
|
async def load_item(self, url: str) -> MovieInfo:
|
|
78
78
|
istek = await self.httpx.get(url)
|
|
79
79
|
secici = Selector(istek.text)
|
|
80
80
|
|
|
81
|
-
title = secici.css("h1.
|
|
82
|
-
poster = secici.css("
|
|
83
|
-
description = secici.css("
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
duration = secici.css("dt:contains('Film Süresi:') + dd time::attr(datetime)").get().strip()
|
|
89
|
-
|
|
90
|
-
duration_minutes = 0
|
|
91
|
-
if duration and duration.startswith("PT") and duration.endswith("M"):
|
|
92
|
-
duration_minutes = int(duration[2:-1])
|
|
81
|
+
title = secici.css("h1.title::text").get().strip()
|
|
82
|
+
poster = secici.css("img.cover-img::attr(src)").get().strip()
|
|
83
|
+
description = secici.css("div.info-description p::text").get().strip()
|
|
84
|
+
rating = secici.css("div.score::text").get().strip()
|
|
85
|
+
year = secici.css("span.date a::text").get().strip()
|
|
86
|
+
actors = secici.css("div.cast-name::text").getall()
|
|
87
|
+
duration = secici.css("div.time::text").get().split()[1].strip()
|
|
93
88
|
|
|
94
89
|
return MovieInfo(
|
|
95
90
|
url = url,
|
|
96
91
|
poster = self.fix_url(poster),
|
|
97
|
-
title = title,
|
|
92
|
+
title = self.clean_title(title),
|
|
98
93
|
description = description,
|
|
99
|
-
tags = tags,
|
|
100
94
|
rating = rating,
|
|
101
95
|
year = year,
|
|
102
96
|
actors = actors,
|
|
103
|
-
duration =
|
|
97
|
+
duration = duration
|
|
104
98
|
)
|
|
105
99
|
|
|
106
|
-
|
|
107
|
-
async def load_links(self, url: str) -> list[
|
|
100
|
+
#@kekik_cache(ttl=15*60)
|
|
101
|
+
async def load_links(self, url: str) -> list[dict]:
|
|
108
102
|
istek = await self.httpx.get(url)
|
|
109
103
|
secici = Selector(istek.text)
|
|
110
104
|
|
|
111
|
-
iframe_src = secici.css("
|
|
112
|
-
|
|
105
|
+
iframe_src = secici.css("iframe::attr(data-src)").get()
|
|
106
|
+
|
|
107
|
+
all_links = [iframe_src] if iframe_src else []
|
|
108
|
+
for link in secici.css("div.video-parts a[data-video_url]"):
|
|
109
|
+
all_links.append(link.attrib.get("data-video_url"))
|
|
110
|
+
|
|
111
|
+
response = []
|
|
112
|
+
for idx, link in enumerate(all_links):
|
|
113
|
+
extractor = self.ex_manager.find_extractor(link)
|
|
114
|
+
response.append({
|
|
115
|
+
"url" : link,
|
|
116
|
+
"name" : f"{extractor.name if extractor else f'Player {idx + 1}'}",
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
return response
|