KekikStream 2.0.3__tar.gz → 2.0.5__tar.gz

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 (92) hide show
  1. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/ContentX.py +13 -0
  2. kekikstream-2.0.5/KekikStream/Extractors/DonilasPlay.py +86 -0
  3. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/Odnoklassniki.py +6 -0
  4. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/PeaceMakerst.py +6 -0
  5. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/RapidVid.py +6 -0
  6. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/SetPlay.py +7 -1
  7. kekikstream-2.0.5/KekikStream/Extractors/VCTPlay.py +41 -0
  8. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/VidMoly.py +6 -0
  9. kekikstream-2.0.5/KekikStream/Plugins/BelgeselX.py +204 -0
  10. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/Dizilla.py +22 -14
  11. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/FilmMakinesi.py +1 -1
  12. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/FilmModu.py +6 -2
  13. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/FullHDFilmizlesene.py +1 -1
  14. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/HDFilmCehennemi.py +1 -1
  15. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/JetFilmizle.py +1 -1
  16. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/KultFilmler.py +1 -1
  17. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/RoketDizi.py +17 -24
  18. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/SelcukFlix.py +51 -52
  19. kekikstream-2.0.5/KekikStream/Plugins/SetFilmIzle.py +243 -0
  20. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/SezonlukDizi.py +28 -7
  21. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/Sinefy.py +11 -8
  22. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/SinemaCX.py +3 -7
  23. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/SuperFilmGeldi.py +1 -1
  24. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/UgurFilm.py +1 -1
  25. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream.egg-info/PKG-INFO +1 -1
  26. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream.egg-info/SOURCES.txt +4 -5
  27. {kekikstream-2.0.3 → kekikstream-2.0.5}/PKG-INFO +1 -1
  28. {kekikstream-2.0.3 → kekikstream-2.0.5}/setup.py +1 -1
  29. kekikstream-2.0.3/KekikStream/Extractors/ContentX_.py +0 -40
  30. kekikstream-2.0.3/KekikStream/Extractors/Odnoklassniki_.py +0 -11
  31. kekikstream-2.0.3/KekikStream/Extractors/PeaceMakerst_.py +0 -7
  32. kekikstream-2.0.3/KekikStream/Extractors/RapidVid_.py +0 -7
  33. kekikstream-2.0.3/KekikStream/Extractors/VidMoly_.py +0 -7
  34. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/CLI/__init__.py +0 -0
  35. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/CLI/pypi_kontrol.py +0 -0
  36. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/Extractor/ExtractorBase.py +0 -0
  37. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
  38. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
  39. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/Extractor/ExtractorModels.py +0 -0
  40. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/Extractor/YTDLPCache.py +0 -0
  41. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/Media/MediaHandler.py +0 -0
  42. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/Media/MediaManager.py +0 -0
  43. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/Plugin/PluginBase.py +0 -0
  44. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
  45. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/Plugin/PluginManager.py +0 -0
  46. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/Plugin/PluginModels.py +0 -0
  47. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/UI/UIManager.py +0 -0
  48. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Core/__init__.py +0 -0
  49. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/CloseLoad.py +0 -0
  50. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/DzenRu.py +0 -0
  51. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/ExPlay.py +0 -0
  52. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/FirePlayer.py +0 -0
  53. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/HDPlayerSystem.py +0 -0
  54. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/JetTv.py +0 -0
  55. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/MailRu.py +0 -0
  56. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/MixPlayHD.py +0 -0
  57. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/MixTiger.py +0 -0
  58. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/MolyStream.py +0 -0
  59. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/PixelDrain.py +0 -0
  60. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/PlayerFilmIzle.py +0 -0
  61. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/SetPrime.py +0 -0
  62. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/SibNet.py +0 -0
  63. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/Sobreatsesuyp.py +0 -0
  64. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/TRsTX.py +0 -0
  65. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/TauVideo.py +0 -0
  66. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/TurboImgz.py +0 -0
  67. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/TurkeyPlayer.py +0 -0
  68. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/VidHide.py +0 -0
  69. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/VidMoxy.py +0 -0
  70. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/VidPapi.py +0 -0
  71. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/VideoSeyred.py +0 -0
  72. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/YTDLP.py +0 -0
  73. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Extractors/YildizKisaFilm.py +0 -0
  74. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/DiziBox.py +0 -0
  75. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/DiziPal.py +0 -0
  76. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/DiziYou.py +0 -0
  77. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/FilmBip.py +0 -0
  78. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/FullHDFilm.py +0 -0
  79. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/RecTV.py +0 -0
  80. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/SineWix.py +0 -0
  81. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/Plugins/Sinezy.py +0 -0
  82. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/__init__.py +0 -0
  83. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/__main__.py +0 -0
  84. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream/requirements.txt +0 -0
  85. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream.egg-info/dependency_links.txt +0 -0
  86. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream.egg-info/entry_points.txt +0 -0
  87. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream.egg-info/requires.txt +0 -0
  88. {kekikstream-2.0.3 → kekikstream-2.0.5}/KekikStream.egg-info/top_level.txt +0 -0
  89. {kekikstream-2.0.3 → kekikstream-2.0.5}/LICENSE +0 -0
  90. {kekikstream-2.0.3 → kekikstream-2.0.5}/MANIFEST.in +0 -0
  91. {kekikstream-2.0.3 → kekikstream-2.0.5}/README.md +0 -0
  92. {kekikstream-2.0.3 → kekikstream-2.0.5}/setup.cfg +0 -0
