KekikStream 2.0.4__py3-none-any.whl → 2.0.5__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.
@@ -7,6 +7,19 @@ class ContentX(ExtractorBase):
7
7
  name = "ContentX"
8
8
  main_url = "https://contentx.me"
9
9
 
10
+ # Birden fazla domain destekle
11
+ supported_domains = [
12
+ "contentx.me", "four.contentx.me",
13
+ "dplayer82.site", "sn.dplayer82.site", "four.dplayer82.site", "org.dplayer82.site",
14
+ "dplayer74.site", "sn.dplayer74.site",
15
+ "hotlinger.com", "sn.hotlinger.com",
16
+ "playru.net", "four.playru.net",
17
+ "pichive.online", "four.pichive.online", "pichive.me", "four.pichive.me"
18
+ ]
19
+
20
+ def can_handle_url(self, url: str) -> bool:
21
+ return any(domain in url for domain in self.supported_domains)
22
+
10
23
  async def extract(self, url, referer=None) -> list[ExtractResult]:
11
24
  if referer:
12
25
  self.httpx.headers.update({"Referer": referer})
@@ -0,0 +1,86 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
+ from Kekik.Sifreleme import AESManager
5
+ import re, json
6
+
7
+ class DonilasPlay(ExtractorBase):
8
+ name = "DonilasPlay"
9
+ main_url = "https://donilasplay.com"
10
+
11
+ async def extract(self, url, referer=None) -> ExtractResult:
12
+ if referer:
13
+ self.httpx.headers.update({"Referer": referer})
14
+
15
+ istek = await self.httpx.get(url)
16
+ istek.raise_for_status()
17
+ i_source = istek.text
18
+
19
+ m3u_link = None
20
+ subtitles = []
21
+
22
+ # bePlayer pattern
23
+ be_player_match = re.search(r"bePlayer\('([^']+)',\s*'(\{[^}]+\})'\);", i_source)
24
+ if be_player_match:
25
+ be_player_pass = be_player_match.group(1)
26
+ be_player_data = be_player_match.group(2)
27
+
28
+ try:
29
+ # AES decrypt
30
+ decrypted = AESManager.decrypt(be_player_data, be_player_pass)
31
+ data = json.loads(decrypted)
32
+
33
+ m3u_link = data.get("video_location")
34
+
35
+ # Altyazıları işle
36
+ str_subtitles = data.get("strSubtitles", [])
37
+ if str_subtitles:
38
+ for sub in str_subtitles:
39
+ label = sub.get("label", "")
40
+ file = sub.get("file", "")
41
+ # Forced altyazıları hariç tut
42
+ if "Forced" in label:
43
+ continue
44
+ if file:
45
+ # Türkçe kontrolü
46
+ keywords = ["tur", "tr", "türkçe", "turkce"]
47
+ language = "Turkish" if any(k in label.lower() for k in keywords) else label
48
+ subtitles.append(Subtitle(
49
+ name = language,
50
+ url = self.fix_url(file)
51
+ ))
52
+ except Exception:
53
+ pass
54
+
55
+ # Fallback: file pattern
56
+ if not m3u_link:
57
+ file_match = re.search(r'file:"([^"]+)"', i_source)
58
+ if file_match:
59
+ m3u_link = file_match.group(1)
60
+
61
+ # tracks pattern for subtitles
62
+ tracks_match = re.search(r'tracks:\[([^\]]+)', i_source)
63
+ if tracks_match:
64
+ try:
65
+ tracks_str = f"[{tracks_match.group(1)}]"
66
+ tracks = json.loads(tracks_str)
67
+ for track in tracks:
68
+ file_url = track.get("file")
69
+ label = track.get("label", "")
70
+ if file_url and "Forced" not in label:
71
+ subtitles.append(Subtitle(
72
+ name = label,
73
+ url = self.fix_url(file_url)
74
+ ))
75
+ except Exception:
76
+ pass
77
+
78
+ if not m3u_link:
79
+ raise ValueError("m3u link not found")
80
+
81
+ return ExtractResult(
82
+ name = self.name,
83
+ url = m3u_link,
84
+ referer = url,
85
+ subtitles = subtitles
86
+ )
@@ -7,6 +7,12 @@ class Odnoklassniki(ExtractorBase):
7
7
  name = "Odnoklassniki"
8
8
  main_url = "https://odnoklassniki.ru"
9
9
 
10
+ # Birden fazla domain destekle
11
+ supported_domains = ["odnoklassniki.ru", "ok.ru"]
12
+
13
+ def can_handle_url(self, url: str) -> bool:
14
+ return any(domain in url for domain in self.supported_domains)
15
+
10
16
  async def extract(self, url, referer=None) -> ExtractResult:
11
17
  if "/video/" in url:
12
18
  url = url.replace("/video/", "/videoembed/")
@@ -7,6 +7,12 @@ class PeaceMakerst(ExtractorBase):
7
7
  name = "PeaceMakerst"
8
8
  main_url = "https://peacemakerst.com"
9
9
 
10
+ # Birden fazla domain destekle
11
+ supported_domains = ["peacemakerst.com", "hdstreamable.com"]
12
+
13
+ def can_handle_url(self, url: str) -> bool:
14
+ return any(domain in url for domain in self.supported_domains)
15
+
10
16
  async def extract(self, url, referer=None) -> ExtractResult:
11
17
  if referer:
12
18
  self.httpx.headers.update({"Referer": referer})
@@ -8,6 +8,12 @@ class RapidVid(ExtractorBase):
8
8
  name = "RapidVid"
9
9
  main_url = "https://rapidvid.net"
10
10
 
