KekikStream 2.1.9__py3-none-any.whl → 2.2.7__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/Extractors/CloseLoad.py +7 -8
- KekikStream/Extractors/Filemoon.py +7 -6
- KekikStream/Extractors/JFVid.py +40 -0
- KekikStream/Extractors/MolyStream.py +6 -5
- KekikStream/Extractors/PlayerFilmIzle.py +6 -2
- KekikStream/Extractors/VidHide.py +0 -1
- KekikStream/Extractors/VidMoly.py +17 -9
- KekikStream/Plugins/BelgeselX.py +39 -20
- KekikStream/Plugins/DiziBox.py +115 -59
- KekikStream/Plugins/DiziPal.py +87 -40
- KekikStream/Plugins/DiziYou.py +105 -64
- KekikStream/Plugins/Dizilla.py +58 -29
- KekikStream/Plugins/FilmBip.py +60 -31
- KekikStream/Plugins/FilmMakinesi.py +75 -51
- KekikStream/Plugins/FilmModu.py +73 -36
- KekikStream/Plugins/FullHDFilm.py +82 -48
- KekikStream/Plugins/FullHDFilmizlesene.py +94 -39
- KekikStream/Plugins/HDFilmCehennemi.py +79 -54
- KekikStream/Plugins/JetFilmizle.py +98 -51
- KekikStream/Plugins/KultFilmler.py +64 -34
- KekikStream/Plugins/RoketDizi.py +43 -26
- KekikStream/Plugins/SelcukFlix.py +27 -14
- KekikStream/Plugins/SetFilmIzle.py +74 -43
- KekikStream/Plugins/SezonlukDizi.py +102 -46
- KekikStream/Plugins/Sinefy.py +130 -101
- KekikStream/Plugins/SinemaCX.py +82 -37
- KekikStream/Plugins/Sinezy.py +61 -47
- KekikStream/Plugins/SuperFilmGeldi.py +72 -36
- KekikStream/Plugins/UgurFilm.py +72 -34
- KekikStream/requirements.txt +1 -1
- {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/METADATA +40 -32
- {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/RECORD +36 -35
- {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/WHEEL +0 -0
- {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/entry_points.txt +0 -0
- {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/licenses/LICENSE +0 -0
- {kekikstream-2.1.9.dist-info → kekikstream-2.2.7.dist-info}/top_level.txt +0 -0
|
@@ -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
|
-
from
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, ExtractResult
|
|
4
|
+
from selectolax.parser import HTMLParser
|
|
5
5
|
import re
|
|
6
6
|
|
|
7
7
|
class SezonlukDizi(PluginBase):
|
|
@@ -41,73 +41,125 @@ class SezonlukDizi(PluginBase):
|
|
|
41
41
|
|
|
42
42
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
43
43
|
istek = await self.httpx.get(f"{url}{page}")
|
|
44
|
-
secici =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
|
|
44
|
+
secici = HTMLParser(istek.text)
|
|
45
|
+
|
|
46
|
+
results = []
|
|
47
|
+
for veri in secici.css("div.afis a"):
|
|
48
|
+
desc_el = veri.css_first("div.description")
|
|
49
|
+
img_el = veri.css_first("img")
|
|
50
|
+
|
|
51
|
+
title = desc_el.text(strip=True) if desc_el else None
|
|
52
|
+
href = veri.attrs.get("href")
|
|
53
|
+
poster = img_el.attrs.get("data-src") if img_el else None
|
|
54
|
+
|
|
55
|
+
if title and href:
|
|
56
|
+
results.append(MainPageResult(
|
|
57
|
+
category = category,
|
|
58
|
+
title = title,
|
|
59
|
+
url = self.fix_url(href),
|
|
60
|
+
poster = self.fix_url(poster) if poster else None,
|
|
61
|
+
))
|
|
62
|
+
|
|
63
|
+
return results
|
|
55
64
|
|
|
56
65
|
async def search(self, query: str) -> list[SearchResult]:
|
|
57
66
|
istek = await self.httpx.get(f"{self.main_url}/diziler.asp?adi={query}")
|
|
58
|
-
secici =
|
|
67
|
+
secici = HTMLParser(istek.text)
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
results = []
|
|
70
|
+
for afis in secici.css("div.afis a.column"):
|
|
71
|
+
desc_el = afis.css_first("div.description")
|
|
72
|
+
img_el = afis.css_first("img")
|
|
73
|
+
|
|
74
|
+
title = desc_el.text(strip=True) if desc_el else None
|
|
75
|
+
href = afis.attrs.get("href")
|
|
76
|
+
poster = img_el.attrs.get("data-src") if img_el else None
|
|
77
|
+
|
|
78
|
+
if title and href:
|
|
79
|
+
results.append(SearchResult(
|
|
80
|
+
title = title,
|
|
81
|
+
url = self.fix_url(href),
|
|
82
|
+
poster = self.fix_url(poster) if poster else None,
|
|
83
|
+
))
|
|
84
|
+
|
|
85
|
+
return results
|
|
68
86
|
|
|
69
87
|
async def load_item(self, url: str) -> SeriesInfo:
|
|
70
88
|
istek = await self.httpx.get(url)
|
|
71
|
-
secici =
|
|
89
|
+
secici = HTMLParser(istek.text)
|
|
90
|
+
|
|
91
|
+
title_el = secici.css_first("div.header")
|
|
92
|
+
title = title_el.text(strip=True) if title_el else ""
|
|
93
|
+
|
|
94
|
+
poster_el = secici.css_first("div.image img")
|
|
95
|
+
poster = poster_el.attrs.get("data-src", "").strip() if poster_el else ""
|
|
96
|
+
|
|
97
|
+
# year: re_first yerine re.search
|
|
98
|
+
year_el = secici.css_first("div.extra span")
|
|
99
|
+
year_text = year_el.text(strip=True) if year_el else ""
|
|
100
|
+
year_match = re.search(r"(\d{4})", year_text)
|
|
101
|
+
year = year_match.group(1) if year_match else None
|
|
72
102
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
tags
|
|
78
|
-
|
|
79
|
-
|
|
103
|
+
# xpath normalized-space yerine doğrudan ID ile element bulup text al
|
|
104
|
+
desc_el = secici.css_first("span#tartismayorum-konu")
|
|
105
|
+
description = desc_el.text(strip=True) if desc_el else ""
|
|
106
|
+
|
|
107
|
+
tags = [a.text(strip=True) for a in secici.css("div.labels a[href*='tur']") if a.text(strip=True)]
|
|
108
|
+
|
|
109
|
+
# rating: re_first yerine re.search
|
|
110
|
+
rating_el = secici.css_first("div.dizipuani a div")
|
|
111
|
+
rating_text = rating_el.text(strip=True) if rating_el else ""
|
|
112
|
+
rating_match = re.search(r"[\d.,]+", rating_text)
|
|
113
|
+
rating = rating_match.group() if rating_match else None
|
|
114
|
+
|
|
115
|
+
actors = []
|
|
80
116
|
|
|
81
117
|
actors_istek = await self.httpx.get(f"{self.main_url}/oyuncular/{url.split('/')[-1]}")
|
|
82
|
-
actors_secici =
|
|
83
|
-
|
|
84
|
-
actor.
|
|
85
|
-
|
|
86
|
-
|
|
118
|
+
actors_secici = HTMLParser(actors_istek.text)
|
|
119
|
+
for actor in actors_secici.css("div.doubling div.ui"):
|
|
120
|
+
header_el = actor.css_first("div.header")
|
|
121
|
+
if header_el and header_el.text(strip=True):
|
|
122
|
+
actors.append(header_el.text(strip=True))
|
|
87
123
|
|
|
88
124
|
episodes_istek = await self.httpx.get(f"{self.main_url}/bolumler/{url.split('/')[-1]}")
|
|
89
|
-
episodes_secici =
|
|
125
|
+
episodes_secici = HTMLParser(episodes_istek.text)
|
|
90
126
|
episodes = []
|
|
91
127
|
|
|
92
128
|
for sezon in episodes_secici.css("table.unstackable"):
|
|
93
129
|
for bolum in sezon.css("tbody tr"):
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
130
|
+
# td:nth-of-type selectolax'ta desteklenmiyor, alternatif yol: tüm td'leri alıp indexle
|
|
131
|
+
tds = bolum.css("td")
|
|
132
|
+
if len(tds) < 4:
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
# 4. td'den isim ve href
|
|
136
|
+
ep_name_el = tds[3].css_first("a")
|
|
137
|
+
ep_name = ep_name_el.text(strip=True) if ep_name_el else None
|
|
138
|
+
ep_href = ep_name_el.attrs.get("href") if ep_name_el else None
|
|
139
|
+
|
|
140
|
+
# 3. td'den episode (re_first yerine re.search)
|
|
141
|
+
ep_episode_el = tds[2].css_first("a")
|
|
142
|
+
ep_episode_text = ep_episode_el.text(strip=True) if ep_episode_el else ""
|
|
143
|
+
ep_episode_match = re.search(r"(\d+)", ep_episode_text)
|
|
144
|
+
ep_episode = ep_episode_match.group(1) if ep_episode_match else None
|
|
145
|
+
|
|
146
|
+
# 2. td'den season (re_first yerine re.search)
|
|
147
|
+
ep_season_text = tds[1].text(strip=True) if tds[1] else ""
|
|
148
|
+
ep_season_match = re.search(r"(\d+)", ep_season_text)
|
|
149
|
+
ep_season = ep_season_match.group(1) if ep_season_match else None
|
|
98
150
|
|
|
99
151
|
if ep_name and ep_href:
|
|
100
152
|
episode = Episode(
|
|
101
153
|
season = ep_season,
|
|
102
154
|
episode = ep_episode,
|
|
103
155
|
title = ep_name,
|
|
104
|
-
url = ep_href,
|
|
156
|
+
url = self.fix_url(ep_href),
|
|
105
157
|
)
|
|
106
158
|
episodes.append(episode)
|
|
107
159
|
|
|
108
160
|
return SeriesInfo(
|
|
109
161
|
url = url,
|
|
110
|
-
poster = poster,
|
|
162
|
+
poster = self.fix_url(poster) if poster else None,
|
|
111
163
|
title = title,
|
|
112
164
|
description = description,
|
|
113
165
|
tags = tags,
|
|
@@ -133,9 +185,10 @@ class SezonlukDizi(PluginBase):
|
|
|
133
185
|
|
|
134
186
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
135
187
|
istek = await self.httpx.get(url)
|
|
136
|
-
secici =
|
|
188
|
+
secici = HTMLParser(istek.text)
|
|
137
189
|
|
|
138
|
-
|
|
190
|
+
dilsec_el = secici.css_first("div#dilsec")
|
|
191
|
+
bid = dilsec_el.attrs.get("data-id") if dilsec_el else None
|
|
139
192
|
if not bid:
|
|
140
193
|
return []
|
|
141
194
|
|
|
@@ -162,9 +215,12 @@ class SezonlukDizi(PluginBase):
|
|
|
162
215
|
headers = {"X-Requested-With": "XMLHttpRequest"},
|
|
163
216
|
data = {"id": veri.get("id")},
|
|
164
217
|
)
|
|
165
|
-
|
|
218
|
+
veri_secici = HTMLParser(veri_response.text)
|
|
219
|
+
|
|
220
|
+
iframe_el = veri_secici.css_first("iframe")
|
|
221
|
+
iframe = iframe_el.attrs.get("src") if iframe_el else None
|
|
166
222
|
|
|
167
|
-
if iframe
|
|
223
|
+
if iframe:
|
|
168
224
|
if "link.asp" in iframe:
|
|
169
225
|
continue
|
|
170
226
|
|
KekikStream/Plugins/Sinefy.py
CHANGED
|
@@ -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
|
-
from
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, MovieInfo, ExtractResult
|
|
4
|
+
from selectolax.parser import HTMLParser
|
|
5
5
|
import re, json, urllib.parse
|
|
6
6
|
|
|
7
7
|
class Sinefy(PluginBase):
|
|
@@ -37,31 +37,35 @@ class Sinefy(PluginBase):
|
|
|
37
37
|
|
|
38
38
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
39
39
|
if "page/" in url:
|
|
40
|
-
|
|
40
|
+
full_url = f"{url}{page}"
|
|
41
41
|
elif "en-yenifilmler" in url or "netflix" in url:
|
|
42
|
-
|
|
42
|
+
full_url = f"{url}/{page}"
|
|
43
43
|
else:
|
|
44
|
-
|
|
44
|
+
full_url = f"{url}&page={page}"
|
|
45
45
|
|
|
46
46
|
resp = await self.httpx.get(full_url)
|
|
47
|
-
sel =
|
|
47
|
+
sel = HTMLParser(resp.text)
|
|
48
48
|
|
|
49
49
|
results = []
|
|
50
|
-
# Kotlin: div.poster-with-subject, div.dark-segment div.poster-md.poster
|
|
51
50
|
for item in sel.css("div.poster-with-subject, div.dark-segment div.poster-md.poster"):
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
51
|
+
h2_el = item.css_first("h2")
|
|
52
|
+
link_el = item.css_first("a")
|
|
53
|
+
img_el = item.css_first("img")
|
|
54
|
+
|
|
55
|
+
title = h2_el.text(strip=True) if h2_el else None
|
|
56
|
+
href = link_el.attrs.get("href") if link_el else None
|
|
57
|
+
poster = img_el.attrs.get("data-srcset") if img_el else None
|
|
58
|
+
|
|
59
|
+
if poster:
|
|
60
|
+
poster = poster.split(",")[0].split(" ")[0]
|
|
61
|
+
|
|
62
|
+
if title and href:
|
|
63
|
+
results.append(MainPageResult(
|
|
64
|
+
category = category,
|
|
65
|
+
title = title,
|
|
66
|
+
url = self.fix_url(href),
|
|
67
|
+
poster = self.fix_url(poster) if poster else None
|
|
68
|
+
))
|
|
65
69
|
|
|
66
70
|
return results
|
|
67
71
|
|
|
@@ -71,13 +75,18 @@ class Sinefy(PluginBase):
|
|
|
71
75
|
c_value = "MTc0NzI2OTAwMDU3ZTEwYmZjMDViNWFmOWIwZDViODg0MjU4MjA1ZmYxOThmZTYwMDdjMWQzMzliNzY5NzFlZmViMzRhMGVmNjgwODU3MGIyZA=="
|
|
72
76
|
|
|
73
77
|
try:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
resp = await self.httpx.get(self.main_url)
|
|
79
|
+
sel = HTMLParser(resp.text)
|
|
80
|
+
|
|
81
|
+
cke_el = sel.css_first("input[name='cKey']")
|
|
82
|
+
cval_el = sel.css_first("input[name='cValue']")
|
|
83
|
+
|
|
84
|
+
cke = cke_el.attrs.get("value") if cke_el else None
|
|
85
|
+
cval = cval_el.attrs.get("value") if cval_el else None
|
|
86
|
+
|
|
87
|
+
if cke and cval:
|
|
88
|
+
c_key = cke
|
|
89
|
+
c_value = cval
|
|
81
90
|
|
|
82
91
|
except Exception:
|
|
83
92
|
pass
|
|
@@ -111,23 +120,23 @@ class Sinefy(PluginBase):
|
|
|
111
120
|
res_array = data.get("data", {}).get("result", [])
|
|
112
121
|
|
|
113
122
|
if not res_array:
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
# Fallback manual parsing ?
|
|
124
|
+
pass
|
|
116
125
|
|
|
117
126
|
for item in res_array:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
name = item.get("object_name")
|
|
128
|
+
slug = item.get("used_slug")
|
|
129
|
+
poster = item.get("object_poster_url")
|
|
130
|
+
|
|
131
|
+
if name and slug:
|
|
132
|
+
if "cdn.ampproject.org" in poster:
|
|
133
|
+
poster = "https://images.macellan.online/images/movie/poster/180/275/80/" + poster.split("/")[-1]
|
|
134
|
+
|
|
135
|
+
results.append(SearchResult(
|
|
136
|
+
title=name,
|
|
137
|
+
url=self.fix_url(slug),
|
|
138
|
+
poster=self.fix_url(poster) if poster else None
|
|
139
|
+
))
|
|
131
140
|
return results
|
|
132
141
|
|
|
133
142
|
except Exception:
|
|
@@ -136,74 +145,85 @@ class Sinefy(PluginBase):
|
|
|
136
145
|
|
|
137
146
|
async def load_item(self, url: str) -> SeriesInfo:
|
|
138
147
|
resp = await self.httpx.get(url)
|
|
139
|
-
sel =
|
|
148
|
+
sel = HTMLParser(resp.text)
|
|
140
149
|
|
|
141
|
-
|
|
142
|
-
|
|
150
|
+
title_el = sel.css_first("h1")
|
|
151
|
+
title = title_el.text(strip=True) if title_el else None
|
|
152
|
+
|
|
153
|
+
img_el = sel.css_first("div.ui.items img")
|
|
154
|
+
poster_info = img_el.attrs.get("data-srcset") if img_el else None
|
|
143
155
|
poster = None
|
|
144
156
|
if poster_info:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
# take 1x
|
|
158
|
+
parts = str(poster_info).split(",")
|
|
159
|
+
for p in parts:
|
|
160
|
+
if "1x" in p:
|
|
161
|
+
poster = p.strip().split(" ")[0]
|
|
162
|
+
break
|
|
151
163
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
164
|
+
desc_el = sel.css_first("p#tv-series-desc")
|
|
165
|
+
description = desc_el.text(strip=True) if desc_el else None
|
|
166
|
+
|
|
167
|
+
tags = [a.text(strip=True) for a in sel.css("div.item.categories a") if a.text(strip=True)]
|
|
168
|
+
|
|
169
|
+
rating_el = sel.css_first("span.color-imdb")
|
|
170
|
+
rating = rating_el.text(strip=True) if rating_el else None
|
|
171
|
+
|
|
172
|
+
actors = [h5.text(strip=True) for h5 in sel.css("div.content h5") if h5.text(strip=True)]
|
|
173
|
+
|
|
174
|
+
year_el = sel.css_first("span.item.year")
|
|
175
|
+
year = year_el.text(strip=True) if year_el else None
|
|
157
176
|
|
|
158
177
|
episodes = []
|
|
159
178
|
season_elements = sel.css("section.episodes-box")
|
|
160
179
|
|
|
161
180
|
if season_elements:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
181
|
+
# Get season links
|
|
182
|
+
season_links = []
|
|
183
|
+
menu = sel.css("div.ui.vertical.fluid.tabular.menu a")
|
|
184
|
+
for link in menu:
|
|
185
|
+
href = link.attrs.get("href")
|
|
186
|
+
if href:
|
|
187
|
+
season_links.append(self.fix_url(href))
|
|
188
|
+
|
|
189
|
+
for s_url in season_links:
|
|
190
|
+
target_url = s_url if "/bolum-" in s_url else f"{s_url}/bolum-1"
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
s_resp = await self.httpx.get(target_url)
|
|
194
|
+
s_sel = HTMLParser(s_resp.text)
|
|
195
|
+
ep_links = s_sel.css("div.ui.list.celled a.item")
|
|
196
|
+
|
|
197
|
+
current_season_no = 1
|
|
198
|
+
match = re.search(r"sezon-(\d+)", target_url)
|
|
199
|
+
if match:
|
|
200
|
+
current_season_no = int(match.group(1))
|
|
201
|
+
|
|
202
|
+
for ep_link in ep_links:
|
|
203
|
+
href = ep_link.attrs.get("href")
|
|
204
|
+
name_el = ep_link.css_first("div.content div.header")
|
|
205
|
+
name = name_el.text(strip=True) if name_el else ""
|
|
206
|
+
|
|
207
|
+
if href:
|
|
208
|
+
ep_no = 0
|
|
209
|
+
match_ep = re.search(r"bolum-(\d+)", href)
|
|
210
|
+
if match_ep:
|
|
211
|
+
ep_no = int(match_ep.group(1))
|
|
212
|
+
|
|
213
|
+
episodes.append(Episode(
|
|
214
|
+
season = current_season_no,
|
|
215
|
+
episode = ep_no,
|
|
216
|
+
title = name,
|
|
217
|
+
url = self.fix_url(href)
|
|
218
|
+
))
|
|
219
|
+
except Exception:
|
|
220
|
+
pass
|
|
201
221
|
|
|
202
222
|
if episodes:
|
|
203
223
|
return SeriesInfo(
|
|
204
224
|
title = title,
|
|
205
225
|
url = url,
|
|
206
|
-
poster = self.fix_url(poster),
|
|
226
|
+
poster = self.fix_url(poster) if poster else None,
|
|
207
227
|
description = description,
|
|
208
228
|
rating = rating,
|
|
209
229
|
tags = tags,
|
|
@@ -215,7 +235,7 @@ class Sinefy(PluginBase):
|
|
|
215
235
|
return MovieInfo(
|
|
216
236
|
title = title,
|
|
217
237
|
url = url,
|
|
218
|
-
poster = self.fix_url(poster),
|
|
238
|
+
poster = self.fix_url(poster) if poster else None,
|
|
219
239
|
description = description,
|
|
220
240
|
rating = rating,
|
|
221
241
|
tags = tags,
|
|
@@ -225,16 +245,25 @@ class Sinefy(PluginBase):
|
|
|
225
245
|
|
|
226
246
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
227
247
|
resp = await self.httpx.get(url)
|
|
228
|
-
sel =
|
|
248
|
+
sel = HTMLParser(resp.text)
|
|
229
249
|
|
|
230
|
-
|
|
250
|
+
iframe_el = sel.css_first("iframe")
|
|
251
|
+
iframe = iframe_el.attrs.get("src") if iframe_el else None
|
|
252
|
+
|
|
231
253
|
if not iframe:
|
|
232
254
|
return []
|
|
233
255
|
|
|
234
256
|
iframe_url = self.fix_url(iframe)
|
|
235
257
|
|
|
236
|
-
#
|
|
237
|
-
|
|
258
|
+
# Try to extract actual video URL, fallback to raw iframe if fails
|
|
259
|
+
try:
|
|
260
|
+
result = await self.extract(iframe_url)
|
|
261
|
+
if result:
|
|
262
|
+
return [result] if not isinstance(result, list) else result
|
|
263
|
+
except Exception:
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
# Fallback: return raw iframe URL
|
|
238
267
|
return [ExtractResult(
|
|
239
268
|
url = iframe_url,
|
|
240
269
|
name = "Sinefy Player"
|