@@ -7,6 +7,19 @@ class ContentX(ExtractorBase):
7
7
  name = "ContentX"
8
8
  main_url = "https://contentx.me"
9
9
 
10
+ # Birden fazla domain destekle
11
+ supported_domains = [
12
+ "contentx.me", "four.contentx.me",
13
+ "dplayer82.site", "sn.dplayer82.site", "four.dplayer82.site", "org.dplayer82.site",
14
+ "dplayer74.site", "sn.dplayer74.site",
15
+ "hotlinger.com", "sn.hotlinger.com",
16
+ "playru.net", "four.playru.net",
17
+ "pichive.online", "four.pichive.online", "pichive.me", "four.pichive.me"
18
+ ]
19
+
20
+ def can_handle_url(self, url: str) -> bool:
21
+ return any(domain in url for domain in self.supported_domains)
22
+
10
23
  async def extract(self, url, referer=None) -> list[ExtractResult]:
11
24
  if referer:
12
25
  self.httpx.headers.update({"Referer": referer})
@@ -0,0 +1,86 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
+ from Kekik.Sifreleme import AESManager
5
+ import re, json
6
+
7
+ class DonilasPlay(ExtractorBase):
8
+ name = "DonilasPlay"
9
+ main_url = "https://donilasplay.com"
10
+
11
+ async def extract(self, url, referer=None) -> ExtractResult:
12
+ if referer:
13
+ self.httpx.headers.update({"Referer": referer})
14
+
15
+ istek = await self.httpx.get(url)
16
+ istek.raise_for_status()
17
+ i_source = istek.text
18
+
19
+ m3u_link = None
20
+ subtitles = []
21
+
22
+ # bePlayer pattern
23
+ be_player_match = re.search(r"bePlayer\('([^']+)',\s*'(\{[^}]+\})'\);", i_source)
24
+ if be_player_match:
25
+ be_player_pass = be_player_match.group(1)
26
+ be_player_data = be_player_match.group(2)
27
+
28
+ try:
29
+ # AES decrypt
30
+ decrypted = AESManager.decrypt(be_player_data, be_player_pass)
31
+ data = json.loads(decrypted)
32
+
33
+ m3u_link = data.get("video_location")
34
+
35
+ # Altyazıları işle
36
+ str_subtitles = data.get("strSubtitles", [])
37
+ if str_subtitles:
38
+ for sub in str_subtitles:
39
+ label = sub.get("label", "")
40
+ file = sub.get("file", "")
41
+ # Forced altyazıları hariç tut
42
+ if "Forced" in label:
43
+ continue
44
+ if file:
45
+ # Türkçe kontrolü
46
+ keywords = ["tur", "tr", "türkçe", "turkce"]
47
+ language = "Turkish" if any(k in label.lower() for k in keywords) else label
48
+ subtitles.append(Subtitle(
49
+ name = language,
50
+ url = self.fix_url(file)
51
+ ))
52
+ except Exception:
53
+ pass
54
+
55
+ # Fallback: file pattern
56
+ if not m3u_link:
57
+ file_match = re.search(r'file:"([^"]+)"', i_source)
58
+ if file_match:
59
+ m3u_link = file_match.group(1)
60
+
61
+ # tracks pattern for subtitles
62
+ tracks_match = re.search(r'tracks:\[([^\]]+)', i_source)
63
+ if tracks_match:
64
+ try:
65
+ tracks_str = f"[{tracks_match.group(1)}]"
66
+ tracks = json.loads(tracks_str)
67
+ for track in tracks:
68
+ file_url = track.get("file")
69
+ label = track.get("label", "")
70
+ if file_url and "Forced" not in label:
71
+ subtitles.append(Subtitle(
72
+ name = label,
73
+ url = self.fix_url(file_url)
74
+ ))
75
+ except Exception:
76
+ pass
77
+
78
+ if not m3u_link:
79
+ raise ValueError("m3u link not found")
80
+
81
+ return ExtractResult(
82
+ name = self.name,
83
+ url = m3u_link,
84
+ referer = url,
85
+ subtitles = subtitles
86
+ )
@@ -7,6 +7,12 @@ class Odnoklassniki(ExtractorBase):
7
7
  name = "Odnoklassniki"