11
+ # Birden fazla domain destekle
12
+ supported_domains = ["rapidvid.net", "rapid.filmmakinesi.to"]
13
+
14
+ def can_handle_url(self, url: str) -> bool:
15
+ return any(domain in url for domain in self.supported_domains)
16
+
11
17
  async def extract(self, url, referer=None) -> ExtractResult:
12
18
  if referer:
13
19
  self.httpx.headers.update({"Referer": referer})
@@ -5,7 +5,13 @@ import re
5
5
 
6
6
  class SetPlay(ExtractorBase):
7
7
  name = "SetPlay"
8
- main_url = "https://setplay.cfd"
8
+ main_url = "https://setplay.shop"
9
+
10
+ # Birden fazla domain destekle
11
+ supported_domains = ["setplay.cfd", "setplay.shop", "setplay.site"]
12
+
13
+ def can_handle_url(self, url: str) -> bool:
14
+ return any(domain in url for domain in self.supported_domains)
9
15
 
10
16
  async def extract(self, url, referer=None) -> ExtractResult:
11
17
  ext_ref = referer or ""
@@ -0,0 +1,41 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult
4
+ from urllib.parse import urlparse, parse_qs
5
+
6
+ class VCTPlay(ExtractorBase):
7
+ name = "VCTPlay"
8
+ main_url = "https://vctplay.site"
9
+
10
+ async def extract(self, url, referer=None) -> ExtractResult:
11
+ if referer:
12
+ self.httpx.headers.update({"Referer": referer})
13
+
14
+ # URL'den video ID'sini çıkar
15
+ # https://vctplay.site/video/2hjDGco5exdv -> 2hjDGco5exdv
16
+ video_id = url.split("/")[-1]
17
+ if "?" in video_id:
18
+ video_id = video_id.split("?")[0]
19
+
20
+ # Manifests URL oluştur
21
+ master_url = f"{self.main_url}/manifests/{video_id}/master.txt"
22
+
23
+ # partKey'den isim belirle
24
+ parsed = urlparse(url)
25
+ params = parse_qs(parsed.query)
26
+ part_key = params.get("partKey", [""])[0]
27
+
28
+ name_suffix = ""
29
+ if "turkcedublaj" in part_key.lower():
30
+ name_suffix = "Dublaj"
31
+ elif "turkcealtyazi" in part_key.lower():
32
+ name_suffix = "Altyazı"
33
+
34
+ display_name = f"{self.name} - {name_suffix}" if name_suffix else self.name
35
+
36
+ return ExtractResult(
37
+ name = display_name,
38
+ url = master_url,
39
+ referer = f"{self.main_url}/",
40
+ subtitles = []
41
+ )
@@ -9,6 +9,12 @@ class VidMoly(ExtractorBase):
9
9
  name = "VidMoly"
10
10
  main_url = "https://vidmoly.to"
11
11
 
12
+ # Birden fazla domain destekle
13
+ supported_domains = ["vidmoly.to", "vidmoly.me", "vidmoly.net"]
14
+
15
+ def can_handle_url(self, url: str) -> bool:
16
+ return any(domain in url for domain in self.supported_domains)
17
+
12
18
  async def extract(self, url: str, referer: str = None) -> ExtractResult:
13
19
  if referer:
14
20
  self.httpx.headers.update({"Referer": referer})
