KekikStream 2.3.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.
Files changed (85) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +3 -2
  2. KekikStream/Core/Extractor/ExtractorLoader.py +8 -14
  3. KekikStream/Core/HTMLHelper.py +120 -49
  4. KekikStream/Core/Plugin/PluginBase.py +30 -12
  5. KekikStream/Core/Plugin/PluginLoader.py +12 -14
  6. KekikStream/Core/Plugin/PluginManager.py +2 -2
  7. KekikStream/Core/Plugin/PluginModels.py +0 -3
  8. KekikStream/Extractors/Abstream.py +27 -0
  9. KekikStream/Extractors/CloseLoad.py +30 -54
  10. KekikStream/Extractors/ContentX.py +27 -72
  11. KekikStream/Extractors/DonilasPlay.py +33 -77
  12. KekikStream/Extractors/DzenRu.py +10 -24
  13. KekikStream/Extractors/ExPlay.py +20 -38
  14. KekikStream/Extractors/Filemoon.py +21 -46
  15. KekikStream/Extractors/HDMomPlayer.py +30 -0
  16. KekikStream/Extractors/HDPlayerSystem.py +13 -31
  17. KekikStream/Extractors/HotStream.py +27 -0
  18. KekikStream/Extractors/JFVid.py +3 -24
  19. KekikStream/Extractors/JetTv.py +21 -34
  20. KekikStream/Extractors/JetV.py +55 -0
  21. KekikStream/Extractors/MailRu.py +11 -29
  22. KekikStream/Extractors/MixPlayHD.py +15 -28
  23. KekikStream/Extractors/MixTiger.py +17 -40
  24. KekikStream/Extractors/MolyStream.py +17 -21
  25. KekikStream/Extractors/Odnoklassniki.py +40 -104
  26. KekikStream/Extractors/PeaceMakerst.py +18 -45
  27. KekikStream/Extractors/PixelDrain.py +8 -16
  28. KekikStream/Extractors/PlayerFilmIzle.py +22 -41
  29. KekikStream/Extractors/RapidVid.py +21 -35
  30. KekikStream/Extractors/SetPlay.py +18 -43
  31. KekikStream/Extractors/SibNet.py +7 -17
  32. KekikStream/Extractors/Sobreatsesuyp.py +23 -45
  33. KekikStream/Extractors/TRsTX.py +23 -53
  34. KekikStream/Extractors/TurboImgz.py +7 -14
  35. KekikStream/Extractors/VCTPlay.py +10 -28
  36. KekikStream/Extractors/Veev.py +145 -0
  37. KekikStream/Extractors/VidBiz.py +62 -0
  38. KekikStream/Extractors/VidHide.py +58 -30
  39. KekikStream/Extractors/VidMoly.py +65 -99
  40. KekikStream/Extractors/VidMoxy.py +16 -27
  41. KekikStream/Extractors/VidPapi.py +24 -54
  42. KekikStream/Extractors/VideoSeyred.py +19 -40
  43. KekikStream/Extractors/Videostr.py +58 -0
  44. KekikStream/Extractors/Vidoza.py +18 -0
  45. KekikStream/Extractors/Vtbe.py +38 -0
  46. KekikStream/Extractors/YTDLP.py +2 -2
  47. KekikStream/Extractors/YildizKisaFilm.py +13 -31
  48. KekikStream/Extractors/Zeus.py +61 -0
  49. KekikStream/Plugins/BelgeselX.py +97 -77
  50. KekikStream/Plugins/DiziBox.py +28 -45
  51. KekikStream/Plugins/DiziMom.py +179 -0
  52. KekikStream/Plugins/DiziPal.py +95 -161
  53. KekikStream/Plugins/DiziYou.py +51 -147
  54. KekikStream/Plugins/Dizilla.py +40 -61
  55. KekikStream/Plugins/FilmBip.py +90 -39
  56. KekikStream/Plugins/FilmEkseni.py +199 -0
  57. KekikStream/Plugins/FilmMakinesi.py +72 -73
  58. KekikStream/Plugins/FilmModu.py +25 -35
  59. KekikStream/Plugins/Filmatek.py +184 -0
  60. KekikStream/Plugins/FilmciBaba.py +155 -0
  61. KekikStream/Plugins/FullHDFilmizlesene.py +16 -37
  62. KekikStream/Plugins/HDFilm.py +243 -0
  63. KekikStream/Plugins/HDFilmCehennemi.py +242 -189
  64. KekikStream/Plugins/JetFilmizle.py +101 -69
  65. KekikStream/Plugins/KultFilmler.py +138 -104
  66. KekikStream/Plugins/RecTV.py +52 -73
  67. KekikStream/Plugins/RoketDizi.py +18 -27
  68. KekikStream/Plugins/SelcukFlix.py +30 -48
  69. KekikStream/Plugins/SetFilmIzle.py +76 -104
  70. KekikStream/Plugins/SezonlukDizi.py +90 -94
  71. KekikStream/Plugins/Sinefy.py +195 -167
  72. KekikStream/Plugins/SinemaCX.py +148 -78
  73. KekikStream/Plugins/Sinezy.py +29 -31
  74. KekikStream/Plugins/SuperFilmGeldi.py +12 -17
  75. KekikStream/Plugins/UgurFilm.py +85 -38
  76. KekikStream/Plugins/Watch32.py +160 -0
  77. KekikStream/Plugins/YabanciDizi.py +176 -211
  78. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/METADATA +1 -1
  79. kekikstream-2.5.3.dist-info/RECORD +99 -0
  80. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/WHEEL +1 -1
  81. KekikStream/Plugins/FullHDFilm.py +0 -249
  82. kekikstream-2.3.9.dist-info/RECORD +0 -84
  83. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/entry_points.txt +0 -0
  84. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/licenses/LICENSE +0 -0
  85. {kekikstream-2.3.9.dist-info → kekikstream-2.5.3.dist-info}/top_level.txt +0 -0