8
8
  main_url = "https://odnoklassniki.ru"
9
9
 
10
+ # Birden fazla domain destekle
11
+ supported_domains = ["odnoklassniki.ru", "ok.ru"]
12
+
13
+ def can_handle_url(self, url: str) -> bool:
14
+ return any(domain in url for domain in self.supported_domains)
15
+
10
16
  async def extract(self, url, referer=None) -> ExtractResult:
11
17
  if "/video/" in url:
12
18
  url = url.replace("/video/", "/videoembed/")
@@ -7,6 +7,12 @@ class PeaceMakerst(ExtractorBase):
7
7
  name = "PeaceMakerst"
8
8
  main_url = "https://peacemakerst.com"
9
9
 
10
+ # Birden fazla domain destekle
11
+ supported_domains = ["peacemakerst.com", "hdstreamable.com"]
12
+
13
+ def can_handle_url(self, url: str) -> bool:
14
+ return any(domain in url for domain in self.supported_domains)
15
+
10
16
  async def extract(self, url, referer=None) -> ExtractResult:
11
17
  if referer:
12
18
  self.httpx.headers.update({"Referer": referer})
@@ -8,6 +8,12 @@ class RapidVid(ExtractorBase):
8
8
  name = "RapidVid"
9
9
  main_url = "https://rapidvid.net"
10
10
 
11
+ # Birden fazla domain destekle
12
+ supported_domains = ["rapidvid.net", "rapid.filmmakinesi.to"]
13
+
14
+ def can_handle_url(self, url: str) -> bool:
15
+ return any(domain in url for domain in self.supported_domains)
16
+
11
17
  async def extract(self, url, referer=None) -> ExtractResult:
12
18
  if referer:
13
19
  self.httpx.headers.update({"Referer": referer})
@@ -5,7 +5,13 @@ import re
5
5
 
6
6
  class SetPlay(ExtractorBase):
7
7
  name = "SetPlay"
8
- main_url = "https://setplay.cfd"
8
+ main_url = "https://setplay.shop"
9
+
10
+ # Birden fazla domain destekle
11
+ supported_domains = ["setplay.cfd", "setplay.shop", "setplay.site"]
12
+
13
+ def can_handle_url(self, url: str) -> bool:
14
+ return any(domain in url for domain in self.supported_domains)
9
15
 
