KekikStream 2.4.9__py3-none-any.whl → 2.5.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.
Potentially problematic release.
This version of KekikStream might be problematic. Click here for more details.
- KekikStream/Core/Plugin/PluginBase.py +8 -2
- KekikStream/Extractors/Abstream.py +27 -0
- KekikStream/Extractors/Filemoon.py +2 -1
- KekikStream/Plugins/KultFilmler.py +136 -68
- KekikStream/Plugins/RecTV.py +9 -5
- KekikStream/Plugins/RoketDizi.py +13 -22
- KekikStream/Plugins/SetFilmIzle.py +47 -19
- KekikStream/Plugins/SezonlukDizi.py +62 -34
- KekikStream/Plugins/Sinefy.py +172 -81
- KekikStream/Plugins/SinemaCX.py +138 -62
- KekikStream/Plugins/Sinezy.py +15 -16
- KekikStream/Plugins/SuperFilmGeldi.py +3 -3
- KekikStream/Plugins/UgurFilm.py +71 -24
- KekikStream/Plugins/Watch32.py +38 -53
- KekikStream/Plugins/YabanciDizi.py +158 -139
- {kekikstream-2.4.9.dist-info → kekikstream-2.5.0.dist-info}/METADATA +1 -1
- {kekikstream-2.4.9.dist-info → kekikstream-2.5.0.dist-info}/RECORD +21 -20
- {kekikstream-2.4.9.dist-info → kekikstream-2.5.0.dist-info}/WHEEL +0 -0
- {kekikstream-2.4.9.dist-info → kekikstream-2.5.0.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.4.9.dist-info → kekikstream-2.5.0.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.4.9.dist-info → kekikstream-2.5.0.dist-info}/top_level.txt +0 -0
KekikStream/Plugins/SinemaCX.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core
|
|
4
|
-
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Subtitle, ExtractResult, HTMLHelper
|
|
4
|
+
import asyncio, contextlib
|
|
5
5
|
|
|
6
6
|
class SinemaCX(PluginBase):
|
|
7
7
|
name = "SinemaCX"
|
|
@@ -46,13 +46,13 @@ class SinemaCX(PluginBase):
|
|
|
46
46
|
if not title:
|
|
47
47
|
continue
|
|
48
48
|
|
|
49
|
-
href
|
|
49
|
+
href = secici.select_attr("div.yanac a", "href", veri)
|
|
50
50
|
poster = secici.select_attr("a.resim img", "data-src", veri) or secici.select_attr("a.resim img", "src", veri)
|
|
51
51
|
|
|
52
52
|
results.append(MainPageResult(
|
|
53
53
|
category = category,
|
|
54
54
|
title = title,
|
|
55
|
-
url = self.fix_url(href)
|
|
55
|
+
url = self.fix_url(href),
|
|
56
56
|
poster = self.fix_url(poster),
|
|
57
57
|
))
|
|
58
58
|
|
|
@@ -68,12 +68,12 @@ class SinemaCX(PluginBase):
|
|
|
68
68
|
if not title:
|
|
69
69
|
continue
|
|
70
70
|
|
|
71
|
-
href
|
|
71
|
+
href = secici.select_attr("div.yanac a", "href", veri)
|
|
72
72
|
poster = secici.select_attr("a.resim img", "data-src", veri) or secici.select_attr("a.resim img", "src", veri)
|
|
73
73
|
|
|
74
74
|
results.append(SearchResult(
|
|
75
75
|
title = title,
|
|
76
|
-
url = self.fix_url(href)
|
|
76
|
+
url = self.fix_url(href),
|
|
77
77
|
poster = self.fix_url(poster),
|
|
78
78
|
))
|
|
79
79
|
|
|
@@ -90,6 +90,8 @@ class SinemaCX(PluginBase):
|
|
|
90
90
|
tags = secici.select_texts("div.f-bilgi div.tur a")
|
|
91
91
|
year = secici.extract_year("ul.detay a[href*='yapim']")
|
|
92
92
|
actors = secici.select_texts("li.oync li.oyuncu-k span.isim")
|
|
93
|
+
_duration = secici.regex_first(r"<span>Süre:\s*</span>\s*(\d+)")
|
|
94
|
+
duration = int(_duration) if _duration else None
|
|
93
95
|
|
|
94
96
|
return MovieInfo(
|
|
95
97
|
url = url,
|
|
@@ -99,71 +101,145 @@ class SinemaCX(PluginBase):
|
|
|
99
101
|
rating = rating,
|
|
100
102
|
tags = tags,
|
|
101
103
|
year = year,
|
|
102
|
-
actors = actors
|
|
104
|
+
actors = actors,
|
|
105
|
+
duration = duration,
|
|
103
106
|
)
|
|
104
107
|
|
|
105
|
-
async def
|
|
106
|
-
|
|
107
|
-
secici = HTMLHelper(
|
|
108
|
+
async def _get_iframe_from_source(self, text: str) -> str | None:
|
|
109
|
+
"""Verilen sayfa kaynağındaki iframe'i (data-vsrc veya src) bulur."""
|
|
110
|
+
secici = HTMLHelper(text)
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
# Öncelik data-vsrc
|
|
113
|
+
if src := secici.select_attr("iframe", "data-vsrc"):
|
|
114
|
+
return self.fix_url(src.split("?img=")[0])
|
|
110
115
|
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
for i in iframe_list
|
|
115
|
-
)
|
|
116
|
+
# Sonra src
|
|
117
|
+
if src := secici.select_attr("iframe", "src"):
|
|
118
|
+
return self.fix_url(src)
|
|
116
119
|
|
|
117
|
-
|
|
118
|
-
alt_url = url.rstrip("/") + "/2/"
|
|
119
|
-
alt_istek = await self.httpx.get(alt_url)
|
|
120
|
+
return None
|
|
120
121
|
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
async def _process_player(self, iframe_url: str, name: str) -> list[ExtractResult]:
|
|
123
|
+
"""Iframe URL'ini işler ve sonuç döndürür."""
|
|
124
|
+
results = []
|
|
123
125
|
|
|
124
|
-
if
|
|
125
|
-
|
|
126
|
+
if "player.filmizle.in" in iframe_url.lower():
|
|
127
|
+
with contextlib.suppress(Exception):
|
|
128
|
+
# Referer önemli
|
|
129
|
+
self.httpx.headers.update({"Referer": f"{self.main_url}/"})
|
|
130
|
+
|
|
131
|
+
# Iframe içeriğini çek (Altyazı ve JS için)
|
|
132
|
+
iframe_resp = await self.httpx.get(iframe_url)
|
|
133
|
+
iframe_text = iframe_resp.text
|
|
134
|
+
|
|
135
|
+
subtitles = []
|
|
136
|
+
if sub_section := HTMLHelper(iframe_text).regex_first(r'playerjsSubtitle\s*=\s*"(.+?)"'):
|
|
137
|
+
for lang, link in HTMLHelper(sub_section).regex_all(r'\[(.*?)](https?://[^\s\",]+)'):
|
|
138
|
+
subtitles.append(Subtitle(name=lang, url=self.fix_url(link)))
|
|
139
|
+
|
|
140
|
+
base_url = HTMLHelper(iframe_url).regex_first(r"https?://([^/]+)")
|
|
141
|
+
if base_url:
|
|
142
|
+
vid_id = iframe_url.split("/")[-1]
|
|
143
|
+
self.httpx.headers.update({"X-Requested-With": "XMLHttpRequest"})
|
|
144
|
+
|
|
145
|
+
vid_istek = await self.httpx.post(f"https://{base_url}/player/index.php?data={vid_id}&do=getVideo")
|
|
146
|
+
vid_data = vid_istek.json()
|
|
147
|
+
|
|
148
|
+
if link := vid_data.get("securedLink"):
|
|
149
|
+
results.append(ExtractResult(
|
|
150
|
+
name = name,
|
|
151
|
+
url = link,
|
|
152
|
+
referer = iframe_url,
|
|
153
|
+
subtitles = subtitles
|
|
154
|
+
))
|
|
155
|
+
else:
|
|
156
|
+
# Standart Extractor
|
|
157
|
+
with contextlib.suppress(Exception):
|
|
158
|
+
extracted = await self.extract(iframe_url)
|
|
159
|
+
if extracted:
|
|
160
|
+
items = extracted if isinstance(extracted, list) else [extracted]
|
|
161
|
+
for item in items:
|
|
162
|
+
item.name = name
|
|
163
|
+
results.append(item)
|
|
164
|
+
|
|
165
|
+
return results
|
|
126
166
|
|
|
127
|
-
|
|
128
|
-
|
|
167
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
168
|
+
istek = await self.httpx.get(url)
|
|
169
|
+
main_text = istek.text
|
|
170
|
+
secici = HTMLHelper(main_text)
|
|
171
|
+
|
|
172
|
+
sources = [] #List of tuple (url, name, needs_fetch)
|
|
173
|
+
|
|
174
|
+
if part_list := secici.select("ul#part li, ul#f_part li"):
|
|
175
|
+
for li in part_list:
|
|
176
|
+
# Aktif Tab (li.tab-aktif veya span.secili)
|
|
177
|
+
if "tab-aktif" in li.attrs.get("class", ""):
|
|
178
|
+
if a_tag := secici.select_first("a", li):
|
|
179
|
+
# Direkt text al (deep=False)
|
|
180
|
+
val = a_tag.text(strip=True, deep=False)
|
|
181
|
+
name = val if val else "SinemaCX"
|
|
182
|
+
sources.append((None, name, False))
|
|
183
|
+
|
|
184
|
+
elif span := secici.select_first("span.secili", li):
|
|
185
|
+
name = span.text(strip=True)
|
|
186
|
+
sources.append((None, name, False))
|
|
187
|
+
|
|
188
|
+
# Pasif Tab
|
|
189
|
+
elif a_tag := secici.select_first("a", li):
|
|
190
|
+
href = a_tag.attrs.get("href")
|
|
191
|
+
# title varsa title, yoksa text (deep=False ile almayı dene önce)
|
|
192
|
+
name = a_tag.attrs.get("title")
|
|
193
|
+
if not name:
|
|
194
|
+
name = a_tag.text(strip=True, deep=False)
|
|
195
|
+
if not name:
|
|
196
|
+
name = a_tag.text(strip=True) # Fallback
|
|
197
|
+
|
|
198
|
+
if href:
|
|
199
|
+
sources.append((self.fix_url(href), name, True))
|
|
200
|
+
else:
|
|
201
|
+
# Tab yoksa, tek parça filmdir.
|
|
202
|
+
sources.append((None, "SinemaCX", False))
|
|
203
|
+
|
|
204
|
+
# 2. Kaynakları İşle
|
|
205
|
+
extract_tasks = []
|
|
206
|
+
|
|
207
|
+
async def process_task(source):
|
|
208
|
+
src_url, src_name, needs_fetch = source
|
|
209
|
+
|
|
210
|
+
iframe_url = None
|
|
211
|
+
if not needs_fetch:
|
|
212
|
+
# Mevcut sayfa (main_text)
|
|
213
|
+
iframe_url = await self._get_iframe_from_source(main_text)
|
|
214
|
+
else:
|
|
215
|
+
# Yeni sayfa fetch et
|
|
216
|
+
with contextlib.suppress(Exception):
|
|
217
|
+
resp = await self.httpx.get(src_url)
|
|
218
|
+
iframe_url = await self._get_iframe_from_source(resp.text)
|
|
219
|
+
|
|
220
|
+
if iframe_url:
|
|
221
|
+
if "youtube.com" in iframe_url or "youtu.be" in iframe_url:
|
|
222
|
+
return []
|
|
223
|
+
return await self._process_player(iframe_url, src_name)
|
|
129
224
|
return []
|
|
130
225
|
|
|
131
|
-
|
|
226
|
+
for src in sources:
|
|
227
|
+
extract_tasks.append(process_task(src))
|
|
132
228
|
|
|
133
|
-
|
|
134
|
-
self.httpx.headers.update({"Referer": f"{self.main_url}/"})
|
|
135
|
-
iframe_istek = await self.httpx.get(iframe)
|
|
136
|
-
iframe_text = iframe_istek.text
|
|
137
|
-
|
|
138
|
-
subtitles = []
|
|
139
|
-
sub_section = HTMLHelper(iframe_text).regex_first(r'playerjsSubtitle\s*=\s*"(.+?)"')
|
|
140
|
-
if sub_section:
|
|
141
|
-
for lang, link in HTMLHelper(sub_section).regex_all(r'\[(.*?)](https?://[^\s\",]+)'):
|
|
142
|
-
subtitles.append(Subtitle(name=lang, url=self.fix_url(link)))
|
|
143
|
-
|
|
144
|
-
# player.filmizle.in kontrolü
|
|
145
|
-
if "player.filmizle.in" in iframe.lower():
|
|
146
|
-
base_url = HTMLHelper(iframe).regex_first(r"https?://([^/]+)")
|
|
147
|
-
if base_url:
|
|
148
|
-
vid_id = iframe.split("/")[-1]
|
|
149
|
-
|
|
150
|
-
self.httpx.headers.update({"X-Requested-With": "XMLHttpRequest"})
|
|
151
|
-
vid_istek = await self.httpx.post(
|
|
152
|
-
f"https://{base_url}/player/index.php?data={vid_id}&do=getVideo",
|
|
153
|
-
)
|
|
154
|
-
vid_data = vid_istek.json()
|
|
155
|
-
|
|
156
|
-
if vid_data.get("securedLink"):
|
|
157
|
-
results.append(ExtractResult(
|
|
158
|
-
name = f"{self.name}",
|
|
159
|
-
url = vid_data["securedLink"],
|
|
160
|
-
referer = iframe,
|
|
161
|
-
subtitles = subtitles
|
|
162
|
-
))
|
|
163
|
-
else:
|
|
164
|
-
# Extractor'a yönlendir
|
|
165
|
-
data = await self.extract(iframe)
|
|
166
|
-
if data:
|
|
167
|
-
results.append(data)
|
|
229
|
+
results_groups = await asyncio.gather(*extract_tasks)
|
|
168
230
|
|
|
169
|
-
|
|
231
|
+
final_results = []
|
|
232
|
+
for group in results_groups:
|
|
233
|
+
if group:
|
|
234
|
+
final_results.extend(group)
|
|
235
|
+
|
|
236
|
+
# Duplicate Eliminasyonu
|
|
237
|
+
unique_results = []
|
|
238
|
+
seen = set()
|
|
239
|
+
for res in final_results:
|
|
240
|
+
key = (res.url, res.name)
|
|
241
|
+
if res.url and key not in seen:
|
|
242
|
+
unique_results.append(res)
|
|
243
|
+
seen.add(key)
|
|
244
|
+
|
|
245
|
+
return unique_results
|
KekikStream/Plugins/Sinezy.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
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
|
|
4
4
|
import base64
|
|
5
5
|
|
|
6
6
|
class Sinezy(PluginBase):
|
|
@@ -42,16 +42,15 @@ class Sinezy(PluginBase):
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
istek = await self.httpx.get(f"{url}page/{page}/")
|
|
46
|
+
secici = HTMLHelper(istek.text)
|
|
47
|
+
|
|
49
48
|
results = []
|
|
50
49
|
for item in secici.select("div.container div.content div.movie_box.move_k"):
|
|
51
50
|
title = secici.select_attr("a", "title", item)
|
|
52
51
|
href = secici.select_attr("a", "href", item)
|
|
53
52
|
poster = secici.select_attr("img", "data-src", item)
|
|
54
|
-
|
|
53
|
+
|
|
55
54
|
if title and href:
|
|
56
55
|
results.append(MainPageResult(
|
|
57
56
|
category = category,
|
|
@@ -63,9 +62,8 @@ class Sinezy(PluginBase):
|
|
|
63
62
|
return results
|
|
64
63
|
|
|
65
64
|
async def search(self, query: str) -> list[SearchResult]:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
secici = HTMLHelper(resp.text)
|
|
65
|
+
istek = await self.httpx.get(f"{self.main_url}/arama/?s={query}")
|
|
66
|
+
secici = HTMLHelper(istek.text)
|
|
69
67
|
|
|
70
68
|
results = []
|
|
71
69
|
for item in secici.select("div.movie_box.move_k"):
|
|
@@ -83,8 +81,8 @@ class Sinezy(PluginBase):
|
|
|
83
81
|
return results
|
|
84
82
|
|
|
85
83
|
async def load_item(self, url: str) -> MovieInfo:
|
|
86
|
-
|
|
87
|
-
secici = HTMLHelper(
|
|
84
|
+
istek = await self.httpx.get(url)
|
|
85
|
+
secici = HTMLHelper(istek.text)
|
|
88
86
|
|
|
89
87
|
title = secici.select_attr("div.detail", "title")
|
|
90
88
|
poster = secici.select_poster("div.move_k img")
|
|
@@ -108,19 +106,20 @@ class Sinezy(PluginBase):
|
|
|
108
106
|
)
|
|
109
107
|
|
|
110
108
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
111
|
-
|
|
112
|
-
secici = HTMLHelper(
|
|
109
|
+
istek = await self.httpx.get(url)
|
|
110
|
+
secici = HTMLHelper(istek.text)
|
|
113
111
|
|
|
114
112
|
encoded = secici.regex_first(r"ilkpartkod\s*=\s*'([^']+)'", secici.html)
|
|
113
|
+
name = secici.select_direct_text("li.pgrup a")
|
|
115
114
|
if encoded:
|
|
116
115
|
try:
|
|
117
|
-
decoded
|
|
116
|
+
decoded = base64.b64decode(encoded).decode('utf-8')
|
|
118
117
|
decoded_sec = HTMLHelper(decoded)
|
|
119
|
-
iframe
|
|
118
|
+
iframe = decoded_sec.select_attr('iframe', 'src')
|
|
120
119
|
|
|
121
120
|
if iframe:
|
|
122
121
|
iframe = self.fix_url(iframe)
|
|
123
|
-
data = await self.extract(iframe)
|
|
122
|
+
data = await self.extract(iframe, name_override=name)
|
|
124
123
|
if data:
|
|
125
124
|
return [data]
|
|
126
125
|
except Exception:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
|
|
4
4
|
|
|
5
5
|
class SuperFilmGeldi(PluginBase):
|
|
6
6
|
name = "SuperFilmGeldi"
|
|
@@ -42,7 +42,7 @@ class SuperFilmGeldi(PluginBase):
|
|
|
42
42
|
if not title_text:
|
|
43
43
|
continue
|
|
44
44
|
|
|
45
|
-
href
|
|
45
|
+
href = secici.select_attr("span.movie-title a", "href", veri)
|
|
46
46
|
poster = secici.select_attr("img", "src", veri)
|
|
47
47
|
|
|
48
48
|
results.append(MainPageResult(
|
|
@@ -64,7 +64,7 @@ class SuperFilmGeldi(PluginBase):
|
|
|
64
64
|
if not title_text:
|
|
65
65
|
continue
|
|
66
66
|
|
|
67
|
-
href
|
|
67
|
+
href = secici.select_attr("span.movie-title a", "href", veri)
|
|
68
68
|
poster = secici.select_attr("img", "src", veri)
|
|
69
69
|
|
|
70
70
|
results.append(SearchResult(
|
KekikStream/Plugins/UgurFilm.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
|
|
4
|
+
import asyncio, contextlib
|
|
4
5
|
|
|
5
6
|
class UgurFilm(PluginBase):
|
|
6
7
|
name = "UgurFilm"
|
|
@@ -33,7 +34,7 @@ class UgurFilm(PluginBase):
|
|
|
33
34
|
if not title:
|
|
34
35
|
continue
|
|
35
36
|
|
|
36
|
-
href
|
|
37
|
+
href = secici.select_attr("a", "href", veri)
|
|
37
38
|
poster = secici.select_attr("img", "src", veri)
|
|
38
39
|
|
|
39
40
|
results.append(MainPageResult(
|
|
@@ -51,8 +52,8 @@ class UgurFilm(PluginBase):
|
|
|
51
52
|
|
|
52
53
|
results = []
|
|
53
54
|
for film in secici.select("div.icerik div"):
|
|
54
|
-
title
|
|
55
|
-
href
|
|
55
|
+
title = secici.select_text("a.baslik span", film)
|
|
56
|
+
href = secici.select_attr("a", "href", film)
|
|
56
57
|
poster = secici.select_attr("img", "src", film)
|
|
57
58
|
|
|
58
59
|
if title and href:
|
|
@@ -95,26 +96,72 @@ class UgurFilm(PluginBase):
|
|
|
95
96
|
results = []
|
|
96
97
|
|
|
97
98
|
part_links = secici.select_attrs("li.parttab a", "href")
|
|
99
|
+
if not part_links:
|
|
100
|
+
part_links = [url]
|
|
98
101
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
iframe = sub_selector.select_attr("div#vast iframe", "src")
|
|
104
|
-
|
|
105
|
-
if iframe and self.main_url in iframe:
|
|
106
|
-
post_data = {
|
|
107
|
-
"vid" : iframe.split("vid=")[-1],
|
|
108
|
-
"alternative" : "vidmoly",
|
|
109
|
-
"ord" : "0",
|
|
110
|
-
}
|
|
111
|
-
player_response = await self.httpx.post(
|
|
102
|
+
async def process_alt(vid: str, alt_name: str, ord_val: str) -> list[ExtractResult]:
|
|
103
|
+
"""Alternatif player kaynağından video linkini çıkarır."""
|
|
104
|
+
with contextlib.suppress(Exception):
|
|
105
|
+
resp = await self.httpx.post(
|
|
112
106
|
url = f"{self.main_url}/player/ajax_sources.php",
|
|
113
|
-
data =
|
|
107
|
+
data = {"vid": vid, "alternative": alt_name, "ord": ord_val}
|
|
114
108
|
)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
109
|
+
if iframe_url := resp.json().get("iframe"):
|
|
110
|
+
data = await self.extract(self.fix_url(iframe_url))
|
|
111
|
+
if not data:
|
|
112
|
+
return []
|
|
113
|
+
|
|
114
|
+
return data if isinstance(data, list) else [data]
|
|
115
|
+
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
async def process_part(part_url: str) -> list[ExtractResult]:
|
|
119
|
+
"""Her bir part sayfasını ve alternatiflerini işler."""
|
|
120
|
+
try:
|
|
121
|
+
# Elimizde zaten olan ana sayfayı tekrar çekmemek için
|
|
122
|
+
if part_url == url:
|
|
123
|
+
sub_sec = secici
|
|
124
|
+
else:
|
|
125
|
+
sub_resp = await self.httpx.get(part_url)
|
|
126
|
+
sub_sec = HTMLHelper(sub_resp.text)
|
|
127
|
+
|
|
128
|
+
iframe = sub_sec.select_attr("div#vast iframe", "src")
|
|
129
|
+
if not iframe:
|
|
130
|
+
return []
|
|
131
|
+
|
|
132
|
+
if self.main_url not in iframe:
|
|
133
|
+
data = await self.extract(self.fix_url(iframe))
|
|
134
|
+
if not data:
|
|
135
|
+
return []
|
|
136
|
+
|
|
137
|
+
return data if isinstance(data, list) else [data]
|
|
138
|
+
|
|
139
|
+
# İç kaynaklı ise 3 alternatif için paralel istek at
|
|
140
|
+
vid = iframe.split("vid=")[-1]
|
|
141
|
+
tasks = [
|
|
142
|
+
process_alt(vid, "vidmoly", "0"),
|
|
143
|
+
process_alt(vid, "ok.ru", "1"),
|
|
144
|
+
process_alt(vid, "mailru", "2")
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
alt_results = await asyncio.gather(*tasks)
|
|
148
|
+
|
|
149
|
+
return [item for sublist in alt_results for item in sublist]
|
|
150
|
+
except Exception:
|
|
151
|
+
return []
|
|
152
|
+
|
|
153
|
+
# Tüm partları paralel işle
|
|
154
|
+
groups = await asyncio.gather(*(process_part(p) for p in part_links))
|
|
155
|
+
|
|
156
|
+
for group in groups:
|
|
157
|
+
results.extend(group)
|
|
158
|
+
|
|
159
|
+
# Duplicate Temizliği
|
|
160
|
+
unique_results = []
|
|
161
|
+
seen = set()
|
|
162
|
+
for res in results:
|
|
163
|
+
if res.url and res.url not in seen:
|
|
164
|
+
unique_results.append(res)
|
|
165
|
+
seen.add(res.url)
|
|
166
|
+
|
|
167
|
+
return unique_results
|
KekikStream/Plugins/Watch32.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, HTMLHelper
|
|
4
|
-
from json import dumps, loads
|
|
5
|
-
import re
|
|
6
4
|
|
|
7
5
|
class Watch32(PluginBase):
|
|
8
6
|
name = "Watch32"
|
|
@@ -39,7 +37,6 @@ class Watch32(PluginBase):
|
|
|
39
37
|
f"{main_url}/genre/western?page=" : "Western",
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
|
|
43
40
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
44
41
|
istek = await self.httpx.get(f"{url}{page}")
|
|
45
42
|
helper = HTMLHelper(istek.text)
|
|
@@ -52,44 +49,40 @@ class Watch32(PluginBase):
|
|
|
52
49
|
url = self.fix_url(helper.select_attr("h2.film-name a", "href", veri)),
|
|
53
50
|
poster = helper.select_attr("img.film-poster-img", "data-src", veri)
|
|
54
51
|
)
|
|
55
|
-
|
|
52
|
+
for veri in items
|
|
56
53
|
]
|
|
57
54
|
|
|
58
55
|
async def search(self, query: str) -> list[SearchResult]:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
istek = await self.httpx.get(url)
|
|
63
|
-
helper = HTMLHelper(istek.text)
|
|
64
|
-
items = helper.select("div.flw-item")
|
|
56
|
+
istek = await self.httpx.get(f"{self.main_url}/search/{query.replace(' ', '-')}")
|
|
57
|
+
secici = HTMLHelper(istek.text)
|
|
65
58
|
|
|
66
59
|
return [
|
|
67
60
|
SearchResult(
|
|
68
|
-
title =
|
|
69
|
-
url = self.fix_url(
|
|
70
|
-
poster =
|
|
61
|
+
title = secici.select_attr("h2.film-name a", "title", veri),
|
|
62
|
+
url = self.fix_url(secici.select_attr("h2.film-name a", "href", veri)),
|
|
63
|
+
poster = secici.select_attr("img.film-poster-img", "data-src", veri)
|
|
71
64
|
)
|
|
72
|
-
|
|
65
|
+
for veri in secici.select("div.flw-item")
|
|
73
66
|
]
|
|
74
67
|
|
|
75
68
|
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
76
69
|
istek = await self.httpx.get(url)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
content_id =
|
|
80
|
-
details =
|
|
81
|
-
name =
|
|
82
|
-
poster =
|
|
83
|
-
description =
|
|
84
|
-
year = str(
|
|
85
|
-
tags =
|
|
86
|
-
rating =
|
|
87
|
-
actors =
|
|
70
|
+
secici = HTMLHelper(istek.text)
|
|
71
|
+
|
|
72
|
+
content_id = secici.select_attr("div.detail_page-watch", "data-id")
|
|
73
|
+
details = secici.select_first("div.detail_page-infor")
|
|
74
|
+
name = secici.select_text("h2.heading-name > a", details)
|
|
75
|
+
poster = secici.select_poster("div.film-poster > img", details)
|
|
76
|
+
description = secici.select_text("div.description", details)
|
|
77
|
+
year = str(secici.extract_year())
|
|
78
|
+
tags = secici.meta_list("Genre", container_selector="div.row-line")
|
|
79
|
+
rating = secici.select_text("button.btn-imdb").replace("N/A", "").split(":")[-1].strip() if secici.select_text("button.btn-imdb") else None
|
|
80
|
+
actors = secici.meta_list("Casts", container_selector="div.row-line")
|
|
88
81
|
|
|
89
82
|
common_info = {
|
|
90
83
|
"url" : url,
|
|
91
84
|
"poster" : self.fix_url(poster),
|
|
92
|
-
"title" : name
|
|
85
|
+
"title" : name,
|
|
93
86
|
"description" : description,
|
|
94
87
|
"tags" : tags,
|
|
95
88
|
"rating" : rating,
|
|
@@ -103,32 +96,30 @@ class Watch32(PluginBase):
|
|
|
103
96
|
episodes = []
|
|
104
97
|
seasons_resp = await self.httpx.get(f"{self.main_url}/ajax/season/list/{content_id}")
|
|
105
98
|
sh = HTMLHelper(seasons_resp.text)
|
|
106
|
-
|
|
99
|
+
|
|
107
100
|
for season in sh.select("a.dropdown-item"):
|
|
108
101
|
season_id = season.attrs.get("data-id")
|
|
109
102
|
s_val, _ = sh.extract_season_episode(season.text())
|
|
110
|
-
|
|
103
|
+
|
|
111
104
|
e_resp = await self.httpx.get(f"{self.main_url}/ajax/season/episodes/{season_id}")
|
|
112
105
|
eh = HTMLHelper(e_resp.text)
|
|
113
|
-
|
|
106
|
+
|
|
114
107
|
for ep in eh.select("a.eps-item"):
|
|
115
108
|
ep_id = ep.attrs.get("data-id")
|
|
116
109
|
ep_title = ep.attrs.get("title", "")
|
|
117
110
|
_, e_val = eh.extract_season_episode(ep_title)
|
|
118
|
-
|
|
111
|
+
|
|
119
112
|
episodes.append(Episode(
|
|
120
113
|
season = s_val or 1,
|
|
121
114
|
episode = e_val or 1,
|
|
122
115
|
title = ep_title,
|
|
123
116
|
url = f"servers/{ep_id}"
|
|
124
117
|
))
|
|
125
|
-
|
|
118
|
+
|
|
126
119
|
return SeriesInfo(**common_info, episodes=episodes)
|
|
127
120
|
|
|
128
121
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
129
122
|
# url in load_links might be the full page URL for movies or "servers/epId" for episodes
|
|
130
|
-
if "servers/" in url:
|
|
131
|
-
data = url.split("/")[-1]
|
|
132
123
|
if "servers/" in url:
|
|
133
124
|
data = url.split("/")[-1]
|
|
134
125
|
servers_url = f"servers/{data}"
|
|
@@ -137,39 +128,33 @@ class Watch32(PluginBase):
|
|
|
137
128
|
servers_url = f"list/{data}"
|
|
138
129
|
else:
|
|
139
130
|
# Re-fetch page to get contentId only if we don't have list/ or servers/
|
|
140
|
-
istek
|
|
141
|
-
|
|
142
|
-
content_id =
|
|
131
|
+
istek = await self.httpx.get(url)
|
|
132
|
+
secici = HTMLHelper(istek.text)
|
|
133
|
+
content_id = secici.select_attr("div.detail_page-watch", "data-id")
|
|
143
134
|
if not content_id:
|
|
144
|
-
# Try to get id from url if direct parse fails, similar to Kotlin logic
|
|
145
|
-
# But Kotlin parses search first. Here we assume we are at the page.
|
|
146
|
-
# If no content_id found, maybe it's not a valid page or structure changed.
|
|
147
135
|
return []
|
|
148
136
|
servers_url = f"list/{content_id}"
|
|
149
137
|
|
|
150
138
|
servers_resp = await self.httpx.get(f"{self.main_url}/ajax/episode/{servers_url}")
|
|
151
|
-
sh
|
|
152
|
-
servers
|
|
153
|
-
|
|
139
|
+
sh = HTMLHelper(servers_resp.text)
|
|
140
|
+
servers = sh.select("a.link-item")
|
|
141
|
+
|
|
154
142
|
results = []
|
|
155
143
|
for server in servers:
|
|
156
|
-
|
|
144
|
+
server_name = server.text(strip=True)
|
|
145
|
+
link_id = server.attrs.get("data-linkid") or server.attrs.get("data-id")
|
|
157
146
|
source_resp = await self.httpx.get(f"{self.main_url}/ajax/episode/sources/{link_id}")
|
|
158
147
|
source_data = source_resp.json()
|
|
159
|
-
video_url
|
|
160
|
-
|
|
148
|
+
video_url = source_data.get("link")
|
|
149
|
+
|
|
161
150
|
if video_url:
|
|
162
|
-
|
|
163
|
-
extract_result = await self.extract(video_url)
|
|
151
|
+
extract_result = await self.extract(video_url, name_override=server_name)
|
|
164
152
|
if extract_result:
|
|
165
|
-
if isinstance(extract_result, list)
|
|
166
|
-
results.extend(extract_result)
|
|
167
|
-
else:
|
|
168
|
-
results.append(extract_result)
|
|
153
|
+
results.extend(extract_result if isinstance(extract_result, list) else [extract_result])
|
|
169
154
|
else:
|
|
170
155
|
results.append(ExtractResult(
|
|
171
156
|
url = video_url,
|
|
172
|
-
name = f"{self.name} | {
|
|
157
|
+
name = f"{self.name} | {server_name}"
|
|
173
158
|
))
|
|
174
|
-
|
|
159
|
+
|
|
175
160
|
return results
|