@@ -1,6 +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, ExtractResult, HTMLHelper
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult, HTMLHelper
4
+ import asyncio
4
5
 
5
6
  class JetFilmizle(PluginBase):
6
7
  name = "JetFilmizle"
@@ -42,24 +43,22 @@ class JetFilmizle(PluginBase):
42
43
 
43
44
  results = []
44
45
  for veri in secici.select("article.movie"):
45
- # h2-h6 içindeki a linki
46
46
  title_text = None
47
47
  for h_tag in ["h2", "h3", "h4", "h5", "h6"]:
48
48
  title_text = secici.select_text(f"{h_tag} a", veri)
49
49
  if title_text:
50
50
  break
51
51
 
52
- href = secici.select_attr("a", "href", veri)
53
- poster = secici.select_poster("img", veri)
54
-
55
52
  title = self.clean_title(title_text) if title_text else None
53
+ href = secici.select_attr("a", "href", veri)
54
+ poster = secici.select_poster("img", veri)
56
55
 
57
56
  if title and href:
58
57
  results.append(MainPageResult(
59
58
  category = category,
60
59
  title = title,
61
60
  url = self.fix_url(href),
62
- poster = self.fix_url(poster) if poster else None,
61
+ poster = self.fix_url(poster),
63
62
  ))
64
63
 
65
64
  return results
@@ -74,23 +73,21 @@ class JetFilmizle(PluginBase):
74
73
 
75
74
  results = []
76
75
  for article in secici.select("article.movie"):
77
- # h2-h6 içindeki a linki
78
76
  title_text = None
79
77
  for h_tag in ["h2", "h3", "h4", "h5", "h6"]:
80
78
  title_text = secici.select_text(f"{h_tag} a", article)
81
79
  if title_text:
82
80
  break
83
81
 
84
- href = secici.select_attr("a", "href", article)
85
- poster = secici.select_poster("img", article)
86
-
87
82
  title = self.clean_title(title_text) if title_text else None
83
+ href = secici.select_attr("a", "href", article)
84
+ poster = secici.select_poster("img", article)
88
85
 
89
86
  if title and href:
90
87
  results.append(SearchResult(
91
88
  title = title,
92
89
  url = self.fix_url(href),
93
- poster = self.fix_url(poster) if poster else None,
90
+ poster = self.fix_url(poster),
94
91
  ))
95
92
 
96
93
  return results
