KekikStream 2.3.7__tar.gz → 2.4.0__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 (98) hide show
  1. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/HTMLHelper.py +2 -2
  2. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/Plugin/PluginModels.py +0 -3
  3. kekikstream-2.4.0/KekikStream/Extractors/HDMomPlayer.py +62 -0
  4. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/MolyStream.py +1 -0
  5. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/VidMoly.py +13 -0
  6. kekikstream-2.4.0/KekikStream/Extractors/Videostr.py +115 -0
  7. kekikstream-2.4.0/KekikStream/Plugins/DiziMom.py +248 -0
  8. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/DiziPal.py +20 -9
  9. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/DiziYou.py +14 -18
  10. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/Dizilla.py +50 -59
  11. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/FilmBip.py +8 -4
  12. kekikstream-2.4.0/KekikStream/Plugins/FilmEkseni.py +140 -0
  13. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/FilmMakinesi.py +1 -1
  14. kekikstream-2.4.0/KekikStream/Plugins/Filmatek.py +188 -0
  15. kekikstream-2.4.0/KekikStream/Plugins/Full4kizle.py +190 -0
  16. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/FullHDFilmizlesene.py +7 -16
  17. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/JetFilmizle.py +6 -1
  18. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/KultFilmler.py +2 -1
  19. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/RecTV.py +59 -24
  20. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/RoketDizi.py +76 -78
  21. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/SelcukFlix.py +90 -67
  22. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/SetFilmIzle.py +27 -8
  23. kekikstream-2.4.0/KekikStream/Plugins/Watch32.py +185 -0
  24. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/YabanciDizi.py +25 -14
  25. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream.egg-info/PKG-INFO +3 -3
  26. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream.egg-info/SOURCES.txt +7 -1
  27. {kekikstream-2.3.7 → kekikstream-2.4.0}/PKG-INFO +3 -3
  28. {kekikstream-2.3.7 → kekikstream-2.4.0}/README.md +2 -2
  29. {kekikstream-2.3.7 → kekikstream-2.4.0}/setup.py +1 -1
  30. kekikstream-2.3.7/KekikStream/Plugins/DiziWatch.py +0 -212
  31. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/CLI/__init__.py +0 -0
  32. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/CLI/pypi_kontrol.py +0 -0
  33. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/Extractor/ExtractorBase.py +0 -0
  34. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
  35. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
  36. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/Extractor/ExtractorModels.py +0 -0
  37. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/Extractor/YTDLPCache.py +0 -0
  38. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/Media/MediaHandler.py +0 -0
  39. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/Media/MediaManager.py +0 -0
  40. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/Plugin/PluginBase.py +0 -0
  41. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
  42. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/Plugin/PluginManager.py +0 -0
  43. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/UI/UIManager.py +0 -0
  44. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Core/__init__.py +0 -0
  45. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/CloseLoad.py +0 -0
  46. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/ContentX.py +0 -0
  47. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/DonilasPlay.py +0 -0
  48. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/DzenRu.py +0 -0
  49. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/ExPlay.py +0 -0
  50. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/Filemoon.py +0 -0
  51. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/HDPlayerSystem.py +0 -0
  52. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/JFVid.py +0 -0
  53. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/JetTv.py +0 -0
  54. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/MailRu.py +0 -0
  55. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/MixPlayHD.py +0 -0
  56. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/MixTiger.py +0 -0
  57. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/Odnoklassniki.py +0 -0
  58. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/PeaceMakerst.py +0 -0
  59. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/PixelDrain.py +0 -0
  60. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/PlayerFilmIzle.py +0 -0
  61. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/RapidVid.py +0 -0
  62. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/SetPlay.py +0 -0
  63. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/SetPrime.py +0 -0
  64. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/SibNet.py +0 -0
  65. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/Sobreatsesuyp.py +0 -0
  66. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/TRsTX.py +0 -0
  67. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/TauVideo.py +0 -0
  68. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/TurboImgz.py +0 -0
  69. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/TurkeyPlayer.py +0 -0
  70. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/VCTPlay.py +0 -0
  71. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/VidHide.py +0 -0
  72. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/VidMoxy.py +0 -0
  73. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/VidPapi.py +0 -0
  74. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/VideoSeyred.py +0 -0
  75. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/YTDLP.py +0 -0
  76. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Extractors/YildizKisaFilm.py +0 -0
  77. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/BelgeselX.py +0 -0
  78. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/DiziBox.py +0 -0
  79. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/FilmModu.py +0 -0
  80. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/FullHDFilm.py +0 -0
  81. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/HDFilmCehennemi.py +0 -0
  82. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/SezonlukDizi.py +0 -0
  83. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/SineWix.py +0 -0
  84. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/Sinefy.py +0 -0
  85. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/SinemaCX.py +0 -0
  86. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/Sinezy.py +0 -0
  87. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/SuperFilmGeldi.py +0 -0
  88. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/Plugins/UgurFilm.py +0 -0
  89. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/__init__.py +0 -0
  90. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/__main__.py +0 -0
  91. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream/requirements.txt +0 -0
  92. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream.egg-info/dependency_links.txt +0 -0
  93. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream.egg-info/entry_points.txt +0 -0
  94. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream.egg-info/requires.txt +0 -0
  95. {kekikstream-2.3.7 → kekikstream-2.4.0}/KekikStream.egg-info/top_level.txt +0 -0
  96. {kekikstream-2.3.7 → kekikstream-2.4.0}/LICENSE +0 -0
  97. {kekikstream-2.3.7 → kekikstream-2.4.0}/MANIFEST.in +0 -0
  98. {kekikstream-2.3.7 → kekikstream-2.4.0}/setup.cfg +0 -0
