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.
- 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
|
@@ -144,14 +144,20 @@ class PluginBase(ABC):
|
|
|
144
144
|
if name_override:
|
|
145
145
|
item.name = name_override
|
|
146
146
|
elif prefix and item.name:
|
|
147
|
-
item.name
|
|
147
|
+
if item.name.lower() in prefix.lower():
|
|
148
|
+
item.name = prefix
|
|
149
|
+
else:
|
|
150
|
+
item.name = f"{prefix} | {item.name}"
|
|
148
151
|
return data
|
|
149
152
|
|
|
150
153
|
# Tekil öğe ise
|
|
151
154
|
if name_override:
|
|
152
155
|
data.name = name_override
|
|
153
156
|
elif prefix and data.name:
|
|
154
|
-
data.name
|
|
157
|
+
if data.name.lower() in prefix.lower():
|
|
158
|
+
data.name = prefix
|
|
159
|
+
else:
|
|
160
|
+
data.name = f"{prefix} | {data.name}"
|
|
155
161
|
|
|
156
162
|
return data
|
|
157
163
|
except Exception as hata:
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
|
|
4
|
+
|
|
5
|
+
class Abstream(ExtractorBase):
|
|
6
|
+
name = "Abstream"
|
|
7
|
+
main_url = "https://abstream.to"
|
|
8
|
+
|
|
9
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
10
|
+
istek = await self.httpx.get(
|
|
11
|
+
url = url,
|
|
12
|
+
headers = {
|
|
13
|
+
"Accept-Language" : "en-US,en;q=0.5",
|
|
14
|
+
"Referer" : referer or self.main_url,
|
|
15
|
+
}
|
|
16
|
+
)
|
|
17
|
+
secici = HTMLHelper(istek.text)
|
|
18
|
+
video_url = secici.regex_first(r'file:"([^"]*)"')
|
|
19
|
+
|
|
20
|
+
if not video_url:
|
|
21
|
+
raise ValueError(f"Abstream: Video URL bulunamadı. {url}")
|
|
22
|
+
|
|
23
|
+
return ExtractResult(
|
|
24
|
+
name = self.name,
|
|
25
|
+
url = video_url,
|
|
26
|
+
referer = referer or self.main_url
|
|
27
|
+
)
|
|
@@ -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
|
-
import base64
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, Subtitle, HTMLHelper
|
|
4
|
+
import base64, asyncio, contextlib
|
|
5
5
|
|
|
6
6
|
class KultFilmler(PluginBase):
|
|
7
7
|
name = "KultFilmler"
|
|
@@ -99,91 +99,159 @@ class KultFilmler(PluginBase):
|
|
|
99
99
|
episodes.append(Episode(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
|
|
100
100
|
|
|
101
101
|
return SeriesInfo(
|
|
102
|
-
url=url,
|
|
103
|
-
|
|
102
|
+
url = url,
|
|
103
|
+
poster = poster,
|
|
104
|
+
title = title,
|
|
105
|
+
description = description,
|
|
106
|
+
tags = tags,
|
|
107
|
+
year = year,
|
|
108
|
+
actors = actors,
|
|
109
|
+
rating = rating,
|
|
110
|
+
episodes = episodes
|
|
104
111
|
)
|
|
105
112
|
|
|
106
113
|
return MovieInfo(
|
|
107
|
-
url=url,
|
|
108
|
-
|
|
114
|
+
url = url,
|
|
115
|
+
poster = poster,
|
|
116
|
+
title = title,
|
|
117
|
+
description = description,
|
|
118
|
+
tags = tags,
|
|
119
|
+
year = year,
|
|
120
|
+
rating = rating,
|
|
121
|
+
actors = actors,
|
|
122
|
+
duration = duration
|
|
109
123
|
)
|
|
110
124
|
|
|
111
|
-
def
|
|
112
|
-
"""Base64
|
|
113
|
-
|
|
114
|
-
if not
|
|
115
|
-
return
|
|
125
|
+
def _decode_iframe(self, content: str) -> str | None:
|
|
126
|
+
"""Base64 kodlanmış iframe verisini çözer"""
|
|
127
|
+
match = HTMLHelper(content).regex_first(r"PHA\+[0-9a-zA-Z+/=]*")
|
|
128
|
+
if not match:
|
|
129
|
+
return None
|
|
116
130
|
|
|
117
|
-
# Padding
|
|
118
|
-
|
|
119
|
-
if
|
|
120
|
-
|
|
131
|
+
# Base64 Padding Fix
|
|
132
|
+
pad = len(match) % 4
|
|
133
|
+
if pad:
|
|
134
|
+
match += "=" * (4 - pad)
|
|
121
135
|
|
|
122
136
|
try:
|
|
123
|
-
decoded = base64.b64decode(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return self.fix_url(iframe_src) if iframe_src else ""
|
|
137
|
+
decoded = base64.b64decode(match).decode("utf-8")
|
|
138
|
+
src = HTMLHelper(decoded).select_attr("iframe", "src")
|
|
139
|
+
return self.fix_url(src) if src else None
|
|
127
140
|
except Exception:
|
|
128
|
-
return
|
|
141
|
+
return None
|
|
129
142
|
|
|
130
|
-
def
|
|
131
|
-
"""
|
|
132
|
-
|
|
143
|
+
async def _resolve_alt_page(self, url: str, title: str) -> tuple[str | None, str]:
|
|
144
|
+
"""Alternatif sayfa kaynak kodunu indirip iframe'i bulur"""
|
|
145
|
+
try:
|
|
146
|
+
res = await self.httpx.get(url)
|
|
147
|
+
return self._decode_iframe(res.text), title
|
|
148
|
+
except Exception:
|
|
149
|
+
return None, title
|
|
150
|
+
|
|
151
|
+
async def _extract_stream(self, iframe_url: str, title: str, subtitles: list[Subtitle]) -> list[ExtractResult]:
|
|
152
|
+
"""Iframe üzerinden stream linklerini ayıklar"""
|
|
153
|
+
results = []
|
|
154
|
+
|
|
155
|
+
# 1. VidMoly Özel Çözümleme(M3U)
|
|
156
|
+
if "vidmoly" in iframe_url:
|
|
157
|
+
with contextlib.suppress(Exception):
|
|
158
|
+
res = await self.httpx.get(
|
|
159
|
+
url = iframe_url,
|
|
160
|
+
headers = {
|
|
161
|
+
"User-Agent" : "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36",
|
|
162
|
+
"Sec-Fetch-Dest" : "iframe"
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
m3u = HTMLHelper(res.text).regex_first(r'file:"([^"]+)"')
|
|
166
|
+
|
|
167
|
+
if m3u:
|
|
168
|
+
results.append(ExtractResult(
|
|
169
|
+
name = title or "VidMoly",
|
|
170
|
+
url = m3u,
|
|
171
|
+
referer = self.main_url,
|
|
172
|
+
subtitles = subtitles
|
|
173
|
+
))
|
|
174
|
+
|
|
175
|
+
return results
|
|
176
|
+
|
|
177
|
+
# 2. Genel Extractor Kullanımı
|
|
178
|
+
with contextlib.suppress(Exception):
|
|
179
|
+
extracted = await self.extract(iframe_url)
|
|
180
|
+
if not extracted:
|
|
181
|
+
return []
|
|
182
|
+
|
|
183
|
+
items = extracted if isinstance(extracted, list) else [extracted]
|
|
184
|
+
for item in items:
|
|
185
|
+
# İsim ve altyazı bilgilerini güncelle
|
|
186
|
+
# Orijinal extractor ismini ezmek için title kullan
|
|
187
|
+
if title:
|
|
188
|
+
item.name = title
|
|
189
|
+
|
|
190
|
+
# Varsa altyazıları ekle
|
|
191
|
+
if subtitles:
|
|
192
|
+
# Copy update daha güvenli (Pydantic model)
|
|
193
|
+
if hasattr(item, "model_copy"):
|
|
194
|
+
item = item.model_copy(update={"subtitles": subtitles})
|
|
195
|
+
else:
|
|
196
|
+
item.subtitles = subtitles
|
|
197
|
+
|
|
198
|
+
results.append(item)
|
|
199
|
+
|
|
200
|
+
return results
|
|
133
201
|
|
|
134
202
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
135
|
-
|
|
136
|
-
|
|
203
|
+
response = await self.httpx.get(url)
|
|
204
|
+
source = response.text
|
|
205
|
+
helper = HTMLHelper(source)
|
|
137
206
|
|
|
138
|
-
|
|
207
|
+
# Altyazı Bul
|
|
208
|
+
sub_url = helper.regex_first(r"(https?://[^\s\"]+\.srt)")
|
|
209
|
+
subtitles = [Subtitle(name="Türkçe", url=sub_url)] if sub_url else []
|
|
139
210
|
|
|
140
|
-
#
|
|
141
|
-
|
|
142
|
-
if main_frame:
|
|
143
|
-
iframes.add(main_frame)
|
|
211
|
+
# İşlenecek kaynakları topla: (Iframe_URL, Başlık)
|
|
212
|
+
sources = []
|
|
144
213
|
|
|
145
|
-
#
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if alt_frame:
|
|
153
|
-
iframes.add(alt_frame)
|
|
214
|
+
# A) Ana Player
|
|
215
|
+
main_iframe = self._decode_iframe(source)
|
|
216
|
+
if main_iframe:
|
|
217
|
+
p_name = helper.select_text("div.parts-middle div.part.active div.part-name") or None
|
|
218
|
+
p_lang = helper.select_attr("div.parts-middle div.part.active div.part-lang span", "title")
|
|
219
|
+
full_title = f"{p_name} | {p_lang}" if p_lang else p_name
|
|
220
|
+
sources.append((main_iframe, full_title))
|
|
154
221
|
|
|
155
|
-
|
|
222
|
+
# B) Alternatif Playerlar (Link Çözümleme Gerektirir)
|
|
223
|
+
alt_tasks = []
|
|
224
|
+
for link in helper.select("div.parts-middle a.post-page-numbers"):
|
|
225
|
+
href = link.attrs.get("href")
|
|
226
|
+
if not href:
|
|
227
|
+
continue
|
|
156
228
|
|
|
157
|
-
|
|
158
|
-
|
|
229
|
+
a_name = helper.select_text("div.part-name", link) or "Alternatif"
|
|
230
|
+
a_lang = helper.select_attr("div.part-lang span", "title", link)
|
|
231
|
+
full_title = f"{a_name} | {a_lang}" if a_lang else a_name
|
|
159
232
|
|
|
160
|
-
|
|
161
|
-
if "vidmoly" in iframe:
|
|
162
|
-
headers = {
|
|
163
|
-
"User-Agent" : "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36",
|
|
164
|
-
"Sec-Fetch-Dest" : "iframe"
|
|
165
|
-
}
|
|
166
|
-
iframe_istek = await self.httpx.get(iframe, headers=headers)
|
|
167
|
-
m3u_match = HTMLHelper(iframe_istek.text).regex_first(r'file:"([^"]+)"')
|
|
233
|
+
alt_tasks.append(self._resolve_alt_page(self.fix_url(href), full_title))
|
|
168
234
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
subtitles = []
|
|
175
|
-
))
|
|
176
|
-
continue
|
|
235
|
+
if alt_tasks:
|
|
236
|
+
resolved_alts = await asyncio.gather(*alt_tasks)
|
|
237
|
+
for iframe, title in resolved_alts:
|
|
238
|
+
if iframe:
|
|
239
|
+
sources.append((iframe, title))
|
|
177
240
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
subtitles.append(Subtitle(name="Türkçe", url=subtitle_url))
|
|
241
|
+
# 3. Tüm kaynakları paralel işle (Extract)
|
|
242
|
+
if not sources:
|
|
243
|
+
return []
|
|
182
244
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
results.append(updated_data)
|
|
245
|
+
extract_tasks = [
|
|
246
|
+
self._extract_stream(iframe, title, subtitles)
|
|
247
|
+
for iframe, title in sources
|
|
248
|
+
]
|
|
188
249
|
|
|
189
|
-
|
|
250
|
+
results_groups = await asyncio.gather(*extract_tasks)
|
|
251
|
+
|
|
252
|
+
# Sonuçları düzleştir
|
|
253
|
+
final_results = []
|
|
254
|
+
for group in results_groups:
|
|
255
|
+
final_results.extend(group)
|
|
256
|
+
|
|
257
|
+
return final_results
|
KekikStream/Plugins/RecTV.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Episode, SeriesInfo, ExtractResult, HTMLHelper
|
|
4
4
|
from json import dumps, loads
|
|
5
|
-
import re
|
|
5
|
+
import re, contextlib
|
|
6
6
|
|
|
7
7
|
class RecTV(PluginBase):
|
|
8
8
|
name = "RecTV"
|
|
@@ -73,11 +73,10 @@ class RecTV(PluginBase):
|
|
|
73
73
|
duration_raw = veri.get("duration")
|
|
74
74
|
duration = None
|
|
75
75
|
if duration_raw:
|
|
76
|
-
|
|
76
|
+
with contextlib.suppress(Exception):
|
|
77
77
|
h = int(HTMLHelper(duration_raw).regex_first(r"(\d+)h") or 0)
|
|
78
78
|
m = int(HTMLHelper(duration_raw).regex_first(r"(\d+)min") or 0)
|
|
79
79
|
duration = h * 60 + m
|
|
80
|
-
except: pass
|
|
81
80
|
|
|
82
81
|
common_info = {
|
|
83
82
|
"url" : url,
|
|
@@ -110,10 +109,15 @@ class RecTV(PluginBase):
|
|
|
110
109
|
tag = " (Altyazı)"; clean_s = re.sub(r"\s*altyaz[ıi]\s*", "", s_title, flags=re.I).strip()
|
|
111
110
|
|
|
112
111
|
ep_data = {"url": self.fix_url(source.get("url")), "title": f"{veri.get('title')} | {s_title} {e_title} - {source.get('title')}", "is_episode": True}
|
|
113
|
-
episodes.append(Episode(
|
|
112
|
+
episodes.append(Episode(
|
|
113
|
+
season = s or 1,
|
|
114
|
+
episode = e or 1,
|
|
115
|
+
title = f"{clean_s} {e_title}{tag} - {source.get('title')}",
|
|
116
|
+
url = dumps(ep_data)
|
|
117
|
+
))
|
|
114
118
|
|
|
115
119
|
return SeriesInfo(**common_info, episodes=episodes, actors=[])
|
|
116
|
-
|
|
120
|
+
|
|
117
121
|
return MovieInfo(**common_info, actors=[])
|
|
118
122
|
|
|
119
123
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
KekikStream/Plugins/RoketDizi.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, SeriesInfo, Episode, ExtractResult, MovieInfo, HTMLHelper
|
|
4
4
|
import base64, json
|
|
5
5
|
|
|
6
6
|
class RoketDizi(PluginBase):
|
|
@@ -26,8 +26,6 @@ class RoketDizi(PluginBase):
|
|
|
26
26
|
secici = HTMLHelper(istek.text)
|
|
27
27
|
|
|
28
28
|
results = []
|
|
29
|
-
|
|
30
|
-
# Use div.new-added-list to find the container, then get items
|
|
31
29
|
for item in secici.select("div.new-added-list > span"):
|
|
32
30
|
title = secici.select_text("span.line-clamp-1", item)
|
|
33
31
|
href = secici.select_attr("a", "href", item)
|
|
@@ -52,7 +50,7 @@ class RoketDizi(PluginBase):
|
|
|
52
50
|
"Referer" : f"{self.main_url}/",
|
|
53
51
|
}
|
|
54
52
|
)
|
|
55
|
-
|
|
53
|
+
|
|
56
54
|
try:
|
|
57
55
|
veri = istek.json()
|
|
58
56
|
encoded = veri.get("response", "")
|
|
@@ -87,39 +85,33 @@ class RoketDizi(PluginBase):
|
|
|
87
85
|
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
88
86
|
resp = await self.httpx.get(url)
|
|
89
87
|
sel = HTMLHelper(resp.text)
|
|
90
|
-
|
|
88
|
+
|
|
91
89
|
next_data_text = sel.select_text("script#__NEXT_DATA__")
|
|
92
90
|
if not next_data_text:
|
|
93
91
|
return SeriesInfo(url=url, title=sel.select_text("h1") or "Bilinmeyen")
|
|
94
92
|
|
|
95
93
|
try:
|
|
96
|
-
next_data
|
|
94
|
+
next_data = json.loads(next_data_text)
|
|
97
95
|
secure_data_raw = next_data["props"]["pageProps"]["secureData"]
|
|
98
|
-
secure_data
|
|
99
|
-
|
|
96
|
+
secure_data = json.loads(base64.b64decode(secure_data_raw).decode('utf-8'))
|
|
97
|
+
|
|
100
98
|
content_item = secure_data.get("contentItem", {})
|
|
101
99
|
content = secure_data.get("content", {}).get("result", {})
|
|
102
|
-
|
|
100
|
+
|
|
103
101
|
title = content_item.get("original_title") or content_item.get("culture_title")
|
|
104
102
|
poster = content_item.get("poster_url") or content_item.get("face_url")
|
|
105
103
|
description = content_item.get("description")
|
|
106
104
|
rating = str(content_item.get("imdb_point") or "")
|
|
107
105
|
year = str(content_item.get("release_year") or "")
|
|
108
106
|
tags = content_item.get("categories", "").split(",")
|
|
109
|
-
|
|
110
|
-
# Actors extraction from getSerieCastsById or getMovieCastsById
|
|
107
|
+
|
|
111
108
|
actors = []
|
|
112
109
|
casts_data = content.get("getSerieCastsById") or content.get("getMovieCastsById")
|
|
113
110
|
if casts_data and casts_data.get("result"):
|
|
114
111
|
actors = [cast.get("name") for cast in casts_data["result"] if cast.get("name")]
|
|
115
112
|
|
|
116
|
-
# Episodes extraction
|
|
117
113
|
episodes = []
|
|
118
114
|
if "Series" in str(content.get("FindedType")):
|
|
119
|
-
# Check for episodes in SecureData -> RelatedResults -> getEpisodeSources (this might be for the current episode)
|
|
120
|
-
# Usually full episode list isn't in secureData, but we can get it from HTML or another API
|
|
121
|
-
# However, many times Next.js pages have them in props
|
|
122
|
-
# Let's fallback to the previous regex method for episodes if not in JSON
|
|
123
115
|
all_urls = HTMLHelper(resp.text).regex_all(r'"url":"([^"]*)"')
|
|
124
116
|
episodes_dict = {}
|
|
125
117
|
for u in all_urls:
|
|
@@ -169,17 +161,16 @@ class RoketDizi(PluginBase):
|
|
|
169
161
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
170
162
|
resp = await self.httpx.get(url)
|
|
171
163
|
sel = HTMLHelper(resp.text)
|
|
172
|
-
|
|
164
|
+
|
|
173
165
|
next_data = sel.select_text("script#__NEXT_DATA__")
|
|
174
166
|
if not next_data:
|
|
175
167
|
return []
|
|
176
168
|
|
|
177
169
|
try:
|
|
178
|
-
data
|
|
179
|
-
secure_data
|
|
170
|
+
data = json.loads(next_data)
|
|
171
|
+
secure_data = data["props"]["pageProps"]["secureData"]
|
|
180
172
|
decoded_json = json.loads(base64.b64decode(secure_data).decode('utf-8'))
|
|
181
173
|
|
|
182
|
-
# secureData içindeki RelatedResults -> getEpisodeSources -> result dizisini al
|
|
183
174
|
sources = decoded_json.get("RelatedResults", {}).get("getEpisodeSources", {}).get("result", [])
|
|
184
175
|
|
|
185
176
|
seen_urls = set()
|
|
@@ -200,8 +191,8 @@ class RoketDizi(PluginBase):
|
|
|
200
191
|
iframe_url = "https://" + iframe_url
|
|
201
192
|
|
|
202
193
|
iframe_url = self.fix_url(iframe_url)
|
|
203
|
-
|
|
204
|
-
# Deduplicate
|
|
194
|
+
|
|
195
|
+
# Deduplicate
|
|
205
196
|
if iframe_url in seen_urls:
|
|
206
197
|
continue
|
|
207
198
|
seen_urls.add(iframe_url)
|
|
@@ -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
|
-
import
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, HTMLHelper
|
|
4
|
+
import asyncio, contextlib
|
|
5
5
|
|
|
6
6
|
class SetFilmIzle(PluginBase):
|
|
7
7
|
name = "SetFilmIzle"
|
|
@@ -35,7 +35,7 @@ class SetFilmIzle(PluginBase):
|
|
|
35
35
|
|
|
36
36
|
def _get_nonce(self, nonce_type: str = "video", referer: str = None) -> str:
|
|
37
37
|
"""Site cache'lenmiş nonce'ları expire olabiliyor, fresh nonce al veya sayfadan çek"""
|
|
38
|
-
|
|
38
|
+
with contextlib.suppress(Exception):
|
|
39
39
|
resp = self.cloudscraper.post(
|
|
40
40
|
f"{self.main_url}/wp-admin/admin-ajax.php",
|
|
41
41
|
headers = {
|
|
@@ -49,17 +49,15 @@ class SetFilmIzle(PluginBase):
|
|
|
49
49
|
if data and data.get("success"):
|
|
50
50
|
nonces = data.get("data", {}).get("nonces", {})
|
|
51
51
|
return nonces.get(nonce_type if nonce_type != "search" else "dt_ajax_search", "")
|
|
52
|
-
except:
|
|
53
|
-
pass
|
|
54
52
|
|
|
55
53
|
# AJAX başarısızsa sayfadan çekmeyi dene
|
|
56
|
-
|
|
54
|
+
with contextlib.suppress(Exception):
|
|
57
55
|
main_resp = self.cloudscraper.get(referer or self.main_url)
|
|
58
56
|
# STMOVIE_AJAX = { ... nonces: { search: "...", ... } }
|
|
59
57
|
nonce = HTMLHelper(main_resp.text).regex_first(rf'"{nonce_type}":\s*"([^"]+)"')
|
|
60
58
|
return nonce or ""
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
|
|
60
|
+
return ""
|
|
63
61
|
|
|
64
62
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
65
63
|
istek = self.cloudscraper.get(url)
|
|
@@ -106,8 +104,8 @@ class SetFilmIzle(PluginBase):
|
|
|
106
104
|
return []
|
|
107
105
|
|
|
108
106
|
secici = HTMLHelper(html)
|
|
109
|
-
results = []
|
|
110
107
|
|
|
108
|
+
results = []
|
|
111
109
|
for item in secici.select("div.items article"):
|
|
112
110
|
title = secici.select_text("h2", item)
|
|
113
111
|
href = secici.select_attr("a", "href", item)
|
|
@@ -175,14 +173,14 @@ class SetFilmIzle(PluginBase):
|
|
|
175
173
|
semaphore = asyncio.Semaphore(5)
|
|
176
174
|
tasks = []
|
|
177
175
|
|
|
178
|
-
async def fetch_and_extract(player):
|
|
176
|
+
async def fetch_and_extract(player) -> list[ExtractResult]:
|
|
179
177
|
async with semaphore:
|
|
180
178
|
source_id = player.attrs.get("data-post-id")
|
|
181
|
-
player_name = player.attrs.get("data-player-name")
|
|
179
|
+
player_name = player.attrs.get("data-player-name") or secici.select_text("b", player)
|
|
182
180
|
part_key = player.attrs.get("data-part-key")
|
|
183
181
|
|
|
184
182
|
if not source_id or "event" in source_id or source_id == "":
|
|
185
|
-
return
|
|
183
|
+
return []
|
|
186
184
|
|
|
187
185
|
try:
|
|
188
186
|
resp = self.cloudscraper.post(
|
|
@@ -192,17 +190,17 @@ class SetFilmIzle(PluginBase):
|
|
|
192
190
|
"action" : "get_video_url",
|
|
193
191
|
"nonce" : nonce,
|
|
194
192
|
"post_id" : source_id,
|
|
195
|
-
"player_name" :
|
|
193
|
+
"player_name" : player.attrs.get("data-player-name") or "",
|
|
196
194
|
"part_key" : part_key or ""
|
|
197
195
|
}
|
|
198
196
|
)
|
|
199
197
|
data = resp.json()
|
|
200
198
|
except:
|
|
201
|
-
return
|
|
199
|
+
return []
|
|
202
200
|
|
|
203
201
|
iframe_url = data.get("data", {}).get("url")
|
|
204
202
|
if not iframe_url:
|
|
205
|
-
return
|
|
203
|
+
return []
|
|
206
204
|
|
|
207
205
|
if "setplay" not in iframe_url and part_key:
|
|
208
206
|
iframe_url = f"{iframe_url}?partKey={part_key}"
|
|
@@ -211,10 +209,40 @@ class SetFilmIzle(PluginBase):
|
|
|
211
209
|
if not label and part_key:
|
|
212
210
|
label = part_key.replace("_", " ").title()
|
|
213
211
|
|
|
214
|
-
|
|
212
|
+
# İsimlendirme Formatı: "FastPlay | Türkçe Dublaj"
|
|
213
|
+
final_name = player_name
|
|
214
|
+
if label:
|
|
215
|
+
final_name = f"{final_name} | {label}" if final_name else label
|
|
215
216
|
|
|
216
|
-
|
|
217
|
+
# Extract et
|
|
218
|
+
extracted = await self.extract(iframe_url)
|
|
219
|
+
if not extracted:
|
|
220
|
+
return []
|
|
221
|
+
|
|
222
|
+
results = []
|
|
223
|
+
items = extracted if isinstance(extracted, list) else [extracted]
|
|
224
|
+
for item in items:
|
|
225
|
+
if final_name:
|
|
226
|
+
item.name = final_name
|
|
227
|
+
results.append(item)
|
|
228
|
+
|
|
229
|
+
return results
|
|
230
|
+
|
|
231
|
+
# Selector Güncellemesi: data-player-name içeren tüm a tagleri
|
|
232
|
+
players = secici.select("a[data-player-name]")
|
|
233
|
+
if not players:
|
|
234
|
+
# Fallback legacy selector
|
|
235
|
+
players = secici.select("nav.player a")
|
|
236
|
+
|
|
237
|
+
for player in players:
|
|
217
238
|
tasks.append(fetch_and_extract(player))
|
|
218
239
|
|
|
219
|
-
|
|
220
|
-
|
|
240
|
+
results_groups = await asyncio.gather(*tasks)
|
|
241
|
+
|
|
242
|
+
# Flatten
|
|
243
|
+
final_results = []
|
|
244
|
+
for group in results_groups:
|
|
245
|
+
if group:
|
|
246
|
+
final_results.extend(group)
|
|
247
|
+
|
|
248
|
+
return final_results
|