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
|
@@ -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
|
-
|
|
5
|
-
import re
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
|
|
4
|
+
import asyncio
|
|
6
5
|
|
|
7
6
|
class JetFilmizle(PluginBase):
|
|
8
7
|
name = "JetFilmizle"
|
|
@@ -40,30 +39,26 @@ class JetFilmizle(PluginBase):
|
|
|
40
39
|
|
|
41
40
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
42
41
|
istek = await self.httpx.get(f"{url}{page}", follow_redirects=True)
|
|
43
|
-
secici =
|
|
42
|
+
secici = HTMLHelper(istek.text)
|
|
44
43
|
|
|
45
44
|
results = []
|
|
46
|
-
for veri in secici.
|
|
47
|
-
|
|
48
|
-
title_link = None
|
|
45
|
+
for veri in secici.select("article.movie"):
|
|
46
|
+
title_text = None
|
|
49
47
|
for h_tag in ["h2", "h3", "h4", "h5", "h6"]:
|
|
50
|
-
|
|
51
|
-
if
|
|
48
|
+
title_text = secici.select_text(f"{h_tag} a", veri)
|
|
49
|
+
if title_text:
|
|
52
50
|
break
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
title = self.clean_title(title_link.text(strip=True)) if title_link else None
|
|
58
|
-
href = link_el.attrs.get("href") if link_el else None
|
|
59
|
-
poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
|
|
52
|
+
title = self.clean_title(title_text) if title_text else None
|
|
53
|
+
href = secici.select_attr("a", "href", veri)
|
|
54
|
+
poster = secici.select_poster("img", veri)
|
|
60
55
|
|
|
61
56
|
if title and href:
|
|
62
57
|
results.append(MainPageResult(
|
|
63
58
|
category = category,
|
|
64
59
|
title = title,
|
|
65
60
|
url = self.fix_url(href),
|
|
66
|
-
poster = self.fix_url(poster)
|
|
61
|
+
poster = self.fix_url(poster),
|
|
67
62
|
))
|
|
68
63
|
|
|
69
64
|
return results
|
|
@@ -74,119 +69,143 @@ class JetFilmizle(PluginBase):
|
|
|
74
69
|
data = {"s": query},
|
|
75
70
|
headers = {"Referer": f"{self.main_url}/"}
|
|
76
71
|
)
|
|
77
|
-
secici =
|
|
72
|
+
secici = HTMLHelper(istek.text)
|
|
78
73
|
|
|
79
74
|
results = []
|
|
80
|
-
for article in secici.
|
|
81
|
-
|
|
82
|
-
title_link = None
|
|
75
|
+
for article in secici.select("article.movie"):
|
|
76
|
+
title_text = None
|
|
83
77
|
for h_tag in ["h2", "h3", "h4", "h5", "h6"]:
|
|
84
|
-
|
|
85
|
-
if
|
|
78
|
+
title_text = secici.select_text(f"{h_tag} a", article)
|
|
79
|
+
if title_text:
|
|
86
80
|
break
|
|
87
81
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
title = self.clean_title(title_link.text(strip=True)) if title_link else None
|
|
92
|
-
href = link_el.attrs.get("href") if link_el else None
|
|
93
|
-
poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
|
|
82
|
+
title = self.clean_title(title_text) if title_text else None
|
|
83
|
+
href = secici.select_attr("a", "href", article)
|
|
84
|
+
poster = secici.select_poster("img", article)
|
|
94
85
|
|
|
95
86
|
if title and href:
|
|
96
87
|
results.append(SearchResult(
|
|
97
88
|
title = title,
|
|
98
89
|
url = self.fix_url(href),
|
|
99
|
-
poster = self.fix_url(poster)
|
|
90
|
+
poster = self.fix_url(poster),
|
|
100
91
|
))
|
|
101
92
|
|
|
102
93
|
return results
|
|
103
94
|
|
|
104
95
|
async def load_item(self, url: str) -> MovieInfo:
|
|
105
96
|
istek = await self.httpx.get(url)
|
|
106
|
-
secici =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
year = None
|
|
126
|
-
yap_match = re.search(r'<div class="yap"[^>]*>([^<]*(?:Vizyon|Yapım)[^<]*)</div>', html_text, re.IGNORECASE)
|
|
127
|
-
if yap_match:
|
|
128
|
-
year_match = re.search(r'(\d{4})', yap_match.group(1))
|
|
129
|
-
if year_match:
|
|
130
|
-
year = year_match.group(1)
|
|
131
|
-
|
|
132
|
-
actors = [a.text(strip=True) for a in secici.css("div[itemprop='actor'] a span") if a.text(strip=True)]
|
|
97
|
+
secici = HTMLHelper(istek.text)
|
|
98
|
+
|
|
99
|
+
title = self.clean_title(secici.select_text("div.movie-exp-title"))
|
|
100
|
+
poster = secici.select_poster("section.movie-exp img")
|
|
101
|
+
description = secici.select_text("section.movie-exp p.aciklama")
|
|
102
|
+
tags = secici.select_texts("section.movie-exp div.catss a")
|
|
103
|
+
rating = secici.select_text("section.movie-exp div.imdb_puan span")
|
|
104
|
+
year = secici.meta_value("Yayın Yılı")
|
|
105
|
+
actors = secici.select_texts("div[itemprop='actor'] a span") or [img.attrs.get("alt") for img in secici.select("div.oyuncular div.oyuncu img") if img.attrs.get("alt")]
|
|
106
|
+
duration = secici.meta_value("Süre")
|
|
107
|
+
duration = duration.split() if duration else None
|
|
108
|
+
|
|
109
|
+
total_minutes = 0
|
|
110
|
+
if duration:
|
|
111
|
+
for i, p in enumerate(duration):
|
|
112
|
+
if p == "saat":
|
|
113
|
+
total_minutes += int(duration[i-1]) * 60
|
|
114
|
+
elif p == "dakika":
|
|
115
|
+
total_minutes += int(duration[i-1])
|
|
133
116
|
|
|
134
117
|
return MovieInfo(
|
|
135
118
|
url = url,
|
|
136
|
-
poster = self.fix_url(poster)
|
|
119
|
+
poster = self.fix_url(poster),
|
|
137
120
|
title = title,
|
|
138
121
|
description = description,
|
|
139
122
|
tags = tags,
|
|
140
123
|
rating = rating,
|
|
141
124
|
year = year,
|
|
142
|
-
actors = actors
|
|
125
|
+
actors = actors,
|
|
126
|
+
duration = total_minutes if total_minutes else None
|
|
143
127
|
)
|
|
144
128
|
|
|
145
|
-
async def
|
|
146
|
-
istek = await self.httpx.get(url)
|
|
147
|
-
secici = HTMLParser(istek.text)
|
|
148
|
-
|
|
129
|
+
async def _process_source(self, url: str, name: str, html: str | None) -> list[ExtractResult]:
|
|
149
130
|
results = []
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
# 2) Sayfa numaralarından linkleri topla (Fragman hariç)
|
|
164
|
-
page_links = []
|
|
165
|
-
for link in secici.css("a.post-page-numbers"):
|
|
166
|
-
span_el = link.css_first("span")
|
|
167
|
-
isim = span_el.text(strip=True) if span_el else ""
|
|
168
|
-
if isim != "Fragman":
|
|
169
|
-
href = link.attrs.get("href")
|
|
170
|
-
if href:
|
|
171
|
-
page_links.append((self.fix_url(href), isim))
|
|
172
|
-
|
|
173
|
-
# 3) Her sayfa linkindeki iframe'leri bul
|
|
174
|
-
for page_url, isim in page_links:
|
|
175
|
-
try:
|
|
176
|
-
page_resp = await self.httpx.get(page_url)
|
|
177
|
-
page_sel = HTMLParser(page_resp.text)
|
|
178
|
-
|
|
179
|
-
for iframe in page_sel.css("div#movie iframe"):
|
|
131
|
+
try:
|
|
132
|
+
if html:
|
|
133
|
+
secici = HTMLHelper(html)
|
|
134
|
+
else:
|
|
135
|
+
resp = await self.httpx.get(url)
|
|
136
|
+
secici = HTMLHelper(resp.text)
|
|
137
|
+
|
|
138
|
+
# Iframe'leri bul
|
|
139
|
+
container = secici.select_first("div#movie") or secici.select_first("div.film-content")
|
|
140
|
+
|
|
141
|
+
if container:
|
|
142
|
+
for iframe in secici.select("iframe", container):
|
|
180
143
|
src = (iframe.attrs.get("src") or
|
|
181
144
|
iframe.attrs.get("data-src") or
|
|
182
145
|
iframe.attrs.get("data-lazy-src"))
|
|
183
|
-
|
|
146
|
+
|
|
184
147
|
if src and src != "about:blank":
|
|
185
148
|
iframe_url = self.fix_url(src)
|
|
186
|
-
|
|
149
|
+
# name_override KULLANMA, extractor kendi ismini versin
|
|
150
|
+
# Sonra biz düzenleriz
|
|
151
|
+
data = await self.extract(iframe_url)
|
|
152
|
+
|
|
187
153
|
if data:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
154
|
+
items = data if isinstance(data, list) else [data]
|
|
155
|
+
|
|
156
|
+
for item in items:
|
|
157
|
+
# Sadece kalite bilgisi içeriyorsa ekle, yoksa sadece buton adını kullan
|
|
158
|
+
# Özellikle Zeus için kalite önemli (1080p, 720p)
|
|
159
|
+
# Diğerlerinde plugin adı (Apollo, JetPlay vb.) önemsiz
|
|
160
|
+
|
|
161
|
+
# Kalite kontrolü (basitçe)
|
|
162
|
+
quality_indicators = ["1080p", "720p", "480p", "360p", "240p", "144p", "4k", "2k"]
|
|
163
|
+
has_quality = any(q in item.name.lower() for q in quality_indicators)
|
|
164
|
+
|
|
165
|
+
if has_quality:
|
|
166
|
+
# Buton Adı | Extractor Adı (Kalite içerdiği için)
|
|
167
|
+
# Örn: Zeus | 1080p
|
|
168
|
+
# Eğer Extractor adı zaten Buton adını içeriyorsa (Zeus | 1080p -> Zeus) tekrar ekleme
|
|
169
|
+
if name.lower() not in item.name.lower():
|
|
170
|
+
item.name = f"{name} | {item.name}"
|
|
171
|
+
else:
|
|
172
|
+
# Kalite yoksa sadece Buton adını kullan
|
|
173
|
+
# Örn: Apollo | JetTv -> JetTv
|
|
174
|
+
item.name = name
|
|
175
|
+
|
|
176
|
+
results.append(item)
|
|
177
|
+
return results
|
|
178
|
+
except Exception:
|
|
179
|
+
return []
|
|
191
180
|
|
|
192
|
-
|
|
181
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
182
|
+
istek = await self.httpx.get(url)
|
|
183
|
+
secici = HTMLHelper(istek.text)
|
|
184
|
+
|
|
185
|
+
sources = []
|
|
186
|
+
if film_part := secici.select_first("div.film_part"):
|
|
187
|
+
# Tüm spanları gez
|
|
188
|
+
for span in secici.select("span", film_part):
|
|
189
|
+
# Eğer bu span bir <a> etiketi içinde değilse, aktif kaynaktır
|
|
190
|
+
if span.parent.tag != "a":
|
|
191
|
+
name = span.text(strip=True)
|
|
192
|
+
if name:
|
|
193
|
+
sources.append((url, name, istek.text)) # html content var
|
|
194
|
+
break
|
|
195
|
+
|
|
196
|
+
# Diğer kaynak linkleri
|
|
197
|
+
for link in secici.select("a.post-page-numbers", film_part):
|
|
198
|
+
name = secici.select_text("span", link) or link.text(strip=True)
|
|
199
|
+
href = link.attrs.get("href")
|
|
200
|
+
if name != "Fragman" and href:
|
|
201
|
+
sources.append((self.fix_url(href), name, None)) # html yok, çekilecek
|
|
202
|
+
|
|
203
|
+
# Eğer film_part yoksa, sadece mevcut sayfayı tara (Tek part olabilir)
|
|
204
|
+
if not sources:
|
|
205
|
+
sources.append((url, "JetFilmizle", istek.text))
|
|
206
|
+
|
|
207
|
+
tasks = []
|
|
208
|
+
for page_url, source_name, html_content in sources:
|
|
209
|
+
tasks.append(self._process_source(page_url, source_name, html_content))
|
|
210
|
+
|
|
211
|
+
return [item for sublist in await asyncio.gather(*tasks) for item in sublist]
|
|
@@ -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
|
-
|
|
5
|
-
import re, base64
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, Subtitle, HTMLHelper
|
|
4
|
+
import base64, asyncio, contextlib
|
|
6
5
|
|
|
7
6
|
class KultFilmler(PluginBase):
|
|
8
7
|
name = "KultFilmler"
|
|
@@ -38,210 +37,221 @@ class KultFilmler(PluginBase):
|
|
|
38
37
|
|
|
39
38
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
40
39
|
istek = await self.httpx.get(url)
|
|
41
|
-
secici =
|
|
40
|
+
secici = HTMLHelper(istek.text)
|
|
42
41
|
|
|
43
42
|
results = []
|
|
44
|
-
for veri in secici.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
title = img_el.attrs.get("alt") if img_el else None
|
|
49
|
-
href = link_el.attrs.get("href") if link_el else None
|
|
50
|
-
poster = img_el.attrs.get("src") if img_el else None
|
|
43
|
+
for veri in secici.select("div.col-md-12 div.movie-box"):
|
|
44
|
+
title = secici.select_attr("div.img img", "alt", veri)
|
|
45
|
+
href = secici.select_attr("a", "href", veri)
|
|
46
|
+
poster = secici.select_attr("div.img img", "src", veri)
|
|
51
47
|
|
|
52
48
|
if title and href:
|
|
53
49
|
results.append(MainPageResult(
|
|
54
50
|
category = category,
|
|
55
51
|
title = title,
|
|
56
52
|
url = self.fix_url(href),
|
|
57
|
-
poster = self.fix_url(poster)
|
|
53
|
+
poster = self.fix_url(poster),
|
|
58
54
|
))
|
|
59
55
|
|
|
60
56
|
return results
|
|
61
57
|
|
|
62
58
|
async def search(self, query: str) -> list[SearchResult]:
|
|
63
59
|
istek = await self.httpx.get(f"{self.main_url}?s={query}")
|
|
64
|
-
secici =
|
|
60
|
+
secici = HTMLHelper(istek.text)
|
|
65
61
|
|
|
66
62
|
results = []
|
|
67
|
-
for veri in secici.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
title = img_el.attrs.get("alt") if img_el else None
|
|
72
|
-
href = link_el.attrs.get("href") if link_el else None
|
|
73
|
-
poster = img_el.attrs.get("src") if img_el else None
|
|
63
|
+
for veri in secici.select("div.movie-box"):
|
|
64
|
+
title = secici.select_attr("div.img img", "alt", veri)
|
|
65
|
+
href = secici.select_attr("a", "href", veri)
|
|
66
|
+
poster = secici.select_attr("div.img img", "src", veri)
|
|
74
67
|
|
|
75
68
|
if title and href:
|
|
76
69
|
results.append(SearchResult(
|
|
77
70
|
title = title,
|
|
78
71
|
url = self.fix_url(href),
|
|
79
|
-
poster = self.fix_url(poster)
|
|
72
|
+
poster = self.fix_url(poster),
|
|
80
73
|
))
|
|
81
74
|
|
|
82
75
|
return results
|
|
83
76
|
|
|
84
77
|
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
85
78
|
istek = await self.httpx.get(url)
|
|
86
|
-
secici =
|
|
87
|
-
|
|
88
|
-
film_img = secici.css_first("div.film-bilgileri img")
|
|
89
|
-
og_title = secici.css_first("[property='og:title']")
|
|
90
|
-
og_image = secici.css_first("[property='og:image']")
|
|
91
|
-
|
|
92
|
-
title = (film_img.attrs.get("alt") if film_img else None) or (og_title.attrs.get("content") if og_title else None)
|
|
93
|
-
poster = self.fix_url(og_image.attrs.get("content")) if og_image else None
|
|
94
|
-
|
|
95
|
-
desc_el = secici.css_first("div.description")
|
|
96
|
-
description = desc_el.text(strip=True) if desc_el else None
|
|
97
|
-
|
|
98
|
-
tags = [a.text(strip=True) for a in secici.css("ul.post-categories a") if a.text(strip=True)]
|
|
99
|
-
|
|
100
|
-
# HTML analizine göre güncellenen alanlar
|
|
101
|
-
year_el = secici.css_first("li.release span a")
|
|
102
|
-
year = year_el.text(strip=True) if year_el else None
|
|
103
|
-
|
|
104
|
-
time_el = secici.css_first("li.time span")
|
|
105
|
-
duration = None
|
|
106
|
-
if time_el:
|
|
107
|
-
time_text = time_el.text(strip=True)
|
|
108
|
-
dur_match = re.search(r"(\d+)", time_text)
|
|
109
|
-
duration = dur_match.group(1) if dur_match else None
|
|
79
|
+
secici = HTMLHelper(istek.text)
|
|
110
80
|
|
|
111
|
-
|
|
112
|
-
|
|
81
|
+
title = self.clean_title(secici.select_attr("div.film-bilgileri img", "alt") or secici.select_attr("[property='og:title']", "content"))
|
|
82
|
+
poster = self.fix_url(secici.select_attr("[property='og:image']", "content"))
|
|
83
|
+
description = secici.select_text("div.description")
|
|
84
|
+
tags = secici.select_texts("ul.post-categories a")
|
|
85
|
+
year = secici.extract_year("li.release span a")
|
|
86
|
+
duration = int(secici.regex_first(r"(\d+)", secici.select_text("li.time span")) or 0)
|
|
87
|
+
rating = secici.regex_first(r"(\d+\.\d+|\d+)", secici.select_text("div.imdb-count"))
|
|
88
|
+
actors = secici.select_texts("div.actors a")
|
|
113
89
|
|
|
114
|
-
actors = [a.text(strip=True) for a in secici.css("div.actors a") if a.text(strip=True)]
|
|
115
|
-
|
|
116
|
-
# Dizi mi kontrol et
|
|
117
90
|
if "/dizi/" in url:
|
|
118
91
|
episodes = []
|
|
119
|
-
for bolum in secici.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
ep_detail = ep_b_el.text(strip=True) if ep_b_el else ""
|
|
128
|
-
|
|
129
|
-
ep_name = f"{ssn_detail} - {ep_detail}"
|
|
130
|
-
|
|
131
|
-
if ep_href:
|
|
132
|
-
ep_season = re.search(r"(\d+)\.", ssn_detail)
|
|
133
|
-
ep_episode = re.search(r"(\d+)\.", ep_detail)
|
|
134
|
-
|
|
135
|
-
episodes.append(Episode(
|
|
136
|
-
season = int(ep_season[1]) if ep_season else 1,
|
|
137
|
-
episode = int(ep_episode[1]) if ep_episode else 1,
|
|
138
|
-
title = ep_name.strip(" -"),
|
|
139
|
-
url = self.fix_url(ep_href),
|
|
140
|
-
))
|
|
92
|
+
for bolum in secici.select("div.episode-box"):
|
|
93
|
+
href = secici.select_attr("div.name a", "href", bolum)
|
|
94
|
+
ssn_detail = secici.select_text("span.episodetitle", bolum) or ""
|
|
95
|
+
ep_detail = secici.select_text("span.episodetitle b", bolum) or ""
|
|
96
|
+
if href:
|
|
97
|
+
s, e = secici.extract_season_episode(f"{ssn_detail} {ep_detail}")
|
|
98
|
+
name = f"{ssn_detail} - {ep_detail}".strip(" -")
|
|
99
|
+
episodes.append(Episode(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
|
|
141
100
|
|
|
142
101
|
return SeriesInfo(
|
|
143
102
|
url = url,
|
|
144
103
|
poster = poster,
|
|
145
|
-
title =
|
|
104
|
+
title = title,
|
|
146
105
|
description = description,
|
|
147
106
|
tags = tags,
|
|
148
107
|
year = year,
|
|
149
108
|
actors = actors,
|
|
150
109
|
rating = rating,
|
|
151
|
-
episodes = episodes
|
|
110
|
+
episodes = episodes
|
|
152
111
|
)
|
|
153
112
|
|
|
154
113
|
return MovieInfo(
|
|
155
114
|
url = url,
|
|
156
115
|
poster = poster,
|
|
157
|
-
title =
|
|
116
|
+
title = title,
|
|
158
117
|
description = description,
|
|
159
118
|
tags = tags,
|
|
160
119
|
year = year,
|
|
161
120
|
rating = rating,
|
|
162
121
|
actors = actors,
|
|
163
|
-
duration =
|
|
122
|
+
duration = duration
|
|
164
123
|
)
|
|
165
124
|
|
|
166
|
-
def
|
|
167
|
-
"""Base64
|
|
168
|
-
|
|
169
|
-
if not
|
|
170
|
-
return
|
|
171
|
-
|
|
172
|
-
atob = atob_match.group()
|
|
125
|
+
def _decode_iframe(self, content: str) -> str | None:
|
|
126
|
+
"""Base64 kodlanmış iframe verisini çözer"""
|
|
127
|
+
match = HTMLHelper(content).regex_first(r"PHA\+[0-9a-zA-Z+/=]*")
|
|
128
|
+
if not match:
|
|
129
|
+
return None
|
|
173
130
|
|
|
174
|
-
# Padding
|
|
175
|
-
|
|
176
|
-
if
|
|
177
|
-
|
|
131
|
+
# Base64 Padding Fix
|
|
132
|
+
pad = len(match) % 4
|
|
133
|
+
if pad:
|
|
134
|
+
match += "=" * (4 - pad)
|
|
178
135
|
|
|
179
136
|
try:
|
|
180
|
-
decoded = base64.b64decode(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return self.fix_url(iframe_el.attrs.get("src")) if iframe_el else ""
|
|
137
|
+
decoded = base64.b64decode(match).decode("utf-8")
|
|
138
|
+
src = HTMLHelper(decoded).select_attr("iframe", "src")
|
|
139
|
+
return self.fix_url(src) if src else None
|
|
184
140
|
except Exception:
|
|
185
|
-
return
|
|
141
|
+
return None
|
|
186
142
|
|
|
187
|
-
def
|
|
188
|
-
"""
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
secici = HTMLParser(istek.text)
|
|
195
|
-
|
|
196
|
-
iframes = set()
|
|
197
|
-
|
|
198
|
-
# Ana iframe
|
|
199
|
-
main_frame = self._get_iframe(istek.text)
|
|
200
|
-
if main_frame:
|
|
201
|
-
iframes.add(main_frame)
|
|
202
|
-
|
|
203
|
-
# Alternatif player'lar
|
|
204
|
-
for player in secici.css("div.container#player"):
|
|
205
|
-
iframe_el = player.css_first("iframe")
|
|
206
|
-
alt_iframe = self.fix_url(iframe_el.attrs.get("src")) if iframe_el else None
|
|
207
|
-
if alt_iframe:
|
|
208
|
-
alt_istek = await self.httpx.get(alt_iframe)
|
|
209
|
-
alt_frame = self._get_iframe(alt_istek.text)
|
|
210
|
-
if alt_frame:
|
|
211
|
-
iframes.add(alt_frame)
|
|
143
|
+
async def _resolve_alt_page(self, url: str, title: str) -> tuple[str | None, str]:
|
|
144
|
+
"""Alternatif sayfa kaynak kodunu indirip iframe'i bulur"""
|
|
145
|
+
try:
|
|
146
|
+
res = await self.httpx.get(url)
|
|
147
|
+
return self._decode_iframe(res.text), title
|
|
148
|
+
except Exception:
|
|
149
|
+
return None, title
|
|
212
150
|
|
|
151
|
+
async def _extract_stream(self, iframe_url: str, title: str, subtitles: list[Subtitle]) -> list[ExtractResult]:
|
|
152
|
+
"""Iframe üzerinden stream linklerini ayıklar"""
|
|
213
153
|
results = []
|
|
214
154
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if
|
|
155
|
+
# 1. VidMoly Özel Çözümleme(M3U)
|
|
156
|
+
if "vidmoly" in iframe_url:
|
|
157
|
+
with contextlib.suppress(Exception):
|
|
158
|
+
res = await self.httpx.get(
|
|
159
|
+
url = iframe_url,
|
|
160
|
+
headers = {
|
|
161
|
+
"User-Agent" : "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36",
|
|
162
|
+
"Sec-Fetch-Dest" : "iframe"
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
m3u = HTMLHelper(res.text).regex_first(r'file:"([^"]+)"')
|
|
166
|
+
|
|
167
|
+
if m3u:
|
|
228
168
|
results.append(ExtractResult(
|
|
229
|
-
name = "VidMoly",
|
|
230
|
-
url =
|
|
169
|
+
name = title or "VidMoly",
|
|
170
|
+
url = m3u,
|
|
231
171
|
referer = self.main_url,
|
|
232
|
-
subtitles =
|
|
172
|
+
subtitles = subtitles
|
|
233
173
|
))
|
|
234
|
-
continue
|
|
235
174
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
175
|
+
return results
|
|
176
|
+
|
|
177
|
+
# 2. Genel Extractor Kullanımı
|
|
178
|
+
with contextlib.suppress(Exception):
|
|
179
|
+
extracted = await self.extract(iframe_url)
|
|
180
|
+
if not extracted:
|
|
181
|
+
return []
|
|
182
|
+
|
|
183
|
+
items = extracted if isinstance(extracted, list) else [extracted]
|
|
184
|
+
for item in items:
|
|
185
|
+
# İsim ve altyazı bilgilerini güncelle
|
|
186
|
+
# Orijinal extractor ismini ezmek için title kullan
|
|
187
|
+
if title:
|
|
188
|
+
item.name = title
|
|
240
189
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
190
|
+
# Varsa altyazıları ekle
|
|
191
|
+
if subtitles:
|
|
192
|
+
# Copy update daha güvenli (Pydantic model)
|
|
193
|
+
if hasattr(item, "model_copy"):
|
|
194
|
+
item = item.model_copy(update={"subtitles": subtitles})
|
|
195
|
+
else:
|
|
196
|
+
item.subtitles = subtitles
|
|
197
|
+
|
|
198
|
+
results.append(item)
|
|
246
199
|
|
|
247
200
|
return results
|
|
201
|
+
|
|
202
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
203
|
+
response = await self.httpx.get(url)
|
|
204
|
+
source = response.text
|
|
205
|
+
helper = HTMLHelper(source)
|
|
206
|
+
|
|
207
|
+
# Altyazı Bul
|
|
208
|
+
sub_url = helper.regex_first(r"(https?://[^\s\"]+\.srt)")
|
|
209
|
+
subtitles = [Subtitle(name="Türkçe", url=sub_url)] if sub_url else []
|
|
210
|
+
|
|
211
|
+
# İşlenecek kaynakları topla: (Iframe_URL, Başlık)
|
|
212
|
+
sources = []
|
|
213
|
+
|
|
214
|
+
# A) Ana Player
|
|
215
|
+
main_iframe = self._decode_iframe(source)
|
|
216
|
+
if main_iframe:
|
|
217
|
+
p_name = helper.select_text("div.parts-middle div.part.active div.part-name") or None
|
|
218
|
+
p_lang = helper.select_attr("div.parts-middle div.part.active div.part-lang span", "title")
|
|
219
|
+
full_title = f"{p_name} | {p_lang}" if p_lang else p_name
|
|
220
|
+
sources.append((main_iframe, full_title))
|
|
221
|
+
|
|
222
|
+
# B) Alternatif Playerlar (Link Çözümleme Gerektirir)
|
|
223
|
+
alt_tasks = []
|
|
224
|
+
for link in helper.select("div.parts-middle a.post-page-numbers"):
|
|
225
|
+
href = link.attrs.get("href")
|
|
226
|
+
if not href:
|
|
227
|
+
continue
|
|
228
|
+
|
|
229
|
+
a_name = helper.select_text("div.part-name", link) or "Alternatif"
|
|
230
|
+
a_lang = helper.select_attr("div.part-lang span", "title", link)
|
|
231
|
+
full_title = f"{a_name} | {a_lang}" if a_lang else a_name
|
|
232
|
+
|
|
233
|
+
alt_tasks.append(self._resolve_alt_page(self.fix_url(href), full_title))
|
|
234
|
+
|
|
235
|
+
if alt_tasks:
|
|
236
|
+
resolved_alts = await asyncio.gather(*alt_tasks)
|
|
237
|
+
for iframe, title in resolved_alts:
|
|
238
|
+
if iframe:
|
|
239
|
+
sources.append((iframe, title))
|
|
240
|
+
|
|
241
|
+
# 3. Tüm kaynakları paralel işle (Extract)
|
|
242
|
+
if not sources:
|
|
243
|
+
return []
|
|
244
|
+
|
|
245
|
+
extract_tasks = [
|
|
246
|
+
self._extract_stream(iframe, title, subtitles)
|
|
247
|
+
for iframe, title in sources
|
|
248
|
+
]
|
|
249
|
+
|
|
250
|
+
results_groups = await asyncio.gather(*extract_tasks)
|
|
251
|
+
|
|
252
|
+
# Sonuçları düzleştir
|
|
253
|
+
final_results = []
|
|
254
|
+
for group in results_groups:
|
|
255
|
+
final_results.extend(group)
|
|
256
|
+
|
|
257
|
+
return final_results
|