@@ -113,8 +113,8 @@ class HTMLHelper:
113
113
  return int(m.group(1)), int(m.group(2))
114
114
 
115
115
  # Ayrı ayrı ara
116
- s = re.search(r"(\d+)\.\s*[Ss]ezon|[Ss]ezon[- ]?(\d+)|-(\d+)-sezon", text, re.I)
117
- e = re.search(r"(\d+)\.\s*[Bb]ölüm|[Bb]olum[- ]?(\d+)|-(\d+)-bolum|[Ee](\d+)", text, re.I)
116
+ s = re.search(r"(\d+)\.\s*[Ss]ezon|[Ss]ezon[- ]?(\d+)|-(\d+)-sezon|S(\d+)|(\d+)\.[Ss]", text, re.I)
117
+ e = re.search(r"(\d+)\.\s*[Bb][öo]l[üu]m|[Bb][öo]l[üu]m[- ]?(\d+)|-(\d+)-bolum|[Ee](\d+)", text, re.I)
118
118
 
119
119
  # İlk bulunan grubu al (None değilse)
120
120
  s_val = next((int(g) for g in s.groups() if g), None) if s else None
@@ -51,9 +51,6 @@ class Episode(BaseModel):
51
51
  if not self.title:
52
52
  self.title = ""
53
53
 
54
- if any(keyword in self.title.lower() for keyword in ["bölüm", "sezon", "episode"]):
55
- self.title = ""
56
-
57
54
  return self
58
55
 
59
56
  class SeriesInfo(BaseModel):
