KekikStream 2.3.3__py3-none-any.whl → 2.3.5__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/HTMLHelper.py +134 -0
- KekikStream/Core/Plugin/PluginBase.py +12 -2
- KekikStream/Core/__init__.py +2 -0
- KekikStream/Extractors/CloseLoad.py +12 -13
- KekikStream/Extractors/ContentX.py +33 -31
- KekikStream/Extractors/DonilasPlay.py +10 -10
- KekikStream/Extractors/DzenRu.py +3 -3
- KekikStream/Extractors/ExPlay.py +10 -10
- KekikStream/Extractors/Filemoon.py +11 -16
- KekikStream/Extractors/JetTv.py +4 -4
- KekikStream/Extractors/MixPlayHD.py +10 -11
- KekikStream/Extractors/MolyStream.py +15 -9
- KekikStream/Extractors/Odnoklassniki.py +4 -4
- KekikStream/Extractors/PeaceMakerst.py +3 -3
- KekikStream/Extractors/PixelDrain.py +6 -5
- KekikStream/Extractors/PlayerFilmIzle.py +6 -10
- KekikStream/Extractors/RapidVid.py +8 -7
- KekikStream/Extractors/SetPlay.py +10 -10
- KekikStream/Extractors/SetPrime.py +3 -6
- KekikStream/Extractors/SibNet.py +4 -5
- KekikStream/Extractors/Sobreatsesuyp.py +5 -5
- KekikStream/Extractors/TRsTX.py +5 -5
- KekikStream/Extractors/TurboImgz.py +3 -4
- KekikStream/Extractors/TurkeyPlayer.py +5 -5
- KekikStream/Extractors/VidHide.py +4 -7
- KekikStream/Extractors/VidMoly.py +24 -25
- KekikStream/Extractors/VidMoxy.py +8 -9
- KekikStream/Extractors/VidPapi.py +5 -7
- KekikStream/Extractors/VideoSeyred.py +3 -3
- KekikStream/Plugins/BelgeselX.py +40 -51
- KekikStream/Plugins/DiziBox.py +53 -81
- KekikStream/Plugins/DiziPal.py +41 -74
- KekikStream/Plugins/DiziWatch.py +217 -0
- KekikStream/Plugins/DiziYou.py +95 -88
- KekikStream/Plugins/Dizilla.py +54 -72
- KekikStream/Plugins/FilmBip.py +24 -49
- KekikStream/Plugins/FilmMakinesi.py +35 -52
- KekikStream/Plugins/FilmModu.py +27 -41
- KekikStream/Plugins/FullHDFilm.py +50 -72
- KekikStream/Plugins/FullHDFilmizlesene.py +35 -51
- KekikStream/Plugins/HDFilmCehennemi.py +48 -62
- KekikStream/Plugins/JetFilmizle.py +32 -50
- KekikStream/Plugins/KultFilmler.py +42 -67
- KekikStream/Plugins/RecTV.py +7 -4
- KekikStream/Plugins/RoketDizi.py +30 -50
- KekikStream/Plugins/SelcukFlix.py +15 -29
- KekikStream/Plugins/SetFilmIzle.py +41 -70
- KekikStream/Plugins/SezonlukDizi.py +47 -65
- KekikStream/Plugins/Sinefy.py +39 -50
- KekikStream/Plugins/SinemaCX.py +31 -55
- KekikStream/Plugins/Sinezy.py +27 -54
- KekikStream/Plugins/SuperFilmGeldi.py +25 -44
- KekikStream/Plugins/UgurFilm.py +23 -48
- KekikStream/Plugins/YabanciDizi.py +274 -0
- {kekikstream-2.3.3.dist-info → kekikstream-2.3.5.dist-info}/METADATA +1 -1
- kekikstream-2.3.5.dist-info/RECORD +85 -0
- kekikstream-2.3.3.dist-info/RECORD +0 -82
- {kekikstream-2.3.3.dist-info → kekikstream-2.3.5.dist-info}/WHEEL +0 -0
- {kekikstream-2.3.3.dist-info → kekikstream-2.3.5.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.3.3.dist-info → kekikstream-2.3.5.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.3.3.dist-info → kekikstream-2.3.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult, HTMLHelper
|
|
4
|
+
import urllib.parse
|
|
5
|
+
|
|
6
|
+
class DiziWatch(PluginBase):
|
|
7
|
+
name = "DiziWatch"
|
|
8
|
+
language = "tr"
|
|
9
|
+
main_url = "https://diziwatch.to"
|
|
10
|
+
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
11
|
+
description = "Diziwatch; en güncel yabancı dizileri ve animeleri, Türkçe altyazılı ve dublaj seçenekleriyle izleyebileceğiniz platform."
|
|
12
|
+
|
|
13
|
+
main_page = {
|
|
14
|
+
f"{main_url}/episodes" : "Yeni Bölümler",
|
|
15
|
+
"9" : "Aksiyon",
|
|
16
|
+
"17" : "Animasyon",
|
|
17
|
+
"5" : "Bilim Kurgu",
|
|
18
|
+
"2" : "Dram",
|
|
19
|
+
"12" : "Fantastik",
|
|
20
|
+
"3" : "Gizem",
|
|
21
|
+
"4" : "Komedi",
|
|
22
|
+
"8" : "Korku",
|
|
23
|
+
"24" : "Macera",
|
|
24
|
+
"14" : "Müzik",
|
|
25
|
+
"7" : "Romantik",
|
|
26
|
+
"23" : "Spor",
|
|
27
|
+
"1" : "Suç",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
super().__init__()
|
|
32
|
+
self.c_key = None
|
|
33
|
+
self.c_value = None
|
|
34
|
+
|
|
35
|
+
async def _init_session(self):
|
|
36
|
+
if self.c_key and self.c_value:
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
# Fetch anime-arsivi to get CSRF tokens
|
|
40
|
+
resp = await self.httpx.get(f"{self.main_url}/anime-arsivi")
|
|
41
|
+
sel = HTMLHelper(resp.text)
|
|
42
|
+
|
|
43
|
+
# form.bg-[rgba(255,255,255,.15)] > input
|
|
44
|
+
# We can just look for the first two inputs in that specific form
|
|
45
|
+
inputs = sel.select("form.bg-\\[rgba\\(255\\,255\\,255\\,\\.15\\)\\] input")
|
|
46
|
+
if len(inputs) >= 2:
|
|
47
|
+
self.c_key = inputs[0].attrs.get("value")
|
|
48
|
+
self.c_value = inputs[1].attrs.get("value")
|
|
49
|
+
|
|
50
|
+
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
51
|
+
await self._init_session()
|
|
52
|
+
|
|
53
|
+
if url.startswith("https://"):
|
|
54
|
+
full_url = f"{url}?page={page}"
|
|
55
|
+
resp = await self.httpx.get(full_url, headers={"Referer": f"{self.main_url}/"})
|
|
56
|
+
sel = HTMLHelper(resp.text)
|
|
57
|
+
items = sel.select("div.swiper-slide a")
|
|
58
|
+
else:
|
|
59
|
+
# Category ID based
|
|
60
|
+
full_url = f"{self.main_url}/anime-arsivi?category={url}&minImdb=&name=&release_year=&sort=date_desc&page={page}"
|
|
61
|
+
resp = await self.httpx.get(full_url, headers={"Referer": f"{self.main_url}/"})
|
|
62
|
+
sel = HTMLHelper(resp.text)
|
|
63
|
+
items = sel.select("div.content-inner a")
|
|
64
|
+
|
|
65
|
+
results = []
|
|
66
|
+
for item in items:
|
|
67
|
+
title = sel.select_text("h2", item)
|
|
68
|
+
href = item.attrs.get("href") if item.tag == "a" else sel.select_attr("a", "href", item)
|
|
69
|
+
poster = sel.select_attr("img", "src", item) or sel.select_attr("img", "data-src", item)
|
|
70
|
+
|
|
71
|
+
if title and href:
|
|
72
|
+
# If it's an episode link, clean it to get show link
|
|
73
|
+
# Regex in Kotlin: /sezon-\d+/bolum-\d+/?$
|
|
74
|
+
clean_href = HTMLHelper(href).regex_replace(r"/sezon-\d+/bolum-\d+/?$", "")
|
|
75
|
+
|
|
76
|
+
# If cleaning changed something, it was an episode link, maybe add it to title
|
|
77
|
+
if clean_href != href:
|
|
78
|
+
se_info = sel.select_text("div.flex.gap-1.items-center", item)
|
|
79
|
+
if se_info:
|
|
80
|
+
title = f"{title} - {se_info}"
|
|
81
|
+
|
|
82
|
+
results.append(MainPageResult(
|
|
83
|
+
category = category,
|
|
84
|
+
title = title,
|
|
85
|
+
url = self.fix_url(clean_href),
|
|
86
|
+
poster = self.fix_url(poster) if poster else None
|
|
87
|
+
))
|
|
88
|
+
|
|
89
|
+
return results
|
|
90
|
+
|
|
91
|
+
async def search(self, query: str) -> list[SearchResult]:
|
|
92
|
+
await self._init_session()
|
|
93
|
+
|
|
94
|
+
post_url = f"{self.main_url}/bg/searchcontent"
|
|
95
|
+
data = {
|
|
96
|
+
"cKey" : self.c_key,
|
|
97
|
+
"cValue" : self.c_value,
|
|
98
|
+
"searchterm" : query
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
headers = {
|
|
102
|
+
"X-Requested-With" : "XMLHttpRequest",
|
|
103
|
+
"Accept" : "application/json, text/javascript, */*; q=0.01",
|
|
104
|
+
"Referer" : f"{self.main_url}/"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
resp = await self.httpx.post(post_url, data=data, headers=headers)
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
raw = resp.json()
|
|
111
|
+
# Kotlin maps this to ApiResponse -> DataWrapper -> Icerikler
|
|
112
|
+
res_array = raw.get("data", {}).get("result", [])
|
|
113
|
+
|
|
114
|
+
results = []
|
|
115
|
+
for item in res_array:
|
|
116
|
+
title = item.get("object_name", "").replace("\\", "")
|
|
117
|
+
slug = item.get("used_slug", "").replace("\\", "")
|
|
118
|
+
poster = item.get("object_poster_url", "")
|
|
119
|
+
|
|
120
|
+
# Cleanup poster URL as in Kotlin
|
|
121
|
+
if poster:
|
|
122
|
+
poster = poster.replace("images-macellan-online.cdn.ampproject.org/i/s/", "") \
|
|
123
|
+
.replace("file.dizilla.club", "file.macellan.online") \
|
|
124
|
+
.replace("images.dizilla.club", "images.macellan.online") \
|
|
125
|
+
.replace("images.dizimia4.com", "images.macellan.online") \
|
|
126
|
+
.replace("file.dizimia4.com", "file.macellan.online")
|
|
127
|
+
poster = HTMLHelper(poster).regex_replace(r"(file\.)[\w\.]+\/?", r"\1macellan.online/")
|
|
128
|
+
poster = HTMLHelper(poster).regex_replace(r"(images\.)[\w\.]+\/?", r"\1macellan.online/")
|
|
129
|
+
poster = poster.replace("/f/f/", "/630/910/")
|
|
130
|
+
|
|
131
|
+
if title and slug:
|
|
132
|
+
results.append(SearchResult(
|
|
133
|
+
title = title,
|
|
134
|
+
url = self.fix_url(slug),
|
|
135
|
+
poster = self.fix_url(poster) if poster else None
|
|
136
|
+
))
|
|
137
|
+
return results
|
|
138
|
+
except Exception:
|
|
139
|
+
return []
|
|
140
|
+
|
|
141
|
+
async def load_item(self, url: str) -> SeriesInfo:
|
|
142
|
+
resp = await self.httpx.get(url)
|
|
143
|
+
sel = HTMLHelper(resp.text)
|
|
144
|
+
|
|
145
|
+
title = sel.select_text("h2")
|
|
146
|
+
poster = sel.select_attr("img.rounded-md", "src")
|
|
147
|
+
description = sel.select_text("div.text-sm")
|
|
148
|
+
|
|
149
|
+
year = sel.regex_first(r"Yap\u0131m Y\u0131l\u0131\s*:\s*(\d+)", resp.text)
|
|
150
|
+
|
|
151
|
+
tags = []
|
|
152
|
+
tags_raw = sel.regex_first(r"T\u00fcr\s*:\s*([^<]+)", resp.text)
|
|
153
|
+
if tags_raw:
|
|
154
|
+
tags = [t.strip() for t in tags_raw.split(",")]
|
|
155
|
+
|
|
156
|
+
rating = sel.select_text(".font-semibold.text-white")
|
|
157
|
+
if rating:
|
|
158
|
+
rating = rating.replace(",", ".").strip()
|
|
159
|
+
|
|
160
|
+
actors = [a.text(strip=True) for a in sel.select("span.valor a")]
|
|
161
|
+
|
|
162
|
+
trailer_match = sel.regex_first(r"embed\/(.*)\?rel", resp.text)
|
|
163
|
+
trailer = f"https://www.youtube.com/embed/{trailer_match}" if trailer_match else None
|
|
164
|
+
|
|
165
|
+
duration_text = sel.select_text("span.runtime")
|
|
166
|
+
duration = duration_text.split(" ")[0] if duration_text else None
|
|
167
|
+
|
|
168
|
+
episodes = []
|
|
169
|
+
# ul a handles episodes
|
|
170
|
+
for ep_link in sel.select("ul a"):
|
|
171
|
+
href = ep_link.attrs.get("href")
|
|
172
|
+
if not href or "/sezon-" not in href:
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
ep_name = sel.select_text("span.hidden.sm\\:block", ep_link)
|
|
176
|
+
|
|
177
|
+
season_match = sel.regex_first(r"sezon-(\d+)", href)
|
|
178
|
+
episode_match = sel.regex_first(r"bolum-(\d+)", href)
|
|
179
|
+
|
|
180
|
+
season = season_match if season_match else None
|
|
181
|
+
episode_num = episode_match if episode_match else None
|
|
182
|
+
|
|
183
|
+
episodes.append(Episode(
|
|
184
|
+
season = int(season) if season and season.isdigit() else None,
|
|
185
|
+
episode = int(episode_num) if episode_num and episode_num.isdigit() else None,
|
|
186
|
+
title = ep_name if ep_name else f"{season}x{episode_num}",
|
|
187
|
+
url = self.fix_url(href)
|
|
188
|
+
))
|
|
189
|
+
|
|
190
|
+
return SeriesInfo(
|
|
191
|
+
title = title,
|
|
192
|
+
url = url,
|
|
193
|
+
poster = self.fix_url(poster) if poster else None,
|
|
194
|
+
description = description,
|
|
195
|
+
rating = rating,
|
|
196
|
+
tags = tags,
|
|
197
|
+
actors = actors,
|
|
198
|
+
year = year,
|
|
199
|
+
episodes = episodes,
|
|
200
|
+
duration = int(duration) if duration and str(duration).isdigit() else None
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
204
|
+
resp = await self.httpx.get(url)
|
|
205
|
+
sel = HTMLHelper(resp.text)
|
|
206
|
+
|
|
207
|
+
iframe = sel.select_attr("iframe", "src")
|
|
208
|
+
if not iframe:
|
|
209
|
+
return []
|
|
210
|
+
|
|
211
|
+
iframe_url = self.fix_url(iframe)
|
|
212
|
+
data = await self.extract(iframe_url, referer=f"{self.main_url}/")
|
|
213
|
+
|
|
214
|
+
if not data:
|
|
215
|
+
return []
|
|
216
|
+
|
|
217
|
+
return data if isinstance(data, list) else [data]
|
KekikStream/Plugins/DiziYou.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, Subtitle, ExtractResult
|
|
4
|
-
from selectolax.parser import HTMLParser
|
|
5
|
-
import re
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, Subtitle, ExtractResult, HTMLHelper
|
|
6
4
|
|
|
7
5
|
class DiziYou(PluginBase):
|
|
8
6
|
name = "DiziYou"
|
|
@@ -31,16 +29,13 @@ class DiziYou(PluginBase):
|
|
|
31
29
|
|
|
32
30
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
33
31
|
istek = await self.httpx.get(f"{url.replace('SAYFA', str(page))}")
|
|
34
|
-
secici =
|
|
32
|
+
secici = HTMLHelper(istek.text)
|
|
35
33
|
|
|
36
34
|
results = []
|
|
37
|
-
for veri in secici.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
title = title_el.text(strip=True) if title_el else None
|
|
42
|
-
href = title_el.attrs.get("href") if title_el else None
|
|
43
|
-
poster = img_el.attrs.get("src") if img_el else None
|
|
35
|
+
for veri in secici.select("div.single-item"):
|
|
36
|
+
title = secici.select_text("div#categorytitle a", veri)
|
|
37
|
+
href = secici.select_attr("div#categorytitle a", "href", veri)
|
|
38
|
+
poster = secici.select_attr("img", "src", veri)
|
|
44
39
|
|
|
45
40
|
if title and href:
|
|
46
41
|
results.append(MainPageResult(
|
|
@@ -54,16 +49,13 @@ class DiziYou(PluginBase):
|
|
|
54
49
|
|
|
55
50
|
async def search(self, query: str) -> list[SearchResult]:
|
|
56
51
|
istek = await self.httpx.get(f"{self.main_url}/?s={query}")
|
|
57
|
-
secici =
|
|
52
|
+
secici = HTMLHelper(istek.text)
|
|
58
53
|
|
|
59
54
|
results = []
|
|
60
|
-
for afis in secici.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
title = title_el.text(strip=True) if title_el else None
|
|
65
|
-
href = title_el.attrs.get("href") if title_el else None
|
|
66
|
-
poster = (img_el.attrs.get("src") or img_el.attrs.get("data-src")) if img_el else None
|
|
55
|
+
for afis in secici.select("div.incontent div#list-series"):
|
|
56
|
+
title = secici.select_text("div#categorytitle a", afis)
|
|
57
|
+
href = secici.select_attr("div#categorytitle a", "href", afis)
|
|
58
|
+
poster = (secici.select_attr("img", "src", afis) or secici.select_attr("img", "data-src", afis))
|
|
67
59
|
|
|
68
60
|
if title and href:
|
|
69
61
|
results.append(SearchResult(
|
|
@@ -76,90 +68,92 @@ class DiziYou(PluginBase):
|
|
|
76
68
|
|
|
77
69
|
async def load_item(self, url: str) -> SeriesInfo:
|
|
78
70
|
istek = await self.httpx.get(url)
|
|
79
|
-
secici =
|
|
71
|
+
secici = HTMLHelper(istek.text)
|
|
80
72
|
html_text = istek.text
|
|
81
73
|
|
|
82
74
|
# Title - div.title h1 içinde
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
title = secici.select_text("div.title h1")
|
|
76
|
+
|
|
86
77
|
# Fallback: Eğer title boşsa URL'den çıkar (telif kısıtlaması olan sayfalar için)
|
|
87
78
|
if not title:
|
|
88
79
|
# URL'den slug'ı al: https://www.diziyou.one/jasmine/ -> jasmine -> Jasmine
|
|
89
80
|
slug = url.rstrip('/').split('/')[-1]
|
|
90
81
|
title = slug.replace('-', ' ').title()
|
|
91
|
-
|
|
82
|
+
|
|
92
83
|
# Poster
|
|
93
|
-
|
|
94
|
-
poster
|
|
84
|
+
poster_src = secici.select_attr("div.category_image img", "src")
|
|
85
|
+
poster = self.fix_url(poster_src) if poster_src else ""
|
|
95
86
|
|
|
96
87
|
# Year - regex ile çıkarma (xpath yerine)
|
|
97
|
-
year =
|
|
98
|
-
year_match = re.search(r"Yapım Yılı.*?(\d{4})", html_text, re.DOTALL | re.IGNORECASE)
|
|
99
|
-
if year_match:
|
|
100
|
-
year = year_match.group(1)
|
|
88
|
+
year = secici.regex_first(r"(?is)Yapım Yılı.*?(\d{4})", secici.html)
|
|
101
89
|
|
|
102
|
-
desc_el = secici.css_first("div.diziyou_desc")
|
|
103
90
|
description = None
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
91
|
+
# Extract inner HTML via regex and clean
|
|
92
|
+
desc_html = secici.regex_first(r'(?s)<div class="diziyou_desc">(.*?)</div>', secici.html)
|
|
93
|
+
if desc_html:
|
|
107
94
|
# Script taglarını kaldır
|
|
108
|
-
desc_html =
|
|
95
|
+
desc_html = HTMLHelper(desc_html).regex_replace(r"(?s)<script.*?</script>", "")
|
|
109
96
|
# div#icerikcat2 ve sonrasını kaldır (meta bilgileri içeriyor)
|
|
110
|
-
desc_html =
|
|
97
|
+
desc_html = HTMLHelper(desc_html).regex_replace(r"(?s)<div id=\"icerikcat2\".*", "")
|
|
111
98
|
# Kalan HTML'den text çıkar
|
|
112
|
-
clean_sel =
|
|
113
|
-
description = clean_sel.
|
|
99
|
+
clean_sel = HTMLHelper(desc_html)
|
|
100
|
+
description = clean_sel.select_text()
|
|
114
101
|
|
|
115
|
-
tags = [
|
|
102
|
+
tags = [secici.select_text(None, a) for a in secici.select("div.genres a") if secici.select_text(None, a)]
|
|
116
103
|
|
|
117
104
|
# Rating - daha spesifik regex ile
|
|
118
|
-
rating =
|
|
119
|
-
rating_match = re.search(r"IMDB\s*:\s*</span>([0-9.]+)", html_text, re.DOTALL | re.IGNORECASE)
|
|
120
|
-
if rating_match:
|
|
121
|
-
rating = rating_match.group(1)
|
|
105
|
+
rating = secici.regex_first(r"(?is)IMDB\s*:\s*</span>([0-9.]+)", secici.html)
|
|
122
106
|
|
|
123
107
|
# Actors - regex ile
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if actors_match:
|
|
127
|
-
actors = [actor.strip() for actor in actors_match.group(1).split(",") if actor.strip()]
|
|
108
|
+
actors_raw = secici.regex_first(r"(?is)Oyuncular.*?</span>([^<]+)", secici.html)
|
|
109
|
+
actors = [actor.strip() for actor in actors_raw.split(",") if actor.strip()] if actors_raw else []
|
|
128
110
|
|
|
129
111
|
episodes = []
|
|
130
|
-
# Episodes -
|
|
131
|
-
for link in secici.
|
|
132
|
-
|
|
133
|
-
if not bolumust:
|
|
134
|
-
continue
|
|
135
|
-
|
|
136
|
-
baslik_el = link.css_first("div.baslik")
|
|
137
|
-
if not baslik_el:
|
|
138
|
-
continue
|
|
139
|
-
|
|
140
|
-
ep_name = baslik_el.text(strip=True)
|
|
141
|
-
ep_href = link.attrs.get("href")
|
|
112
|
+
# Episodes - daha fazla DOM/URL kalıbını destekle
|
|
113
|
+
for link in secici.select("a"):
|
|
114
|
+
ep_href = secici.select_attr("a", "href", link)
|
|
142
115
|
if not ep_href:
|
|
143
116
|
continue
|
|
144
117
|
|
|
145
|
-
#
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
118
|
+
# Link metni veya alt başlık al
|
|
119
|
+
ep_name = (secici.select_text(None, link) or "").strip()
|
|
120
|
+
title_child = secici.select_text("div.baslik", link) or secici.select_text("div.bolumismi", link)
|
|
121
|
+
if title_child:
|
|
122
|
+
ep_name = title_child
|
|
123
|
+
|
|
124
|
+
# Önce metin üzerinden sezon/bölüm çıkart
|
|
125
|
+
s_val, e_val = HTMLHelper.extract_season_episode(ep_name)
|
|
126
|
+
|
|
127
|
+
# URL bazlı kalıplar: -1-sezon-2-bolum gibi
|
|
128
|
+
if not (s_val or e_val):
|
|
129
|
+
pairs = HTMLHelper(ep_href).regex_all(r"-(\d+)-sezon-(\d+)-bolum")
|
|
130
|
+
if pairs:
|
|
131
|
+
s_val, e_val = int(pairs[0][0]), int(pairs[0][1])
|
|
132
|
+
else:
|
|
133
|
+
pairs = HTMLHelper(ep_href).regex_all(r"(\d+)-sezon-(\d+)-bolum")
|
|
134
|
+
if pairs:
|
|
135
|
+
s_val, e_val = int(pairs[0][0]), int(pairs[0][1])
|
|
136
|
+
else:
|
|
137
|
+
e_val_str = HTMLHelper(ep_href).regex_first(r"(\d+)-bolum")
|
|
138
|
+
if e_val_str:
|
|
139
|
+
e_val = int(e_val_str)
|
|
140
|
+
# Metin üzerinden son bir deneme
|
|
141
|
+
if not e_val:
|
|
142
|
+
e_str = HTMLHelper(ep_name).regex_first(r"(\d+)\s*\.\s*[Bb]ölüm")
|
|
143
|
+
if e_str:
|
|
144
|
+
e_val = int(e_str)
|
|
145
|
+
if not s_val:
|
|
146
|
+
s_str = HTMLHelper(ep_name).regex_first(r"(\d+)\s*\.\s*[Ss]ezon")
|
|
147
|
+
if s_str:
|
|
148
|
+
s_val = int(s_str)
|
|
149
|
+
|
|
150
|
+
if e_val or HTMLHelper(ep_href).regex_first(r"-\d+-sezon-\d+-bolum"):
|
|
151
|
+
episodes.append(Episode(
|
|
152
|
+
season = s_val,
|
|
153
|
+
episode = e_val,
|
|
154
|
+
title = ep_name if ep_name else None,
|
|
160
155
|
url = self.fix_url(ep_href),
|
|
161
|
-
)
|
|
162
|
-
episodes.append(episode)
|
|
156
|
+
))
|
|
163
157
|
|
|
164
158
|
return SeriesInfo(
|
|
165
159
|
url = url,
|
|
@@ -175,29 +169,42 @@ class DiziYou(PluginBase):
|
|
|
175
169
|
|
|
176
170
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
177
171
|
istek = await self.httpx.get(url)
|
|
178
|
-
secici =
|
|
172
|
+
secici = HTMLHelper(istek.text)
|
|
179
173
|
|
|
180
174
|
# Title ve episode name - None kontrolü ekle
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
ep_name_el = secici.css_first("div#bolum-ismi")
|
|
185
|
-
ep_name = ep_name_el.text(strip=True) if ep_name_el else ""
|
|
175
|
+
item_title = secici.select_text("div.title h1")
|
|
176
|
+
ep_name = secici.select_text("div#bolum-ismi")
|
|
186
177
|
|
|
187
178
|
# Player src'den item_id çıkar
|
|
188
|
-
|
|
189
|
-
player_src =
|
|
179
|
+
# Player src'den item_id çıkar - önce özel player seçicisini dene
|
|
180
|
+
player_src = None
|
|
181
|
+
# Yaygın locatorlar
|
|
182
|
+
for sel in ["iframe#diziyouPlayer", "div.player iframe", "iframe[src*='/episodes/']", "iframe"]:
|
|
183
|
+
p = secici.select_attr(sel, "src")
|
|
184
|
+
if p and "youtube.com" not in p.lower():
|
|
185
|
+
player_src = p
|
|
186
|
+
break
|
|
187
|
+
|
|
188
|
+
# Eğer hâlâ bulunamadıysa, varsa bir bölüm sayfasına git ve oradan player'ı çek
|
|
189
|
+
if not player_src:
|
|
190
|
+
for a in secici.select("a"):
|
|
191
|
+
href = secici.select_attr("a", "href", a)
|
|
192
|
+
if not href:
|
|
193
|
+
continue
|
|
194
|
+
if HTMLHelper(href).regex_first(r"(-\d+-sezon-\d+-bolum|/bolum|/episode|/episodes|/play)"):
|
|
195
|
+
break
|
|
196
|
+
|
|
190
197
|
if not player_src:
|
|
191
198
|
return [] # Player bulunamadıysa boş liste döndür
|
|
192
|
-
|
|
199
|
+
|
|
193
200
|
item_id = player_src.split("/")[-1].replace(".html", "")
|
|
194
201
|
|
|
195
202
|
subtitles = []
|
|
196
203
|
stream_urls = []
|
|
197
204
|
|
|
198
|
-
for secenek in secici.
|
|
199
|
-
opt_id =
|
|
200
|
-
op_name =
|
|
205
|
+
for secenek in secici.select("span.diziyouOption"):
|
|
206
|
+
opt_id = secici.select_attr("span.diziyouOption", "id", secenek)
|
|
207
|
+
op_name = secici.select_text("span.diziyouOption", secenek)
|
|
201
208
|
|
|
202
209
|
match opt_id:
|
|
203
210
|
case "turkceAltyazili":
|