@@ -0,0 +1,204 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
4
+ from parsel import Selector
5
+ import re
6
+
7
+ class BelgeselX(PluginBase):
8
+ name = "BelgeselX"
9
+ language = "tr"
10
+ main_url = "https://belgeselx.com"
11
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
+ description = "2022 yılında son çıkan belgeselleri belgeselx.com'da izle. En yeni belgeseller, türkçe altyazılı yada dublaj olarak 1080p kalitesinde hd belgesel izle."
13
+
14
+ main_page = {
15
+ f"{main_url}/konu/turk-tarihi-belgeselleri&page=" : "Türk Tarihi",
16
+ f"{main_url}/konu/tarih-belgeselleri&page=" : "Tarih",
17
+ f"{main_url}/konu/seyehat-belgeselleri&page=" : "Seyahat",
18
+ f"{main_url}/konu/seri-belgeseller&page=" : "Seri",
19
+ f"{main_url}/konu/savas-belgeselleri&page=" : "Savaş",
20
+ f"{main_url}/konu/sanat-belgeselleri&page=" : "Sanat",
21
+ f"{main_url}/konu/psikoloji-belgeselleri&page=" : "Psikoloji",
22
+ f"{main_url}/konu/polisiye-belgeselleri&page=" : "Polisiye",
23
+ f"{main_url}/konu/otomobil-belgeselleri&page=" : "Otomobil",
24
+ f"{main_url}/konu/nazi-belgeselleri&page=" : "Nazi",
25
+ f"{main_url}/konu/muhendislik-belgeselleri&page=" : "Mühendislik",
26
+ f"{main_url}/konu/kultur-din-belgeselleri&page=" : "Kültür Din",
27
+ f"{main_url}/konu/kozmik-belgeseller&page=" : "Kozmik",
28
+ f"{main_url}/konu/hayvan-belgeselleri&page=" : "Hayvan",
29
+ f"{main_url}/konu/eski-tarih-belgeselleri&page=" : "Eski Tarih",
30
+ f"{main_url}/konu/egitim-belgeselleri&page=" : "Eğitim",
31
+ f"{main_url}/konu/dunya-belgeselleri&page=" : "Dünya",
32
+ f"{main_url}/konu/doga-belgeselleri&page=" : "Doğa",
33
+ f"{main_url}/konu/bilim-belgeselleri&page=" : "Bilim"
34
+ }
35
+
36
+ @staticmethod
37
+ def _to_title_case(text: str) -> str:
38
+ """Türkçe için title case dönüşümü."""
39
+ return " ".join(
40
+ word.lower().replace("i", "İ").capitalize() if word.lower().startswith("i") else word.capitalize()
41
+ for word in text.split()
42
+ )
43
+
44
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
45
+ istek = self.cloudscraper.get(f"{url}{page}")
46
+ secici = Selector(istek.text)
47
+
48
+ results = []
49
+ for item in secici.css("div.gen-movie-contain > div.gen-info-contain > div.gen-movie-info"):
50
+ title = item.css("div.gen-movie-info > h3 a::text").get()
51
+ href = item.css("div.gen-movie-info > h3 a::attr(href)").get()
52
+ parent = item.xpath("../../..")
53
+ poster = parent.css("div.gen-movie-img > img::attr(src)").get()
54
+
55
+ if title and href:
56
+ results.append(MainPageResult(
57
+ category = category,
58
+ title = self._to_title_case(title.strip()),
59
+ url = self.fix_url(href),
60
+ poster = self.fix_url(poster) if poster else None
61
+ ))
62
+
63
+ return results
64
+
65
+ async def search(self, query: str) -> list[SearchResult]:
66
+ # Google Custom Search API kullanıyor
67
+ cx = "016376594590146270301:iwmy65ijgrm"
68
+
69
+ token_resp = self.cloudscraper.get(f"https://cse.google.com/cse.js?cx={cx}")
70
+ token_text = token_resp.text
71
+
72
+ cse_lib_match = re.search(r'cselibVersion": "(.*)"', token_text)
73
+ cse_tok_match = re.search(r'cse_token": "(.*)"', token_text)
74
+
75
+ if not cse_lib_match or not cse_tok_match:
76
+ return []
77
+
78
+ cse_lib = cse_lib_match.group(1)
79
+ cse_tok = cse_tok_match.group(1)
80
+
81
+ search_url = (
82
+ f"https://cse.google.com/cse/element/v1?"
83
+ f"rsz=filtered_cse&num=100&hl=tr&source=gcsc&cselibv={cse_lib}&cx={cx}"
84
+ f"&q={query}&safe=off&cse_tok={cse_tok}&sort=&exp=cc%2Capo&oq={query}"
85
+ f"&callback=google.search.cse.api9969&rurl=https%3A%2F%2Fbelgeselx.com%2F"
86
+ )
87
+
88
+ resp = self.cloudscraper.get(search_url)
89
+ resp_text = resp.text
90
+
91
+ titles = re.findall(r'"titleNoFormatting": "(.*?)"', resp_text)
92
+ urls = re.findall(r'"url": "(.*?)"', resp_text)
93
+ images = re.findall(r'"ogImage": "(.*?)"', resp_text)
94
+
95
+ results = []
96
+ for i, title in enumerate(titles):
97
+ url_val = urls[i] if i < len(urls) else None
98
+ poster = images[i] if i < len(images) else None
99
+
100
+ if not url_val or "diziresimleri" not in url_val:
101
+ # URL'den belgesel linkini oluştur
102
+ if poster and "diziresimleri" in poster:
103
+ file_name = poster.rsplit("/", 1)[-1]
104
+ file_name = re.sub(r"\.(jpe?g|png|webp)$", "", file_name)
105
+ url_val = f"{self.main_url}/belgeseldizi/{file_name}"
106
+ else:
107
+ continue
108
+
109
+ clean_title = title.split("İzle")[0].strip()
110
+ results.append(SearchResult(
111
+ title = self._to_title_case(clean_title),
112
+ url = url_val,
113
+ poster = poster
114
+ ))
115
+
116
+ return results
117
+
118
+ async def load_item(self, url: str) -> SeriesInfo:
119
+ istek = await self.httpx.get(url)
120
+ secici = Selector(istek.text)
121
+
122
+ title = secici.css("h2.gen-title::text").get()
123
+ poster = secici.css("div.gen-tv-show-top img::attr(src)").get()
124
+ description = secici.css("div.gen-single-tv-show-info p::text").get()
125
+
126
+ tags = []
127
+ for tag_link in secici.css("div.gen-socail-share a[href*='belgeselkanali']"):
128
+ tag_href = tag_link.css("::attr(href)").get()
129
+ if tag_href:
130
+ tag_name = tag_href.rsplit("/", 1)[-1].replace("-", " ")
131
+ tags.append(self._to_title_case(tag_name))
132
+
133
+ episodes = []
134
+ counter = 0
135
+ for ep_item in secici.css("div.gen-movie-contain"):
136
+ ep_name = ep_item.css("div.gen-movie-info h3 a::text").get()
137
+ ep_href = ep_item.css("div.gen-movie-info h3 a::attr(href)").get()
138
+
139
+ if not ep_name or not ep_href:
140
+ continue
141
+
142
+ season_text = ep_item.css("div.gen-single-meta-holder ul li::text").get() or ""
143
+ episode_match = re.search(r"Bölüm (\d+)", season_text)
144
+ season_match = re.search(r"Sezon (\d+)", season_text)
145
+
146
+ ep_episode = int(episode_match.group(1)) if episode_match else counter
147
+ ep_season = int(season_match.group(1)) if season_match else 1
148
+
149
+ counter += 1
150
+
151
+ episodes.append(Episode(
152
+ season = ep_season,
153
+ episode = ep_episode,
154
+ title = ep_name.strip(),
155
+ url = self.fix_url(ep_href)
156
+ ))
157
+
158
+ return SeriesInfo(
159
+ url = url,
160
+ poster = self.fix_url(poster) if poster else None,
161
+ title = self._to_title_case(title.strip()) if title else None,
162
+ description = description.strip() if description else None,
163
+ tags = tags,
164
+ episodes = episodes
165
+ )
166
+
167
+ async def load_links(self, url: str) -> list[dict]:
168
+ istek = await self.httpx.get(url)
169
+ text = istek.text
170
+
171
+ # fnc_addWatch div'inden data-episode ID'sini al
172
+ ep_match = re.search(r'<div[^>]*class=["\'][^"\']*fnc_addWatch[^"\']*["\'][^>]*data-episode=["\'](\d+)["\']', text)
173
+ if not ep_match:
174
+ return []
175
+
176
+ episode_id = ep_match.group(1)
177
+ iframe_url = f"{self.main_url}/video/data/new4.php?id={episode_id}"
178
+
179
+ iframe_resp = await self.httpx.get(iframe_url, headers={"Referer": url})
180
+ iframe_text = iframe_resp.text
181
+
182
+ links = []
183
+ for match in re.finditer(r'file:"([^"]+)", label: "([^"]+)"', iframe_text):
184
+ video_url = match.group(1)
185
+ quality = match.group(2)
186
+
187
+ source_name = "Google" if quality == "FULL" else self.name
188
+ quality_str = "1080p" if quality == "FULL" else quality
189
+
190
+ links.append({
191
+ "url" : video_url,
192
+ "name" : f"{source_name} | {quality_str}",
193
+ "referer" : url
194
+ })
195
+
196
+ return links
197
+
198
+ async def play(self, **kwargs):
199
+ extract_result = ExtractResult(**kwargs)
200
+ self.media_handler.title = kwargs.get("name")
201
+ if self.name not in self.media_handler.title:
202
+ self.media_handler.title = f"{self.name} | {self.media_handler.title}"
203
+
204
+ self.media_handler.play_media(extract_result)
@@ -7,7 +7,7 @@ import re
7
7
  class RecTV(PluginBase):