@@ -0,0 +1,62 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
4
+ from Kekik.Sifreleme import AESManager
5
+ import re
6
+ import json
7
+
8
+ class HDMomPlayer(ExtractorBase):
9
+ name = "HDMomPlayer"
10
+ main_url = "hdmomplayer.com"
11
+
12
+ async def extract(self, url: str, referer: str = None) -> ExtractResult | None:
13
+ if referer:
14
+ self.httpx.headers.update({"Referer": referer})
15
+
16
+ try:
17
+ response = await self.httpx.get(url)
18
+ page_source = response.text
19
+
20
+ m3u_link = None
21
+
22
+ # Regex for bePlayer matches
23
+ # Matches: bePlayer('PASS', '{DATA}');
24
+ helper = HTMLHelper(page_source)
25
+ be_matches = helper.regex_all(r"bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);")
26
+
27
+ if be_matches:
28
+ pass_val, data_val = be_matches[0]
29
+
30
+ try:
31
+ # Use Kekik.Sifreleme.AESManager as requested
32
+ decrypted = AESManager.decrypt(data_val, pass_val).replace("\\", "")
33
+
34
+ # Search for video_location in decrypted string
35
+ # Kotlin: video_location":"([^"]+)
36
+ m_loc = re.search(r'video_location":"([^"]+)"', decrypted)
37
+ if m_loc:
38
+ m3u_link = m_loc.group(1).replace(r"\/", "/")
39
+ except Exception:
40
+ pass
41
+
42
+ if not m3u_link:
43
+ # Fallback regex
44
+ # file:"..."
45
+ m_file = re.search(r'file:"([^"]+)"', page_source)
46
+ if m_file:
47
+ m3u_link = m_file.group(1)
48
+
49
+ if not m3u_link:
50
+ return None
51
+
52
+ # Fix URL if needed
53
+ if m3u_link.startswith("//"):
54
+ m3u_link = "https:" + m3u_link
55
+
56
+ return ExtractResult(
57
+ name = "HDMomPlayer",
58
+ url = m3u_link,
59
+ referer = url,
60
+ )
61
+ except Exception:
62
+ return None
@@ -9,6 +9,7 @@ class MolyStream(ExtractorBase):
9
9
 
10
10
  # Birden fazla domain destekle
11
11
  supported_domains = [
12
+ "dbx.molystream.org",
12
13
  "ydx.molystream.org",
13
14
  "yd.sheila.stream",
14
15
  "ydf.popcornvakti.net",
@@ -24,6 +24,8 @@ class VidMoly(ExtractorBase):
24
24
 
25
25
  if ".me" in url:
26
26
  url = url.replace(".me", ".net")
27
+ if ".to" in url:
28
+ url = url.replace(".to", ".net")
27
29
 
28
30
  # VidMoly bazen redirect ediyor, takip et
29
31
  response = await self.httpx.get(url, follow_redirects=True)
@@ -70,6 +72,17 @@ class VidMoly(ExtractorBase):
70
72
  if sub.get("kind") == "captions"
71
73
  ]
72
74
 
75
+ if "#EXTM3U" in response.text:
76
+ for line in response.text.splitlines():
77
+ line = line.strip().replace('"', '').replace("'", "")
78
+ if line.startswith("http"):
79
+ return ExtractResult(
80
+ name = self.name,
81
+ url = line,
82
+ referer = self.main_url,
83
+ subtitles = subtitles
84
+ )
85
+
73
86
  if script_str := resp_sec.regex_first(r"sources:\s*\[(.*?)\],", flags= re.DOTALL):
74
87
  script_content = script_str
75
88
  # Video kaynaklarını ayrıştır
