KekikStream 1.8.9__py3-none-any.whl → 1.9.1__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.
@@ -9,23 +9,50 @@ class ExtractorManager:
9
9
  self.extractor_loader = ExtractorLoader(extractor_dir)
10
10
  self.extractors = self.extractor_loader.load_all()
11
11
 
12
- def find_extractor(self, link) -> ExtractorBase:
13
- # Verilen bağlantıyı işleyebilecek çıkarıcıyı bul
12
+ # Extractor instance'larını cache'le
13
+ self._extractor_instances = []
14
+ self._ytdlp_extractor = None
15
+
14
16
  for extractor_cls in self.extractors:
15
- extractor:ExtractorBase = extractor_cls()
17
+ instance = extractor_cls()
18
+
19
+ # YTDLP'yi ayrı tut (BAŞA koyacağız - artık hızlı!)
20
+ if instance.name == "yt-dlp":
21
+ self._ytdlp_extractor = instance
22
+ else:
23
+ self._extractor_instances.append(instance)
24
+
25
+ # YTDLP'yi EN BAŞA ekle
26
+ if self._ytdlp_extractor:
27
+ self._extractor_instances.insert(0, self._ytdlp_extractor)
28
+
29
+ def find_extractor(self, link) -> ExtractorBase:
30
+ """
31
+ Verilen bağlantıyı işleyebilecek çıkarıcıyı bul
32
+
33
+ - Cached instance'lar kullanılır
34
+ - YTDLP en son denenir (yavaş olduğu için)
35
+ """
36
+ # Cached instance'ları kullan (yeniden oluşturma yok!)
37
+ for extractor in self._extractor_instances:
16
38
  if extractor.can_handle_url(link):
17
39
  return extractor
18
40
 
19
41
  return None
20
42
 
21
43
  def map_links_to_extractors(self, links) -> dict:
22
- # Bağlantıları uygun çıkarıcılarla eşleştir
44
+ """
45
+ Bağlantıları uygun çıkarıcılarla eşleştir
46
+
47
+ - Cached instance'lar kullanılır
48
+ - İlk eşleşmede break (gereksiz kontrol yok)
49
+ """
23
50
  mapping = {}
24
51
  for link in links:
25
- for extractor_cls in self.extractors:
26
- extractor:ExtractorBase = extractor_cls()
52
+ # Cached instance'ları kullan
53
+ for extractor in self._extractor_instances:
27
54
  if extractor.can_handle_url(link):
28
55
  mapping[link] = f"{extractor.name:<30} » {link.replace(extractor.main_url, '')}"
29
- break
56
+ break # İlk eşleşmede dur
30
57
 
31
- return mapping
58
+ return mapping
@@ -2,62 +2,13 @@
2
2
 
3
3
  from ...CLI import konsol
4
4
  from ..Extractor.ExtractorModels import ExtractResult
5
- import subprocess, os, yt_dlp
5
+ import subprocess, os
6
6
 
7
7
  class MediaHandler:
8
8
  def __init__(self, title: str = "KekikStream"):
9
9
  self.title = title
10
10
  self.headers = {}
11
11
 
12
- def should_use_ytdlp(self, url: str, user_agent: str) -> bool:
13
- """
14
- yt-dlp gereken durumları profesyonel şekilde tespit et
15
-
16
- yt-dlp'nin native Python API'sini simulate mode ile kullanarak
17
- güvenilir ve performanslı tespit yapar.
18
-
19
- Args:
20
- url: Video URL'si
21
- user_agent: User-Agent string'i
22
-
23
- Returns:
24
- bool: yt-dlp kullanılması gerekiyorsa True
25
- """
26
- # 1. User-Agent bazlı kontrol (mevcut davranışı koru - RecTV, MolyStream için)
27
- ytdlp_user_agents = [
28
- "googleusercontent",
29
- "Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0"
30
- ]
31
-
32
- if user_agent in ytdlp_user_agents:
33
- konsol.log("[cyan][ℹ] User-Agent bazlı yt-dlp tespiti[/cyan]")
34
- return True
35
-
36
- # 2. yt-dlp'nin native Python API'sini kullan (simulate mode)
37
- try:
38
- ydl_opts = {
39
- "simulate" : True, # Download yok, sadece tespit
40
- "quiet" : True, # Log kirliliği yok
41
- "no_warnings" : True, # Uyarı mesajları yok
42
- "extract_flat" : True # Minimal işlem
43
- }
44
-
45
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
46
- # URL'yi işleyebiliyor mu kontrol et
47
- info = ydl.extract_info(url, download=False, process=False)
48
-
49
- # Generic extractor ise atla
50
- if info and info.get("extractor_key") != "Generic":
51
- konsol.log(f"[cyan][ℹ] yt-dlp extractor: {info.get('extractor_key', 'Unknown')}[/cyan]")
52
- return True
53
-
54
- return False
55
-
56
- except Exception as e:
57
- # yt-dlp işleyemezse False döndür
58
- konsol.log(f"[yellow][⚠] yt-dlp kontrol hatası: {e}[/yellow]")
59
- return False
60
-
61
12
  def play_media(self, extract_data: ExtractResult):