8
8
  name = "RecTV"
9
9
  language = "tr"
10
- main_url = "https://m.prectv50.sbs"
10
+ main_url = "https://m.prectv60.lol"
11
11
  favicon = "https://rectvapk.cc/wp-content/uploads/2023/02/Rec-TV.webp"
12
12
  description = "RecTv APK, Türkiye’deki en popüler Çevrimiçi Medya Akış platformlarından biridir. Filmlerin, Canlı Sporların, Web Dizilerinin ve çok daha fazlasının keyfini ücretsiz çıkarın."
13
13
 
@@ -0,0 +1,243 @@
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
4
+ from parsel import Selector
5
+ import re, json
6
+
7
+ class SetFilmIzle(PluginBase):
8
+ name = "SetFilmIzle"
9
+ language = "tr"
10
+ main_url = "https://www.setfilmizle.uk"
11
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
+ description = "Setfilmizle sitemizde, donma yaşamadan Türkçe dublaj ve altyazılı filmleri ile dizileri muhteşem 1080p full HD kalitesinde izleyebilirsiniz."
13
+
14
+ main_page = {
15
+ f"{main_url}/tur/aile/" : "Aile",
16
+ f"{main_url}/tur/aksiyon/" : "Aksiyon",
17
+ f"{main_url}/tur/animasyon/" : "Animasyon",
18
+ f"{main_url}/tur/belgesel/" : "Belgesel",
19
+ f"{main_url}/tur/bilim-kurgu/" : "Bilim-Kurgu",
20
+ f"{main_url}/tur/biyografi/" : "Biyografi",
21
+ f"{main_url}/tur/dini/" : "Dini",
22
+ f"{main_url}/tur/dram/" : "Dram",
23
+ f"{main_url}/tur/fantastik/" : "Fantastik",
24
+ f"{main_url}/tur/genclik/" : "Gençlik",
25
+ f"{main_url}/tur/gerilim/" : "Gerilim",
26
+ f"{main_url}/tur/gizem/" : "Gizem",
27
+ f"{main_url}/tur/komedi/" : "Komedi",
28
+ f"{main_url}/tur/korku/" : "Korku",
29
+ f"{main_url}/tur/macera/" : "Macera",
30
+ f"{main_url}/tur/romantik/" : "Romantik",
31
+ f"{main_url}/tur/savas/" : "Savaş",
32
+ f"{main_url}/tur/suc/" : "Suç",
33
+ f"{main_url}/tur/tarih/" : "Tarih",
34
+ f"{main_url}/tur/western/" : "Western"
35
+ }
36
+
37
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
38
+ istek = self.cloudscraper.get(url)
39
+ secici = Selector(istek.text)
40
+
41
+ results = []
42
+ for item in secici.css("div.items article"):
43
+ title = item.css("h2::text").get()
44
+ href = item.css("a::attr(href)").get()
45
+ poster = item.css("img::attr(data-src)").get()
46
+
47
+ if title and href:
48
+ results.append(MainPageResult(
49
+ category = category,
50
+ title = title.strip(),
51
+ url = self.fix_url(href),
52
+ poster = self.fix_url(poster) if poster else None
53
+ ))
54
+
55
+ return results
56
+
57
+ async def search(self, query: str) -> list[SearchResult]:
58
+ # Ana sayfadan nonce değerini al
59
+ main_resp = self.cloudscraper.get(self.main_url)
60
+
61
+ # Birden fazla nonce pattern dene
62
+ nonce = ""
63
+ nonce_patterns = [
64
+ r'nonces:\s*\{\s*search:\s*"([^"]+)"', # STMOVIE_AJAX.nonces.search
65
+ r'"search":\s*"([a-zA-Z0-9]+)"', # JSON format
66
+ r"nonce:\s*'([^']*)'",
67
+ r'"nonce":"([^"]+)"',
68
+ r'data-nonce="([^"]+)"',
69
+ ]
70
+ for pattern in nonce_patterns:
71
+ match = re.search(pattern, main_resp.text)
72
+ if match:
73
+ nonce = match.group(1)
74
+ break
75
+
76
+ search_resp = self.cloudscraper.post(
77
+ f"{self.main_url}/wp-admin/admin-ajax.php",
78
+ headers = {
79
+ "X-Requested-With" : "XMLHttpRequest",
80
+ "Content-Type" : "application/x-www-form-urlencoded",
81
+ "Referer" : f"{self.main_url}/"
82
+ },
83
+ data = {
84
+ "action" : "ajax_search",
85
+ "search" : query,
86
+ "original_search" : query,
87
+ "nonce" : nonce
88
+ }
89
+ )
90
+
91
+ try:
92
+ data = search_resp.json()
93
+ html = data.get("html", "")
94
+ except:
95
+ return []
96
+
97
+ secici = Selector(text=html)
98
+ results = []
99
+
100
+ for item in secici.css("div.items article"):
101
+ title = item.css("h2::text").get()
102
+ href = item.css("a::attr(href)").get()
103
+ poster = item.css("img::attr(data-src)").get()
104
+
105
+ if title and href:
106
+ results.append(SearchResult(
107
+ title = title.strip(),
108
+ url = self.fix_url(href),
109
+ poster = self.fix_url(poster) if poster else None
110
+ ))
111
+
112
+ return results
113
+
114
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
115
+ istek = await self.httpx.get(url)
116
+ secici = Selector(istek.text)
117
+
118
+ raw_title = secici.css("h1::text").get() or ""
119
+ title = re.sub(r"\s*izle.*$", "", raw_title, flags=re.IGNORECASE).strip()
120
+ poster = secici.css("div.poster img::attr(src)").get()
121
+ description = secici.css("div.wp-content p::text").get()
122
+ year = secici.css("div.extra span.C a::text").get()
123
+ if year:
124
+ year_match = re.search(r"\d{4}", year)
125
+ year = year_match.group() if year_match else None
126
+ tags = [a.css("::text").get().strip() for a in secici.css("div.sgeneros a") if a.css("::text").get()]
127
+ duration = secici.css("span.runtime::text").get()
128
+ if duration:
129
+ dur_match = re.search(r"\d+", duration)
130
+ duration = int(dur_match.group()) if dur_match else None
131
+
132
+ actors = [span.css("::text").get().strip() for span in secici.css("span.valor a > span") if span.css("::text").get()]
133
+
134
+ trailer_match = re.search(r'embed/([^?]*)\?rel', istek.text)
135
+ trailer = f"https://www.youtube.com/embed/{trailer_match.group(1)}" if trailer_match else None
136
+
137
+ # Dizi mi film mi kontrol et
138
+ is_series = "/dizi/" in url
139
+
140
+ if is_series:
141
+ year_elem = secici.css("a[href*='/yil/']::text").get()
142
+ if year_elem:
143
+ year_match = re.search(r"\d{4}", year_elem)
144
+ year = year_match.group() if year_match else year
145
+
146
+ dur_elem = secici.css("div#info span:contains('Dakika')::text").get()
147
+ if dur_elem:
148
+ dur_match = re.search(r"\d+", dur_elem)
149
+ duration = int(dur_match.group()) if dur_match else duration
150
+
151
+ episodes = []
152
+ for ep_item in secici.css("div#episodes ul.episodios li"):
153
+ ep_href = ep_item.css("h4.episodiotitle a::attr(href)").get()
154
+ ep_name = ep_item.css("h4.episodiotitle a::text").get()
155
+
156
+ if not ep_href or not ep_name:
157
+ continue
158
+
159
+ ep_detail = ep_name.strip()
160
+ season_match = re.search(r"(\d+)\.\s*Sezon", ep_detail)
161
+ episode_match = re.search(r"Sezon\s+(\d+)\.\s*Bölüm", ep_detail)
162
+
163
+ ep_season = int(season_match.group(1)) if season_match else 1
164
+ ep_episode = int(episode_match.group(1)) if episode_match else None
165
+
166
+ episodes.append(Episode(
167
+ season = ep_season,
168
+ episode = ep_episode,
169
+ title = ep_name.strip(),
170
+ url = self.fix_url(ep_href)
171
+ ))
172
+
173
+ return SeriesInfo(
174
+ url = url,
175
+ poster = self.fix_url(poster) if poster else None,
176
+ title = title,
177
+ description = description.strip() if description else None,
178
+ tags = tags,
179
+ year = year,
180
+ duration = duration,
181
+ actors = actors,
182
+ episodes = episodes
183
+ )
184
+
185
+ return MovieInfo(
186
+ url = url,
187
+ poster = self.fix_url(poster) if poster else None,
188
+ title = title,
189
+ description = description.strip() if description else None,
190
+ tags = tags,
191
+ year = year,
192
+ duration = duration,
193
+ actors = actors
194
+ )
195
+
196
+ async def load_links(self, url: str) -> list[dict]:
197
+ istek = await self.httpx.get(url)
198
+ secici = Selector(istek.text)
199
+
200
+ nonce = secici.css("div#playex::attr(data-nonce)").get() or ""
201
+
202
+ links = []
203
+ for player in secici.css("nav.player a"):
204
+ source_id = player.css("::attr(data-post-id)").get()
205
+ player_name = player.css("::attr(data-player-name)").get()
206
+ part_key = player.css("::attr(data-part-key)").get()
207
+
208
+ if not source_id or "event" in source_id or source_id == "":
209
+ continue
210
+
211
+ # Multipart form request
212
+ try:
213
+ resp = self.cloudscraper.post(
214
+ f"{self.main_url}/wp-admin/admin-ajax.php",
215
+ headers = {"Referer": url},
216
+ data = {
217
+ "action" : "get_video_url",
218
+ "nonce" : nonce,
219
+ "post_id" : source_id,
220
+ "player_name" : player_name or "",
221
+ "part_key" : part_key or ""
222
+ }
223
+ )
224
+ data = resp.json()
225
+ except:
226
+ continue
227
+
228
+ iframe_url = data.get("data", {}).get("url")
229
+ if not iframe_url:
230
+ continue
231
+
232
+ # SetPlay URL'si için part_key ekleme
233
+ if "setplay" not in iframe_url and part_key:
234
+ iframe_url = f"{iframe_url}?partKey={part_key}"
235
+
236
+ extractor = self.ex_manager.find_extractor(iframe_url)
237
+ links.append({
238
+ "url" : iframe_url,
239
+ "name" : extractor.name if extractor else player_name or "Direct Link",
240
+ "referer" : self.main_url
241
+ })
242
+
243
+ return links
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: KekikStream
3
- Version: 2.0.4
3
+ Version: 2.0.5
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
@@ -17,8 +17,8 @@ KekikStream/Core/Plugin/PluginManager.py,sha256=CZVg1eegi8vfMfccx0DRV0Box8kXz-ao
17
17
  KekikStream/Core/Plugin/PluginModels.py,sha256=Yvx-6Fkn8QCIcuqAkFbCP5EJcq3XBkK_P8S0tRNhS6E,2476