@@ -0,0 +1,115 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper, Subtitle
4
+ from urllib.parse import quote
5
+ from json import loads
6
+
7
+ class Videostr(ExtractorBase):
8
+ name = "Videostr"
9
+ main_url = "videostr.net"
10
+
11
+ async def extract(self, url: str, referer: str = None) -> ExtractResult | None:
12
+ headers = {
13
+ "Accept": "*/*",
14
+ "X-Requested-With": "XMLHttpRequest",
15
+ "Referer": "https://videostr.net",
16
+ }
17
+
18
+ # 1. Get nonce page
19
+ # Kotlin: url.substringAfterLast("/").substringBefore("?")
20
+ id = url.split("?")[0].split("/")[-1]
21
+
22
+ istek = await self.httpx.get(url, headers=headers)
23
+ if istek.status_code != 200:
24
+ return None
25
+
26
+ responsenonce = istek.text
27
+
28
+ # Find nonce
29
+ # Regex: \b[a-zA-Z0-9]{48}\b
30
+ # Or 3 blocks of 16 chars
31
+ helper = HTMLHelper(responsenonce)
32
+ nonce = helper.regex_first(r"\b[a-zA-Z0-9]{48}\b")
33
+ if not nonce:
34
+ # Fallback regex as per Kotlin: \b([a-zA-Z0-9]{16})\b.*?\b([a-zA-Z0-9]{16})\b.*?\b([a-zA-Z0-9]{16})\b
35
+ # In Python we can just look for the combined matches if regex_first handles grouping poorly or try finding all
36
+ import re
37
+ m = re.search(r"\b([a-zA-Z0-9]{16})\b.*?\b([a-zA-Z0-9]{16})\b.*?\b([a-zA-Z0-9]{16})\b", responsenonce, re.DOTALL)
38
+ if m:
39
+ nonce = m.group(1) + m.group(2) + m.group(3)
40
+
41
+ if not nonce:
42
+ return None
43
+
44
+ # 2. Get Sources
45
+ api_url = f"https://videostr.net/embed-1/v3/e-1/getSources?id={id}&_k={nonce}"
46
+
47
+ api_resp = await self.httpx.get(api_url, headers=headers)
48
+ if api_resp.status_code != 200:
49
+ return None
50
+
51
+ # Parse JSON
52
+ try:
53
+ data = api_resp.json()
54
+ except:
55
+ return None
56
+
57
+ sources = data.get("sources", [])
58
+ if not sources:
59
+ return None
60
+
61
+ encrypted_file = sources[0].get("file")
62
+ if not encrypted_file:
63
+ return None
64
+
65
+ m3u8_url = None
66
+
67
+ if ".m3u8" in encrypted_file:
68
+ m3u8_url = encrypted_file
69
+ else:
70
+ # Decrypt
71
+ # Need key from github
72
+ key_url = "https://raw.githubusercontent.com/yogesh-hacker/MegacloudKeys/refs/heads/main/keys.json"
73
+ key_resp = await self.httpx.get(key_url)
74
+ if key_resp.status_code == 200:
75
+ try:
76
+ keys = key_resp.json()
77
+ vidstr_key = keys.get("vidstr") # As per Kotlin code: gson.fromJson(keyJson, Megakey::class.java)?.vidstr
78
+
79
+ if vidstr_key:
80
+ # Use Google Script to decrypt
81
+ decode_url = "https://script.google.com/macros/s/AKfycbxHbYHbrGMXYD2-bC-C43D3njIbU-wGiYQuJL61H4vyy6YVXkybMNNEPJNPPuZrD1gRVA/exec"
82
+
83
+ full_url = f"{decode_url}?encrypted_data={quote(encrypted_file)}&nonce={quote(nonce)}&secret={quote(vidstr_key)}"
84
+
85
+ decrypted_resp = await self.httpx.get(full_url)
86
+ if decrypted_resp.status_code == 200:
87
+ # Response is JSON {"file": "..."} usually or text?
88
+ # Kotlin says: Regex("\"file\":\"(.*?)\"").find(decryptedResponse)
89
+ m_file = re.search(r'"file":"(.*?)"', decrypted_resp.text)
90
+ if m_file:
91
+ m3u8_url = m_file.group(1).replace(r"\/", "/")
92
+ except Exception as e:
93
+ # print(f"Decryption error: {e}")
94
+ pass
95
+
96
+ if not m3u8_url:
97
+ return None
98
+
99
+ # Subtitles
100
+ # Kotlin: response.tracks
101
+ subtitles = []
102
+ tracks = data.get("tracks", [])
103
+ for t in tracks:
104
+ if t.get("kind") in ["captions", "subtitles"]:
105
+ subtitles.append(Subtitle(
106
+ name = t.get("label", "Altyazı"),
107
+ url = t.get("file")
108
+ ))
109
+
110
+ return ExtractResult(
111
+ name = "Videostr",
112
+ url = m3u8_url,
113
+ referer = "https://videostr.net/",
114
+ subtitles= subtitles
115
+ )
@@ -0,0 +1,248 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, MovieInfo, SeriesInfo, Episode, ExtractResult, HTMLHelper
4
+ from json import dumps, loads
5
+ import re
6
+
7
+ class DiziMom(PluginBase):
8
+ name = "DiziMom"
9
+ language = "tr"
10
+ main_url = "https://www.dizimom.one"
11
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
+ description = "Binlerce yerli yabancı dizi arşivi, tüm sezonlar, kesintisiz bölümler. Sadece dizi izle, Dizimom heryerde seninle!"
13
+
14
+ main_page = {
15
+ f"{main_url}/tum-bolumler/page" : "Son Bölümler",
16
+ f"{main_url}/yerli-dizi-izle/page" : "Yerli Diziler",
17
+ f"{main_url}/yabanci-dizi-izle/page" : "Yabancı Diziler",
18
+ f"{main_url}/tv-programlari-izle/page" : "TV Programları",
19
+ }
20
+
21
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
22
+ istek = await self.httpx.get(f"{url}/{page}/")
23
+ helper = HTMLHelper(istek.text)
24
+
25
+ if "/tum-bolumler/" in url:
26
+ items = helper.select("div.episode-box")
27
+ results = []
28
+ for item in items:
29
+ name_el = helper.select_first("div.episode-name a", item)
30
+ if not name_el: continue
31
+ name = name_el.text(strip=True).split(" izle")[0]
32
+ title = name.replace(".Sezon ", "x").replace(".Bölüm", "")
33
+
34
+ ep_href = self.fix_url(name_el.attrs.get("href"))
35
+ pass
36
+
37
+ # Revert to standard categories if "tum-bolumler" is complex
38
+ return []
39
+ else:
40
+ items = helper.select("div.single-item")
41
+ return [
42
+ MainPageResult(
43
+ category = category,
44
+ title = helper.select_text("div.categorytitle a", item).split(" izle")[0],
45
+ url = self.fix_url(helper.select_attr("div.categorytitle a", "href", item)),
46
+ poster = self.fix_url(helper.select_attr("div.cat-img img", "src", item))
47
+ )
48
+ for item in items
49
+ ]
50
+
51
+ async def search(self, query: str) -> list[SearchResult]:
52
+ url = f"{self.main_url}/?s={query}"
53
+ istek = await self.httpx.get(url)
54
+ helper = HTMLHelper(istek.text)
55
+ items = helper.select("div.single-item")
56
+
57
+ return [
58
+ SearchResult(
59
+ title = helper.select_text("div.categorytitle a", item).split(" izle")[0],
60
+ url = self.fix_url(helper.select_attr("div.categorytitle a", "href", item)),
61
+ poster = self.fix_url(helper.select_attr("div.cat-img img", "src", item))
62
+ )
63
+ for item in items
64
+ ]
65
+
66
+ async def load_item(self, url: str) -> SeriesInfo:
67
+ istek = await self.httpx.get(url)
68
+ helper = HTMLHelper(istek.text)
69
+
70
+ title_raw = helper.select_text("div.title h1")
71
+ title = title_raw.split(" izle")[0] if title_raw else "Bilinmiyor"
72
+
73
+ poster = self.fix_url(helper.select_attr("div.category_image img", "src"))
74
+
75
+ # Custom extraction for fields that were using xpath_text
76
+ year = None
77
+ rating = None
78
+ actors = None
79
+
80
+ # Regex approach for specific fields might be safer/easier if structure varies
81
+ # Matches: Yapım Yılı : </span> 2025
82
+ year_val = helper.regex_first(r"Yapım Yılı\s*:\s*(?:</span>)?\s*(\d{4})")
83
+ if year_val:
84
+ year = int(year_val)
85
+
86
+ rating_val = helper.regex_first(r"IMDB\s*:\s*([\d\.]+)")
87
+ if rating_val:
88
+ rating = rating_val
89
+
90
+ actors_val = helper.regex_first(r"Oyuncular\s*:\s*(.+?)(?:</div>|<br|$)")
91
+ if not actors_val:
92
+ # Try selecting the div text directly if regex fails due to HTML tags
93
+ # Find div containing "Oyuncular :"
94
+ all_divs = helper.select("div")
95
+ for div in all_divs:
96
+ txt = div.text()
97
+ if "Oyuncular :" in txt:
98
+ actors_val = txt.split("Oyuncular :")[1].strip()
99
+ break
100
+
101
+ if actors_val:
102
+ # Remove footer / junk from actors
103
+ if "IMDB :" in actors_val:
104
+ actors_val = actors_val.split("IMDB :")[0].strip()
105
+
106
+ if "IMDB :" in actors_val:
107
+ actors_val = actors_val.split("IMDB :")[0].strip()
108
+
109
+ # Remove '×' and other junk if present at end
110
+ if "×" in actors_val:
111
+ actors_val = actors_val.split("×")[0].strip()
112
+
113
+ # Remove simple tags if any remaining
114
+ clean_actors = [a.strip() for a in actors_val.split(",")]
115
+ # Filter empty
116
+ clean_actors = [a for a in clean_actors if a]
117
+
118
+ actors = ", ".join(clean_actors)
119
+
120
+ description_raw = helper.select_text("div.category_desc")
121
+ description = None
122
+ if description_raw:
123
+ # Clean header "The Librarians izle..." etc. if present, usually it is fine.
124
+ # Clean "IMDB :" if attached
125
+ if "IMDB :" in description_raw:
126
+ description_raw = description_raw.split("IMDB :")[0].strip()
127
+
128
+ # Clean footer text start
129
+ # The footer block usually starts with "Dizimom, dizi ve film..."
130
+ # If we find "Dizimom," and it's not at the start (meaning it's part of the footer appended), split there.
131
+ # Note: The description might legitimately start with "Dizimom," strictly speaking, but unlikely for a series description.
132
+ if "Dizimom," in description_raw:
133
+ description = description_raw.split("Dizimom,")[0].strip()
134
+ elif "dizi izle film izle" in description_raw:
135
+ description = description_raw.split("dizi izle film izle")[0].strip()
136
+ else:
137
+ description = description_raw
138
+
139
+ # Fallback cleanup for JSON
140
+ if description and "{" in description:
141
+ description = description.split("{")[0].strip()
142
+
143
+ tags = helper.select_all_text("div.genres a")
144
+
145
+ # Improved year regex
146
+ if not year:
147
+ # Look for "Yapım Yılı : 2014" pattern in ANY text
148
+ # Get all text from category_text which usually contains it
149
+ meta_text = helper.select_text("div.category_text")
150
+ if meta_text:
151
+ match = re.search(r"Yapım Yılı\s*:\s*(\d{4})", meta_text)
152
+ if match:
153
+ year = int(match.group(1))
154
+
155
+ episodes = []
156
+ ep_items = helper.select("div.bolumust")
157
+ for item in ep_items:
158
+ ep_name_raw = helper.select_text("div.baslik", item)
159
+ ep_href = self.fix_url(helper.select_attr("a", "href", item))
160
+
161
+ if ep_name_raw:
162
+ # 1.Sezon 1.Bölüm
163
+ s_m = re.search(r"(\d+)\.Sezon", ep_name_raw)
164
+ e_m = re.search(r"(\d+)\.Bölüm", ep_name_raw)
165
+
166
+ season = int(s_m.group(1)) if s_m else 1
167
+ episode = int(e_m.group(1)) if e_m else 1
168
+
169
+ name = ep_name_raw.split(" izle")[0].replace(title, "").strip()
170
+
171
+ episodes.append(Episode(
172
+ season = season,
173
+ episode = episode,
174
+ title = name,
175
+ url = ep_href
176
+ ))
177
+
178
+ return SeriesInfo(
179
+ url = url,
180
+ poster = poster,
181
+ title = title,
182
+ description = description,
183
+ tags = tags,
184
+ rating = rating,
185
+ year = str(year) if year else None,
186
+ actors = actors,
187
+ episodes = episodes
188
+ )
189
+
190
+ async def load_links(self, url: str) -> list[ExtractResult]:
191
+ # Login simulation headers
192
+ headers = {
193
+ "User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
194
+ "sec-ch-ua": 'Not/A)Brand";v="8", "Chromium";v="137", "Google Chrome";v="137"',
195
+ "sec-ch-ua-mobile": "?1",
196
+ "sec-ch-ua-platform": "Android"
197
+ }
198
+
199
+ # Simulate login (as seen in Kotlin)
200
+ login_url = f"{self.main_url}/wp-login.php"
201
+ login_data = {
202
+ "log": "keyiflerolsun",
203
+ "pwd": "12345",
204
+ "rememberme": "forever",
205
+ "redirect_to": self.main_url
206
+ }
207
+
208
+ await self.httpx.post(login_url, headers=headers, data=login_data)
209
+
210
+ istek = await self.httpx.get(url, headers=headers)
211
+ helper = HTMLHelper(istek.text)
212
+
213
+ iframes = []
214
+
215
+ main_iframe = helper.select_attr("iframe[src]", "src")
216
+ if main_iframe:
217
+ iframes.append(main_iframe)
218
+
219
+ sources = helper.select("div.sources a")
220
+ for source in sources:
221
+ href = source.attrs.get("href")
222
+ if href:
223
+ sub_istek = await self.httpx.get(href, headers=headers)
224
+ sub_helper = HTMLHelper(sub_istek.text)
225
+ sub_iframe = sub_helper.select_attr("div.video p iframe", "src")
226
+ if sub_iframe:
227
+ iframes.append(sub_iframe)
228
+
229
+ results = []
230
+ for iframe_url in iframes:
231
+ # Check for known extractors
232
+ if iframe_url.startswith("//"):
233
+ iframe_url = f"https:{iframe_url}"
234
+
235
+ extract_result = await self.extract(iframe_url)
236
+ if extract_result:
237
+ if isinstance(extract_result, list):
238
+ results.extend(extract_result)
239
+ else:
240
+ results.append(extract_result)
241
+ else:
242
+ results.append(ExtractResult(
243
+ url = iframe_url,
244
+ name = f"{self.name} | External",
245
+ referer = self.main_url
246
+ ))
247
+
248
+ return results
@@ -127,18 +127,27 @@ class DiziPal(PluginBase):
127
127
 
