KekikStream 2.3.4__tar.gz → 2.3.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 (91) hide show
  1. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/Plugin/PluginBase.py +8 -1
  2. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/ContentX.py +27 -22
  3. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/MolyStream.py +10 -0
  4. kekikstream-2.3.5/KekikStream/Plugins/DiziWatch.py +217 -0
  5. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/Dizilla.py +3 -1
  6. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/SezonlukDizi.py +3 -3
  7. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/Sinefy.py +5 -0
  8. kekikstream-2.3.5/KekikStream/Plugins/YabanciDizi.py +274 -0
  9. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream.egg-info/PKG-INFO +1 -1
  10. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream.egg-info/SOURCES.txt +3 -1
  11. {kekikstream-2.3.4 → kekikstream-2.3.5}/PKG-INFO +1 -1
  12. {kekikstream-2.3.4 → kekikstream-2.3.5}/setup.py +1 -1
  13. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/CLI/__init__.py +0 -0
  14. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/CLI/pypi_kontrol.py +0 -0
  15. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/Extractor/ExtractorBase.py +0 -0
  16. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
  17. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
  18. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/Extractor/ExtractorModels.py +0 -0
  19. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/Extractor/YTDLPCache.py +0 -0
  20. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/HTMLHelper.py +0 -0
  21. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/Media/MediaHandler.py +0 -0
  22. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/Media/MediaManager.py +0 -0
  23. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
  24. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/Plugin/PluginManager.py +0 -0
  25. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/Plugin/PluginModels.py +0 -0
  26. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/UI/UIManager.py +0 -0
  27. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Core/__init__.py +0 -0
  28. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/CloseLoad.py +0 -0
  29. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/DonilasPlay.py +0 -0
  30. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/DzenRu.py +0 -0
  31. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/ExPlay.py +0 -0
  32. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/Filemoon.py +0 -0
  33. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/HDPlayerSystem.py +0 -0
  34. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/JFVid.py +0 -0
  35. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/JetTv.py +0 -0
  36. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/MailRu.py +0 -0
  37. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/MixPlayHD.py +0 -0
  38. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/MixTiger.py +0 -0
  39. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/Odnoklassniki.py +0 -0
  40. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/PeaceMakerst.py +0 -0
  41. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/PixelDrain.py +0 -0
  42. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/PlayerFilmIzle.py +0 -0
  43. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/RapidVid.py +0 -0
  44. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/SetPlay.py +0 -0
  45. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/SetPrime.py +0 -0
  46. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/SibNet.py +0 -0
  47. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/Sobreatsesuyp.py +0 -0
  48. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/TRsTX.py +0 -0
  49. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/TauVideo.py +0 -0
  50. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/TurboImgz.py +0 -0
  51. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/TurkeyPlayer.py +0 -0
  52. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/VCTPlay.py +0 -0
  53. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/VidHide.py +0 -0
  54. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/VidMoly.py +0 -0
  55. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/VidMoxy.py +0 -0
  56. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/VidPapi.py +0 -0
  57. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/VideoSeyred.py +0 -0
  58. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/YTDLP.py +0 -0
  59. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Extractors/YildizKisaFilm.py +0 -0
  60. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/BelgeselX.py +0 -0
  61. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/DiziBox.py +0 -0
  62. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/DiziPal.py +0 -0
  63. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/DiziYou.py +0 -0
  64. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/FilmBip.py +0 -0
  65. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/FilmMakinesi.py +0 -0
  66. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/FilmModu.py +0 -0
  67. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/FullHDFilm.py +0 -0
  68. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/FullHDFilmizlesene.py +0 -0
  69. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/HDFilmCehennemi.py +0 -0
  70. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/JetFilmizle.py +0 -0
  71. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/KultFilmler.py +0 -0
  72. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/RecTV.py +0 -0
  73. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/RoketDizi.py +0 -0
  74. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/SelcukFlix.py +0 -0
  75. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/SetFilmIzle.py +0 -0
  76. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/SineWix.py +0 -0
  77. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/SinemaCX.py +0 -0
  78. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/Sinezy.py +0 -0
  79. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/SuperFilmGeldi.py +0 -0
  80. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/Plugins/UgurFilm.py +0 -0
  81. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/__init__.py +0 -0
  82. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/__main__.py +0 -0
  83. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream/requirements.txt +0 -0
  84. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream.egg-info/dependency_links.txt +0 -0
  85. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream.egg-info/entry_points.txt +0 -0
  86. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream.egg-info/requires.txt +0 -0
  87. {kekikstream-2.3.4 → kekikstream-2.3.5}/KekikStream.egg-info/top_level.txt +0 -0
  88. {kekikstream-2.3.4 → kekikstream-2.3.5}/LICENSE +0 -0
  89. {kekikstream-2.3.4 → kekikstream-2.3.5}/MANIFEST.in +0 -0
  90. {kekikstream-2.3.4 → kekikstream-2.3.5}/README.md +0 -0
  91. {kekikstream-2.3.4 → kekikstream-2.3.5}/setup.cfg +0 -0