@@ -99,81 +96,116 @@ class JetFilmizle(PluginBase):
99
96
  istek = await self.httpx.get(url)
100
97
  secici = HTMLHelper(istek.text)
101
98
 
102
- title = self.clean_title(secici.select_text("div.movie-exp-title")) if secici.select_text("div.movie-exp-title") else None
103
-
104
- poster = secici.select_poster("section.movie-exp img")
105
- poster = poster.strip() if poster else None
106
-
99
+ title = self.clean_title(secici.select_text("div.movie-exp-title"))
100
+ poster = secici.select_poster("section.movie-exp img")
107
101
  description = secici.select_text("section.movie-exp p.aciklama")
108
-
109
- tags = secici.select_all_text("section.movie-exp div.catss a")
110
-
111
- rating = secici.select_text("section.movie-exp div.imdb_puan span")
112
-
113
- year = secici.extract_year("div.yap")
114
-
115
- actors = secici.select_all_text("div[itemprop='actor'] a span")
116
- if not actors: # Fallback to img alt
117
- actors = [img.attrs.get("alt") for img in secici.select("div.oyuncular div.oyuncu img") if img.attrs.get("alt")]
118
-
119
- duration = secici.regex_first(r"(\d+)\s*dk", istek.text)
102
+ tags = secici.select_texts("section.movie-exp div.catss a")
103
+ rating = secici.select_text("section.movie-exp div.imdb_puan span")
104
+ year = secici.meta_value("Yayın Yılı")
105
+ actors = secici.select_texts("div[itemprop='actor'] a span") or [img.attrs.get("alt") for img in secici.select("div.oyuncular div.oyuncu img") if img.attrs.get("alt")]
106
+ duration = secici.meta_value("Süre")
107
+ duration = duration.split() if duration else None
108
+
109
+ total_minutes = 0
110
+ if duration:
111
+ for i, p in enumerate(duration):
112
+ if p == "saat":
113
+ total_minutes += int(duration[i-1]) * 60
114
+ elif p == "dakika":
115
+ total_minutes += int(duration[i-1])
120
116
 
121
117
  return MovieInfo(
122
118
  url = url,
123
- poster = self.fix_url(poster) if poster else None,
119
+ poster = self.fix_url(poster),
124
120
  title = title,
125
121
  description = description,
126
122
  tags = tags,
127
123
  rating = rating,
128
124
  year = year,
129
125
  actors = actors,
130
- duration = int(duration) if duration else None
126
+ duration = total_minutes if total_minutes else None
131
127
  )
132
128
 
133
- async def load_links(self, url: str) -> list[ExtractResult]:
134
- istek = await self.httpx.get(url)
135
- secici = HTMLHelper(istek.text)
136
-
129
+ async def _process_source(self, url: str, name: str, html: str | None) -> list[ExtractResult]:
137
130
  results = []
138
-
139
- # 1) Ana iframe'leri kontrol et
140
- for iframe in secici.select("iframe"):
141
- src = (iframe.attrs.get("src") or
142
- iframe.attrs.get("data-src") or
143
- iframe.attrs.get("data-lazy-src"))
144
-
145
- if src and src != "about:blank":
146
- iframe_url = self.fix_url(src)
147
- data = await self.extract(iframe_url)
148
- if data:
149
- results.append(data)
150
-
151
- # 2) Sayfa numaralarından linkleri topla (Fragman hariç)
152
- page_links = []
153
- for link in secici.select("a.post-page-numbers"):
154
- isim = secici.select_text("span", link) or ""
155
- if isim != "Fragman":
156
- href = link.attrs.get("href")
157
- if href:
158
- page_links.append((self.fix_url(href), isim))
159
-
160
- # 3) Her sayfa linkindeki iframe'leri bul
161
- for page_url, isim in page_links:
162
- try:
163
- page_resp = await self.httpx.get(page_url)
164
- page_sel = HTMLHelper(page_resp.text)
165
-
166
- for iframe in page_sel.select("div#movie iframe"):
131
+ try:
132
+ if html:
133
+ secici = HTMLHelper(html)
134
+ else:
135
+ resp = await self.httpx.get(url)
136
+ secici = HTMLHelper(resp.text)
137
+
138
+ # Iframe'leri bul
139
+ container = secici.select_first("div#movie") or secici.select_first("div.film-content")
140
+
141
+ if container:
142
+ for iframe in secici.select("iframe", container):
167
143
  src = (iframe.attrs.get("src") or
168
144
  iframe.attrs.get("data-src") or
169
145
  iframe.attrs.get("data-lazy-src"))