128
128
  poster = self.fix_url(secici.select_attr("meta[property='og:image']", "content")) if secici.select_attr("meta[property='og:image']", "content") else None
129
129
 
130
- # XPath yerine regex ile HTML'den çıkarma
131
- year = secici.regex_first(r'(?is)Yapım Yılı.*?<div[^>]*>(\d{4})</div>', secici.html)
132
-
133
- description = secici.select_text("div.summary p")
130
+ # Sidebar bilgilerini topla
131
+ info = {}
132
+ for li in secici.select("li"):
133
+ key = secici.select_text("div.key", li)
134
+ val = secici.select_text("div.value", li)
135
+ if key and val:
136
+ info[key.strip(":")] = val.strip()
137
+
138
+ year = info.get("Yapım Yılı")
139
+ rating = info.get("IMDB Puanı")
140
+
141
+ tags_raw = info.get("Türler", "")
142
+ tags = [t.strip() for t in tags_raw.split() if t.strip()] if tags_raw else None
134
143
 
135
- rating = secici.regex_first(r'(?is)IMDB Puanı.*?<div[^>]*>([0-9.]+)</div>', secici.html)
144
+ actors_raw = info.get("Oyuncular")
145
+ actors = [a.strip() for a in actors_raw.split(",") if a.strip()] if actors_raw else None
136
146
 
