KekikStream 1.9.2__py3-none-any.whl → 1.9.7__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.
@@ -7,12 +7,27 @@ class ExtractorManager:
7
7
  def __init__(self, extractor_dir="Extractors"):
8
8
  # Çıkarıcı yükleyiciyi başlat ve tüm çıkarıcıları yükle
9
9
  self.extractor_loader = ExtractorLoader(extractor_dir)
10
- self.extractors = self.extractor_loader.load_all()
10
+ self.extractors = self.extractor_loader.load_all() # Sadece class'lar
11
11
 
12
- # Extractor instance'larını cache'le
13
- self._extractor_instances = []
12
+ # Lazy loading: Instance'lar ilk kullanımda oluşturulacak
13
+ self._extractor_instances = None # None = henüz oluşturulmadı
14
14
  self._ytdlp_extractor = None
15
+ self._initialized = False
16
+
17
+ def _ensure_initialized(self):
18
+ """
19
+ Lazy initialization: İlk kullanımda TÜM extractorları initialize et
20
+
21
+ Startup'ta sadece class'ları yükledik (hızlı).
22
+ Şimdi instance'ları oluştur ve cache'le (bir kere).
23
+ """
24
+ if self._initialized:
25
+ return
15
26
 
27
+ # Instance listesi oluştur
28
+ self._extractor_instances = []
29
+
30
+ # TÜM extractorları instance'la
16
31
  for extractor_cls in self.extractors:
17
32
  instance = extractor_cls()
18
33
 
@@ -26,10 +41,15 @@ class ExtractorManager:
26
41
  if self._ytdlp_extractor:
27
42
  self._extractor_instances.insert(0, self._ytdlp_extractor)
28
43
 
44
+ self._initialized = True
45
+
29
46
  def find_extractor(self, link) -> ExtractorBase:
30
47
  """
31
48
  Verilen bağlantıyı işleyebilecek çıkarıcıyı bul
32
49
  """
50
+ # Lazy loading: İlk kullanımda extractorları initialize et
51
+ self._ensure_initialized()
52
+
33
53
  # Cached instance'ları kullan
34
54
  for extractor in self._extractor_instances:
35
55
  if extractor.can_handle_url(link):
@@ -41,6 +61,9 @@ class ExtractorManager:
41
61
  """
42
62
  Bağlantıları uygun çıkarıcılarla eşleştir
43
63
  """
64
+ # Lazy loading: İlk kullanımda extractorları initialize et
65
+ self._ensure_initialized()
66
+
44
67
  mapping = {}
45
68
  for link in links:
46
69
  # Cached instance'ları kullan
@@ -11,86 +11,74 @@ class RoketDizi(PluginBase):
11
11
  favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
12
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."
13
13
 
14
-
15
-
16
14
  main_page = {
17
- "dizi/tur/aksiyon" : "Aksiyon",
18
- "dizi/tur/bilim-kurgu" : "Bilim Kurgu",
19
- "dizi/tur/gerilim" : "Gerilim",
20
- "dizi/tur/fantastik" : "Fantastik",
21
- "dizi/tur/komedi" : "Komedi",
22
- "dizi/tur/korku" : "Korku",
23
- "dizi/tur/macera" : "Macera",
24
- "dizi/tur/suc" : "Suç"
15
+ f"{main_url}/dizi/tur/aksiyon" : "Aksiyon",
16
+ f"{main_url}/dizi/tur/bilim-kurgu" : "Bilim Kurgu",
17
+ f"{main_url}/dizi/tur/gerilim" : "Gerilim",
18
+ f"{main_url}/dizi/tur/fantastik" : "Fantastik",
19
+ f"{main_url}/dizi/tur/komedi" : "Komedi",
20
+ f"{main_url}/dizi/tur/korku" : "Korku",
21
+ f"{main_url}/dizi/tur/macera" : "Macera",
22
+ f"{main_url}/dizi/tur/suc" : "Suç"
25
23
  }
26
24
 
27
25
  async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
28
- full_url = f"{self.main_url}/{url}?&page={page}"
29
- resp = await self.httpx.get(full_url)
30
- sel = Selector(resp.text)
26
+ istek = await self.httpx.get(f"{url}?&page={page}")
27
+ secici = Selector(istek.text)
31
28
 
