KekikStream 1.8.0__py3-none-any.whl → 1.8.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,37 +6,35 @@ import subprocess, os
6
6
 
7
7
  class MediaHandler:
8
8
  def __init__(self, title: str = "KekikStream"):
9
- self.title = title
9
+ self.title = title
10
+ self.headers = {}
10
11
 
11
12
  def play_media(self, extract_data: ExtractResult):
12
- # Headers dict'ini user_agent ve referer'dan oluştur
13
- headers = {}
14
-
15
13
  # user-agent ekle (varsayılan veya extract_data'dan)
16
14
  user_agent = extract_data.user_agent or "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)"
17
- headers["user-agent"] = user_agent
18
-
15
+ self.headers["user-agent"] = user_agent
16
+
19
17
  # referer ekle
20
18
  if extract_data.referer:
21
- headers["referer"] = extract_data.referer
22
-
19
+ self.headers["referer"] = extract_data.referer
20
+
23
21
  # Google Drive gibi özel durumlar için yt-dlp kullan
24
22
  if user_agent in ["googleusercontent", "Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0"]:
25
- return self.play_with_ytdlp(extract_data, headers)
23
+ return self.play_with_ytdlp(extract_data)
26
24
 
27
25
  # İşletim sistemine göre oynatıcı seç
28
26
  if subprocess.check_output(['uname', '-o']).strip() == b'Android':
29
- return self.play_with_android_mxplayer(extract_data, headers)
27
+ return self.play_with_android_mxplayer(extract_data)
30
28
 
31
29
  # Alt yazılar varsa mpv kullan
32
30
  if extract_data.subtitles:
33
- return self.play_with_mpv(extract_data, headers)
31
+ return self.play_with_mpv(extract_data)
34
32
 
35
- return self.play_with_vlc(extract_data, headers) or self.play_with_mpv(extract_data, headers)
33
+ return self.play_with_vlc(extract_data) or self.play_with_mpv(extract_data)
36
34
 
37
- def play_with_vlc(self, extract_data: ExtractResult, headers: dict):
35
+ def play_with_vlc(self, extract_data: ExtractResult):
38
36
  konsol.log(f"[yellow][»] VLC ile Oynatılıyor : {extract_data.url}")
39
- # konsol.print(headers)
37
+ # konsol.print(self.headers)
40
38
  try:
41
39
  vlc_command = ["vlc", "--quiet"]
42
40
 
@@ -46,11 +44,11 @@ class MediaHandler:
46
44
  f"--input-title-format={self.title}"
47
45
  ])
48
46
 
49
- if "user-agent" in headers:
50
- vlc_command.append(f"--http-user-agent={headers.get('user-agent')}")
47
+ if "user-agent" in self.headers:
48
+ vlc_command.append(f"--http-user-agent={self.headers.get('user-agent')}")
51
49
 
52
- if "referer" in headers:
53
- vlc_command.append(f"--http-referrer={headers.get('referer')}")
50
+ if "referer" in self.headers:
51
+ vlc_command.append(f"--http-referrer={self.headers.get('referer')}")
54
52
 