18
18
  KekikStream/Core/UI/UIManager.py,sha256=T4V_kdTTWa-UDamgLSKa__dWJuzcvRK9NuwBlzU9Bzc,1693
19
19
  KekikStream/Extractors/CloseLoad.py,sha256=rc1lYy32ThB4ApC1hZdqVQ3cAVHvRGHOMdjomWeDgaQ,884
20
- KekikStream/Extractors/ContentX.py,sha256=u1sDdM79MNx9OdPTPcAA0OQDS7537IO8aJlffxhMi8c,2976
21
- KekikStream/Extractors/ContentX_.py,sha256=2JjSm6mNFNwvJA68HH-YYDg-m_Cx7nNx1SgGcqKZCUk,1405
20
+ KekikStream/Extractors/ContentX.py,sha256=TEXJppAZJLkf0x8jxaKnrCVAcabqdBFBswinpqjG_70,3513
21
+ KekikStream/Extractors/DonilasPlay.py,sha256=Lr60pEht96SMlXICYWo9J5dOwV4ty8fetBCCqJ3ARUY,3221
22
22
  KekikStream/Extractors/DzenRu.py,sha256=X0Rhm1-W4YjQwVrJs8YFqVcCxMaZi8rsKiLhK_ZsYlU,1185
23
23
  KekikStream/Extractors/ExPlay.py,sha256=EJNVKAbaIxlbOsCx7J9aLfNHKOFoqSLZZUw7W4QYeH0,1827