@@ -122,7 +122,14 @@ class PluginBase(ABC):
122
122
  try:
123
123
  data = await extractor.extract(url, referer=referer)
124
124
 
125
- # prefix varsa name'e ekle
125
+ # Liste ise her bir öğe için prefix ekle
126
+ if isinstance(data, list):
127
+ for item in data:
128
+ if prefix and item.name:
129
+ item.name = f"{prefix} | {item.name}"
130
+ return data
131
+
132
+ # Tekil öğe ise
126
133
  if prefix and data.name:
127
134
  data.name = f"{prefix} | {data.name}"
128
135
 
@@ -30,7 +30,7 @@ class ContentX(ExtractorBase):
30
30
  istek.raise_for_status()
31
31
  i_source = istek.text
32
32
 
33
- i_extract_value = HTMLHelper(i_source).regex_first(r"window\.openPlayer\('([^']+)'\)")
33
+ i_extract_value = HTMLHelper(i_source).regex_first(r"window\.openPlayer\('([^']+)'")
34
34
  if not i_extract_value:
35
35
  raise ValueError("i_extract is null")
36
36
 
@@ -47,8 +47,12 @@ class ContentX(ExtractorBase):
47
47
  name = sub_lang.replace("\\u0131", "ı")
48
48
  .replace("\\u0130", "İ")
49
49
  .replace("\\u00fc", "ü")
50
- .replace("\\u00e7", "ç"),
51
- url = self.fix_url(sub_url.replace("\\", ""))
50
+ .replace("\\u00e7", "ç")
51
+ .replace("\\u011f", "ğ")
52
+ .replace("\\u015f", "ş")
53
+ .replace("\\u011e", "Ğ")
54
+ .replace("\\u015e", "Ş"),
55
+ url = self.fix_url(sub_url.replace("\\/", "/").replace("\\", ""))
52
56
  )
53
57
  )
54
58
 
@@ -61,7 +65,7 @@ class ContentX(ExtractorBase):
61
65
  if not m3u_link:
62
66
  raise ValueError("vidExtract is null")
63
67
 
64
- m3u_link = m3u_link.replace("\\", "")
68
+ m3u_link = m3u_link.replace("\\", "").replace("/m.php", "/master.m3u8")
65
69
  results = [
66
70
  ExtractResult(
67
71
  name = self.name,
@@ -71,24 +75,25 @@ class ContentX(ExtractorBase):
71
75
  )
72
76
  ]
73
77
 
74
- dublaj_value = HTMLHelper(i_source).regex_first(r',\"([^\"]+)\",\"Türkçe\"')
78
+ dublaj_value = HTMLHelper(i_source).regex_first(r'["\']([^"\']+)["\'],["\']Türkçe["\']')
75
79
  if dublaj_value:
76
- dublaj_source_request = await self.httpx.get(f"{base_url}/source2.php?v={dublaj_value}", headers={"Referer": referer or base_url})
77
- dublaj_source_request.raise_for_status()
78
-
79
- dublaj_source = dublaj_source_request.text
80
- dublaj_link = HTMLHelper(dublaj_source).regex_first(r'file":"([^\"]+)"')
81
- if not dublaj_link:
82
- raise ValueError("dublajExtract is null")
83
-
84
- dublaj_link = dublaj_link.replace("\\", "")
85
- results.append(
86
- ExtractResult(
87
- name = f"{self.name} Türkçe Dublaj",
88
- url = dublaj_link,
89
- referer = url,
90
- subtitles = []
91
- )
92
- )
80
+ try:
81
+ dublaj_source_request = await self.httpx.get(f"{base_url}/source2.php?v={dublaj_value}", headers={"Referer": referer or base_url})
82
+ dublaj_source_request.raise_for_status()
83
+
84
+ dublaj_source = dublaj_source_request.text
85
+ dublaj_link = HTMLHelper(dublaj_source).regex_first(r'file":"([^\"]+)"')
86
+ if dublaj_link:
87
+ dublaj_link = dublaj_link.replace("\\", "")
88
+ results.append(
89
+ ExtractResult(
90
+ name = f"{self.name} Türkçe Dublaj",
91
+ url = dublaj_link,
92
+ referer = url,
93
+ subtitles = []
94
+ )
95
+ )
96
+ except Exception:
97
+ pass
93
98
 
