KekikStream 2.2.9__py3-none-any.whl → 2.3.9__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 (62) hide show
  1. KekikStream/Core/HTMLHelper.py +134 -0
  2. KekikStream/Core/Plugin/PluginBase.py +22 -4
  3. KekikStream/Core/Plugin/PluginLoader.py +3 -2
  4. KekikStream/Core/Plugin/PluginManager.py +2 -2
  5. KekikStream/Core/__init__.py +2 -0
  6. KekikStream/Extractors/CloseLoad.py +12 -13
  7. KekikStream/Extractors/ContentX.py +33 -31
  8. KekikStream/Extractors/DonilasPlay.py +10 -10
  9. KekikStream/Extractors/DzenRu.py +3 -3
  10. KekikStream/Extractors/ExPlay.py +10 -10
  11. KekikStream/Extractors/Filemoon.py +11 -16
  12. KekikStream/Extractors/JetTv.py +4 -4
  13. KekikStream/Extractors/MixPlayHD.py +10 -11
  14. KekikStream/Extractors/MolyStream.py +16 -9
  15. KekikStream/Extractors/Odnoklassniki.py +4 -4
  16. KekikStream/Extractors/PeaceMakerst.py +3 -3
  17. KekikStream/Extractors/PixelDrain.py +6 -5
  18. KekikStream/Extractors/PlayerFilmIzle.py +6 -10
  19. KekikStream/Extractors/RapidVid.py +8 -7
  20. KekikStream/Extractors/SetPlay.py +10 -10
  21. KekikStream/Extractors/SetPrime.py +3 -6
  22. KekikStream/Extractors/SibNet.py +4 -5
  23. KekikStream/Extractors/Sobreatsesuyp.py +5 -5
  24. KekikStream/Extractors/TRsTX.py +5 -5
  25. KekikStream/Extractors/TurboImgz.py +3 -4
  26. KekikStream/Extractors/TurkeyPlayer.py +5 -5
  27. KekikStream/Extractors/VidHide.py +4 -7
  28. KekikStream/Extractors/VidMoly.py +37 -25
  29. KekikStream/Extractors/VidMoxy.py +8 -9
  30. KekikStream/Extractors/VidPapi.py +5 -7
  31. KekikStream/Extractors/VideoSeyred.py +3 -3
  32. KekikStream/Plugins/BelgeselX.py +40 -51
  33. KekikStream/Plugins/DiziBox.py +53 -81
  34. KekikStream/Plugins/DiziPal.py +50 -72
  35. KekikStream/Plugins/DiziYou.py +96 -83
  36. KekikStream/Plugins/Dizilla.py +95 -107
  37. KekikStream/Plugins/FilmBip.py +29 -50
  38. KekikStream/Plugins/FilmMakinesi.py +84 -46
  39. KekikStream/Plugins/FilmModu.py +27 -41
  40. KekikStream/Plugins/FullHDFilm.py +57 -62
  41. KekikStream/Plugins/FullHDFilmizlesene.py +32 -57
  42. KekikStream/Plugins/HDFilmCehennemi.py +51 -65
  43. KekikStream/Plugins/JetFilmizle.py +38 -51
  44. KekikStream/Plugins/KultFilmler.py +43 -67
  45. KekikStream/Plugins/RecTV.py +34 -9
  46. KekikStream/Plugins/RoketDizi.py +89 -111
  47. KekikStream/Plugins/SelcukFlix.py +102 -93
  48. KekikStream/Plugins/SetFilmIzle.py +65 -75
  49. KekikStream/Plugins/SezonlukDizi.py +47 -65
  50. KekikStream/Plugins/Sinefy.py +70 -70
  51. KekikStream/Plugins/SinemaCX.py +31 -55
  52. KekikStream/Plugins/Sinezy.py +27 -54
  53. KekikStream/Plugins/SuperFilmGeldi.py +25 -44
  54. KekikStream/Plugins/UgurFilm.py +23 -48
  55. KekikStream/Plugins/YabanciDizi.py +285 -0
  56. {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/METADATA +1 -1
  57. kekikstream-2.3.9.dist-info/RECORD +84 -0
  58. kekikstream-2.2.9.dist-info/RECORD +0 -82
  59. {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/WHEEL +0 -0
  60. {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/entry_points.txt +0 -0
  61. {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.dist-info}/licenses/LICENSE +0 -0
  62. {kekikstream-2.2.9.dist-info → kekikstream-2.3.9.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, ExtractResult
4
- from selectolax.parser import HTMLParser
5
- import re
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
6
4
 
7
5
  class FilmBip(PluginBase):
8
6
  name = "FilmBip"
@@ -37,16 +35,13 @@ class FilmBip(PluginBase):
37
35
  page_url = page_url.rstrip("/")
38
36
 
39
37
  istek = await self.httpx.get(page_url)
40
- secici = HTMLParser(istek.text)
38
+ secici = HTMLHelper(istek.text)
41
39
 
42
40
  results = []
43
- for veri in secici.css("div.poster-long"):
44
- img = veri.css_first("a.block img.lazy")
45
- link_el = veri.css_first("a.block")
46
-
47
- title = img.attrs.get("alt") if img else None
48
- href = link_el.attrs.get("href") if link_el else None
49
- poster = (img.attrs.get("data-src") or img.attrs.get("src")) if img else None
41
+ for veri in secici.select("div.poster-long"):
42
+ title = secici.select_attr("a.block img.lazy", "alt", veri)
43
+ href = secici.select_attr("a.block", "href", veri)
44
+ poster = secici.select_poster("a.block img.lazy", veri)
50
45
 
51
46
  if title and href:
52
47
  results.append(MainPageResult(
@@ -80,17 +75,13 @@ class FilmBip(PluginBase):
80
75
  except Exception:
81
76
  return []
82
77
 
83
- secici = HTMLParser(html_content)
78
+ secici = HTMLHelper(html_content)
84
79
 
85
80
  results = []
86
- for veri in secici.css("li"):
87
- link_el = veri.css_first("a.block.truncate")
88
- href_el = veri.css_first("a")
89
- img_el = veri.css_first("img.lazy")
90
-
91
- title = link_el.text(strip=True) if link_el else None
92
- href = href_el.attrs.get("href") if href_el else None
93
- poster = img_el.attrs.get("data-src") if img_el else None
81
+ for veri in secici.select("li"):
82
+ title = secici.select_text("a.block.truncate", veri)
83
+ href = secici.select_attr("a", "href", veri)
84
+ poster = secici.select_attr("img.lazy", "data-src", veri)
94
85
 
95
86
  if title and href:
96
87
  results.append(SearchResult(
@@ -103,47 +94,36 @@ class FilmBip(PluginBase):
103
94
 
104
95
  async def load_item(self, url: str) -> MovieInfo:
105
96
  istek = await self.httpx.get(url)
106
- secici = HTMLParser(istek.text)
97
+ secici = HTMLHelper(istek.text)
107
98
  html_text = istek.text
108
99
 
109
- title_el = secici.css_first("div.page-title h1")
110
- title = title_el.text(strip=True) if title_el else ""
100
+ title = secici.select_text("div.page-title h1") or ""
111
101
 
112
- og_image = secici.css_first("meta[property='og:image']")
113
- poster = og_image.attrs.get("content") if og_image else None
102
+ poster = secici.select_attr("meta[property='og:image']", "content")
114
103
 
115
- trailer_el = secici.css_first("div.series-profile-trailer")
116
- trailer = trailer_el.attrs.get("data-yt") if trailer_el else None
104
+ trailer = secici.select_attr("div.series-profile-trailer", "data-yt")
117
105
 
118
- desc_el = secici.css_first("div.series-profile-infos-in.article p")
119
- if not desc_el:
120
- desc_el = secici.css_first("div.series-profile-summary p")
121
- description = desc_el.text(strip=True) if desc_el else None
106
+ description = secici.select_text("div.series-profile-infos-in.article p") or secici.select_text("div.series-profile-summary p")
122
107
 
123
- tags = [a.text(strip=True) for a in secici.css("div.series-profile-type.tv-show-profile-type a") if a.text(strip=True)]
108
+ tags = secici.select_all_text("div.series-profile-type.tv-show-profile-type a")
124
109
 
125
110
  # XPath yerine regex kullanarak yıl, süre vs. çıkarma
126
- year = None
127
- year_match = re.search(r'Yapım yılı.*?<p[^>]*>(\d{4})</p>', html_text, re.IGNORECASE | re.DOTALL)
128
- if year_match:
129
- year = year_match.group(1)
111
+ year = secici.regex_first(r"(?is)Yap\u0131m y\u0131l\u0131.*?<p[^>]*>(.*?)<\/p>")
112
+ if not year:
113
+ # Fallback: Başlığın sonundaki parantezli yılı yakala
114
+ year = secici.regex_first(r"\((\d{4})\)", title)
130
115
 
131
- duration = None
132
- duration_match = re.search(r'Süre.*?<p[^>]*>(\d+)', html_text, re.IGNORECASE | re.DOTALL)
133
- if duration_match:
134
- duration = duration_match.group(1)
116
+ duration_raw = secici.regex_first(r"(?is)S\u00fcre.*?<p[^>]*>(.*?)<\/p>")
117
+ duration = secici.regex_first(r"(\d+)", duration_raw) if duration_raw else None
135
118
 
136
- rating = None
137
- rating_match = re.search(r'IMDB Puanı.*?<span[^>]*>([0-9.]+)</span>', html_text, re.IGNORECASE | re.DOTALL)
138
- if rating_match:
139
- rating = rating_match.group(1)
119
+ rating = secici.regex_first(r"(?is)IMDB Puan\u0131.*?<span[^>]*>(.*?)<\/span>")
140
120
 
141
- actors = [img.attrs.get("alt") for img in secici.css("div.series-profile-cast ul li a img") if img.attrs.get("alt")]
121
+ actors = [img.attrs.get("alt") for img in secici.select("div.series-profile-cast ul li a img") if img.attrs.get("alt")]
142
122
 
143
123
  return MovieInfo(
144
124
  url = url,
145
125
  poster = self.fix_url(poster) if poster else None,
146
- title = self.clean_title(title) if title else "",
126
+ title = HTMLHelper(title).regex_replace(r"\(\d{4}\)", "").strip() if title else "",
147
127
  description = description,
148
128
  tags = tags,
149
129
  year = year,
@@ -154,13 +134,12 @@ class FilmBip(PluginBase):
154
134
 
155
135
  async def load_links(self, url: str) -> list[ExtractResult]:
156
136
  istek = await self.httpx.get(url)
157
- secici = HTMLParser(istek.text)
137
+ secici = HTMLHelper(istek.text)
158
138
 
159
139
  results = []
160
140
 
161
- for player in secici.css("div#tv-spoox2"):
162
- iframe_el = player.css_first("iframe")
163
- iframe = iframe_el.attrs.get("src") if iframe_el else None
141
+ for player in secici.select("div#tv-spoox2"):
142
+ iframe = secici.select_attr("iframe", "src", player)
164
143
 
165
144
  if iframe:
166
145
  iframe = self.fix_url(iframe)
@@ -1,7 +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, ExtractResult
4
- from selectolax.parser import HTMLParser
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, HTMLHelper
5
4
 
6
5
  class FilmMakinesi(PluginBase):
7
6
  name = "FilmMakinesi"
@@ -36,17 +35,13 @@ class FilmMakinesi(PluginBase):
36
35
 
37
36
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
38
37
  istek = self.cloudscraper.get(f"{url}{'' if page == 1 else f'page/{page}/'}")
39
- secici = HTMLParser(istek.text)
38
+ secici = HTMLHelper(istek.text)
40
39
 
41
40
  results = []
42
- for veri in secici.css("div.item-relative"):
43
- title_el = veri.css_first("div.title")
44
- link_el = veri.css_first("a")
45
- img_el = veri.css_first("img")
46
-
47
- title = title_el.text(strip=True) if title_el else None
48
- href = link_el.attrs.get("href") if link_el else None
49
- poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
41
+ for veri in secici.select("div.item-relative"):
42
+ title = secici.select_text("div.title", veri)
43
+ href = secici.select_attr("a", "href", veri)
44
+ poster = secici.select_poster("img", veri)
50
45
 
51
46
  if title and href:
52
47
  results.append(MainPageResult(
@@ -60,17 +55,13 @@ class FilmMakinesi(PluginBase):
60
55
 
61
56
  async def search(self, query: str) -> list[SearchResult]:
62
57
  istek = await self.httpx.get(f"{self.main_url}/arama/?s={query}")
63
- secici = HTMLParser(istek.text)
58
+ secici = HTMLHelper(istek.text)
64
59
 
65
60
  results = []
66
- for article in secici.css("div.item-relative"):
67
- title_el = article.css_first("div.title")
68
- link_el = article.css_first("a")
69
- img_el = article.css_first("img")
70
-
71
- title = title_el.text(strip=True) if title_el else None
72
- href = link_el.attrs.get("href") if link_el else None
73
- poster = (img_el.attrs.get("data-src") or img_el.attrs.get("src")) if img_el else None
61
+ for article in secici.select("div.item-relative"):
62
+ title = secici.select_text("div.title", article)
63
+ href = secici.select_attr("a", "href", article)
64
+ poster = secici.select_poster("img", article)
74
65
 
75
66
  if title and href:
76
67
  results.append(SearchResult(
@@ -81,40 +72,88 @@ class FilmMakinesi(PluginBase):
81
72
 
82
73
  return results
83
74
 
84
- async def load_item(self, url: str) -> MovieInfo:
75
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
85
76
  istek = await self.httpx.get(url)
86
- secici = HTMLParser(istek.text)
77
+ secici = HTMLHelper(istek.text)
87
78
 
88
- title_el = secici.css_first("h1.title")
89
- title = title_el.text(strip=True) if title_el else ""
79
+ title = secici.select_text("h1.title") or ""
90
80
 
91
- poster_el = secici.css_first("img.cover-img")
92
- poster = poster_el.attrs.get("src", "").strip() if poster_el else ""
81
+ poster = secici.select_attr("img.cover-img", "src") or ""
82
+ poster = poster.strip()
93
83
 
94
- desc_el = secici.css_first("div.info-description p")
95
- description = desc_el.text(strip=True) if desc_el else ""
84
+ description = secici.select_text("div.info-description p") or ""
96
85
 
97
- rating_el = secici.css_first("div.score")
98
- rating = None
99
- if rating_el:
100
- rating_text = rating_el.text(strip=True)
101
- if rating_text:
102
- rating = rating_text.split()[0]
86
+ rating_text = secici.select_text("div.score") or ""
87
+ rating = None
88
+ if rating_text:
89
+ rating = rating_text.split()[0]
103
90
 
104
- year_el = secici.css_first("span.date a")
105
- year = year_el.text(strip=True) if year_el else ""
91
+ year = secici.select_text("span.date a") or ""
106
92
 
107
- actors = [el.text(strip=True) for el in secici.css("div.cast-name") if el.text(strip=True)]
108
- tags = [el.text(strip=True) for el in secici.css("div.genre a") if el.text(strip=True)]
93
+ actors = secici.select_all_text("div.cast-name")
94
+ tags = [a.text(strip=True) for a in secici.select("div.type a") if "/tur/" in (a.attrs.get("href") or "")]
109
95
 
110
- duration_el = secici.css_first("div.time")
111
- duration = None
112
- if duration_el:
113
- duration_text = duration_el.text(strip=True)
96
+ duration = None
97
+ duration_text = secici.select_text("div.time") or None
98
+ if duration_text:
114
99
  parts = duration_text.split()
115
100
  if len(parts) > 1:
116
101
  duration = parts[1].strip()
117
102
 
103
+ # Dizi mi kontrol et - sezon/bölüm linkleri var mı?
104
+ episodes = []
105
+ all_links = secici.select("a[href]")
106
+ for link in all_links:
107
+ href = link.attrs.get("href", "")
108
+ pairs = HTMLHelper(href).regex_all(r"/sezon-(\d+)/bolum-(\d+)")
109
+ if pairs:
110
+ season_no = int(pairs[0][0])
111
+ ep_no = int(pairs[0][1])
112
+
113
+ # Bölüm başlığını çıkar - text'ten gerçek ismi al
114
+ # Format: "22 Eylül 2014 / 44 dk /1. Sezon / 1. BölümPilot"
115
+ full_text = link.text(strip=True)
116
+ # "Bölüm" kelimesinden sonraki kısmı al
117
+ ep_title = ""
118
+ if "Bölüm" in full_text:
119
+ parts = full_text.split("Bölüm")
120
+ if len(parts) > 1:
121
+ ep_title = parts[-1].strip()
122
+
123
+ episodes.append(Episode(
124
+ season = season_no,
125
+ episode = ep_no,
126
+ title = ep_title,
127
+ url = self.fix_url(href)
128
+ ))
129
+
130
+ # Bölümler varsa SeriesInfo döndür
131
+ if episodes:
132
+ # Tekrar eden bölümleri kaldır
133
+ seen = set()
134
+ unique_episodes = []
135
+ for ep in episodes:
136
+ key = (ep.season, ep.episode)
137
+ if key not in seen:
138
+ seen.add(key)
139
+ unique_episodes.append(ep)
140
+
141
+ # Sırala
142
+ unique_episodes.sort(key=lambda x: (x.season or 0, x.episode or 0))
143
+
144
+ return SeriesInfo(
145
+ url = url,
146
+ poster = self.fix_url(poster) if poster else None,
147
+ title = self.clean_title(title),
148
+ description = description,
149
+ tags = tags,
150
+ rating = rating,
151
+ year = year,
152
+ actors = actors,
153
+ duration = duration,
154
+ episodes = unique_episodes
155
+ )
156
+
118
157
  return MovieInfo(
119
158
  url = url,
120
159
  poster = self.fix_url(poster) if poster else None,
@@ -129,12 +168,12 @@ class FilmMakinesi(PluginBase):
129
168
 
130
169
  async def load_links(self, url: str) -> list[ExtractResult]:
131
170
  istek = await self.httpx.get(url)
132
- secici = HTMLParser(istek.text)
171
+ secici = HTMLHelper(istek.text)
133
172
 
134
173
  response = []
135
174
 
136
175
  # Video parts linklerini ve etiketlerini al
137
- for link in secici.css("div.video-parts a[data-video_url]"):
176
+ for link in secici.select("div.video-parts a[data-video_url]"):
138
177
  video_url = link.attrs.get("data-video_url")
139
178
  label = link.text(strip=True) if link.text(strip=True) else ""
140
179
 
@@ -145,8 +184,7 @@ class FilmMakinesi(PluginBase):
145
184
 
146
185
  # Eğer video-parts yoksa iframe kullan
147
186
  if not response:
148
- iframe_el = secici.css_first("iframe")
149
- iframe_src = iframe_el.attrs.get("data-src") if iframe_el else None
187
+ iframe_src = secici.select_attr("iframe", "data-src")
150
188
  if iframe_src:
151
189
  data = await self.extract(iframe_src)
152
190
  if data:
@@ -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,16 +40,13 @@ 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(
@@ -65,16 +60,13 @@ class FilmModu(PluginBase):
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(
@@ -87,31 +79,25 @@ class FilmModu(PluginBase):
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")
82
+ secici = HTMLHelper(istek.text)
94
83
 
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 ""
84
+ org_title = secici.select_text("div.titles h1") or ""
85
+ alt_title = secici.select_text("div.titles h2") or ""
97
86
  title = f"{org_title} - {alt_title}" if alt_title else org_title
98
87
 
99
- poster_el = secici.css_first("img.img-responsive")
100
- poster = poster_el.attrs.get("src") if poster_el else None
88
+ poster = secici.select_attr("img.img-responsive", "src") if secici.select_attr("img.img-responsive", "src") else None
101
89
 
102
- desc_el = secici.css_first("p[itemprop='description']")
103
- description = desc_el.text(strip=True) if desc_el else None
90
+ description = secici.select_text("p[itemprop='description']") or None
104
91
 
105
- tags = [a.text(strip=True) for a in secici.css("a[href*='film-tur/']") if a.text(strip=True)]
92
+ tags = secici.select_all_text("a[href*='film-tur/']")
106
93
 
107
- year_el = secici.css_first("span[itemprop='dateCreated']")
108
- year = year_el.text(strip=True) if year_el else None
94
+ year = secici.select_text("span[itemprop='dateCreated']") or None
109
95
 
110
96
  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))
97
+ for a in secici.select("a[itemprop='actor']"):
98
+ name = secici.select_text("span", a)
99
+ if name:
100
+ actors.append(name)
115
101
 
116
102
  return MovieInfo(
117
103
  url = url,
@@ -125,9 +111,9 @@ class FilmModu(PluginBase):
125
111
 
126
112
  async def load_links(self, url: str) -> list[ExtractResult]:
127
113
  istek = await self.httpx.get(url)
128
- secici = HTMLParser(istek.text)
114
+ secici = HTMLHelper(istek.text)
129
115
 
130
- alternates = secici.css("div.alternates a")
116
+ alternates = secici.select("div.alternates a")
131
117
  if not alternates:
132
118
  return []
133
119
 
@@ -144,14 +130,14 @@ class FilmModu(PluginBase):
144
130
  alt_istek = await self.httpx.get(alt_link)
145
131
  alt_text = alt_istek.text
146
132
 
147
- vid_id = re.search(r"var videoId = '(.*)'", alt_text)
148
- vid_type = re.search(r"var videoType = '(.*)'", alt_text)
133
+ vid_id = HTMLHelper(alt_text).regex_first(r"var videoId = '([^']*)'")
134
+ vid_type = HTMLHelper(alt_text).regex_first(r"var videoType = '([^']*)'")
149
135
 
150
136
  if not vid_id or not vid_type:
151
137
  continue
152
138
 
153
139
  source_istek = await self.httpx.get(
154
- f"{self.main_url}/get-source?movie_id={vid_id[1]}&type={vid_type[1]}"
140
+ f"{self.main_url}/get-source?movie_id={vid_id}&type={vid_type}"
155
141
  )
156
142
  source_data = source_istek.json()
157
143
 
@@ -1,8 +1,7 @@
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, SeriesInfo, Episode, ExtractResult, Subtitle
4
- from selectolax.parser import HTMLParser
5
- import re, base64
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, Subtitle, HTMLHelper
4
+ import base64
6
5
 
7
6
  class FullHDFilm(PluginBase):
8
7
  name = "FullHDFilm"
@@ -48,16 +47,13 @@ class FullHDFilm(PluginBase):
48
47
  })
49
48
 
50
49
  istek = await self.httpx.get(page_url)
51
- secici = HTMLParser(istek.text)
50
+ secici = HTMLHelper(istek.text)
52
51
 
53
52
  results = []
54
- for veri in secici.css("div.movie-poster"):
55
- img_el = veri.css_first("img")
56
- link_el = veri.css_first("a")
57
-
58
- alt = img_el.attrs.get("alt") if img_el else None
59
- poster = img_el.attrs.get("src") if img_el else None
60
- href = link_el.attrs.get("href") if link_el else None
53
+ for veri in secici.select("div.movie-poster"):
54
+ alt = secici.select_attr("img", "alt", veri)
55
+ poster = secici.select_attr("img", "src", veri)
56
+ href = secici.select_attr("a", "href", veri)
61
57
 
62
58
  if alt and href:
63
59
  results.append(MainPageResult(
@@ -71,16 +67,13 @@ class FullHDFilm(PluginBase):
71
67
 
72
68
  async def search(self, query: str) -> list[SearchResult]:
73
69
  istek = await self.httpx.get(f"{self.main_url}/?s={query}")
74
- secici = HTMLParser(istek.text)
70
+ secici = HTMLHelper(istek.text)
75
71
 
76
72
  results = []
77
- for veri in secici.css("div.movie-poster"):
78
- img_el = veri.css_first("img")
79
- link_el = veri.css_first("a")
80
-
81
- alt = img_el.attrs.get("alt") if img_el else None
82
- poster = img_el.attrs.get("src") if img_el else None
83
- href = link_el.attrs.get("href") if link_el else None
73
+ for veri in secici.select("div.movie-poster"):
74
+ alt = secici.select_attr("img", "alt", veri)
75
+ poster = secici.select_attr("img", "src", veri)
76
+ href = secici.select_attr("a", "href", veri)
84
77
 
85
78
  if alt and href:
86
79
  results.append(SearchResult(
@@ -93,58 +86,59 @@ class FullHDFilm(PluginBase):
93
86
 
94
87
  async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
95
88
  istek = await self.httpx.get(url)
96
- secici = HTMLParser(istek.text)
89
+ secici = HTMLHelper(istek.text)
97
90
  html_text = istek.text
98
91
 
99
- title_el = secici.css_first("h1")
100
- title = title_el.text(strip=True) if title_el else ""
92
+ title = secici.select_text("h1") or ""
101
93
 
102
- poster_el = secici.css_first("div.poster img")
103
- poster = self.fix_url(poster_el.attrs.get("src")) if poster_el else ""
94
+ poster = secici.select_attr("div.poster img", "src") or ""
95
+ poster = self.fix_url(poster)
104
96
 
105
- actors_el = secici.css_first("div.oyuncular.info")
106
97
  actors = []
107
- if actors_el:
108
- actors_text = actors_el.text(strip=True)
109
- if actors_text:
110
- actors_text = actors_text.replace("Oyuncular:", "").strip()
111
- actors = [a.strip() for a in actors_text.split(",")]
98
+ actors_text = secici.select_text("div.oyuncular.info")
99
+ if actors_text:
100
+ actors_text = actors_text.replace("Oyuncular:", "").strip()
101
+ actors = [a.strip() for a in actors_text.split(",")]
102
+
103
+ # Year: önce div.yayin-tarihi.info dene
104
+ year = secici.extract_year("div.yayin-tarihi.info")
112
105
 
113
- # Year: re_first ile regex
114
- year_el = secici.css_first("div.yayin-tarihi.info")
115
- year_text = year_el.text(strip=True) if year_el else ""
116
- year_match = re.search(r"(\d{4})", year_text)
117
- year = year_match.group(1) if year_match else None
106
+ # Fallback: h1'in parent'ından (2019) formatında ara
107
+ if not year:
108
+ # Parent'ın tüm text içeriğinden yıl çıkar
109
+ title_text = secici.select_text("h1")
110
+ if title_text:
111
+ # h1 parent'ındaki (2019) gibi text'i bul
112
+ year = secici.regex_first(r"\((\d{4})\)")
118
113
 
119
- tags = [a.text(strip=True) for a in secici.css("div.tur.info a") if a.text(strip=True)]
114
+ tags = secici.select_all_text("div.tur.info a")
120
115
 
121
116
  # Rating: regex
122
- rating_el = secici.css_first("div.imdb")
123
- rating_text = rating_el.text(strip=True) if rating_el else ""
124
- rating_match = re.search(r"IMDb\s*([\d\.]+)", rating_text)
125
- rating = rating_match.group(1) if rating_match else None
117
+ rating_text = secici.select_text("div.imdb") or ""
118
+ rating = HTMLHelper(rating_text).regex_first(r"IMDb\s*([\d\.]+)")
126
119
 
127
- # Description: others div'den önceki div içindeki text
128
- # XPath yerine basit yaklaşım: özet sınıfı ile veya ilk paragraf
129
- description = None
130
- desc_el = secici.css_first("div.film")
131
- if desc_el:
132
- description = desc_el.text(strip=True)
120
+ # Description: önce div.film dene, yoksa og:description meta tag'ını kullan
121
+ description = secici.select_text("div.film")
122
+
123
+ # Fallback: og:description meta tag'ı
124
+ if not description:
125
+ og_desc = secici.select_attr("meta[property='og:description']", "content")
126
+ if og_desc:
127
+ description = og_desc
133
128
 
134
129
  # Kotlin referansı: URL'de -dizi kontrolü veya tags içinde "dizi" kontrolü
135
130
  is_series = "-dizi" in url.lower() or any("dizi" in tag.lower() for tag in tags)
136
131
 
137
132
  if is_series:
138
133
  episodes = []
139
- part_elements = secici.css("li.psec")
134
+ part_elements = secici.select("li.psec")
140
135
 
141
136
  # pdata değerlerini çıkar
142
- pdata_matches = re.findall(r"pdata\['([^']+)'\]\s*=\s*'([^']+)'", html_text)
137
+ pdata_matches = HTMLHelper(html_text).regex_all(r"pdata\['([^']+)'\]\s*=\s*'([^']+)'")
143
138
 
144
139
  for idx, el in enumerate(part_elements):
145
140
  part_id = el.attrs.get("id")
146
- link_el = el.css_first("a")
147
- part_name = link_el.text(strip=True) if link_el else None
141
+ part_name = secici.select_text("a", el)
148
142
 
149
143
  if not part_name:
150
144
  continue
@@ -154,11 +148,11 @@ class FullHDFilm(PluginBase):
154
148
  continue
155
149
 
156
150
  # Sezon ve bölüm numarası çıkar
157
- sz_match = re.search(r'(\d+)\s*sezon', part_id.lower() if part_id else "")
158
- ep_match = re.search(r'^(\d+)\.', part_name)
159
-
160
- sz_num = int(sz_match.group(1)) if sz_match else 1
161
- ep_num = int(ep_match.group(1)) if ep_match else idx + 1
151
+ sz_val = HTMLHelper(part_id.lower() if part_id else "").regex_first(r'(\d+)\s*sezon')
152
+ ep_val = HTMLHelper(part_name).regex_first(r'^(\d+)\.')
153
+
154
+ sz_num = int(sz_val) if sz_val and sz_val.isdigit() else 1
155
+ ep_num = int(ep_val) if ep_val and ep_val.isdigit() else idx + 1
162
156
 
163
157
  # pdata'dan video URL'si çık (varsa)
164
158
  video_url = url # Varsayılan olarak ana URL kullan
@@ -197,14 +191,14 @@ class FullHDFilm(PluginBase):
197
191
 
198
192
  def _get_iframe(self, source_code: str) -> str:
199
193
  """Base64 kodlu iframe'i çözümle"""
200
- match = re.search(r'<script[^>]*>(PCEtLWJhc2xpazp[^<]*)</script>', source_code)
201
- if not match:
194
+ script_val = HTMLHelper(source_code).regex_first(r'<script[^>]*>(PCEtLWJhc2xpazp[^<]*)</script>')
195
+ if not script_val:
202
196
  return ""
203
197
 
204
198
  try:
205
- decoded_html = base64.b64decode(match[1]).decode("utf-8")
206
- iframe_match = re.search(r'<iframe[^>]+src=["\']([^"\']+)["\']', decoded_html)
207
- return self.fix_url(iframe_match[1]) if iframe_match else ""
199
+ decoded_html = base64.b64decode(script_val).decode("utf-8")
200
+ iframe_src = HTMLHelper(decoded_html).regex_first(r'<iframe[^>]+src=["\']([^"\']+)["\']')
201
+ return self.fix_url(iframe_src) if iframe_src else ""
208
202
  except Exception:
209
203
  return ""
210
204
 
@@ -217,8 +211,9 @@ class FullHDFilm(PluginBase):
217
211
  ]
218
212
 
219
213
  for pattern in patterns:
220
- if match := re.search(pattern, source_code):
221
- return match[1]
214
+ val = HTMLHelper(source_code).regex_first(pattern)
215
+ if val:
216
+ return val
222
217
 
223
218
  return None
224
219