24
24
  KekikStream/Extractors/FirePlayer.py,sha256=HlJjFgii0fGZK7cgwpoZAIoajabl7IQX6ZrAQT1fBIw,2188
@@ -28,15 +28,12 @@ KekikStream/Extractors/MailRu.py,sha256=xQVCWwYqNoG5T43VAW1_m0v4e80FbO-1pNPKkwhT
28
28
  KekikStream/Extractors/MixPlayHD.py,sha256=POV_yq3KoZ6S6EqFsKYULEBz92NdUa2BpYKNo0eNQH8,1552
29
29
  KekikStream/Extractors/MixTiger.py,sha256=mMEYhhr5-u6GgIrLESeFTRdwDykgSXKJO4KtkMML1bw,2124
30
30
  KekikStream/Extractors/MolyStream.py,sha256=IeeBw9tJJrL5QQ-t2Yp-a-6lnDc3Y00UNiaN6m-o-7c,1160
31
- KekikStream/Extractors/Odnoklassniki.py,sha256=EQAuCdr69vN11ZBE5uThuXqlR8NVWanROWYkubo22mo,3809
32
- KekikStream/Extractors/Odnoklassniki_.py,sha256=AbcJ65Pq1zA2wQGXZNgNBToweOniFYR3jkWXFXJJpfg,316
33
- KekikStream/Extractors/PeaceMakerst.py,sha256=ZKk454eNZpeamht61UH9yMYe00_zGb3MSyujfDlbVDc,2096
34
- KekikStream/Extractors/PeaceMakerst_.py,sha256=-PdSR8yFy6MEc6EodXvHksj8O_j6Xu8yceoSjM2pGYg,246
31
+ KekikStream/Extractors/Odnoklassniki.py,sha256=YfFRCL3Ag5N4zDzK9ZLOr3HVQcsETFQpff1px02imJ0,4019
32
+ KekikStream/Extractors/PeaceMakerst.py,sha256=pEgJb3KDfEPAUjbuvrYbUlxIciKgED-Vd0arrRO3QCk,2317
35
33
  KekikStream/Extractors/PixelDrain.py,sha256=Uk2pPvtBzaKtRXu1iNO8FLHd0EAuIOIzI1H_n02tg-U,964
36
34
  KekikStream/Extractors/PlayerFilmIzle.py,sha256=vDumIya3gWN8h1to5XCzBWePa6gdNU2DVTK3OrPr04U,2425
37
- KekikStream/Extractors/RapidVid.py,sha256=TxNnLUmYaAHi-rFkoLu4eQULzkUSvQnfoEZAjyzQFns,2917
38
- KekikStream/Extractors/RapidVid_.py,sha256=siYK1oDCASzV8MhhDbGh5qlSDtZLi4EHNlO0HwdgeNU,233
39
- KekikStream/Extractors/SetPlay.py,sha256=FsbLlYFXp7_28-ta6XAoqDQAEGYTVZOZayebkY1mWe8,1906
35
+ KekikStream/Extractors/RapidVid.py,sha256=FgAZfgtnKE57KRiRIFw-6_ZES0Trp5QriNbRTfSBGQs,3139
36
+ KekikStream/Extractors/SetPlay.py,sha256=FjHYtC6xTeeikTkrW6jz0TpWz0kaoJ5qHhr_H7aThEY,2135
40
37
  KekikStream/Extractors/SetPrime.py,sha256=ob09y-Sm91YR7rIRzikhZiMHX6D4Djm5QzFTg6KbO4k,1536
