KekikStream 0.7.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of KekikStream might be problematic. Click here for more details.

Files changed (59) hide show
  1. KekikStream/CLI/__init__.py +4 -0
  2. KekikStream/CLI/pypi_kontrol.py +30 -0
  3. KekikStream/Core/ExtractorBase.py +42 -0
  4. KekikStream/Core/ExtractorLoader.py +82 -0
  5. KekikStream/Core/ExtractorModels.py +19 -0
  6. KekikStream/Core/MediaHandler.py +142 -0
  7. KekikStream/Core/PluginBase.py +80 -0
  8. KekikStream/Core/PluginLoader.py +61 -0
  9. KekikStream/Core/PluginModels.py +63 -0
  10. KekikStream/Core/__init__.py +9 -0
  11. KekikStream/Extractors/CloseLoad.py +31 -0
  12. KekikStream/Extractors/ContentX.py +80 -0
  13. KekikStream/Extractors/FourCX.py +7 -0
  14. KekikStream/Extractors/FourPichive.py +7 -0
  15. KekikStream/Extractors/FourPlayRu.py +7 -0
  16. KekikStream/Extractors/HDStreamAble.py +7 -0
  17. KekikStream/Extractors/Hotlinger.py +7 -0
  18. KekikStream/Extractors/MailRu.py +40 -0
  19. KekikStream/Extractors/MixPlayHD.py +42 -0
  20. KekikStream/Extractors/Odnoklassniki.py +106 -0
  21. KekikStream/Extractors/OkRuHTTP.py +7 -0
  22. KekikStream/Extractors/OkRuSSL.py +7 -0
  23. KekikStream/Extractors/PeaceMakerst.py +57 -0
  24. KekikStream/Extractors/Pichive.py +7 -0
  25. KekikStream/Extractors/PixelDrain.py +28 -0
  26. KekikStream/Extractors/PlayRu.py +7 -0
  27. KekikStream/Extractors/RapidVid.py +60 -0
  28. KekikStream/Extractors/SibNet.py +29 -0
  29. KekikStream/Extractors/Sobreatsesuyp.py +59 -0
  30. KekikStream/Extractors/TRsTX.py +67 -0
  31. KekikStream/Extractors/TauVideo.py +34 -0
  32. KekikStream/Extractors/TurboImgz.py +25 -0
  33. KekikStream/Extractors/VidMoly.py +85 -0
  34. KekikStream/Extractors/VidMoxy.py +50 -0
  35. KekikStream/Extractors/VideoSeyred.py +47 -0
  36. KekikStream/Managers/ExtractorManager.py +27 -0
  37. KekikStream/Managers/MediaManager.py +19 -0
  38. KekikStream/Managers/PluginManager.py +19 -0
  39. KekikStream/Managers/UIManager.py +49 -0
  40. KekikStream/Managers/__init__.py +6 -0
  41. KekikStream/Plugins/DiziBox.py +143 -0
  42. KekikStream/Plugins/DiziYou.py +127 -0
  43. KekikStream/Plugins/Dizilla.py +106 -0
  44. KekikStream/Plugins/FilmMakinesi.py +65 -0
  45. KekikStream/Plugins/FullHDFilmizlesene.py +78 -0
  46. KekikStream/Plugins/JetFilmizle.py +92 -0
  47. KekikStream/Plugins/RecTV.py +113 -0
  48. KekikStream/Plugins/SezonlukDizi.py +108 -0
  49. KekikStream/Plugins/SineWix.py +108 -0
  50. KekikStream/Plugins/UgurFilm.py +75 -0
  51. KekikStream/__init__.py +255 -0
  52. KekikStream/__main__.py +6 -0
  53. KekikStream/requirements.txt +10 -0
  54. KekikStream-0.7.1.dist-info/LICENSE +674 -0
  55. KekikStream-0.7.1.dist-info/METADATA +94 -0
  56. KekikStream-0.7.1.dist-info/RECORD +59 -0
  57. KekikStream-0.7.1.dist-info/WHEEL +5 -0
  58. KekikStream-0.7.1.dist-info/entry_points.txt +2 -0
  59. KekikStream-0.7.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,85 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+ # ! https://github.com/recloudstream/cloudstream/blob/master/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidmoly.kt
