KekikStream 2.2.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.
Potentially problematic release.
This version of KekikStream might be problematic. Click here for more details.
- KekikStream/Core/Extractor/ExtractorBase.py +3 -2
- KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
- KekikStream/Core/HTMLHelper.py +205 -0
- KekikStream/Core/Plugin/PluginBase.py +48 -12
- KekikStream/Core/Plugin/PluginLoader.py +13 -14
- KekikStream/Core/Plugin/PluginManager.py +2 -2
- KekikStream/Core/Plugin/PluginModels.py +0 -3
- KekikStream/Core/__init__.py +2 -0
- KekikStream/Extractors/Abstream.py +27 -0
- KekikStream/Extractors/CloseLoad.py +31 -56
- KekikStream/Extractors/ContentX.py +28 -71
- KekikStream/Extractors/DonilasPlay.py +34 -78
- KekikStream/Extractors/DzenRu.py +11 -25
- KekikStream/Extractors/ExPlay.py +20 -38
- KekikStream/Extractors/Filemoon.py +23 -53
- 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 +17 -31
- KekikStream/Extractors/MixTiger.py +17 -40
- KekikStream/Extractors/MolyStream.py +25 -22
- KekikStream/Extractors/Odnoklassniki.py +41 -105
- KekikStream/Extractors/PeaceMakerst.py +20 -47
- KekikStream/Extractors/PixelDrain.py +9 -16
- KekikStream/Extractors/PlayerFilmIzle.py +23 -46
- KekikStream/Extractors/RapidVid.py +23 -36
- KekikStream/Extractors/SetPlay.py +19 -44
- KekikStream/Extractors/SetPrime.py +3 -6
- KekikStream/Extractors/SibNet.py +8 -19
- KekikStream/Extractors/Sobreatsesuyp.py +25 -47
- KekikStream/Extractors/TRsTX.py +25 -55
- KekikStream/Extractors/TurboImgz.py +8 -16
- KekikStream/Extractors/TurkeyPlayer.py +5 -5
- KekikStream/Extractors/VCTPlay.py +10 -28
- KekikStream/Extractors/Veev.py +145 -0
- KekikStream/Extractors/VidBiz.py +62 -0
- KekikStream/Extractors/VidHide.py +59 -34
- KekikStream/Extractors/VidMoly.py +67 -89
- KekikStream/Extractors/VidMoxy.py +17 -29
- KekikStream/Extractors/VidPapi.py +26 -58
- KekikStream/Extractors/VideoSeyred.py +21 -42
- 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 +108 -99
- KekikStream/Plugins/DiziBox.py +61 -106
- KekikStream/Plugins/DiziMom.py +179 -0
- KekikStream/Plugins/DiziPal.py +104 -192
- KekikStream/Plugins/DiziYou.py +66 -149
- KekikStream/Plugins/Dizilla.py +93 -126
- KekikStream/Plugins/FilmBip.py +102 -72
- KekikStream/Plugins/FilmEkseni.py +199 -0
- KekikStream/Plugins/FilmMakinesi.py +101 -64
- KekikStream/Plugins/FilmModu.py +35 -59
- KekikStream/Plugins/Filmatek.py +184 -0
- KekikStream/Plugins/FilmciBaba.py +155 -0
- KekikStream/Plugins/FullHDFilmizlesene.py +32 -78
- KekikStream/Plugins/HDFilm.py +243 -0
- KekikStream/Plugins/HDFilmCehennemi.py +261 -222
- KekikStream/Plugins/JetFilmizle.py +117 -98
- KekikStream/Plugins/KultFilmler.py +153 -143
- KekikStream/Plugins/RecTV.py +53 -49
- KekikStream/Plugins/RoketDizi.py +92 -123
- KekikStream/Plugins/SelcukFlix.py +86 -95
- KekikStream/Plugins/SetFilmIzle.py +105 -143
- KekikStream/Plugins/SezonlukDizi.py +106 -128
- KekikStream/Plugins/Sinefy.py +194 -166
- KekikStream/Plugins/SinemaCX.py +159 -113
- KekikStream/Plugins/Sinezy.py +44 -73
- KekikStream/Plugins/SuperFilmGeldi.py +28 -52
- KekikStream/Plugins/UgurFilm.py +94 -72
- KekikStream/Plugins/Watch32.py +160 -0
- KekikStream/Plugins/YabanciDizi.py +250 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/METADATA +1 -1
- kekikstream-2.5.3.dist-info/RECORD +99 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/WHEEL +1 -1
- KekikStream/Plugins/FullHDFilm.py +0 -254
- kekikstream-2.2.9.dist-info/RECORD +0 -82
- {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/top_level.txt +0 -0
KekikStream/Plugins/FilmBip.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core
|
|
4
|
-
from
|
|
5
|
-
import re
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
|
|
4
|
+
from contextlib import suppress
|
|
6
5
|
|
|
7
6
|
class FilmBip(PluginBase):
|
|
8
7
|
name = "FilmBip"
|
|
@@ -37,23 +36,20 @@ class FilmBip(PluginBase):
|
|
|
37
36
|
page_url = page_url.rstrip("/")
|
|
38
37
|
|
|
39
38
|
istek = await self.httpx.get(page_url)
|
|
40
|
-
secici =
|
|
39
|
+
secici = HTMLHelper(istek.text)
|
|
41
40
|
|
|
42
41
|
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
|
|
42
|
+
for veri in secici.select("div.poster-long"):
|
|
43
|
+
title = secici.select_attr("a.block img.lazy", "alt", veri)
|
|
44
|
+
href = secici.select_attr("a.block", "href", veri)
|
|
45
|
+
poster = secici.select_poster("a.block img.lazy", veri)
|
|
50
46
|
|
|
51
47
|
if title and href:
|
|
52
48
|
results.append(MainPageResult(
|
|
53
49
|
category = category,
|
|
54
50
|
title = title,
|
|
55
51
|
url = self.fix_url(href),
|
|
56
|
-
poster = self.fix_url(poster)
|
|
52
|
+
poster = self.fix_url(poster),
|
|
57
53
|
))
|
|
58
54
|
|
|
59
55
|
return results
|
|
@@ -80,92 +76,126 @@ class FilmBip(PluginBase):
|
|
|
80
76
|
except Exception:
|
|
81
77
|
return []
|
|
82
78
|
|
|
83
|
-
secici =
|
|
79
|
+
secici = HTMLHelper(html_content)
|
|
84
80
|
|
|
85
81
|
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
|
|
82
|
+
for veri in secici.select("li"):
|
|
83
|
+
title = secici.select_text("a.block.truncate", veri)
|
|
84
|
+
href = secici.select_attr("a", "href", veri)
|
|
85
|
+
poster = secici.select_attr("img.lazy", "data-src", veri)
|
|
94
86
|
|
|
95
87
|
if title and href:
|
|
96
88
|
results.append(SearchResult(
|
|
97
89
|
title = title.strip(),
|
|
98
90
|
url = self.fix_url(href),
|
|
99
|
-
poster = self.fix_url(poster)
|
|
91
|
+
poster = self.fix_url(poster),
|
|
100
92
|
))
|
|
101
93
|
|
|
102
94
|
return results
|
|
103
95
|
|
|
104
96
|
async def load_item(self, url: str) -> MovieInfo:
|
|
105
97
|
istek = await self.httpx.get(url)
|
|
106
|
-
secici =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
desc_el = secici.css_first("div.series-profile-infos-in.article p")
|
|
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
|
|
122
|
-
|
|
123
|
-
tags = [a.text(strip=True) for a in secici.css("div.series-profile-type.tv-show-profile-type a") if a.text(strip=True)]
|
|
124
|
-
|
|
125
|
-
# XPath yerine regex kullanarak yıl, süre vs. çıkarma
|
|
126
|
-
year = None
|
|
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)
|
|
130
|
-
|
|
131
|
-
duration = None
|
|
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)
|
|
135
|
-
|
|
136
|
-
rating = None
|
|
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)
|
|
140
|
-
|
|
141
|
-
actors = [img.attrs.get("alt") for img in secici.css("div.series-profile-cast ul li a img") if img.attrs.get("alt")]
|
|
98
|
+
secici = HTMLHelper(istek.text)
|
|
99
|
+
|
|
100
|
+
title = self.clean_title(secici.select_direct_text("div.page-title h1"))
|
|
101
|
+
poster = secici.select_poster("div.series-profile-image a img")
|
|
102
|
+
description = secici.select_text("div.series-profile-infos-in.article p") or secici.select_text("div.series-profile-summary p")
|
|
103
|
+
tags = secici.select_texts("div.series-profile-type.tv-show-profile-type a")
|
|
104
|
+
year = secici.extract_year("div.series-profile-infos-in") or secici.regex_first(r"\((\d{4})\)", title)
|
|
105
|
+
duration = secici.regex_first(r"(\d+)", secici.meta_value("Süre", container_selector="div.series-profile-infos"))
|
|
106
|
+
rating = secici.meta_value("IMDB Puanı", container_selector="div.series-profile-infos")
|
|
107
|
+
rating = rating.split("(")[0] if rating else None
|
|
108
|
+
actors = secici.select_attrs("div.series-profile-cast ul li a img", "alt")
|
|
142
109
|
|
|
143
110
|
return MovieInfo(
|
|
144
111
|
url = url,
|
|
145
|
-
poster = self.fix_url(poster)
|
|
146
|
-
title =
|
|
112
|
+
poster = self.fix_url(poster),
|
|
113
|
+
title = title,
|
|
147
114
|
description = description,
|
|
148
115
|
tags = tags,
|
|
149
116
|
year = year,
|
|
150
117
|
rating = rating,
|
|
151
|
-
duration =
|
|
118
|
+
duration = duration,
|
|
152
119
|
actors = actors,
|
|
153
120
|
)
|
|
154
121
|
|
|
155
122
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
156
123
|
istek = await self.httpx.get(url)
|
|
157
|
-
secici =
|
|
124
|
+
secici = HTMLHelper(istek.text)
|
|
158
125
|
|
|
159
126
|
results = []
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
127
|
+
for tab in secici.select("ul.tab.alternative-group li[data-number]"):
|
|
128
|
+
tab_id = tab.attrs.get("data-number")
|
|
129
|
+
tab_name = secici.select_text(None, tab)
|
|
130
|
+
tab_hash = tab.attrs.get("data-group-hash")
|
|
131
|
+
|
|
132
|
+
if not tab_id:
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
button_data = [] # (player_name, iframe_url)
|
|
136
|
+
|
|
137
|
+
# İlgili content divini bul
|
|
138
|
+
content_div = secici.select_first(f"div#{tab_id}")
|
|
139
|
+
|
|
140
|
+
# Eğer div var ve içi doluysa oradan al
|
|
141
|
+
if content_div and secici.select("ul li button", content_div):
|
|
142
|
+
buttons = secici.select("ul li button", content_div)
|
|
143
|
+
for btn in buttons:
|
|
144
|
+
button_data.append((btn.text(strip=True), btn.attrs.get("data-hhs")))
|
|
145
|
+
|
|
146
|
+
elif tab_hash:
|
|
147
|
+
# Div yok veya boş, AJAX ile çek
|
|
148
|
+
with suppress(Exception):
|
|
149
|
+
hash_resp = await self.httpx.post(
|
|
150
|
+
url = f"{self.main_url}/get/video/group",
|
|
151
|
+
headers = {
|
|
152
|
+
"X-Requested-With" : "XMLHttpRequest",
|
|
153
|
+
"Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8",
|
|
154
|
+
"Referer" : url
|
|
155
|
+
},
|
|
156
|
+
data = {"hash": tab_hash}
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if hash_resp.status_code == 200:
|
|
160
|
+
json_data = hash_resp.json()
|
|
161
|
+
if json_data.get("success"):
|
|
162
|
+
# 1. Videos listesi (API yanıtı)
|
|
163
|
+
if videos := json_data.get("videos"):
|
|
164
|
+
for vid in videos:
|
|
165
|
+
button_data.append((vid.get("name"), vid.get("link")))
|
|
166
|
+
|
|
167
|
+
# 2. HTML content (Fallback)
|
|
168
|
+
else:
|
|
169
|
+
html_content = json_data.get("content") or json_data.get("html") or json_data.get("theme")
|
|
170
|
+
if html_content:
|
|
171
|
+
sub_helper = HTMLHelper(html_content)
|
|
172
|
+
sub_btns = sub_helper.select("ul li button")
|
|
173
|
+
for btn in sub_btns:
|
|
174
|
+
button_data.append((btn.text(strip=True), btn.attrs.get("data-hhs")))
|
|
175
|
+
|
|
176
|
+
for player_name, iframe_url in button_data:
|
|
177
|
+
with suppress(Exception):
|
|
178
|
+
if iframe_url:
|
|
179
|
+
data = await self.extract(
|
|
180
|
+
url = self.fix_url(iframe_url),
|
|
181
|
+
name_override = f"{tab_name} | {player_name}"
|
|
182
|
+
)
|
|
183
|
+
if data:
|
|
184
|
+
if isinstance(data, list):
|
|
185
|
+
results.extend(data)
|
|
186
|
+
else:
|
|
187
|
+
results.append(data)
|
|
188
|
+
|
|
189
|
+
# Eğer hiç sonuç bulunamazsa fallback
|
|
190
|
+
if not results:
|
|
191
|
+
for player in secici.select("div#tv-spoox2"):
|
|
192
|
+
if iframe := secici.select_attr("iframe", "src", player):
|
|
193
|
+
iframe = self.fix_url(iframe)
|
|
194
|
+
data = await self.extract(iframe)
|
|
195
|
+
if data:
|
|
196
|
+
if isinstance(data, list):
|
|
197
|
+
results.extend(data)
|
|
198
|
+
else:
|
|
199
|
+
results.append(data)
|
|
170
200
|
|
|
171
201
|
return results
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
|
|
4
|
+
import asyncio
|
|
5
|
+
|
|
6
|
+
class FilmEkseni(PluginBase):
|
|
7
|
+
name = "FilmEkseni"
|
|
8
|
+
language = "tr"
|
|
9
|
+
main_url = "https://filmekseni.cc"
|
|
10
|
+
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
11
|
+
description = "Film Ekseni ⚡️ Vizyonda ki, en güncel ve en yeni filmleri full hd kalitesinde türkçe dublaj ve altyazı seçenekleriyle 1080p olarak izleyebileceğiniz adresiniz."
|
|
12
|
+
|
|
13
|
+
main_page = {
|
|
14
|
+
f"{main_url}/tur/aile-filmleri/page" : "Aile Filmleri",
|
|
15
|
+
f"{main_url}/tur/aksiyon-filmleri/page" : "Aksiyon Filmleri",
|
|
16
|
+
f"{main_url}/tur/animasyon-film-izle/page" : "Animasyon Filmleri",
|
|
17
|
+
f"{main_url}/tur/bilim-kurgu-filmleri/page" : "Bilim Kurgu Filmleri",
|
|
18
|
+
f"{main_url}/tur/biyografi-filmleri/page" : "Biyografi Filmleri",
|
|
19
|
+
f"{main_url}/tur/dram-filmleri-izle/page" : "Dram Filmleri",
|
|
20
|
+
f"{main_url}/tur/fantastik-filmler/page" : "Fantastik Filmleri",
|
|
21
|
+
f"{main_url}/tur/gerilim-filmleri/page" : "Gerilim Filmleri",
|
|
22
|
+
f"{main_url}/tur/gizem-filmleri/page" : "Gizem Filmleri",
|
|
23
|
+
f"{main_url}/tur/komedi-filmleri/page" : "Komedi Filmleri",
|
|
24
|
+
f"{main_url}/tur/korku-filmleri/page" : "Korku Filmleri",
|
|
25
|
+
f"{main_url}/tur/macera-filmleri/page" : "Macera Filmleri",
|
|
26
|
+
f"{main_url}/tur/romantik-filmler/page" : "Romantik Filmleri",
|
|
27
|
+
f"{main_url}/tur/savas-filmleri/page" : "Savaş Filmleri",
|
|
28
|
+
f"{main_url}/tur/suc-filmleri/page" : "Suç Filmleri",
|
|
29
|
+
f"{main_url}/tur/tarih-filmleri/page" : "Tarih Filmleri",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
33
|
+
istek = await self.httpx.get(f"{url}/{page}/")
|
|
34
|
+
secici = HTMLHelper(istek.text)
|
|
35
|
+
posters = secici.select("div.poster")
|
|
36
|
+
|
|
37
|
+
return [
|
|
38
|
+
MainPageResult(
|
|
39
|
+
category = category,
|
|
40
|
+
title = self.clean_title(secici.select_text("h2", veri)),
|
|
41
|
+
url = secici.select_attr("a", "href", veri),
|
|
42
|
+
poster = secici.select_attr("img", "data-src", veri)
|
|
43
|
+
)
|
|
44
|
+
for veri in posters
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
async def search(self, query: str) -> list[SearchResult]:
|
|
48
|
+
istek = await self.httpx.post(
|
|
49
|
+
url = f"{self.main_url}/search/",
|
|
50
|
+
headers = {
|
|
51
|
+
"X-Requested-With" : "XMLHttpRequest",
|
|
52
|
+
"Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8",
|
|
53
|
+
"Referer" : self.main_url,
|
|
54
|
+
},
|
|
55
|
+
data = {"query": query}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
veriler = istek.json().get("result", [])
|
|
59
|
+
|
|
60
|
+
return [
|
|
61
|
+
SearchResult(
|
|
62
|
+
title = veri.get("title"),
|
|
63
|
+
url = f"{self.main_url}/{veri.get('slug')}",
|
|
64
|
+
poster = f"{self.main_url}/uploads/poster/{veri.get('cover')}" if veri.get('cover') else None,
|
|
65
|
+
)
|
|
66
|
+
for veri in veriler
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
async def load_item(self, url: str) -> MovieInfo:
|
|
70
|
+
istek = await self.httpx.get(url)
|
|
71
|
+
secici = HTMLHelper(istek.text)
|
|
72
|
+
|
|
73
|
+
title = self.clean_title(secici.select_text("div.page-title h1"))
|
|
74
|
+
poster = secici.select_poster("picture.poster-auto img")
|
|
75
|
+
description = secici.select_direct_text("article.text-white p")
|
|
76
|
+
year = secici.extract_year("div.page-title", "strong a")
|
|
77
|
+
tags = secici.select_texts("div.pb-2 a[href*='/tur/']")
|
|
78
|
+
rating = secici.select_text("div.rate")
|
|
79
|
+
duration = secici.regex_first(r"(\d+)", secici.select_text("div.d-flex.flex-column.text-nowrap"))
|
|
80
|
+
actors = secici.select_texts("div.card-body.p-0.pt-2 .story-item .story-item-title")
|
|
81
|
+
|
|
82
|
+
return MovieInfo(
|
|
83
|
+
url = url,
|
|
84
|
+
poster = self.fix_url(poster),
|
|
85
|
+
title = title,
|
|
86
|
+
description = description,
|
|
87
|
+
tags = tags,
|
|
88
|
+
rating = rating,
|
|
89
|
+
year = year,
|
|
90
|
+
actors = actors,
|
|
91
|
+
duration = duration
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
async def _get_source_links(self, name: str, url: str, is_active: bool, initial_helper: HTMLHelper | None = None) -> list[ExtractResult]:
|
|
95
|
+
try:
|
|
96
|
+
if is_active and initial_helper:
|
|
97
|
+
secici = initial_helper
|
|
98
|
+
else:
|
|
99
|
+
resp = await self.httpx.get(url)
|
|
100
|
+
secici = HTMLHelper(resp.text)
|
|
101
|
+
|
|
102
|
+
iframe = secici.select_first("div.card-video iframe")
|
|
103
|
+
if not iframe:
|
|
104
|
+
return []
|
|
105
|
+
|
|
106
|
+
iframe_url = iframe.attrs.get("data-src") or iframe.attrs.get("src")
|
|
107
|
+
if not iframe_url:
|
|
108
|
+
return []
|
|
109
|
+
|
|
110
|
+
iframe_url = self.fix_url(iframe_url)
|
|
111
|
+
results = []
|
|
112
|
+
|
|
113
|
+
# VIP / EksenLoad mantığı
|
|
114
|
+
if "eksenload" in iframe_url or name == "VIP":
|
|
115
|
+
video_id = iframe_url.split("/")[-1]
|
|
116
|
+
master_url = f"https://eksenload.site/uploads/encode/{video_id}/master.m3u8"
|
|
117
|
+
results.append(ExtractResult(
|
|
118
|
+
url = master_url,
|
|
119
|
+
name = name,
|
|
120
|
+
referer = self.main_url
|
|
121
|
+
))
|
|
122
|
+
else:
|
|
123
|
+
# Diğerleri (Moly, vs.) için extract
|
|
124
|
+
# Name override: "Kaynak Adı | Player Adı" olacak şekilde
|
|
125
|
+
extracted = await self.extract(iframe_url, name_override=name)
|
|
126
|
+
if extracted:
|
|
127
|
+
if isinstance(extracted, list):
|
|
128
|
+
results.extend(extracted)
|
|
129
|
+
else:
|
|
130
|
+
results.append(extracted)
|
|
131
|
+
|
|
132
|
+
return results
|
|
133
|
+
except Exception:
|
|
134
|
+
return []
|
|
135
|
+
|
|
136
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
137
|
+
istek = await self.httpx.get(url)
|
|
138
|
+
secici = HTMLHelper(istek.text)
|
|
139
|
+
|
|
140
|
+
# Dil sekmelerini bul (Dublaj, Altyazı vb.)
|
|
141
|
+
# Fragman vb. linkleri dahil etmemek için sadece 'a.nav-link' bakıyoruz
|
|
142
|
+
lang_tabs = [
|
|
143
|
+
tab for tab in secici.select("ul.nav-tabs.nav-slider a.nav-link")
|
|
144
|
+
if "fragman" not in tab.text().lower()
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
# Player panellerini bul
|
|
148
|
+
tab_panes = secici.select("div.tab-pane")
|
|
149
|
+
|
|
150
|
+
sources = [] # (name, url, is_active)
|
|
151
|
+
|
|
152
|
+
# Eğer dil sekmeleri ve paneller eşleşiyorsa (ideal durum)
|
|
153
|
+
if lang_tabs and tab_panes:
|
|
154
|
+
for i, pane in enumerate(tab_panes):
|
|
155
|
+
if i >= len(lang_tabs):
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
lang_name = lang_tabs[i].text(strip=True)
|
|
159
|
+
player_links = secici.select("a.nav-link", element=pane)
|
|
160
|
+
|
|
161
|
+
for link in player_links:
|
|
162
|
+
p_name = link.text(strip=True)
|
|
163
|
+
if not p_name or any(x in p_name.lower() for x in ["paylaş", "indir", "hata"]):
|
|
164
|
+
continue
|
|
165
|
+
|
|
166
|
+
href = link.attrs.get("href")
|
|
167
|
+
if not href or href == "#":
|
|
168
|
+
continue
|
|
169
|
+
|
|
170
|
+
# Yeni isim "Moly | Türkçe Dublaj"
|
|
171
|
+
full_name = f"{p_name} | {lang_name}"
|
|
172
|
+
is_active = "active" in link.attrs.get("class", "")
|
|
173
|
+
|
|
174
|
+
sources.append((full_name, self.fix_url(href), is_active))
|
|
175
|
+
|
|
176
|
+
# Eğer panel yapısı beklediğimizden farklıysa eski mantığa dön
|
|
177
|
+
if not sources:
|
|
178
|
+
if nav_links := secici.select("nav.card-nav a.nav-link"):
|
|
179
|
+
seen_urls = set()
|
|
180
|
+
for link in nav_links:
|
|
181
|
+
if link.attrs.get("href") == "#":
|
|
182
|
+
continue # Sinema Modu vb.
|
|
183
|
+
|
|
184
|
+
name = link.text(strip=True)
|
|
185
|
+
href = link.attrs.get("href")
|
|
186
|
+
is_active = "active" in link.attrs.get("class", "")
|
|
187
|
+
|
|
188
|
+
if href and href not in seen_urls:
|
|
189
|
+
seen_urls.add(href)
|
|
190
|
+
sources.append((name, self.fix_url(href), is_active))
|
|
191
|
+
else:
|
|
192
|
+
# Nav yoksa mevcut sayfayı (Varsayılan/VIP) al
|
|
193
|
+
sources.append(("VIP", url, True))
|
|
194
|
+
|
|
195
|
+
tasks = []
|
|
196
|
+
for name, link_url, is_active in sources:
|
|
197
|
+
tasks.append(self._get_source_links(name, link_url, is_active, secici if is_active else None))
|
|
198
|
+
|
|
199
|
+
return [item for sublist in await asyncio.gather(*tasks) for item in sublist]
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core
|
|
4
|
-
from selectolax.parser import HTMLParser
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, HTMLHelper
|
|
5
4
|
|
|
6
5
|
class FilmMakinesi(PluginBase):
|
|
7
6
|
name = "FilmMakinesi"
|
|
@@ -36,89 +35,96 @@ class FilmMakinesi(PluginBase):
|
|
|
36
35
|
|
|
37
36
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
38
37
|
istek = self.cloudscraper.get(f"{url}{'' if page == 1 else f'page/{page}/'}")
|
|
39
|
-
secici =
|
|
38
|
+
secici = HTMLHelper(istek.text)
|
|
40
39
|
|
|
41
40
|
results = []
|
|
42
|
-
for veri in secici.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
title = title_el.text(strip=True) if title_el else None
|
|
48
|
-
href = link_el.attrs.get("href") if link_el else None
|
|
49
|
-
poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
|
|
41
|
+
for veri in secici.select("div.item-relative"):
|
|
42
|
+
title = secici.select_text("div.title", veri)
|
|
43
|
+
href = secici.select_attr("a", "href", veri)
|
|
44
|
+
poster = secici.select_poster("img", veri)
|
|
50
45
|
|
|
51
46
|
if title and href:
|
|
52
47
|
results.append(MainPageResult(
|
|
53
48
|
category = category,
|
|
54
49
|
title = title,
|
|
55
50
|
url = self.fix_url(href),
|
|
56
|
-
poster = self.fix_url(poster)
|
|
51
|
+
poster = self.fix_url(poster),
|
|
57
52
|
))
|
|
58
53
|
|
|
59
54
|
return results
|
|
60
55
|
|
|
61
56
|
async def search(self, query: str) -> list[SearchResult]:
|
|
62
57
|
istek = await self.httpx.get(f"{self.main_url}/arama/?s={query}")
|
|
63
|
-
secici =
|
|
58
|
+
secici = HTMLHelper(istek.text)
|
|
64
59
|
|
|
65
60
|
results = []
|
|
66
|
-
for article in secici.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
title = title_el.text(strip=True) if title_el else None
|
|
72
|
-
href = link_el.attrs.get("href") if link_el else None
|
|
73
|
-
poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
|
|
61
|
+
for article in secici.select("div.item-relative"):
|
|
62
|
+
title = secici.select_text("div.title", article)
|
|
63
|
+
href = secici.select_attr("a", "href", article)
|
|
64
|
+
poster = secici.select_poster("img", article)
|
|
74
65
|
|
|
75
66
|
if title and href:
|
|
76
67
|
results.append(SearchResult(
|
|
77
|
-
title = title
|
|
78
|
-
url = self.fix_url(href
|
|
79
|
-
poster = self.fix_url(poster
|
|
68
|
+
title = title,
|
|
69
|
+
url = self.fix_url(href),
|
|
70
|
+
poster = self.fix_url(poster),
|
|
80
71
|
))
|
|
81
72
|
|
|
82
73
|
return results
|
|
83
74
|
|
|
84
|
-
async def load_item(self, url: str) -> MovieInfo:
|
|
75
|
+
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
85
76
|
istek = await self.httpx.get(url)
|
|
86
|
-
secici =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
77
|
+
secici = HTMLHelper(istek.text)
|
|
78
|
+
|
|
79
|
+
title = self.clean_title(secici.select_text("h1.title"))
|
|
80
|
+
poster = secici.select_poster("img.cover-img")
|
|
81
|
+
description = secici.select_text("div.info-description")
|
|
82
|
+
rating = secici.select_text("div.info div.imdb b")
|
|
83
|
+
year = secici.select_text("span.date a")
|
|
84
|
+
actors = secici.select_texts("div.cast-name")
|
|
85
|
+
tags = secici.select_texts("div.type a[href*='/tur/']")
|
|
86
|
+
duration = secici.regex_first(r"(\d+)", secici.select_text("div.time"))
|
|
87
|
+
|
|
88
|
+
episodes = []
|
|
89
|
+
for link in secici.select("a[href]"):
|
|
90
|
+
href = link.attrs.get("href", "")
|
|
91
|
+
s, e = secici.extract_season_episode(href)
|
|
92
|
+
if s and e:
|
|
93
|
+
name = link.text(strip=True).split("Bölüm")[-1].strip() if "Bölüm" in link.text() else ""
|
|
94
|
+
episodes.append(Episode(
|
|
95
|
+
season = s,
|
|
96
|
+
episode = e,
|
|
97
|
+
title = name,
|
|
98
|
+
url = self.fix_url(href)
|
|
99
|
+
))
|
|
109
100
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
101
|
+
# Tekrar edenleri temizle ve sırala
|
|
102
|
+
if episodes:
|
|
103
|
+
seen = set()
|
|
104
|
+
unique = []
|
|
105
|
+
for ep in episodes:
|
|
106
|
+
if (ep.season, ep.episode) not in seen:
|
|
107
|
+
seen.add((ep.season, ep.episode))
|
|
108
|
+
unique.append(ep)
|
|
109
|
+
unique.sort(key=lambda x: (x.season or 0, x.episode or 0))
|
|
110
|
+
|
|
111
|
+
return SeriesInfo(
|
|
112
|
+
url = url,
|
|
113
|
+
poster = self.fix_url(poster),
|
|
114
|
+
title = title,
|
|
115
|
+
description = description,
|
|
116
|
+
tags = tags,
|
|
117
|
+
rating = rating,
|
|
118
|
+
year = year,
|
|
119
|
+
actors = actors,
|
|
120
|
+
duration = duration,
|
|
121
|
+
episodes = unique
|
|
122
|
+
)
|
|
117
123
|
|
|
118
124
|
return MovieInfo(
|
|
119
125
|
url = url,
|
|
120
|
-
poster = self.fix_url(poster)
|
|
121
|
-
title =
|
|
126
|
+
poster = self.fix_url(poster),
|
|
127
|
+
title = title,
|
|
122
128
|
description = description,
|
|
123
129
|
tags = tags,
|
|
124
130
|
rating = rating,
|
|
@@ -129,27 +135,58 @@ class FilmMakinesi(PluginBase):
|
|
|
129
135
|
|
|
130
136
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
131
137
|
istek = await self.httpx.get(url)
|
|
132
|
-
secici =
|
|
138
|
+
secici = HTMLHelper(istek.text)
|
|
133
139
|
|
|
134
|
-
response
|
|
140
|
+
response = []
|
|
141
|
+
shared_subs = []
|
|
135
142
|
|
|
136
143
|
# Video parts linklerini ve etiketlerini al
|
|
137
|
-
for link in secici.
|
|
144
|
+
for link in secici.select("div.video-parts a[data-video_url]"):
|
|
138
145
|
video_url = link.attrs.get("data-video_url")
|
|
139
146
|
label = link.text(strip=True) if link.text(strip=True) else ""
|
|
140
147
|
|
|
141
148
|
if video_url:
|
|
142
149
|
data = await self.extract(video_url, prefix=label.split()[0] if label else None)
|
|
143
150
|
if data:
|
|
144
|
-
|
|
151
|
+
if isinstance(data, list):
|
|
152
|
+
for d in data:
|
|
153
|
+
response.append(d)
|
|
154
|
+
if d.subtitles:
|
|
155
|
+
shared_subs.extend(d.subtitles)
|
|
156
|
+
else:
|
|
157
|
+
response.append(data)
|
|
158
|
+
if data.subtitles:
|
|
159
|
+
shared_subs.extend(data.subtitles)
|
|
145
160
|
|
|
146
161
|
# Eğer video-parts yoksa iframe kullan
|
|
147
162
|
if not response:
|
|
148
|
-
|
|
149
|
-
iframe_src = iframe_el.attrs.get("data-src") if iframe_el else None
|
|
163
|
+
iframe_src = secici.select_attr("iframe", "data-src")
|
|
150
164
|
if iframe_src:
|
|
151
165
|
data = await self.extract(iframe_src)
|
|
152
166
|
if data:
|
|
153
|
-
|
|
167
|
+
if isinstance(data, list):
|
|
168
|
+
for d in data:
|
|
169
|
+
response.append(d)
|
|
170
|
+
if d.subtitles:
|
|
171
|
+
shared_subs.extend(d.subtitles)
|
|
172
|
+
else:
|
|
173
|
+
response.append(data)
|
|
174
|
+
if data.subtitles:
|
|
175
|
+
shared_subs.extend(data.subtitles)
|
|
176
|
+
|
|
177
|
+
# Altyazıları Dağıt
|
|
178
|
+
if shared_subs:
|
|
179
|
+
unique_subs = []
|
|
180
|
+
seen_urls = set()
|
|
181
|
+
for sub in shared_subs:
|
|
182
|
+
if sub.url not in seen_urls:
|
|
183
|
+
seen_urls.add(sub.url)
|
|
184
|
+
unique_subs.append(sub)
|
|
185
|
+
|
|
186
|
+
for res in response:
|
|
187
|
+
current_urls = {s.url for s in res.subtitles}
|
|
188
|
+
for sub in unique_subs:
|
|
189
|
+
if sub.url not in current_urls:
|
|
190
|
+
res.subtitles.append(sub)
|
|
154
191
|
|
|
155
192
|
return response
|