41
38
  KekikStream/Extractors/SibNet.py,sha256=zJTkzlr34ufKCWzKKCgJrzhb2o-fpjTjFdi38gv6N6g,849
42
39
  KekikStream/Extractors/Sobreatsesuyp.py,sha256=qlSQHUHjTjBoY0nsuZQWAjnfswbPORkBg6rUuP7SagA,2000
@@ -44,14 +41,15 @@ KekikStream/Extractors/TRsTX.py,sha256=mbSRGnQt26a73SbqwtY9rpiYFwgRgVbvA6bkGb_Pv
44
41
  KekikStream/Extractors/TauVideo.py,sha256=2ai9BwwM6qlCgxK7E0B642LtOF5y4hEb9tQ2aDpbMtc,1112
45
42
  KekikStream/Extractors/TurboImgz.py,sha256=nnWnP1K4JZbMj6S-YuXxej31UZtF4JcboSW4n7A4A5c,824
46
43
  KekikStream/Extractors/TurkeyPlayer.py,sha256=FX_H3vzXjAD7IjK11bjJVVw_VdPQ4n6YQLfjQ6E3t7o,1247
44
+ KekikStream/Extractors/VCTPlay.py,sha256=1BCl2_vVIrwvG56LCzl2KE5g2CUaMAhzImOZMdZpZCQ,1377
47
45
  KekikStream/Extractors/VidHide.py,sha256=TVoS9CMV1WSE83vPC4FFxsNE71n-_DtVJm66_1Wt8pU,2538
48
- KekikStream/Extractors/VidMoly.py,sha256=bxzIymYHrms4gBdK2jfsoug2tjq-PuicOxeR2NjcAjw,3654
49
- KekikStream/Extractors/VidMoly_.py,sha256=737BzqogzknVhSebZgVDh70gsEgNo9s2mafySrDzr6o,219
46
+ KekikStream/Extractors/VidMoly.py,sha256=zuo3LqaDJ0Qs6H6l9z5TjosTSpsQ37KEBXQpGVO9AsI,3878
50
47
  KekikStream/Extractors/VidMoxy.py,sha256=LT7wTKBtuuagXwfGjWZwQF2NQGuChurZJ-I6gM0Jcek,1771
51
48
  KekikStream/Extractors/VidPapi.py,sha256=g9ohdL9VJrxy4N7xerbIRz3ZxjsXFHlJWy0NaZ31hFY,3259
52
49
  KekikStream/Extractors/VideoSeyred.py,sha256=M6QPZ_isX9vM_7LPo-2I_8Cf1vB9awHw8vvzBODtoiQ,1977
53
50
  KekikStream/Extractors/YTDLP.py,sha256=KKvvv6XiKM57NON2Vpw24O31xUKpgcovHTPYh05QHW8,6543
54
51
  KekikStream/Extractors/YildizKisaFilm.py,sha256=R_JlrOVeMiDlXYcuTdItnKvidyx8_u3B14fSrxew2aE,1316
52
+ KekikStream/Plugins/BelgeselX.py,sha256=WdCeU_Zvsph0kHt7jAWaZ3DQ_2rxaFChmhGKPcHLJpo,8728
55
53
  KekikStream/Plugins/DiziBox.py,sha256=sxM7ckKeKwMrMkRNUAvh5wE9wdOuVda6Ag_zAdwSvi8,9935
56
54
  KekikStream/Plugins/DiziPal.py,sha256=MBONjermWBm3sN-8ZSILnfXI2F_V2kH65gpTNOuL9dI,10198
57
55
  KekikStream/Plugins/DiziYou.py,sha256=xE0INtCLOZDo73sWQoYT6Su8T66hGK9rBD-gXnk8MiU,9016
@@ -64,9 +62,10 @@ KekikStream/Plugins/FullHDFilmizlesene.py,sha256=8ozxyuJnivFe1Cu_Fb8HjVPH-ptKCSW
64
62
  KekikStream/Plugins/HDFilmCehennemi.py,sha256=-PV2u4ep6TvpuycaivkmaWie60k99T-rNtqfg5n7k5M,9584
65
63
  KekikStream/Plugins/JetFilmizle.py,sha256=Jm2cYEgV_bRXVjrDtnLTelvyxGHVAUCZkKmP3lUkEA8,6631
66
64
  KekikStream/Plugins/KultFilmler.py,sha256=fABm74yQhEUsvuWRSEK0jiy0VJflxtFZ5p65UDC_NP0,9103
67
- KekikStream/Plugins/RecTV.py,sha256=hsOmH4xharsscSnJMGZfvAxSJ37akfUPl1RlO0te1jA,7544
65
+ KekikStream/Plugins/RecTV.py,sha256=dF3Ogf7KE_zpfLQRjVcEJQMuWtBtHo6iB73_ECQEJ58,7544
68
66
  KekikStream/Plugins/RoketDizi.py,sha256=PY9Cf6X0I21FdMj0K48WwvwRPtznoqI3mMsctXAeLHs,8672
69
67
  KekikStream/Plugins/SelcukFlix.py,sha256=rajt0BbdiJDFvihD5HmjwizNdgmOXb3BN8c4Ju7aRkc,12533
68
+ KekikStream/Plugins/SetFilmIzle.py,sha256=deHLi0PvOUOz6L7wru-t8jWCHrYJ01qfWMLAyGAzLcg,9727
70
69
  KekikStream/Plugins/SezonlukDizi.py,sha256=vW-Mvk63Y9sAJmPnY1IdFwBzCMEB2tF03zwHVETZTtI,7262