10
16
  async def extract(self, url, referer=None) -> ExtractResult:
11
17
  ext_ref = referer or ""
@@ -0,0 +1,41 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult
4
+ from urllib.parse import urlparse, parse_qs
5
+
6
+ class VCTPlay(ExtractorBase):
7
+ name = "VCTPlay"
8
+ main_url = "https://vctplay.site"
9
+
10
+ async def extract(self, url, referer=None) -> ExtractResult:
11
+ if referer:
12
+ self.httpx.headers.update({"Referer": referer})
13
+
14
+ # URL'den video ID'sini çıkar
15
+ # https://vctplay.site/video/2hjDGco5exdv -> 2hjDGco5exdv
16
+ video_id = url.split("/")[-1]
17
+ if "?" in video_id:
18
+ video_id = video_id.split("?")[0]
19
+
20
+ # Manifests URL oluştur
21
+ master_url = f"{self.main_url}/manifests/{video_id}/master.txt"
22
+
23
+ # partKey'den isim belirle
24
+ parsed = urlparse(url)
25
+ params = parse_qs(parsed.query)
26
+ part_key = params.get("partKey", [""])[0]
27
+
28
+ name_suffix = ""
29
+ if "turkcedublaj" in part_key.lower():
30
+ name_suffix = "Dublaj"
31
+ elif "turkcealtyazi" in part_key.lower():
32
+ name_suffix = "Altyazı"
33
+
34
+ display_name = f"{self.name} - {name_suffix}" if name_suffix else self.name
35
+
36
+ return ExtractResult(
37
+ name = display_name,
38
+ url = master_url,
39
+ referer = f"{self.main_url}/",
40
+ subtitles = []
41
+ )
@@ -9,6 +9,12 @@ class VidMoly(ExtractorBase):
9
9
  name = "VidMoly"
10
10
  main_url = "https://vidmoly.to"
11
11
 
12
+ # Birden fazla domain destekle
13
+ supported_domains = ["vidmoly.to", "vidmoly.me", "vidmoly.net"]
14
+
15
+ def can_handle_url(self, url: str) -> bool:
16
+ return any(domain in url for domain in self.supported_domains)
17
+
12
18
  async def extract(self, url: str, referer: str = None) -> ExtractResult:
13
19
  if referer:
14
20
  self.httpx.headers.update({"Referer": referer})