55
53
  vlc_command.extend(
56
54
  f"--sub-file={subtitle.url}" for subtitle in extract_data.subtitles
@@ -63,23 +61,23 @@ class MediaHandler:
63
61
  return True
64
62
  except subprocess.CalledProcessError as hata:
65
63
  konsol.print(f"[red]VLC oynatma hatası: {hata}[/red]")
66
- konsol.print({"title": self.title, "url": extract_data.url, "headers": headers})
64
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
67
65
  return False
68
66
  except FileNotFoundError:
69
67
  konsol.print("[red]VLC bulunamadı! VLC kurulu olduğundan emin olun.[/red]")
70
- # konsol.print({"title": self.title, "url": extract_data.url, "headers": headers})
68
+ # konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
71
69
  return False
72
70
 
73
- def play_with_mpv(self, extract_data: ExtractResult, headers: dict):
71
+ def play_with_mpv(self, extract_data: ExtractResult):
74
72
  konsol.log(f"[yellow][»] MPV ile Oynatılıyor : {extract_data.url}")
75
- # konsol.print(headers)
73
+ # konsol.print(self.headers)
76
74
  try:
77
75
  mpv_command = ["mpv"]
78
76
 
79
77
  if self.title:
80
78
  mpv_command.append(f"--force-media-title={self.title}")
81
79
 
82
- for key, value in headers.items():
80
+ for key, value in self.headers.items():
83
81
  mpv_command.append(f"--http-header-fields={key}: {value}")
84
82
 
85
83
  mpv_command.extend(
@@ -92,18 +90,18 @@ class MediaHandler:
92
90
 
93
91
  except subprocess.CalledProcessError as hata:
94
92
  konsol.print(f"[red]mpv oynatma hatası: {hata}[/red]")
95
- konsol.print({"title": self.title, "url": extract_data.url, "headers": headers})
93
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
96
94
  except FileNotFoundError:
97
95
  konsol.print("[red]mpv bulunamadı! mpv kurulu olduğundan emin olun.[/red]")
98
- konsol.print({"title": self.title, "url": extract_data.url, "headers": headers})
96
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
99
97
 
100
- def play_with_ytdlp(self, extract_data: ExtractResult, headers: dict):
98
+ def play_with_ytdlp(self, extract_data: ExtractResult):
101
99
  konsol.log(f"[yellow][»] yt-dlp ile Oynatılıyor : {extract_data.url}")
102
- # konsol.print(headers)
100
+ # konsol.print(self.headers)
103
101
  try:
104
102
  ytdlp_command = ["yt-dlp", "--quiet", "--no-warnings"]
105
103
 
106
- for key, value in headers.items():
104
+ for key, value in self.headers.items():
107
105
  ytdlp_command.extend(["--add-header", f"{key}: {value}"])
108
106
 
109
107
  ytdlp_command.extend([
@@ -125,14 +123,14 @@ class MediaHandler:
125
123
 
126
124
  except subprocess.CalledProcessError as hata:
127
125
  konsol.print(f"[red]Oynatma hatası: {hata}[/red]")
128
- konsol.print({"title": self.title, "url": extract_data.url, "headers": headers})
126
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
129
127
  except FileNotFoundError:
130
128
  konsol.print("[red]yt-dlp veya mpv bulunamadı! Kurulumlarından emin olun.[/red]")
131
- konsol.print({"title": self.title, "url": extract_data.url, "headers": headers})
129
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
132
130
 
133
- def play_with_android_mxplayer(self, extract_data: ExtractResult, headers: dict):
131
+ def play_with_android_mxplayer(self, extract_data: ExtractResult):
134
132
  konsol.log(f"[yellow][»] MxPlayer ile Oynatılıyor : {extract_data.url}")
135
- # konsol.print(headers)
133
+ # konsol.print(self.headers)
136
134
  paketler = [
137
135
  "com.mxtech.videoplayer.ad/.ActivityScreen", # Free sürüm
138
136
  "com.mxtech.videoplayer.pro/.ActivityScreen" # Pro sürüm
@@ -157,7 +155,7 @@ class MediaHandler:
157
155
 
158
156
  except subprocess.CalledProcessError as hata:
159
157
  konsol.print(f"[red]{paket} oynatma hatası: {hata}[/red]")
160
- konsol.print({"title": self.title, "url": extract_data.url, "headers": headers})
158
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
161
159
  except FileNotFoundError:
162
160
  konsol.print(f"Paket: {paket}, Hata: MX Player kurulu değil")
163
- konsol.print({"title": self.title, "url": extract_data.url, "headers": headers})
161
+ konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
@@ -7,9 +7,9 @@ import re
7
7
  class DiziPal(PluginBase):
8
8
  name = "DiziPal"
9
9
  language = "tr"
10
- main_url = "https://dizipal1222.com"
10
+ main_url = "https://dizipal1223.com"
11
11
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
- description = "Yabancı Dizi ve Film izle."
12
+ description = "dizipal güncel, dizipal yeni ve gerçek adresi. dizipal en yeni dizi ve filmleri güvenli ve hızlı şekilde sunar."
13
13
 
14
14
  main_page = {
15
15
  f"{main_url}/diziler/son-bolumler" : "Son Bölümler",
@@ -53,7 +53,7 @@ class DiziYou(PluginBase):
53
53
  SearchResult(
54
54
  title = afis.css("div#categorytitle a::text").get().strip(),
55
55
  url = self.fix_url(afis.css("div#categorytitle a::attr(href)").get()),
56
- poster = self.fix_url(afis.css("img::attr(src)").get()),
56
+ poster = self.fix_url(afis.css("img::attr(src)").get() or afis.css("img::attr(data-src)").get())
57
57
  )
58
58
  for afis in secici.css("div.incontent div#list-series")
59
59
  ]
@@ -63,8 +63,19 @@ class DiziYou(PluginBase):
63
63
  istek = await self.httpx.get(url)
64
64
  secici = Selector(istek.text)
65
65
 
66
- title = secici.css("h1::text").get().strip()
67
- poster = self.fix_url(secici.css("div.category_image img::attr(src)").get().strip())
66
+ # Title - div.title h1 içinde
67
+ title_raw = secici.css("div.title h1::text").get()
68
+ title = title_raw.strip() if title_raw else ""
69
+
70
+ # Fallback: Eğer title boşsa URL'den çıkar (telif kısıtlaması olan sayfalar için)
71
+ if not title:
72
+ # URL'den slug'ı al: https://www.diziyou.one/jasmine/ -> jasmine -> Jasmine
73
+ slug = url.rstrip('/').split('/')[-1]
74
+ title = slug.replace('-', ' ').title()
75
+
76
+ # Poster
77
+ poster_raw = secici.css("div.category_image img::attr(src)").get()
78
+ poster = self.fix_url(poster_raw) if poster_raw else ""
68
79
  year = secici.xpath("//span[contains(., 'Yapım Yılı')]/following-sibling::text()[1]").get()
69
80
  description = secici.css("div.diziyou_desc::text").get()
70
81
  if description:
@@ -75,13 +86,21 @@ class DiziYou(PluginBase):
75
86
  actors = [actor.strip() for actor in _actors.split(",")] if _actors else []
76
87
 
77
88
  episodes = []
78
- for it in secici.css("div.bolumust"):
79
- ep_name = it.css("div.baslik::text").get().strip()
80
- ep_href = it.xpath("ancestor::a/@href").get()
81
- if not ep_name or not ep_href:
89
+ # Episodes - bolumust her bölüm için bir <a> içinde
90
+ # :has() parsel'de çalışmıyor, XPath kullanıyoruz
91
+ for link in secici.xpath('//a[div[@class="bolumust"]]'):
92
+ ep_name_raw = link.css("div.baslik::text").get()
93
+ if not ep_name_raw:
94
+ continue
95
+ ep_name = ep_name_raw.strip()
96
+
97
+ ep_href = self.fix_url(link.css("::attr(href)").get())
98
+ if not ep_href:
82
99
  continue
83
100
 
84
- ep_name_clean = it.css("div.bolumismi::text").get().strip().replace("(", "").replace(")", "").strip() if it.css("div.bolumismi::text").get() else ep_name
101
+ # Bölüm ismi varsa al
102
+ ep_name_raw_clean = link.css("div.bolumismi::text").get()
103
+ ep_name_clean = ep_name_raw_clean.strip().replace("(", "").replace(")", "").strip() if ep_name_raw_clean else ep_name
85
104
 
86
105
  ep_episode = re.search(r"(\d+)\. Bölüm", ep_name)[1]
87
106
  ep_season = re.search(r"(\d+)\. Sezon", ep_name)[1]
@@ -112,9 +131,19 @@ class DiziYou(PluginBase):
112
131
  istek = await self.httpx.get(url)
113
132
  secici = Selector(istek.text)
114
133
 
115
- item_title = secici.css("div.title h1::text").get()
116
- ep_name = secici.css("div#bolum-ismi::text").get().strip()
117
- item_id = secici.css("iframe#diziyouPlayer::attr(src)").get().split("/")[-1].replace(".html", "")
134
+ # Title ve episode name - None kontrolü ekle
135
+ item_title_raw = secici.css("div.title h1::text").get()
136
+ item_title = item_title_raw.strip() if item_title_raw else ""
137
+
138
+ ep_name_raw = secici.css("div#bolum-ismi::text").get()
139
+ ep_name = ep_name_raw.strip() if ep_name_raw else ""
140
+
141
+ # Player src'den item_id çıkar
142
+ player_src = secici.css("iframe#diziyouPlayer::attr(src)").get()
143
+ if not player_src:
144
+ return [] # Player bulunamadıysa boş liste döndür
145
+
146
+ item_id = player_src.split("/")[-1].replace(".html", "")
118
147
 
119
148
  subtitles = []
120
149
  stream_urls = []
@@ -16,7 +16,6 @@ class Dizilla(PluginBase):
16
16
 
17
17
  main_page = {
18
18
  f"{main_url}/tum-bolumler" : "Altyazılı Bölümler",
19
- f"{main_url}/dublaj-bolumler" : "Dublaj Bölümler",
20
19
  f"{main_url}/dizi-turu/aile" : "Aile",
21
20
  f"{main_url}/dizi-turu/aksiyon" : "Aksiyon",
22
21
  f"{main_url}/dizi-turu/bilim-kurgu" : "Bilim Kurgu",
@@ -35,11 +34,11 @@ class Dizilla(PluginBase):
35
34
  ana_sayfa.extend([
36
35
  MainPageResult(
37
36
  category = category,
38
- title = veri.css("h2::text").get(),
39
- url = self.fix_url(veri.css("::attr(href)").get()),
37
+ title = veri.css("span.font-normal::text").get(),
38
+ url = self.fix_url(veri.css("a::attr(href)").get()),
40
39
  poster = self.fix_url(veri.css("img::attr(src)").get() or veri.css("img::attr(data-src)").get())
41
40
  )
42
- for veri in secici.css("div.grid-cols-3 a")
41
+ for veri in secici.css("span.watchlistitem-")
43
42
  ])
44
43
  else:
45
44
  for veri in secici.css("div.tab-content > div.grid a"):
@@ -127,7 +126,11 @@ class Dizilla(PluginBase):
127
126
  poster = self.fix_url(veri.get("image"))
128
127
  description = veri.get("description")
129
128
  year = veri.get("datePublished").split("-")[0]
130
- tags = []
129
+
130
+ # Tags extraction from page content (h3 tag)
131
+ tags_raw = secici.css("h3.text-white.opacity-60::text").get()
132
+ tags = [t.strip() for t in tags_raw.split(",")] if tags_raw else []
133
+
131
134
  rating = veri.get("aggregateRating", {}).get("ratingValue")
132
135
  actors = [actor.get("name") for actor in veri.get("actor", []) if actor.get("name")]
133
136
 
@@ -8,7 +8,7 @@ class FilmBip(PluginBase):
8
8
  language = "tr"
9
9
  main_url = "https://filmbip.com"
10
10
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
11
- description = "Film izleme sitesi."
11
+ description = "FilmBip adlı film sitemizde Full HD film izle. Yerli ve yabancı filmleri Türkçe dublaj veya altyazılı şekilde 1080p yüksek kalite film izle"
12
12
 
13
13
  main_page = {
14
14
  f"{main_url}/filmler/SAYFA" : "Yeni Filmler",
@@ -2,12 +2,14 @@
2
2
 
3
3
  from KekikStream.Core import PluginBase, MainPageResult, SearchResult, SeriesInfo, Episode, MovieInfo
4
4
  from parsel import Selector
5
- import re, base64, json, urllib.parse
5
+ import re, base64, json
6
6
 
7
7
  class RoketDizi(PluginBase):
8
8
  name = "RoketDizi"
9
9
  lang = "tr"
10
- main_url = "https://flatscher.net"
10
+ main_url = "https://roketdizi.to"
11
+ favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
+ description = "Türkiye'nin en tatlış yabancı dizi izleme sitesi. Türkçe dublaj, altyazılı, eski ve yeni yabancı dizilerin yanı sıra kore (asya) dizileri izleyebilirsiniz."
11
13
 
12
14
  # Domain doğrulama ve anti-bot mekanizmaları var
13
15
  requires_cffi = True
@@ -25,81 +27,70 @@ class RoketDizi(PluginBase):
25
27
 
26
28
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
27
29
  full_url = f"{self.main_url}/{url}?&page={page}"
28
- resp = await self.cffi.get(full_url)
29
- sel = Selector(resp.text)
30
+ resp = await self.cffi.get(full_url)
31
+ sel = Selector(resp.text)
30
32
 
31
33
  results = []
32
34
 
33
35
  for item in sel.css("div.w-full.p-4 span.bg-\\[\\#232323\\]"):
34
- title = item.css("span.font-normal.line-clamp-1::text").get()
35
- href = item.css("a::attr(href)").get()
36
- poster= item.css("img::attr(src)").get()
36
+ title = item.css("span.font-normal.line-clamp-1::text").get()
37
+ href = item.css("a::attr(href)").get()
38
+ poster = item.css("img::attr(src)").get()
37
39
 
38
40
  if title and href:
39
41
  results.append(MainPageResult(
40
- category=category,
41
- title=title,
42
- url=self.fix_url(href),
43
- poster=self.fix_url(poster)
42
+ category = category,
43
+ title = title,
44
+ url = self.fix_url(href),
45
+ poster = self.fix_url(poster)
44
46
  ))
45
47
  return results
46
48
 
47
- async def get_domain(self):
48
- try:
49
- domain_list = await self.cffi.get("https://raw.githubusercontent.com/Kraptor123/domainListesi/refs/heads/main/eklenti_domainleri.txt")
50
- if domain_list.status_code == 200:
51
- for line in domain_list.text.split("|"):
52
- if line.strip().startswith("RoketDizi"):
53
- domain = line.split(":")[-1].strip()
54
- if "http" not in domain:
55
- domain = f"https://{domain}"
56
- return domain
57
- except Exception:
58
- pass
59
- return self.main_url
60
-
61
49
  async def search(self, query: str) -> list[SearchResult]:
62
- current_domain = await self.get_domain()
63
-
64
- # Get Cookies and Keys
65
- main_req = await self.cffi.get(current_domain)
66
- sel = Selector(main_req.text)
67
-
68
- c_key = sel.css("input[name='cKey']::attr(value)").get()
69
- c_value = sel.css("input[name='cValue']::attr(value)").get()
70
-
71
- post_url = f"{current_domain}/api/bg/searchContent?searchterm={query}"
50
+ post_url = f"{self.main_url}/api/bg/searchContent?searchterm={query}"
72
51
 
73
52
  headers = {
74
- "Accept": "application/json, text/javascript, */*; q=0.01",
75
- "X-Requested-With": "XMLHttpRequest",
76
- "Referer": f"{current_domain}/",
77
- "CNT": "vakTR"
53
+ "Accept" : "application/json, text/javascript, */*; q=0.01",
54
+ "X-Requested-With" : "XMLHttpRequest",
55
+ "Referer" : f"{self.main_url}/",
78
56
  }
79
-
80
- data = {}
81
- if c_key and c_value:
82
- data = {"cKey": c_key, "cValue": c_value}
83
57
 
84
- search_req = await self.cffi.post(post_url, data=data, headers=headers)
58
+ search_req = await self.cffi.post(post_url, headers=headers)
85
59
 
86
60
  try:
87
61
  resp_json = search_req.json()
88
- if not resp_json.get("state"):
62
+
63
+ # Response is base64 encoded!
64
+ if not resp_json.get("success"):
65
+ return []
66
+
67
+ encoded_response = resp_json.get("response", "")
68
+ if not encoded_response:
69
+ return []
70
+
71
+ # Decode base64
72
+ decoded = base64.b64decode(encoded_response).decode('utf-8')
73
+ data = json.loads(decoded)
74
+
75
+ if not data.get("state"):
89
76
  return []
90
77
 
91
- html_content = resp_json.get("html", "").strip()
92
- sel_results = Selector(html_content)
93
-
94
78
  results = []
95
- items = re.findall(r'<a href="([^"]+)".*?data-srcset="([^"]+).*?<span class="text-white">([^<]+)', html_content, re.DOTALL)
79
+ result_items = data.get("result", [])
96
80
 
97
- for href, poster, title in items:
98
- results.append(SearchResult(
99
- title=title.strip(),
100
- url=self.fix_url(href.strip(), current_domain),
101
- poster=self.fix_url(poster.strip(), current_domain)
102
- ))
81
+ for item in result_items:
82
+ title = item.get("object_name", "")
83
+ slug = item.get("used_slug", "")
84
+ poster = item.get("object_poster_url", "")
85
+
86
+ if title and slug:
87
+ # Construct full URL from slug
88
+ full_url = f"{self.main_url}/{slug}"
89
+ results.append(SearchResult(
90
+ title = title.strip(),
91
+ url = full_url,
92
+ poster = self.fix_url(poster) if poster else None
93
+ ))
103
94
 
104
95
  return results
105
96
 
@@ -115,14 +106,41 @@ class RoketDizi(PluginBase):
115
106
  poster = sel.css("div.w-full.page-top img::attr(src)").get()
116
107
  description = sel.css("div.mt-2.text-sm::text").get()
117
108
 
118
- year = None # Implement if critical
109
+ # Tags - genre bilgileri (Detaylar bölümünde)
110
+ tags = []
111
+ genre_text = sel.css("h3.text-white.opacity-90::text").get()
112
+ if genre_text:
113
+ tags = [t.strip() for t in genre_text.split(",")]
119
114
 
120
- tags = sel.css("h3.text-white.opacity-60::text").get()
121
- if tags:
122
- tags = [t.strip() for t in tags.split(",")]
123
-
115
+ # Rating
124
116
  rating = sel.css("div.flex.items-center span.text-white.text-sm::text").get()
125
- actors = sel.css("div.global-box h5::text").getall()
117
+
118
+ # Year ve Actors - Detaylar (Details) bölümünden
119
+ year = None
120
+ actors = []
121
+
122
+ # Detaylar bölümündeki tüm flex-col div'leri al
123
+ detail_items = sel.css("div.flex.flex-col")
124
+ for item in detail_items:
125
+ # Label ve value yapısı: span.text-base ve span.text-sm.opacity-90
126
+ label = item.css("span.text-base::text").get()
127
+ value = item.css("span.text-sm.opacity-90::text").get()
128
+
129
+ if label and value:
130
+ label = label.strip()
131
+ value = value.strip()
132
+
133
+ # Yayın tarihi (yıl)
134
+ if label == "Yayın tarihi":
135
+ # "16 Ekim 2018" formatından yılı çıkar
136
+ year_match = re.search(r'\d{4}', value)
137
+ if year_match:
138
+ year = year_match.group()
139
+
140
+ # Yaratıcılar veya Oyuncular
141
+ elif label in ["Yaratıcılar", "Oyuncular"]:
142
+ if value:
143
+ actors.append(value)
126
144
 
127
145
  # Check urls for episodes
128
146
  all_urls = re.findall(r'"url":"([^"]*)"', resp.text)
@@ -130,33 +148,38 @@ class RoketDizi(PluginBase):
130
148
 
131
149
  episodes = []
132
150
  if is_series:
133
- seen_eps = set()
151
+ # Dict kullanarak duplicate'leri önle ama sıralı tut
152
+ episodes_dict = {}
134
153
  for u in all_urls:
135
- if "bolum" in u and u not in seen_eps:
136
- seen_eps.add(u)
154
+ if "bolum" in u and u not in episodes_dict:
137
155
  season_match = re.search(r'/sezon-(\d+)', u)
138
156
  ep_match = re.search(r'/bolum-(\d+)', u)
139
157
 
140
158
  season = int(season_match.group(1)) if season_match else 1
141
159
  episode_num = int(ep_match.group(1)) if ep_match else 1
142
160
 
143
- episodes.append(Episode(
144
- season=season,
145
- episode=episode_num,
146
- title=f"{season}. Sezon {episode_num}. Bölüm", # Placeholder title
147
- url=self.fix_url(u, self.get_domain_sync(url))
148
- ))
161
+ # Key olarak (season, episode) tuple kullan
162
+ key = (season, episode_num)
163
+ episodes_dict[key] = Episode(
164
+ season = season,
165
+ episode = episode_num,
166
+ title = f"{season}. Sezon {episode_num}. Bölüm",
167
+ url = self.fix_url(u)
168
+ )
169
+
170
+ # Sıralı liste oluştur
171
+ episodes = [episodes_dict[key] for key in sorted(episodes_dict.keys())]
149
172
 
150
173
  return SeriesInfo(
151
- title=title,
152
- url=url,
153
- poster=self.fix_url(poster),
154
- description=description,
155
- tags=tags,
156
- rating=rating,
157
- actors=actors,
158
- episodes=episodes,
159
- year=year
174
+ title = title,
175
+ url = url,
176
+ poster = self.fix_url(poster),
177
+ description = description,
178
+ tags = tags,
179
+ rating = rating,
180
+ actors = actors,
181
+ episodes = episodes,
182
+ year = year
160
183
  )
161
184
 
162
185
  async def load_links(self, url: str) -> list[dict]:
@@ -195,13 +218,3 @@ class RoketDizi(PluginBase):
195
218
 
196
219
  except Exception:
197
220
  return []
198
-
199
- def fix_url(self, url: str, domain:str=None) -> str:
200
- if not url: return ""
201
- if url.startswith("http"): return url
202
- base = domain or self.main_url
203
- return f"https:{url}" if url.startswith("//") else urllib.parse.urljoin(base, url)
204
-
205
- def get_domain_sync(self, url:str):
206
- parsed = urllib.parse.urlparse(url)
207
- return f"{parsed.scheme}://{parsed.netloc}"
@@ -7,7 +7,7 @@ import re
7
7
  class SinemaCX(PluginBase):
8
8
  name = "SinemaCX"
9
9
  language = "tr"
10
- main_url = "https://www.sinema.now"
10
+ main_url = "https://www.sinema.onl"
11
11
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
12
  description = "HD Film izle, Türkçe Dublaj ve Altyazılı filmler."
13
13
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: KekikStream
3
- Version: 1.8.0
3
+ Version: 1.8.2
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
@@ -8,7 +8,7 @@ KekikStream/Core/Extractor/ExtractorBase.py,sha256=0GO8u5YzsboYMLk6kmSJmzqLZAbX_
8
8
  KekikStream/Core/Extractor/ExtractorLoader.py,sha256=7uxUXTAuF65KKkmbI6iRiCiUhx-IqrronB7ixhchcTU,4289
9
9
  KekikStream/Core/Extractor/ExtractorManager.py,sha256=4L1H3jiTnf0kTq4W6uS7n95bBYHlKJ8_hh0og8z4erQ,1244
10
10
  KekikStream/Core/Extractor/ExtractorModels.py,sha256=Qj_gbIeGRewaZXNfYkTi4FFRRq6XBOc0HS0tXGDwajI,445
11
- KekikStream/Core/Media/MediaHandler.py,sha256=b3rKaNWVyU-bt54NwoZYWGEhTXiNx2KnR8AyYOe-EFo,6958
11
+ KekikStream/Core/Media/MediaHandler.py,sha256=taLA3rXsN_VfNn7nIc_OZKaaif8kuEZznhH_EaOiQQk,6874
12
12
  KekikStream/Core/Media/MediaManager.py,sha256=AaUq2D7JSJIphjoAj2fjLOJjswm7Qf5hjYCbBdrbnDU,438
13
13
  KekikStream/Core/Plugin/PluginBase.py,sha256=hlvErfJd_11wuhpIzI0Bfa0y3R4g5xM4VakSQJ-54mY,4425
14
14
  KekikStream/Core/Plugin/PluginLoader.py,sha256=yZxMug-OcJ5RBm4fQkoquKrZxcBU7Pvt4IcY-d0WU8g,3393
@@ -56,10 +56,10 @@ KekikStream/Extractors/VidPapi.py,sha256=g9ohdL9VJrxy4N7xerbIRz3ZxjsXFHlJWy0NaZ3
56
56
  KekikStream/Extractors/VideoSeyred.py,sha256=M6QPZ_isX9vM_7LPo-2I_8Cf1vB9awHw8vvzBODtoiQ,1977
57
57
  KekikStream/Extractors/YildizKisaFilm.py,sha256=R_JlrOVeMiDlXYcuTdItnKvidyx8_u3B14fSrxew2aE,1316
58
58
  KekikStream/Plugins/DiziBox.py,sha256=x7lChsXwaKbWeIPs9uN-jU1ZlVG73a7_1H5_emoPlUQ,10092
59
- KekikStream/Plugins/DiziPal.py,sha256=8tJh7n9nts-tnKFmw2wn8XbKXjQZ3U7V3TxFBf4rNmg,10108
60
- KekikStream/Plugins/DiziYou.py,sha256=oFET6bLG8YuqLBhndw50ppY90mGVYj39vXMdkIWjehM,7918
61
- KekikStream/Plugins/Dizilla.py,sha256=aaORb3ZfNZm5gXB940d3Q9FgRjuNiVtQoY64W0hYbOA,7450
62
- KekikStream/Plugins/FilmBip.py,sha256=wBp8jmjYuxv5nnI76skD2R_oSuS7CWhGTyQpM1YujHg,6054
59
+ KekikStream/Plugins/DiziPal.py,sha256=MBONjermWBm3sN-8ZSILnfXI2F_V2kH65gpTNOuL9dI,10198
60
+ KekikStream/Plugins/DiziYou.py,sha256=Gj4PKMKHLO7yOfGt-W9XjXjGpYwnhiJGdzt0-mqMMiM,9145
61
+ KekikStream/Plugins/Dizilla.py,sha256=VrpX875q5zp27w0zWgN4NE-ImpuIGAFWxzVhVITbAl0,7588
62
+ KekikStream/Plugins/FilmBip.py,sha256=Tfx2dUc1Qs7ZQoJtsBtjOJXCKmTe56m74lNhuUYYU14,6182
63
63
  KekikStream/Plugins/FilmMakinesi.py,sha256=CdV4k44dw0Q4rO3x4GobSDT1KGrkgit0OfTPxZfmfJ8,5314
64
64
  KekikStream/Plugins/FilmModu.py,sha256=b27hchMoYZwG3I-kM1sveW7rHKOF5OuepdjPgKIehEM,6706
65
65
  KekikStream/Plugins/FullHDFilm.py,sha256=kkb-JtWf23uiEzP9f_uds0tROYiKOyxcX0D-jNtQFi0,7005
@@ -68,18 +68,18 @@ KekikStream/Plugins/HDFilmCehennemi.py,sha256=cI8pQEPF0xo9vVI2fCPktjvCuH8N8f5lI0
68
68
  KekikStream/Plugins/JetFilmizle.py,sha256=0UgHnBTkd4mMXKSLjYxAixFMGXrVa0qOLqYiLb6RKZQ,6092
69
69
  KekikStream/Plugins/KultFilmler.py,sha256=VZET3RUoOVYKE-C2YbkMW8oNcxz6zE2pR7a3z-B4nD4,8987
70
70
  KekikStream/Plugins/RecTV.py,sha256=dJBHc0Um-hizxj__lz3To0mA7An86YpoN3hZPX4xJic,7673
71
- KekikStream/Plugins/RoketDizi.py,sha256=lmdGNkv8QdmOsYcC3BQbviXHL94qVzz5onGNOXF8c9w,7813
71
+ KekikStream/Plugins/RoketDizi.py,sha256=C6ID2sfEwf0Tw8_tO-pc1nx8TB0Ii_rCVRzlHA7E-oA,8547
72
72
  KekikStream/Plugins/SelcukFlix.py,sha256=F3Rv2TRpfM-SZUAqjTPsQChS_Ejoh5q1Mo5fBFlsLy0,9013
73
73
  KekikStream/Plugins/SezonlukDizi.py,sha256=IomeNsVlji1TCimv8JwdMquKLcxZwwJQOZYzJ5v0OdE,6455
74
74
  KekikStream/Plugins/SineWix.py,sha256=dPFUlHece9zo57Xjttdp_w-K9J_d1_8C-U6u1twWLh8,7493
75
75
  KekikStream/Plugins/Sinefy.py,sha256=mRwtQYiH7I_1k4EZS0g4MhnRDEx0NXwY8sa5tI33dog,8368
76
- KekikStream/Plugins/SinemaCX.py,sha256=8wMwqsCGM7l-co-vMb38vtSIeejYBTqjHUY3TWPs6uk,7177
76
+ KekikStream/Plugins/SinemaCX.py,sha256=DUvYa7J4a2D5ivLO_sQiaStoV5FDxmz8onJyFwAidvY,7177
77
77
  KekikStream/Plugins/Sinezy.py,sha256=t7InHEtKdIVyBQj8HJWwWcHhRP9AXKKXYE0O3YD8MYg,3647
78
78
  KekikStream/Plugins/SuperFilmGeldi.py,sha256=Ohm21BPsJH_S1tx5i2APEgAOD25k2NiwRP7rSgAKvRs,5289
79
79
  KekikStream/Plugins/UgurFilm.py,sha256=BUoDQOQF6tsj2tZj06OL9ga7j_oj116p3C2GQsnMXTU,4961
80
- kekikstream-1.8.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
81
- kekikstream-1.8.0.dist-info/METADATA,sha256=KrOs3wvXSQnm7F4txU5kfh0a2KJXk1BxXH6EUWrciFw,4983
82
- kekikstream-1.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
83
- kekikstream-1.8.0.dist-info/entry_points.txt,sha256=dFwdiTx8djyehI0Gsz-rZwjAfZzUzoBSrmzRu9ubjJc,50
84
- kekikstream-1.8.0.dist-info/top_level.txt,sha256=DNmGJDXl27Drdfobrak8KYLmocW_uznVYFJOzcjUgmY,12
85
- kekikstream-1.8.0.dist-info/RECORD,,
80
+ kekikstream-1.8.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
81
+ kekikstream-1.8.2.dist-info/METADATA,sha256=nXpNw-DQqIKw6ni5DSDAtdPUR913PoRoS5h-Hi4En7I,4983
82
+ kekikstream-1.8.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
83
+ kekikstream-1.8.2.dist-info/entry_points.txt,sha256=dFwdiTx8djyehI0Gsz-rZwjAfZzUzoBSrmzRu9ubjJc,50
84
+ kekikstream-1.8.2.dist-info/top_level.txt,sha256=DNmGJDXl27Drdfobrak8KYLmocW_uznVYFJOzcjUgmY,12
85
+ kekikstream-1.8.2.dist-info/RECORD,,