KekikStream 1.7.2__tar.gz → 1.8.1__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.
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Extractor/ExtractorBase.py +23 -6
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Extractor/ExtractorModels.py +5 -7
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Media/MediaHandler.py +16 -20
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Media/MediaManager.py +0 -3
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Plugin/PluginBase.py +29 -8
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Plugin/PluginModels.py +25 -26
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/CloseLoad.py +3 -4
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/ContentX.py +4 -6
- kekikstream-1.8.1/KekikStream/Extractors/DzenRu.py +38 -0
- kekikstream-1.8.1/KekikStream/Extractors/ExPlay.py +53 -0
- kekikstream-1.8.1/KekikStream/Extractors/FirePlayer.py +60 -0
- kekikstream-1.8.1/KekikStream/Extractors/HDPlayerSystem.py +41 -0
- kekikstream-1.8.1/KekikStream/Extractors/JetTv.py +45 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/MailRu.py +3 -4
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/MixPlayHD.py +2 -3
- kekikstream-1.8.1/KekikStream/Extractors/MixTiger.py +57 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/MolyStream.py +5 -5
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/Odnoklassniki.py +7 -7
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/PeaceMakerst.py +4 -5
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/PixelDrain.py +1 -2
- kekikstream-1.8.1/KekikStream/Extractors/PlayerFilmIzle.py +62 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/RapidVid.py +2 -3
- kekikstream-1.8.1/KekikStream/Extractors/SetPlay.py +57 -0
- kekikstream-1.8.1/KekikStream/Extractors/SetPrime.py +45 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/SibNet.py +2 -3
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/Sobreatsesuyp.py +4 -5
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/TRsTX.py +4 -5
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/TauVideo.py +2 -3
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/TurboImgz.py +2 -3
- kekikstream-1.8.1/KekikStream/Extractors/TurkeyPlayer.py +34 -0
- kekikstream-1.8.1/KekikStream/Extractors/VidHide.py +72 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/VidMoly.py +4 -5
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/VidMoxy.py +2 -3
- kekikstream-1.8.1/KekikStream/Extractors/VidPapi.py +89 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/VideoSeyred.py +3 -4
- kekikstream-1.8.1/KekikStream/Extractors/YildizKisaFilm.py +41 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/DiziBox.py +16 -16
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/DiziPal.py +15 -15
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/DiziYou.py +7 -7
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/Dizilla.py +10 -6
- kekikstream-1.8.1/KekikStream/Plugins/FilmBip.py +145 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/FilmMakinesi.py +3 -3
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/FilmModu.py +9 -9
- kekikstream-1.8.1/KekikStream/Plugins/FullHDFilm.py +164 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/FullHDFilmizlesene.py +3 -3
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/HDFilmCehennemi.py +6 -3
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/JetFilmizle.py +16 -9
- kekikstream-1.8.1/KekikStream/Plugins/KultFilmler.py +219 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/RecTV.py +17 -17
- kekikstream-1.8.1/KekikStream/Plugins/RoketDizi.py +181 -0
- kekikstream-1.8.1/KekikStream/Plugins/SelcukFlix.py +216 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/SezonlukDizi.py +11 -8
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/SineWix.py +7 -7
- kekikstream-1.8.1/KekikStream/Plugins/Sinefy.py +214 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/SinemaCX.py +13 -13
- kekikstream-1.8.1/KekikStream/Plugins/Sinezy.py +99 -0
- kekikstream-1.8.1/KekikStream/Plugins/SuperFilmGeldi.py +121 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Plugins/UgurFilm.py +6 -6
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/__init__.py +34 -24
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/requirements.txt +2 -2
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/PKG-INFO +3 -2
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/SOURCES.txt +21 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/requires.txt +1 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/PKG-INFO +3 -2
- {kekikstream-1.7.2 → kekikstream-1.8.1}/setup.py +3 -2
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/CLI/__init__.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/CLI/pypi_kontrol.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/Plugin/PluginManager.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/UI/UIManager.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Core/__init__.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/FourCX.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/FourPichive.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/FourPlayRu.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/HDStreamAble.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/Hotlinger.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/OkRuHTTP.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/OkRuSSL.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/Pichive.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/PlayRu.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/Extractors/VidMolyMe.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream/__main__.py +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/dependency_links.txt +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/entry_points.txt +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/KekikStream.egg-info/top_level.txt +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/LICENSE +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/MANIFEST.in +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/README.md +0 -0
- {kekikstream-1.7.2 → kekikstream-1.8.1}/setup.cfg +0 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from curl_cffi import AsyncSession
|
|
5
5
|
from cloudscraper import CloudScraper
|
|
6
|
+
from httpx import AsyncClient
|
|
6
7
|
from typing import Optional
|
|
7
8
|
from .ExtractorModels import ExtractResult
|
|
8
9
|
from urllib.parse import urljoin
|
|
@@ -11,13 +12,27 @@ class ExtractorBase(ABC):
|
|
|
11
12
|
# Çıkarıcının temel özellikleri
|
|
12
13
|
name = "Extractor"
|
|
13
14
|
main_url = ""
|
|
15
|
+
requires_cffi = False
|
|
14
16
|
|
|
15
17
|
def __init__(self):
|
|
16
|
-
#
|
|
17
|
-
self.cffi = AsyncSession(impersonate="firefox135")
|
|
18
|
+
# cloudscraper - for bypassing Cloudflare
|
|
18
19
|
self.cloudscraper = CloudScraper()
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
# httpx - lightweight and safe for most HTTP requests
|
|
22
|
+
self.httpx = AsyncClient(
|
|
23
|
+
timeout = 3,
|
|
24
|
+
follow_redirects = True,
|
|
25
|
+
)
|
|
26
|
+
self.httpx.headers.update(self.cloudscraper.headers)
|
|
27
|
+
self.httpx.cookies.update(self.cloudscraper.cookies)
|
|
28
|
+
|
|
29
|
+
# curl_cffi - only initialize if needed for anti-bot bypass
|
|
30
|
+
self.cffi = None
|
|
31
|
+
|
|
32
|
+
if self.requires_cffi:
|
|
33
|
+
self.cffi = AsyncSession(impersonate="firefox135")
|
|
34
|
+
self.cffi.cookies.update(self.cloudscraper.cookies)
|
|
35
|
+
self.cffi.headers.update({"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 15.7; rv:135.0) Gecko/20100101 Firefox/135.0"})
|
|
21
36
|
|
|
22
37
|
def can_handle_url(self, url: str) -> bool:
|
|
23
38
|
# URL'nin bu çıkarıcı tarafından işlenip işlenemeyeceğini kontrol et
|
|
@@ -29,8 +44,10 @@ class ExtractorBase(ABC):
|
|
|
29
44
|
pass
|
|
30
45
|
|
|
31
46
|
async def close(self):
|
|
32
|
-
|
|
33
|
-
await self.
|
|
47
|
+
"""Close both HTTP clients if they exist."""
|
|
48
|
+
await self.httpx.aclose()
|
|
49
|
+
if self.cffi:
|
|
50
|
+
await self.cffi.close()
|
|
34
51
|
|
|
35
52
|
def fix_url(self, url: str) -> str:
|
|
36
53
|
# Eksik URL'leri düzelt ve tam URL formatına çevir
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel
|
|
4
|
-
from typing import List, Optional
|
|
5
|
-
|
|
6
4
|
|
|
7
5
|
class Subtitle(BaseModel):
|
|
8
6
|
"""Altyazı modeli."""
|
|
@@ -12,8 +10,8 @@ class Subtitle(BaseModel):
|
|
|
12
10
|
|
|
13
11
|
class ExtractResult(BaseModel):
|
|
14
12
|
"""Extractor'ın döndürmesi gereken sonuç modeli."""
|
|
15
|
-
name
|
|
16
|
-
url
|
|
17
|
-
referer
|
|
18
|
-
|
|
19
|
-
subtitles
|
|
13
|
+
name : str
|
|
14
|
+
url : str
|
|
15
|
+
referer : str | None = None
|
|
16
|
+
user_agent : str | None = None
|
|
17
|
+
subtitles : list[Subtitle] = []
|
|
@@ -5,33 +5,29 @@ from ..Extractor.ExtractorModels import ExtractResult
|
|
|
5
5
|
import subprocess, os
|
|
6
6
|
|
|
7
7
|
class MediaHandler:
|
|
8
|
-
def __init__(self, title: str = "KekikStream"
|
|
9
|
-
# Varsayılan HTTP başlıklarını ayarla
|
|
10
|
-
if headers is None:
|
|
11
|
-
headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)"}
|
|
12
|
-
|
|
13
|
-
self.headers = headers
|
|
8
|
+
def __init__(self, title: str = "KekikStream"):
|
|
14
9
|
self.title = title
|
|
10
|
+
self.headers = {}
|
|
15
11
|
|
|
16
12
|
def play_media(self, extract_data: ExtractResult):
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
# user-agent ekle (varsayılan veya extract_data'dan)
|
|
14
|
+
user_agent = extract_data.user_agent or "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5)"
|
|
15
|
+
self.headers["user-agent"] = user_agent
|
|
20
16
|
|
|
21
|
-
#
|
|
22
|
-
if extract_data.
|
|
23
|
-
self.headers
|
|
17
|
+
# referer ekle
|
|
18
|
+
if extract_data.referer:
|
|
19
|
+
self.headers["referer"] = extract_data.referer
|
|
24
20
|
|
|
25
21
|
# Google Drive gibi özel durumlar için yt-dlp kullan
|
|
26
|
-
if
|
|
22
|
+
if user_agent in ["googleusercontent", "Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0"]:
|
|
27
23
|
return self.play_with_ytdlp(extract_data)
|
|
28
24
|
|
|
29
25
|
# İşletim sistemine göre oynatıcı seç
|
|
30
26
|
if subprocess.check_output(['uname', '-o']).strip() == b'Android':
|
|
31
27
|
return self.play_with_android_mxplayer(extract_data)
|
|
32
28
|
|
|
33
|
-
#
|
|
34
|
-
if
|
|
29
|
+
# Alt yazılar varsa mpv kullan
|
|
30
|
+
if extract_data.subtitles:
|
|
35
31
|
return self.play_with_mpv(extract_data)
|
|
36
32
|
|
|
37
33
|
return self.play_with_vlc(extract_data) or self.play_with_mpv(extract_data)
|
|
@@ -48,11 +44,11 @@ class MediaHandler:
|
|
|
48
44
|
f"--input-title-format={self.title}"
|
|
49
45
|
])
|
|
50
46
|
|
|
51
|
-
if "
|
|
52
|
-
vlc_command.append(f"--http-user-agent={self.headers.get('
|
|
47
|
+
if "user-agent" in self.headers:
|
|
48
|
+
vlc_command.append(f"--http-user-agent={self.headers.get('user-agent')}")
|
|
53
49
|
|
|
54
|
-
if "
|
|
55
|
-
vlc_command.append(f"--http-referrer={self.headers.get('
|
|
50
|
+
if "referer" in self.headers:
|
|
51
|
+
vlc_command.append(f"--http-referrer={self.headers.get('referer')}")
|
|
56
52
|
|
|
57
53
|
vlc_command.extend(
|
|
58
54
|
f"--sub-file={subtitle.url}" for subtitle in extract_data.subtitles
|
|
@@ -162,4 +158,4 @@ class MediaHandler:
|
|
|
162
158
|
konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
|
|
163
159
|
except FileNotFoundError:
|
|
164
160
|
konsol.print(f"Paket: {paket}, Hata: MX Player kurulu değil")
|
|
165
|
-
konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
|
|
161
|
+
konsol.print({"title": self.title, "url": extract_data.url, "headers": self.headers})
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from curl_cffi import AsyncSession
|
|
5
5
|
from cloudscraper import CloudScraper
|
|
6
|
+
from httpx import AsyncClient
|
|
6
7
|
from .PluginModels import MainPageResult, SearchResult, MovieInfo
|
|
7
8
|
from ..Media.MediaHandler import MediaHandler
|
|
8
9
|
from ..Extractor.ExtractorManager import ExtractorManager
|
|
@@ -16,6 +17,8 @@ class PluginBase(ABC):
|
|
|
16
17
|
favicon = f"https://www.google.com/s2/favicons?domain={main_url}&sz=64"
|
|
17
18
|
description = "No description provided."
|
|
18
19
|
|
|
20
|
+
requires_cffi = False
|
|
21
|
+
|
|
19
22
|
main_page = {}
|
|
20
23
|
|
|
21
24
|
async def url_update(self, new_url: str):
|
|
@@ -24,17 +27,32 @@ class PluginBase(ABC):
|
|
|
24
27
|
self.main_url = new_url
|
|
25
28
|
|
|
26
29
|
def __init__(self):
|
|
27
|
-
|
|
30
|
+
# cloudscraper - for bypassing Cloudflare
|
|
28
31
|
self.cloudscraper = CloudScraper()
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
|
|
33
|
+
# httpx - lightweight and safe for most HTTP requests
|
|
34
|
+
self.httpx = AsyncClient(
|
|
35
|
+
timeout = 3,
|
|
36
|
+
follow_redirects = True,
|
|
37
|
+
)
|
|
38
|
+
self.httpx.headers.update(self.cloudscraper.headers)
|
|
39
|
+
self.httpx.cookies.update(self.cloudscraper.cookies)
|
|
40
|
+
|
|
41
|
+
# curl_cffi - only initialize if needed for anti-bot bypass
|
|
42
|
+
self.cffi = None
|
|
43
|
+
|
|
44
|
+
if self.requires_cffi:
|
|
45
|
+
self.cffi = AsyncSession(impersonate="firefox135")
|
|
46
|
+
self.cffi.cookies.update(self.cloudscraper.cookies)
|
|
47
|
+
self.cffi.headers.update({"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 15.7; rv:135.0) Gecko/20100101 Firefox/135.0"})
|
|
48
|
+
|
|
31
49
|
self.media_handler = MediaHandler()
|
|
32
50
|
self.ex_manager = ExtractorManager()
|
|
33
51
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
52
|
+
@abstractmethod
|
|
53
|
+
async def get_main_page(self, page: int, url: str, category: str) -> list[MainPageResult]:
|
|
54
|
+
"""Ana sayfadaki popüler içerikleri döndürür."""
|
|
55
|
+
pass
|
|
38
56
|
|
|
39
57
|
@abstractmethod
|
|
40
58
|
async def search(self, query: str) -> list[SearchResult]:
|
|
@@ -72,7 +90,10 @@ class PluginBase(ABC):
|
|
|
72
90
|
pass
|
|
73
91
|
|
|
74
92
|
async def close(self):
|
|
75
|
-
|
|
93
|
+
"""Close both HTTP clients if they exist."""
|
|
94
|
+
await self.httpx.aclose()
|
|
95
|
+
if self.cffi:
|
|
96
|
+
await self.cffi.close()
|
|
76
97
|
|
|
77
98
|
def fix_url(self, url: str) -> str:
|
|
78
99
|
if not url:
|
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, field_validator, model_validator
|
|
4
|
-
from typing import List, Optional
|
|
5
4
|
|
|
6
5
|
class MainPageResult(BaseModel):
|
|
7
6
|
"""Ana sayfa sonucunda dönecek veri modeli."""
|
|
8
7
|
category : str
|
|
9
8
|
title : str
|
|
10
9
|
url : str
|
|
11
|
-
poster :
|
|
10
|
+
poster : str | None = None
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class SearchResult(BaseModel):
|
|
15
14
|
"""Arama sonucunda dönecek veri modeli."""
|
|
16
15
|
title : str
|
|
17
16
|
url : str
|
|
18
|
-
poster :
|
|
17
|
+
poster : str | None = None
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
class MovieInfo(BaseModel):
|
|
22
21
|
"""Bir medya öğesinin bilgilerini tutan model."""
|
|
23
22
|
url : str
|
|
24
|
-
poster :
|
|
25
|
-
title :
|
|
26
|
-
description :
|
|
27
|
-
tags :
|
|
28
|
-
rating :
|
|
29
|
-
year :
|
|
30
|
-
actors :
|
|
31
|
-
duration :
|
|
23
|
+
poster : str | None = None
|
|
24
|
+
title : str | None = None
|
|
25
|
+
description : str | None = None
|
|
26
|
+
tags : str | None = None
|
|
27
|
+
rating : str | None = None
|
|
28
|
+
year : str | None = None
|
|
29
|
+
actors : str | None = None
|
|
30
|
+
duration : int | None = None
|
|
32
31
|
|
|
33
32
|
@field_validator("tags", "actors", mode="before")
|
|
34
33
|
@classmethod
|
|
@@ -42,10 +41,10 @@ class MovieInfo(BaseModel):
|
|
|
42
41
|
|
|
43
42
|
|
|
44
43
|
class Episode(BaseModel):
|
|
45
|
-
season :
|
|
46
|
-
episode :
|
|
47
|
-
title :
|
|
48
|
-
url :
|
|
44
|
+
season : int | None = None
|
|
45
|
+
episode : int | None = None
|
|
46
|
+
title : str | None = None
|
|
47
|
+
url : str | None = None
|
|
49
48
|
|
|
50
49
|
@model_validator(mode="after")
|
|
51
50
|
def check_title(self) -> "Episode":
|
|
@@ -58,16 +57,16 @@ class Episode(BaseModel):
|
|
|
58
57
|
return self
|
|
59
58
|
|
|
60
59
|
class SeriesInfo(BaseModel):
|
|
61
|
-
url :
|
|
62
|
-
poster :
|
|
63
|
-
title :
|
|
64
|
-
description :
|
|
65
|
-
tags :
|
|
66
|
-
rating :
|
|
67
|
-
year :
|
|
68
|
-
actors :
|
|
69
|
-
duration :
|
|
70
|
-
episodes :
|
|
60
|
+
url : str | None = None
|
|
61
|
+
poster : str | None = None
|
|
62
|
+
title : str | None = None
|
|
63
|
+
description : str | None = None
|
|
64
|
+
tags : str | None = None
|
|
65
|
+
rating : str | None = None
|
|
66
|
+
year : str | None = None
|
|
67
|
+
actors : str | None = None
|
|
68
|
+
duration : int | None = None
|
|
69
|
+
episodes : list[Episode] | None = None
|
|
71
70
|
|
|
72
71
|
@field_validator("tags", "actors", mode="before")
|
|
73
72
|
@classmethod
|
|
@@ -77,4 +76,4 @@ class SeriesInfo(BaseModel):
|
|
|
77
76
|
@field_validator("rating", "year", mode="before")
|
|
78
77
|
@classmethod
|
|
79
78
|
def ensure_string(cls, value):
|
|
80
|
-
return str(value) if value is not None else value
|
|
79
|
+
return str(value) if value is not None else value
|
|
@@ -10,9 +10,9 @@ class CloseLoadExtractor(ExtractorBase):
|
|
|
10
10
|
|
|
11
11
|
async def extract(self, url, referer=None) -> ExtractResult:
|
|
12
12
|
if referer:
|
|
13
|
-
self.
|
|
13
|
+
self.httpx.headers.update({"Referer": referer})
|
|
14
14
|
|
|
15
|
-
istek = await self.
|
|
15
|
+
istek = await self.httpx.get(url)
|
|
16
16
|
istek.raise_for_status()
|
|
17
17
|
|
|
18
18
|
eval_func = re.compile(r'\s*(eval\(function[\s\S].*)\s*').findall(istek.text)[0]
|
|
@@ -22,6 +22,5 @@ class CloseLoadExtractor(ExtractorBase):
|
|
|
22
22
|
name = self.name,
|
|
23
23
|
url = m3u_link,
|
|
24
24
|
referer = self.main_url,
|
|
25
|
-
headers = {},
|
|
26
25
|
subtitles = []
|
|
27
|
-
)
|
|
26
|
+
)
|
|
@@ -9,9 +9,9 @@ class ContentX(ExtractorBase):
|
|
|
9
9
|
|
|
10
10
|
async def extract(self, url, referer=None) -> list[ExtractResult]:
|
|
11
11
|
if referer:
|
|
12
|
-
self.
|
|
12
|
+
self.httpx.headers.update({"Referer": referer})
|
|
13
13
|
|
|
14
|
-
istek = await self.
|
|
14
|
+
istek = await self.httpx.get(url)
|
|
15
15
|
istek.raise_for_status()
|
|
16
16
|
i_source = istek.text
|
|
17
17
|
|
|
@@ -39,7 +39,7 @@ class ContentX(ExtractorBase):
|
|
|
39
39
|
)
|
|
40
40
|
)
|
|
41
41
|
|
|
42
|
-
vid_source_request = await self.
|
|
42
|
+
vid_source_request = await self.httpx.get(f"{self.main_url}/source2.php?v={i_extract_value}", headers={"Referer": referer or self.main_url})
|
|
43
43
|
vid_source_request.raise_for_status()
|
|
44
44
|
|
|
45
45
|
vid_source = vid_source_request.text
|
|
@@ -53,14 +53,13 @@ class ContentX(ExtractorBase):
|
|
|
53
53
|
name = self.name,
|
|
54
54
|
url = m3u_link,
|
|
55
55
|
referer = url,
|
|
56
|
-
headers = {},
|
|
57
56
|
subtitles = subtitles
|
|
58
57
|
)
|
|
59
58
|
]
|
|
60
59
|
|
|
61
60
|
if i_dublaj := re.search(r',\"([^"]+)\",\"Türkçe"', i_source):
|
|
62
61
|
dublaj_value = i_dublaj[1]
|
|
63
|
-
dublaj_source_request = await self.
|
|
62
|
+
dublaj_source_request = await self.httpx.get(f"{self.main_url}/source2.php?v={dublaj_value}", headers={"Referer": referer or self.main_url})
|
|
64
63
|
dublaj_source_request.raise_for_status()
|
|
65
64
|
|
|
66
65
|
dublaj_source = dublaj_source_request.text
|
|
@@ -74,7 +73,6 @@ class ContentX(ExtractorBase):
|
|
|
74
73
|
name = f"{self.name} Türkçe Dublaj",
|
|
75
74
|
url = dublaj_link,
|
|
76
75
|
referer = url,
|
|
77
|
-
headers = {},
|
|
78
76
|
subtitles = []
|
|
79
77
|
)
|
|
80
78
|
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
class DzenRu(ExtractorBase):
|
|
7
|
+
name = "DzenRu"
|
|
8
|
+
main_url = "https://dzen.ru"
|
|
9
|
+
|
|
10
|
+
async def extract(self, url, referer=None) -> ExtractResult:
|
|
11
|
+
video_key = url.split("/")[-1]
|
|
12
|
+
video_url = f"{self.main_url}/embed/{video_key}"
|
|
13
|
+
|
|
14
|
+
if referer:
|
|
15
|
+
self.httpx.headers.update({"Referer": referer})
|
|
16
|
+
|
|
17
|
+
istek = await self.httpx.get(video_url)
|
|
18
|
+
istek.raise_for_status()
|
|
19
|
+
|
|
20
|
+
# okcdn.ru linklerini bul
|
|
21
|
+
matches = re.findall(r'https://vd\d+\.okcdn\.ru/\?[^"\'\\\s]+', istek.text)
|
|
22
|
+
|
|
23
|
+
if not matches:
|
|
24
|
+
raise ValueError("DzenRu video link not found")
|
|
25
|
+
|
|
26
|
+
# Benzersiz linkleri al, son kaliteyi kullan
|
|
27
|
+
unique_links = list(set(matches))
|
|
28
|
+
best_link = unique_links[-1] if unique_links else None
|
|
29
|
+
|
|
30
|
+
if not best_link:
|
|
31
|
+
raise ValueError("No valid video URL found")
|
|
32
|
+
|
|
33
|
+
return ExtractResult(
|
|
34
|
+
name = self.name,
|
|
35
|
+
url = best_link,
|
|
36
|
+
referer = self.main_url,
|
|
37
|
+
subtitles = []
|
|
38
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
|
+
import re
|
|
5
|
+
from urllib.parse import urlparse, parse_qs
|
|
6
|
+
|
|
7
|
+
class ExPlay(ExtractorBase):
|
|
8
|
+
name = "ExPlay"
|
|
9
|
+
main_url = "https://explay.store"
|
|
10
|
+
|
|
11
|
+
async def extract(self, url, referer=None) -> ExtractResult:
|
|
12
|
+
ext_ref = referer or ""
|
|
13
|
+
|
|
14
|
+
# URL parsing for partKey
|
|
15
|
+
parsed = urlparse(url)
|
|
16
|
+
params = parse_qs(parsed.query)
|
|
17
|
+
part_key = params.get("partKey", [""])[0]
|
|
18
|
+
clean_url = url.split("?partKey=")[0]
|
|
19
|
+
|
|
20
|
+
if referer:
|
|
21
|
+
self.httpx.headers.update({"Referer": referer})
|
|
22
|
+
|
|
23
|
+
istek = await self.httpx.get(clean_url)
|
|
24
|
+
istek.raise_for_status()
|
|
25
|
+
|
|
26
|
+
# videoUrl çıkar
|
|
27
|
+
video_url_match = re.search(r'videoUrl":"([^",]+)"', istek.text)
|
|
28
|
+
if not video_url_match:
|
|
29
|
+
raise ValueError("videoUrl not found")
|
|
30
|
+
video_url = video_url_match[1].replace("\\", "")
|
|
31
|
+
|
|
32
|
+
# videoServer çıkar
|
|
33
|
+
video_server_match = re.search(r'videoServer":"([^",]+)"', istek.text)
|
|
34
|
+
if not video_server_match:
|
|
35
|
+
raise ValueError("videoServer not found")
|
|
36
|
+
video_server = video_server_match[1]
|
|
37
|
+
|
|
38
|
+
# title çıkar
|
|
39
|
+
title_match = re.search(r'title":"([^",]+)"', istek.text)
|
|
40
|
+
title = title_match[1].split(".")[-1] if title_match else "Unknown"
|
|
41
|
+
|
|
42
|
+
if part_key and "turkce" in part_key.lower():
|
|
43
|
+
title = part_key # Or nicer formatting like SetPlay
|
|
44
|
+
|
|
45
|
+
# M3U8 link oluştur
|
|
46
|
+
m3u_link = f"{self.main_url}{video_url}?s={video_server}"
|
|
47
|
+
|
|
48
|
+
return ExtractResult(
|
|
49
|
+
name = f"{self.name} - {title}",
|
|
50
|
+
url = m3u_link,
|
|
51
|
+
referer = clean_url,
|
|
52
|
+
subtitles = []
|
|
53
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
+
from Kekik.Sifreleme import Packer
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
class FirePlayer(ExtractorBase):
|
|
8
|
+
name = "FirePlayer"
|
|
9
|
+
main_url = "https://Player.filmizle.in"
|
|
10
|
+
|
|
11
|
+
def can_handle_url(self, url: str) -> bool:
|
|
12
|
+
return "filmizle.in" in url or "fireplayer" in url.lower()
|
|
13
|
+
|
|
14
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
15
|
+
if not referer:
|
|
16
|
+
referer = "https://sinezy.site/"
|
|
17
|
+
|
|
18
|
+
headers = {
|
|
19
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
20
|
+
"Referer": referer
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
istek = await self.httpx.get(url, headers=headers)
|
|
24
|
+
|
|
25
|
+
# Unpack usage similar to VidMoxy / suggestion
|
|
26
|
+
# Find the packed code block
|
|
27
|
+
match = re.search(r'(eval\(function\(p,a,c,k,e,d\)[\s\S]+?)\s*</script>', istek.text)
|
|
28
|
+
if match:
|
|
29
|
+
packed_code = match.group(1)
|
|
30
|
+
unpacked = Packer.unpack(packed_code)
|
|
31
|
+
else:
|
|
32
|
+
unpacked = istek.text
|
|
33
|
+
|
|
34
|
+
# Normalize escaped slashes
|
|
35
|
+
unpacked = unpacked.replace(r"\/", "/")
|
|
36
|
+
|
|
37
|
+
video_url = None
|
|
38
|
+
|
|
39
|
+
# Look for .mp4 or .m3u8 urls directly first
|
|
40
|
+
url_match = re.search(r'(https?://[^"\'\s]+\.(?:mp4|m3u8))', unpacked)
|
|
41
|
+
if url_match:
|
|
42
|
+
video_url = url_match.group(1)
|
|
43
|
+
|
|
44
|
+
if not video_url:
|
|
45
|
+
# Fallback: find all 'file': '...' and pick best
|
|
46
|
+
files = re.findall(r'file\s*:\s*["\']([^"\']+)["\']', unpacked)
|
|
47
|
+
for f in files:
|
|
48
|
+
if f.strip() and not f.endswith(".jpg") and not f.endswith(".png") and not f.endswith(".vtt"):
|
|
49
|
+
video_url = f
|
|
50
|
+
break
|
|
51
|
+
|
|
52
|
+
if not video_url:
|
|
53
|
+
raise ValueError("Could not find video URL in unpacked content")
|
|
54
|
+
|
|
55
|
+
return ExtractResult(
|
|
56
|
+
name = self.name,
|
|
57
|
+
url = video_url,
|
|
58
|
+
referer = url,
|
|
59
|
+
user_agent = headers.get("User-Agent", "")
|
|
60
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult
|
|
4
|
+
|
|
5
|
+
class HDPlayerSystem(ExtractorBase):
|
|
6
|
+
name = "HDPlayerSystem"
|
|
7
|
+
main_url = "https://hdplayersystem.com"
|
|
8
|
+
|
|
9
|
+
async def extract(self, url, referer=None) -> ExtractResult:
|
|
10
|
+
ext_ref = referer or ""
|
|
11
|
+
|
|
12
|
+
if "video/" in url:
|
|
13
|
+
vid_id = url.split("video/")[-1]
|
|
14
|
+
else:
|
|
15
|
+
vid_id = url.split("?data=")[-1]
|
|
16
|
+
|
|
17
|
+
post_url = f"{self.main_url}/player/index.php?data={vid_id}&do=getVideo"
|
|
18
|
+
|
|
19
|
+
response = await self.httpx.post(
|
|
20
|
+
url = post_url,
|
|
21
|
+
data = {"hash": vid_id, "r": ext_ref},
|
|
22
|
+
headers = {
|
|
23
|
+
"Referer" : ext_ref,
|
|
24
|
+
"Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8",
|
|
25
|
+
"X-Requested-With" : "XMLHttpRequest"
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
response.raise_for_status()
|
|
29
|
+
|
|
30
|
+
video_data = response.json()
|
|
31
|
+
m3u_link = video_data.get("securedLink")
|
|
32
|
+
|
|
33
|
+
if not m3u_link:
|
|
34
|
+
raise ValueError("securedLink not found in response")
|
|
35
|
+
|
|
36
|
+
return ExtractResult(
|
|
37
|
+
name = self.name,
|
|
38
|
+
url = m3u_link,
|
|
39
|
+
referer = url,
|
|
40
|
+
subtitles = []
|
|
41
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
|
+
|
|
3
|
+
from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
|
|
4
|
+
import re, json
|
|
5
|
+
|
|
6
|
+
class JetTv(ExtractorBase):
|
|
7
|
+
name = "JetTv"
|
|
8
|
+
main_url = "https://jetv.xyz"
|
|
9
|
+
|
|
10
|
+
async def extract(self, url: str, referer: str = None) -> ExtractResult:
|
|
11
|
+
istek = await self.httpx.get(url)
|
|
12
|
+
document = istek.text
|
|
13
|
+
|
|
14
|
+
# 1. Yöntem: API üzerinden alma
|
|
15
|
+
master_url = ""
|
|
16
|
+
final_ref = f"{self.main_url}/"
|
|
17
|
+
|
|
18
|
+
if "id=" in url:
|
|
19
|
+
vid_id = url.split("id=")[-1]
|
|
20
|
+
api_url = f"https://jetv.xyz/apollo/get_video.php?id={vid_id}"
|
|
21
|
+
try:
|
|
22
|
+
# Referer olarak video sayfasının kendisi gönderilmeli
|
|
23
|
+
api_resp = await self.httpx.get(api_url, headers={"Referer": url})
|
|
24
|
+
api_json = api_resp.json()
|
|
25
|
+
|
|
26
|
+
if api_json.get("success"):
|
|
27
|
+
master_url = api_json.get("masterUrl", "")
|
|
28
|
+
final_ref = api_json.get("referrerUrl") or final_ref
|
|
29
|
+
except Exception:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
# 2. Yöntem: Regex Fallback
|
|
33
|
+
if not master_url:
|
|
34
|
+
if match := re.search(r"file: '([^']*)'", document, re.IGNORE_CASE):
|
|
35
|
+
master_url = match.group(1)
|
|
36
|
+
|
|
37
|
+
if not master_url:
|
|
38
|
+
raise ValueError(f"JetTv: Video kaynağı bulunamadı. {url}")
|
|
39
|
+
|
|
40
|
+
return ExtractResult(
|
|
41
|
+
name = self.name,
|
|
42
|
+
url = master_url,
|
|
43
|
+
referer = final_ref,
|
|
44
|
+
subtitles = []
|
|
45
|
+
)
|
|
@@ -11,9 +11,9 @@ class MailRuExtractor(ExtractorBase):
|
|
|
11
11
|
video_meta_url = f"{self.main_url}/+/video/meta/{vid_id}"
|
|
12
12
|
|
|
13
13
|
if referer:
|
|
14
|
-
self.
|
|
14
|
+
self.httpx.headers.update({"Referer": referer})
|
|
15
15
|
|
|
16
|
-
istek = await self.
|
|
16
|
+
istek = await self.httpx.get(video_meta_url)
|
|
17
17
|
istek.raise_for_status()
|
|
18
18
|
|
|
19
19
|
video_key = istek.cookies.get("video_key")
|
|
@@ -34,6 +34,5 @@ class MailRuExtractor(ExtractorBase):
|
|
|
34
34
|
name = self.name,
|
|
35
35
|
url = video_url,
|
|
36
36
|
referer = self.main_url,
|
|
37
|
-
headers = {"Cookie": f"video_key={video_key}"},
|
|
38
37
|
subtitles = []
|
|
39
|
-
)
|
|
38
|
+
)
|
|
@@ -10,9 +10,9 @@ class MixPlayHD(ExtractorBase):
|
|
|
10
10
|
|
|
11
11
|
async def extract(self, url, referer=None) -> ExtractResult:
|
|
12
12
|
if referer:
|
|
13
|
-
self.
|
|
13
|
+
self.httpx.headers.update({"Referer": referer})
|
|
14
14
|
|
|
15
|
-
istek = await self.
|
|
15
|
+
istek = await self.httpx.get(url)
|
|
16
16
|
istek.raise_for_status()
|
|
17
17
|
|
|
18
18
|
be_player_match = re.search(r"bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);", istek.text)
|
|
@@ -36,7 +36,6 @@ class MixPlayHD(ExtractorBase):
|
|
|
36
36
|
name = self.name,
|
|
37
37
|
url = video_url_match[1],
|
|
38
38
|
referer = self.main_url,
|
|
39
|
-
headers = {},
|
|
40
39
|
subtitles = []
|
|
41
40
|
)
|
|
42
41
|
else:
|