KekikStream 1.7.6__py3-none-any.whl → 1.9.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 +2 -14
- KekikStream/Core/Extractor/ExtractorModels.py +5 -7
- KekikStream/Core/Media/MediaHandler.py +44 -26
- KekikStream/Core/Media/MediaManager.py +0 -3
- KekikStream/Core/Plugin/PluginBase.py +2 -15
- KekikStream/Core/Plugin/PluginModels.py +25 -26
- KekikStream/Extractors/CloseLoad.py +1 -2
- KekikStream/Extractors/ContentX.py +0 -2
- KekikStream/Extractors/DzenRu.py +0 -1
- KekikStream/Extractors/ExPlay.py +0 -1
- KekikStream/Extractors/FirePlayer.py +4 -5
- KekikStream/Extractors/HDPlayerSystem.py +0 -1
- KekikStream/Extractors/JetTv.py +0 -1
- KekikStream/Extractors/MailRu.py +1 -2
- KekikStream/Extractors/MixPlayHD.py +0 -1
- KekikStream/Extractors/MixTiger.py +1 -5
- KekikStream/Extractors/MolyStream.py +5 -5
- KekikStream/Extractors/Odnoklassniki.py +6 -6
- KekikStream/Extractors/PeaceMakerst.py +0 -1
- KekikStream/Extractors/PixelDrain.py +0 -1
- KekikStream/Extractors/PlayerFilmIzle.py +5 -5
- KekikStream/Extractors/RapidVid.py +0 -1
- KekikStream/Extractors/SetPlay.py +0 -1
- KekikStream/Extractors/SetPrime.py +0 -1
- KekikStream/Extractors/SibNet.py +0 -1
- KekikStream/Extractors/Sobreatsesuyp.py +0 -1
- KekikStream/Extractors/TRsTX.py +0 -1
- KekikStream/Extractors/TauVideo.py +0 -1
- KekikStream/Extractors/TurboImgz.py +0 -1
- KekikStream/Extractors/TurkeyPlayer.py +5 -5
- KekikStream/Extractors/VidHide.py +5 -5
- KekikStream/Extractors/VidMoly.py +0 -1
- KekikStream/Extractors/VidMoxy.py +0 -1
- KekikStream/Extractors/VidPapi.py +0 -1
- KekikStream/Extractors/VideoSeyred.py +0 -1
- KekikStream/Extractors/YTDLP.py +109 -0
- KekikStream/Extractors/YildizKisaFilm.py +0 -1
- KekikStream/Plugins/DiziBox.py +3 -8
- KekikStream/Plugins/DiziPal.py +5 -5
- KekikStream/Plugins/DiziYou.py +44 -19
- KekikStream/Plugins/Dizilla.py +38 -27
- KekikStream/Plugins/FilmBip.py +1 -1
- KekikStream/Plugins/FilmMakinesi.py +1 -5
- KekikStream/Plugins/FilmModu.py +3 -3
- KekikStream/Plugins/FullHDFilmizlesene.py +1 -5
- KekikStream/Plugins/HDFilmCehennemi.py +14 -21
- KekikStream/Plugins/JetFilmizle.py +2 -6
- KekikStream/Plugins/RecTV.py +12 -16
- KekikStream/Plugins/RoketDizi.py +105 -93
- KekikStream/Plugins/SelcukFlix.py +160 -67
- KekikStream/Plugins/SezonlukDizi.py +1 -5
- KekikStream/Plugins/SineWix.py +4 -8
- KekikStream/Plugins/Sinefy.py +72 -51
- KekikStream/Plugins/SinemaCX.py +4 -4
- KekikStream/Plugins/Sinezy.py +74 -42
- KekikStream/Plugins/UgurFilm.py +2 -6
- KekikStream/__init__.py +5 -8
- KekikStream/requirements.txt +2 -3
- kekikstream-1.9.0.dist-info/METADATA +290 -0
- kekikstream-1.9.0.dist-info/RECORD +86 -0
- kekikstream-1.7.6.dist-info/METADATA +0 -110
- kekikstream-1.7.6.dist-info/RECORD +0 -85
- {kekikstream-1.7.6.dist-info → kekikstream-1.9.0.dist-info}/WHEEL +0 -0
- {kekikstream-1.7.6.dist-info → kekikstream-1.9.0.dist-info}/entry_points.txt +0 -0
- {kekikstream-1.7.6.dist-info → kekikstream-1.9.0.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-1.7.6.dist-info → kekikstream-1.9.0.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, Subtitle
|
|
4
4
|
from parsel import Selector
|
|
5
5
|
from Kekik.Sifreleme import Packer, StreamDecoder
|
|
6
6
|
import random, string, re
|
|
@@ -12,8 +12,7 @@ class HDFilmCehennemi(PluginBase):
|
|
|
12
12
|
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
13
13
|
description = "Türkiye'nin en hızlı hd film izleme sitesi"
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
requires_cffi = True
|
|
15
|
+
|
|
17
16
|
|
|
18
17
|
main_page = {
|
|
19
18
|
f"{main_url}" : "Yeni Eklenen Filmler",
|
|
@@ -32,9 +31,8 @@ class HDFilmCehennemi(PluginBase):
|
|
|
32
31
|
f"{main_url}/tur/romantik-filmleri-izle-1" : "Romantik Filmleri"
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
#@kekik_cache(ttl=60*60)
|
|
36
34
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
37
|
-
istek = await self.
|
|
35
|
+
istek = await self.httpx.get(f"{url}", follow_redirects=True)
|
|
38
36
|
secici = Selector(istek.text)
|
|
39
37
|
|
|
40
38
|
return [
|
|
@@ -47,9 +45,8 @@ class HDFilmCehennemi(PluginBase):
|
|
|
47
45
|
for veri in secici.css("div.section-content a.poster")
|
|
48
46
|
]
|
|
49
47
|
|
|
50
|
-
#@kekik_cache(ttl=60*60)
|
|
51
48
|
async def search(self, query: str) -> list[SearchResult]:
|
|
52
|
-
istek = await self.
|
|
49
|
+
istek = await self.httpx.get(
|
|
53
50
|
url = f"{self.main_url}/search/?q={query}",
|
|
54
51
|
headers = {
|
|
55
52
|
"Referer" : f"{self.main_url}/",
|
|
@@ -76,9 +73,8 @@ class HDFilmCehennemi(PluginBase):
|
|
|
76
73
|
|
|
77
74
|
return results
|
|
78
75
|
|
|
79
|
-
#@kekik_cache(ttl=60*60)
|
|
80
76
|
async def load_item(self, url: str) -> MovieInfo:
|
|
81
|
-
istek = await self.
|
|
77
|
+
istek = await self.httpx.get(url, headers = {"Referer": f"{self.main_url}/"})
|
|
82
78
|
secici = Selector(istek.text)
|
|
83
79
|
|
|
84
80
|
title = secici.css("h1.section-title::text").get().strip()
|
|
@@ -111,11 +107,10 @@ class HDFilmCehennemi(PluginBase):
|
|
|
111
107
|
def generate_random_cookie(self):
|
|
112
108
|
return "".join(random.choices(string.ascii_letters + string.digits, k=16))
|
|
113
109
|
|
|
114
|
-
#@kekik_cache(ttl=15*60)
|
|
115
110
|
async def cehennempass(self, video_id: str) -> list[dict]:
|
|
116
111
|
results = []
|
|
117
112
|
|
|
118
|
-
istek = await self.
|
|
113
|
+
istek = await self.httpx.post(
|
|
119
114
|
url = "https://cehennempass.pw/process_quality_selection.php",
|
|
120
115
|
headers = {
|
|
121
116
|
"Referer" : f"https://cehennempass.pw/download/{video_id}",
|
|
@@ -132,7 +127,7 @@ class HDFilmCehennemi(PluginBase):
|
|
|
132
127
|
"referer" : f"https://cehennempass.pw/download/{video_id}"
|
|
133
128
|
})
|
|
134
129
|
|
|
135
|
-
istek = await self.
|
|
130
|
+
istek = await self.httpx.post(
|
|
136
131
|
url = "https://cehennempass.pw/process_quality_selection.php",
|
|
137
132
|
headers = {
|
|
138
133
|
"Referer" : f"https://cehennempass.pw/download/{video_id}",
|
|
@@ -151,10 +146,9 @@ class HDFilmCehennemi(PluginBase):
|
|
|
151
146
|
|
|
152
147
|
return results
|
|
153
148
|
|
|
154
|
-
#@kekik_cache(ttl=15*60)
|
|
155
149
|
async def invoke_local_source(self, iframe: str, source: str, url: str):
|
|
156
|
-
self.
|
|
157
|
-
istek = await self.
|
|
150
|
+
self.httpx.headers.update({"Referer": f"{self.main_url}/"})
|
|
151
|
+
istek = await self.httpx.get(iframe)
|
|
158
152
|
|
|
159
153
|
try:
|
|
160
154
|
eval_func = re.compile(r'\s*(eval\(function[\s\S].*)\s*').findall(istek.text)[0]
|
|
@@ -182,9 +176,8 @@ class HDFilmCehennemi(PluginBase):
|
|
|
182
176
|
"subtitles" : subtitles
|
|
183
177
|
}]
|
|
184
178
|
|
|
185
|
-
#@kekik_cache(ttl=15*60)
|
|
186
179
|
async def load_links(self, url: str) -> list[dict]:
|
|
187
|
-
istek = await self.
|
|
180
|
+
istek = await self.httpx.get(url)
|
|
188
181
|
secici = Selector(istek.text)
|
|
189
182
|
|
|
190
183
|
results = []
|
|
@@ -195,7 +188,7 @@ class HDFilmCehennemi(PluginBase):
|
|
|
195
188
|
source = f"{link.css('::text').get().replace('(HDrip Xbet)', '').strip()} {lang_code}"
|
|
196
189
|
video_id = link.css("::attr(data-video)").get()
|
|
197
190
|
|
|
198
|
-
api_get = await self.
|
|
191
|
+
api_get = await self.httpx.get(
|
|
199
192
|
url = f"{self.main_url}/video/{video_id}/",
|
|
200
193
|
headers = {
|
|
201
194
|
"Content-Type" : "application/json",
|
|
@@ -219,9 +212,9 @@ class HDFilmCehennemi(PluginBase):
|
|
|
219
212
|
|
|
220
213
|
return results
|
|
221
214
|
|
|
222
|
-
async def play(self,
|
|
223
|
-
extract_result = ExtractResult(
|
|
224
|
-
self.media_handler.title = name
|
|
215
|
+
async def play(self, **kwargs):
|
|
216
|
+
extract_result = ExtractResult(**kwargs)
|
|
217
|
+
self.media_handler.title = kwargs.get("name")
|
|
225
218
|
if self.name not in self.media_handler.title:
|
|
226
219
|
self.media_handler.title = f"{self.name} | {self.media_handler.title}"
|
|
227
220
|
|
|
@@ -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
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo
|
|
4
4
|
from parsel import Selector
|
|
5
5
|
|
|
6
6
|
class JetFilmizle(PluginBase):
|
|
@@ -19,9 +19,8 @@ class JetFilmizle(PluginBase):
|
|
|
19
19
|
f"{main_url}/kategoriler/yesilcam-filmleri-izlee/page/" : "Yeşilçam Filmleri"
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
#@kekik_cache(ttl=60*60)
|
|
23
22
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
24
|
-
istek = await self.httpx.get(f"{url}{page}",
|
|
23
|
+
istek = await self.httpx.get(f"{url}{page}", follow_redirects=True)
|
|
25
24
|
secici = Selector(istek.text)
|
|
26
25
|
|
|
27
26
|
return [
|
|
@@ -34,7 +33,6 @@ class JetFilmizle(PluginBase):
|
|
|
34
33
|
for veri in secici.css("article.movie") if veri.css("h2 a::text, h3 a::text, h4 a::text, h5 a::text, h6 a::text").get()
|
|
35
34
|
]
|
|
36
35
|
|
|
37
|
-
#@kekik_cache(ttl=60*60)
|
|
38
36
|
async def search(self, query: str) -> list[SearchResult]:
|
|
39
37
|
istek = await self.httpx.post(
|
|
40
38
|
url = f"{self.main_url}/filmara.php",
|
|
@@ -60,7 +58,6 @@ class JetFilmizle(PluginBase):
|
|
|
60
58
|
|
|
61
59
|
return results
|
|
62
60
|
|
|
63
|
-
#@kekik_cache(ttl=60*60)
|
|
64
61
|
async def load_item(self, url: str) -> MovieInfo:
|
|
65
62
|
istek = await self.httpx.get(url)
|
|
66
63
|
secici = Selector(istek.text)
|
|
@@ -92,7 +89,6 @@ class JetFilmizle(PluginBase):
|
|
|
92
89
|
actors = actors
|
|
93
90
|
)
|
|
94
91
|
|
|
95
|
-
#@kekik_cache(ttl=15*60)
|
|
96
92
|
async def load_links(self, url: str) -> list[dict]:
|
|
97
93
|
istek = await self.httpx.get(url)
|
|
98
94
|
secici = Selector(istek.text)
|
KekikStream/Plugins/RecTV.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
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Episode, SeriesInfo, ExtractResult, Subtitle
|
|
4
4
|
from json import dumps, loads
|
|
5
5
|
import re
|
|
6
6
|
|
|
@@ -30,7 +30,6 @@ class RecTV(PluginBase):
|
|
|
30
30
|
f"{main_url}/api/movie/by/filtres/5/created/SAYFA/{sw_key}/" : "Romantik"
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
#@kekik_cache(ttl=60*60)
|
|
34
33
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
35
34
|
self.httpx.headers.update({"user-agent": "okhttp/4.12.0"})
|
|
36
35
|
istek = await self.httpx.get(f"{url.replace('SAYFA', str(int(page) - 1))}")
|
|
@@ -46,7 +45,6 @@ class RecTV(PluginBase):
|
|
|
46
45
|
for veri in veriler
|
|
47
46
|
]
|
|
48
47
|
|
|
49
|
-
#@kekik_cache(ttl=60*60)
|
|
50
48
|
async def search(self, query: str) -> list[SearchResult]:
|
|
51
49
|
self.httpx.headers.update({"user-agent": "okhttp/4.12.0"})
|
|
52
50
|
istek = await self.httpx.get(f"{self.main_url}/api/search/{query}/{self.sw_key}/")
|
|
@@ -67,7 +65,6 @@ class RecTV(PluginBase):
|
|
|
67
65
|
for veri in tum_veri
|
|
68
66
|
]
|
|
69
67
|
|
|
70
|
-
#@kekik_cache(ttl=60*60)
|
|
71
68
|
async def load_item(self, url: str) -> MovieInfo:
|
|
72
69
|
self.httpx.headers.update({"user-agent": "okhttp/4.12.0"})
|
|
73
70
|
veri = loads(url)
|
|
@@ -119,10 +116,7 @@ class RecTV(PluginBase):
|
|
|
119
116
|
actors = []
|
|
120
117
|
)
|
|
121
118
|
|
|
122
|
-
#@kekik_cache(ttl=15*60)
|
|
123
119
|
async def load_links(self, url: str) -> list[dict]:
|
|
124
|
-
self.media_handler.headers.update({"User-Agent": "googleusercontent"})
|
|
125
|
-
|
|
126
120
|
try:
|
|
127
121
|
veri = loads(url)
|
|
128
122
|
except Exception:
|
|
@@ -132,9 +126,10 @@ class RecTV(PluginBase):
|
|
|
132
126
|
# Eğer dizi bölümü ise (bizim oluşturduğumuz yapı)
|
|
133
127
|
if veri.get("is_episode"):
|
|
134
128
|
return [{
|
|
135
|
-
"url"
|
|
136
|
-
"name"
|
|
137
|
-
"
|
|
129
|
+
"url" : veri.get("url"),
|
|
130
|
+
"name" : veri.get("title", "Bölüm"),
|
|
131
|
+
"user_agent" : "googleusercontent",
|
|
132
|
+
"referer" : "https://twitter.com/"
|
|
138
133
|
}]
|
|
139
134
|
|
|
140
135
|
# Film ise (RecTV API yapısı)
|
|
@@ -146,16 +141,17 @@ class RecTV(PluginBase):
|
|
|
146
141
|
continue
|
|
147
142
|
|
|
148
143
|
results.append({
|
|
149
|
-
"url"
|
|
150
|
-
"name"
|
|
151
|
-
"
|
|
144
|
+
"url" : video_link,
|
|
145
|
+
"name" : f"{veri.get('title')} - {kaynak.get('title')}",
|
|
146
|
+
"user_agent" : "googleusercontent",
|
|
147
|
+
"referer" : "https://twitter.com/"
|
|
152
148
|
})
|
|
153
149
|
|
|
154
150
|
return results
|
|
155
151
|
|
|
156
|
-
async def play(self,
|
|
157
|
-
extract_result = ExtractResult(
|
|
158
|
-
self.media_handler.title = name
|
|
152
|
+
async def play(self, **kwargs):
|
|
153
|
+
extract_result = ExtractResult(**kwargs)
|
|
154
|
+
self.media_handler.title = kwargs.get("name")
|
|
159
155
|
if self.name not in self.media_handler.title:
|
|
160
156
|
self.media_handler.title = f"{self.name} | {self.media_handler.title}"
|
|
161
157
|
|
KekikStream/Plugins/RoketDizi.py
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, MovieInfo
|
|
4
4
|
from parsel import Selector
|
|
5
|
-
import re, base64, json
|
|
5
|
+
import re, base64, json
|
|
6
6
|
|
|
7
7
|
class RoketDizi(PluginBase):
|
|
8
8
|
name = "RoketDizi"
|
|
9
9
|
lang = "tr"
|
|
10
|
-
main_url = "https://
|
|
10
|
+
main_url = "https://roketdizi.to"
|
|
11
|
+
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
12
|
+
description = "Türkiye'nin en tatlış yabancı dizi izleme sitesi. Türkçe dublaj, altyazılı, eski ve yeni yabancı dizilerin yanı sıra kore (asya) dizileri izleyebilirsiniz."
|
|
13
|
+
|
|
11
14
|
|
|
12
|
-
# Domain doğrulama ve anti-bot mekanizmaları var
|
|
13
|
-
requires_cffi = True
|
|
14
15
|
|
|
15
16
|
main_page = {
|
|
16
17
|
"dizi/tur/aksiyon" : "Aksiyon",
|
|
@@ -25,81 +26,70 @@ class RoketDizi(PluginBase):
|
|
|
25
26
|
|
|
26
27
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
27
28
|
full_url = f"{self.main_url}/{url}?&page={page}"
|
|
28
|
-
resp
|
|
29
|
-
sel
|
|
29
|
+
resp = await self.httpx.get(full_url)
|
|
30
|
+
sel = Selector(resp.text)
|
|
30
31
|
|
|
31
32
|
results = []
|
|
32
33
|
|
|
33
34
|
for item in sel.css("div.w-full.p-4 span.bg-\\[\\#232323\\]"):
|
|
34
|
-
title
|
|
35
|
-
href
|
|
36
|
-
poster= item.css("img::attr(src)").get()
|
|
35
|
+
title = item.css("span.font-normal.line-clamp-1::text").get()
|
|
36
|
+
href = item.css("a::attr(href)").get()
|
|
37
|
+
poster = item.css("img::attr(src)").get()
|
|
37
38
|
|
|
38
39
|
if title and href:
|
|
39
40
|
results.append(MainPageResult(
|
|
40
|
-
category=category,
|
|
41
|
-
title=title,
|
|
42
|
-
url=self.fix_url(href),
|
|
43
|
-
poster=self.fix_url(poster)
|
|
41
|
+
category = category,
|
|
42
|
+
title = title,
|
|
43
|
+
url = self.fix_url(href),
|
|
44
|
+
poster = self.fix_url(poster)
|
|
44
45
|
))
|
|
45
46
|
return results
|
|
46
47
|
|
|
47
|
-
async def get_domain(self):
|
|
48
|
-
try:
|
|
49
|
-
domain_list = await self.cffi.get("https://raw.githubusercontent.com/Kraptor123/domainListesi/refs/heads/main/eklenti_domainleri.txt")
|
|
50
|
-
if domain_list.status_code == 200:
|
|
51
|
-
for line in domain_list.text.split("|"):
|
|
52
|
-
if line.strip().startswith("RoketDizi"):
|
|
53
|
-
domain = line.split(":")[-1].strip()
|
|
54
|
-
if "http" not in domain:
|
|
55
|
-
domain = f"https://{domain}"
|
|
56
|
-
return domain
|
|
57
|
-
except Exception:
|
|
58
|
-
pass
|
|
59
|
-
return self.main_url
|
|
60
|
-
|
|
61
48
|
async def search(self, query: str) -> list[SearchResult]:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# Get Cookies and Keys
|
|
65
|
-
main_req = await self.cffi.get(current_domain)
|
|
66
|
-
sel = Selector(main_req.text)
|
|
67
|
-
|
|
68
|
-
c_key = sel.css("input[name='cKey']::attr(value)").get()
|
|
69
|
-
c_value = sel.css("input[name='cValue']::attr(value)").get()
|
|
70
|
-
|
|
71
|
-
post_url = f"{current_domain}/api/bg/searchContent?searchterm={query}"
|
|
49
|
+
post_url = f"{self.main_url}/api/bg/searchContent?searchterm={query}"
|
|
72
50
|
|
|
73
51
|
headers = {
|
|
74
|
-
"Accept": "application/json, text/javascript, */*; q=0.01",
|
|
75
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
76
|
-
"Referer": f"{
|
|
77
|
-
"CNT": "vakTR"
|
|
52
|
+
"Accept" : "application/json, text/javascript, */*; q=0.01",
|
|
53
|
+
"X-Requested-With" : "XMLHttpRequest",
|
|
54
|
+
"Referer" : f"{self.main_url}/",
|
|
78
55
|
}
|
|
79
|
-
|
|
80
|
-
data = {}
|
|
81
|
-
if c_key and c_value:
|
|
82
|
-
data = {"cKey": c_key, "cValue": c_value}
|
|
83
56
|
|
|
84
|
-
search_req = await self.
|
|
57
|
+
search_req = await self.httpx.post(post_url, headers=headers)
|
|
85
58
|
|
|
86
59
|
try:
|
|
87
60
|
resp_json = search_req.json()
|
|
88
|
-
|
|
61
|
+
|
|
62
|
+
# Response is base64 encoded!
|
|
63
|
+
if not resp_json.get("success"):
|
|
64
|
+
return []
|
|
65
|
+
|
|
66
|
+
encoded_response = resp_json.get("response", "")
|
|
67
|
+
if not encoded_response:
|
|
68
|
+
return []
|
|
69
|
+
|
|
70
|
+
# Decode base64
|
|
71
|
+
decoded = base64.b64decode(encoded_response).decode('utf-8')
|
|
72
|
+
data = json.loads(decoded)
|
|
73
|
+
|
|
74
|
+
if not data.get("state"):
|
|
89
75
|
return []
|
|
90
76
|
|
|
91
|
-
html_content = resp_json.get("html", "").strip()
|
|
92
|
-
sel_results = Selector(html_content)
|
|
93
|
-
|
|
94
77
|
results = []
|
|
95
|
-
|
|
78
|
+
result_items = data.get("result", [])
|
|
96
79
|
|
|
97
|
-
for
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
80
|
+
for item in result_items:
|
|
81
|
+
title = item.get("object_name", "")
|
|
82
|
+
slug = item.get("used_slug", "")
|
|
83
|
+
poster = item.get("object_poster_url", "")
|
|
84
|
+
|
|
85
|
+
if title and slug:
|
|
86
|
+
# Construct full URL from slug
|
|
87
|
+
full_url = f"{self.main_url}/{slug}"
|
|
88
|
+
results.append(SearchResult(
|
|
89
|
+
title = title.strip(),
|
|
90
|
+
url = full_url,
|
|
91
|
+
poster = self.fix_url(poster) if poster else None
|
|
92
|
+
))
|
|
103
93
|
|
|
104
94
|
return results
|
|
105
95
|
|
|
@@ -108,21 +98,48 @@ class RoketDizi(PluginBase):
|
|
|
108
98
|
|
|
109
99
|
async def load_item(self, url: str) -> SeriesInfo:
|
|
110
100
|
# Note: Handling both Movie and Series logic in one, returning SeriesInfo generally or MovieInfo
|
|
111
|
-
resp = await self.
|
|
101
|
+
resp = await self.httpx.get(url)
|
|
112
102
|
sel = Selector(resp.text)
|
|
113
103
|
|
|
114
104
|
title = sel.css("h1.text-white::text").get()
|
|
115
105
|
poster = sel.css("div.w-full.page-top img::attr(src)").get()
|
|
116
106
|
description = sel.css("div.mt-2.text-sm::text").get()
|
|
117
107
|
|
|
118
|
-
|
|
108
|
+
# Tags - genre bilgileri (Detaylar bölümünde)
|
|
109
|
+
tags = []
|
|
110
|
+
genre_text = sel.css("h3.text-white.opacity-90::text").get()
|
|
111
|
+
if genre_text:
|
|
112
|
+
tags = [t.strip() for t in genre_text.split(",")]
|
|
119
113
|
|
|
120
|
-
|
|
121
|
-
if tags:
|
|
122
|
-
tags = [t.strip() for t in tags.split(",")]
|
|
123
|
-
|
|
114
|
+
# Rating
|
|
124
115
|
rating = sel.css("div.flex.items-center span.text-white.text-sm::text").get()
|
|
125
|
-
|
|
116
|
+
|
|
117
|
+
# Year ve Actors - Detaylar (Details) bölümünden
|
|
118
|
+
year = None
|
|
119
|
+
actors = []
|
|
120
|
+
|
|
121
|
+
# Detaylar bölümündeki tüm flex-col div'leri al
|
|
122
|
+
detail_items = sel.css("div.flex.flex-col")
|
|
123
|
+
for item in detail_items:
|
|
124
|
+
# Label ve value yapısı: span.text-base ve span.text-sm.opacity-90
|
|
125
|
+
label = item.css("span.text-base::text").get()
|
|
126
|
+
value = item.css("span.text-sm.opacity-90::text").get()
|
|
127
|
+
|
|
128
|
+
if label and value:
|
|
129
|
+
label = label.strip()
|
|
130
|
+
value = value.strip()
|
|
131
|
+
|
|
132
|
+
# Yayın tarihi (yıl)
|
|
133
|
+
if label == "Yayın tarihi":
|
|
134
|
+
# "16 Ekim 2018" formatından yılı çıkar
|
|
135
|
+
year_match = re.search(r'\d{4}', value)
|
|
136
|
+
if year_match:
|
|
137
|
+
year = year_match.group()
|
|
138
|
+
|
|
139
|
+
# Yaratıcılar veya Oyuncular
|
|
140
|
+
elif label in ["Yaratıcılar", "Oyuncular"]:
|
|
141
|
+
if value:
|
|
142
|
+
actors.append(value)
|
|
126
143
|
|
|
127
144
|
# Check urls for episodes
|
|
128
145
|
all_urls = re.findall(r'"url":"([^"]*)"', resp.text)
|
|
@@ -130,37 +147,42 @@ class RoketDizi(PluginBase):
|
|
|
130
147
|
|
|
131
148
|
episodes = []
|
|
132
149
|
if is_series:
|
|
133
|
-
|
|
150
|
+
# Dict kullanarak duplicate'leri önle ama sıralı tut
|
|
151
|
+
episodes_dict = {}
|
|
134
152
|
for u in all_urls:
|
|
135
|
-
if "bolum" in u and u not in
|
|
136
|
-
seen_eps.add(u)
|
|
153
|
+
if "bolum" in u and u not in episodes_dict:
|
|
137
154
|
season_match = re.search(r'/sezon-(\d+)', u)
|
|
138
155
|
ep_match = re.search(r'/bolum-(\d+)', u)
|
|
139
156
|
|
|
140
157
|
season = int(season_match.group(1)) if season_match else 1
|
|
141
158
|
episode_num = int(ep_match.group(1)) if ep_match else 1
|
|
142
159
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
160
|
+
# Key olarak (season, episode) tuple kullan
|
|
161
|
+
key = (season, episode_num)
|
|
162
|
+
episodes_dict[key] = Episode(
|
|
163
|
+
season = season,
|
|
164
|
+
episode = episode_num,
|
|
165
|
+
title = f"{season}. Sezon {episode_num}. Bölüm",
|
|
166
|
+
url = self.fix_url(u)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Sıralı liste oluştur
|
|
170
|
+
episodes = [episodes_dict[key] for key in sorted(episodes_dict.keys())]
|
|
149
171
|
|
|
150
172
|
return SeriesInfo(
|
|
151
|
-
title=title,
|
|
152
|
-
url=url,
|
|
153
|
-
poster=self.fix_url(poster),
|
|
154
|
-
description=description,
|
|
155
|
-
tags=tags,
|
|
156
|
-
rating=rating,
|
|
157
|
-
actors=actors,
|
|
158
|
-
episodes=episodes,
|
|
159
|
-
year=year
|
|
173
|
+
title = title,
|
|
174
|
+
url = url,
|
|
175
|
+
poster = self.fix_url(poster),
|
|
176
|
+
description = description,
|
|
177
|
+
tags = tags,
|
|
178
|
+
rating = rating,
|
|
179
|
+
actors = actors,
|
|
180
|
+
episodes = episodes,
|
|
181
|
+
year = year
|
|
160
182
|
)
|
|
161
183
|
|
|
162
184
|
async def load_links(self, url: str) -> list[dict]:
|
|
163
|
-
resp = await self.
|
|
185
|
+
resp = await self.httpx.get(url)
|
|
164
186
|
sel = Selector(resp.text)
|
|
165
187
|
|
|
166
188
|
next_data = sel.css("script#__NEXT_DATA__::text").get()
|
|
@@ -195,13 +217,3 @@ class RoketDizi(PluginBase):
|
|
|
195
217
|
|
|
196
218
|
except Exception:
|
|
197
219
|
return []
|
|
198
|
-
|
|
199
|
-
def fix_url(self, url: str, domain:str=None) -> str:
|
|
200
|
-
if not url: return ""
|
|
201
|
-
if url.startswith("http"): return url
|
|
202
|
-
base = domain or self.main_url
|
|
203
|
-
return f"https:{url}" if url.startswith("//") else urllib.parse.urljoin(base, url)
|
|
204
|
-
|
|
205
|
-
def get_domain_sync(self, url:str):
|
|
206
|
-
parsed = urllib.parse.urlparse(url)
|
|
207
|
-
return f"{parsed.scheme}://{parsed.netloc}"
|