32
29
  results = []
33
30
 
34
- for item in sel.css("div.w-full.p-4 span.bg-\\[\\#232323\\]"):
35
- title = item.css("span.font-normal.line-clamp-1::text").get()
36
- href = item.css("a::attr(href)").get()
37
- poster = item.css("img::attr(src)").get()
38
-
39
- if title and href:
40
- results.append(MainPageResult(
41
- category = category,
42
- title = title,
43
- url = self.fix_url(href),
44
- poster = self.fix_url(poster)
45
- ))
31
+ for item in secici.css("div.w-full.p-4 span.bg-\\[\\#232323\\]"):
32
+ title = item.css("span.font-normal.line-clamp-1::text").get()
33
+ href = item.css("a::attr(href)").get()
34
+ poster = item.css("img::attr(src)").get()
35
+
36
+ if title and href:
37
+ results.append(MainPageResult(
38
+ category = category,
39
+ title = self.clean_title(title),
40
+ url = self.fix_url(href),
41
+ poster = self.fix_url(poster)
42
+ ))
43
+
46
44
  return results
47
45
 
48
46
  async def search(self, query: str) -> list[SearchResult]:
49
- post_url = f"{self.main_url}/api/bg/searchContent?searchterm={query}"
50
-
51
- headers = {
52
- "Accept" : "application/json, text/javascript, */*; q=0.01",
53
- "X-Requested-With" : "XMLHttpRequest",
54
- "Referer" : f"{self.main_url}/",
55
- }
56
-
57
- search_req = await self.httpx.post(post_url, headers=headers)
47
+ istek = await self.httpx.post(
48
+ url = f"{self.main_url}/api/bg/searchContent?searchterm={query}",
49
+ headers = {
50
+ "Accept" : "application/json, text/javascript, */*; q=0.01",
51
+ "X-Requested-With" : "XMLHttpRequest",
52
+ "Referer" : f"{self.main_url}/",
53
+ }
54
+ )
58
55
 
59
56
  try:
60
- resp_json = search_req.json()
61
-
62
- # Response is base64 encoded!
63
- if not resp_json.get("success"):
64
- return []
65
-
66
- encoded_response = resp_json.get("response", "")
67
- if not encoded_response:
57
+ veri = istek.json()
58
+ encoded = veri.get("response", "")
59
+ if not encoded:
68
60
  return []
69
-
70
- # Decode base64
71
- decoded = base64.b64decode(encoded_response).decode('utf-8')
72
- data = json.loads(decoded)
73
-
74
- if not data.get("state"):
61
+
62
+ decoded = base64.b64decode(encoded).decode("utf-8")
63
+ veri = json.loads(decoded)
64
+
65
+ if not veri.get("state"):
75
66
  return []
76
-
67
+
77
68
  results = []
78
- result_items = data.get("result", [])
79
-
80
- for item in result_items:
81
- title = item.get("object_name", "")
82
- slug = item.get("used_slug", "")
69
+
70
+ for item in veri.get("result", []):
71
+ title = item.get("object_name", "")
72
+ slug = item.get("used_slug", "")
83
73
  poster = item.get("object_poster_url", "")
84
-
74
+
85
75
  if title and slug:
86
- # Construct full URL from slug
87
- full_url = f"{self.main_url}/{slug}"
88
76
  results.append(SearchResult(
89
- title = title.strip(),
90
- url = full_url,
77
+ title = self.clean_title(title.strip()),
78
+ url = self.fix_url(f"{self.main_url}/{slug}"),
91
79
  poster = self.fix_url(poster) if poster else None
92
80
  ))
93
-
81
+
94
82
  return results
95
83
 
96
84
  except Exception:
@@ -100,24 +88,24 @@ class RoketDizi(PluginBase):
100
88
  # Note: Handling both Movie and Series logic in one, returning SeriesInfo generally or MovieInfo
101
89
  resp = await self.httpx.get(url)
102
90
  sel = Selector(resp.text)
103
-
91
+
104
92
  title = sel.css("h1.text-white::text").get()
105
93
  poster = sel.css("div.w-full.page-top img::attr(src)").get()
106
94
  description = sel.css("div.mt-2.text-sm::text").get()