@@ -0,0 +1,204 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode
4
+ from parsel import Selector
5
+ import re
6
+
7
+ class BelgeselX(PluginBase):
8
+ name = "BelgeselX"
9
+ language = "tr"
10
+ main_url = "https://belgeselx.com"
11
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
+ description = "2022 yılında son çıkan belgeselleri belgeselx.com'da izle. En yeni belgeseller, türkçe altyazılı yada dublaj olarak 1080p kalitesinde hd belgesel izle."
13
+
14
+ main_page = {
15
+ f"{main_url}/konu/turk-tarihi-belgeselleri&page=" : "Türk Tarihi",
16
+ f"{main_url}/konu/tarih-belgeselleri&page=" : "Tarih",
17
+ f"{main_url}/konu/seyehat-belgeselleri&page=" : "Seyahat",
18
+ f"{main_url}/konu/seri-belgeseller&page=" : "Seri",
19
+ f"{main_url}/konu/savas-belgeselleri&page=" : "Savaş",
20
+ f"{main_url}/konu/sanat-belgeselleri&page=" : "Sanat",
21
+ f"{main_url}/konu/psikoloji-belgeselleri&page=" : "Psikoloji",
22
+ f"{main_url}/konu/polisiye-belgeselleri&page=" : "Polisiye",
23
+ f"{main_url}/konu/otomobil-belgeselleri&page=" : "Otomobil",
24
+ f"{main_url}/konu/nazi-belgeselleri&page=" : "Nazi",
25
+ f"{main_url}/konu/muhendislik-belgeselleri&page=" : "Mühendislik",
26
+ f"{main_url}/konu/kultur-din-belgeselleri&page=" : "Kültür Din",
27
+ f"{main_url}/konu/kozmik-belgeseller&page=" : "Kozmik",
28
+ f"{main_url}/konu/hayvan-belgeselleri&page=" : "Hayvan",
29
+ f"{main_url}/konu/eski-tarih-belgeselleri&page=" : "Eski Tarih",
30
+ f"{main_url}/konu/egitim-belgeselleri&page=" : "Eğitim",
31
+ f"{main_url}/konu/dunya-belgeselleri&page=" : "Dünya",
32
+ f"{main_url}/konu/doga-belgeselleri&page=" : "Doğa",
33
+ f"{main_url}/konu/bilim-belgeselleri&page=" : "Bilim"
34
+ }
35
+
36
+ @staticmethod
37
+ def _to_title_case(text: str) -> str:
38
+ """Türkçe için title case dönüşümü."""
39
+ return " ".join(
40
+ word.lower().replace("i", "İ").capitalize() if word.lower().startswith("i") else word.capitalize()
41
+ for word in text.split()
42
+ )
43
+
44
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
45
+ istek = self.cloudscraper.get(f"{url}{page}")
46
+ secici = Selector(istek.text)
47
+
48
+ results = []
49
+ for item in secici.css("div.gen-movie-contain > div.gen-info-contain > div.gen-movie-info"):
50
+ title = item.css("div.gen-movie-info > h3 a::text").get()
51
+ href = item.css("div.gen-movie-info > h3 a::attr(href)").get()
52
+ parent = item.xpath("../../..")
53
+ poster = parent.css("div.gen-movie-img > img::attr(src)").get()
54
+
55
+ if title and href:
56
+ results.append(MainPageResult(
57
+ category = category,
58
+ title = self._to_title_case(title.strip()),
59
+ url = self.fix_url(href),
60
+ poster = self.fix_url(poster) if poster else None
61
+ ))
62
+
63
+ return results
64
+
65
+ async def search(self, query: str) -> list[SearchResult]:
66
+ # Google Custom Search API kullanıyor
67
+ cx = "016376594590146270301:iwmy65ijgrm"
68
+
69
+ token_resp = self.cloudscraper.get(f"https://cse.google.com/cse.js?cx={cx}")
70
+ token_text = token_resp.text
71
+
72
+ cse_lib_match = re.search(r'cselibVersion": "(.*)"', token_text)
73
+ cse_tok_match = re.search(r'cse_token": "(.*)"', token_text)
74
+
75
+ if not cse_lib_match or not cse_tok_match:
76
+ return []
77
+
78
+ cse_lib = cse_lib_match.group(1)
79
+ cse_tok = cse_tok_match.group(1)
80
+
81
+ search_url = (
82
+ f"https://cse.google.com/cse/element/v1?"
83
+ f"rsz=filtered_cse&num=100&hl=tr&source=gcsc&cselibv={cse_lib}&cx={cx}"
84
+ f"&q={query}&safe=off&cse_tok={cse_tok}&sort=&exp=cc%2Capo&oq={query}"
85
+ f"&callback=google.search.cse.api9969&rurl=https%3A%2F%2Fbelgeselx.com%2F"
86
+ )
87
+
88
+ resp = self.cloudscraper.get(search_url)
89
+ resp_text = resp.text
90
+
91
+ titles = re.findall(r'"titleNoFormatting": "(.*?)"', resp_text)
92
+ urls = re.findall(r'"url": "(.*?)"', resp_text)
93
+ images = re.findall(r'"ogImage": "(.*?)"', resp_text)
94
+
95
+ results = []
96
+ for i, title in enumerate(titles):
97
+ url_val = urls[i] if i < len(urls) else None
98
+ poster = images[i] if i < len(images) else None
99
+
100
+ if not url_val or "diziresimleri" not in url_val:
101
+ # URL'den belgesel linkini oluştur
102
+ if poster and "diziresimleri" in poster:
103
+ file_name = poster.rsplit("/", 1)[-1]
104
+ file_name = re.sub(r"\.(jpe?g|png|webp)$", "", file_name)
105
+ url_val = f"{self.main_url}/belgeseldizi/{file_name}"
106
+ else:
107
+ continue
108
+
109
+ clean_title = title.split("İzle")[0].strip()
110
+ results.append(SearchResult(
111
+ title = self._to_title_case(clean_title),
112
+ url = url_val,
113
+ poster = poster
114
+ ))
115
+
116
+ return results
117
+
118
+ async def load_item(self, url: str) -> SeriesInfo:
119
+ istek = await self.httpx.get(url)
120
+ secici = Selector(istek.text)
121
+
122
+ title = secici.css("h2.gen-title::text").get()
123
+ poster = secici.css("div.gen-tv-show-top img::attr(src)").get()
124
+ description = secici.css("div.gen-single-tv-show-info p::text").get()
125
+
126
+ tags = []
127
+ for tag_link in secici.css("div.gen-socail-share a[href*='belgeselkanali']"):
128
+ tag_href = tag_link.css("::attr(href)").get()
129
+ if tag_href:
130
+ tag_name = tag_href.rsplit("/", 1)[-1].replace("-", " ")
131
+ tags.append(self._to_title_case(tag_name))
132
+
133
+ episodes = []
134
+ counter = 0
135
+ for ep_item in secici.css("div.gen-movie-contain"):
136
+ ep_name = ep_item.css("div.gen-movie-info h3 a::text").get()
137
+ ep_href = ep_item.css("div.gen-movie-info h3 a::attr(href)").get()
138
+
139
+ if not ep_name or not ep_href:
140
+ continue
141
+
142
+ season_text = ep_item.css("div.gen-single-meta-holder ul li::text").get() or ""
143
+ episode_match = re.search(r"Bölüm (\d+)", season_text)
144
+ season_match = re.search(r"Sezon (\d+)", season_text)
145
+
146
+ ep_episode = int(episode_match.group(1)) if episode_match else counter
147
+ ep_season = int(season_match.group(1)) if season_match else 1
148
+
149
+ counter += 1
150
+
151
+ episodes.append(Episode(
152
+ season = ep_season,
153
+ episode = ep_episode,
154
+ title = ep_name.strip(),
155
+ url = self.fix_url(ep_href)
156
+ ))
157
+
158
+ return SeriesInfo(
159
+ url = url,
160
+ poster = self.fix_url(poster) if poster else None,
161
+ title = self._to_title_case(title.strip()) if title else None,
162
+ description = description.strip() if description else None,
163
+ tags = tags,
164
+ episodes = episodes
165
+ )
166
+
167
+ async def load_links(self, url: str) -> list[dict]:
168
+ istek = await self.httpx.get(url)
169
+ text = istek.text
170
+
171
+ # fnc_addWatch div'inden data-episode ID'sini al
172
+ ep_match = re.search(r'<div[^>]*class=["\'][^"\']*fnc_addWatch[^"\']*["\'][^>]*data-episode=["\'](\d+)["\']', text)
173
+ if not ep_match:
174
+ return []
175
+
176
+ episode_id = ep_match.group(1)
177
+ iframe_url = f"{self.main_url}/video/data/new4.php?id={episode_id}"
178
+
179
+ iframe_resp = await self.httpx.get(iframe_url, headers={"Referer": url})
180
+ iframe_text = iframe_resp.text
181
+
182
+ links = []
183
+ for match in re.finditer(r'file:"([^"]+)", label: "([^"]+)"', iframe_text):
184
+ video_url = match.group(1)
185
+ quality = match.group(2)
186
+
187
+ source_name = "Google" if quality == "FULL" else self.name
188
+ quality_str = "1080p" if quality == "FULL" else quality
189
+
190
+ links.append({
191
+ "url" : video_url,
192
+ "name" : f"{source_name} | {quality_str}",
193
+ "referer" : url
194
+ })
195
+
196
+ return links
197
+
198
+ async def play(self, **kwargs):
199
+ extract_result = ExtractResult(**kwargs)
200
+ self.media_handler.title = kwargs.get("name")
201
+ if self.name not in self.media_handler.title:
202
+ self.media_handler.title = f"{self.name} | {self.media_handler.title}"
203
+
204
+ self.media_handler.play_media(extract_result)
@@ -179,17 +179,25 @@ class Dizilla(PluginBase):
179
179
  decrypted = await self.decrypt_response(secure_data)