94
99
  return results[0] if len(results) == 1 else results
@@ -7,6 +7,16 @@ class MolyStream(ExtractorBase):
7
7
  name = "MolyStream"
8
8
  main_url = "https://dbx.molystream.org"
9
9
 
10
+ # Birden fazla domain destekle
11
+ supported_domains = [
12
+ "ydx.molystream.org",
13
+ "yd.sheila.stream",
14
+ "ydf.popcornvakti.net",
15
+ ]
16
+
17
+ def can_handle_url(self, url: str) -> bool:
18
+ return any(domain in url for domain in self.supported_domains)
19
+
10
20
  async def extract(self, url, referer=None) -> ExtractResult:
11
21
  if "doctype html" in url:
12
22
  secici = HTMLHelper(url)
@@ -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, SeriesInfo, Episode, ExtractResult, HTMLHelper
4
+ import urllib.parse
5
+
6
+ class DiziWatch(PluginBase):
7
+ name = "DiziWatch"
8
+ language = "tr"
9
+ main_url = "https://diziwatch.to"
10
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
11
+ description = "Diziwatch; en güncel yabancı dizileri ve animeleri, Türkçe altyazılı ve dublaj seçenekleriyle izleyebileceğiniz platform."
12
+
13
+ main_page = {
14
+ f"{main_url}/episodes" : "Yeni Bölümler",
15
+ "9" : "Aksiyon",
16
+ "17" : "Animasyon",
17
+ "5" : "Bilim Kurgu",
18
+ "2" : "Dram",
19
+ "12" : "Fantastik",
20
+ "3" : "Gizem",
21
+ "4" : "Komedi",
22
+ "8" : "Korku",
23
+ "24" : "Macera",
24
+ "14" : "Müzik",
25
+ "7" : "Romantik",
26
+ "23" : "Spor",
27
+ "1" : "Suç",
28
+ }
29
+
30
+ def __init__(self):
31
+ super().__init__()
32
+ self.c_key = None
33
+ self.c_value = None
34
+
35
+ async def _init_session(self):
36
+ if self.c_key and self.c_value:
37
+ return
38
+
39
+ # Fetch anime-arsivi to get CSRF tokens
40
+ resp = await self.httpx.get(f"{self.main_url}/anime-arsivi")
41
+ sel = HTMLHelper(resp.text)
42
+
43
+ # form.bg-[rgba(255,255,255,.15)] > input
44
+ # We can just look for the first two inputs in that specific form
45
+ inputs = sel.select("form.bg-\\[rgba\\(255\\,255\\,255\\,\\.15\\)\\] input")
46
+ if len(inputs) >= 2:
47
+ self.c_key = inputs[0].attrs.get("value")
48
+ self.c_value = inputs[1].attrs.get("value")
49
+
50
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
51
+ await self._init_session()
52
+
53
+ if url.startswith("https://"):
54
+ full_url = f"{url}?page={page}"
55
+ resp = await self.httpx.get(full_url, headers={"Referer": f"{self.main_url}/"})
56
+ sel = HTMLHelper(resp.text)
57
+ items = sel.select("div.swiper-slide a")
58
+ else:
59
+ # Category ID based
60
+ full_url = f"{self.main_url}/anime-arsivi?category={url}&minImdb=&name=&release_year=&sort=date_desc&page={page}"
61
+ resp = await self.httpx.get(full_url, headers={"Referer": f"{self.main_url}/"})
62
+ sel = HTMLHelper(resp.text)
63
+ items = sel.select("div.content-inner a")
64
+
65
+ results = []
66
+ for item in items:
67
+ title = sel.select_text("h2", item)
68
+ href = item.attrs.get("href") if item.tag == "a" else sel.select_attr("a", "href", item)
69
+ poster = sel.select_attr("img", "src", item) or sel.select_attr("img", "data-src", item)
70
+
71
+ if title and href:
72
+ # If it's an episode link, clean it to get show link
73
+ # Regex in Kotlin: /sezon-\d+/bolum-\d+/?$
74
+ clean_href = HTMLHelper(href).regex_replace(r"/sezon-\d+/bolum-\d+/?$", "")
75
+
76
+ # If cleaning changed something, it was an episode link, maybe add it to title
77
+ if clean_href != href:
78
+ se_info = sel.select_text("div.flex.gap-1.items-center", item)
79
+ if se_info:
80
+ title = f"{title} - {se_info}"
81
+
82
+ results.append(MainPageResult(
83
+ category = category,
84
+ title = title,
85
+ url = self.fix_url(clean_href),
86
+ poster = self.fix_url(poster) if poster else None
87
+ ))
88
+
89
+ return results
90
+
91
+ async def search(self, query: str) -> list[SearchResult]:
92
+ await self._init_session()
93
+
94
+ post_url = f"{self.main_url}/bg/searchcontent"
95
+ data = {
96
+ "cKey" : self.c_key,
97
+ "cValue" : self.c_value,
98
+ "searchterm" : query
99
+ }
100
+
101
+ headers = {
102
+ "X-Requested-With" : "XMLHttpRequest",
103
+ "Accept" : "application/json, text/javascript, */*; q=0.01",
104
+ "Referer" : f"{self.main_url}/"
105
+ }
106
+
107
+ resp = await self.httpx.post(post_url, data=data, headers=headers)
108
+
109
+ try:
110
+ raw = resp.json()
111
+ # Kotlin maps this to ApiResponse -> DataWrapper -> Icerikler
112
+ res_array = raw.get("data", {}).get("result", [])
113
+
114
+ results = []
115
+ for item in res_array:
116
+ title = item.get("object_name", "").replace("\\", "")
117
+ slug = item.get("used_slug", "").replace("\\", "")
118
+ poster = item.get("object_poster_url", "")
119
+
120
+ # Cleanup poster URL as in Kotlin
121
+ if poster:
122
+ poster = poster.replace("images-macellan-online.cdn.ampproject.org/i/s/", "") \
123
+ .replace("file.dizilla.club", "file.macellan.online") \
124
+ .replace("images.dizilla.club", "images.macellan.online") \
125
+ .replace("images.dizimia4.com", "images.macellan.online") \
126
+ .replace("file.dizimia4.com", "file.macellan.online")
127
+ poster = HTMLHelper(poster).regex_replace(r"(file\.)[\w\.]+\/?", r"\1macellan.online/")
128
+ poster = HTMLHelper(poster).regex_replace(r"(images\.)[\w\.]+\/?", r"\1macellan.online/")
129
+ poster = poster.replace("/f/f/", "/630/910/")
130
+
131
+ if title and slug:
132
+ results.append(SearchResult(
133
+ title = title,
134
+ url = self.fix_url(slug),
135
+ poster = self.fix_url(poster) if poster else None
136
+ ))
137
+ return results
138
+ except Exception:
139
+ return []
140
+
141
+ async def load_item(self, url: str) -> SeriesInfo:
142
+ resp = await self.httpx.get(url)
143
+ sel = HTMLHelper(resp.text)
144
+
145
+ title = sel.select_text("h2")
146
+ poster = sel.select_attr("img.rounded-md", "src")
147
+ description = sel.select_text("div.text-sm")
148
+
149
+ year = sel.regex_first(r"Yap\u0131m Y\u0131l\u0131\s*:\s*(\d+)", resp.text)
150
+
151
+ tags = []
152
+ tags_raw = sel.regex_first(r"T\u00fcr\s*:\s*([^<]+)", resp.text)
153
+ if tags_raw:
154
+ tags = [t.strip() for t in tags_raw.split(",")]
155
+
156
+ rating = sel.select_text(".font-semibold.text-white")
157
+ if rating:
158
+ rating = rating.replace(",", ".").strip()
159
+
160
+ actors = [a.text(strip=True) for a in sel.select("span.valor a")]
161
+
162
+ trailer_match = sel.regex_first(r"embed\/(.*)\?rel", resp.text)
163
+ trailer = f"https://www.youtube.com/embed/{trailer_match}" if trailer_match else None
164
+
165
+ duration_text = sel.select_text("span.runtime")
166
+ duration = duration_text.split(" ")[0] if duration_text else None
167
+
168
+ episodes = []
169
+ # ul a handles episodes
170
+ for ep_link in sel.select("ul a"):
171
+ href = ep_link.attrs.get("href")
172
+ if not href or "/sezon-" not in href:
173
+ continue
174
+
175
+ ep_name = sel.select_text("span.hidden.sm\\:block", ep_link)
176
+
177
+ season_match = sel.regex_first(r"sezon-(\d+)", href)
178
+ episode_match = sel.regex_first(r"bolum-(\d+)", href)
179
+
180
+ season = season_match if season_match else None
181
+ episode_num = episode_match if episode_match else None
182
+
183
+ episodes.append(Episode(
184
+ season = int(season) if season and season.isdigit() else None,
185
+ episode = int(episode_num) if episode_num and episode_num.isdigit() else None,
186
+ title = ep_name if ep_name else f"{season}x{episode_num}",
187
+ url = self.fix_url(href)
188
+ ))
189
+
190
+ return SeriesInfo(
191
+ title = title,
192
+ url = url,
193
+ poster = self.fix_url(poster) if poster else None,
194
+ description = description,
195
+ rating = rating,
196
+ tags = tags,
197
+ actors = actors,
198
+ year = year,
199
+ episodes = episodes,
200
+ duration = int(duration) if duration and str(duration).isdigit() else None
201
+ )
202
+
203
+ async def load_links(self, url: str) -> list[ExtractResult]:
204
+ resp = await self.httpx.get(url)
205
+ sel = HTMLHelper(resp.text)
206
+
207
+ iframe = sel.select_attr("iframe", "src")
208
+ if not iframe:
209
+ return []
210
+
211
+ iframe_url = self.fix_url(iframe)
212
+ data = await self.extract(iframe_url, referer=f"{self.main_url}/")
213
+
214
+ if not data:
215
+ return []
216
+
217
+ return data if isinstance(data, list) else [data]
@@ -248,4 +248,6 @@ class Dizilla(PluginBase):
248
248
  return []
