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/UgurFilm.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
|
-
|
|
5
|
-
import re
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
|
|
4
|
+
import asyncio, contextlib
|
|
6
5
|
|
|
7
6
|
class UgurFilm(PluginBase):
|
|
8
7
|
name = "UgurFilm"
|
|
@@ -26,46 +25,36 @@ class UgurFilm(PluginBase):
|
|
|
26
25
|
|
|
27
26
|
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
28
27
|
istek = await self.httpx.get(f"{url}{page}", follow_redirects=True)
|
|
29
|
-
secici =
|
|
28
|
+
secici = HTMLHelper(istek.text)
|
|
30
29
|
|
|
31
30
|
results = []
|
|
32
|
-
for veri in secici.
|
|
31
|
+
for veri in secici.select("div.icerik div"):
|
|
33
32
|
# Title is in the second span (a.baslik > span), not the first span (class="sol" which is empty)
|
|
34
|
-
|
|
35
|
-
title = title_el.text(strip=True) if title_el else None
|
|
33
|
+
title = secici.select_text("a.baslik span", veri)
|
|
36
34
|
if not title:
|
|
37
35
|
continue
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
href = link_el.attrs.get("href") if link_el else None
|
|
43
|
-
poster = img_el.attrs.get("src") if img_el else None
|
|
37
|
+
href = secici.select_attr("a", "href", veri)
|
|
38
|
+
poster = secici.select_attr("img", "src", veri)
|
|
44
39
|
|
|
45
40
|
results.append(MainPageResult(
|
|
46
41
|
category = category,
|
|
47
42
|
title = title,
|
|
48
43
|
url = self.fix_url(href) if href else "",
|
|
49
|
-
poster = self.fix_url(poster)
|
|
44
|
+
poster = self.fix_url(poster),
|
|
50
45
|
))
|
|
51
46
|
|
|
52
47
|
return results
|
|
53
48
|
|
|
54
49
|
async def search(self, query: str) -> list[SearchResult]:
|
|
55
50
|
istek = await self.httpx.get(f"{self.main_url}/?s={query}")
|
|
56
|
-
secici =
|
|
51
|
+
secici = HTMLHelper(istek.text)
|
|
57
52
|
|
|
58
53
|
results = []
|
|
59
|
-
for film in secici.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
link_el = film.css_first("a")
|
|
65
|
-
img_el = film.css_first("img")
|
|
66
|
-
|
|
67
|
-
href = link_el.attrs.get("href") if link_el else None
|
|
68
|
-
poster = img_el.attrs.get("src") if img_el else None
|
|
54
|
+
for film in secici.select("div.icerik div"):
|
|
55
|
+
title = secici.select_text("a.baslik span", film)
|
|
56
|
+
href = secici.select_attr("a", "href", film)
|
|
57
|
+
poster = secici.select_attr("img", "src", film)
|
|
69
58
|
|
|
70
59
|
if title and href:
|
|
71
60
|
results.append(SearchResult(
|
|
@@ -78,68 +67,101 @@ class UgurFilm(PluginBase):
|
|
|
78
67
|
|
|
79
68
|
async def load_item(self, url: str) -> MovieInfo:
|
|
80
69
|
istek = await self.httpx.get(url)
|
|
81
|
-
secici =
|
|
82
|
-
|
|
83
|
-
title_el = secici.css_first("div.bilgi h2")
|
|
84
|
-
title = title_el.text(strip=True) if title_el else ""
|
|
85
|
-
|
|
86
|
-
poster_el = secici.css_first("div.resim img")
|
|
87
|
-
poster = poster_el.attrs.get("src", "").strip() if poster_el else ""
|
|
88
|
-
|
|
89
|
-
desc_el = secici.css_first("div.slayt-aciklama")
|
|
90
|
-
description = desc_el.text(strip=True) if desc_el else ""
|
|
70
|
+
secici = HTMLHelper(istek.text)
|
|
91
71
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
actors = []
|
|
101
|
-
for actor in secici.css("li.oyuncu-k"):
|
|
102
|
-
span_el = actor.css_first("span")
|
|
103
|
-
if span_el and span_el.text(strip=True):
|
|
104
|
-
actors.append(span_el.text(strip=True))
|
|
72
|
+
title = secici.select_text("div.bilgi h2")
|
|
73
|
+
poster = secici.select_poster("div.resim img")
|
|
74
|
+
description = secici.select_text("div.slayt-aciklama")
|
|
75
|
+
rating = secici.select_text("b#puandegistir")
|
|
76
|
+
tags = secici.select_texts("p.tur a[href*='/category/']")
|
|
77
|
+
year = secici.extract_year("a[href*='/yil/']")
|
|
78
|
+
actors = secici.select_texts("li.oyuncu-k span")
|
|
79
|
+
duration = secici.regex_first(r"(\d+) Dakika", secici.select_text("div.bilgi b"))
|
|
105
80
|
|
|
106
81
|
return MovieInfo(
|
|
107
|
-
url =
|
|
108
|
-
poster = self.fix_url(poster)
|
|
82
|
+
url = url,
|
|
83
|
+
poster = self.fix_url(poster),
|
|
109
84
|
title = title,
|
|
110
85
|
description = description,
|
|
86
|
+
rating = rating,
|
|
111
87
|
tags = tags,
|
|
112
88
|
year = year,
|
|
113
89
|
actors = actors,
|
|
90
|
+
duration = duration
|
|
114
91
|
)
|
|
115
92
|
|
|
116
93
|
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
117
94
|
istek = await self.httpx.get(url)
|
|
118
|
-
secici =
|
|
95
|
+
secici = HTMLHelper(istek.text)
|
|
119
96
|
results = []
|
|
120
97
|
|
|
121
|
-
part_links =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
sub_response = await self.httpx.get(part_link)
|
|
125
|
-
sub_selector = HTMLParser(sub_response.text)
|
|
98
|
+
part_links = secici.select_attrs("li.parttab a", "href")
|
|
99
|
+
if not part_links:
|
|
100
|
+
part_links = [url]
|
|
126
101
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
post_data = {
|
|
132
|
-
"vid" : iframe.split("vid=")[-1],
|
|
133
|
-
"alternative" : "vidmoly",
|
|
134
|
-
"ord" : "0",
|
|
135
|
-
}
|
|
136
|
-
player_response = await self.httpx.post(
|
|
102
|
+
async def process_alt(vid: str, alt_name: str, ord_val: str) -> list[ExtractResult]:
|
|
103
|
+
"""Alternatif player kaynağından video linkini çıkarır."""
|
|
104
|
+
with contextlib.suppress(Exception):
|
|
105
|
+
resp = await self.httpx.post(
|
|
137
106
|
url = f"{self.main_url}/player/ajax_sources.php",
|
|
138
|
-
data =
|
|
107
|
+
data = {"vid": vid, "alternative": alt_name, "ord": ord_val}
|
|
139
108
|
)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
109
|
+
if iframe_url := resp.json().get("iframe"):
|
|
110
|
+
data = await self.extract(self.fix_url(iframe_url))
|
|
111
|
+
if not data:
|
|
112
|
+
return []
|
|
113
|
+
|
|
114
|
+
return data if isinstance(data, list) else [data]
|
|
115
|
+
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
async def process_part(part_url: str) -> list[ExtractResult]:
|
|
119
|
+
"""Her bir part sayfasını ve alternatiflerini işler."""
|
|
120
|
+
try:
|
|
121
|
+
# Elimizde zaten olan ana sayfayı tekrar çekmemek için
|
|
122
|
+
if part_url == url:
|
|
123
|
+
sub_sec = secici
|
|
124
|
+
else:
|
|
125
|
+
sub_resp = await self.httpx.get(part_url)
|
|
126
|
+
sub_sec = HTMLHelper(sub_resp.text)
|
|
127
|
+
|
|
128
|
+
iframe = sub_sec.select_attr("div#vast iframe", "src")
|
|
129
|
+
if not iframe:
|
|
130
|
+
return []
|
|
131
|
+
|
|
132
|
+
if self.main_url not in iframe:
|
|
133
|
+
data = await self.extract(self.fix_url(iframe))
|
|
134
|
+
if not data:
|
|
135
|
+
return []
|
|
136
|
+
|
|
137
|
+
return data if isinstance(data, list) else [data]
|
|
138
|
+
|
|
139
|
+
# İç kaynaklı ise 3 alternatif için paralel istek at
|
|
140
|
+
vid = iframe.split("vid=")[-1]
|
|
141
|
+
tasks = [
|
|
142
|
+
process_alt(vid, "vidmoly", "0"),
|
|
143
|
+
process_alt(vid, "ok.ru", "1"),
|
|
144
|
+
process_alt(vid, "mailru", "2")
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
alt_results = await asyncio.gather(*tasks)
|
|
148
|
+
|
|
149
|
+
return [item for sublist in alt_results for item in sublist]
|
|
150
|
+
except Exception:
|
|
151
|
+
return []
|
|
152
|
+
|
|
153
|
+
# Tüm partları paralel işle
|
|
154
|
+
groups = await asyncio.gather(*(process_part(p) for p in part_links))
|
|
155
|
+
|
|
156
|
+
for group in groups:
|
|
157
|
+
results.extend(group)
|
|
158
|
+
|
|
159
|
+
# Duplicate Temizliği
|
|
160
|
+
unique_results = []
|
|
161
|
+
seen = set()
|
|
162
|
+
for res in results:
|
|
163
|
+
if res.url and res.url not in seen:
|
|
164
|
+
unique_results.append(res)
|
|
165
|
+
seen.add(res.url)
|
|
166
|
+
|
|
167
|
+
return unique_results
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, HTMLHelper
|
|
4
|
+
|
|
5
|
+
class Watch32(PluginBase):
|
|
6
|
+
name = "Watch32"
|
|
7
|
+
language = "en"
|
|
8
|
+
main_url = "https://watch32.sx"
|
|
9
|
+
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
10
|
+
description = "Watch Your Favorite Movies & TV Shows Online - Streaming For Free. With Movies & TV Shows Full HD. Find Your Movies & Watch NOW!"
|
|
11
|
+
|
|
12
|
+
main_page = {
|
|
13
|
+
# Main Categories
|
|
14
|
+
f"{main_url}/movie?page=" : "Popular Movies",
|
|
15
|
+
f"{main_url}/tv-show?page=" : "Popular TV Shows",
|
|
16
|
+
f"{main_url}/coming-soon?page=" : "Coming Soon",
|
|
17
|
+
f"{main_url}/top-imdb?page=" : "Top IMDB Rating",
|
|
18
|
+
# Genre Categories
|
|
19
|
+
f"{main_url}/genre/action?page=" : "Action",
|
|
20
|
+
f"{main_url}/genre/adventure?page=" : "Adventure",
|
|
21
|
+
f"{main_url}/genre/animation?page=" : "Animation",
|
|
22
|
+
f"{main_url}/genre/biography?page=" : "Biography",
|
|
23
|
+
f"{main_url}/genre/comedy?page=" : "Comedy",
|
|
24
|
+
f"{main_url}/genre/crime?page=" : "Crime",
|
|
25
|
+
f"{main_url}/genre/documentary?page=" : "Documentary",
|
|
26
|
+
f"{main_url}/genre/drama?page=" : "Drama",
|
|
27
|
+
f"{main_url}/genre/family?page=" : "Family",
|
|
28
|
+
f"{main_url}/genre/fantasy?page=" : "Fantasy",
|
|
29
|
+
f"{main_url}/genre/history?page=" : "History",
|
|
30
|
+
f"{main_url}/genre/horror?page=" : "Horror",
|
|
31
|
+
f"{main_url}/genre/music?page=" : "Music",
|
|
32
|
+
f"{main_url}/genre/mystery?page=" : "Mystery",
|
|
33
|
+
f"{main_url}/genre/romance?page=" : "Romance",
|
|
34
|
+
f"{main_url}/genre/science-fiction?page=" : "Science Fiction",
|
|
35
|
+
f"{main_url}/genre/thriller?page=" : "Thriller",
|
|
36
|
+
f"{main_url}/genre/war?page=" : "War",
|
|
37
|
+
f"{main_url}/genre/western?page=" : "Western",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
41
|
+
istek = await self.httpx.get(f"{url}{page}")
|
|
42
|
+
helper = HTMLHelper(istek.text)
|
|
43
|
+
items = helper.select("div.flw-item")
|
|
44
|
+
|
|
45
|
+
return [
|
|
46
|
+
MainPageResult(
|
|
47
|
+
category = category,
|
|
48
|
+
title = helper.select_attr("h2.film-name a", "title", veri),
|
|
49
|
+
url = self.fix_url(helper.select_attr("h2.film-name a", "href", veri)),
|
|
50
|
+
poster = helper.select_attr("img.film-poster-img", "data-src", veri)
|
|
51
|
+
)
|
|
52
|
+
for veri in items
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
async def search(self, query: str) -> list[SearchResult]:
|
|
56
|
+
istek = await self.httpx.get(f"{self.main_url}/search/{query.replace(' ', '-')}")
|
|
57
|
+
secici = HTMLHelper(istek.text)
|
|
58
|
+
|
|
59
|
+
return [
|
|
60
|
+
SearchResult(
|
|
61
|
+
title = secici.select_attr("h2.film-name a", "title", veri),
|
|
62
|
+
url = self.fix_url(secici.select_attr("h2.film-name a", "href", veri)),
|
|
63
|
+
poster = secici.select_attr("img.film-poster-img", "data-src", veri)
|
|
64
|
+
)
|
|
65
|
+
for veri in secici.select("div.flw-item")
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
|
|
69
|
+
istek = await self.httpx.get(url)
|
|
70
|
+
secici = HTMLHelper(istek.text)
|
|
71
|
+
|
|
72
|
+
content_id = secici.select_attr("div.detail_page-watch", "data-id")
|
|
73
|
+
details = secici.select_first("div.detail_page-infor")
|
|
74
|
+
name = secici.select_text("h2.heading-name > a", details)
|
|
75
|
+
poster = secici.select_poster("div.film-poster > img", details)
|
|
76
|
+
description = secici.select_text("div.description", details)
|
|
77
|
+
year = str(secici.extract_year())
|
|
78
|
+
tags = secici.meta_list("Genre", container_selector="div.row-line")
|
|
79
|
+
rating = secici.select_text("button.btn-imdb").replace("N/A", "").split(":")[-1].strip() if secici.select_text("button.btn-imdb") else None
|
|
80
|
+
actors = secici.meta_list("Casts", container_selector="div.row-line")
|
|
81
|
+
|
|
82
|
+
common_info = {
|
|
83
|
+
"url" : url,
|
|
84
|
+
"poster" : self.fix_url(poster),
|
|
85
|
+
"title" : name,
|
|
86
|
+
"description" : description,
|
|
87
|
+
"tags" : tags,
|
|
88
|
+
"rating" : rating,
|
|
89
|
+
"year" : year,
|
|
90
|
+
"actors" : actors
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if "movie" in url:
|
|
94
|
+
return MovieInfo(**common_info)
|
|
95
|
+
|
|
96
|
+
episodes = []
|
|
97
|
+
seasons_resp = await self.httpx.get(f"{self.main_url}/ajax/season/list/{content_id}")
|
|
98
|
+
sh = HTMLHelper(seasons_resp.text)
|
|
99
|
+
|
|
100
|
+
for season in sh.select("a.dropdown-item"):
|
|
101
|
+
season_id = season.attrs.get("data-id")
|
|
102
|
+
s_val, _ = sh.extract_season_episode(season.text())
|
|
103
|
+
|
|
104
|
+
e_resp = await self.httpx.get(f"{self.main_url}/ajax/season/episodes/{season_id}")
|
|
105
|
+
eh = HTMLHelper(e_resp.text)
|
|
106
|
+
|
|
107
|
+
for ep in eh.select("a.eps-item"):
|
|
108
|
+
ep_id = ep.attrs.get("data-id")
|
|
109
|
+
ep_title = ep.attrs.get("title", "")
|
|
110
|
+
_, e_val = eh.extract_season_episode(ep_title)
|
|
111
|
+
|
|
112
|
+
episodes.append(Episode(
|
|
113
|
+
season = s_val or 1,
|
|
114
|
+
episode = e_val or 1,
|
|
115
|
+
title = ep_title,
|
|
116
|
+
url = f"servers/{ep_id}"
|
|
117
|
+
))
|
|
118
|
+
|
|
119
|
+
return SeriesInfo(**common_info, episodes=episodes)
|
|
120
|
+
|
|
121
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
122
|
+
# url in load_links might be the full page URL for movies or "servers/epId" for episodes
|
|
123
|
+
if "servers/" in url:
|
|
124
|
+
data = url.split("/")[-1]
|
|
125
|
+
servers_url = f"servers/{data}"
|
|
126
|
+
elif "list/" in url:
|
|
127
|
+
data = url.split("/")[-1]
|
|
128
|
+
servers_url = f"list/{data}"
|
|
129
|
+
else:
|
|
130
|
+
# Re-fetch page to get contentId only if we don't have list/ or servers/
|
|
131
|
+
istek = await self.httpx.get(url)
|
|
132
|
+
secici = HTMLHelper(istek.text)
|
|
133
|
+
content_id = secici.select_attr("div.detail_page-watch", "data-id")
|
|
134
|
+
if not content_id:
|
|
135
|
+
return []
|
|
136
|
+
servers_url = f"list/{content_id}"
|
|
137
|
+
|
|
138
|
+
servers_resp = await self.httpx.get(f"{self.main_url}/ajax/episode/{servers_url}")
|
|
139
|
+
sh = HTMLHelper(servers_resp.text)
|
|
140
|
+
servers = sh.select("a.link-item")
|
|
141
|
+
|
|
142
|
+
results = []
|
|
143
|
+
for server in servers:
|
|
144
|
+
server_name = server.text(strip=True)
|
|
145
|
+
link_id = server.attrs.get("data-linkid") or server.attrs.get("data-id")
|
|
146
|
+
source_resp = await self.httpx.get(f"{self.main_url}/ajax/episode/sources/{link_id}")
|
|
147
|
+
source_data = source_resp.json()
|
|
148
|
+
video_url = source_data.get("link")
|
|
149
|
+
|
|
150
|
+
if video_url:
|
|
151
|
+
extract_result = await self.extract(video_url, name_override=server_name)
|
|
152
|
+
if extract_result:
|
|
153
|
+
results.extend(extract_result if isinstance(extract_result, list) else [extract_result])
|
|
154
|
+
else:
|
|
155
|
+
results.append(ExtractResult(
|
|
156
|
+
url = video_url,
|
|
157
|
+
name = f"{self.name} | {server_name}"
|
|
158
|
+
))
|
|
159
|
+
|
|
160
|
+
return results
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, MovieInfo, Episode, ExtractResult, HTMLHelper
|
|
4
|
+
import asyncio, time, json
|
|
5
|
+
|
|
6
|
+
class YabanciDizi(PluginBase):
|
|
7
|
+
name = "YabanciDizi"
|
|
8
|
+
language = "tr"
|
|
9
|
+
main_url = "https://yabancidizi.so"
|
|
10
|
+
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
11
|
+
description = "Yabancidizi.so platformu üzerinden en güncel yabancı dizileri ve filmleri izleyebilir, favori içeriklerinizi takip edebilirsiniz."
|
|
12
|
+
|
|
13
|
+
main_page = {
|
|
14
|
+
f"{main_url}/kesfet/eyJvcmRlciI6ImRhdGVfYm90dG9tIiwia2F0ZWdvcnkiOlsiMTciXX0=" : "Diziler",
|
|
15
|
+
f"{main_url}/kesfet/eyJvcmRlciI6ImRhdGVfYm90dG9tIiwia2F0ZWdvcnkiOlsiMTgiXX0=" : "Filmler",
|
|
16
|
+
f"{main_url}/kesfet/eyJvcmRlciI6ImRhdGVfYm90dG9tIiwiY291bnRyeSI6eyJLUiI6IktSIn19" : "Kdrama",
|
|
17
|
+
f"{main_url}/kesfet/eyJvcmRlciI6ImRhdGVfYm90dG9tIiwiY291bnRyeSI6eyJKUCI6IkpQIn0sImNhdGVnb3J5IjpbXX0=" : "Jdrama",
|
|
18
|
+
f"{main_url}/kesfet/eyJvcmRlciI6ImRhdGVfYm90dG9tIiwiY2F0ZWdvcnkiOnsiMyI6IjMifX0=" : "Animasyon",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
22
|
+
istek = await self.httpx.get(
|
|
23
|
+
url = url if page == 1 else f"{url}/{page}",
|
|
24
|
+
headers = {"Referer": f"{self.main_url}/"}
|
|
25
|
+
)
|
|
26
|
+
secici = HTMLHelper(istek.text)
|
|
27
|
+
|
|
28
|
+
results = []
|
|
29
|
+
for item in secici.select("li.mb-lg, li.segment-poster"):
|
|
30
|
+
title = secici.select_text("h2", item)
|
|
31
|
+
href = secici.select_attr("a", "href", item)
|
|
32
|
+
poster = secici.select_attr("img", "src", item)
|
|
33
|
+
|
|
34
|
+
if title and href:
|
|
35
|
+
results.append(MainPageResult(
|
|
36
|
+
category = category,
|
|
37
|
+
title = title,
|
|
38
|
+
url = self.fix_url(href),
|
|
39
|
+
poster = self.fix_url(poster),
|
|
40
|
+
))
|
|
41
|
+
|
|
42
|
+
return results
|
|
43
|
+
|
|
44
|
+
async def search(self, query: str) -> list[SearchResult]:
|
|
45
|
+
istek = await self.httpx.post(
|
|
46
|
+
url = f"{self.main_url}/search?qr={query}",
|
|
47
|
+
headers = {
|
|
48
|
+
"X-Requested-With" : "XMLHttpRequest",
|
|
49
|
+
"Referer" : f"{self.main_url}/"
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
raw = istek.json()
|
|
55
|
+
res_array = raw.get("data", {}).get("result", [])
|
|
56
|
+
|
|
57
|
+
results = []
|
|
58
|
+
for item in res_array:
|
|
59
|
+
title = item.get("s_name")
|
|
60
|
+
image = item.get("s_image")
|
|
61
|
+
slug = item.get("s_link")
|
|
62
|
+
s_type = item.get("s_type") # 0: dizi, 1: film
|
|
63
|
+
|
|
64
|
+
poster = f"{self.main_url}/uploads/series/{image}" if image else None
|
|
65
|
+
|
|
66
|
+
if s_type == "1":
|
|
67
|
+
href = f"{self.main_url}/film/{slug}"
|
|
68
|
+
else:
|
|
69
|
+
href = f"{self.main_url}/dizi/{slug}"
|
|
70
|
+
|
|
71
|
+
if title and slug:
|
|
72
|
+
results.append(SearchResult(
|
|
73
|
+
title = title,
|
|
74
|
+
url = self.fix_url(href),
|
|
75
|
+
poster = self.fix_url(poster)
|
|
76
|
+
))
|
|
77
|
+
return results
|
|
78
|
+
except Exception:
|
|
79
|
+
return []
|
|
80
|
+
|
|
81
|
+
async def load_item(self, url: str) -> SeriesInfo | MovieInfo:
|
|
82
|
+
istek = await self.httpx.get(url, follow_redirects=True)
|
|
83
|
+
secici = HTMLHelper(istek.text)
|
|
84
|
+
|
|
85
|
+
title = (secici.select_attr("meta[property='og:title']", "content") or "").split("|")[0].strip() or secici.select_text("h1")
|
|
86
|
+
poster = secici.select_poster("meta[property='og:image']")
|
|
87
|
+
description = secici.select_text("p#tv-series-desc")
|
|
88
|
+
year = secici.extract_year("td div.truncate")
|
|
89
|
+
tags = secici.meta_list("Türü", container_selector="div.item")
|
|
90
|
+
rating = secici.meta_value("IMDb Puanı", container_selector="div.item")
|
|
91
|
+
duration = int(secici.regex_first(r"(\d+)", secici.meta_value("Süre", container_selector="div.item")) or 0)
|
|
92
|
+
actors = secici.meta_list("Oyuncular", container_selector="div.item") or secici.select_texts("div#common-cast-list div.item h5")
|
|
93
|
+
|
|
94
|
+
common_info = {
|
|
95
|
+
"url" : url,
|
|
96
|
+
"poster" : self.fix_url(poster),
|
|
97
|
+
"title" : title,
|
|
98
|
+
"description" : description,
|
|
99
|
+
"tags" : tags,
|
|
100
|
+
"rating" : rating,
|
|
101
|
+
"year" : year,
|
|
102
|
+
"actors" : actors,
|
|
103
|
+
"duration" : duration
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if "/film/" in url:
|
|
107
|
+
return MovieInfo(**common_info)
|
|
108
|
+
|
|
109
|
+
episodes = []
|
|
110
|
+
for bolum in secici.select("div.episodes-list div.ui td:has(h6)"):
|
|
111
|
+
link = secici.select_first("a", bolum)
|
|
112
|
+
if link:
|
|
113
|
+
href = link.attrs.get("href")
|
|
114
|
+
name = secici.select_text("h6", bolum) or link.text(strip=True)
|
|
115
|
+
s, e = secici.extract_season_episode(href)
|
|
116
|
+
episodes.append(Episode(
|
|
117
|
+
season = s or 1,
|
|
118
|
+
episode = e or 1,
|
|
119
|
+
title = name,
|
|
120
|
+
url = self.fix_url(href)
|
|
121
|
+
))
|
|
122
|
+
|
|
123
|
+
if episodes and (episodes[0].episode or 0) > (episodes[-1].episode or 0):
|
|
124
|
+
episodes.reverse()
|
|
125
|
+
|
|
126
|
+
return SeriesInfo(**common_info, episodes=episodes)
|
|
127
|
+
|
|
128
|
+
async def load_links(self, url: str) -> list[ExtractResult]:
|
|
129
|
+
loop = asyncio.get_event_loop()
|
|
130
|
+
|
|
131
|
+
# 1. Ana sayfayı çek
|
|
132
|
+
istek = await loop.run_in_executor(None, lambda: self.cloudscraper.get(url, headers={"Referer": f"{self.main_url}/"}))
|
|
133
|
+
secici = HTMLHelper(istek.text)
|
|
134
|
+
|
|
135
|
+
results = []
|
|
136
|
+
timestamp_ms = int(time.time() * 1000) - 50000
|
|
137
|
+
|
|
138
|
+
# 2. Dil Tablarını Bul
|
|
139
|
+
tabs = secici.select("div#series-tabs a")
|
|
140
|
+
|
|
141
|
+
async def process_tab(tab_el):
|
|
142
|
+
data_eid = tab_el.attrs.get("data-eid")
|
|
143
|
+
data_type = tab_el.attrs.get("data-type") # 1: Altyazı, 2: Dublaj
|
|
144
|
+
if not data_eid or not data_type:
|
|
145
|
+
return []
|
|
146
|
+
|
|
147
|
+
dil_adi = "Dublaj" if data_type == "2" else "Altyazı"
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
post_resp = await loop.run_in_executor(None, lambda: self.cloudscraper.post(
|
|
151
|
+
url = f"{self.main_url}/ajax/service",
|
|
152
|
+
headers = {
|
|
153
|
+
"X-Requested-With" : "XMLHttpRequest",
|
|
154
|
+
"Referer" : url
|
|
155
|
+
},
|
|
156
|
+
data = {
|
|
157
|
+
"lang" : data_type,
|
|
158
|
+
"episode" : data_eid,
|
|
159
|
+
"type" : "langTab"
|
|
160
|
+
},
|
|
161
|
+
cookies = {"udys": str(timestamp_ms)}
|
|
162
|
+
))
|
|
163
|
+
|
|
164
|
+
res_json = post_resp.json()
|
|
165
|
+
if not res_json.get("data"): return []
|
|
166
|
+
|
|
167
|
+
res_sel = HTMLHelper(res_json["data"])
|
|
168
|
+
sources = []
|
|
169
|
+
|
|
170
|
+
for item in res_sel.select("div.item"):
|
|
171
|
+
name = item.text(strip=True)
|
|
172
|
+
data_link = item.attrs.get("data-link")
|
|
173
|
+
if not data_link: continue
|
|
174
|
+
|
|
175
|
+
# Link normalizasyonu
|
|
176
|
+
safe_link = data_link.replace("/", "_").replace("+", "-")
|
|
177
|
+
|
|
178
|
+
# API Endpoint belirleme
|
|
179
|
+
api_path = None
|
|
180
|
+
if "VidMoly" in name:
|
|
181
|
+
api_path = "moly"
|
|
182
|
+
elif "Okru" in name:
|
|
183
|
+
api_path = "ruplay"
|
|
184
|
+
elif "Mac" in name:
|
|
185
|
+
api_path = "drive"
|
|
186
|
+
|
|
187
|
+
if api_path:
|
|
188
|
+
sources.append({
|
|
189
|
+
"name" : name,
|
|
190
|
+
"api_url" : f"{self.main_url}/api/{api_path}/{safe_link}",
|
|
191
|
+
"dil" : dil_adi
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
tab_results = []
|
|
195
|
+
for src in sources:
|
|
196
|
+
try:
|
|
197
|
+
# API sayfasını çekip içindeki iframe'i bulalım
|
|
198
|
+
api_resp = await loop.run_in_executor(None, lambda: self.cloudscraper.get(
|
|
199
|
+
src["api_url"],
|
|
200
|
+
headers={"Referer": f"{self.main_url}/"},
|
|
201
|
+
cookies={"udys": str(timestamp_ms)}
|
|
202
|
+
))
|
|
203
|
+
|
|
204
|
+
api_sel = HTMLHelper(api_resp.text)
|
|
205
|
+
iframe = api_sel.select_attr("iframe", "src")
|
|
206
|
+
|
|
207
|
+
if not iframe and "drive" in src["api_url"]:
|
|
208
|
+
t_sec = int(time.time())
|
|
209
|
+
drives_url = f"{src['api_url'].replace('/api/drive/', '/api/drives/')}?t={t_sec}"
|
|
210
|
+
api_resp = await loop.run_in_executor(None, lambda: self.cloudscraper.get(
|
|
211
|
+
drives_url,
|
|
212
|
+
headers={"Referer": src["api_url"]},
|
|
213
|
+
cookies={"udys": str(timestamp_ms)}
|
|
214
|
+
))
|
|
215
|
+
api_sel = HTMLHelper(api_resp.text)
|
|
216
|
+
iframe = api_sel.select_attr("iframe", "src")
|
|
217
|
+
|
|
218
|
+
if iframe:
|
|
219
|
+
prefix = f"{src['dil']} | {src['name']}"
|
|
220
|
+
extracted = await self.extract(self.fix_url(iframe), prefix=prefix)
|
|
221
|
+
if extracted:
|
|
222
|
+
tab_results.extend(extracted if isinstance(extracted, list) else [extracted])
|
|
223
|
+
except Exception:
|
|
224
|
+
continue
|
|
225
|
+
return tab_results
|
|
226
|
+
|
|
227
|
+
except Exception:
|
|
228
|
+
return []
|
|
229
|
+
|
|
230
|
+
if tabs:
|
|
231
|
+
results_groups = await asyncio.gather(*(process_tab(tab) for tab in tabs))
|
|
232
|
+
for group in results_groups:
|
|
233
|
+
results.extend(group)
|
|
234
|
+
else:
|
|
235
|
+
# Tab yoksa mevcut sayfada iframe ara
|
|
236
|
+
iframe = secici.select_attr("iframe", "src")
|
|
237
|
+
if iframe:
|
|
238
|
+
extracted = await self.extract(self.fix_url(iframe), name_override="Main")
|
|
239
|
+
if extracted:
|
|
240
|
+
results.extend(extracted if isinstance(extracted, list) else [extracted])
|
|
241
|
+
|
|
242
|
+
# Duplicate kontrolü
|
|
243
|
+
unique_results = []
|
|
244
|
+
seen = set()
|
|
245
|
+
for res in results:
|
|
246
|
+
if res.url and res.url not in seen:
|
|
247
|
+
unique_results.append(res)
|
|
248
|
+
seen.add(res.url)
|
|
249
|
+
|
|
250
|
+
return unique_results
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: KekikStream
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.3
|
|
4
4
|
Summary: terminal üzerinden medya içeriği aramanızı ve VLC/MPV gibi popüler medya oynatıcılar aracılığıyla doğrudan izlemenizi sağlayan modüler ve genişletilebilir bir bıdı bıdı
|
|
5
5
|
Home-page: https://github.com/keyiflerolsun/KekikStream
|
|
6
6
|
Author: keyiflerolsun
|