170
-
146
+
171
147
  if src and src != "about:blank":
172
148
  iframe_url = self.fix_url(src)
173
- data = await self.extract(iframe_url, prefix=isim)
149
+ # name_override KULLANMA, extractor kendi ismini versin
150
+ # Sonra biz düzenleriz
151
+ data = await self.extract(iframe_url)
152
+
174
153
  if data:
175
- results.append(data)
176
- except Exception:
177
- continue
154
+ items = data if isinstance(data, list) else [data]
155
+
156
+ for item in items:
157
+ # Sadece kalite bilgisi içeriyorsa ekle, yoksa sadece buton adını kullan
158
+ # Özellikle Zeus için kalite önemli (1080p, 720p)
159
+ # Diğerlerinde plugin adı (Apollo, JetPlay vb.) önemsiz
160
+
161
+ # Kalite kontrolü (basitçe)
162
+ quality_indicators = ["1080p", "720p", "480p", "360p", "240p", "144p", "4k", "2k"]
163
+ has_quality = any(q in item.name.lower() for q in quality_indicators)
164
+
165
+ if has_quality:
166
+ # Buton Adı | Extractor Adı (Kalite içerdiği için)
167
+ # Örn: Zeus | 1080p
168
+ # Eğer Extractor adı zaten Buton adını içeriyorsa (Zeus | 1080p -> Zeus) tekrar ekleme
169
+ if name.lower() not in item.name.lower():
170
+ item.name = f"{name} | {item.name}"
171
+ else:
172
+ # Kalite yoksa sadece Buton adını kullan
173
+ # Örn: Apollo | JetTv -> JetTv
174
+ item.name = name
175
+
176
+ results.append(item)
177
+ return results
178
+ except Exception:
179
+ return []
178
180
 
179
- return results
181
+ async def load_links(self, url: str) -> list[ExtractResult]:
182
+ istek = await self.httpx.get(url)
183
+ secici = HTMLHelper(istek.text)
184
+
185
+ sources = []
186
+ if film_part := secici.select_first("div.film_part"):
187
+ # Tüm spanları gez
188
+ for span in secici.select("span", film_part):
189
+ # Eğer bu span bir <a> etiketi içinde değilse, aktif kaynaktır
190
+ if span.parent.tag != "a":
191
+ name = span.text(strip=True)
192
+ if name:
193
+ sources.append((url, name, istek.text)) # html content var
194
+ break
195
+
196
+ # Diğer kaynak linkleri
197
+ for link in secici.select("a.post-page-numbers", film_part):
198
+ name = secici.select_text("span", link) or link.text(strip=True)
199
+ href = link.attrs.get("href")
200
+ if name != "Fragman" and href:
201
+ sources.append((self.fix_url(href), name, None)) # html yok, çekilecek
202
+
203
+ # Eğer film_part yoksa, sadece mevcut sayfayı tara (Tek part olabilir)
204
+ if not sources:
205
+ sources.append((url, "JetFilmizle", istek.text))
206
+
207
+ tasks = []
208
+ for page_url, source_name, html_content in sources:
209
+ tasks.append(self._process_source(page_url, source_name, html_content))
210
+
211
+ return [item for sublist in await asyncio.gather(*tasks) for item in sublist]
@@ -1,7 +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, HTMLHelper
4
- import base64
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, Subtitle, HTMLHelper
4
+ import base64, asyncio, contextlib
5
5
 
6
6
  class KultFilmler(PluginBase):
7
7
  name = "KultFilmler"
@@ -50,7 +50,7 @@ class KultFilmler(PluginBase):
50
50
  category = category,
51
51
  title = title,
52
52
  url = self.fix_url(href),
53
- poster = self.fix_url(poster) if poster else None,
53
+ poster = self.fix_url(poster),
54
54
  ))
55
55
 
56
56
  return results