3
+
4
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
5
+ import re, asyncio, contextlib, json
6
+
7
+ class VidMoly(ExtractorBase):
8
+ name = "VidMoly"
9
+ main_url = "https://vidmoly.to"
10
+
11
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
12
+ if referer:
13
+ self.oturum.headers.update({"Referer": referer})
14
+
15
+ self.oturum.headers.update({
16
+ "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
17
+ "Sec-Fetch-Dest" : "iframe",
18
+ })
19
+
20
+ # Embed URL oluştur
21
+ embed_url = url.replace("/w/", "/embed-") + "-920x360.html" if "/w/" in url else url
22
+ script_content = None
23
+ attempts = 0
24
+
25
+ # Script verisini almak için deneme yap
26
+ while attempts < 10 and not script_content:
27
+ attempts += 1
28
+ response = await self.oturum.get(embed_url)
29
+ response.raise_for_status()
30
+
31
+ script_match = re.search(r"sources:\s*\[(.*?)\],", response.text, re.DOTALL)
32
+ script_content = script_match[1] if script_match else None
33
+ if not script_content:
34
+ await asyncio.sleep(0.5)
35
+
36
+ if not script_content:
37
+ raise ValueError("Gerekli script bulunamadı.")
38
+
39
+ # Video kaynaklarını ayrıştır
40
+ video_data = self._add_marks(script_content, "file")
41
+ try:
42
+ video_sources = json.loads(f"[{video_data}]")
43
+ except json.JSONDecodeError as hata:
44
+ raise ValueError("Video kaynakları ayrıştırılamadı.") from hata
45
+
46
+ # Altyazı kaynaklarını ayrıştır
47
+ subtitles = []
48
+ if subtitle_match := re.search(r"tracks:\s*\[(.*?)\]", response.text, re.DOTALL):
49
+ subtitle_data = self._add_marks(subtitle_match[1], "file")
50
+ subtitle_data = self._add_marks(subtitle_data, "label")
51
+ subtitle_data = self._add_marks(subtitle_data, "kind")
52
+
53
+ with contextlib.suppress(json.JSONDecodeError):
54
+ subtitle_sources = json.loads(f"[{subtitle_data}]")
55
+ subtitles = [
56
+ Subtitle(
57
+ name = sub.get("label"),
58
+ url = self.fix_url(sub.get("file")),
59
+ )
60
+ for sub in subtitle_sources
61
+ if sub.get("kind") == "captions"
62
+ ]
63
+ # İlk video kaynağını al
64
+ video_url = None
65
+ for source in video_sources:
66
+ if file_url := source.get("file"):
67
+ video_url = file_url
68
+ break
69
+
70
+ if not video_url:
71
+ raise ValueError("Video URL bulunamadı.")
72
+
73
+ await self.close()
74
+ return ExtractResult(
75
+ name = self.name,
76
+ url = video_url,
77
+ referer = self.main_url,
78
+ subtitles = subtitles
79
+ )
80
+
81
+ def _add_marks(self, text: str, field: str) -> str:
82
+ """
83
+ Verilen alanı çift tırnak içine alır.
84
+ """
85
+ return re.sub(rf"\"?{field}\"?", f"\"{field}\"", text)
@@ -0,0 +1,50 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
+ from Kekik.Sifreleme import Packer, HexCodec
5
+ import re
6
+
7
+ class VidMoxy(ExtractorBase):
8
+ name = "VidMoxy"
9
+ main_url = "https://vidmoxy.com"
10
+
11
+ async def extract(self, url, referer=None) -> ExtractResult:
12
+ if referer:
13
+ self.oturum.headers.update({"Referer": referer})
14
+
15
+ istek = await self.oturum.get(url)
16
+ istek.raise_for_status()
17
+
18
+ subtitles = []
19
+ subtitle_matches = re.findall(r'captions","file":"([^"]+)","label":"([^"]+)"', istek.text)
20
+ seen_subtitles = set()
21
+
22
+ for sub_url, sub_lang in subtitle_matches:
23
+ if sub_url in seen_subtitles:
24
+ continue
25
+
26
+ seen_subtitles.add(sub_url)
27
+ decoded_lang = (
28
+ sub_lang.replace("\\u0131", "ı")
29
+ .replace("\\u0130", "İ")
30
+ .replace("\\u00fc", "ü")
31
+ .replace("\\u00e7", "ç")
32
+ )
33
+ subtitles.append(Subtitle(name=decoded_lang, url=sub_url.replace("\\", "")))
34
+
35
+ try:
36
+ escaped_hex = re.findall(r'file": "(.*)",', istek.text)[0]
37
+ except Exception:
38
+ eval_jwsetup = re.compile(r'\};\s*(eval\(function[\s\S]*?)var played = \d+;').findall(istek.text)[0]
39
+ jwsetup = Packer.unpack(Packer.unpack(eval_jwsetup))
40
+ escaped_hex = re.findall(r'file":"(.*)","label', jwsetup)[0]
41
+
42
+ m3u_link = HexCodec.decode(escaped_hex)
43
+
44
+ await self.close()
45
+ return ExtractResult(
46
+ name = self.name,
47
+ url = m3u_link,
48
+ referer = self.main_url,
49
+ subtitles = subtitles
50
+ )
@@ -0,0 +1,47 @@
1
+ # ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle
4
+ import json
5
+
6
+ class VideoSeyred(ExtractorBase):
7
+ name = "VideoSeyred"
8
+ main_url = "https://videoseyred.in"
9
+
10
+ async def extract(self, url, referer=None) -> ExtractResult:
11
+ if referer:
12
+ self.oturum.headers.update({"Referer": referer})
13
+
14
+ video_id = url.split("embed/")[1].split("?")[0]
15
+ video_url = f"{self.main_url}/playlist/{video_id}.json"
16
+
17
+ response = await self.oturum.get(video_url)
18
+ response.raise_for_status()
19
+
20
+ try:
21
+ if response_list := json.loads(response.text):
22
+ response_data = response_list[0]
23
+ else:
24
+ raise ValueError("Empty response from VideoSeyred.")
25
+
26
+ except (json.JSONDecodeError, IndexError) as hata:
27
+ raise RuntimeError(f"Failed to parse response: {hata}") from hata
28
+
29
+ subtitles = [
30
+ Subtitle(name=track["label"], url=self.fix_url(track["file"]))
31
+ for track in response_data.get("tracks", [])
32
+ if track.get("kind") == "captions" and track.get("label")
33
+ ]
34
+
35
+ if video_links := [
36
+ ExtractResult(
37
+ name = self.name,
38
+ url = self.fix_url(source["file"]),
39
+ referer = self.main_url,
40
+ subtitles = subtitles,
41
+ )
42
+ for source in response_data.get("sources", [])
43
+ ]:
44
+ # En yüksek kaliteli videoyu döndür (varsayılan olarak ilk video)
45
+ return video_links[0] if len(video_links) == 1 else video_links
46
+ else:
47
+ raise ValueError("No video links found in the response.")
@@ -0,0 +1,27 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from ..Core import ExtractorLoader, ExtractorBase
4
+
5
+ class ExtractorManager:
6
+ def __init__(self, extractor_dir="Extractors"):
7
+ self.extractor_loader = ExtractorLoader(extractor_dir)
8
+ self.extractors = self.extractor_loader.load_all()
9
+
10
+ def find_extractor(self, link):
11
+ for extractor_cls in self.extractors:
12
+ extractor:ExtractorBase = extractor_cls()
13
+ if extractor.can_handle_url(link):
14
+ return extractor
15
+
16
+ return None
17
+
18
+ def map_links_to_extractors(self, links):
19
+ mapping = {}
20
+ for link in links:
21
+ for extractor_cls in self.extractors:
22
+ extractor:ExtractorBase = extractor_cls()
23
+ if extractor.can_handle_url(link):
24
+ mapping[link] = f"{extractor.name:<30} » {link.replace(extractor.main_url, '')}"
25
+ break
26
+
27
+ return mapping
@@ -0,0 +1,19 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from ..Core import MediaHandler
4
+
5
+ class MediaManager:
6
+ def __init__(self):
7
+ self.media_handler = MediaHandler()
8
+
9
+ def set_title(self, title):
10
+ self.media_handler.title = title
11
+
12
+ def get_title(self):
13
+ return self.media_handler.title
14
+
15
+ def set_headers(self, headers):
16
+ self.media_handler.headers.update(headers)
17
+
18
+ def play_media(self, extract_data):
19
+ self.media_handler.play_media(extract_data)
@@ -0,0 +1,19 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from ..Core import PluginLoader, PluginBase
4
+
5
+ class PluginManager:
6
+ def __init__(self, plugin_dir="Plugins"):
7
+ self.plugin_loader = PluginLoader(plugin_dir)
8
+ self.plugins = self.plugin_loader.load_all()
9
+
10
+ def get_plugin_names(self):
11
+ return sorted(list(self.plugins.keys()))
12
+
13
+ def select_plugin(self, plugin_name):
14
+ return self.plugins.get(plugin_name)
15
+
16
+ async def close_plugins(self):
17
+ for plugin in self.plugins.values():
18
+ if isinstance(plugin, PluginBase):
19
+ await plugin.close()
@@ -0,0 +1,49 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from ..CLI import konsol
4
+ from rich.panel import Panel
5
+ from rich.table import Table
6
+ from InquirerPy import inquirer
7
+ import os
8
+
9
+ class UIManager:
10
+ @staticmethod
11
+ def clear_console():
12
+ # return True
13
+ os.system("cls" if os.name == "nt" else "clear")
14
+
15
+ @staticmethod
16
+ async def select_from_list(message, choices):
17
+ return await inquirer.select(message=message, choices=choices, max_height="75%").execute_async()
18
+
19
+ @staticmethod
20
+ async def select_from_fuzzy(message, choices):
21
+ return await inquirer.fuzzy(
22
+ message = message,
23
+ choices = choices,
24
+ validate = lambda result: result in [choice if isinstance(choice, str) else choice["value"] for choice in choices],
25
+ filter = lambda result: result,
26
+ max_height = "75%"
27
+ ).execute_async()
28
+
29
+ @staticmethod
30
+ async def prompt_text(message):
31
+ return await inquirer.text(message=message).execute_async()
32
+
33
+ @staticmethod
34
+ def display_media_info(plugin_name, media_info):
35
+ table = Table(show_header=False, box=None)
36
+ table.add_column(justify="right", style="cyan", no_wrap=True)
37
+ table.add_column(style="magenta")
38
+
39
+ for key, value in media_info.dict().items():
40
+ if key == "episodes":
41
+ continue
42
+
43
+ if isinstance(value, str):
44
+ if '",' in value:
45
+ continue
46
+
47
+ table.add_row(f"[bold cyan]{key.capitalize()}[/bold cyan]", str(value))
48
+
49
+ konsol.print(Panel(table, title=f"[bold green]{plugin_name}[/bold green]", expand=False))
@@ -0,0 +1,6 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from .PluginManager import PluginManager
4
+ from .ExtractorManager import ExtractorManager
5
+ from .UIManager import UIManager
6
+ from .MediaManager import MediaManager
@@ -0,0 +1,143 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import PluginBase, SearchResult, SeriesInfo, Episode
4
+ from Kekik.Sifreleme import CryptoJS
5
+ from parsel import Selector
6
+ import re, urllib.parse, base64, contextlib, asyncio
7
+
8
+ class DiziBox(PluginBase):
9
+ name = "DiziBox"
10
+ main_url = "https://www.dizibox.plus"
11
+
12
+ async def search(self, query: str) -> list[SearchResult]:
13
+ self.oturum.cookies.update({
14
+ "LockUser" : "true",
15
+ "isTrustedUser" : "true",
16
+ "dbxu" : "1722403730363"
17
+ })
18
+ istek = await self.oturum.get(f"{self.main_url}/?s={query}")
19
+ secici = Selector(istek.text)
20
+
21
+ return [
22
+ SearchResult(
23
+ title = item.css("h3 a::text").get(),
24
+ url = self.fix_url(item.css("h3 a::attr(href)").get()),
25
+ poster = self.fix_url(item.css("img::attr(src)").get()),
26
+ )
27
+ for item in secici.css("article.detailed-article")
28
+ ]
29
+
30
+ async def load_item(self, url: str) -> SeriesInfo:
31
+ istek = await self.oturum.get(url)
32
+ secici = Selector(istek.text)
33
+
34
+ title = secici.css("div.tv-overview h1 a::text").get()
35
+ poster = self.fix_url(secici.css("div.tv-overview figure img::attr(src)").get())
36
+ description = secici.css("div.tv-story p::text").get()
37
+ year = secici.css("a[href*='/yil/']::text").re_first(r"(\d{4})")
38
+ tags = secici.css("a[href*='/tur/']::text").getall()
39
+ rating = secici.css("span.label-imdb b::text").re_first(r"[\d.,]+")
40
+ actors = [actor.css("::text").get() for actor in secici.css("a[href*='/oyuncu/']")]
41
+
42
+ episodes = []
43
+ for sezon_link in secici.css("div#seasons-list a::attr(href)").getall():
44
+ sezon_url = self.fix_url(sezon_link)
45
+ sezon_istek = await self.oturum.get(sezon_url)
46
+ sezon_secici = Selector(sezon_istek.text)
47
+
48
+ for bolum in sezon_secici.css("article.grid-box"):
49
+ ep_secici = bolum.css("div.post-title a::text")
50
+
51
+ ep_title = ep_secici.get()
52
+ ep_href = self.fix_url(bolum.css("div.post-title a::attr(href)").get())
53
+ ep_season = ep_secici.re_first(r"(\d+)\. ?Sezon")
54
+ ep_episode = ep_secici.re_first(r"(\d+)\. ?Bölüm")
55
+
56
+ if ep_title and ep_href:
57
+ episodes.append(Episode(
58
+ season = ep_season,
59
+ episode = ep_episode,
60
+ title = ep_title.strip(),
61
+ url = ep_href,
62
+ ))
63
+
64
+ return SeriesInfo(
65
+ url = url,
66
+ poster = poster,
67
+ title = title,
68
+ description = description,
69
+ tags = tags,
70
+ rating = rating,
71
+ year = year,
72
+ episodes = episodes,
73
+ actors = actors,
74
+ )
75
+
76
+ async def _iframe_decode(self, name:str, iframe_link:str, referer:str) -> list[str]:
77
+ results = []
78
+
79
+ if "/player/king/king.php" in iframe_link:
80
+ iframe_link = iframe_link.replace("king.php?v=", "king.php?wmode=opaque&v=")
81
+ self.oturum.headers.update({"Referer": referer})
82
+
83
+ istek = await self.oturum.get(iframe_link)
84
+ secici = Selector(istek.text)
85
+ iframe = secici.css("div#Player iframe::attr(src)").get()
86
+
87
+ self.oturum.headers.update({"Referer": self.main_url})
88
+ istek = await self.oturum.get(iframe)
89
+
90
+ crypt_data = re.search(r"CryptoJS\.AES\.decrypt\(\"(.*)\",\"", istek.text)[1]
91
+ crypt_pass = re.search(r"\",\"(.*)\"\);", istek.text)[1]
92
+
93
+ results.append(CryptoJS.decrypt(crypt_pass, crypt_data))
94
+
95
+ elif "/player/moly/moly.php" in iframe_link:
96
+ iframe_link = iframe_link.replace("moly.php?h=", "moly.php?wmode=opaque&h=")
97
+ self.oturum.headers.update({"Referer": referer})
98
+ while True:
99
+ await asyncio.sleep(.3)
100
+ with contextlib.suppress(Exception):
101
+ istek = await self.oturum.get(iframe_link)
102
+
103
+ if atob_data := re.search(r"unescape\(\"(.*)\"\)", istek.text):
104
+ decoded_atob = urllib.parse.unquote(atob_data[1])
105
+ str_atob = base64.b64decode(decoded_atob).decode("utf-8")
106
+
107
+ if iframe := Selector(str_atob).css("div#Player iframe::attr(src)").get():
108
+ results.append(iframe)
109
+
110
+ break
111
+
112
+ elif "/player/haydi.php" in iframe_link:
113
+ okru_url = base64.b64decode(iframe_link.split("?v=")[-1]).decode("utf-8")
114
+ results.append(okru_url)
115
+
116
+ return results
117
+
118
+ async def load_links(self, url: str) -> list[str]:
119
+ istek = await self.oturum.get(url)
120
+ secici = Selector(istek.text)
121
+
122
+ iframes = []
123
+ if main_iframe := secici.css("div#video-area iframe::attr(src)").get():
124
+ if decoded := await self._iframe_decode(self.name, main_iframe, url):
125
+ iframes.extend(decoded)
126
+
127
+ for alternatif in secici.css("div.video-toolbar option[value]"):
128
+ alt_name = alternatif.css("::text").get()
129
+ alt_link = alternatif.css("::attr(value)").get()
130
+
131
+ if not alt_link:
132
+ continue
133
+
134
+ self.oturum.headers.update({"Referer": url})
135
+ alt_istek = await self.oturum.get(alt_link)
136
+ alt_istek.raise_for_status()
137
+
138
+ alt_secici = Selector(alt_istek.text)
139
+ if alt_iframe := alt_secici.css("div#video-area iframe::attr(src)").get():
140
+ if decoded := await self._iframe_decode(alt_name, alt_iframe, url):
141
+ iframes.extend(decoded)
142
+
143
+ return iframes
@@ -0,0 +1,127 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import PluginBase, SearchResult, SeriesInfo, Episode, Subtitle, ExtractResult
4
+ from parsel import Selector
5
+ import re
6
+
7
+ class DiziYou(PluginBase):
8
+ name = "DiziYou"
9
+ main_url = "https://www.diziyou.co"
10
+
11
+ async def search(self, query: str) -> list[SearchResult]:
12
+ istek = await self.oturum.get(f"{self.main_url}/?s={query}")
13
+ secici = Selector(istek.text)
14
+
15
+ return [
16
+ SearchResult(
17
+ title = afis.css("div#categorytitle a::text").get().strip(),
18
+ url = self.fix_url(afis.css("div#categorytitle a::attr(href)").get()),
19
+ poster = self.fix_url(afis.css("img::attr(src)").get()),
20
+ )
21
+ for afis in secici.css("div.incontent div#list-series")
22
+ ]
23
+
24
+ async def load_item(self, url: str) -> SeriesInfo:
25
+ istek = await self.oturum.get(url)
26
+ secici = Selector(istek.text)
27
+
28
+ title = secici.css("h1::text").get().strip()
29
+ poster = self.fix_url(secici.css("div.category_image img::attr(src)").get().strip())
30
+ year = secici.xpath("//span[contains(., 'Yapım Yılı')]/following-sibling::text()[1]").get()
31
+ description = secici.css("div.diziyou_desc::text").get().strip()
32
+ tags = secici.css("div.genres a::text").getall()
33
+ rating = secici.xpath("//span[contains(., 'IMDB')]/following-sibling::text()[1]").get()
34
+ _actors = secici.xpath("//span[contains(., 'Oyuncular')]/following-sibling::text()[1]").get()
35
+ actors = [actor.strip() for actor in _actors.split(",")] if _actors else []
36
+
37
+ episodes = []
38
+ for it in secici.css("div.bolumust"):
39
+ ep_name = it.css("div.baslik::text").get().strip()
40
+ ep_href = it.xpath("ancestor::a/@href").get()
41
+ if not ep_name or not ep_href:
42
+ continue
43
+
44
+ ep_name_clean = it.css("div.bolumismi::text").get().strip().replace("(", "").replace(")", "").strip() if it.css("div.bolumismi::text").get() else ep_name
45
+
46
+ ep_episode = re.search(r"(\d+)\. Bölüm", ep_name)[1]
47
+ ep_season = re.search(r"(\d+)\. Sezon", ep_name)[1]
48
+
49
+ episode = Episode(
50
+ season = ep_season,
51
+ episode = ep_episode,
52
+ title = ep_name_clean,
53
+ url = ep_href,
54
+ )
55
+
56
+ episodes.append(episode)
57
+
58
+ return SeriesInfo(
59
+ url = url,
60
+ poster = poster,
61
+ title = title,
62
+ description = description,
63
+ tags = tags,
64
+ rating = rating,
65
+ year = year,
66
+ episodes = episodes,
67
+ actors = actors
68
+ )
69
+
70
+ async def load_links(self, url: str) -> list[str]:
71
+ istek = await self.oturum.get(url)
72
+ secici = Selector(istek.text)
73
+
74
+ item_title = secici.css("div.title h1::text").get().strip()
75
+ item_id = secici.css("iframe#diziyouPlayer::attr(src)").get().split("/")[-1].replace(".html", "")
76
+
77
+ subtitles = []
78
+ stream_urls = []
79
+
80
+ for secenek in secici.css("span.diziyouOption"):
81
+ opt_id = secenek.css("::attr(id)").get()
82
+ op_name = secenek.css("::text").get()
83
+
84
+ match opt_id:
85
+ case "turkceAltyazili":
86
+ subtitles.append(Subtitle(
87
+ name = op_name,
88
+ url = self.fix_url(f"https://storage.diziyou.co/subtitles/{item_id}/tr.vtt"),
89
+ ))
90
+ veri = {
91
+ "dil": "Orjinal Dil",
92
+ "url": f"https://storage.diziyou.co/episodes/{item_id}/play.m3u8"
93
+ }
94
+ if veri not in stream_urls:
95
+ stream_urls.append(veri)
96
+ case "ingilizceAltyazili":
97
+ subtitles.append(Subtitle(
98
+ name = op_name,
99
+ url = self.fix_url(f"https://storage.diziyou.co/subtitles/{item_id}/en.vtt"),
100
+ ))
101
+ veri = {
102
+ "dil": "Orjinal Dil",
103
+ "url": f"https://storage.diziyou.co/episodes/{item_id}/play.m3u8"
104
+ }
105
+ if veri not in stream_urls:
106
+ stream_urls.append(veri)
107
+ case "turkceDublaj":
108
+ stream_urls.append({
109
+ "dil": "Dublaj",
110
+ "url": f"https://storage.diziyou.co/episodes/{item_id}_tr/play.m3u8"
111
+ })
112
+
113
+
114
+ for stream in stream_urls:
115
+ self._data[stream.get("url")] = {
116
+ "name" : f"{self.name} | {stream.get('dil')} | {item_title}",
117
+ "ext_name" : f"{self.name} | {stream.get('dil')}",
118
+ "referer" : url,
119
+ "subtitles" : subtitles
120
+ }
121
+
122
+ return [stream.get("url") for stream in stream_urls]
123
+
124
+ async def play(self, name: str, url: str, referer: str, subtitles: list[Subtitle]):
125
+ extract_result = ExtractResult(name=name, url=url, referer=referer, subtitles=subtitles)
126
+ self.media_handler.title = name
127
+ self.media_handler.play_media(extract_result)