180
180
  results = decrypted.get("RelatedResults", {}).get("getEpisodeSources", {}).get("result", [])
181
181
 
182
- links = []
183
- for result in results:
184
- iframe_src = Selector(result.get("source_content")).css("iframe::attr(src)").get()
185
- iframe_url = self.fix_url(iframe_src)
186
- if not iframe_url:
187
- continue
188
-
189
- extractor = self.ex_manager.find_extractor(iframe_url)
190
- links.append({
191
- "url" : iframe_url,
192
- "name" : f"{extractor.name if extractor else 'Main Player'} | {result.get('language_name')}",
193
- })
194
-
195
- return links
182
+ if not results:
183
+ return []
184
+
185
+ # Get first source (matching Kotlin)
186
+ first_result = results[0]
187
+ source_content = first_result.get("source_content", "")
188
+
189
+ # Clean the source_content string (matching Kotlin: .replace("\"", "").replace("\\", ""))
190
+ cleaned_source = source_content.replace('"', '').replace('\\', '')
191
+
192
+ # Parse cleaned HTML
193
+ iframe_src = Selector(cleaned_source).css("iframe::attr(src)").get()
194
+ iframe_url = self.fix_url(iframe_src)
195
+
196
+ if not iframe_url:
197
+ return []
198
+
199
+ extractor = self.ex_manager.find_extractor(iframe_url)
200
+ return [{
201
+ "url" : iframe_url,
202
+ "name" : f"{extractor.name if extractor else 'Main Player'} | {first_result.get('language_name', 'Unknown')}",
203
+ }]
@@ -8,7 +8,7 @@ class FilmMakinesi(PluginBase):
8
8
  language = "tr"