@@ -69,7 +69,7 @@ class KultFilmler(PluginBase):
69
69
  results.append(SearchResult(
70
70
  title = title,
71
71
  url = self.fix_url(href),
72
- poster = self.fix_url(poster) if poster else None,
72
+ poster = self.fix_url(poster),
73
73
  ))
74
74
 
75
75
  return results
@@ -78,146 +78,180 @@ class KultFilmler(PluginBase):
78
78
  istek = await self.httpx.get(url)
79
79
  secici = HTMLHelper(istek.text)
80
80
 
81
- title = secici.select_attr("div.film-bilgileri img", "alt") or secici.select_attr("[property='og:title']", "content")
82
- poster = self.fix_url(secici.select_attr("[property='og:image']", "content")) if secici.select_attr("[property='og:image']", "content") else None
83
-
81
+ title = self.clean_title(secici.select_attr("div.film-bilgileri img", "alt") or secici.select_attr("[property='og:title']", "content"))
82
+ poster = self.fix_url(secici.select_attr("[property='og:image']", "content"))
84
83
  description = secici.select_text("div.description")
84
+ tags = secici.select_texts("ul.post-categories a")
85
+ year = secici.extract_year("li.release span a")
86
+ duration = int(secici.regex_first(r"(\d+)", secici.select_text("li.time span")) or 0)
87
+ rating = secici.regex_first(r"(\d+\.\d+|\d+)", secici.select_text("div.imdb-count"))
88
+ actors = secici.select_texts("div.actors a")
85
89
 
86
- tags = [a.text(strip=True) for a in secici.select("ul.post-categories a") if a.text(strip=True)]
87
-
88
- # HTML analizine göre güncellenen alanlar
89
- year = secici.select_text("li.release span a")
90
-
91
- time_text = secici.select_text("li.time span")
92
- duration = secici.regex_first(r"(\d+)", time_text) if time_text else None
93
-
94
- rating_text = secici.select_text("div.imdb-count")
95
- rating = secici.regex_first(r"(\d+\.\d+|\d+)", rating_text) if rating_text else None
96
-
97
- actors = [a.text(strip=True) for a in secici.select("div.actors a") if a.text(strip=True)]
98
-
99
- # Dizi mi kontrol et
100
90
  if "/dizi/" in url:
101
91
  episodes = []
102
92
  for bolum in secici.select("div.episode-box"):
103
- ep_href = secici.select_attr("div.name a", "href", bolum)
104
-
93
+ href = secici.select_attr("div.name a", "href", bolum)
105
94
  ssn_detail = secici.select_text("span.episodetitle", bolum) or ""
106
95
  ep_detail = secici.select_text("span.episodetitle b", bolum) or ""
107
-
108
- ep_name = f"{ssn_detail} - {ep_detail}"
109
-
110
- if ep_href:
111
- ep_season = secici.regex_first(r"(\d+)\.", ssn_detail)
112
- ep_episode = secici.regex_first(r"(\d+)\.", ep_detail)
113
-
114
- episodes.append(Episode(
115
- season = int(ep_season) if ep_season else 1,
116
- episode = int(ep_episode) if ep_episode else 1,
117
- title = ep_name.strip(" -"),
118
- url = self.fix_url(ep_href),
119
- ))
96
+ if href:
97
+ s, e = secici.extract_season_episode(f"{ssn_detail} {ep_detail}")
98
+ name = f"{ssn_detail} - {ep_detail}".strip(" -")
99
+ episodes.append(Episode(season=s or 1, episode=e or 1, title=name, url=self.fix_url(href)))
120
100
 
121
101
  return SeriesInfo(
122
102
  url = url,
123
103
  poster = poster,
124
- title = self.clean_title(title) if title else "",
104
+ title = title,
125
105
  description = description,
126
106
  tags = tags,
127
107
  year = year,
128
108
  actors = actors,
129
109
  rating = rating,
130
- episodes = episodes,
110
+ episodes = episodes
131
111
  )
132
112
 
133
113
  return MovieInfo(
134
114
  url = url,
135
115
  poster = poster,
136
- title = self.clean_title(title) if title else "",
116
+ title = title,
137
117
  description = description,
138
118
  tags = tags,
139
119
  year = year,
140
120
  rating = rating,
141
121
  actors = actors,
142
- duration = int(duration) if duration else None,
122
+ duration = duration
143
123
  )