107
-
95
+
108
96
  # Tags - genre bilgileri (Detaylar bölümünde)
109
97
  tags = []
110
98
  genre_text = sel.css("h3.text-white.opacity-90::text").get()
111
99
  if genre_text:
112
100
  tags = [t.strip() for t in genre_text.split(",")]
113
-
101
+
114
102
  # Rating
115
- rating = sel.css("div.flex.items-center span.text-white.text-sm::text").get()
116
-
103
+ rating = sel.css("span.text-white.text-sm.font-bold::text").get()
104
+
117
105
  # Year ve Actors - Detaylar (Details) bölümünden
118
106
  year = None
119
107
  actors = []
120
-
108
+
121
109
  # Detaylar bölümündeki tüm flex-col div'leri al
122
110
  detail_items = sel.css("div.flex.flex-col")
123
111
  for item in detail_items:
@@ -144,7 +132,7 @@ class RoketDizi(PluginBase):
144
132
  # Check urls for episodes
145
133
  all_urls = re.findall(r'"url":"([^"]*)"', resp.text)
146
134
  is_series = any("bolum-" in u for u in all_urls)
147
-
135
+
148
136
  episodes = []
149
137
  if is_series:
150
138
  # Dict kullanarak duplicate'leri önle ama sıralı tut
@@ -153,10 +141,10 @@ class RoketDizi(PluginBase):
153
141
  if "bolum" in u and u not in episodes_dict:
154
142
  season_match = re.search(r'/sezon-(\d+)', u)
155
143
  ep_match = re.search(r'/bolum-(\d+)', u)
156
-
144
+
157
145
  season = int(season_match.group(1)) if season_match else 1
158
146
  episode_num = int(ep_match.group(1)) if ep_match else 1
159
-
147
+
160
148
  # Key olarak (season, episode) tuple kullan
161
149
  key = (season, episode_num)
162
150
  episodes_dict[key] = Episode(
@@ -165,10 +153,10 @@ class RoketDizi(PluginBase):
165
153
  title = f"{season}. Sezon {episode_num}. Bölüm",
166
154
  url = self.fix_url(u)
167
155
  )
168
-
156
+
169
157
  # Sıralı liste oluştur
170
158
  episodes = [episodes_dict[key] for key in sorted(episodes_dict.keys())]
