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.

Files changed (88) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +3 -2
  2. KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
  3. KekikStream/Core/HTMLHelper.py +205 -0
  4. KekikStream/Core/Plugin/PluginBase.py +48 -12
  5. KekikStream/Core/Plugin/PluginLoader.py +13 -14
  6. KekikStream/Core/Plugin/PluginManager.py +2 -2
  7. KekikStream/Core/Plugin/PluginModels.py +0 -3
  8. KekikStream/Core/__init__.py +2 -0
  9. KekikStream/Extractors/Abstream.py +27 -0
  10. KekikStream/Extractors/CloseLoad.py +31 -56
  11. KekikStream/Extractors/ContentX.py +28 -71
  12. KekikStream/Extractors/DonilasPlay.py +34 -78
  13. KekikStream/Extractors/DzenRu.py +11 -25
  14. KekikStream/Extractors/ExPlay.py +20 -38
  15. KekikStream/Extractors/Filemoon.py +23 -53
  16. KekikStream/Extractors/HDMomPlayer.py +30 -0
  17. KekikStream/Extractors/HDPlayerSystem.py +13 -31
  18. KekikStream/Extractors/HotStream.py +27 -0
  19. KekikStream/Extractors/JFVid.py +3 -24
  20. KekikStream/Extractors/JetTv.py +21 -34
  21. KekikStream/Extractors/JetV.py +55 -0
  22. KekikStream/Extractors/MailRu.py +11 -29
  23. KekikStream/Extractors/MixPlayHD.py +17 -31
  24. KekikStream/Extractors/MixTiger.py +17 -40
  25. KekikStream/Extractors/MolyStream.py +25 -22
  26. KekikStream/Extractors/Odnoklassniki.py +41 -105
  27. KekikStream/Extractors/PeaceMakerst.py +20 -47
  28. KekikStream/Extractors/PixelDrain.py +9 -16
  29. KekikStream/Extractors/PlayerFilmIzle.py +23 -46
  30. KekikStream/Extractors/RapidVid.py +23 -36
  31. KekikStream/Extractors/SetPlay.py +19 -44
  32. KekikStream/Extractors/SetPrime.py +3 -6
  33. KekikStream/Extractors/SibNet.py +8 -19
  34. KekikStream/Extractors/Sobreatsesuyp.py +25 -47
  35. KekikStream/Extractors/TRsTX.py +25 -55
  36. KekikStream/Extractors/TurboImgz.py +8 -16
  37. KekikStream/Extractors/TurkeyPlayer.py +5 -5
  38. KekikStream/Extractors/VCTPlay.py +10 -28
  39. KekikStream/Extractors/Veev.py +145 -0
  40. KekikStream/Extractors/VidBiz.py +62 -0
  41. KekikStream/Extractors/VidHide.py +59 -34
  42. KekikStream/Extractors/VidMoly.py +67 -89
  43. KekikStream/Extractors/VidMoxy.py +17 -29
  44. KekikStream/Extractors/VidPapi.py +26 -58
  45. KekikStream/Extractors/VideoSeyred.py +21 -42
  46. KekikStream/Extractors/Videostr.py +58 -0
  47. KekikStream/Extractors/Vidoza.py +18 -0
  48. KekikStream/Extractors/Vtbe.py +38 -0
  49. KekikStream/Extractors/YTDLP.py +2 -2
  50. KekikStream/Extractors/YildizKisaFilm.py +13 -31
  51. KekikStream/Extractors/Zeus.py +61 -0
  52. KekikStream/Plugins/BelgeselX.py +108 -99
  53. KekikStream/Plugins/DiziBox.py +61 -106
  54. KekikStream/Plugins/DiziMom.py +179 -0
  55. KekikStream/Plugins/DiziPal.py +104 -192
  56. KekikStream/Plugins/DiziYou.py +66 -149
  57. KekikStream/Plugins/Dizilla.py +93 -126
  58. KekikStream/Plugins/FilmBip.py +102 -72
  59. KekikStream/Plugins/FilmEkseni.py +199 -0
  60. KekikStream/Plugins/FilmMakinesi.py +101 -64
  61. KekikStream/Plugins/FilmModu.py +35 -59
  62. KekikStream/Plugins/Filmatek.py +184 -0
  63. KekikStream/Plugins/FilmciBaba.py +155 -0
  64. KekikStream/Plugins/FullHDFilmizlesene.py +32 -78
  65. KekikStream/Plugins/HDFilm.py +243 -0
  66. KekikStream/Plugins/HDFilmCehennemi.py +261 -222
  67. KekikStream/Plugins/JetFilmizle.py +117 -98
  68. KekikStream/Plugins/KultFilmler.py +153 -143
  69. KekikStream/Plugins/RecTV.py +53 -49
  70. KekikStream/Plugins/RoketDizi.py +92 -123
  71. KekikStream/Plugins/SelcukFlix.py +86 -95
  72. KekikStream/Plugins/SetFilmIzle.py +105 -143
  73. KekikStream/Plugins/SezonlukDizi.py +106 -128
  74. KekikStream/Plugins/Sinefy.py +194 -166
  75. KekikStream/Plugins/SinemaCX.py +159 -113
  76. KekikStream/Plugins/Sinezy.py +44 -73
  77. KekikStream/Plugins/SuperFilmGeldi.py +28 -52
  78. KekikStream/Plugins/UgurFilm.py +94 -72
  79. KekikStream/Plugins/Watch32.py +160 -0
  80. KekikStream/Plugins/YabanciDizi.py +250 -0
  81. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/METADATA +1 -1
  82. kekikstream-2.5.3.dist-info/RECORD +99 -0
  83. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/WHEEL +1 -1
  84. KekikStream/Plugins/FullHDFilm.py +0 -254
  85. kekikstream-2.2.9.dist-info/RECORD +0 -82
  86. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/entry_points.txt +0 -0
  87. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/licenses/LICENSE +0 -0
  88. {kekikstream-2.2.9.dist-info → kekikstream-2.5.3.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Subtitle, ExtractResult
4
- from selectolax.parser import HTMLParser
5
- import re
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Subtitle, ExtractResult, HTMLHelper
6
4
 
7
5
  class FilmModu(PluginBase):
8
6
  name = "FilmModu"
@@ -42,97 +40,77 @@ class FilmModu(PluginBase):
42
40
 
43
41
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
44
42
  istek = await self.httpx.get(url.replace("SAYFA", str(page)))
45
- secici = HTMLParser(istek.text)
43
+ secici = HTMLHelper(istek.text)
46
44
 
47
45
  results = []
48
- for veri in secici.css("div.movie"):
49
- link_el = veri.css_first("a")
50
- img_el = veri.css_first("picture img")
51
-
52
- title = link_el.text(strip=True) if link_el else None
53
- href = link_el.attrs.get("href") if link_el else None
54
- poster = img_el.attrs.get("data-src") if img_el else None
46
+ for veri in secici.select("div.movie"):
47
+ title = secici.select_text("a", veri)
48
+ href = secici.select_attr("a", "href", veri)
49
+ poster = secici.select_attr("picture img", "data-src", veri)
55
50
 
56
51
  if title and href:
57
52
  results.append(MainPageResult(
58
53
  category = category,
59
54
  title = title,
60
55
  url = self.fix_url(href),
61
- poster = self.fix_url(poster) if poster else None,
56
+ poster = self.fix_url(poster),
62
57
  ))
63
58
 
64
59
  return results
65
60
 
66
61
  async def search(self, query: str) -> list[SearchResult]:
67
62
  istek = await self.httpx.get(f"{self.main_url}/film-ara?term={query}")
68
- secici = HTMLParser(istek.text)
63
+ secici = HTMLHelper(istek.text)
69
64
 
70
65
  results = []
71
- for veri in secici.css("div.movie"):
72
- link_el = veri.css_first("a")
73
- img_el = veri.css_first("picture img")
74
-
75
- title = link_el.text(strip=True) if link_el else None
76
- href = link_el.attrs.get("href") if link_el else None
77
- poster = img_el.attrs.get("data-src") if img_el else None
66
+ for veri in secici.select("div.movie"):
67
+ title = secici.select_text("a", veri)
68
+ href = secici.select_attr("a", "href", veri)
69
+ poster = secici.select_attr("picture img", "data-src", veri)
78
70
 
79
71
  if title and href:
80
72
  results.append(SearchResult(
81
73
  title = title,
82
74
  url = self.fix_url(href),
83
- poster = self.fix_url(poster) if poster else None,
75
+ poster = self.fix_url(poster),
84
76
  ))
85
77
 
86
78
  return results
87
79
 
88
80
  async def load_item(self, url: str) -> MovieInfo:
89
81
  istek = await self.httpx.get(url)
90
- secici = HTMLParser(istek.text)
91
-
92
- org_title_el = secici.css_first("div.titles h1")
93
- alt_title_el = secici.css_first("div.titles h2")
94
-
95
- org_title = org_title_el.text(strip=True) if org_title_el else ""
96
- alt_title = alt_title_el.text(strip=True) if alt_title_el else ""
97
- title = f"{org_title} - {alt_title}" if alt_title else org_title
98
-
99
- poster_el = secici.css_first("img.img-responsive")
100
- poster = poster_el.attrs.get("src") if poster_el else None
101
-
102
- desc_el = secici.css_first("p[itemprop='description']")
103
- description = desc_el.text(strip=True) if desc_el else None
104
-
105
- tags = [a.text(strip=True) for a in secici.css("a[href*='film-tur/']") if a.text(strip=True)]
106
-
107
- year_el = secici.css_first("span[itemprop='dateCreated']")
108
- year = year_el.text(strip=True) if year_el else None
109
-
110
- actors = []
111
- for a in secici.css("a[itemprop='actor']"):
112
- span_el = a.css_first("span")
113
- if span_el and span_el.text(strip=True):
114
- actors.append(span_el.text(strip=True))
82
+ secici = HTMLHelper(istek.text)
83
+
84
+ org_title = secici.select_text("div.titles h1")
85
+ alt_title = secici.select_text("div.titles h2")
86
+ title = f"{org_title} - {alt_title}" if alt_title else (org_title)
87
+ poster = secici.select_poster("img.img-responsive")
88
+ description = secici.select_text("p[itemprop='description']")
89
+ tags = secici.select_texts("a[href*='film-tur/']")
90
+ rating = secici.meta_value("IMDB")
91
+ year = secici.extract_year("span[itemprop='dateCreated']")
92
+ actors = secici.select_texts("a[itemprop='actor'] span")
115
93
 
116
94
  return MovieInfo(
117
95
  url = url,
118
- poster = self.fix_url(poster) if poster else None,
96
+ poster = self.fix_url(poster),
119
97
  title = title,
120
98
  description = description,
121
99
  tags = tags,
100
+ rating = rating,
122
101
  year = year,
123
- actors = actors,
102
+ actors = actors
124
103
  )
125
104
 
126
105
  async def load_links(self, url: str) -> list[ExtractResult]:
127
106
  istek = await self.httpx.get(url)
128
- secici = HTMLParser(istek.text)
107
+ secici = HTMLHelper(istek.text)
129
108
 
130
- alternates = secici.css("div.alternates a")
109
+ alternates = secici.select("div.alternates a")
131
110
  if not alternates:
132
111
  return []
133
112
 
134
113
  results = []
135
-
136
114
  for alternatif in alternates:
137
115
  alt_link = alternatif.attrs.get("href")
138
116
  alt_name = alternatif.text(strip=True)
@@ -140,20 +118,18 @@ class FilmModu(PluginBase):
140
118
  if alt_name == "Fragman" or not alt_link:
141
119
  continue
142
120
 
143
- alt_link = self.fix_url(alt_link)
121
+ alt_link = self.fix_url(alt_link)
144
122
  alt_istek = await self.httpx.get(alt_link)
145
- alt_text = alt_istek.text
123
+ secici = HTMLHelper(alt_istek.text)
146
124
 
147
- vid_id = re.search(r"var videoId = '(.*)'", alt_text)
148
- vid_type = re.search(r"var videoType = '(.*)'", alt_text)
125
+ vid_id = secici.regex_first(r"var videoId = '([^']*)'")
126
+ vid_type = secici.regex_first(r"var videoType = '([^']*)'")
149
127
 
150
128
  if not vid_id or not vid_type:
151
129
  continue
152
130
 
153
- source_istek = await self.httpx.get(
154
- f"{self.main_url}/get-source?movie_id={vid_id[1]}&type={vid_type[1]}"
155
- )
156
- source_data = source_istek.json()
131
+ source_istek = await self.httpx.get(f"{self.main_url}/get-source?movie_id={vid_id}&type={vid_type}")
132
+ source_data = source_istek.json()
157
133
 
158
134
  if source_data.get("subtitle"):
159
135
  subtitle_url = self.fix_url(source_data["subtitle"])
@@ -0,0 +1,184 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
4
+ from urllib.parse import unquote
5
+ from contextlib import suppress
6
+
7
+ class Filmatek(PluginBase):
8
+ name = "Filmatek"
9
+ language = "tr"
10
+ main_url = "https://filmatek.net"
11
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
+ description = "Sosyalizmin Sineması Veritabanı"
13
+
14
+ main_page = {
15
+ f"{main_url}/tur/aile/page" : "Aile",
16
+ f"{main_url}/tur/aksiyon/page" : "Aksiyon",
17
+ f"{main_url}/tur/animasyon/page" : "Animasyon",
18
+ f"{main_url}/tur/bilim-kurgu/page" : "Bilim Kurgu",
19
+ f"{main_url}/tur/komedi/page" : "Komedi",
20
+ f"{main_url}/tur/korku/page" : "Korku",
21
+ f"{main_url}/tur/macera/page" : "Macera",
22
+ f"{main_url}/tur/romantik/page" : "Romantik",
23
+ f"{main_url}/tur/suc/page" : "Suç",
24
+ f"{main_url}/tur/yerli-filmler/page" : "Yerli Filmler",
25
+ f"{main_url}/film-arsivi/page" : "Tüm Filmler",
26
+ }
27
+
28
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
29
+ istek = await self.httpx.get(f"{url}/{page}/")
30
+ secici = HTMLHelper(istek.text)
31
+
32
+ results = []
33
+ for item in secici.select("div.items article, #archive-content article"):
34
+ title_el = secici.select_first("div.data h3 a, h3 a", item)
35
+ if not title_el:
36
+ continue
37
+
38
+ title = title_el.text(strip=True)
39
+ href = self.fix_url(title_el.attrs.get("href"))
40
+ poster = self.fix_url(secici.select_poster("img", item))
41
+
42
+ results.append(MainPageResult(
43
+ category = category,
44
+ title = title,
45
+ url = href,
46
+ poster = poster
47
+ ))
48
+
49
+ return results
50
+
51
+ async def search(self, query: str) -> list[SearchResult]:
52
+ istek = await self.httpx.get(f"{self.main_url}/?s={query}")
53
+ secici = HTMLHelper(istek.text)
54
+
55
+ results = []
56
+ for item in secici.select("div.result-item"):
57
+ title_el = secici.select_first("div.title a", item)
58
+ if not title_el:
59
+ continue
60
+
61
+ title = title_el.text(strip=True)
62
+ href = self.fix_url(title_el.attrs.get("href"))
63
+ poster = self.fix_url(secici.select_poster("div.image img", item))
64
+
65
+ results.append(SearchResult(
66
+ title = title,
67
+ url = href,
68
+ poster = poster
69
+ ))
70
+
71
+ return results
72
+
73
+ async def load_item(self, url: str) -> MovieInfo:
74
+ istek = await self.httpx.get(url)
75
+ secici = HTMLHelper(istek.text)
76
+
77
+ title = self.clean_title(secici.select_text("div.data h1, h1"))
78
+ poster = secici.select_poster("div.poster img") or secici.select_attr("meta[property='og:image']", "content")
79
+ description = secici.select_text("div.wp-content p") or secici.select_attr("meta[property='og:description']", "content")
80
+ year = secici.extract_year("span.date")
81
+ rating = secici.select_text("span.dt_rating_vgs") or secici.select_text("span.dt_rating_vmanual")
82
+ duration = secici.regex_first(r"(\d+)", secici.select_text("span.runtime"))
83
+ tags = secici.select_texts("div.sgeneros a")
84
+ actors = secici.select_texts("div.person div.name a")
85
+
86
+ return MovieInfo(
87
+ url = url,
88
+ title = title,
89
+ description = description,
90
+ poster = self.fix_url(poster),
91
+ year = year,
92
+ rating = rating,
93
+ duration = duration,
94
+ tags = tags,
95
+ actors = actors
96
+ )
97
+
98
+ async def load_links(self, url: str) -> list[ExtractResult]:
99
+ istek = await self.httpx.get(url)
100
+ secici = HTMLHelper(istek.text)
101
+
102
+ # Player seçeneklerini bul
103
+ options = secici.select("div#playeroptions ul.ajax_mode li.dooplay_player_option")
104
+ if not options:
105
+ # Fallback: Body class'tan post_id
106
+ body_class = secici.select_attr("body", "class") or ""
107
+ if pid := secici.regex_first(r"postid-(\d+)", body_class):
108
+ options = [{"data-post": pid, "data-nume": "1", "data-type": "movie", "title": "Varsayılan"}]
109
+ else:
110
+ options = []
111
+
112
+ results = []
113
+ for opt in options:
114
+ if isinstance(opt, dict):
115
+ post_id = opt.get("data-post")
116
+ nume = opt.get("data-nume")
117
+ type_ = opt.get("data-type")
118
+ title = opt.get("title")
119
+ else:
120
+ post_id = opt.attrs.get("data-post")
121
+ nume = opt.attrs.get("data-nume")
122
+ type_ = opt.attrs.get("data-type")
123
+ title = secici.select_text("span.title", opt)
124
+
125
+ if not post_id or not nume:
126
+ continue
127
+
128
+ try:
129
+ # Need to use post with data
130
+ player_resp = await self.httpx.post(
131
+ url = f"{self.main_url}/wp-admin/admin-ajax.php",
132
+ headers = {
133
+ "X-Requested-With" : "XMLHttpRequest",
134
+ "Referer" : url,
135
+ "Content-Type" : "application/x-www-form-urlencoded"
136
+ },
137
+ data = {
138
+ "action" : "doo_player_ajax",
139
+ "post" : post_id,
140
+ "nume" : nume,
141
+ "type" : type_
142
+ }
143
+ )
144
+
145
+ content = player_resp.text.replace(r"\/", "/")
146
+ iframe_url = secici.regex_first(r'(?:src|url)["\']?\s*[:=]\s*["\']([^"\']+)["\']', content)
147
+
148
+ if iframe_url:
149
+ if iframe_url.startswith("/"):
150
+ iframe_url = self.main_url + iframe_url
151
+
152
+ iframe_url = self.fix_url(iframe_url)
153
+
154
+ # Unwrap internal JWPlayer
155
+ if "jwplayer/?source=" in iframe_url:
156
+ with suppress(Exception):
157
+ raw_source = iframe_url.split("source=")[1].split("&")[0]
158
+ iframe_url = unquote(raw_source)
159
+
160
+ # Direct media files
161
+ if ".m3u8" in iframe_url or ".mp4" in iframe_url:
162
+ results.append(ExtractResult(
163
+ name = f"{title} | Direct",
164
+ url = iframe_url,
165
+ referer = url
166
+ ))
167
+ else:
168
+ extracted = await self.extract(iframe_url, prefix=title)
169
+ if extracted:
170
+ if isinstance(extracted, list):
171
+ results.extend(extracted)
172
+ else:
173
+ results.append(extracted)
174
+ else:
175
+ results.append(ExtractResult(
176
+ name = f"{title} | External",
177
+ url = iframe_url,
178
+ referer = url
179
+ ))
180
+ except Exception as e:
181
+ # print(f"Filmatek Error: {e}")
182
+ pass
183
+
184
+ return results
@@ -0,0 +1,155 @@
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 FilmciBaba(PluginBase):
6
+ name = "FilmciBaba"
7
+ language = "tr"
8
+ main_url = "https://4kizle.live"
9
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
10
+ 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."
11
+
12
+ main_page = {
13
+ f"{main_url}/Kategori/en-populer-filmler/page" : "En Popüler Filmler",
14
+ f"{main_url}/Kategori/tur/aksiyon-filmleri/page" : "Aksiyon",
15
+ f"{main_url}/Kategori/tur/macera-filmleri/page" : "Macera",
16
+ f"{main_url}/Kategori/tur/bilim-kurgu-filmleri/page" : "Bilim Kurgu",
17
+ f"{main_url}/Kategori/tur/fantastik-filmler/page" : "Fantastik",
18
+ f"{main_url}/Kategori/tur/korku-filmleri/page" : "Korku",
19
+ f"{main_url}/Kategori/tur/gerilim-filmleri-hd/page" : "Gerilim",
20
+ f"{main_url}/Kategori/tur/gizem-filmleri/page" : "Gizem",
21
+ f"{main_url}/Kategori/tur/dram-filmleri-hd/page" : "Dram",
22
+ f"{main_url}/Kategori/tur/komedi-filmleri-hd/page" : "Komedi",
23
+ f"{main_url}/Kategori/tur/romantik-filmler/page" : "Romantik",
24
+ f"{main_url}/Kategori/tur/aile-filmleri/page" : "Aile",
25
+ f"{main_url}/Kategori/tur/animasyon-filmleri/page" : "Animasyon",
26
+ f"{main_url}/Kategori/tur/biyografi-filmleri/page" : "Biyografi",
27
+ f"{main_url}/Kategori/tur/polisiye-suc-filmleri/page" : "Polisiye / Suç",
28
+ f"{main_url}/Kategori/tur/savas-filmleri/page" : "Savaş",
29
+ f"{main_url}/Kategori/tur/western-filmler/page" : "Western",
30
+ f"{main_url}/Kategori/tur/hint-filmleri/page" : "Hint Filmleri",
31
+ f"{main_url}/Kategori/tur/kore-filmleri/page" : "Kore Filmleri",
32
+ f"{main_url}/Kategori/tur/yerli-filmler-izle/page" : "Yerli Filmler",
33
+ f"{main_url}/Kategori/tur/yerli-diziler/page" : "Yerli Diziler",
34
+ f"{main_url}/Kategori/tur/18-erotik-filmler/page" : "+18 Erotik Filmler",
35
+ }
36
+
37
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
38
+ istek = await self.httpx.get(f"{url}/{page}/")
39
+ secici = HTMLHelper(istek.text)
40
+
41
+ results = []
42
+ for item in secici.select("div.movie-preview"):
43
+ title_el = secici.select_first(".movie-title a", item)
44
+ if not title_el:
45
+ continue
46
+
47
+ title = self.clean_title(title_el.text(strip=True))
48
+ href = self.fix_url(title_el.attrs.get("href"))
49
+ poster = self.fix_url(secici.select_poster(".movie-poster img", item))
50
+
51
+ results.append(MainPageResult(
52
+ category = category,
53
+ title = title,
54
+ url = href,
55
+ poster = poster
56
+ ))
57
+
58
+ return results
59
+
60
+ async def search(self, query: str) -> list[SearchResult]:
61
+ istek = await self.httpx.get(f"{self.main_url}/?s={query}")
62
+ secici = HTMLHelper(istek.text)
63
+
64
+ results = []
65
+ for item in secici.select("div.movie-preview"):
66
+ title_el = secici.select_first(".movie-title a", item)
67
+ if not title_el:
68
+ continue
69
+
70
+ title = self.clean_title(title_el.text(strip=True))
71
+ href = self.fix_url(title_el.attrs.get("href"))
72
+ poster = self.fix_url(secici.select_poster(".movie-poster img", item))
73
+
74
+ results.append(SearchResult(
75
+ title = title,
76
+ url = href,
77
+ poster = poster
78
+ ))
79
+
80
+ return results
81
+
82
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
83
+ istek = await self.httpx.get(url)
84
+ secici = HTMLHelper(istek.text)
85
+
86
+ title = self.clean_title(secici.select_text("h1"))
87
+ poster = secici.select_poster(".poster img")
88
+ description = secici.select_text(".excerpt p")
89
+ year = secici.extract_year(".release", ".movie-info")
90
+ rating = secici.regex_first(r"([\d\.]+)", secici.select_text(".imdb-rating"))
91
+ tags = secici.select_texts("div.categories a[href*='/Kategori/tur/']")
92
+ actors = secici.select_texts("a[href*='/oyuncular/']") or secici.select_texts(".cast-list .actor-name, .cast-list a")
93
+
94
+ # Bölüm linklerini kontrol et
95
+ ep_elements = secici.select(".parts-middle a, .parts-middle .part.active")
96
+
97
+ if not ep_elements:
98
+ return MovieInfo(
99
+ url = url,
100
+ title = title,
101
+ description = description,
102
+ poster = self.fix_url(poster),
103
+ year = year,
104
+ rating = rating if rating != "." else None,
105
+ tags = tags,
106
+ actors = actors
107
+ )
108
+
109
+ episodes = []
110
+ for i, el in enumerate(ep_elements):
111
+ name = secici.select_text(".part-name", el) or f"Bölüm {i+1}"
112
+ href = el.attrs.get("href") or url
113
+ s, e = secici.extract_season_episode(name)
114
+ episodes.append(Episode(season=s or 1, episode=e or (i + 1), title=name, url=self.fix_url(href)))
115
+
116
+ return SeriesInfo(
117
+ url = url,
118
+ title = title,
119
+ description = description,
120
+ poster = self.fix_url(poster),
121
+ year = year,
122
+ rating = rating,
123
+ tags = tags,
124
+ actors = actors,
125
+ episodes = episodes
126
+ )
127
+
128
+ async def load_links(self, url: str) -> list[ExtractResult]:
129
+ istek = await self.httpx.get(url)
130
+ secici = HTMLHelper(istek.text)
131
+
132
+ iframe = secici.select_attr(".center-container iframe", "src")
133
+ if not iframe:
134
+ iframe = secici.select_attr("iframe[src*='hotstream.club']", "src")
135
+
136
+ results = []
137
+
138
+ if iframe:
139
+ iframe = self.fix_url(iframe)
140
+
141
+ # Use general extract method
142
+ extracted = await self.extract(iframe)
143
+ if extracted:
144
+ if isinstance(extracted, list):
145
+ results.extend(extracted)
146
+ else:
147
+ results.append(extracted)
148
+ else:
149
+ results.append(ExtractResult(
150
+ name = "FilmciBaba | External",
151
+ url = iframe,
152
+ referer = url
153
+ ))
154
+
155
+ return results