144
124
 
145
- def _get_iframe(self, source_code: str) -> str:
146
- """Base64 kodlu iframe'i çözümle"""
147
- atob = HTMLHelper(source_code).regex_first(r"PHA\+[0-9a-zA-Z+/=]*")
148
- if not atob:
149
- return ""
125
+ def _decode_iframe(self, content: str) -> str | None:
126
+ """Base64 kodlanmış iframe verisini çözer"""
127
+ match = HTMLHelper(content).regex_first(r"PHA\+[0-9a-zA-Z+/=]*")
128
+ if not match:
129
+ return None
150
130
 
151
- # Padding düzelt
152
- padding = 4 - len(atob) % 4
153
- if padding < 4:
154
- atob = atob + "=" * padding
131
+ # Base64 Padding Fix
132
+ pad = len(match) % 4
133
+ if pad:
134
+ match += "=" * (4 - pad)
155
135
 
156
136
  try:
157
- decoded = base64.b64decode(atob).decode("utf-8")
158
- secici = HTMLHelper(decoded)
159
- iframe_src = secici.select_attr("iframe", "src")
160
- return self.fix_url(iframe_src) if iframe_src else ""
137
+ decoded = base64.b64decode(match).decode("utf-8")
138
+ src = HTMLHelper(decoded).select_attr("iframe", "src")
139
+ return self.fix_url(src) if src else None
161
140
  except Exception:
162
- return ""
141
+ return None
163
142
 
164
- def _extract_subtitle_url(self, source_code: str) -> str | None:
165
- """Altyazı URL'sini çıkar"""
166
- return HTMLHelper(source_code).regex_first(r"(https?://[^\s\"]+\.srt)")
167
-
168
- async def load_links(self, url: str) -> list[ExtractResult]:
169
- istek = await self.httpx.get(url)
170
- secici = HTMLHelper(istek.text)
171
-
172
- iframes = set()
173
-
174
- # Ana iframe
175
- main_frame = self._get_iframe(istek.text)
176
- if main_frame:
177
- iframes.add(main_frame)
178
-
179
- # Alternatif player'lar
180
- for player in secici.select("div.container#player"):
181
- iframe_src = secici.select_attr("iframe", "src", player)
182
- alt_iframe = self.fix_url(iframe_src) if iframe_src else None
183
- if alt_iframe:
184
- alt_istek = await self.httpx.get(alt_iframe)
185
- alt_frame = self._get_iframe(alt_istek.text)
186
- if alt_frame:
187
- iframes.add(alt_frame)
143
+ async def _resolve_alt_page(self, url: str, title: str) -> tuple[str | None, str]:
144
+ """Alternatif sayfa kaynak kodunu indirip iframe'i bulur"""
145
+ try:
146
+ res = await self.httpx.get(url)
147
+ return self._decode_iframe(res.text), title
148
+ except Exception:
149
+ return None, title
188
150
 
151
+ async def _extract_stream(self, iframe_url: str, title: str, subtitles: list[Subtitle]) -> list[ExtractResult]:
152
+ """Iframe üzerinden stream linklerini ayıklar"""
189
153
  results = []
190
154
 
191
- for iframe in iframes:
192
- subtitles = []
193
-
194
- # VidMoly özel işleme
195
- if "vidmoly" in iframe:
196
- headers = {
197
- "User-Agent" : "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36",
198
- "Sec-Fetch-Dest" : "iframe"
199
- }
200
- iframe_istek = await self.httpx.get(iframe, headers=headers)
201
- m3u_match = HTMLHelper(iframe_istek.text).regex_first(r'file:"([^"]+)"')
202
-
203
- if m3u_match:
155
+ # 1. VidMoly Özel Çözümleme(M3U)
156
+ if "vidmoly" in iframe_url:
157
+ with contextlib.suppress(Exception):
158
+ res = await self.httpx.get(
159
+ url = iframe_url,
160
+ headers = {
161
+ "User-Agent" : "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36",
162
+ "Sec-Fetch-Dest" : "iframe"
163
+ }
164
+ )
165
+ m3u = HTMLHelper(res.text).regex_first(r'file:"([^"]+)"')
166
+
167
+ if m3u:
204
168
  results.append(ExtractResult(
205
- name = "VidMoly",
206
- url = m3u_match,
169
+ name = title or "VidMoly",
170
+ url = m3u,
207
171
  referer = self.main_url,
208
- subtitles = []
172
+ subtitles = subtitles
209
173
  ))
