KekikStream 2.3.2__py3-none-any.whl → 2.3.4__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 +4 -1
- KekikStream/Core/__init__.py +2 -0
- KekikStream/Extractors/CloseLoad.py +12 -13
- KekikStream/Extractors/ContentX.py +12 -15
- 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 +5 -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/DiziYou.py +95 -88
- KekikStream/Plugins/Dizilla.py +51 -71
- KekikStream/Plugins/FilmBip.py +24 -49
- KekikStream/Plugins/FilmMakinesi.py +35 -52
- KekikStream/Plugins/FilmModu.py +27 -41
- KekikStream/Plugins/FullHDFilm.py +57 -62
- 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 +34 -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-2.3.2.dist-info → kekikstream-2.3.4.dist-info}/METADATA +1 -1
- kekikstream-2.3.4.dist-info/RECORD +83 -0
- kekikstream-2.3.2.dist-info/RECORD +0 -82
- {kekikstream-2.3.2.dist-info → kekikstream-2.3.4.dist-info}/WHEEL +0 -0
- {kekikstream-2.3.2.dist-info → kekikstream-2.3.4.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.3.2.dist-info → kekikstream-2.3.4.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.3.2.dist-info → kekikstream-2.3.4.dist-info}/top_level.txt +0 -0
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":
|
KekikStream/Plugins/Dizilla.py
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
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, ExtractResult
|
|
4
|
-
from selectolax.parser import HTMLParser
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult, HTMLHelper
|
|
5
4
|
from json import loads
|
|
6
5
|
from urllib.parse import urlparse, urlunparse
|
|
7
6
|
from Crypto.Cipher import AES
|
|
8
7
|
from base64 import b64decode
|
|
9
|
-
import re
|
|
10
8
|
|
|
11
9
|
class Dizilla(PluginBase):
|
|
12
10
|
name = "Dizilla"
|
|
@@ -52,45 +50,26 @@ class Dizilla(PluginBase):
|
|
|
52
50
|
])
|
|
53
51
|
else:
|
|
54
52
|
istek = await self.httpx.get(url.replace("SAYFA", str(page)))
|
|
55
|
-
secici =
|
|
53
|
+
secici = HTMLHelper(istek.text)
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
opacity_el = veri.css_first("div[class*='opacity-80']")
|
|
63
|
-
ep_name = opacity_el.text(strip=True) if opacity_el else None
|
|
64
|
-
if not ep_name:
|
|
55
|
+
# Genel olarak dizi sayfalarına giden linkleri al
|
|
56
|
+
for veri in secici.select('a[href*="/dizi/"]'):
|
|
57
|
+
href = secici.select_attr('a', 'href', veri)
|
|
58
|
+
title = secici.select_text(None, veri)
|
|
59
|
+
if not href or not title:
|
|
65
60
|
continue
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
href = link_el.attrs.get("href") if link_el else None
|
|
79
|
-
else:
|
|
80
|
-
href = None
|
|
81
|
-
|
|
82
|
-
poster_el = ep_secici.css_first("img.imgt")
|
|
83
|
-
poster = poster_el.attrs.get("src") if poster_el else None
|
|
84
|
-
|
|
85
|
-
if href:
|
|
86
|
-
ana_sayfa.append(
|
|
87
|
-
MainPageResult(
|
|
88
|
-
category = category,
|
|
89
|
-
title = title,
|
|
90
|
-
url = self.fix_url(href),
|
|
91
|
-
poster = self.fix_url(poster) if poster else None
|
|
92
|
-
)
|
|
93
|
-
)
|
|
62
|
+
# Detay sayfasından poster vb. bilgileri al
|
|
63
|
+
ep_req = await self.httpx.get(self.fix_url(href))
|
|
64
|
+
ep_secici = HTMLHelper(ep_req.text)
|
|
65
|
+
poster = ep_secici.select_attr('img.imgt', 'src') or ep_secici.select_attr('img', 'src')
|
|
66
|
+
|
|
67
|
+
ana_sayfa.append(MainPageResult(
|
|
68
|
+
category = category,
|
|
69
|
+
title = title,
|
|
70
|
+
url = self.fix_url(href),
|
|
71
|
+
poster = self.fix_url(poster) if poster else None
|
|
72
|
+
))
|
|
94
73
|
|
|
95
74
|
return ana_sayfa
|
|
96
75
|
|
|
@@ -124,9 +103,10 @@ class Dizilla(PluginBase):
|
|
|
124
103
|
# -> https://images.macellan.online/...
|
|
125
104
|
if "cdn.ampproject.org" in url:
|
|
126
105
|
# /i/s/ veya /ii/s/ gibi AMP prefix'lerinden sonraki kısmı al
|
|
127
|
-
|
|
106
|
+
helper = HTMLHelper(url)
|
|
107
|
+
match = helper.regex_first(r"cdn\.ampproject\.org/[^/]+/s/(.+)$")
|
|
128
108
|
if match:
|
|
129
|
-
return f"https://{match
|
|
109
|
+
return f"https://{match}"
|
|
130
110
|
return url
|
|
131
111
|
|
|
132
112
|
async def search(self, query: str) -> list[SearchResult]:
|
|
@@ -155,64 +135,64 @@ class Dizilla(PluginBase):
|
|
|
155
135
|
|
|
156
136
|
async def load_item(self, url: str) -> SeriesInfo:
|
|
157
137
|
istek = await self.httpx.get(url)
|
|
158
|
-
secici =
|
|
138
|
+
secici = HTMLHelper(istek.text)
|
|
159
139
|
|
|
160
|
-
title = secici.
|
|
161
|
-
title = title.text(strip=True) if title else None
|
|
140
|
+
title = secici.select_text("div.poster.poster h2")
|
|
162
141
|
if not title:
|
|
163
142
|
return None
|
|
164
143
|
|
|
165
|
-
|
|
166
|
-
poster = self.fix_url(
|
|
144
|
+
poster = secici.select_attr("div.w-full.page-top.relative img", "src")
|
|
145
|
+
poster = self.fix_url(poster) if poster else None
|
|
167
146
|
|
|
168
147
|
# Year extraction (Kotlin: [1] index for w-fit min-w-fit)
|
|
169
|
-
info_boxes = secici.
|
|
148
|
+
info_boxes = secici.select("div.w-fit.min-w-fit")
|
|
170
149
|
year = None
|
|
171
150
|
if len(info_boxes) > 1:
|
|
172
|
-
|
|
173
|
-
if
|
|
174
|
-
year_text = year_el.text(strip=True)
|
|
151
|
+
year_text = secici.select_text("span.text-sm.opacity-60", info_boxes[1])
|
|
152
|
+
if year_text:
|
|
175
153
|
year = year_text.split(" ")[-1] if " " in year_text else year_text
|
|
176
154
|
|
|
177
|
-
|
|
178
|
-
description = description_el.text(strip=True) if description_el else None
|
|
155
|
+
description = secici.select_text("div.mt-2.text-sm")
|
|
179
156
|
|
|
180
|
-
|
|
181
|
-
tags = [t.strip() for t in
|
|
157
|
+
tags_text = secici.select_text("div.poster.poster h3")
|
|
158
|
+
tags = [t.strip() for t in tags_text.split(",")] if tags_text else []
|
|
182
159
|
|
|
183
|
-
actors =
|
|
160
|
+
actors = secici.select_all_text("div.global-box h5")
|
|
184
161
|
|
|
185
162
|
episodeses = []
|
|
186
163
|
# Seasons links iteration
|
|
187
|
-
season_links = secici.
|
|
164
|
+
season_links = secici.select("div.flex.items-center.flex-wrap.gap-2.mb-4 a")
|
|
188
165
|
for sezon in season_links:
|
|
189
|
-
sezon_href =
|
|
166
|
+
sezon_href = secici.select_attr("a", "href", sezon)
|
|
167
|
+
sezon_href = self.fix_url(sezon_href)
|
|
190
168
|
sezon_req = await self.httpx.get(sezon_href)
|
|
191
169
|
|
|
192
170
|
season_num = None
|
|
193
171
|
try:
|
|
194
172
|
# URL'den sezon numarasını çek: ...-N-sezon formatı
|
|
195
|
-
season_match =
|
|
173
|
+
season_match = secici.regex_first(r"-(\d+)-sezon", sezon_href)
|
|
196
174
|
if season_match:
|
|
197
|
-
season_num = int(season_match
|
|
175
|
+
season_num = int(season_match)
|
|
198
176
|
except:
|
|
199
177
|
pass
|
|
200
178
|
|
|
201
|
-
sezon_secici =
|
|
202
|
-
for bolum in sezon_secici.
|
|
179
|
+
sezon_secici = HTMLHelper(sezon_req.text)
|
|
180
|
+
for bolum in sezon_secici.select("div.episodes div.cursor-pointer"):
|
|
203
181
|
# Kotlin: bolum.select("a").last()
|
|
204
|
-
links =
|
|
182
|
+
links = sezon_secici.select("a", bolum)
|
|
205
183
|
if not links:
|
|
206
184
|
continue
|
|
207
185
|
|
|
208
186
|
ep_link = links[-1]
|
|
209
|
-
ep_name =
|
|
210
|
-
ep_href =
|
|
187
|
+
ep_name = sezon_secici.select_text("a", ep_link)
|
|
188
|
+
ep_href = sezon_secici.select_attr("a", "href", ep_link)
|
|
189
|
+
ep_href = self.fix_url(ep_href)
|
|
211
190
|
|
|
212
191
|
# Episode number (first link's text usually)
|
|
213
192
|
ep_num = None
|
|
214
193
|
try:
|
|
215
|
-
|
|
194
|
+
ep_num_text = sezon_secici.select_text("a", links[0])
|
|
195
|
+
ep_num = int(ep_num_text) if ep_num_text else None
|
|
216
196
|
except:
|
|
217
197
|
pass
|
|
218
198
|
|
|
@@ -236,13 +216,13 @@ class Dizilla(PluginBase):
|
|
|
236
216
|
|
|
237
217
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
238
218
|
istek = await self.httpx.get(url)
|
|
239
|
-
secici =
|
|
219
|
+
secici = HTMLHelper(istek.text)
|
|
240
220
|
|
|
241
|
-
|
|
242
|
-
if not
|
|
221
|
+
next_data_text = secici.select_text("script#__NEXT_DATA__")
|
|
222
|
+
if not next_data_text:
|
|
243
223
|
return []
|
|
244
224
|
|
|
245
|
-
next_data
|
|
225
|
+
next_data = loads(next_data_text)
|
|
246
226
|
secure_data = next_data.get("props", {}).get("pageProps", {}).get("secureData", {})
|
|
247
227
|
decrypted = await self.decrypt_response(secure_data)
|
|
248
228
|
results = decrypted.get("RelatedResults", {}).get("getEpisodeSources", {}).get("result", [])
|
|
@@ -258,8 +238,8 @@ class Dizilla(PluginBase):
|
|
|
258
238
|
cleaned_source = source_content.replace('"', '').replace('\\', '')
|
|
259
239
|
|
|
260
240
|
# Parse cleaned HTML
|
|
261
|
-
|
|
262
|
-
iframe_src =
|
|
241
|
+
iframe_secici = HTMLHelper(cleaned_source)
|
|
242
|
+
iframe_src = iframe_secici.select_attr("iframe", "src")
|
|
263
243
|
|
|
264
244
|
# Referer check (matching Kotlin: loadExtractor(iframe, "${mainUrl}/", ...))
|
|
265
245
|
iframe_url = self.fix_url(iframe_src) if iframe_src else None
|
KekikStream/Plugins/FilmBip.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, MovieInfo, ExtractResult
|
|
4
|
-
from selectolax.parser import HTMLParser
|
|
5
|
-
import re
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
|
|
6
4
|
|
|
7
5
|
class FilmBip(PluginBase):
|
|
8
6
|
name = "FilmBip"
|
|
@@ -37,16 +35,13 @@ class FilmBip(PluginBase):
|
|
|
37
35
|
page_url = page_url.rstrip("/")
|
|
38
36
|
|
|
39
37
|
istek = await self.httpx.get(page_url)
|
|
40
|
-
secici =
|
|
38
|
+
secici = HTMLHelper(istek.text)
|
|
41
39
|
|
|
42
40
|
results = []
|
|
43
|
-
for veri in secici.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
title = img.attrs.get("alt") if img else None
|
|
48
|
-
href = link_el.attrs.get("href") if link_el else None
|
|
49
|
-
poster = (img.attrs.get("data-src") or img.attrs.get("src")) if img else None
|
|
41
|
+
for veri in secici.select("div.poster-long"):
|
|
42
|
+
title = secici.select_attr("a.block img.lazy", "alt", veri)
|
|
43
|
+
href = secici.select_attr("a.block", "href", veri)
|
|
44
|
+
poster = secici.select_poster("a.block img.lazy", veri)
|
|
50
45
|
|
|
51
46
|
if title and href:
|
|
52
47
|
results.append(MainPageResult(
|
|
@@ -80,17 +75,13 @@ class FilmBip(PluginBase):
|
|
|
80
75
|
except Exception:
|
|
81
76
|
return []
|
|
82
77
|
|
|
83
|
-
secici =
|
|
78
|
+
secici = HTMLHelper(html_content)
|
|
84
79
|
|
|
85
80
|
results = []
|
|
86
|
-
for veri in secici.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
title = link_el.text(strip=True) if link_el else None
|
|
92
|
-
href = href_el.attrs.get("href") if href_el else None
|
|
93
|
-
poster = img_el.attrs.get("data-src") if img_el else None
|
|
81
|
+
for veri in secici.select("li"):
|
|
82
|
+
title = secici.select_text("a.block.truncate", veri)
|
|
83
|
+
href = secici.select_attr("a", "href", veri)
|
|
84
|
+
poster = secici.select_attr("img.lazy", "data-src", veri)
|
|
94
85
|
|
|
95
86
|
if title and href:
|
|
96
87
|
results.append(SearchResult(
|
|
@@ -103,42 +94,27 @@ class FilmBip(PluginBase):
|
|
|
103
94
|
|
|
104
95
|
async def load_item(self, url: str) -> MovieInfo:
|
|
105
96
|
istek = await self.httpx.get(url)
|
|
106
|
-
secici =
|
|
97
|
+
secici = HTMLHelper(istek.text)
|
|
107
98
|
html_text = istek.text
|
|
108
99
|
|
|
109
|
-
|
|
110
|
-
title = title_el.text(strip=True) if title_el else ""
|
|
100
|
+
title = secici.select_text("div.page-title h1") or ""
|
|
111
101
|
|
|
112
|
-
|
|
113
|
-
poster = og_image.attrs.get("content") if og_image else None
|
|
102
|
+
poster = secici.select_attr("meta[property='og:image']", "content")
|
|
114
103
|
|
|
115
|
-
|
|
116
|
-
trailer = trailer_el.attrs.get("data-yt") if trailer_el else None
|
|
104
|
+
trailer = secici.select_attr("div.series-profile-trailer", "data-yt")
|
|
117
105
|
|
|
118
|
-
|
|
119
|
-
if not desc_el:
|
|
120
|
-
desc_el = secici.css_first("div.series-profile-summary p")
|
|
121
|
-
description = desc_el.text(strip=True) if desc_el else None
|
|
106
|
+
description = secici.select_text("div.series-profile-infos-in.article p") or secici.select_text("div.series-profile-summary p")
|
|
122
107
|
|
|
123
|
-
tags =
|
|
108
|
+
tags = secici.select_all_text("div.series-profile-type.tv-show-profile-type a")
|
|
124
109
|
|
|
125
110
|
# XPath yerine regex kullanarak yıl, süre vs. çıkarma
|
|
126
|
-
year =
|
|
127
|
-
year_match = re.search(r'Yapım yılı.*?<p[^>]*>(\d{4})</p>', html_text, re.IGNORECASE | re.DOTALL)
|
|
128
|
-
if year_match:
|
|
129
|
-
year = year_match.group(1)
|
|
111
|
+
year = secici.regex_first(r'(?i)Yapım yılı.*?<p[^>]*>(\d{4})</p>', secici.html)
|
|
130
112
|
|
|
131
|
-
duration =
|
|
132
|
-
duration_match = re.search(r'Süre.*?<p[^>]*>(\d+)', html_text, re.IGNORECASE | re.DOTALL)
|
|
133
|
-
if duration_match:
|
|
134
|
-
duration = duration_match.group(1)
|
|
113
|
+
duration = secici.regex_first(r'(?i)Süre.*?<p[^>]*>(\d+)', secici.html)
|
|
135
114
|
|
|
136
|
-
rating =
|
|
137
|
-
rating_match = re.search(r'IMDB Puanı.*?<span[^>]*>([0-9.]+)</span>', html_text, re.IGNORECASE | re.DOTALL)
|
|
138
|
-
if rating_match:
|
|
139
|
-
rating = rating_match.group(1)
|
|
115
|
+
rating = secici.regex_first(r'(?i)IMDB Puanı.*?<span[^>]*>([0-9.]+)</span>', secici.html)
|
|
140
116
|
|
|
141
|
-
actors = [img.attrs.get("alt") for img in secici.
|
|
117
|
+
actors = [img.attrs.get("alt") for img in secici.select("div.series-profile-cast ul li a img") if img.attrs.get("alt")]
|
|
142
118
|
|
|
143
119
|
return MovieInfo(
|
|
144
120
|
url = url,
|
|
@@ -154,13 +130,12 @@ class FilmBip(PluginBase):
|
|
|
154
130
|
|
|
155
131
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
156
132
|
istek = await self.httpx.get(url)
|
|
157
|
-
secici =
|
|
133
|
+
secici = HTMLHelper(istek.text)
|
|
158
134
|
|
|
159
135
|
results = []
|
|
160
136
|
|
|
161
|
-
for player in secici.
|
|
162
|
-
|
|
163
|
-
iframe = iframe_el.attrs.get("src") if iframe_el else None
|
|
137
|
+
for player in secici.select("div#tv-spoox2"):
|
|
138
|
+
iframe = secici.select_attr("iframe", "src", player)
|
|
164
139
|
|
|
165
140
|
if iframe:
|
|
166
141
|
iframe = self.fix_url(iframe)
|