137
- tags_raw = secici.regex_first(r'(?is)Türler.*?<div[^>]*>([^<]+)</div>', secici.html)
138
- tags = [t.strip() for t in tags_raw.split() if t.strip()] if tags_raw else None
147
+ description = secici.select_text("div.summary p")
139
148
 
140
- duration_raw = secici.regex_first(r'(?is)Ortalama Süre.*?<div[^>]*>(\d+)', secici.html)
141
- duration = int(duration_raw) if duration_raw else None
149
+ duration_raw = info.get("Ortalama Süre")
150
+ duration = int(secici.regex_first(r"(\d+)", duration_raw)) if duration_raw else None
142
151
 
143
152
  if "/dizi/" in url:
144
153
  title = secici.select_text("div.cover h5")
@@ -177,6 +186,7 @@ class DiziPal(PluginBase):
177
186
  year = year,
178
187
  duration = duration,
179
188
  episodes = episodes if episodes else None,
189
+ actors = actors,
180
190
  )
181
191
  else:
182
192
  # Film için title - g-title div'lerinin 2. olanı
@@ -192,6 +202,7 @@ class DiziPal(PluginBase):
192
202
  rating = rating,
193
203
  year = year,
194
204
  duration = duration,
205
+ actors = actors,
195
206
  )