210
- continue
211
174
 
212
- # Altyazı çıkar
213
- subtitle_url = self._extract_subtitle_url(url)
214
- if subtitle_url:
215
- subtitles.append(Subtitle(name="Türkçe", url=subtitle_url))
175
+ return results
176
+
177
+ # 2. Genel Extractor Kullanımı
178
+ with contextlib.suppress(Exception):
179
+ extracted = await self.extract(iframe_url)
180
+ if not extracted:
181
+ return []
182
+
183
+ items = extracted if isinstance(extracted, list) else [extracted]
184
+ for item in items:
185
+ # İsim ve altyazı bilgilerini güncelle
186
+ # Orijinal extractor ismini ezmek için title kullan
187
+ if title:
188
+ item.name = title
216
189
 
217
- data = await self.extract(iframe)
218
- if data:
219
- # ExtractResult objesi immutable, yeni bir kopya oluştur
220
- updated_data = data.model_copy(update={"subtitles": subtitles}) if subtitles else data
221
- results.append(updated_data)
190
+ # Varsa altyazıları ekle
191
+ if subtitles:
192
+ # Copy update daha güvenli (Pydantic model)
193
+ if hasattr(item, "model_copy"):
194
+ item = item.model_copy(update={"subtitles": subtitles})
195
+ else:
196
+ item.subtitles = subtitles
197
+
198
+ results.append(item)
222
199
 
223
200
  return results
201
+
202
+ async def load_links(self, url: str) -> list[ExtractResult]:
203
+ response = await self.httpx.get(url)
204
+ source = response.text
205
+ helper = HTMLHelper(source)
206
+
207
+ # Altyazı Bul
208
+ sub_url = helper.regex_first(r"(https?://[^\s\"]+\.srt)")
209
+ subtitles = [Subtitle(name="Türkçe", url=sub_url)] if sub_url else []
210
+
211
+ # İşlenecek kaynakları topla: (Iframe_URL, Başlık)
212
+ sources = []
213
+
214
+ # A) Ana Player
215
+ main_iframe = self._decode_iframe(source)
216
+ if main_iframe:
217
+ p_name = helper.select_text("div.parts-middle div.part.active div.part-name") or None
218
+ p_lang = helper.select_attr("div.parts-middle div.part.active div.part-lang span", "title")
219
+ full_title = f"{p_name} | {p_lang}" if p_lang else p_name
220
+ sources.append((main_iframe, full_title))
221
+
222
+ # B) Alternatif Playerlar (Link Çözümleme Gerektirir)
223
+ alt_tasks = []
224
+ for link in helper.select("div.parts-middle a.post-page-numbers"):
225
+ href = link.attrs.get("href")
226
+ if not href:
227
+ continue
228
+
229
+ a_name = helper.select_text("div.part-name", link) or "Alternatif"
230
+ a_lang = helper.select_attr("div.part-lang span", "title", link)
231
+ full_title = f"{a_name} | {a_lang}" if a_lang else a_name
232
+
233
+ alt_tasks.append(self._resolve_alt_page(self.fix_url(href), full_title))
234
+
235
+ if alt_tasks:
236
+ resolved_alts = await asyncio.gather(*alt_tasks)
237
+ for iframe, title in resolved_alts:
238
+ if iframe:
239
+ sources.append((iframe, title))
240
+
241
+ # 3. Tüm kaynakları paralel işle (Extract)
242
+ if not sources:
243
+ return []
244
+
245
+ extract_tasks = [
246
+ self._extract_stream(iframe, title, subtitles)
247
+ for iframe, title in sources
248
+ ]
249
+
250
+ results_groups = await asyncio.gather(*extract_tasks)
251
+
252
+ # Sonuçları düzleştir
253
+ final_results = []
254
+ for group in results_groups:
255
+ final_results.extend(group)
256
+
257
+ return final_results