KekikStream 2.3.9__py3-none-any.whl → 2.5.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/Extractor/ExtractorBase.py +3 -2
- KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
- KekikStream/Core/HTMLHelper.py +120 -49
- KekikStream/Core/Plugin/PluginBase.py +35 -12
- KekikStream/Core/Plugin/PluginLoader.py +12 -14
- KekikStream/Core/Plugin/PluginManager.py +2 -2
- KekikStream/Core/Plugin/PluginModels.py +0 -3
- KekikStream/Extractors/Abstream.py +27 -0
- KekikStream/Extractors/CloseLoad.py +30 -54
- KekikStream/Extractors/ContentX.py +27 -72
- KekikStream/Extractors/DonilasPlay.py +33 -77
- KekikStream/Extractors/DzenRu.py +10 -24
- KekikStream/Extractors/ExPlay.py +20 -38
- KekikStream/Extractors/Filemoon.py +21 -46
- KekikStream/Extractors/HDMomPlayer.py +30 -0
- KekikStream/Extractors/HDPlayerSystem.py +13 -31
- KekikStream/Extractors/HotStream.py +27 -0
- KekikStream/Extractors/JFVid.py +3 -24
- KekikStream/Extractors/JetTv.py +21 -34
- KekikStream/Extractors/JetV.py +55 -0
- KekikStream/Extractors/MailRu.py +11 -29
- KekikStream/Extractors/MixPlayHD.py +15 -28
- KekikStream/Extractors/MixTiger.py +17 -40
- KekikStream/Extractors/MolyStream.py +17 -21
- KekikStream/Extractors/Odnoklassniki.py +40 -104
- KekikStream/Extractors/PeaceMakerst.py +18 -45
- KekikStream/Extractors/PixelDrain.py +8 -16
- KekikStream/Extractors/PlayerFilmIzle.py +22 -41
- KekikStream/Extractors/RapidVid.py +21 -35
- KekikStream/Extractors/SetPlay.py +18 -43
- KekikStream/Extractors/SibNet.py +7 -17
- KekikStream/Extractors/Sobreatsesuyp.py +23 -45
- KekikStream/Extractors/TRsTX.py +23 -53
- KekikStream/Extractors/TurboImgz.py +7 -14
- KekikStream/Extractors/VCTPlay.py +10 -28
- KekikStream/Extractors/Veev.py +145 -0
- KekikStream/Extractors/VidBiz.py +62 -0
- KekikStream/Extractors/VidHide.py +58 -30
- KekikStream/Extractors/VidMoly.py +65 -99
- KekikStream/Extractors/VidMoxy.py +16 -27
- KekikStream/Extractors/VidPapi.py +24 -54
- KekikStream/Extractors/VideoSeyred.py +19 -40
- KekikStream/Extractors/Videostr.py +58 -0
- KekikStream/Extractors/Vidoza.py +18 -0
- KekikStream/Extractors/Vtbe.py +38 -0
- KekikStream/Extractors/YTDLP.py +2 -2
- KekikStream/Extractors/YildizKisaFilm.py +13 -31
- KekikStream/Extractors/Zeus.py +61 -0
- KekikStream/Plugins/BelgeselX.py +97 -77
- KekikStream/Plugins/DiziBox.py +28 -45
- KekikStream/Plugins/DiziMom.py +179 -0
- KekikStream/Plugins/DiziPal.py +95 -161
- KekikStream/Plugins/DiziYou.py +51 -147
- KekikStream/Plugins/Dizilla.py +40 -61
- KekikStream/Plugins/FilmBip.py +90 -39
- KekikStream/Plugins/FilmEkseni.py +199 -0
- KekikStream/Plugins/FilmMakinesi.py +72 -73
- KekikStream/Plugins/FilmModu.py +25 -35
- KekikStream/Plugins/Filmatek.py +184 -0
- KekikStream/Plugins/FilmciBaba.py +155 -0
- KekikStream/Plugins/FullHDFilmizlesene.py +16 -37
- KekikStream/Plugins/HDFilm.py +243 -0
- KekikStream/Plugins/HDFilmCehennemi.py +242 -189
- KekikStream/Plugins/JetFilmizle.py +101 -69
- KekikStream/Plugins/KultFilmler.py +138 -104
- KekikStream/Plugins/RecTV.py +52 -73
- KekikStream/Plugins/RoketDizi.py +18 -27
- KekikStream/Plugins/SelcukFlix.py +30 -48
- KekikStream/Plugins/SetFilmIzle.py +76 -104
- KekikStream/Plugins/SezonlukDizi.py +90 -94
- KekikStream/Plugins/Sinefy.py +195 -167
- KekikStream/Plugins/SinemaCX.py +148 -78
- KekikStream/Plugins/Sinezy.py +29 -31
- KekikStream/Plugins/SuperFilmGeldi.py +12 -17
- KekikStream/Plugins/UgurFilm.py +85 -38
- KekikStream/Plugins/Watch32.py +160 -0
- KekikStream/Plugins/YabanciDizi.py +176 -211
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/METADATA +1 -1
- kekikstream-2.5.4.dist-info/RECORD +99 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/WHEEL +1 -1
- KekikStream/Plugins/FullHDFilm.py +0 -249
- kekikstream-2.3.9.dist-info/RECORD +0 -84
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.3.9.dist-info → kekikstream-2.5.4.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
|
|
4
|
+
import asyncio
|
|
4
5
|
|
|
5
6
|
class JetFilmizle(PluginBase):
|
|
6
7
|
name = "JetFilmizle"
|
|
@@ -42,24 +43,22 @@ class JetFilmizle(PluginBase):
|
|
|
42
43
|
|
|
43
44
|
results = []
|
|
44
45
|
for veri in secici.select("article.movie"):
|
|
45
|
-
# h2-h6 içindeki a linki
|
|
46
46
|
title_text = None
|
|
47
47
|
for h_tag in ["h2", "h3", "h4", "h5", "h6"]:
|
|
48
48
|
title_text = secici.select_text(f"{h_tag} a", veri)
|
|
49
49
|
if title_text:
|
|
50
50
|
break
|
|
51
51
|
|
|
52
|
-
href = secici.select_attr("a", "href", veri)
|
|
53
|
-
poster = secici.select_poster("img", veri)
|
|
54
|
-
|
|
55
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)
|
|
56
55
|
|
|
57
56
|
if title and href:
|
|
58
57
|
results.append(MainPageResult(
|
|
59
58
|
category = category,
|
|
60
59
|
title = title,
|
|
61
60
|
url = self.fix_url(href),
|
|
62
|
-
poster = self.fix_url(poster)
|
|
61
|
+
poster = self.fix_url(poster),
|
|
63
62
|
))
|
|
64
63
|
|
|
65
64
|
return results
|
|
@@ -74,23 +73,21 @@ class JetFilmizle(PluginBase):
|
|
|
74
73
|
|
|
75
74
|
results = []
|
|
76
75
|
for article in secici.select("article.movie"):
|
|
77
|
-
# h2-h6 içindeki a linki
|
|
78
76
|
title_text = None
|
|
79
77
|
for h_tag in ["h2", "h3", "h4", "h5", "h6"]:
|
|
80
78
|
title_text = secici.select_text(f"{h_tag} a", article)
|
|
81
79
|
if title_text:
|
|
82
80
|
break
|
|
83
81
|
|
|
84
|
-
href = secici.select_attr("a", "href", article)
|
|
85
|
-
poster = secici.select_poster("img", article)
|
|
86
|
-
|
|
87
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)
|
|
88
85
|
|
|
89
86
|
if title and href:
|
|
90
87
|
results.append(SearchResult(
|
|
91
88
|
title = title,
|
|
92
89
|
url = self.fix_url(href),
|
|
93
|
-
poster = self.fix_url(poster)
|
|
90
|
+
poster = self.fix_url(poster),
|
|
94
91
|
))
|
|
95
92
|
|
|
96
93
|
return results
|
|
@@ -99,81 +96,116 @@ class JetFilmizle(PluginBase):
|
|
|
99
96
|
istek = await self.httpx.get(url)
|
|
100
97
|
secici = HTMLHelper(istek.text)
|
|
101
98
|
|
|
102
|
-
title
|
|
103
|
-
|
|
104
|
-
poster = secici.select_poster("section.movie-exp img")
|
|
105
|
-
poster = poster.strip() if poster else None
|
|
106
|
-
|
|
99
|
+
title = self.clean_title(secici.select_text("div.movie-exp-title"))
|
|
100
|
+
poster = secici.select_poster("section.movie-exp img")
|
|
107
101
|
description = secici.select_text("section.movie-exp p.aciklama")
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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])
|
|
120
116
|
|
|
121
117
|
return MovieInfo(
|
|
122
118
|
url = url,
|
|
123
|
-
poster = self.fix_url(poster)
|
|
119
|
+
poster = self.fix_url(poster),
|
|
124
120
|
title = title,
|
|
125
121
|
description = description,
|
|
126
122
|
tags = tags,
|
|
127
123
|
rating = rating,
|
|
128
124
|
year = year,
|
|
129
125
|
actors = actors,
|
|
130
|
-
duration =
|
|
126
|
+
duration = total_minutes if total_minutes else None
|
|
131
127
|
)
|
|
132
128
|
|
|
133
|
-
async def
|
|
134
|
-
istek = await self.httpx.get(url)
|
|
135
|
-
secici = HTMLHelper(istek.text)
|
|
136
|
-
|
|
129
|
+
async def _process_source(self, url: str, name: str, html: str | None) -> list[ExtractResult]:
|
|
137
130
|
results = []
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
# 2) Sayfa numaralarından linkleri topla (Fragman hariç)
|
|
152
|
-
page_links = []
|
|
153
|
-
for link in secici.select("a.post-page-numbers"):
|
|
154
|
-
isim = secici.select_text("span", link) or ""
|
|
155
|
-
if isim != "Fragman":
|
|
156
|
-
href = link.attrs.get("href")
|
|
157
|
-
if href:
|
|
158
|
-
page_links.append((self.fix_url(href), isim))
|
|
159
|
-
|
|
160
|
-
# 3) Her sayfa linkindeki iframe'leri bul
|
|
161
|
-
for page_url, isim in page_links:
|
|
162
|
-
try:
|
|
163
|
-
page_resp = await self.httpx.get(page_url)
|
|
164
|
-
page_sel = HTMLHelper(page_resp.text)
|
|
165
|
-
|
|
166
|
-
for iframe in page_sel.select("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):
|
|
167
143
|
src = (iframe.attrs.get("src") or
|
|
168
144
|
iframe.attrs.get("data-src") or
|
|
169
145
|
iframe.attrs.get("data-lazy-src"))
|
|
170
|
-
|
|
146
|
+
|
|
171
147
|
if src and src != "about:blank":
|
|
172
148
|
iframe_url = self.fix_url(src)
|
|
173
|
-
|
|
149
|
+
# name_override KULLANMA, extractor kendi ismini versin
|
|
150
|
+
# Sonra biz düzenleriz
|
|
151
|
+
data = await self.extract(iframe_url)
|
|
152
|
+
|
|
174
153
|
if data:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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 []
|
|
178
180
|
|
|
179
|
-
|
|
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,7 +1,7 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
|
-
from KekikStream.Core
|
|
4
|
-
import base64
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, Subtitle, HTMLHelper
|
|
4
|
+
import base64, asyncio, contextlib
|
|
5
5
|
|
|
6
6
|
class KultFilmler(PluginBase):
|
|
7
7
|
name = "KultFilmler"
|
|
@@ -50,7 +50,7 @@ class KultFilmler(PluginBase):
|
|
|
50
50
|
category = category,
|
|
51
51
|
title = title,
|
|
52
52
|
url = self.fix_url(href),
|
|
53
|
-
poster = self.fix_url(poster)
|
|
53
|
+
poster = self.fix_url(poster),
|
|
54
54
|
))
|
|
55
55
|
|
|
56
56
|
return results
|
|
@@ -69,7 +69,7 @@ class KultFilmler(PluginBase):
|
|
|
69
69
|
results.append(SearchResult(
|
|
70
70
|
title = title,
|
|
71
71
|
url = self.fix_url(href),
|
|
72
|
-
poster = self.fix_url(poster)
|
|
72
|
+
poster = self.fix_url(poster),
|
|
73
73
|
))
|
|
74
74
|
|
|
75
75
|
return results
|
|
@@ -78,146 +78,180 @@ class KultFilmler(PluginBase):
|
|
|
78
78
|
istek = await self.httpx.get(url)
|
|
79
79
|
secici = HTMLHelper(istek.text)
|
|
80
80
|
|
|
81
|
-
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
|
-
|
|
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"))
|
|
84
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")
|
|
85
89
|
|
|
86
|
-
tags = [a.text(strip=True) for a in secici.select("ul.post-categories a") if a.text(strip=True)]
|
|
87
|
-
|
|
88
|
-
# HTML analizine göre güncellenen alanlar
|
|
89
|
-
year = secici.select_text("li.release span a")
|
|
90
|
-
|
|
91
|
-
time_text = secici.select_text("li.time span")
|
|
92
|
-
duration = secici.regex_first(r"(\d+)", time_text) if time_text else None
|
|
93
|
-
|
|
94
|
-
rating_text = secici.select_text("div.imdb-count")
|
|
95
|
-
rating = secici.regex_first(r"(\d+\.\d+|\d+)", rating_text) if rating_text else None
|
|
96
|
-
|
|
97
|
-
actors = [a.text(strip=True) for a in secici.select("div.actors a") if a.text(strip=True)]
|
|
98
|
-
|
|
99
|
-
# Dizi mi kontrol et
|
|
100
90
|
if "/dizi/" in url:
|
|
101
91
|
episodes = []
|
|
102
92
|
for bolum in secici.select("div.episode-box"):
|
|
103
|
-
|
|
104
|
-
|
|
93
|
+
href = secici.select_attr("div.name a", "href", bolum)
|
|
105
94
|
ssn_detail = secici.select_text("span.episodetitle", bolum) or ""
|
|
106
95
|
ep_detail = secici.select_text("span.episodetitle b", bolum) or ""
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
ep_season = secici.regex_first(r"(\d+)\.", ssn_detail)
|
|
112
|
-
ep_episode = secici.regex_first(r"(\d+)\.", ep_detail)
|
|
113
|
-
|
|
114
|
-
episodes.append(Episode(
|
|
115
|
-
season = int(ep_season) if ep_season else 1,
|
|
116
|
-
episode = int(ep_episode) if ep_episode else 1,
|
|
117
|
-
title = ep_name.strip(" -"),
|
|
118
|
-
url = self.fix_url(ep_href),
|
|
119
|
-
))
|
|
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)))
|
|
120
100
|
|
|
121
101
|
return SeriesInfo(
|
|
122
102
|
url = url,
|
|
123
103
|
poster = poster,
|
|
124
|
-
title =
|
|
104
|
+
title = title,
|
|
125
105
|
description = description,
|
|
126
106
|
tags = tags,
|
|
127
107
|
year = year,
|
|
128
108
|
actors = actors,
|
|
129
109
|
rating = rating,
|
|
130
|
-
episodes = episodes
|
|
110
|
+
episodes = episodes
|
|
131
111
|
)
|
|
132
112
|
|
|
133
113
|
return MovieInfo(
|
|
134
114
|
url = url,
|
|
135
115
|
poster = poster,
|
|
136
|
-
title =
|
|
116
|
+
title = title,
|
|
137
117
|
description = description,
|
|
138
118
|
tags = tags,
|
|
139
119
|
year = year,
|
|
140
120
|
rating = rating,
|
|
141
121
|
actors = actors,
|
|
142
|
-
duration =
|
|
122
|
+
duration = duration
|
|
143
123
|
)
|
|
144
124
|
|
|
145
|
-
def
|
|
146
|
-
"""Base64
|
|
147
|
-
|
|
148
|
-
if not
|
|
149
|
-
return
|
|
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
|
|
150
130
|
|
|
151
|
-
# Padding
|
|
152
|
-
|
|
153
|
-
if
|
|
154
|
-
|
|
131
|
+
# Base64 Padding Fix
|
|
132
|
+
pad = len(match) % 4
|
|
133
|
+
if pad:
|
|
134
|
+
match += "=" * (4 - pad)
|
|
155
135
|
|
|
156
136
|
try:
|
|
157
|
-
decoded = base64.b64decode(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return self.fix_url(iframe_src) if iframe_src 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
|
|
161
140
|
except Exception:
|
|
162
|
-
return
|
|
141
|
+
return None
|
|
163
142
|
|
|
164
|
-
def
|
|
165
|
-
"""
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
iframes = set()
|
|
173
|
-
|
|
174
|
-
# Ana iframe
|
|
175
|
-
main_frame = self._get_iframe(istek.text)
|
|
176
|
-
if main_frame:
|
|
177
|
-
iframes.add(main_frame)
|
|
178
|
-
|
|
179
|
-
# Alternatif player'lar
|
|
180
|
-
for player in secici.select("div.container#player"):
|
|
181
|
-
iframe_src = secici.select_attr("iframe", "src", player)
|
|
182
|
-
alt_iframe = self.fix_url(iframe_src) if iframe_src else None
|
|
183
|
-
if alt_iframe:
|
|
184
|
-
alt_istek = await self.httpx.get(alt_iframe)
|
|
185
|
-
alt_frame = self._get_iframe(alt_istek.text)
|
|
186
|
-
if alt_frame:
|
|
187
|
-
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
|
|
188
150
|
|
|
151
|
+
async def _extract_stream(self, iframe_url: str, title: str, subtitles: list[Subtitle]) -> list[ExtractResult]:
|
|
152
|
+
"""Iframe üzerinden stream linklerini ayıklar"""
|
|
189
153
|
results = []
|
|
190
154
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
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:
|
|
204
168
|
results.append(ExtractResult(
|
|
205
|
-
name = "VidMoly",
|
|
206
|
-
url =
|
|
169
|
+
name = title or "VidMoly",
|
|
170
|
+
url = m3u,
|
|
207
171
|
referer = self.main_url,
|
|
208
|
-
subtitles =
|
|
172
|
+
subtitles = subtitles
|
|
209
173
|
))
|
|
210
|
-
continue
|
|
211
174
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
|
216
189
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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)
|
|
222
199
|
|
|
223
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
|