9
9
  main_url = "https://filmmakinesi.to"
10
10
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
11
- description = "Film Makinesi, en yeni ve en güncel filmleri sitemizde full HD kalite farkı ile izleyebilirsiniz. HD film izle denildiğinde akla gelen en kaliteli film izleme sitesi."
11
+ description = "Film Makinesi ile en yeni ve güncel filmleri Full HD kalite farkı ile izleyebilirsiniz. Film izle denildiğinde akla gelen en kaliteli film sitesi."
12
12
 
13
13
  main_page = {
14
14
  f"{main_url}/filmler-1/" : "Son Filmler",
@@ -9,7 +9,7 @@ class FilmModu(PluginBase):
9
9
  language = "tr"
10
10
  main_url = "https://www.filmmodu.ws"
11
11
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
- description = "HD Film izle, Türkçe Dublaj ve Altyazılı filmler."
12
+ description = "Film modun geldiyse yüksek kalitede yeni filmleri izle, 1080p izleyebileceğiniz reklamsız tek film sitesi."
13
13
 
14
14
  main_page = {
15
15
  f"{main_url}/hd-film-kategori/4k-film-izle?page=SAYFA" : "4K",
@@ -91,9 +91,13 @@ class FilmModu(PluginBase):
91
91
  istek = await self.httpx.get(url)
92
92
  secici = Selector(istek.text)
93
93
 
94
+ alternates = secici.css("div.alternates a")
95
+ if not alternates:
96
+ return [] # No alternates available
97
+
94
98
  results = []
95
99
 
96
- for alternatif in secici.css("div.alternates a"):
100
+ for alternatif in alternates:
97
101
  alt_link = self.fix_url(alternatif.css("::attr(href)").get())
98
102
  alt_name = alternatif.css("::text").get()
99
103
 
@@ -10,7 +10,7 @@ class FullHDFilmizlesene(PluginBase):
10
10
  language = "tr"
11
11
  main_url = "https://www.fullhdfilmizlesene.tv"
12
12
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
13
- description = "Sinema zevkini evinize kadar getirdik. Türkiye'nin lider Film sitesinde, en yeni filmleri Full HD izleyin."
13
+ description = "Türkiye'nin ilk ve lider HD film izleme platformu, kaliteli ve sorunsuz hizmetiyle sinema keyfini zirveye taşır."
14
14
 
15
15
  main_page = {
16
16
  f"{main_url}/en-cok-izlenen-hd-filmler/" : "En Çok izlenen Filmler",
@@ -10,7 +10,7 @@ class HDFilmCehennemi(PluginBase):
10
10
  language = "tr"
11
11
  main_url = "https://www.hdfilmcehennemi.ws"
12
12
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
13
- description = "Türkiye'nin en hızlı hd film izleme sitesi"
13
+ description = "Türkiye'nin en hızlı hd film izleme sitesi. Tek ve gerçek hdfilmcehennemi sitesi."
14
14
 
15
15
 
16
16
 
@@ -8,7 +8,7 @@ class JetFilmizle(PluginBase):
8
8
  language = "tr"
9
9
  main_url = "https://jetfilmizle.website"
10
10
  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"
11
+ 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
12
 
13
13
  main_page = {
14
14
  f"{main_url}/page/" : "Son Filmler",
@@ -9,7 +9,7 @@ class KultFilmler(PluginBase):
9
9
  language = "tr"
10
10
  main_url = "https://kultfilmler.net"
11
11
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
- description = "Kült film ve dizi izleme sitesi."
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
13
 
14
14
  main_page = {
15
15
  f"{main_url}/category/aile-filmleri-izle" : "Aile",
@@ -185,6 +185,7 @@ class RoketDizi(PluginBase):
185
185
  # secureData içindeki RelatedResults -> getEpisodeSources -> result dizisini al
186
186
  sources = decoded_json.get("RelatedResults", {}).get("getEpisodeSources", {}).get("result", [])
187
187
 
188
+ seen_urls = set()
188
189
  results = []
189
190
  for source in sources:
190
191
  source_content = source.get("source_content", "")
@@ -195,35 +196,27 @@ class RoketDizi(PluginBase):
195
196
  continue
196
197
 
197
198
  iframe_url = iframe_match.group(1)
198
- if "http" not in iframe_url:
199
- if iframe_url.startswith("//"):
200
- iframe_url = "https:" + iframe_url
201
- else:
202
- iframe_url = "https://" + iframe_url
199
+
200
+ # Fix URL protocol
201
+ if not iframe_url.startswith("http"):
202
+ if iframe_url.startswith("//"):
203
+ iframe_url = "https:" + iframe_url
204
+ else:
205
+ iframe_url = "https://" + iframe_url
206
+
207
+ iframe_url = self.fix_url(iframe_url)
208
+
209
+ # Deduplicate
210
+ if iframe_url in seen_urls:
211
+ continue
212
+ seen_urls.add(iframe_url)
203
213
 
204
214
  # Check extractor
205
215
  extractor = self.ex_manager.find_extractor(iframe_url)
206
- ext_name = extractor.name if extractor else ""
207
-
208
- # Metadata'dan bilgileri al
209
- source_name = source.get("source_name", "")
210
- language_name = source.get("language_name", "")
211
- quality_name = source.get("quality_name", "")
212
-
213
- # İsmi oluştur
214
- name_parts = []
215
- if source_name:
216
- name_parts.append(source_name)
217
- if ext_name:
218
- name_parts.append(ext_name)
219
- if language_name:
220
- name_parts.append(language_name)
221
- if quality_name:
222
- name_parts.append(quality_name)
223
-
216
+
224
217
  results.append({
225
218
  "url" : iframe_url,
226
- "name" : " | ".join(name_parts)
219
+ "name" : extractor.name if extractor else "Player"
227
220
  })
228
221
 
229
222
  return results