71
70
  KekikStream/Plugins/SineWix.py,sha256=xdTeg8GHUpyZsY6EQ5I1ZIT4-lB_qwBwvHQVmdMPpEI,7364
72
71
  KekikStream/Plugins/Sinefy.py,sha256=riB2lgSSG5MkgxnVtX1OlApBcHG6vVjfiHyJ6h8_6DM,9965
@@ -74,9 +73,9 @@ KekikStream/Plugins/SinemaCX.py,sha256=dIJUOOtWSyMx7vGOE1NjrsCeW1n5DHh40--KSiW0k
74
73
  KekikStream/Plugins/Sinezy.py,sha256=gdszlee5QpUka0qMzGMbBoXwJCtZbe5hlA5o9FJQI1o,6226
75
74
  KekikStream/Plugins/SuperFilmGeldi.py,sha256=4kgdWpYCLBSAn2XfL1usFG33LsTtDvo28fmDecwNA_U,5480
76
75
  KekikStream/Plugins/UgurFilm.py,sha256=sQatQ2zb9NER8J52DRLI5K9EnYFv4I1ZgZ22HtauX3Q,4813
77
- kekikstream-2.0.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
78
- kekikstream-2.0.4.dist-info/METADATA,sha256=irIRhTOfz2XbPRVYyIlgpxioS8Dq1urrnsLFyLcBsSQ,10090
79
- kekikstream-2.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
80
- kekikstream-2.0.4.dist-info/entry_points.txt,sha256=dFwdiTx8djyehI0Gsz-rZwjAfZzUzoBSrmzRu9ubjJc,50
81
- kekikstream-2.0.4.dist-info/top_level.txt,sha256=DNmGJDXl27Drdfobrak8KYLmocW_uznVYFJOzcjUgmY,12
82
- kekikstream-2.0.4.dist-info/RECORD,,
76
+ kekikstream-2.0.5.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
77
+ kekikstream-2.0.5.dist-info/METADATA,sha256=ZjBv64LipReZl9qjFkDcELL93TtS5hOVlaqkm1iGG2I,10090
78
+ kekikstream-2.0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
79
+ kekikstream-2.0.5.dist-info/entry_points.txt,sha256=dFwdiTx8djyehI0Gsz-rZwjAfZzUzoBSrmzRu9ubjJc,50
80
+ kekikstream-2.0.5.dist-info/top_level.txt,sha256=DNmGJDXl27Drdfobrak8KYLmocW_uznVYFJOzcjUgmY,12
81
+ kekikstream-2.0.5.dist-info/RECORD,,
@@ -1,55 +0,0 @@
1
- # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
-
3
- from KekikStream.Extractors.ContentX import ContentX
4
-
5
- class SNDPlayer(ContentX):
6
- name = "SNDPlayer"
7
- main_url = "https://sn.dplayer82.site"
8
-
9
- class FourDPlayer(ContentX):
10
- name = "FourDPlayer"
11
- main_url = "https://four.dplayer82.site"
12
-
13
- class ORGDPlayer(ContentX):
14
- name = "ORGDPlayer"
15
- main_url = "https://org.dplayer82.site"
16
-
17
- class SNDPlayer74(ContentX):
18
- name = "SNDPlayer74"
19
- main_url = "https://sn.dplayer74.site"
20
-
21
- class Hotlinger(ContentX):
22
- name = "Hotlinger"
23
- main_url = "https://hotlinger.com"
24
-
25
- class SNHotlinger(ContentX):
26
- name = "SNHotlinger"
27
- main_url = "https://sn.hotlinger.com"
28
-
29
- class PlayRu(ContentX):
30
- name = "PlayRu"
31
- main_url = "https://playru.net"
32
-
33
- class FourPlayRu(ContentX):
34
- name = "FourPlayRu"
35
- main_url = "https://four.playru.net"
36
-
37
- class Pichive(ContentX):
38
- name = "Pichive"
39
- main_url = "https://pichive.online"
40
-
41
- class PichiveMe(ContentX):
42
- name = "PichiveMe"
43
- main_url = "https://pichive.me"
44
-
45
- class FourPichiveOnline(ContentX):
46
- name = "FourPichiveOnline"
47
- main_url = "https://four.pichive.online"
48
-
49
- class FourPichiveMe(ContentX):
50
- name = "FourPichive"
51
- main_url = "https://four.pichive.me"
52
-
53
- class FourCX(ContentX):
54
- name = "FourCX"
55
- main_url = "https://four.contentx.me"
@@ -1,11 +0,0 @@
1
- # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
-
3
- from KekikStream.Extractors.Odnoklassniki import Odnoklassniki
4
-
5
- class OkRuHTTP(Odnoklassniki):
6
- name = "OkRuHTTP"
7
- main_url = "http://ok.ru"
8
-
9
- class OkRuSSL(Odnoklassniki):
10
- name = "OkRuSSL"
11
- main_url = "https://ok.ru"
@@ -1,7 +0,0 @@
1
- # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
-
3
- from KekikStream.Extractors.PeaceMakerst import PeaceMakerst
4
-
5
- class HDStreamAble(PeaceMakerst):
6
- name = "HDStreamAble"
7
- main_url = "https://hdstreamable.com"
@@ -1,7 +0,0 @@
1
- # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
-
3
- from KekikStream.Extractors.RapidVid import RapidVid
4
-
5
- class RapidFilm(RapidVid):
6
- name = "RapidFilm"
7
- main_url = "https://rapid.filmmakinesi.to"
@@ -1,7 +0,0 @@
1
- # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
-
3
- from KekikStream.Extractors.VidMoly import VidMoly
4
-
5
- class VidMolyMe(VidMoly):
6
- name = "VidMolyMe"
7
- main_url = "https://vidmoly.me"