KekikStream 2.3.9__py3-none-any.whl → 2.4.1__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.

@@ -0,0 +1,205 @@
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
+ import re
5
+ from json import loads
6
+ from urllib.parse import unquote
7
+
8
+ class Filmatek(PluginBase):
9
+ name = "Filmatek"
10
+ language = "tr"
11
+ main_url = "https://filmatek.net"
12
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
13
+ description = "Sosyalizmin Sineması Veritabanı"
14
+
15
+ # Main page categories
16
+ main_page = {
17
+ f"{main_url}/tur/aile/page" : "Aile",
18
+ f"{main_url}/tur/aksiyon/page" : "Aksiyon",
19
+ f"{main_url}/tur/animasyon/page" : "Animasyon",
20
+ f"{main_url}/tur/bilim-kurgu/page" : "Bilim Kurgu",
21
+ f"{main_url}/tur/komedi/page" : "Komedi",
22
+ f"{main_url}/tur/korku/page" : "Korku",
23
+ f"{main_url}/tur/macera/page" : "Macera",
24
+ f"{main_url}/tur/romantik/page" : "Romantik",
25
+ f"{main_url}/tur/suc/page" : "Suç",
26
+ f"{main_url}/tur/yerli-filmler/page" : "Yerli Filmler",
27
+ f"{main_url}/film-arsivi/page" : "Tüm Filmler",
28
+ }
29
+
30
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
31
+ target_url = f"{url}/{page}/"
32
+ istek = await self.httpx.get(target_url)
33
+ helper = HTMLHelper(istek.text)
34
+
35
+ items = helper.select("div.items article, #archive-content article")
36
+ results = []
37
+
38
+ for item in items:
39
+ title_el = helper.select_first("div.data h3 a, h3 a", item)
40
+ if not title_el: continue
41
+
42
+ title = title_el.text(strip=True)
43
+ href = self.fix_url(title_el.attrs.get("href"))
44
+
45
+ img_el = helper.select_first("img", item)
46
+ poster = self.fix_url(img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
47
+
48
+ results.append(MainPageResult(
49
+ category = category,
50
+ title = title,
51
+ url = href,
52
+ poster = poster
53
+ ))
54
+
55
+ return results
56
+
57
+ async def search(self, query: str) -> list[SearchResult]:
58
+ url = f"{self.main_url}/?s={query}"
59
+ istek = await self.httpx.get(url)
60
+ helper = HTMLHelper(istek.text)
61
+
62
+ items = helper.select("div.result-item")
63
+ results = []
64
+
65
+ for item in items:
66
+ title_el = helper.select_first("div.title a", item)
67
+ if not title_el: continue
68
+
69
+ title = title_el.text(strip=True)
70
+ href = self.fix_url(title_el.attrs.get("href"))
71
+
72
+ img_el = helper.select_first("div.image img", item)
73
+ poster = self.fix_url(img_el.attrs.get("src")) if img_el else None
74
+
75
+ results.append(SearchResult(
76
+ title = title,
77
+ url = href,
78
+ poster = poster
79
+ ))
80
+
81
+ return results
82
+
83
+ async def load_item(self, url: str) -> MovieInfo:
84
+ istek = await self.httpx.get(url)
85
+ helper = HTMLHelper(istek.text)
86
+
87
+ title = helper.select_text("div.data h1, h1") or "Bilinmiyor"
88
+
89
+ poster_el = helper.select_first("div.poster img")
90
+ poster = self.fix_url(poster_el.attrs.get("src")) if poster_el else None
91
+ if not poster:
92
+ poster = helper.select_attr("meta[property='og:image']", "content")
93
+
94
+ description = helper.select_text("div.wp-content p")
95
+ if not description:
96
+ description = helper.select_attr("meta[property='og:description']", "content")
97
+
98
+ year_text = helper.select_text("span.date")
99
+ year = year_text.strip()[-4:] if year_text else None
100
+
101
+ # Rating extraction updated
102
+ rating = helper.select_text("span.dt_rating_vgs") or helper.select_text("span.dt_rating_vmanual")
103
+
104
+ # Duration extraction
105
+ duration = None
106
+ duration_text = helper.select_text("span.runtime")
107
+ if duration_text:
108
+ # "80 Dak." -> "80"
109
+ duration = duration_text.split()[0]
110
+
111
+ tags = helper.select_all_text("div.sgeneros a")
112
+
113
+ # Actors
114
+ actors_list = []
115
+ actor_els = helper.select("div.person")
116
+ for el in actor_els:
117
+ name = helper.select_text("div.name a", el)
118
+ if name:
119
+ actors_list.append(name.strip())
120
+ actors = ", ".join(actors_list) if actors_list else None
121
+
122
+ return MovieInfo(
123
+ url = url,
124
+ title = title,
125
+ description = description,
126
+ poster = poster,
127
+ year = year,
128
+ rating = rating,
129
+ duration = duration,
130
+ tags = tags,
131
+ actors = actors
132
+ )
133
+
134
+ async def load_links(self, url: str) -> list[ExtractResult]:
135
+ istek = await self.httpx.get(url)
136
+ html = istek.text
137
+ helper = HTMLHelper(html)
138
+
139
+ # Get Post ID from body class usually "postid-123"
140
+ body_class = helper.select_attr("body", "class") or ""
141
+ post_id_match = re.search(r"postid-(\d+)", body_class)
142
+
143
+ results = []
144
+
145
+ if post_id_match:
146
+ post_id = post_id_match.group(1)
147
+
148
+ # AJAX request for player
149
+ ajax_url = f"{self.main_url}/wp-admin/admin-ajax.php"
150
+ data = {
151
+ "action": "doo_player_ajax",
152
+ "post": post_id,
153
+ "nume": "1", # Usually implies source number 1? Kotlin uses "1" hardcoded.
154
+ "type": "movie"
155
+ }
156
+
157
+ headers = {
158
+ "X-Requested-With": "XMLHttpRequest",
159
+ "Referer": url,
160
+ "Content-Type": "application/x-www-form-urlencoded"
161
+ }
162
+
163
+ try:
164
+ # Need to use post with data
165
+ player_resp = await self.httpx.post(ajax_url, data=data, headers=headers)
166
+
167
+ # Kotlin parses it as text and cleans slashes
168
+ content = player_resp.text.replace(r"\/", "/")
169
+
170
+ # Regex for URL
171
+ # Kotlin: (?:src|url)["']?\s*[:=]\s*["']([^"']+)["']
172
+ src_match = re.search(r'(?:src|url)["\']?\s*[:=]\s*["\']([^"\']+)["\']', content)
173
+
174
+ if src_match:
175
+ iframe_url = src_match.group(1)
176
+ if iframe_url.startswith("/"):
177
+ iframe_url = self.main_url + iframe_url
178
+
179
+ iframe_url = self.fix_url(iframe_url)
180
+
181
+ # Unwrap internal JWPlayer
182
+ if "jwplayer/?source=" in iframe_url:
183
+ try:
184
+ raw_source = iframe_url.split("source=")[1].split("&")[0]
185
+ iframe_url = unquote(raw_source)
186
+ except:
187
+ pass
188
+
189
+ extracted = await self.extract(iframe_url)
190
+ if extracted:
191
+ if isinstance(extracted, list):
192
+ results.extend(extracted)
193
+ else:
194
+ results.append(extracted)
195
+ else:
196
+ results.append(ExtractResult(
197
+ name = "Filmatek | External",
198
+ url = iframe_url,
199
+ referer = url
200
+ ))
201
+ except Exception as e:
202
+ # print(f"Filmatek Error: {e}")
203
+ pass
204
+
205
+ return results
@@ -0,0 +1,274 @@
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
+ import re
5
+
6
+ class Full4kizle(PluginBase):
7
+ name = "Full4kizle"
8
+ language = "tr"
9
+ main_url = "https://izlehdfilm.cc"
10
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
11
+ description = "Filmci Baba, film izleme sitesi 4k Full film izle, 1080p ve 4k kalite de sinema filmleri ve dizileri, tek parça hd kalitede türkçe dublajlı filmler seyret."
12
+
13
+ main_page = {
14
+ f"{main_url}/Kategori/en-populer-filmler/page" : "En Popüler Filmler",
15
+ f"{main_url}/Kategori/tur/aksiyon-filmleri/page" : "Aksiyon",
16
+ f"{main_url}/Kategori/tur/macera-filmleri/page" : "Macera",
17
+ f"{main_url}/Kategori/tur/bilim-kurgu-filmleri/page" : "Bilim Kurgu",
18
+ f"{main_url}/Kategori/tur/fantastik-filmler/page" : "Fantastik",
19
+ f"{main_url}/Kategori/tur/korku-filmleri/page" : "Korku",
20
+ f"{main_url}/Kategori/tur/gerilim-filmleri-hd/page" : "Gerilim",
21
+ f"{main_url}/Kategori/tur/gizem-filmleri/page" : "Gizem",
22
+ f"{main_url}/Kategori/tur/dram-filmleri-hd/page" : "Dram",
23
+ f"{main_url}/Kategori/tur/komedi-filmleri-hd/page" : "Komedi",
24
+ f"{main_url}/Kategori/tur/romantik-filmler/page" : "Romantik",
25
+ f"{main_url}/Kategori/tur/aile-filmleri/page" : "Aile",
26
+ f"{main_url}/Kategori/tur/animasyon-filmleri/page" : "Animasyon",
27
+ f"{main_url}/Kategori/tur/biyografi-filmleri/page" : "Biyografi",
28
+ f"{main_url}/Kategori/tur/polisiye-suc-filmleri/page" : "Polisiye / Suç",
29
+ f"{main_url}/Kategori/tur/savas-filmleri/page" : "Savaş",
30
+ f"{main_url}/Kategori/tur/western-filmler/page" : "Western",
31
+ f"{main_url}/Kategori/tur/hint-filmleri/page" : "Hint Filmleri",
32
+ f"{main_url}/Kategori/tur/kore-filmleri/page" : "Kore Filmleri",
33
+ f"{main_url}/Kategori/tur/yerli-filmler-izle/page" : "Yerli Filmler",
34
+ f"{main_url}/Kategori/tur/yerli-diziler/page" : "Yerli Diziler",
35
+ f"{main_url}/Kategori/tur/18-erotik-filmler/page" : "+18 Erotik Filmler",
36
+ }
37
+
38
+
39
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
40
+ target_url = f"{url}/{page}/"
41
+ istek = await self.httpx.get(target_url)
42
+ helper = HTMLHelper(istek.text)
43
+
44
+ items = helper.select("div.movie-preview")
45
+ results = []
46
+
47
+ for item in items:
48
+ title_el = helper.select_first(".movie-title a", item)
49
+ if not title_el: continue
50
+
51
+ title = title_el.text(strip=True)
52
+ # Remove " izle" case insensitive
53
+ title = re.sub(r"(?i) izle", "", title).strip()
54
+
55
+ href = self.fix_url(title_el.attrs.get("href"))
56
+
57
+ poster_el = helper.select_first(".movie-poster img", item)
58
+ poster = self.fix_url(poster_el.attrs.get("src")) if poster_el else None
59
+
60
+ results.append(MainPageResult(
61
+ category = category,
62
+ title = title,
63
+ url = href,
64
+ poster = poster
65
+ ))
66
+
67
+ return results
68
+
69
+ async def search(self, query: str) -> list[SearchResult]:
70
+ url = f"{self.main_url}/?s={query}"
71
+ istek = await self.httpx.get(url)
72
+ helper = HTMLHelper(istek.text)
73
+
74
+ items = helper.select("div.movie-preview")
75
+ results = []
76
+
77
+ for item in items:
78
+ title_el = helper.select_first(".movie-title a", item)
79
+ if not title_el: continue
80
+
81
+ title = title_el.text(strip=True)
82
+ # Remove " izle" case insensitive
83
+ title = re.sub(r"(?i) izle", "", title).strip()
84
+
85
+ href = self.fix_url(title_el.attrs.get("href"))
86
+
87
+ poster_el = helper.select_first(".movie-poster img", item)
88
+ poster = self.fix_url(poster_el.attrs.get("src")) if poster_el else None
89
+
90
+ results.append(SearchResult(
91
+ title = title,
92
+ url = href,
93
+ poster = poster
94
+ ))
95
+
96
+ return results
97
+
98
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
99
+ istek = await self.httpx.get(url)
100
+ helper = HTMLHelper(istek.text)
101
+
102
+ title_raw = helper.select_text("h1") or "Bilinmiyor"
103
+ title = re.sub(r"(?i)izle", "", title_raw).strip()
104
+
105
+ poster_el = helper.select_first(".poster img")
106
+ poster = self.fix_url(poster_el.attrs.get("src")) if poster_el else None
107
+
108
+ description = helper.select_text(".excerpt p")
109
+
110
+ # Robust metadata extraction using Regex
111
+
112
+ # Initialize year first
113
+ year = None
114
+
115
+ # Try .release first (legacy) or directly regex
116
+ rel_text = helper.select_text(".release")
117
+ if rel_text:
118
+ m_y = re.search(r"(\d{4})", rel_text)
119
+ if m_y: year = m_y.group(1)
120
+
121
+ # Year fallbacks
122
+ if not year:
123
+ # Try finding year in text like "Yapım: 2024" or just isolated year in release date
124
+ m_year = helper.regex_first(r"Yapım:\s*(\d{4})") or helper.regex_first(r"Yıl:\s*(\d{4})")
125
+ if m_year:
126
+ year = m_year
127
+
128
+ # Rating
129
+ rating_text = helper.select_text(".imdb-rating")
130
+ if rating_text:
131
+ rating = rating_text.replace("IMDB Puanı", "").strip()
132
+ else:
133
+ rating = helper.regex_first(r"IMDB\s*:\s*([\d\.]+)")
134
+
135
+ # Duration
136
+ duration = None
137
+ duration_val = helper.regex_first(r"Süre:\s*(\d+)")
138
+ if duration_val:
139
+ duration = int(duration_val)
140
+
141
+ # Actors - Extract from actor links
142
+ actors = None
143
+ actors_list = []
144
+
145
+ # Site uses: <a href=".../oyuncular/...">Actor Name</a>
146
+ actor_els = helper.select("a[href*='/oyuncular/']")
147
+ if actor_els:
148
+ actors_list = [el.text(strip=True) for el in actor_els if el.text(strip=True)]
149
+
150
+ # Fallback: Try .cast-list selector
151
+ if not actors_list:
152
+ actor_els = helper.select(".cast-list .actor-name, .cast-list a")
153
+ if actor_els:
154
+ actors_list = [el.text(strip=True) for el in actor_els if el.text(strip=True)]
155
+
156
+ if actors_list:
157
+ actors = ", ".join(actors_list)
158
+
159
+
160
+ # Tags (Genres) - Extract from genre links
161
+ tags = None
162
+ tags_list = []
163
+
164
+ # Site uses: <a href=".../tur/...">Genre Name</a> or <a href=".../Kategori/tur/...">
165
+ tag_els = helper.select("a[href*='/tur/'], a[href*='/Kategori/tur/']")
166
+ if tag_els:
167
+ tags_list = [el.text(strip=True) for el in tag_els if el.text(strip=True)]
168
+
169
+ # Fallback: Try .genres selector
170
+ if not tags_list:
171
+ tag_els = helper.select(".genres a, .genre a")
172
+ if tag_els:
173
+ tags_list = [el.text(strip=True) for el in tag_els if el.text(strip=True)]
174
+
175
+ # Remove duplicates while preserving order
176
+ if tags_list:
177
+ seen = set()
178
+ unique_tags = []
179
+ for tag in tags_list:
180
+ if tag not in seen:
181
+ seen.add(tag)
182
+ unique_tags.append(tag)
183
+ tags = unique_tags if unique_tags else None
184
+
185
+
186
+ # Check for Episodes to decide if Series or Movie
187
+ ep_elements = helper.select(".parts-middle a, .parts-middle .part.active")
188
+
189
+ if not ep_elements:
190
+ # Movie
191
+ return MovieInfo(
192
+ url = url,
193
+ title = title,
194
+ description = description,
195
+ poster = poster,
196
+ year = year,
197
+ rating = rating,
198
+ duration = duration,
199
+ tags = tags,
200
+ actors = actors
201
+ )
202
+ else:
203
+ # Series
204
+ episodes = []
205
+ for i, el in enumerate(ep_elements):
206
+ ep_name = helper.select_text(".part-name", el) or f"Bölüm {i+1}"
207
+ ep_href = el.attrs.get("href")
208
+ if not ep_href:
209
+ ep_href = url # Current page if href is empty/active?
210
+ ep_href = self.fix_url(ep_href)
211
+
212
+ # Parse season/episode from name if possible
213
+ # Kotlin: find digit for season, substringAfter("Sezon") digit for episode
214
+ season = 1
215
+ episode = i + 1
216
+
217
+ # Simple heuristic similar to Kotlin
218
+ # "1. Sezon 5. Bölüm"
219
+ s_match = re.search(r"(\d+)\.\s*Sezon", ep_name)
220
+ e_match = re.search(r"(\d+)\.\s*Bölüm", ep_name)
221
+
222
+ if s_match:
223
+ season = int(s_match.group(1))
224
+ if e_match:
225
+ episode = int(e_match.group(1))
226
+
227
+ episodes.append(Episode(
228
+ season = season,
229
+ episode = episode,
230
+ title = ep_name,
231
+ url = ep_href
232
+ ))
233
+
234
+ return SeriesInfo(
235
+ url = url,
236
+ title = title,
237
+ description = description,
238
+ poster = poster,
239
+ year = year,
240
+ rating = rating,
241
+ duration = duration,
242
+ tags = tags,
243
+ actors = actors,
244
+ episodes = episodes
245
+ )
246
+
247
+ async def load_links(self, url: str) -> list[ExtractResult]:
248
+ istek = await self.httpx.get(url)
249
+ helper = HTMLHelper(istek.text)
250
+
251
+ iframe = helper.select_attr(".center-container iframe", "src")
252
+ if not iframe:
253
+ iframe = helper.select_attr("iframe[src*='hotstream.club']", "src")
254
+
255
+ results = []
256
+
257
+ if iframe:
258
+ iframe = self.fix_url(iframe)
259
+
260
+ # Use general extract method
261
+ extracted = await self.extract(iframe)
262
+ if extracted:
263
+ if isinstance(extracted, list):
264
+ results.extend(extracted)
265
+ else:
266
+ results.append(extracted)
267
+ else:
268
+ results.append(ExtractResult(
269
+ name = "Full4kizle | External",
270
+ url = iframe,
271
+ referer = url
272
+ ))
273
+
274
+ return results
@@ -2,6 +2,7 @@
2
2
 
3
3
  from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Episode, SeriesInfo, ExtractResult, HTMLHelper
4
4
  from json import dumps, loads
5
+ import re
5
6
 
6
7
  class RecTV(PluginBase):
7
8
  name = "RecTV"
@@ -75,26 +76,38 @@ class RecTV(PluginBase):
75
76
 
76
77
  episodes = []
77
78
  for season in dizi_veri:
79
+ season_title = season.get("title", "").strip()
78
80
  for episode in season.get("episodes"):
79
- # Bölüm için gerekli bilgileri JSON olarak sakla
80
- ep_data = {
81
- "url" : self.fix_url(episode.get("sources")[0].get("url")),
82
- "title" : f"{veri.get('title')} | {season.get('title', '1. Sezon')} {episode.get('title', '1. Bölüm')}",
83
- "is_episode" : True
84
- }
85
-
86
- # Extract season/episode numbers using helper
87
- s1, _ = HTMLHelper.extract_season_episode(season.get("title") or "")
88
- _, e2 = HTMLHelper.extract_season_episode(episode.get("title") or "")
89
-
90
- ep_model = Episode(
91
- season = s1 or 1,
92
- episode = e2 or 1,
93
- title = episode.get("title"),
94
- url = dumps(ep_data),
95
- )
96
-
97
- episodes.append(ep_model)
81
+ ep_label = episode.get("title", "").strip()
82
+ for source in episode.get("sources"):
83
+ # Bölüm için gerekli bilgileri JSON olarak sakla
84
+ ep_data = {
85
+ "url" : self.fix_url(source.get("url")),
86
+ "title" : f"{veri.get('title')} | {season_title} {ep_label} - {source.get('title')}",
87
+ "is_episode" : True
88
+ }
89
+
90
+ # Extract season/episode numbers using helper
91
+ s1, _ = HTMLHelper.extract_season_episode(season_title or "")
92
+ _, e2 = HTMLHelper.extract_season_episode(ep_label or "")
93
+
94
+ tag = ""
95
+ clean_season = season_title
96
+ if "dublaj" in season_title.lower():
97
+ tag = " (Dublaj)"
98
+ clean_season = re.sub(r"\s*dublaj\s*", "", season_title, flags=re.I).strip()
99
+ elif any(x in season_title.lower() for x in ["altyazı", "altyazi"]):
100
+ tag = " (Altyazı)"
101
+ clean_season = re.sub(r"\s*altyaz[ıi]\s*", "", season_title, flags=re.I).strip()
102
+
103
+ ep_model = Episode(
104
+ season = s1 or 1,
105
+ episode = e2 or 1,
106
+ title = f"{clean_season} {ep_label}{tag} - {source.get('title')}",
107
+ url = dumps(ep_data),
108
+ )
109
+
110
+ episodes.append(ep_model)
98
111
 
99
112
  # Süreyi dakikaya çevir (Örn: "1h 59min")
100
113
  duration_raw = veri.get("duration")