196
207
 
197
208
  async def load_links(self, url: str) -> list[ExtractResult]:
@@ -72,7 +72,7 @@ class DiziYou(PluginBase):
72
72
  html_text = istek.text
73
73
 
74
74
  # Title - div.title h1 içinde
75
- title = secici.select_text("div.title h1")
75
+ title = (secici.select_text("div.title h1") or "").strip()
76
76
 
77
77
  # Fallback: Eğer title boşsa URL'den çıkar (telif kısıtlaması olan sayfalar için)
78
78
  if not title:
@@ -81,23 +81,19 @@ class DiziYou(PluginBase):
81
81
  title = slug.replace('-', ' ').title()
82
82
 
83
83
  # Poster
84
- poster_src = secici.select_attr("div.category_image img", "src")
84
+ poster_src = secici.select_attr("div.category_image img", "src") or secici.select_attr("meta[property='og:image']", "content")
85
85
  poster = self.fix_url(poster_src) if poster_src else ""
86
86
 
87
87
  # Year - regex ile çıkarma (xpath yerine)
88
88
  year = secici.regex_first(r"(?is)Yapım Yılı.*?(\d{4})", secici.html)
89
89
 
90
- description = None
91
- # Extract inner HTML via regex and clean
92
- desc_html = secici.regex_first(r'(?s)<div class="diziyou_desc">(.*?)</div>', secici.html)
93
- if desc_html:
94
- # Script taglarını kaldır
95
- desc_html = HTMLHelper(desc_html).regex_replace(r"(?s)<script.*?</script>", "")
96
- # div#icerikcat2 ve sonrasını kaldır (meta bilgileri içeriyor)
97
- desc_html = HTMLHelper(desc_html).regex_replace(r"(?s)<div id=\"icerikcat2\".*", "")
98
- # Kalan HTML'den text çıkar
99
- clean_sel = HTMLHelper(desc_html)
100
- description = clean_sel.select_text()
90
+ description_el = secici.select("div.diziyou_desc") or secici.select("div#icerikcat")
91
+ description = ""
92
+ if description_el:
93
+ # Scriptleri temizle
94
+ for script in secici.select("script", description_el[0]):
95
+ script.decompose()
96
+ description = secici.select_text(None, description_el[0])
101
97
 