171
-
159
+
172
160
  return SeriesInfo(
173
161
  title = title,
174
162
  url = url,
@@ -188,31 +176,56 @@ class RoketDizi(PluginBase):
188
176
  next_data = sel.css("script#__NEXT_DATA__::text").get()
189
177
  if not next_data:
190
178
  return []
191
-
179
+
192
180
  try:
193
181
  data = json.loads(next_data)
194
182
  secure_data = data["props"]["pageProps"]["secureData"]
195
- decoded = base64.b64decode(secure_data).decode('utf-8')
196
-
183
+ decoded_json = json.loads(base64.b64decode(secure_data).decode('utf-8'))
184
+
185
+ # secureData içindeki RelatedResults -> getEpisodeSources -> result dizisini al
186
+ sources = decoded_json.get("RelatedResults", {}).get("getEpisodeSources", {}).get("result", [])
187
+
197
188
  results = []
198
- matches = re.findall(r'iframe src=\\"([^"]*)\\"', decoded)
199
- for m in matches:
200
- iframe_url = m.replace('\\', '')
189
+ for source in sources:
190
+ source_content = source.get("source_content", "")
191
+
192
+ # iframe URL'ini source_content'ten çıkar
193
+ iframe_match = re.search(r'<iframe[^>]*src=["\']([^"\']*)["\']', source_content)
194
+ if not iframe_match:
195
+ continue
196
+
197
+ iframe_url = iframe_match.group(1)
201
198
  if "http" not in iframe_url:
202
199
  if iframe_url.startswith("//"):
203
200
  iframe_url = "https:" + iframe_url
204
201
  else:
205
- iframe_url = "https://" + iframe_url # fallback
206
-
202
+ iframe_url = "https://" + iframe_url
203
+
207
204
  # Check extractor
208
205
  extractor = self.ex_manager.find_extractor(iframe_url)
209
- name = extractor.name if extractor else "Iframe"
210
-
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
+
211
224
  results.append({
212
- "url": iframe_url,
213
- "name": name
225
+ "url" : iframe_url,
226
+ "name" : " | ".join(name_parts)
214
227
  })
215
-
228
+
216
229
  return results
217
230
 
218
231
  except Exception:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: KekikStream
3
- Version: 1.9.2
3
+ Version: 1.9.7
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
@@ -92,13 +92,6 @@ pip install -U KekikStream
92
92
  KekikStream
93
93
  ```
94
94
 
95
- **Kütüphane (örnek arama):**
96
- ```python
97
- from KekikStream import Manager
98
- results = Manager().search("vikings")
99
- print(results[0].title)
100
- ```
101
-
102
95
  ---
103
96
 
104
97
  ## ✨ Özellikler
@@ -139,11 +132,11 @@ class MyPlugin(PluginBase):
139
132
 
140
133
  ### 🎬 Oynatıcı Desteği
141
134
 
142
- | Oynatıcı | Platform | Özellikler |
143
- |----------|----------|------------|
144
- | **VLC** | Desktop | Custom headers, subtitles, varsayılan |
145
- | **MPV** | Desktop | Custom headers, subtitles |
146
- | **MX Player** | Android | ADB üzerinden |
135
+ | Oynatıcı | Platform | Özellikler |
136
+ |---------------|----------|---------------------------|
137
+ | **MPV** | Desktop | Custom headers, subtitles |
138
+ | **VLC** | Desktop | Custom headers, subtitles |
139
+ | **MX Player** | Android | ADB üzerinden |
147
140
 
148
141
  > Özel durumlar için (Google Drive vb.) arka planda otomatik olarak yt-dlp devreye girer.
149
142
 
@@ -175,8 +168,8 @@ graph TB
175
168
  end
176
169
 
177
170
  subgraph Players
178
- VLC[🎥 VLC]
179
171
  MPV[🎥 MPV]
172
+ VLC[🎥 VLC]
180
173
  MX[🎥 MX Player]
181
174
  end
182
175
 
@@ -246,13 +239,13 @@ KekikStream/
246
239
 
247
240
  ## 📊 Performans
248
241
 
249
- | Metrik | Değer |
250
- |--------|-------|
251
- | Plugin Sayısı | 20+ |
252
- | Extractor Sayısı | 40+ |
242
+ | Metrik | Değer |
243
+ |----------------------|------------------|
244
+ | Plugin Sayısı | 20+ |
245
+ | Extractor Sayısı | 40+ |
253
246
  | Desteklenen Platform | Desktop, Android |
254
- | Async Arama | ✅ |
255
- | Cache Desteği | ✅ |
247
+ | Async Arama | ✅ |
248
+ | Cache Desteği | ✅ |
256
249
 
257
250
  ---
258
251
 
@@ -6,7 +6,7 @@ KekikStream/CLI/pypi_kontrol.py,sha256=q6fNs6EKJDc5VuUFig9DBzLzNPp_kMD1vOVgLElci
6
6
  KekikStream/Core/__init__.py,sha256=ar2MZQF83ryfLfydEXcfjdwNe4Too_HT6bP-D_4TopA,710
7
7
  KekikStream/Core/Extractor/ExtractorBase.py,sha256=Yj7CGvm2ZKxlvuUkZu0X1Pl0JMH250W7hyqv09duTmE,1637
8
8
  KekikStream/Core/Extractor/ExtractorLoader.py,sha256=7uxUXTAuF65KKkmbI6iRiCiUhx-IqrronB7ixhchcTU,4289
9
- KekikStream/Core/Extractor/ExtractorManager.py,sha256=HNWiaNCVQH_nztYMas2Z57BGxlEv4OAyE6TiMAUV-98,1850
9
+ KekikStream/Core/Extractor/ExtractorManager.py,sha256=W8LkEl0TXLYvbF6PWw9Q0BEYs17UQiHHrEOqBDyB_Eg,2652
10
10
  KekikStream/Core/Extractor/ExtractorModels.py,sha256=Qj_gbIeGRewaZXNfYkTi4FFRRq6XBOc0HS0tXGDwajI,445
11
11
  KekikStream/Core/Media/MediaHandler.py,sha256=MEn3spPAThVloN3WcoCwWhpoyMA7tAZvcwYjmjJsX3U,7678
12
12
  KekikStream/Core/Media/MediaManager.py,sha256=AaUq2D7JSJIphjoAj2fjLOJjswm7Qf5hjYCbBdrbnDU,438
@@ -69,7 +69,7 @@ KekikStream/Plugins/HDFilmCehennemi.py,sha256=D2VvgNvCcHfjkitzSfA19OFp-mI0sgUm6O
69
69
  KekikStream/Plugins/JetFilmizle.py,sha256=3tcx4ZT7oQs_xolhQeiqgirpWtknBjP7J7Qgy0bvIeM,5963
70
70
  KekikStream/Plugins/KultFilmler.py,sha256=VZET3RUoOVYKE-C2YbkMW8oNcxz6zE2pR7a3z-B4nD4,8987
71
71
  KekikStream/Plugins/RecTV.py,sha256=dF3Ogf7KE_zpfLQRjVcEJQMuWtBtHo6iB73_ECQEJ58,7544
72
- KekikStream/Plugins/RoketDizi.py,sha256=isVijJ6YRtsBQ6DVB2K3n6uV-1GXZahujLF7IWvBrqs,8472
72
+ KekikStream/Plugins/RoketDizi.py,sha256=h0g0Wi72TG4et7TS7ANro9LLlCiR_W0CJdkWCUzprE8,9027
73
73
  KekikStream/Plugins/SelcukFlix.py,sha256=WYVtGMxngpqrXk7PX_B4ya6Pji7dOjQsXoukk30f2d0,12744
74
74
  KekikStream/Plugins/SezonlukDizi.py,sha256=dT2xPPkdaYV43qsL4Le_5Yel8eoPkHtnXIOXqz-Ya-c,6326
75
75
  KekikStream/Plugins/SineWix.py,sha256=xdTeg8GHUpyZsY6EQ5I1ZIT4-lB_qwBwvHQVmdMPpEI,7364
@@ -78,9 +78,9 @@ KekikStream/Plugins/SinemaCX.py,sha256=DUvYa7J4a2D5ivLO_sQiaStoV5FDxmz8onJyFwAid
78
78
  KekikStream/Plugins/Sinezy.py,sha256=EttAZogKoKMP8RP_X1fSfi8vVxA2RWizwgnLkmnhERQ,5675
79
79
  KekikStream/Plugins/SuperFilmGeldi.py,sha256=Ohm21BPsJH_S1tx5i2APEgAOD25k2NiwRP7rSgAKvRs,5289
80
80
  KekikStream/Plugins/UgurFilm.py,sha256=eKGzmSi8k_QbXnYPWXZRdmCxxc32zZh4rynmdxCbm1o,4832
81
- kekikstream-1.9.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
82
- kekikstream-1.9.2.dist-info/METADATA,sha256=oSVDCg2TGVoevF_FSpN__QE-U8ulNJa2vsHnr-nRwrY,9035
83
- kekikstream-1.9.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
84
- kekikstream-1.9.2.dist-info/entry_points.txt,sha256=dFwdiTx8djyehI0Gsz-rZwjAfZzUzoBSrmzRu9ubjJc,50
85
- kekikstream-1.9.2.dist-info/top_level.txt,sha256=DNmGJDXl27Drdfobrak8KYLmocW_uznVYFJOzcjUgmY,12
86
- kekikstream-1.9.2.dist-info/RECORD,,
81
+ kekikstream-1.9.7.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
82
+ kekikstream-1.9.7.dist-info/METADATA,sha256=ndEj1WGK_Z66uhklNoEm5xYej1mggq3P7U1h-K9lrFo,9079
83
+ kekikstream-1.9.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
84
+ kekikstream-1.9.7.dist-info/entry_points.txt,sha256=dFwdiTx8djyehI0Gsz-rZwjAfZzUzoBSrmzRu9ubjJc,50
85
+ kekikstream-1.9.7.dist-info/top_level.txt,sha256=DNmGJDXl27Drdfobrak8KYLmocW_uznVYFJOzcjUgmY,12
86
+ kekikstream-1.9.7.dist-info/RECORD,,