KekikStream 1.7.1__py3-none-any.whl → 2.2.0__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 (88) hide show
  1. KekikStream/Core/Extractor/ExtractorBase.py +20 -9
  2. KekikStream/Core/Extractor/ExtractorLoader.py +25 -17
  3. KekikStream/Core/Extractor/ExtractorManager.py +53 -9
  4. KekikStream/Core/Extractor/ExtractorModels.py +5 -7
  5. KekikStream/Core/Extractor/YTDLPCache.py +35 -0
  6. KekikStream/Core/Media/MediaHandler.py +44 -26
  7. KekikStream/Core/Media/MediaManager.py +0 -3
  8. KekikStream/Core/Plugin/PluginBase.py +82 -22
  9. KekikStream/Core/Plugin/PluginLoader.py +11 -7
  10. KekikStream/Core/Plugin/PluginModels.py +25 -26
  11. KekikStream/Core/__init__.py +1 -0
  12. KekikStream/Extractors/CloseLoad.py +21 -7
  13. KekikStream/Extractors/ContentX.py +21 -6
  14. KekikStream/Extractors/DonilasPlay.py +86 -0
  15. KekikStream/Extractors/DzenRu.py +38 -0
  16. KekikStream/Extractors/ExPlay.py +53 -0
  17. KekikStream/Extractors/Filemoon.py +78 -0
  18. KekikStream/Extractors/HDPlayerSystem.py +41 -0
  19. KekikStream/Extractors/JetTv.py +45 -0
  20. KekikStream/Extractors/MailRu.py +3 -4
  21. KekikStream/Extractors/MixPlayHD.py +2 -3
  22. KekikStream/Extractors/MixTiger.py +57 -0
  23. KekikStream/Extractors/MolyStream.py +5 -5
  24. KekikStream/Extractors/Odnoklassniki.py +13 -7
  25. KekikStream/Extractors/PeaceMakerst.py +10 -5
  26. KekikStream/Extractors/PixelDrain.py +1 -2
  27. KekikStream/Extractors/PlayerFilmIzle.py +65 -0
  28. KekikStream/Extractors/RapidVid.py +23 -8
  29. KekikStream/Extractors/SetPlay.py +66 -0
  30. KekikStream/Extractors/SetPrime.py +45 -0
  31. KekikStream/Extractors/SibNet.py +2 -3
  32. KekikStream/Extractors/Sobreatsesuyp.py +4 -5
  33. KekikStream/Extractors/TRsTX.py +4 -5
  34. KekikStream/Extractors/TauVideo.py +2 -3
  35. KekikStream/Extractors/TurboImgz.py +2 -3
  36. KekikStream/Extractors/TurkeyPlayer.py +34 -0
  37. KekikStream/Extractors/VCTPlay.py +41 -0
  38. KekikStream/Extractors/VidHide.py +81 -0
  39. KekikStream/Extractors/VidMoly.py +55 -34
  40. KekikStream/Extractors/VidMoxy.py +2 -3
  41. KekikStream/Extractors/VidPapi.py +89 -0
  42. KekikStream/Extractors/VideoSeyred.py +3 -4
  43. KekikStream/Extractors/YTDLP.py +211 -0
  44. KekikStream/Extractors/YildizKisaFilm.py +41 -0
  45. KekikStream/Plugins/BelgeselX.py +196 -0
  46. KekikStream/Plugins/DiziBox.py +25 -34
  47. KekikStream/Plugins/DiziPal.py +24 -35
  48. KekikStream/Plugins/DiziYou.py +54 -37
  49. KekikStream/Plugins/Dizilla.py +66 -46
  50. KekikStream/Plugins/FilmBip.py +142 -0
  51. KekikStream/Plugins/FilmMakinesi.py +36 -28
  52. KekikStream/Plugins/FilmModu.py +20 -24
  53. KekikStream/Plugins/FullHDFilm.py +220 -0
  54. KekikStream/Plugins/FullHDFilmizlesene.py +9 -15
  55. KekikStream/Plugins/HDFilmCehennemi.py +141 -69
  56. KekikStream/Plugins/JetFilmizle.py +85 -52
  57. KekikStream/Plugins/KultFilmler.py +217 -0
  58. KekikStream/Plugins/RecTV.py +22 -34
  59. KekikStream/Plugins/RoketDizi.py +222 -0
  60. KekikStream/Plugins/SelcukFlix.py +328 -0
  61. KekikStream/Plugins/SetFilmIzle.py +252 -0
  62. KekikStream/Plugins/SezonlukDizi.py +54 -21
  63. KekikStream/Plugins/SineWix.py +17 -29
  64. KekikStream/Plugins/Sinefy.py +241 -0
  65. KekikStream/Plugins/SinemaCX.py +154 -0
  66. KekikStream/Plugins/Sinezy.py +143 -0
  67. KekikStream/Plugins/SuperFilmGeldi.py +130 -0
  68. KekikStream/Plugins/UgurFilm.py +13 -19
  69. KekikStream/__init__.py +47 -56
  70. KekikStream/requirements.txt +3 -4
  71. kekikstream-2.2.0.dist-info/METADATA +312 -0
  72. kekikstream-2.2.0.dist-info/RECORD +81 -0
  73. KekikStream/Extractors/FourCX.py +0 -7
  74. KekikStream/Extractors/FourPichive.py +0 -7
  75. KekikStream/Extractors/FourPlayRu.py +0 -7
  76. KekikStream/Extractors/HDStreamAble.py +0 -7
  77. KekikStream/Extractors/Hotlinger.py +0 -7
  78. KekikStream/Extractors/OkRuHTTP.py +0 -7
  79. KekikStream/Extractors/OkRuSSL.py +0 -7
  80. KekikStream/Extractors/Pichive.py +0 -7
  81. KekikStream/Extractors/PlayRu.py +0 -7
  82. KekikStream/Extractors/VidMolyMe.py +0 -7
  83. kekikstream-1.7.1.dist-info/METADATA +0 -109
  84. kekikstream-1.7.1.dist-info/RECORD +0 -63
  85. {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/WHEEL +0 -0
  86. {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/entry_points.txt +0 -0
  87. {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/licenses/LICENSE +0 -0
  88. {kekikstream-1.7.1.dist-info → kekikstream-2.2.0.dist-info}/top_level.txt +0 -0
@@ -1,14 +1,15 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import kekik_cache, PluginBase, MainPageResult, SearchResult, MovieInfo
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, ExtractResult
4
4
  from parsel import Selector
5
+ import re
5
6
 
6
7
  class JetFilmizle(PluginBase):
7
8
  name = "JetFilmizle"
8
9
  language = "tr"
9
10
  main_url = "https://jetfilmizle.website"
10
11
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
11
- description = "Binlerce Film İzleme Seçeneğiyle En İyi Film İzleme Sitesi"
12
+ description = "Film izle, Yerli, Yabancı film izle, Türkçe dublaj, alt yazılı seçenekleriyle ödül almış filmleri Full HD kalitesiyle ve jetfilmizle hızıyla donmadan ücretsizce izleyebilirsiniz."
12
13
 
13
14
  main_page = {
14
15
  f"{main_url}/page/" : "Son Filmler",
@@ -16,12 +17,29 @@ class JetFilmizle(PluginBase):
16
17
  f"{main_url}/editorun-secimi/page/" : "Editörün Seçimi",
17
18
  f"{main_url}/turk-film-izle/page/" : "Türk Filmleri",
18
19
  f"{main_url}/cizgi-filmler-izle/page/" : "Çizgi Filmler",
19
- f"{main_url}/kategoriler/yesilcam-filmleri-izlee/page/" : "Yeşilçam Filmleri"
20
+ f"{main_url}/kategoriler/yesilcam-filmleri-izlee/page/" : "Yeşilçam Filmleri",
21
+ f"{main_url}/film-turu/aile-filmleri-izle/page/" : "Aile Filmleri",
22
+ f"{main_url}/film-turu/aksiyon-filmleri/page/" : "Aksiyon Filmleri",
23
+ f"{main_url}/film-turu/animasyon-filmler-izle/page/" : "Animasyon Filmleri",
24
+ f"{main_url}/film-turu/bilim-kurgu-filmler/page/" : "Bilim Kurgu Filmleri",
25
+ f"{main_url}/film-turu/dram-filmleri-izle/page/" : "Dram Filmleri",
26
+ f"{main_url}/film-turu/fantastik-filmleri-izle/page/" : "Fantastik Filmler",
27
+ f"{main_url}/film-turu/gerilim-filmleri/page/" : "Gerilim Filmleri",
28
+ f"{main_url}/film-turu/gizem-filmleri/page/" : "Gizem Filmleri",
29
+ f"{main_url}/film-turu/komedi-film-full-izle/page/" : "Komedi Filmleri",
30
+ f"{main_url}/film-turu/korku-filmleri-izle/page/" : "Korku Filmleri",
31
+ f"{main_url}/film-turu/macera-filmleri/page/" : "Macera Filmleri",
32
+ f"{main_url}/film-turu/muzikal/page/" : "Müzikal Filmler",
33
+ f"{main_url}/film-turu/polisiye/page/" : "Polisiye Filmler",
34
+ f"{main_url}/film-turu/romantik-film-izle/page/" : "Romantik Filmler",
35
+ f"{main_url}/film-turu/savas-filmi-izle/page/" : "Savaş Filmleri",
36
+ f"{main_url}/film-turu/spor/page/" : "Spor Filmleri",
37
+ f"{main_url}/film-turu/suc-filmleri/page/" : "Suç Filmleri",
38
+ f"{main_url}/film-turu/tarihi-filmler/page/" : "Tarihi Filmleri",
20
39
  }
21
40
 
22
- #@kekik_cache(ttl=60*60)
23
41
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
24
- istek = await self.cffi.get(f"{url}{page}", allow_redirects=True)
42
+ istek = await self.httpx.get(f"{url}{page}", follow_redirects=True)
25
43
  secici = Selector(istek.text)
26
44
 
27
45
  return [
@@ -34,9 +52,8 @@ class JetFilmizle(PluginBase):
34
52
  for veri in secici.css("article.movie") if veri.css("h2 a::text, h3 a::text, h4 a::text, h5 a::text, h6 a::text").get()
35
53
  ]
36
54
 
37
- #@kekik_cache(ttl=60*60)
38
55
  async def search(self, query: str) -> list[SearchResult]:
39
- istek = await self.cffi.post(
56
+ istek = await self.httpx.post(
40
57
  url = f"{self.main_url}/filmara.php",
41
58
  data = {"s": query},
42
59
  headers = {"Referer": f"{self.main_url}/"}
@@ -60,18 +77,31 @@ class JetFilmizle(PluginBase):
60
77
 
61
78
  return results
62
79
 
63
- #@kekik_cache(ttl=60*60)
64
80
  async def load_item(self, url: str) -> MovieInfo:
65
- istek = await self.cffi.get(url)
81
+ istek = await self.httpx.get(url)
66
82
  secici = Selector(istek.text)
67
83
 
68
84
  title = self.clean_title(secici.css("div.movie-exp-title::text").get())
69
- poster = secici.css("section.movie-exp img::attr(data-src), section.movie-exp img::attr(src)").get().strip()
70
- description = secici.css("section.movie-exp p.aciklama::text").get().strip()
85
+ poster_raw = secici.css("section.movie-exp img::attr(data-src), section.movie-exp img::attr(src)").get()
86
+ poster = poster_raw.strip() if poster_raw else None
87
+
88
+ desc_raw = secici.css("section.movie-exp p.aciklama::text").get()
89
+ description = desc_raw.strip() if desc_raw else None
90
+
71
91
  tags = secici.css("section.movie-exp div.catss a::text").getall()
72
- rating = secici.css("section.movie-exp div.imdb_puan span::text").get().strip()
73
- year = secici.xpath("//div[@class='yap' and (contains(., 'Vizyon') or contains(., 'Yapım'))]/text()").get()
74
- year = year.strip() if year else None
92
+
93
+ rating_raw = secici.css("section.movie-exp div.imdb_puan span::text").get()
94
+ rating = rating_raw.strip() if rating_raw else None
95
+
96
+
97
+ # Year - div.yap içinde 4 haneli sayı ara
98
+ year_div = secici.xpath("//div[@class='yap' and (contains(., 'Vizyon') or contains(., 'Yapım'))]/text()").get()
99
+ year = None
100
+ if year_div:
101
+ year_match = re.search(r'(\d{4})', year_div.strip())
102
+ if year_match:
103
+ year = year_match.group(1)
104
+
75
105
  actors = secici.css("div[itemprop='actor'] a span::text").getall()
76
106
 
77
107
  return MovieInfo(
@@ -85,47 +115,50 @@ class JetFilmizle(PluginBase):
85
115
  actors = actors
86
116
  )
87
117
 
88
- #@kekik_cache(ttl=15*60)
89
- async def load_links(self, url: str) -> list[dict]:
90
- istek = await self.cffi.get(url)
118
+ async def load_links(self, url: str) -> list[ExtractResult]:
119
+ istek = await self.httpx.get(url)
91
120
  secici = Selector(istek.text)
92
121
 
93
- iframes = []
94
- if main_iframe := secici.css("div#movie iframe::attr(data-src), div#movie iframe::attr(data), div#movie iframe::attr(src)").get():
95
- iframes.append(self.fix_url(main_iframe))
122
+ results = []
96
123
 
97
- for part in secici.css("div.film_part a"):
98
- part_href = part.attrib.get("href")
99
- if not part_href:
124
+ # 1) Ana iframe'leri kontrol et
125
+ for iframe in secici.css("iframe"):
126
+ src = (iframe.css("::attr(src)").get() or
127
+ iframe.css("::attr(data-src)").get() or
128
+ iframe.css("::attr(data-lazy-src)").get())
129
+
130
+ if src and src != "about:blank":
131
+ iframe_url = self.fix_url(src)
132
+ data = await self.extract(iframe_url)
133
+ if data:
134
+ results.append(data)
135
+
136
+ # 2) Sayfa numaralarından linkleri topla (Fragman hariç)
137
+ page_links = []
138
+ for link in secici.css("a.post-page-numbers"):
139
+ isim = link.css("span::text").get() or ""
140
+ if isim != "Fragman":
141
+ href = link.css("::attr(href)").get()
142
+ if href:
143
+ page_links.append((self.fix_url(href), isim))
144
+
145
+ # 3) Her sayfa linkindeki iframe'leri bul
146
+ for page_url, isim in page_links:
147
+ try:
148
+ page_resp = await self.httpx.get(page_url)
149
+ page_sel = Selector(page_resp.text)
150
+
151
+ for iframe in page_sel.css("div#movie iframe"):
152
+ src = (iframe.css("::attr(src)").get() or
153
+ iframe.css("::attr(data-src)").get() or
154
+ iframe.css("::attr(data-lazy-src)").get())
155
+
156
+ if src and src != "about:blank":
157
+ iframe_url = self.fix_url(src)
158
+ data = await self.extract(iframe_url, prefix=isim)
159
+ if data:
160
+ results.append(data)
161
+ except Exception:
100
162
  continue
101
163
 
102
- part_istek = await self.cffi.get(part_href)
103
- part_secici = Selector(part_istek.text)
104
-
105
- if iframe := part_secici.css("div#movie iframe::attr(data-src), div#movie iframe::attr(data), div#movie iframe::attr(src)").get():
106
- iframes.append(self.fix_url(iframe))
107
- else:
108
- for link in part_secici.css("div#movie p a"):
109
- if download_link := link.attrib.get("href"):
110
- iframes.append(self.fix_url(download_link))
111
-
112
- processed_iframes = []
113
- for iframe in iframes:
114
- if "jetv.xyz" in iframe:
115
- jetv_istek = await self.cffi.get(iframe)
116
- jetv_secici = Selector(jetv_istek.text)
117
-
118
- if jetv_iframe := jetv_secici.css("iframe::attr(src)").get():
119
- processed_iframes.append(self.fix_url(jetv_iframe))
120
- else:
121
- processed_iframes.append(iframe)
122
-
123
- results = []
124
- for idx, iframe in enumerate(processed_iframes):
125
- extractor = self.ex_manager.find_extractor(iframe)
126
- results.append({
127
- "url" : iframe,
128
- "name" : f"{extractor.name if extractor else f'Player {idx + 1}'}"
129
- })
130
-
131
164
  return results
@@ -0,0 +1,217 @@
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, Subtitle
4
+ from parsel import Selector
5
+ import re, base64
6
+
7
+ class KultFilmler(PluginBase):
8
+ name = "KultFilmler"
9
+ language = "tr"
10
+ main_url = "https://kultfilmler.net"
11
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
+ description = "Kült Filmler özenle en iyi filmleri derler ve iyi bir altyazılı film izleme deneyimi sunmayı amaçlar. Reklamsız 1080P Altyazılı Film izle..."
13
+
14
+ main_page = {
15
+ f"{main_url}/category/aile-filmleri-izle" : "Aile",
16
+ f"{main_url}/category/aksiyon-filmleri-izle" : "Aksiyon",
17
+ f"{main_url}/category/animasyon-filmleri-izle" : "Animasyon",
18
+ f"{main_url}/category/belgesel-izle" : "Belgesel",
19
+ f"{main_url}/category/bilim-kurgu-filmleri-izle": "Bilim Kurgu",
20
+ f"{main_url}/category/biyografi-filmleri-izle" : "Biyografi",
21
+ f"{main_url}/category/dram-filmleri-izle" : "Dram",
22
+ f"{main_url}/category/fantastik-filmleri-izle" : "Fantastik",
23
+ f"{main_url}/category/gerilim-filmleri-izle" : "Gerilim",
24
+ f"{main_url}/category/gizem-filmleri-izle" : "Gizem",
25
+ f"{main_url}/category/kara-filmleri-izle" : "Kara Film",
26
+ f"{main_url}/category/kisa-film-izle" : "Kısa Metraj",
27
+ f"{main_url}/category/komedi-filmleri-izle" : "Komedi",
28
+ f"{main_url}/category/korku-filmleri-izle" : "Korku",
29
+ f"{main_url}/category/macera-filmleri-izle" : "Macera",
30
+ f"{main_url}/category/muzik-filmleri-izle" : "Müzik",
31
+ f"{main_url}/category/polisiye-filmleri-izle" : "Polisiye",
32
+ f"{main_url}/category/romantik-filmleri-izle" : "Romantik",
33
+ f"{main_url}/category/savas-filmleri-izle" : "Savaş",
34
+ f"{main_url}/category/suc-filmleri-izle" : "Suç",
35
+ f"{main_url}/category/tarih-filmleri-izle" : "Tarih",
36
+ f"{main_url}/category/yerli-filmleri-izle" : "Yerli",
37
+ }
38
+
39
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
40
+ istek = await self.httpx.get(url)
41
+ secici = Selector(istek.text)
42
+
43
+ results = []
44
+ for veri in secici.css("div.col-md-12 div.movie-box"):
45
+ title = veri.css("div.img img::attr(alt)").get()
46
+ href = self.fix_url(veri.css("a::attr(href)").get())
47
+ poster = self.fix_url(veri.css("div.img img::attr(src)").get())
48
+
49
+ if title and href:
50
+ results.append(MainPageResult(
51
+ category = category,
52
+ title = title,
53
+ url = href,
54
+ poster = poster,
55
+ ))
56
+
57
+ return results
58
+
59
+ async def search(self, query: str) -> list[SearchResult]:
60
+ istek = await self.httpx.get(f"{self.main_url}?s={query}")
61
+ secici = Selector(istek.text)
62
+
63
+ results = []
64
+ for veri in secici.css("div.movie-box"):
65
+ title = veri.css("div.img img::attr(alt)").get()
66
+ href = self.fix_url(veri.css("a::attr(href)").get())
67
+ poster = self.fix_url(veri.css("div.img img::attr(src)").get())
68
+
69
+ if title and href:
70
+ results.append(SearchResult(
71
+ title = title,
72
+ url = href,
73
+ poster = poster,
74
+ ))
75
+
76
+ return results
77
+
78
+ async def load_item(self, url: str) -> MovieInfo | SeriesInfo:
79
+ istek = await self.httpx.get(url)
80
+ secici = Selector(istek.text)
81
+
82
+ title = secici.css("div.film-bilgileri img::attr(alt)").get() or secici.css("[property='og:title']::attr(content)").get()
83
+ poster = self.fix_url(secici.css("[property='og:image']::attr(content)").get())
84
+ description = secici.css("div.description::text").get()
85
+ tags = secici.css("ul.post-categories a::text").getall()
86
+ # HTML analizine göre güncellenen alanlar
87
+ year = secici.css("li.release span a::text").get()
88
+ duration = secici.css("li.time span::text").re_first(r"(\d+)")
89
+ rating = secici.css("div.imdb-count::text").get()
90
+ actors = secici.css("div.actors a::text").getall()
91
+ if rating:
92
+ rating = rating.strip()
93
+
94
+ # Dizi mi kontrol et
95
+ if "/dizi/" in url:
96
+ episodes = []
97
+ for bolum in secici.css("div.episode-box"):
98
+ ep_href = self.fix_url(bolum.css("div.name a::attr(href)").get())
99
+ ssn_detail = bolum.css("span.episodetitle::text").get() or ""
100
+ ep_detail = bolum.css("span.episodetitle b::text").get() or ""
101
+ ep_name = f"{ssn_detail} - {ep_detail}"
102
+
103
+ if ep_href:
104
+ ep_season = re.search(r"(\d+)\.", ssn_detail)
105
+ ep_episode = re.search(r"(\d+)\.", ep_detail)
106
+
107
+ episodes.append(Episode(
108
+ season = int(ep_season[1]) if ep_season else 1,
109
+ episode = int(ep_episode[1]) if ep_episode else 1,
110
+ title = ep_name.strip(" -"),
111
+ url = ep_href,
112
+ ))
113
+
114
+ return SeriesInfo(
115
+ url = url,
116
+ poster = poster,
117
+ title = self.clean_title(title) if title else "",
118
+ description = description,
119
+ tags = tags,
120
+ year = year,
121
+ actors = actors,
122
+ rating = rating,
123
+ episodes = episodes,
124
+ )
125
+
126
+ return MovieInfo(
127
+ url = url,
128
+ poster = poster,
129
+ title = self.clean_title(title) if title else "",
130
+ description = description,
131
+ tags = tags,
132
+ year = year,
133
+ rating = rating,
134
+ actors = actors,
135
+ duration = int(duration) if duration else None,
136
+ )
137
+
138
+ def _get_iframe(self, source_code: str) -> str:
139
+ """Base64 kodlu iframe'i çözümle"""
140
+ atob_match = re.search(r"PHA\+[0-9a-zA-Z+/=]*", source_code)
141
+ if not atob_match:
142
+ return ""
143
+
144
+ atob = atob_match.group()
145
+
146
+ # Padding düzelt
147
+ padding = 4 - len(atob) % 4
148
+ if padding < 4:
149
+ atob = atob + "=" * padding
150
+
151
+ try:
152
+ decoded = base64.b64decode(atob).decode("utf-8")
153
+ secici = Selector(text=decoded)
154
+ return self.fix_url(secici.css("iframe::attr(src)").get()) or ""
155
+ except Exception:
156
+ return ""
157
+
158
+ def _extract_subtitle_url(self, source_code: str) -> str | None:
159
+ """Altyazı URL'sini çıkar"""
160
+ match = re.search(r"(https?://[^\s\"]+\.srt)", source_code)
161
+ return match[1] if match else None
162
+
163
+ async def load_links(self, url: str) -> list[ExtractResult]:
164
+ istek = await self.httpx.get(url)
165
+ secici = Selector(istek.text)
166
+
167
+ iframes = set()
168
+
169
+ # Ana iframe
170
+ main_frame = self._get_iframe(istek.text)
171
+ if main_frame:
172
+ iframes.add(main_frame)
173
+
174
+ # Alternatif player'lar
175
+ for player in secici.css("div.container#player"):
176
+ alt_iframe = self.fix_url(player.css("iframe::attr(src)").get())
177
+ if alt_iframe:
178
+ alt_istek = await self.httpx.get(alt_iframe)
179
+ alt_frame = self._get_iframe(alt_istek.text)
180
+ if alt_frame:
181
+ iframes.add(alt_frame)
182
+
183
+ results = []
184
+
185
+ for iframe in iframes:
186
+ subtitles = []
187
+
188
+ # VidMoly özel işleme
189
+ if "vidmoly" in iframe:
190
+ headers = {
191
+ "User-Agent" : "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36",
192
+ "Sec-Fetch-Dest" : "iframe"
193
+ }
194
+ iframe_istek = await self.httpx.get(iframe, headers=headers)
195
+ m3u_match = re.search(r'file:"([^"]+)"', iframe_istek.text)
196
+
197
+ if m3u_match:
198
+ results.append(ExtractResult(
199
+ name = "VidMoly",
200
+ url = m3u_match[1],
201
+ referer = self.main_url,
202
+ subtitles = []
203
+ ))
204
+ continue
205
+
206
+ # Altyazı çıkar
207
+ subtitle_url = self._extract_subtitle_url(url)
208
+ if subtitle_url:
209
+ subtitles.append(Subtitle(name="Türkçe", url=subtitle_url))
210
+
211
+ data = await self.extract(iframe)
212
+ if data:
213
+ # ExtractResult objesi immutable, yeni bir kopya oluştur
214
+ updated_data = data.model_copy(update={"subtitles": subtitles}) if subtitles else data
215
+ results.append(updated_data)
216
+
217
+ return results
@@ -1,6 +1,6 @@
1
1
  # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
2
 
3
- from KekikStream.Core import kekik_cache, PluginBase, MainPageResult, SearchResult, MovieInfo, Episode, SeriesInfo, ExtractResult, Subtitle
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, Episode, SeriesInfo, ExtractResult
4
4
  from json import dumps, loads
5
5
  import re
6
6
 
@@ -30,10 +30,9 @@ class RecTV(PluginBase):
30
30
  f"{main_url}/api/movie/by/filtres/5/created/SAYFA/{sw_key}/" : "Romantik"
31
31
  }
32
32
 
33
- #@kekik_cache(ttl=60*60)
34
33
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
35
- self.cffi.headers.update({"user-agent": "okhttp/4.12.0"})
36
- istek = await self.cffi.get(f"{url.replace('SAYFA', str(int(page) - 1))}")
34
+ self.httpx.headers.update({"user-agent": "okhttp/4.12.0"})
35
+ istek = await self.httpx.get(f"{url.replace('SAYFA', str(int(page) - 1))}")
37
36
  veriler = istek.json()
38
37
 
39
38
  return [
@@ -46,10 +45,9 @@ class RecTV(PluginBase):
46
45
  for veri in veriler
47
46
  ]
48
47
 
49
- #@kekik_cache(ttl=60*60)
50
48
  async def search(self, query: str) -> list[SearchResult]:
51
- self.cffi.headers.update({"user-agent": "okhttp/4.12.0"})
52
- istek = await self.cffi.get(f"{self.main_url}/api/search/{query}/{self.sw_key}/")
49
+ self.httpx.headers.update({"user-agent": "okhttp/4.12.0"})
50
+ istek = await self.httpx.get(f"{self.main_url}/api/search/{query}/{self.sw_key}/")
53
51
 
54
52
  kanallar = istek.json().get("channels")
55
53
  icerikler = istek.json().get("posters")
@@ -67,14 +65,13 @@ class RecTV(PluginBase):
67
65
  for veri in tum_veri
68
66
  ]
69
67
 
70
- #@kekik_cache(ttl=60*60)
71
68
  async def load_item(self, url: str) -> MovieInfo:
72
- self.cffi.headers.update({"user-agent": "okhttp/4.12.0"})
69
+ self.httpx.headers.update({"user-agent": "okhttp/4.12.0"})
73
70
  veri = loads(url)
74
71
 
75
72
  match veri.get("type"):
76
73
  case "serie":
77
- dizi_istek = await self.cffi.get(f"{self.main_url}/api/season/by/serie/{veri.get('id')}/{self.sw_key}/")
74
+ dizi_istek = await self.httpx.get(f"{self.main_url}/api/season/by/serie/{veri.get('id')}/{self.sw_key}/")
78
75
  dizi_veri = dizi_istek.json()
79
76
 
80
77
  episodes = []
@@ -119,23 +116,21 @@ class RecTV(PluginBase):
119
116
  actors = []
120
117
  )
121
118
 
122
- #@kekik_cache(ttl=15*60)
123
- async def load_links(self, url: str) -> list[dict]:
124
- self.media_handler.headers.update({"User-Agent": "googleusercontent"})
125
-
119
+ async def load_links(self, url: str) -> list[ExtractResult]:
126
120
  try:
127
121
  veri = loads(url)
128
122
  except Exception:
129
123
  # JSON değilse düz URL'dir (eski yapı veya hata)
130
- return [{"url": url, "name": "Video"}]
124
+ return [ExtractResult(url=url, name="Video")]
131
125
 
132
126
  # Eğer dizi bölümü ise (bizim oluşturduğumuz yapı)
133
127
  if veri.get("is_episode"):
134
- return [{
135
- "url" : veri.get("url"),
136
- "name" : veri.get("title", "Bölüm"),
137
- "referer" : "https://twitter.com/"
138
- }]
128
+ return [ExtractResult(
129
+ url = veri.get("url"),
130
+ name = veri.get("title", "Bölüm"),
131
+ user_agent = "googleusercontent",
132
+ referer = "https://twitter.com/"
133
+ )]
139
134
 
140
135
  # Film ise (RecTV API yapısı)
141
136
  results = []
@@ -145,18 +140,11 @@ class RecTV(PluginBase):
145
140
  if "otolinkaff" in video_link:
146
141
  continue
147
142
 
148
- results.append({
149
- "url" : video_link,
150
- "name" : f"{veri.get('title')} - {kaynak.get('title')}",
151
- "referer" : "https://twitter.com/"
152
- })
153
-
154
- return results
155
-
156
- async def play(self, name: str, url: str, referer: str, subtitles: list[Subtitle]):
157
- extract_result = ExtractResult(name=name, url=url, referer=referer, subtitles=subtitles)
158
- self.media_handler.title = name
159
- if self.name not in self.media_handler.title:
160
- self.media_handler.title = f"{self.name} | {self.media_handler.title}"
143
+ results.append(ExtractResult(
144
+ url = video_link,
145
+ name = f"{veri.get('title')} - {kaynak.get('title')}",
146
+ user_agent = "googleusercontent",
147
+ referer = "https://twitter.com/"
148
+ ))
161
149
 
162
- self.media_handler.play_media(extract_result)
150
+ return results