249
249
 
250
250
  data = await self.extract(iframe_url, referer=f"{self.main_url}/", prefix=first_result.get('language_name', 'Unknown'))
251
- return [data] if data else []
251
+ if not data:
252
+ return []
253
+ return data if isinstance(data, list) else [data]
@@ -75,9 +75,9 @@ class SezonlukDizi(PluginBase):
75
75
 
76
76
  results = []
77
77
  for afis in secici.select("div.afis a"):
78
- title = secici.select_text("div.description", veri)
79
- href = secici.select_attr("a", "href", veri)
80
- poster = secici.select_attr("img", "data-src", veri)
78
+ title = secici.select_text("div.description", afis)
79
+ href = secici.select_attr("a", "href", afis)
80
+ poster = secici.select_attr("img", "data-src", afis)
81
81
 
82
82
  if title and href:
83
83
  results.append(SearchResult(
@@ -159,6 +159,11 @@ class Sinefy(PluginBase):
159
159
  actors = [h5.text(strip=True) for h5 in sel.select("div.content h5") if h5.text(strip=True)]
160
160
 
161
161
  year = sel.select_text("span.item.year")
162
+ if not year and title:
163
+ # Try to extract year from title like "Movie Name(2024)"
164
+ year_match = sel.regex_first(r"\((\d{4})\)", title)
165
+ if year_match:
166
+ year = year_match
162
167
 
163
168
  episodes = []
164
169
  episodes_box_list = sel.select("section.episodes-box")
@@ -0,0 +1,274 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, MovieInfo, Episode, ExtractResult, HTMLHelper
4
+ import json, asyncio, time
5
+
6
+ class YabanciDizi(PluginBase):
7
+ name = "YabanciDizi"
8
+ language = "tr"
9
+ main_url = "https://yabancidizi.so"
10
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
11
+ description = "Yabancidizi.so platformu üzerinden en güncel yabancı dizileri ve filmleri izleyebilir, favori içeriklerinizi takip edebilirsiniz."
12
+
13
+ main_page = {
14
+ f"{main_url}/kesfet/eyJvcmRlciI6ImRhdGVfYm90dG9tIiwia2F0ZWdvcnkiOlsiMTciXX0=" : "Diziler",
15
+ f"{main_url}/kesfet/eyJvcmRlciI6ImRhdGVfYm90dG9tIiwia2F0ZWdvcnkiOlsiMTgiXX0=" : "Filmler",
16
+ f"{main_url}/kesfet/eyJvcmRlciI6ImRhdGVfYm90dG9tIiwiY291bnRyeSI6eyJLUiI6IktSIn19" : "Kdrama",
17
+ f"{main_url}/kesfet/eyJvcmRlciI6ImRhdGVfYm90dG9tIiwiY291bnRyeSI6eyJKUCI6IkpQIn0sImNhdGVnb3J5IjpbXX0=" : "Jdrama",
18
+ f"{main_url}/kesfet/eyJvcmRlciI6ImRhdGVfYm90dG9tIiwiY2F0ZWdvcnkiOnsiMyI6IjMifX0=" : "Animasyon",
19
+ }
20
+
21
+ async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
22
+ full_url = url if page == 1 else f"{url}/{page}"
23
+
24
+ resp = await self.httpx.get(full_url, headers={"Referer": f"{self.main_url}/"})
25
+ sel = HTMLHelper(resp.text)
26
+
27
+ results = []
28
+ for item in sel.select("li.mb-lg, li.segment-poster"):
29
+ title = sel.select_text("h2", item)
30
+ href = sel.select_attr("a", "href", item)
31
+ poster = sel.select_attr("img", "src", item)
32
+ score = sel.select_text("span.rating", item)
33
+
34
+ if title and href:
35
+ results.append(MainPageResult(
36
+ category = category,
37
+ title = title,
38
+ url = self.fix_url(href),
39
+ poster = self.fix_url(poster) if poster else None,
40
+ ))
41
+
42
+ return results
43
+
44
+ async def search(self, query: str) -> list[SearchResult]:
45
+ search_url = f"{self.main_url}/search?qr={query}"
46
+
47
+ headers = {
48
+ "X-Requested-With" : "XMLHttpRequest",
49
+ "Referer" : f"{self.main_url}/"
50
+ }
51
+
52
+ resp = await self.httpx.post(search_url, headers=headers)
53
+
54
+ try:
55
+ raw = resp.json()
56
+ # Kotlin mapping: JsonResponse -> Data -> ResultItem
57
+ res_array = raw.get("data", {}).get("result", [])
58
+
59
+ results = []
60
+ for item in res_array:
61
+ title = item.get("s_name")
62
+ image = item.get("s_image")
63
+ slug = item.get("s_link")
64
+ s_type = item.get("s_type") # 0: dizi, 1: film
65
+
66
+ poster = f"{self.main_url}/uploads/series/{image}" if image else None
67
+
68
+ if s_type == "1":
69
+ href = f"{self.main_url}/film/{slug}"
70
+ else:
71
+ href = f"{self.main_url}/dizi/{slug}"
72
+
73
+ if title and slug:
74
+ results.append(SearchResult(
75
+ title = title,
76
+ url = self.fix_url(href),
77
+ poster = self.fix_url(poster) if poster else None
78
+ ))
79
+ return results
80
+ except Exception:
81
+ return []
82
+
83
+ async def load_item(self, url: str) -> SeriesInfo | MovieInfo:
84
+ resp = await self.httpx.get(url, follow_redirects=True)
85
+ sel = HTMLHelper(resp.text)
86
+
87
+ og_title = sel.select_attr("meta[property='og:title']", "content")
88
+ title = og_title.split("|")[0].strip() if og_title else sel.select_text("h1")
89
+
90
+ poster = sel.select_attr("meta[property='og:image']", "content")
91
+ description = sel.select_text("p#tv-series-desc")
92
+
93
+ year = sel.select_text("td div.truncate")
94
+ if year:
95
+ year = year.strip()
96
+
97
+ tags = []
98
+ rating = None
99
+ duration = None
100
+ year = None
101
+ actors = []
102
+ for item in sel.select("div.item"):
103
+ text = item.text(strip=True)
104
+ if "T\u00fcr\u00fc:" in text:
105
+ tags = [t.strip() for t in text.replace("T\u00fcr\u00fc:", "").split(",")]
106
+ elif "IMDb Puan\u0131" in text:
107
+ rating = text.replace("IMDb Puan\u0131", "").strip()
108
+ elif "Yap\u0131m Y\u0131l\u0131" in text:
109
+ year_match = sel.regex_first(r"(\d{4})", text)
110
+ if year_match:
111
+ year = year_match
112
+ elif "Takip\u00e7iler" in text:
113
+ continue
114
+ elif "S\u00fcre" in text:
115
+ dur_match = sel.regex_first(r"(\d+)", text)
116
+ if dur_match:
117
+ duration = dur_match
118
+ elif "Oyuncular:" in text:
119
+ actors = [a.text(strip=True) for a in sel.select("a", item)]
120
+
121
+ if not actors:
122
+ actors = [a.text(strip=True) for a in sel.select("div#common-cast-list div.item h5")]
123
+
124
+ trailer_match = sel.regex_first(r"embed\/(.*)\?rel", resp.text)
125
+ trailer = f"https://www.youtube.com/embed/{trailer_match}" if trailer_match else None
126
+
127
+ if "/film/" in url:
128
+ return MovieInfo(
129
+ title = title,
130
+ url = url,
131
+ poster = self.fix_url(poster) if poster else None,
132
+ description = description,
133
+ rating = rating,
134
+ tags = tags,
135
+ actors = actors,
136
+ year = year,
137
+ duration = int(duration) if duration and duration.isdigit() else None
138
+ )
139
+ else:
140
+ episodes = []
141
+ for bolum_item in sel.select("div.episodes-list div.ui td:has(h6)"):
142
+ link_el = sel.select_first("a", bolum_item)
143
+ if not link_el: continue
144
+
145
+ bolum_href = link_el.attrs.get("href")
146
+ bolum_name = sel.select_text("h6", bolum_item) or link_el.text(strip=True)
147
+
148
+ season = sel.regex_first(r"sezon-(\d+)", bolum_href)
149
+ episode = sel.regex_first(r"bolum-(\d+)", bolum_href)
150
+
151
+ ep_season = int(season) if season and season.isdigit() else None
152
+ ep_episode = int(episode) if episode and episode.isdigit() else None
153
+
154
+ episodes.append(Episode(
155
+ season = ep_season,
156
+ episode = ep_episode,
157
+ title = bolum_name,
158
+ url = self.fix_url(bolum_href)
159
+ ))
160
+
161
+ if episodes and (episodes[0].episode or 0) > (episodes[-1].episode or 0):
162
+ episodes.reverse()
163
+
164
+ return SeriesInfo(
165
+ title = title,
166
+ url = url,
167
+ poster = self.fix_url(poster) if poster else None,
168
+ description = description,
169
+ rating = rating,
170
+ tags = tags,
171
+ actors = actors,
172
+ year = year,
173
+ episodes = episodes
174
+ )
175
+
176
+ async def load_links(self, url: str) -> list[ExtractResult]:
177
+ resp = await self.httpx.get(url, headers={"Referer": f"{self.main_url}/"})
178
+ sel = HTMLHelper(resp.text)
179
+
180
+ results = []
181
+
182
+ # Method 1: alternatives-for-this
183
+ for alt in sel.select("div.alternatives-for-this div.item:not(.active)"):
184
+ data_hash = alt.attrs.get("data-hash")
185
+ data_link = alt.attrs.get("data-link")
186
+ q_type = alt.attrs.get("data-querytype")
187
+
188
+ if not data_hash or not data_link: continue
189
+
190
+ try:
191
+ post_resp = await self.httpx.post(
192
+ f"{self.main_url}/ajax/service",
193
+ data = {
194
+ "link" : data_link,
195
+ "hash" : data_hash,
196
+ "querytype" : q_type,
197
+ "type" : "videoGet"
198
+ },
199
+ headers = {
200
+ "X-Requested-With" : "XMLHttpRequest",
201
+ "Referer" : f"{self.main_url}/"
202
+ },
203
+ cookies = {"udys": "1760709729873", "level": "1"}
204
+ )
205
+
206
+ service_data = post_resp.json()
207
+ api_iframe = service_data.get("api_iframe")
208
+ if api_iframe:
209
+ extract_res = await self._fetch_and_extract(api_iframe, prefix="Alt")
210
+ if extract_res:
211
+ results.extend(extract_res if isinstance(extract_res, list) else [extract_res])
212
+ except Exception:
213
+ continue
214
+
215
+ # Method 2: pointing[data-eid]
216
+ for id_el in sel.select("a.ui.pointing[data-eid]"):
217
+ dil = id_el.text(strip=True)
218
+ v_lang = "tr" if "Dublaj" in dil else "en"
219
+ data_eid = id_el.attrs.get("data-eid")
220
+
221
+ try:
222
+ post_resp = await self.httpx.post(
223
+ f"{self.main_url}/ajax/service",
224
+ data = {
225
+ "e_id" : data_eid,
226
+ "v_lang" : v_lang,
227
+ "type" : "get_whatwehave"
228
+ },
229
+ headers = {
230
+ "X-Requested-With" : "XMLHttpRequest",
231
+ "Referer" : f"{self.main_url}/"
232
+ },
233
+ cookies = {"udys": "1760709729873", "level": "1"}
234
+ )
235
+
236
+ service_data = post_resp.json()
237
+ api_iframe = service_data.get("api_iframe")
238
+ if api_iframe:
239
+ extract_res = await self._fetch_and_extract(api_iframe, prefix=dil)
240
+ if extract_res:
241
+ results.extend(extract_res if isinstance(extract_res, list) else [extract_res])
242
+ except Exception:
243
+ continue
244
+
245
+ return results
246
+
247
+ async def _fetch_and_extract(self, iframe_url, prefix=""):
248
+ # Initial fetch
249
+ resp = await self.httpx.get(
250
+ iframe_url,
251
+ headers = {"Referer": f"{self.main_url}/"},
252
+ cookies = {"udys": "1760709729873", "level": "1"}
253
+ )
254
+
255
+ # Handle "Lütfen bekleyiniz" check from Kotlin
256
+ if "Lütfen bekleyiniz" in resp.text:
257
+ await asyncio.sleep(1)
258
+ timestamp = int(time.time())
259
+ # Retry with t=timestamp as in Kotlin
260
+ sep = "&" if "?" in iframe_url else "?"
261
+ resp = await self.httpx.get(
262
+ f"{iframe_url}{sep}t={timestamp}",
263
+ headers = {"Referer": f"{self.main_url}/"},
264
+ cookies = resp.cookies # Use cookies from first response
265
+ )
266
+
267
+ sel = HTMLHelper(resp.text)
268
+ final_iframe = sel.select_attr("iframe", "src")
269
+
270
+ if final_iframe:
271
+ final_url = self.fix_url(final_iframe)
272
+ return await self.extract(final_url, referer=f"{self.main_url}/", prefix=prefix)
273
+
274
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: KekikStream
3
- Version: 2.3.4
3
+ Version: 2.3.5
4
4
  Summary: terminal üzerinden medya içeriği aramanızı ve VLC/MPV gibi popüler medya oynatıcılar aracılığıyla doğrudan izlemenizi sağlayan modüler ve genişletilebilir bir bıdı bıdı
5
5
  Home-page: https://github.com/keyiflerolsun/KekikStream
6
6
  Author: keyiflerolsun
@@ -64,6 +64,7 @@ KekikStream/Extractors/YildizKisaFilm.py
64
64
  KekikStream/Plugins/BelgeselX.py
65
65
  KekikStream/Plugins/DiziBox.py
66
66
  KekikStream/Plugins/DiziPal.py
67
+ KekikStream/Plugins/DiziWatch.py
67
68
  KekikStream/Plugins/DiziYou.py
68
69
  KekikStream/Plugins/Dizilla.py
69
70
  KekikStream/Plugins/FilmBip.py
@@ -84,4 +85,5 @@ KekikStream/Plugins/Sinefy.py
84
85
  KekikStream/Plugins/SinemaCX.py
85
86
  KekikStream/Plugins/Sinezy.py
86
87
  KekikStream/Plugins/SuperFilmGeldi.py
87
- KekikStream/Plugins/UgurFilm.py
88
+ KekikStream/Plugins/UgurFilm.py
89
+ KekikStream/Plugins/YabanciDizi.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: KekikStream
3
- Version: 2.3.4
3
+ Version: 2.3.5
4
4
  Summary: terminal üzerinden medya içeriği aramanızı ve VLC/MPV gibi popüler medya oynatıcılar aracılığıyla doğrudan izlemenizi sağlayan modüler ve genişletilebilir bir bıdı bıdı
5
5
  Home-page: https://github.com/keyiflerolsun/KekikStream
6
6
  Author: keyiflerolsun
@@ -6,7 +6,7 @@ from io import open
6
6
  setup(
7
7
  # ? Genel Bilgiler
8
8
  name = "KekikStream",
9
- version = "2.3.4",
9
+ version = "2.3.5",
10
10
  url = "https://github.com/keyiflerolsun/KekikStream",
11
11
  description = "terminal üzerinden medya içeriği aramanızı ve VLC/MPV gibi popüler medya oynatıcılar aracılığıyla doğrudan izlemenizi sağlayan modüler ve genişletilebilir bir bıdı bıdı",
12
12
  keywords = ["KekikStream", "KekikAkademi", "keyiflerolsun"],
File without changes
File without changes
File without changes
File without changes