102
98
  tags = [secici.select_text(None, a) for a in secici.select("div.genres a") if secici.select_text(None, a)]
103
99
 
@@ -109,9 +105,9 @@ class DiziYou(PluginBase):
109
105
  actors = [actor.strip() for actor in actors_raw.split(",") if actor.strip()] if actors_raw else []
110
106
 
111
107
  episodes = []
112
- # Episodes - daha fazla DOM/URL kalıbını destekle
113
- for link in secici.select("a"):
114
- ep_href = secici.select_attr("a", "href", link)
108
+ # Episodes - div#scrollbar-container a (kısıtlı alan)
109
+ for link in secici.select("div#scrollbar-container a"):
110
+ ep_href = secici.select_attr(None, "href", link)
115
111
  if not ep_href:
116
112
  continue
117
113
 
@@ -179,9 +175,9 @@ class DiziYou(PluginBase):
179
175
  # Player src'den item_id çıkar - önce özel player seçicisini dene
180
176
  player_src = None
181
177
  # Yaygın locatorlar
182
- for sel in ["iframe#diziyouPlayer", "div.player iframe", "iframe[src*='/episodes/']", "iframe"]:
178
+ for sel in ["iframe#diziyouPlayer", "div.player iframe", "iframe[src*='/player/']", "iframe[src*='/episodes/']", "iframe"]:
183
179
  p = secici.select_attr(sel, "src")
184
- if p and "youtube.com" not in p.lower():
180
+ if p and any(x in p.lower() for x in ["/player/", "/episodes/", "diziyou"]):
185
181
  player_src = p
186
182
  break
187
183