KekikStream 2.3.9__py3-none-any.whl → 2.5.3__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 +3 -2
- KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
- KekikStream/Core/HTMLHelper.py +120 -49
- KekikStream/Core/Plugin/PluginBase.py +30 -12
- KekikStream/Core/Plugin/PluginLoader.py +12 -14
- KekikStream/Core/Plugin/PluginManager.py +2 -2
- KekikStream/Core/Plugin/PluginModels.py +0 -3
- KekikStream/Extractors/Abstream.py +27 -0
- KekikStream/Extractors/CloseLoad.py +30 -54
- KekikStream/Extractors/ContentX.py +27 -72
- KekikStream/Extractors/DonilasPlay.py +33 -77
- KekikStream/Extractors/DzenRu.py +10 -24
- KekikStream/Extractors/ExPlay.py +20 -38
- KekikStream/Extractors/Filemoon.py +21 -46
- KekikStream/Extractors/HDMomPlayer.py +30 -0
- KekikStream/Extractors/HDPlayerSystem.py +13 -31
- KekikStream/Extractors/HotStream.py +27 -0
- KekikStream/Extractors/JFVid.py +3 -24
- KekikStream/Extractors/JetTv.py +21 -34
- KekikStream/Extractors/JetV.py +55 -0
- KekikStream/Extractors/MailRu.py +11 -29
- KekikStream/Extractors/MixPlayHD.py +15 -28
- KekikStream/Extractors/MixTiger.py +17 -40
- KekikStream/Extractors/MolyStream.py +17 -21
- KekikStream/Extractors/Odnoklassniki.py +40 -104
- KekikStream/Extractors/PeaceMakerst.py +18 -45
- KekikStream/Extractors/PixelDrain.py +8 -16
- KekikStream/Extractors/PlayerFilmIzle.py +22 -41
- KekikStream/Extractors/RapidVid.py +21 -35
- KekikStream/Extractors/SetPlay.py +18 -43
- KekikStream/Extractors/SibNet.py +7 -17
- KekikStream/Extractors/Sobreatsesuyp.py +23 -45
- KekikStream/Extractors/TRsTX.py +23 -53
- KekikStream/Extractors/TurboImgz.py +7 -14
- KekikStream/Extractors/VCTPlay.py +10 -28
- KekikStream/Extractors/Veev.py +145 -0
- KekikStream/Extractors/VidBiz.py +62 -0
- KekikStream/Extractors/VidHide.py +58 -30
- KekikStream/Extractors/VidMoly.py +65 -99
- KekikStream/Extractors/VidMoxy.py +16 -27
- KekikStream/Extractors/VidPapi.py +24 -54
- KekikStream/Extractors/VideoSeyred.py +19 -40
- KekikStream/Extractors/Videostr.py +58 -0
- KekikStream/Extractors/Vidoza.py +18 -0
- KekikStream/Extractors/Vtbe.py +38 -0
- KekikStream/Extractors/YTDLP.py +2 -2
- KekikStream/Extractors/YildizKisaFilm.py +13 -31
- KekikStream/Extractors/Zeus.py +61 -0
- KekikStream/Plugins/BelgeselX.py +97 -77
- KekikStream/Plugins/DiziBox.py +28 -45
- KekikStream/Plugins/DiziMom.py +179 -0
- KekikStream/Plugins/DiziPal.py +95 -161
- KekikStream/Plugins/DiziYou.py +51 -147
- KekikStream/Plugins/Dizilla.py +40 -61
- KekikStream/Plugins/FilmBip.py +90 -39
- KekikStream/Plugins/FilmEkseni.py +199 -0
- KekikStream/Plugins/FilmMakinesi.py +72 -73
- KekikStream/Plugins/FilmModu.py +25 -35
- KekikStream/Plugins/Filmatek.py +184 -0
- KekikStream/Plugins/FilmciBaba.py +155 -0
- KekikStream/Plugins/FullHDFilmizlesene.py +16 -37
- KekikStream/Plugins/HDFilm.py +243 -0
- KekikStream/Plugins/HDFilmCehennemi.py +242 -189
- KekikStream/Plugins/JetFilmizle.py +101 -69
- KekikStream/Plugins/KultFilmler.py +138 -104
- KekikStream/Plugins/RecTV.py +52 -73
- KekikStream/Plugins/RoketDizi.py +18 -27
- KekikStream/Plugins/SelcukFlix.py +30 -48
- KekikStream/Plugins/SetFilmIzle.py +76 -104
- KekikStream/Plugins/SezonlukDizi.py +90 -94
- KekikStream/Plugins/Sinefy.py +195 -167
- KekikStream/Plugins/SinemaCX.py +148 -78
- KekikStream/Plugins/Sinezy.py +29 -31
- KekikStream/Plugins/SuperFilmGeldi.py +12 -17
- KekikStream/Plugins/UgurFilm.py +85 -38
- KekikStream/Plugins/Watch32.py +160 -0
- KekikStream/Plugins/YabanciDizi.py +176 -211
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/METADATA +1 -1
- kekikstream-2.5.3.dist-info/RECORD +99 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/WHEEL +1 -1
- KekikStream/Plugins/FullHDFilm.py +0 -249
- kekikstream-2.3.9.dist-info/RECORD +0 -84
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/top_level.txt +0 -0
|
@@ -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)
|
|
@@ -76,7 +74,7 @@ class SetFilmIzle(PluginBase):
|
|
|
76
74
|
category = category,
|
|
77
75
|
title = title,
|
|
78
76
|
url = self.fix_url(href),
|
|
79
|
-
poster = self.fix_url(poster)
|
|
77
|
+
poster = self.fix_url(poster)
|
|
80
78
|
))
|
|
81
79
|
|
|
82
80
|
return results
|
|
@@ -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)
|
|
@@ -117,7 +115,7 @@ class SetFilmIzle(PluginBase):
|
|
|
117
115
|
results.append(SearchResult(
|
|
118
116
|
title = title,
|
|
119
117
|
url = self.fix_url(href),
|
|
120
|
-
poster = self.fix_url(poster)
|
|
118
|
+
poster = self.fix_url(poster)
|
|
121
119
|
))
|
|
122
120
|
|
|
123
121
|
return results
|
|
@@ -125,95 +123,39 @@ class SetFilmIzle(PluginBase):
|
|
|
125
123
|
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
126
124
|
istek = self.cloudscraper.get(url)
|
|
127
125
|
secici = HTMLHelper(istek.text)
|
|
128
|
-
html_text = istek.text
|
|
129
|
-
|
|
130
|
-
raw_title = secici.select_text("h1") or secici.select_text(".titles h1") or secici.select_attr("meta[property='og:title']", "content") or ""
|
|
131
|
-
title = HTMLHelper(raw_title).regex_replace(r"(?i)\s*izle.*$", "", flags=0).strip()
|
|
132
|
-
|
|
133
|
-
poster = secici.select_attr("div.poster img", "src")
|
|
134
126
|
|
|
127
|
+
title = self.clean_title(secici.select_text("h1") or secici.select_text(".titles h1") or secici.select_attr("meta[property='og:title']", "content"))
|
|
128
|
+
poster = secici.select_poster("div.poster img")
|
|
135
129
|
description = secici.select_text("div.wp-content p")
|
|
130
|
+
rating = secici.select_text("b#repimdb strong") or secici.regex_first(r"([\d.]+)", secici.select_text("div.imdb"))
|
|
131
|
+
year = secici.extract_year("div.extra span.valor")
|
|
132
|
+
tags = secici.select_texts("div.sgeneros a")
|
|
133
|
+
duration = int(secici.regex_first(r"(\d+)", secici.select_text("span.runtime")) or 0)
|
|
134
|
+
actors = secici.select_texts("span.valor a[href*='/oyuncu/']")
|
|
135
|
+
|
|
136
|
+
common_info = {
|
|
137
|
+
"url" : url,
|
|
138
|
+
"poster" : self.fix_url(poster),
|
|
139
|
+
"title" : title,
|
|
140
|
+
"description" : description,
|
|
141
|
+
"tags" : tags,
|
|
142
|
+
"rating" : rating,
|
|
143
|
+
"year" : year,
|
|
144
|
+
"duration" : duration,
|
|
145
|
+
"actors" : actors
|
|
146
|
+
}
|
|
136
147
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
# Yıl için info bölümünden veya regex ile yakala
|
|
140
|
-
year = secici.regex_first(r'(\d{4})', secici.select_text("div.extra span.valor") or secici.select_text("span.valor") or "")
|
|
141
|
-
if not year:
|
|
142
|
-
year = secici.regex_first(r'<span>(\d{4})</span>', html_text) or secici.regex_first(r'(\d{4})', html_text)
|
|
143
|
-
|
|
144
|
-
tags = [a.text(strip=True) for a in secici.select("div.sgeneros a") if a.text(strip=True)]
|
|
145
|
-
|
|
146
|
-
duration_text = secici.select_text("span.runtime")
|
|
147
|
-
duration = int(secici.regex_first(r"\d+", duration_text)) if duration_text and secici.regex_first(r"\d+", duration_text) else None
|
|
148
|
-
|
|
149
|
-
actors = [a.text(strip=True) for a in secici.select("span.valor a") if "/oyuncu/" in (a.attrs.get("href") or "")]
|
|
150
|
-
if not actors:
|
|
151
|
-
actors = secici.regex_all(r'href="[^"]*/oyuncu/[^"]*">([^<]+)</a>')
|
|
152
|
-
|
|
153
|
-
trailer = None
|
|
154
|
-
if trailer_id := secici.regex_first(r'embed/([^?]*)\?rel', html_text):
|
|
155
|
-
trailer = f"https://www.youtube.com/embed/{trailer_id}"
|
|
156
|
-
|
|
157
|
-
# Dizi mi film mi kontrol et
|
|
158
|
-
is_series = "/dizi/" in url
|
|
159
|
-
|
|
160
|
-
if is_series:
|
|
161
|
-
year_elem = secici.select_text("a[href*='/yil/']")
|
|
162
|
-
if year_elem:
|
|
163
|
-
year = secici.regex_first(r"\d{4}", year_elem) or year
|
|
164
|
-
|
|
165
|
-
# Duration from info section
|
|
166
|
-
for span in secici.select("div#info span"):
|
|
167
|
-
span_text = span.text(strip=True) if span.text() else ""
|
|
168
|
-
if "Dakika" in span_text:
|
|
169
|
-
duration = secici.regex_first(r"\d+", span_text) and int(secici.regex_first(r"\d+", span_text))
|
|
170
|
-
break
|
|
171
|
-
|
|
148
|
+
if "/dizi/" in url:
|
|
172
149
|
episodes = []
|
|
173
150
|
for ep_item in secici.select("div#episodes ul.episodios li"):
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
ep_detail = ep_name
|
|
181
|
-
ep_season = secici.regex_first(r"(\d+)\.\s*Sezon", ep_detail) or 1
|
|
182
|
-
ep_episode = secici.regex_first(r"Sezon\s+(\d+)\.\s*Bölüm", ep_detail)
|
|
151
|
+
href = secici.select_attr("h4.episodiotitle a", "href", ep_item)
|
|
152
|
+
name = secici.select_direct_text("h4.episodiotitle a", ep_item)
|
|
153
|
+
if href and name:
|
|
154
|
+
s, e = secici.extract_season_episode(name)
|
|
155
|
+
episodes.append(Episode(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
|
|
156
|
+
return SeriesInfo(**common_info, episodes=episodes)
|
|
183
157
|
|
|
184
|
-
|
|
185
|
-
ep_episode = int(ep_episode) if isinstance(ep_episode, str) and ep_episode.isdigit() else None
|
|
186
|
-
|
|
187
|
-
episodes.append(Episode(
|
|
188
|
-
season = ep_season,
|
|
189
|
-
episode = ep_episode,
|
|
190
|
-
title = ep_name,
|
|
191
|
-
url = self.fix_url(ep_href)
|
|
192
|
-
))
|
|
193
|
-
return SeriesInfo(
|
|
194
|
-
url = url,
|
|
195
|
-
poster = self.fix_url(poster) if poster else None,
|
|
196
|
-
title = title,
|
|
197
|
-
description = description,
|
|
198
|
-
tags = tags,
|
|
199
|
-
rating = rating,
|
|
200
|
-
year = year,
|
|
201
|
-
duration = duration,
|
|
202
|
-
actors = actors,
|
|
203
|
-
episodes = episodes
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
return MovieInfo(
|
|
207
|
-
url = url,
|
|
208
|
-
poster = self.fix_url(poster) if poster else None,
|
|
209
|
-
title = title,
|
|
210
|
-
description = description,
|
|
211
|
-
tags = tags,
|
|
212
|
-
rating = rating,
|
|
213
|
-
year = year,
|
|
214
|
-
duration = duration,
|
|
215
|
-
actors = actors
|
|
216
|
-
)
|
|
158
|
+
return MovieInfo(**common_info)
|
|
217
159
|
|
|
218
160
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
219
161
|
istek = await self.httpx.get(url)
|
|
@@ -231,14 +173,14 @@ class SetFilmIzle(PluginBase):
|
|
|
231
173
|
semaphore = asyncio.Semaphore(5)
|
|
232
174
|
tasks = []
|
|
233
175
|
|
|
234
|
-
async def fetch_and_extract(player):
|
|
176
|
+
async def fetch_and_extract(player) -> list[ExtractResult]:
|
|
235
177
|
async with semaphore:
|
|
236
178
|
source_id = player.attrs.get("data-post-id")
|
|
237
|
-
player_name = player.attrs.get("data-player-name")
|
|
179
|
+
player_name = player.attrs.get("data-player-name") or secici.select_text("b", player)
|
|
238
180
|
part_key = player.attrs.get("data-part-key")
|
|
239
181
|
|
|
240
182
|
if not source_id or "event" in source_id or source_id == "":
|
|
241
|
-
return
|
|
183
|
+
return []
|
|
242
184
|
|
|
243
185
|
try:
|
|
244
186
|
resp = self.cloudscraper.post(
|
|
@@ -248,17 +190,17 @@ class SetFilmIzle(PluginBase):
|
|
|
248
190
|
"action" : "get_video_url",
|
|
249
191
|
"nonce" : nonce,
|
|
250
192
|
"post_id" : source_id,
|
|
251
|
-
"player_name" :
|
|
193
|
+
"player_name" : player.attrs.get("data-player-name") or "",
|
|
252
194
|
"part_key" : part_key or ""
|
|
253
195
|
}
|
|
254
196
|
)
|
|
255
197
|
data = resp.json()
|
|
256
198
|
except:
|
|
257
|
-
return
|
|
199
|
+
return []
|
|
258
200
|
|
|
259
201
|
iframe_url = data.get("data", {}).get("url")
|
|
260
202
|
if not iframe_url:
|
|
261
|
-
return
|
|
203
|
+
return []
|
|
262
204
|
|
|
263
205
|
if "setplay" not in iframe_url and part_key:
|
|
264
206
|
iframe_url = f"{iframe_url}?partKey={part_key}"
|
|
@@ -267,10 +209,40 @@ class SetFilmIzle(PluginBase):
|
|
|
267
209
|
if not label and part_key:
|
|
268
210
|
label = part_key.replace("_", " ").title()
|
|
269
211
|
|
|
270
|
-
|
|
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
|
|
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
|
|
271
230
|
|
|
272
|
-
|
|
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:
|
|
273
238
|
tasks.append(fetch_and_extract(player))
|
|
274
239
|
|
|
275
|
-
|
|
276
|
-
|
|
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
|
|
@@ -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 asyncio
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult, HTMLHelper
|
|
4
|
+
import asyncio, contextlib
|
|
5
5
|
|
|
6
6
|
class SezonlukDizi(PluginBase):
|
|
7
7
|
name = "SezonlukDizi"
|
|
@@ -40,10 +40,10 @@ class SezonlukDizi(PluginBase):
|
|
|
40
40
|
|
|
41
41
|
async def _get_asp_data(self) -> dict:
|
|
42
42
|
js_req = await self.httpx.get(f"{self.main_url}/js/site.min.js")
|
|
43
|
-
js
|
|
44
|
-
alt
|
|
45
|
-
emb
|
|
46
|
-
|
|
43
|
+
js = HTMLHelper(js_req.text)
|
|
44
|
+
alt = js.regex_first(r"dataAlternatif(.*?)\.asp")
|
|
45
|
+
emb = js.regex_first(r"dataEmbed(.*?)\.asp")
|
|
46
|
+
|
|
47
47
|
return {
|
|
48
48
|
"alternatif": alt or "",
|
|
49
49
|
"embed": emb or ""
|
|
@@ -64,13 +64,13 @@ class SezonlukDizi(PluginBase):
|
|
|
64
64
|
category = category,
|
|
65
65
|
title = title,
|
|
66
66
|
url = self.fix_url(href),
|
|
67
|
-
poster = self.fix_url(poster)
|
|
67
|
+
poster = self.fix_url(poster),
|
|
68
68
|
))
|
|
69
69
|
|
|
70
70
|
return results
|
|
71
71
|
|
|
72
72
|
async def search(self, query: str) -> list[SearchResult]:
|
|
73
|
-
istek = await self.httpx.get(f"{self.main_url}/diziler.asp?
|
|
73
|
+
istek = await self.httpx.get(f"{self.main_url}/diziler.asp?q={query}")
|
|
74
74
|
secici = HTMLHelper(istek.text)
|
|
75
75
|
|
|
76
76
|
results = []
|
|
@@ -83,7 +83,7 @@ class SezonlukDizi(PluginBase):
|
|
|
83
83
|
results.append(SearchResult(
|
|
84
84
|
title = title,
|
|
85
85
|
url = self.fix_url(href),
|
|
86
|
-
poster = self.fix_url(poster)
|
|
86
|
+
poster = self.fix_url(poster),
|
|
87
87
|
))
|
|
88
88
|
|
|
89
89
|
return results
|
|
@@ -92,67 +92,40 @@ class SezonlukDizi(PluginBase):
|
|
|
92
92
|
istek = await self.httpx.get(url)
|
|
93
93
|
secici = HTMLHelper(istek.text)
|
|
94
94
|
|
|
95
|
-
title
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
#
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
for sezon in episodes_secici.select("table.unstackable"):
|
|
126
|
-
for bolum in episodes_secici.select("tbody tr", sezon):
|
|
127
|
-
# td:nth-of-type selectolax'ta desteklenmiyor, alternatif yol: tüm td'leri alıp indexle
|
|
128
|
-
tds = episodes_secici.select("td", bolum)
|
|
129
|
-
if len(tds) < 4:
|
|
130
|
-
continue
|
|
131
|
-
|
|
132
|
-
# 4. td'den isim ve href
|
|
133
|
-
ep_name = episodes_secici.select_text("a", tds[3])
|
|
134
|
-
ep_href = episodes_secici.select_attr("a", "href", tds[3])
|
|
135
|
-
|
|
136
|
-
# 3. td'den episode (re_first yerine regex)
|
|
137
|
-
ep_episode_text = episodes_secici.select_text("a", tds[2]) or ""
|
|
138
|
-
ep_episode = episodes_secici.regex_first(r"(\d+)", ep_episode_text)
|
|
139
|
-
|
|
140
|
-
# 2. td'den season (re_first yerine regex)
|
|
141
|
-
ep_season_text = tds[1].text(strip=True) if tds[1] else ""
|
|
142
|
-
ep_season = secici.regex_first(r"(\d+)", ep_season_text)
|
|
143
|
-
|
|
144
|
-
if ep_name and ep_href:
|
|
145
|
-
episode = Episode(
|
|
146
|
-
season = ep_season,
|
|
147
|
-
episode = ep_episode,
|
|
148
|
-
title = ep_name,
|
|
149
|
-
url = self.fix_url(ep_href),
|
|
150
|
-
)
|
|
151
|
-
episodes.append(episode)
|
|
95
|
+
title = secici.select_text("div.header") or ""
|
|
96
|
+
poster = secici.select_poster("div.image img")
|
|
97
|
+
year = secici.extract_year("div.extra span")
|
|
98
|
+
description = secici.select_text("span#tartismayorum-konu")
|
|
99
|
+
tags = secici.select_texts("div.labels a[href*='tur']")
|
|
100
|
+
rating = secici.regex_first(r"[\d.,]+", secici.select_text("div.dizipuani a div"))
|
|
101
|
+
|
|
102
|
+
# Actors extraction
|
|
103
|
+
id_slug = url.split('/')[-1]
|
|
104
|
+
a_resp = await self.httpx.get(f"{self.main_url}/oyuncular/{id_slug}")
|
|
105
|
+
a_sel = HTMLHelper(a_resp.text)
|
|
106
|
+
actors = a_sel.select_texts("div.doubling div.ui div.header")
|
|
107
|
+
|
|
108
|
+
# Episodes extraction
|
|
109
|
+
e_resp = await self.httpx.get(f"{self.main_url}/bolumler/{id_slug}")
|
|
110
|
+
e_sel = HTMLHelper(e_resp.text)
|
|
111
|
+
episodes = []
|
|
112
|
+
for row in e_sel.select("table.unstackable tbody tr"):
|
|
113
|
+
tds = e_sel.select("td", row)
|
|
114
|
+
if len(tds) >= 4:
|
|
115
|
+
name = e_sel.select_text("a", tds[3])
|
|
116
|
+
href = e_sel.select_attr("a", "href", tds[3])
|
|
117
|
+
if name and href:
|
|
118
|
+
s, e = e_sel.extract_season_episode(f"{tds[1].text(strip=True)} {tds[2].text(strip=True)}")
|
|
119
|
+
episodes.append(Episode(
|
|
120
|
+
season = s or 1,
|
|
121
|
+
episode = e or 1,
|
|
122
|
+
title = name,
|
|
123
|
+
url = self.fix_url(href)
|
|
124
|
+
))
|
|
152
125
|
|
|
153
126
|
return SeriesInfo(
|
|
154
127
|
url = url,
|
|
155
|
-
poster = self.fix_url(poster)
|
|
128
|
+
poster = self.fix_url(poster),
|
|
156
129
|
title = title,
|
|
157
130
|
description = description,
|
|
158
131
|
tags = tags,
|
|
@@ -163,10 +136,10 @@ class SezonlukDizi(PluginBase):
|
|
|
163
136
|
)
|
|
164
137
|
|
|
165
138
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
166
|
-
istek
|
|
167
|
-
secici
|
|
139
|
+
istek = await self.httpx.get(url)
|
|
140
|
+
secici = HTMLHelper(istek.text)
|
|
168
141
|
asp_data = await self._get_asp_data()
|
|
169
|
-
|
|
142
|
+
|
|
170
143
|
bid = secici.select_attr("div#dilsec", "data-id")
|
|
171
144
|
if not bid:
|
|
172
145
|
return []
|
|
@@ -174,41 +147,64 @@ class SezonlukDizi(PluginBase):
|
|
|
174
147
|
semaphore = asyncio.Semaphore(5)
|
|
175
148
|
tasks = []
|
|
176
149
|
|
|
177
|
-
async def fetch_and_extract(veri, dil_etiketi):
|
|
150
|
+
async def fetch_and_extract(veri, dil_etiketi) -> list[ExtractResult]:
|
|
178
151
|
async with semaphore:
|
|
179
152
|
try:
|
|
180
153
|
embed_resp = await self.httpx.post(
|
|
181
|
-
f"{self.main_url}/ajax/dataEmbed{asp_data['embed']}.asp",
|
|
154
|
+
url = f"{self.main_url}/ajax/dataEmbed{asp_data['embed']}.asp",
|
|
182
155
|
headers = {"X-Requested-With": "XMLHttpRequest"},
|
|
183
156
|
data = {"id": str(veri.get("id"))}
|
|
184
157
|
)
|
|
185
158
|
embed_secici = HTMLHelper(embed_resp.text)
|
|
186
|
-
iframe_src
|
|
187
|
-
|
|
188
|
-
if iframe_src:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
159
|
+
iframe_src = embed_secici.select_attr("iframe", "src") or embed_secici.regex_first(r'src="(.*?)"')
|
|
160
|
+
|
|
161
|
+
if not iframe_src:
|
|
162
|
+
return []
|
|
163
|
+
|
|
164
|
+
iframe_url = self.fix_url(iframe_src)
|
|
165
|
+
|
|
166
|
+
real_url = iframe_url
|
|
167
|
+
if "url=" in iframe_url:
|
|
168
|
+
real_url = HTMLHelper(iframe_url).regex_first(r"url=([^&]+)")
|
|
169
|
+
if real_url:
|
|
170
|
+
real_url = self.fix_url(real_url)
|
|
171
|
+
|
|
172
|
+
source_name = veri.get('baslik') or "SezonlukDizi"
|
|
173
|
+
full_name = f"{dil_etiketi} - {source_name}"
|
|
174
|
+
|
|
175
|
+
extracted = await self.extract(real_url, referer=f"{self.main_url}/")
|
|
176
|
+
|
|
177
|
+
if not extracted:
|
|
178
|
+
return []
|
|
179
|
+
|
|
180
|
+
results = []
|
|
181
|
+
items = extracted if isinstance(extracted, list) else [extracted]
|
|
182
|
+
for item in items:
|
|
183
|
+
item.name = full_name
|
|
184
|
+
results.append(item)
|
|
185
|
+
return results
|
|
186
|
+
|
|
187
|
+
except Exception:
|
|
188
|
+
return []
|
|
197
189
|
|
|
198
190
|
for dil_kodu, dil_etiketi in [("1", "Altyazı"), ("0", "Dublaj")]:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
191
|
+
with contextlib.suppress(Exception):
|
|
192
|
+
altyazi_resp = await self.httpx.post(
|
|
193
|
+
url = f"{self.main_url}/ajax/dataAlternatif{asp_data['alternatif']}.asp",
|
|
194
|
+
headers = {"X-Requested-With": "XMLHttpRequest"},
|
|
195
|
+
data = {"bid": bid, "dil": dil_kodu}
|
|
196
|
+
)
|
|
197
|
+
|
|
206
198
|
data_json = altyazi_resp.json()
|
|
207
199
|
if data_json.get("status") == "success" and data_json.get("data"):
|
|
208
200
|
for veri in data_json["data"]:
|
|
209
201
|
tasks.append(fetch_and_extract(veri, dil_etiketi))
|
|
210
|
-
except:
|
|
211
|
-
continue
|
|
212
202
|
|
|
213
|
-
|
|
214
|
-
|
|
203
|
+
results_groups = await asyncio.gather(*tasks)
|
|
204
|
+
|
|
205
|
+
final_results = []
|
|
206
|
+
for group in results_groups:
|
|
207
|
+
if group:
|
|
208
|
+
final_results.extend(group)
|
|
209
|
+
|
|
210
|
+
return final_results
|