62
13
  # user-agent ekle (varsayılan veya extract_data'dan)
63
14
  user_agent = extract_data.user_agent or "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)"
@@ -67,18 +18,14 @@ class MediaHandler:
67
18
  if extract_data.referer:
68
19
  self.headers["referer"] = extract_data.referer
69
20
 
21
+ # Özel Durumlar (RecTV vs. Googleusercontent)
22
+ if user_agent in ["googleusercontent", "Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0"]:
23
+ return self.play_with_ytdlp(extract_data)
24
+
70
25
  # İşletim sistemine göre oynatıcı seç (Android durumu)
71
26
  if subprocess.check_output(['uname', '-o']).strip() == b'Android':
72
27
  return self.play_with_android_mxplayer(extract_data)
73
28
 
74
- # Akıllı yt-dlp tespiti
75
- if self.should_use_ytdlp(extract_data.url, user_agent):
76
- konsol.log("[green][✓] yt-dlp kullanılacak[/green]")
77
- success = self.play_with_ytdlp(extract_data)
78
- if success:
79
- return True
80
- konsol.log("[yellow][⚠] yt-dlp başarısız, standart oynatıcılar deneniyor...[/yellow]")
81
-
82
29
  # Oynatıcı öncelik sırası (fallback zincirleme)
83
30
  players = [
84
31
  ("MPV", self.play_with_mpv),
@@ -0,0 +1,172 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
+ from urllib.parse import urlparse
5
+ from yt_dlp.extractor import gen_extractors
6
+ import yt_dlp, re, sys, os
7
+
8
+ class YTDLP(ExtractorBase):
9
+ name = "yt-dlp"
10
+ main_url = "" # Universal - tüm siteleri destekler
11
+
12
+ _FAST_DOMAIN_RE = None # compiled mega-regex (host üstünden)
13
+
14
+ @classmethod
15
+ def _init_fast_domain_regex(cls):
16
+ if cls._FAST_DOMAIN_RE is not None:
17
+ return
18
+
19
+ domains = set()
20
+
21
+ # yt-dlp extractor'larının _VALID_URL regex'lerinden domain yakala
22
+ # Regex metinlerinde domainler genelde "\." şeklinde geçer.
23
+ domain_pat = re.compile(r"(?:[a-z0-9-]+\\\.)+[a-z]{2,}", re.IGNORECASE)
24
+
25
+ for ie in gen_extractors():
26
+ # Generic'i fast-path'e dahil etmiyoruz
27
+ if getattr(ie, "IE_NAME", "").lower() == "generic":
28
+ continue
29
+
30
+ valid = getattr(ie, "_VALID_URL", None)
31
+ if not valid or not isinstance(valid, str):
32
+ continue
33
+
34
+ for m in domain_pat.findall(valid):
35
+ d = m.replace(r"\.", ".").lower()
36
+
37
+ # Çok agresif/şüpheli şeyleri elemek istersen burada filtre koyabilirsin
38
+ # (genelde gerek kalmıyor)
39
+ domains.add(d)
40
+
41
+ # Hiç domain çıkmazsa (çok uç durum) fallback: boş regex
42
+ if not domains:
43
+ cls._FAST_DOMAIN_RE = re.compile(r"$^") # hiçbir şeye match etmez
44
+ return
45
+
46
+ # Host eşleştirmesi: subdomain destekli (m.youtube.com, player.vimeo.com vs.)
47
+ # (?:^|.*\.) (domain1|domain2|...) $
48
+ joined = "|".join(sorted(re.escape(d) for d in domains))
49
+ pattern = rf"(?:^|.*\.)(?:{joined})$"
50
+ cls._FAST_DOMAIN_RE = re.compile(pattern, re.IGNORECASE)
51
+
52
+ def __init__(self):
53
+ self.__class__._init_fast_domain_regex()
54
+
55
+ def can_handle_url(self, url: str) -> bool:
56
+ """
57
+ Fast-path: URL host'unu tek mega-regex ile kontrol et (loop yok)
58
+ Slow-path: gerekirse mevcut extract_info tabanlı kontrolün
59
+ """
60
+ # URL parse + host al
61
+ try:
62
+ parsed = urlparse(url)
63
+ host = (parsed.hostname or "").lower()
64
+ except Exception:
65
+ host = ""
66
+
67
+ # Şemasız URL desteği: "youtube.com/..." gibi
68
+ if not host and "://" not in url:
69
+ try:
70
+ parsed = urlparse("https://" + url)
71
+ host = (parsed.hostname or "").lower()
72
+ except Exception:
73
+ host = ""
74
+
75
+ # Fast-path
76
+ if host and self.__class__._FAST_DOMAIN_RE.search(host):
77
+ return True
78
+
79
+ # SLOW PATH: Diğer siteler için yt-dlp'nin native kontrolü
80
+ try:
81
+ # stderr'ı geçici olarak kapat (hata mesajlarını gizle)
82
+ old_stderr = sys.stderr
83
+ sys.stderr = open(os.devnull, "w")
84
+
85
+ try:
86
+ ydl_opts = {
87
+ "simulate" : True, # Download yok, sadece tespit
88
+ "quiet" : True, # Log kirliliği yok
89
+ "no_warnings" : True, # Uyarı mesajları yok
90
+ "extract_flat" : True, # Minimal işlem
91
+ "no_check_certificates" : True,
92
+ "ignoreerrors" : True # Hataları yoksay
93
+ }
94
+
95
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
96
+ # URL'yi işleyebiliyor mu kontrol et
97
+ info = ydl.extract_info(url, download=False, process=False)
98
+
99
+ # Generic extractor ise atla
100
+ if info and info.get("extractor_key") != "Generic":
101
+ return True
102
+
103
+ return False
104
+ finally:
105
+ # stderr'ı geri yükle
106
+ sys.stderr.close()
107
+ sys.stderr = old_stderr
108
+
109
+ except Exception:
110
+ # yt-dlp işleyemezse False döndür
111
+ return False
112
+
113
+ async def extract(self, url: str, referer: str | None = None) -> ExtractResult:
114
+ ydl_opts = {
115
+ "quiet" : True,
116
+ "no_warnings" : True,
117
+ "extract_flat" : False, # Tam bilgi al
118
+ "format" : "best", # En iyi kalite
119
+ "no_check_certificates" : True
120
+ }
121
+
122
+ # Referer varsa header olarak ekle
123
+ if referer:
124
+ ydl_opts["http_headers"] = {"Referer": referer}
125
+
126
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
127
+ info = ydl.extract_info(url, download=False)
128
+
129
+ if not info:
130
+ raise ValueError("yt-dlp video bilgisi döndürmedi")
131
+
132
+ # Video URL'sini al
133
+ video_url = info.get("url")
134
+ if not video_url:
135
+ # Bazen formatlar listesinde olabilir
136
+ formats = info.get("formats", [])
137
+ if formats:
138
+ video_url = formats[-1].get("url") # Son format (genellikle en iyi)
139
+
140
+ if not video_url:
141
+ raise ValueError("Video URL bulunamadı")
142
+
143
+ # Altyazıları çıkar
144
+ subtitles = []
145
+ if subtitle_data := info.get("subtitles"):
146
+ for lang, subs in subtitle_data.items():
147
+ for sub in subs:
148
+ if sub_url := sub.get("url"):
149
+ subtitles.append(
150
+ Subtitle(
151
+ name=f"{lang} ({sub.get('ext', 'unknown')})",
152
+ url=sub_url
153
+ )
154
+ )
155
+
156
+ # User-Agent al
157
+ user_agent = None
158
+ http_headers = info.get("http_headers", {})
159
+ if http_headers:
160
+ user_agent = http_headers.get("User-Agent")
161
+
162
+ return ExtractResult(
163
+ name = self.name,
164
+ url = video_url,
165
+ referer = referer or info.get("webpage_url"),
166
+ user_agent = user_agent,
167
+ subtitles = subtitles
168
+ )
169
+
170
+ async def close(self):
171
+ """yt-dlp için cleanup gerekmez"""
172
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: KekikStream
3
- Version: 1.8.9
3
+ Version: 1.9.1
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,9 +6,9 @@ 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=4L1H3jiTnf0kTq4W6uS7n95bBYHlKJ8_hh0og8z4erQ,1244
9
+ KekikStream/Core/Extractor/ExtractorManager.py,sha256=NU1IgKzkS-kporEC58aaYo20rVZK_pilrD495hJpcY8,2112
10
10
  KekikStream/Core/Extractor/ExtractorModels.py,sha256=Qj_gbIeGRewaZXNfYkTi4FFRRq6XBOc0HS0tXGDwajI,445
11
- KekikStream/Core/Media/MediaHandler.py,sha256=9TVF0zuoj_1jSVOoU-juD6n_XsjnjiHg1IFJrpg1m8k,9705
11
+ KekikStream/Core/Media/MediaHandler.py,sha256=MEn3spPAThVloN3WcoCwWhpoyMA7tAZvcwYjmjJsX3U,7678
12
12
  KekikStream/Core/Media/MediaManager.py,sha256=AaUq2D7JSJIphjoAj2fjLOJjswm7Qf5hjYCbBdrbnDU,438
13
13
  KekikStream/Core/Plugin/PluginBase.py,sha256=uzJb8DqJfXOteteSBhG9QWUrFgb4JTByV_GbODz-9gs,3872
14
14
  KekikStream/Core/Plugin/PluginLoader.py,sha256=yZxMug-OcJ5RBm4fQkoquKrZxcBU7Pvt4IcY-d0WU8g,3393
@@ -54,6 +54,7 @@ KekikStream/Extractors/VidMolyMe.py,sha256=ogLiFUJVqFbhtzQrZ1gSB9me85DiHvntjWtSv
54
54
  KekikStream/Extractors/VidMoxy.py,sha256=LT7wTKBtuuagXwfGjWZwQF2NQGuChurZJ-I6gM0Jcek,1771
55
55
  KekikStream/Extractors/VidPapi.py,sha256=g9ohdL9VJrxy4N7xerbIRz3ZxjsXFHlJWy0NaZ31hFY,3259
56
56
  KekikStream/Extractors/VideoSeyred.py,sha256=M6QPZ_isX9vM_7LPo-2I_8Cf1vB9awHw8vvzBODtoiQ,1977
57
+ KekikStream/Extractors/YTDLP.py,sha256=O7JkwKMVhCd3RK0yfR5_-mCW5OMOUf3AXpWjOlYJPss,6327
57
58
  KekikStream/Extractors/YildizKisaFilm.py,sha256=R_JlrOVeMiDlXYcuTdItnKvidyx8_u3B14fSrxew2aE,1316
58
59
  KekikStream/Plugins/DiziBox.py,sha256=sxM7ckKeKwMrMkRNUAvh5wE9wdOuVda6Ag_zAdwSvi8,9935
59
60
  KekikStream/Plugins/DiziPal.py,sha256=MBONjermWBm3sN-8ZSILnfXI2F_V2kH65gpTNOuL9dI,10198
@@ -77,9 +78,9 @@ KekikStream/Plugins/SinemaCX.py,sha256=DUvYa7J4a2D5ivLO_sQiaStoV5FDxmz8onJyFwAid
77
78
  KekikStream/Plugins/Sinezy.py,sha256=EttAZogKoKMP8RP_X1fSfi8vVxA2RWizwgnLkmnhERQ,5675
78
79
  KekikStream/Plugins/SuperFilmGeldi.py,sha256=Ohm21BPsJH_S1tx5i2APEgAOD25k2NiwRP7rSgAKvRs,5289
79
80
  KekikStream/Plugins/UgurFilm.py,sha256=eKGzmSi8k_QbXnYPWXZRdmCxxc32zZh4rynmdxCbm1o,4832
80
- kekikstream-1.8.9.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
81
- kekikstream-1.8.9.dist-info/METADATA,sha256=biuumq6cqyGbTCNqBqMbXN0npvJGYrdZK8DokgOOxeQ,9035
82
- kekikstream-1.8.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
83
- kekikstream-1.8.9.dist-info/entry_points.txt,sha256=dFwdiTx8djyehI0Gsz-rZwjAfZzUzoBSrmzRu9ubjJc,50
84
- kekikstream-1.8.9.dist-info/top_level.txt,sha256=DNmGJDXl27Drdfobrak8KYLmocW_uznVYFJOzcjUgmY,12
85
- kekikstream-1.8.9.dist-info/RECORD,,
81
+ kekikstream-1.9.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
82
+ kekikstream-1.9.1.dist-info/METADATA,sha256=fr1gPLHXvTA3HFxD8u0xIZfIrhOwIBf7EHnhMOBodzs,9035
83
+ kekikstream-1.9.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
84
+ kekikstream-1.9.1.dist-info/entry_points.txt,sha256=dFwdiTx8djyehI0Gsz-rZwjAfZzUzoBSrmzRu9ubjJc,50
85
+ kekikstream-1.9.1.dist-info/top_level.txt,sha256=DNmGJDXl27Drdfobrak8KYLmocW_uznVYFJOzcjUgmY,12
86
+ kekikstream-1.9.1.dist-info/RECORD,,