KekikStream 2.4.2__tar.gz → 2.4.4__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.
Files changed (135) hide show
  1. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/Extractor/ExtractorBase.py +3 -2
  2. kekikstream-2.4.4/KekikStream/Core/HTMLHelper.py +228 -0
  3. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/Plugin/PluginBase.py +3 -2
  4. kekikstream-2.4.4/KekikStream/Extractors/CloseLoad.py +52 -0
  5. kekikstream-2.4.4/KekikStream/Extractors/ContentX.py +54 -0
  6. kekikstream-2.4.4/KekikStream/Extractors/DonilasPlay.py +42 -0
  7. kekikstream-2.4.4/KekikStream/Extractors/DzenRu.py +24 -0
  8. kekikstream-2.4.4/KekikStream/Extractors/ExPlay.py +35 -0
  9. kekikstream-2.4.4/KekikStream/Extractors/Filemoon.py +63 -0
  10. kekikstream-2.4.4/KekikStream/Extractors/HDMomPlayer.py +30 -0
  11. kekikstream-2.4.4/KekikStream/Extractors/HDPlayerSystem.py +23 -0
  12. kekikstream-2.4.4/KekikStream/Extractors/HotStream.py +27 -0
  13. kekikstream-2.4.4/KekikStream/Extractors/JFVid.py +19 -0
  14. kekikstream-2.4.4/KekikStream/Extractors/JetTv.py +32 -0
  15. kekikstream-2.4.4/KekikStream/Extractors/MailRu.py +20 -0
  16. kekikstream-2.4.4/KekikStream/Extractors/MixPlayHD.py +28 -0
  17. kekikstream-2.4.4/KekikStream/Extractors/MixTiger.py +34 -0
  18. kekikstream-2.4.4/KekikStream/Extractors/MolyStream.py +38 -0
  19. kekikstream-2.4.4/KekikStream/Extractors/Odnoklassniki.py +41 -0
  20. kekikstream-2.4.4/KekikStream/Extractors/PeaceMakerst.py +36 -0
  21. kekikstream-2.4.4/KekikStream/Extractors/PixelDrain.py +20 -0
  22. kekikstream-2.4.4/KekikStream/Extractors/PlayerFilmIzle.py +46 -0
  23. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Extractors/RapidVid.py +21 -35
  24. kekikstream-2.4.4/KekikStream/Extractors/SetPlay.py +41 -0
  25. kekikstream-2.4.4/KekikStream/Extractors/SibNet.py +17 -0
  26. kekikstream-2.4.4/KekikStream/Extractors/Sobreatsesuyp.py +37 -0
  27. kekikstream-2.4.4/KekikStream/Extractors/TRsTX.py +37 -0
  28. kekikstream-2.4.4/KekikStream/Extractors/TurboImgz.py +17 -0
  29. kekikstream-2.4.4/KekikStream/Extractors/VCTPlay.py +23 -0
  30. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Extractors/VidHide.py +10 -31
  31. kekikstream-2.4.4/KekikStream/Extractors/VidMoly.py +98 -0
  32. kekikstream-2.4.4/KekikStream/Extractors/VidMoxy.py +37 -0
  33. kekikstream-2.4.4/KekikStream/Extractors/VidPapi.py +57 -0
  34. kekikstream-2.4.4/KekikStream/Extractors/VideoSeyred.py +32 -0
  35. kekikstream-2.4.4/KekikStream/Extractors/Videostr.py +58 -0
  36. kekikstream-2.4.4/KekikStream/Extractors/Vidoza.py +18 -0
  37. kekikstream-2.4.4/KekikStream/Extractors/YildizKisaFilm.py +23 -0
  38. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/BelgeselX.py +63 -69
  39. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/DiziBox.py +16 -36
  40. kekikstream-2.4.4/KekikStream/Plugins/DiziMom.py +155 -0
  41. kekikstream-2.4.4/KekikStream/Plugins/DiziPal.py +167 -0
  42. kekikstream-2.4.4/KekikStream/Plugins/DiziYou.py +135 -0
  43. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/Dizilla.py +18 -44
  44. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/FilmBip.py +10 -24
  45. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/FilmEkseni.py +12 -32
  46. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/FilmMakinesi.py +24 -77
  47. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/FilmModu.py +11 -18
  48. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/Filmatek.py +13 -39
  49. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/Full4kizle.py +33 -133
  50. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/FullHDFilm.py +23 -93
  51. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/FullHDFilmizlesene.py +10 -29
  52. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/HDFilmCehennemi.py +27 -66
  53. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/JetFilmizle.py +19 -20
  54. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/KultFilmler.py +16 -50
  55. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/RecTV.py +47 -85
  56. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/SelcukFlix.py +29 -47
  57. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/SetFilmIzle.py +28 -84
  58. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/SezonlukDizi.py +27 -59
  59. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/Sinefy.py +37 -100
  60. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/SinemaCX.py +12 -18
  61. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/Sinezy.py +11 -12
  62. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/SuperFilmGeldi.py +8 -13
  63. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/UgurFilm.py +14 -14
  64. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/Watch32.py +42 -74
  65. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/YabanciDizi.py +33 -87
  66. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream.egg-info/PKG-INFO +1 -1
  67. {kekikstream-2.4.2 → kekikstream-2.4.4}/PKG-INFO +1 -1
  68. {kekikstream-2.4.2 → kekikstream-2.4.4}/setup.py +1 -1
  69. kekikstream-2.4.2/KekikStream/Core/HTMLHelper.py +0 -134
  70. kekikstream-2.4.2/KekikStream/Extractors/CloseLoad.py +0 -76
  71. kekikstream-2.4.2/KekikStream/Extractors/ContentX.py +0 -99
  72. kekikstream-2.4.2/KekikStream/Extractors/DonilasPlay.py +0 -86
  73. kekikstream-2.4.2/KekikStream/Extractors/DzenRu.py +0 -38
  74. kekikstream-2.4.2/KekikStream/Extractors/ExPlay.py +0 -53
  75. kekikstream-2.4.2/KekikStream/Extractors/Filemoon.py +0 -89
  76. kekikstream-2.4.2/KekikStream/Extractors/HDMomPlayer.py +0 -62
  77. kekikstream-2.4.2/KekikStream/Extractors/HDPlayerSystem.py +0 -41
  78. kekikstream-2.4.2/KekikStream/Extractors/HotStream.py +0 -45
  79. kekikstream-2.4.2/KekikStream/Extractors/JFVid.py +0 -40
  80. kekikstream-2.4.2/KekikStream/Extractors/JetTv.py +0 -45
  81. kekikstream-2.4.2/KekikStream/Extractors/MailRu.py +0 -38
  82. kekikstream-2.4.2/KekikStream/Extractors/MixPlayHD.py +0 -41
  83. kekikstream-2.4.2/KekikStream/Extractors/MixTiger.py +0 -57
  84. kekikstream-2.4.2/KekikStream/Extractors/MolyStream.py +0 -42
  85. kekikstream-2.4.2/KekikStream/Extractors/Odnoklassniki.py +0 -117
  86. kekikstream-2.4.2/KekikStream/Extractors/PeaceMakerst.py +0 -63
  87. kekikstream-2.4.2/KekikStream/Extractors/PixelDrain.py +0 -28
  88. kekikstream-2.4.2/KekikStream/Extractors/PlayerFilmIzle.py +0 -65
  89. kekikstream-2.4.2/KekikStream/Extractors/SetPlay.py +0 -66
  90. kekikstream-2.4.2/KekikStream/Extractors/SibNet.py +0 -27
  91. kekikstream-2.4.2/KekikStream/Extractors/Sobreatsesuyp.py +0 -59
  92. kekikstream-2.4.2/KekikStream/Extractors/TRsTX.py +0 -67
  93. kekikstream-2.4.2/KekikStream/Extractors/TurboImgz.py +0 -24
  94. kekikstream-2.4.2/KekikStream/Extractors/VCTPlay.py +0 -41
  95. kekikstream-2.4.2/KekikStream/Extractors/VidMoly.py +0 -132
  96. kekikstream-2.4.2/KekikStream/Extractors/VidMoxy.py +0 -48
  97. kekikstream-2.4.2/KekikStream/Extractors/VidPapi.py +0 -87
  98. kekikstream-2.4.2/KekikStream/Extractors/VideoSeyred.py +0 -53
  99. kekikstream-2.4.2/KekikStream/Extractors/Videostr.py +0 -115
  100. kekikstream-2.4.2/KekikStream/Extractors/Vidoza.py +0 -25
  101. kekikstream-2.4.2/KekikStream/Extractors/YildizKisaFilm.py +0 -41
  102. kekikstream-2.4.2/KekikStream/Plugins/DiziMom.py +0 -247
  103. kekikstream-2.4.2/KekikStream/Plugins/DiziPal.py +0 -260
  104. kekikstream-2.4.2/KekikStream/Plugins/DiziYou.py +0 -243
  105. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/CLI/__init__.py +0 -0
  106. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/CLI/pypi_kontrol.py +0 -0
  107. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
  108. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
  109. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/Extractor/ExtractorModels.py +0 -0
  110. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/Extractor/YTDLPCache.py +0 -0
  111. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/Media/MediaHandler.py +0 -0
  112. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/Media/MediaManager.py +0 -0
  113. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
  114. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/Plugin/PluginManager.py +0 -0
  115. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/Plugin/PluginModels.py +0 -0
  116. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/UI/UIManager.py +0 -0
  117. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Core/__init__.py +0 -0
  118. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Extractors/SetPrime.py +0 -0
  119. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Extractors/TauVideo.py +0 -0
  120. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Extractors/TurkeyPlayer.py +0 -0
  121. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Extractors/YTDLP.py +0 -0
  122. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/RoketDizi.py +0 -0
  123. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/Plugins/SineWix.py +0 -0
  124. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/__init__.py +0 -0
  125. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/__main__.py +0 -0
  126. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream/requirements.txt +0 -0
  127. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream.egg-info/SOURCES.txt +0 -0
  128. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream.egg-info/dependency_links.txt +0 -0
  129. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream.egg-info/entry_points.txt +0 -0
  130. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream.egg-info/requires.txt +0 -0
  131. {kekikstream-2.4.2 → kekikstream-2.4.4}/KekikStream.egg-info/top_level.txt +0 -0
  132. {kekikstream-2.4.2 → kekikstream-2.4.4}/LICENSE +0 -0
  133. {kekikstream-2.4.2 → kekikstream-2.4.4}/MANIFEST.in +0 -0
  134. {kekikstream-2.4.2 → kekikstream-2.4.4}/README.md +0 -0
  135. {kekikstream-2.4.2 → kekikstream-2.4.4}/setup.cfg +0 -0
@@ -49,6 +49,7 @@ class ExtractorBase(ABC):
49
49
  return ""
50
50
 
51
51
  if url.startswith("http") or url.startswith("{\""):
52
- return url
52
+ return url.replace("\\", "")
53
53
 
54
- return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
54
+ url = f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
55
+ return url.replace("\\", "")
@@ -0,0 +1,228 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from selectolax.parser import HTMLParser, Node
7
+
8
+
9
+ class HTMLHelper:
10
+ """
11
+ Selectolax ile HTML parsing işlemlerini temiz, kısa ve okunabilir hale getiren yardımcı sınıf.
12
+ """
13
+
14
+ def __init__(self, html: str):
15
+ self.html = html
16
+ self.parser = HTMLParser(html)
17
+
18
+ # ========================
19
+ # SELECTOR (CSS) İŞLEMLERİ
20
+ # ========================
21
+
22
+ def _root(self, element: Node | None) -> Node | HTMLParser:
23
+ """İşlem yapılacak temel elementi döndürür."""
24
+ return element if element is not None else self.parser
25
+
26
+ def select(self, selector: str, element: Node | None = None) -> list[Node]:
27
+ """CSS selector ile tüm eşleşen elementleri döndür."""
28
+ return self._root(element).css(selector)
29
+
30
+ def select_first(self, selector: str | None, element: Node | None = None) -> Node | None:
31
+ """CSS selector ile ilk eşleşen elementi döndür."""
32
+ if not selector:
33
+ return element
34
+ return self._root(element).css_first(selector)
35
+
36
+ def select_text(self, selector: str | None = None, element: Node | None = None, strip: bool = True) -> str | None:
37
+ """CSS selector ile element bul ve text içeriğini döndür."""
38
+ el = self.select_first(selector, element)
39
+ if not el:
40
+ return None
41
+ val = el.text(strip=strip)
42
+ return val or None
43
+
44
+ def select_texts(self, selector: str, element: Node | None = None, strip: bool = True) -> list[str]:
45
+ """CSS selector ile tüm eşleşen elementlerin text içeriklerini döndür."""
46
+ out: list[str] = []
47
+ for el in self.select(selector, element):
48
+ txt = el.text(strip=strip)
49
+ if txt:
50
+ out.append(txt)
51
+ return out
52
+
53
+ def select_attr(self, selector: str | None, attr: str, element: Node | None = None) -> str | None:
54
+ """CSS selector ile element bul ve attribute değerini döndür."""
55
+ el = self.select_first(selector, element)
56
+ return el.attrs.get(attr) if el else None
57
+
58
+ def select_attrs(self, selector: str, attr: str, element: Node | None = None) -> list[str]:
59
+ """CSS selector ile tüm eşleşen elementlerin attribute değerlerini döndür."""
60
+ out: list[str] = []
61
+ for el in self.select(selector, element):
62
+ val = el.attrs.get(attr)
63
+ if val:
64
+ out.append(val)
65
+ return out
66
+
67
+ def select_poster(self, selector: str = "img", element: Node | None = None) -> str | None:
68
+ """Poster URL'sini çıkar. Önce data-src, sonra src dener."""
69
+ el = self.select_first(selector, element)
70
+ if not el:
71
+ return None
72
+ return el.attrs.get("data-src") or el.attrs.get("src")
73
+
74
+ def select_direct_text(self, selector: str, element: Node | None = None, strip: bool = True) -> str | None:
75
+ """
76
+ Elementin yalnızca "kendi" düz metnini döndürür (child elementlerin text'ini katmadan).
77
+ Selectolax sürüm farklarına göre deep=False dene, yoksa node sibling-walk ile fallback yapar.
78
+ """
79
+ el = self.select_first(selector, element)
80
+ if not el:
81
+ return None
82
+
83
+ # 1) Bazı sürümlerde var: sadece direct text
84
+ try:
85
+ val = el.text(strip=strip, deep=False) # type: ignore[call-arg]
86
+ return val or None
87
+ except TypeError:
88
+ pass # deep parametresi yok, fallback'e geç
89
+
90
+ # 2) Fallback: direct children'ı el.child + next ile dolaş
91
+ parts: list[str] = []
92
+ ch = el.child
93
+ while ch is not None:
94
+ if ch.tag == "-text":
95
+ t = ch.text(strip=strip)
96
+ if t:
97
+ parts.append(t)
98
+ elif ch.tag == "br":
99
+ parts.append("\n")
100
+ ch = ch.next
101
+
102
+ out = "".join(parts).strip()
103
+ return out or None
104
+
105
+ # ========================
106
+ # META (LABEL -> VALUE) İŞLEMLERİ
107
+ # ========================
108
+
109
+ def meta_value(self, label: str, container_selector: str | None = None, strip: bool = True) -> str | None:
110
+ """
111
+ Herhangi bir container içinde: LABEL metnini içeren bir elementten SONRA gelen metni döndürür.
112
+ label örn: "Oyuncular", "Yapım Yılı", "IMDB"
113
+ """
114
+ needle = label.casefold()
115
+
116
+ # Belirli bir container varsa içinde ara, yoksa tüm dökümanda
117
+ targets = self.select(container_selector) if container_selector else [self.parser.body]
118
+
119
+ for root in targets:
120
+ if not root: continue
121
+
122
+ # Kalın/vurgulu elementlerde (span, strong, b, label, dt) label'ı ara
123
+ for label_el in self.select("span, strong, b, label, dt", root):
124
+ txt = (label_el.text(strip=True) or "").casefold()
125
+ if needle not in txt:
126
+ continue
127
+
128
+ # 1) Elementin kendi içindeki text'te LABEL: VALUE formatı olabilir
129
+ # "Oyuncular: Brad Pitt" gibi. LABEL: sonrasını al.
130
+ full_txt = label_el.text(strip=strip)
131
+ if ":" in full_txt and needle in full_txt.split(":")[0].casefold():
132
+ val = full_txt.split(":", 1)[1].strip()
133
+ if val: return val
134
+
135
+ # 2) Label sonrası gelen ilk text node'u veya element'i al
136
+ curr = label_el.next
137
+ while curr:
138
+ if curr.tag == "-text":
139
+ val = curr.text(strip=strip).strip(" :")
140
+ if val: return val
141
+ elif curr.tag != "br":
142
+ val = curr.text(strip=strip).strip(" :")
143
+ if val: return val
144
+ else: # <br> gördüysek satır bitmiştir
145
+ break
146
+ curr = curr.next
147
+
148
+ return None
149
+
150
+ def meta_list(self, label: str, container_selector: str | None = None, sep: str = ",") -> list[str]:
151
+ """meta_value(...) çıktısını veya label'ın ebeveynindeki linkleri listeye döndürür."""
152
+ needle = label.casefold()
153
+ targets = self.select(container_selector) if container_selector else [self.parser.body]
154
+
155
+ for root in targets:
156
+ if not root: continue
157
+ for label_el in self.select("span, strong, b, label, dt", root):
158
+ if needle in (label_el.text(strip=True) or "").casefold():
159
+ # Eğer elementin ebeveyninde linkler varsa (Kutucuklu yapı), onları al
160
+ links = self.select_texts("a", label_el.parent)
161
+ if links: return links
162
+
163
+ # Yoksa düz metin olarak meta_value mantığıyla al
164
+ raw = self.meta_value(label, container_selector=container_selector)
165
+ if not raw: return []
166
+ return [x.strip() for x in raw.split(sep) if x.strip()]
167
+
168
+ return []
169
+
170
+ # ========================
171
+ # REGEX İŞLEMLERİ
172
+ # ========================
173
+
174
+ def _regex_source(self, target: str | int | None) -> str:
175
+ """Regex için kaynak metni döndürür."""
176
+ return target if isinstance(target, str) else self.html
177
+
178
+ def _regex_flags(self, target: str | int | None, flags: int) -> int:
179
+ """Regex flags değerini döndürür."""
180
+ return target if isinstance(target, int) else flags
181
+
182
+ def regex_first(self, pattern: str, target: str | int | None = None, flags: int = 0, group: int | None = 1) -> str | tuple | None:
183
+ """Regex ile arama yap, istenen grubu döndür (group=None ise tüm grupları tuple olarak döndür)."""
184
+ match = re.search(pattern, self._regex_source(target), self._regex_flags(target, flags))
185
+ if not match:
186
+ return None
187
+
188
+ if group is None:
189
+ return match.groups()
190
+
191
+ last_idx = match.lastindex or 0
192
+ return match.group(group) if last_idx >= group else match.group(0)
193
+
194
+ def regex_all(self, pattern: str, target: str | int | None = None, flags: int = 0) -> list[str]:
195
+ """Regex ile tüm eşleşmeleri döndür."""
196
+ return re.findall(pattern, self._regex_source(target), self._regex_flags(target, flags))
197
+
198
+ def regex_replace(self, pattern: str, repl: str, target: str | int | None = None, flags: int = 0) -> str:
199
+ """Regex ile replace yap."""
200
+ return re.sub(pattern, repl, self._regex_source(target), flags=self._regex_flags(target, flags))
201
+
202
+ # ========================
203
+ # ÖZEL AYIKLAYICILAR
204
+ # ========================
205
+
206
+ @staticmethod
207
+ def extract_season_episode(text: str) -> tuple[int | None, int | None]:
208
+ """Metin içinden sezon ve bölüm numarasını çıkar."""
209
+ if m := re.search(r"[Ss](\d+)[Ee](\d+)", text):
210
+ return int(m.group(1)), int(m.group(2))
211
+
212
+ s = re.search(r"(\d+)\.\s*[Ss]ezon|[Ss]ezon[- ]?(\d+)|-(\d+)-sezon|S(\d+)|(\d+)\.[Ss]", text, re.I)
213
+ e = re.search(r"(\d+)\.\s*[Bb][öo]l[üu]m|[Bb][öo]l[üu]m[- ]?(\d+)|-(\d+)-bolum|[Ee](\d+)", text, re.I)
214
+
215
+ s_val = next((int(g) for g in s.groups() if g), None) if s else None
216
+ e_val = next((int(g) for g in e.groups() if g), None) if e else None
217
+
218
+ return s_val, e_val
219
+
220
+ def extract_year(self, *selectors: str, pattern: str = r"(\d{4})") -> int | None:
221
+ """Birden fazla selector veya regex ile yıl bilgisini çıkar."""
222
+ for selector in selectors:
223
+ if text := self.select_text(selector):
224
+ if m := re.search(r"(\d{4})", text):
225
+ return int(m.group(1))
226
+
227
+ val = self.regex_first(pattern)
228
+ return int(val) if val and val.isdigit() else None
@@ -101,9 +101,10 @@ class PluginBase(ABC):
101
101
  return ""
102
102
 
103
103
  if url.startswith("http") or url.startswith("{\""):
104
- return url
104
+ return url.replace("\\", "")
105
105
 
106
- return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
106
+ url = f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
107
+ return url.replace("\\", "")
107
108
 
108
109
  async def extract(self, url: str, referer: str = None, prefix: str | None = None) -> ExtractResult | None:
109
110
  """
@@ -0,0 +1,52 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
4
+ from Kekik.Sifreleme import Packer, StreamDecoder
5
+ import json, contextlib
6
+
7
+ class CloseLoad(ExtractorBase):
8
+ name = "CloseLoad"
9
+ main_url = "https://closeload.filmmakinesi.to"
10
+
11
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
12
+ self.httpx.headers.update({
13
+ "Referer" : referer or self.main_url,
14
+ "Origin" : self.main_url
15
+ })
16
+
17
+ resp = await self.httpx.get(url)
18
+ sel = HTMLHelper(resp.text)
19
+
20
+ # 1. JSON-LD'den Dene
21
+ m3u8_url = None
22
+ for script in sel.select("script[type='application/ld+json']"):
23
+ with contextlib.suppress(Exception):
24
+ data = json.loads(script.text(strip=True))
25
+ if content_url := data.get("contentUrl"):
26
+ if content_url.startswith("http"):
27
+ m3u8_url = content_url
28
+ break
29
+
30
+ # 2. Packed Script Fallback
31
+ if not m3u8_url:
32
+ if packed := sel.regex_first(r"(eval\(function\(p,a,c,k,e,d\).+?)\s*</script>"):
33
+ m3u8_url = StreamDecoder.extract_stream_url(Packer.unpack(packed))
34
+
35
+ if not m3u8_url:
36
+ raise ValueError(f"CloseLoad: Video URL bulunamadı. {url}")
37
+
38
+ subtitles = []
39
+ for track in sel.select("track"):
40
+ src = track.attrs.get("src")
41
+ if src:
42
+ subtitles.append(Subtitle(
43
+ name = track.attrs.get("label") or track.attrs.get("srclang") or "Altyazı",
44
+ url = self.fix_url(src)
45
+ ))
46
+
47
+ return ExtractResult(
48
+ name = self.name,
49
+ url = m3u8_url,
50
+ referer = self.main_url,
51
+ subtitles = subtitles
52
+ )
@@ -0,0 +1,54 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
4
+
5
+ class ContentX(ExtractorBase):
6
+ name = "ContentX"
7
+ main_url = "https://contentx.me"
8
+
9
+ # Birden fazla domain destekle
10
+ supported_domains = [
11
+ "contentx.me", "four.contentx.me",
12
+ "dplayer82.site", "sn.dplayer82.site", "four.dplayer82.site", "org.dplayer82.site",
13
+ "dplayer74.site", "sn.dplayer74.site",
14
+ "hotlinger.com", "sn.hotlinger.com",
15
+ "playru.net", "four.playru.net",
16
+ "pichive.online", "four.pichive.online", "pichive.me", "four.pichive.me"
17
+ ]
18
+
19
+ def can_handle_url(self, url: str) -> bool:
20
+ return any(domain in url for domain in self.supported_domains)
21
+
22
+ async def extract(self, url: str, referer: str = None) -> list[ExtractResult] | ExtractResult:
23
+ ref = referer or self.get_base_url(url)
24
+ self.httpx.headers.update({"Referer": ref})
25
+
26
+ resp = await self.httpx.get(url)
27
+ sel = HTMLHelper(resp.text)
28
+
29
+ v_id = sel.regex_first(r"window\.openPlayer\('([^']+)'")
30
+ if not v_id:
31
+ raise ValueError(f"ContentX: ID bulunamadı. {url}")
32
+
33
+ subtitles = []
34
+ for s_url, s_lang in sel.regex_all(r'"file":"([^\"]+)","label":"([^\"]+)"'):
35
+ decoded_lang = s_lang.encode().decode('unicode_escape')
36
+ subtitles.append(Subtitle(name=decoded_lang, url=self.fix_url(s_url.replace("\\", ""))))
37
+
38
+ results = []
39
+ # Base m3u8
40
+ vid_resp = await self.httpx.get(f"{self.get_base_url(url)}/source2.php?v={v_id}", headers={"Referer": url})
41
+ if m3u8_link := HTMLHelper(vid_resp.text).regex_first(r'file":"([^\"]+)"'):
42
+ m3u8_link = m3u8_link.replace("\\", "").replace("/m.php", "/master.m3u8")
43
+ results.append(ExtractResult(name=self.name, url=m3u8_link, referer=url, subtitles=subtitles))
44
+
45
+ # Dublaj Kontrolü
46
+ if dub_id := sel.regex_first(r'["\']([^"\']+)["\'],["\']Türkçe["\']'):
47
+ dub_resp = await self.httpx.get(f"{self.get_base_url(url)}/source2.php?v={dub_id}", headers={"Referer": url})
48
+ if dub_link := HTMLHelper(dub_resp.text).regex_first(r'file":"([^\"]+)"'):
49
+ results.append(ExtractResult(name=f"{self.name} Türkçe Dublaj", url=dub_link.replace("\\", ""), referer=url))
50
+
51
+ if not results:
52
+ raise ValueError(f"ContentX: Video linki bulunamadı. {url}")
53
+
54
+ return results[0] if len(results) == 1 else results
@@ -0,0 +1,42 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, Subtitle, HTMLHelper
4
+ from Kekik.Sifreleme import AESManager
5
+ import json, contextlib
6
+
7
+ class DonilasPlay(ExtractorBase):
8
+ name = "DonilasPlay"
9
+ main_url = "https://donilasplay.com"
10
+
11
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
12
+ self.httpx.headers.update({"Referer": referer or url})
13
+
14
+ resp = await self.httpx.get(url)
15
+ sel = HTMLHelper(resp.text)
16
+
17
+ m3u8_url = None
18
+ subtitles = []
19
+
20
+ # 1. bePlayer (AES)
21
+ if be_match := sel.regex_first(r"bePlayer\('([^']+)',\s*'(\{[^}]+\})'\);", group=None):
22
+ pass_val, data_val = be_match
23
+ with contextlib.suppress(Exception):
24
+ data = json.loads(AESManager.decrypt(data_val, pass_val))
25
+ m3u8_url = data.get("video_location")
26
+ for sub in data.get("strSubtitles", []):
27
+ if "Forced" not in sub.get("label", ""):
28
+ subtitles.append(Subtitle(name=sub.get("label"), url=self.fix_url(sub.get("file"))))
29
+
30
+ # 2. Fallback
31
+ if not m3u8_url:
32
+ m3u8_url = sel.regex_first(r'file:"([^"]+)"')
33
+ if tracks_match := sel.regex_first(r'tracks:\[([^\]]+)'):
34
+ with contextlib.suppress(Exception):
35
+ for track in json.loads(f"[{tracks_match}]"):
36
+ if "Forced" not in track.get("label", ""):
37
+ subtitles.append(Subtitle(name=track.get("label"), url=self.fix_url(track.get("file"))))
38
+
39
+ if not m3u8_url:
40
+ raise ValueError(f"DonilasPlay: Video linki bulunamadı. {url}")
41
+
42
+ return ExtractResult(name=self.name, url=m3u8_url, referer=url, subtitles=subtitles)
@@ -0,0 +1,24 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
4
+
5
+ class DzenRu(ExtractorBase):
6
+ name = "DzenRu"
7
+ main_url = "https://dzen.ru"
8
+
9
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
10
+ video_key = url.split("/")[-1]
11
+ v_url = f"{self.main_url}/embed/{video_key}"
12
+
13
+ if referer:
14
+ self.httpx.headers.update({"Referer": referer})
15
+
16
+ resp = await self.httpx.get(v_url)
17
+ sel = HTMLHelper(resp.text)
18
+
19
+ # Benzersiz okcdn.ru linklerini bul ve en yüksek kaliteyi (genelde sonuncu) seç
20
+ links = sel.regex_all(r'https://vd\d+\.okcdn\.ru/\?[^"\'\\\s]+')
21
+ if not links:
22
+ raise ValueError(f"DzenRu: Video linki bulunamadı. {url}")
23
+
24
+ return ExtractResult(name=self.name, url=list(set(links))[-1], referer=self.main_url)
@@ -0,0 +1,35 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
4
+ from urllib.parse import urlparse, parse_qs
5
+
6
+ class ExPlay(ExtractorBase):
7
+ name = "ExPlay"
8
+ main_url = "https://explay.store"
9
+
10
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
11
+ self.httpx.headers.update({"Referer": referer or url})
12
+
13
+ # Clean URL from partKey for initial request
14
+ clean_url = url.split("?")[0]
15
+ resp = await self.httpx.get(clean_url)
16
+ sel = HTMLHelper(resp.text)
17
+
18
+ v_url = sel.regex_first(r'videoUrl":"([^",]+)"')
19
+ v_srv = sel.regex_first(r'videoServer":"([^",]+)"')
20
+ if not v_url or not v_srv:
21
+ raise ValueError(f"ExPlay: Video url/server bulunamadı. {url}")
22
+
23
+ params = parse_qs(urlparse(url).query)
24
+ part_key = params.get("partKey", [""])[0]
25
+
26
+ suffix = part_key or "Bilinmiyor"
27
+ if not part_key:
28
+ title = sel.regex_first(r'title":"([^",]+)"')
29
+ if title: suffix = title.split(".")[-1]
30
+
31
+ return ExtractResult(
32
+ name = f"{self.name} - {suffix}",
33
+ url = f"{self.main_url}{v_url.replace('\\', '')}?s={v_srv}",
34
+ referer = clean_url
35
+ )
@@ -0,0 +1,63 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
4
+ from Kekik.Sifreleme import Packer
5
+
6
+ class Filemoon(ExtractorBase):
7
+ name = "Filemoon"
8
+ main_url = "https://filemoon.to"
9
+
10
+ # Filemoon'un farklı domainlerini destekle
11
+ supported_domains = [
12
+ "filemoon.to",
13
+ "filemoon.in",
14
+ "filemoon.sx",
15
+ "filemoon.nl",
16
+ "filemoon.com"
17
+ ]
18
+
19
+ def can_handle_url(self, url: str) -> bool:
20
+ return any(domain in url for domain in self.supported_domains)
21
+
22
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
23
+ default_headers = {
24
+ "Referer" : url,
25
+ "Sec-Fetch-Dest" : "iframe",
26
+ "Sec-Fetch-Mode" : "navigate",
27
+ "Sec-Fetch-Site" : "cross-site",
28
+ "User-Agent" : "Mozilla/5.0 (X11; Linux x86_64; rv:137.0) Gecko/20100101 Firefox/137.0"
29
+ }
30
+ self.httpx.headers.update(default_headers)
31
+
32
+ # İlk sayfayı al
33
+ istek = await self.httpx.get(url)
34
+ secici = HTMLHelper(istek.text)
35
+
36
+ # Eğer iframe varsa, iframe'e git
37
+ iframe_src = secici.select_attr("iframe", "src")
38
+ m3u8_url = None
39
+
40
+ if iframe_src:
41
+ url = self.fix_url(iframe_src)
42
+ istek = await self.httpx.get(url)
43
+ secici = HTMLHelper(istek.text)
44
+
45
+ # script p,a,c,k,e,d içinde ara
46
+ script_data = secici.regex_first(r"(eval\(function\(p,a,c,k,e,d\).+?)\s*</script>")
47
+ if script_data:
48
+ unpacked = Packer.unpack(script_data)
49
+ m3u8_url = HTMLHelper(unpacked).regex_first(r'sources:\[\{file:"(.*?)"')
50
+
51
+ if not m3u8_url:
52
+ # Fallback
53
+ m3u8_url = secici.regex_first(r'sources:\s*\[\s*\{\s*file:\s*"([^"]+)"') or secici.regex_first(r'file:\s*"([^\"]*?\.m3u8[^"]*)"')
54
+
55
+ if not m3u8_url:
56
+ raise ValueError(f"Filemoon: Video URL bulunamadı. {url}")
57
+
58
+ return ExtractResult(
59
+ name = self.name,
60
+ url = self.fix_url(m3u8_url),
61
+ referer = f"{self.get_base_url(url)}/",
62
+ user_agent = default_headers["User-Agent"]
63
+ )
@@ -0,0 +1,30 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
4
+ from Kekik.Sifreleme import AESManager
5
+ import contextlib, json
6
+
7
+ class HDMomPlayer(ExtractorBase):
8
+ name = "HDMomPlayer"
9
+ main_url = "https://hdmomplayer.com"
10
+
11
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
12
+ self.httpx.headers.update({"Referer": referer or url})
13
+
14
+ resp = await self.httpx.get(url)
15
+ sel = HTMLHelper(resp.text)
16
+
17
+ m3u8_url = None
18
+ if match := sel.regex_first(r"bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);", group=None):
19
+ pass_val, data_val = match
20
+ with contextlib.suppress(Exception):
21
+ decrypted = AESManager.decrypt(data_val, pass_val)
22
+ m3u8_url = HTMLHelper(decrypted).regex_first(r'video_location":"([^"]+)"')
23
+
24
+ if not m3u8_url:
25
+ m3u8_url = sel.regex_first(r'file:"([^"]+)"')
26
+
27
+ if not m3u8_url:
28
+ raise ValueError(f"HDMomPlayer: Video linki bulunamadı. {url}")
29
+
30
+ return ExtractResult(name=self.name, url=self.fix_url(m3u8_url), referer=url)
@@ -0,0 +1,23 @@
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: str, referer: str = None) -> ExtractResult:
10
+ ref = referer or self.main_url
11
+ v_id = url.split("video/")[-1] if "video/" in url else url.split("?data=")[-1]
12
+
13
+ resp = await self.httpx.post(
14
+ f"{self.main_url}/player/index.php?data={v_id}&do=getVideo",
15
+ data = {"hash": v_id, "r": ref},
16
+ headers = {"Referer": ref, "X-Requested-With": "XMLHttpRequest"}
17
+ )
18
+
19
+ m3u8_url = resp.json().get("securedLink")
20
+ if not m3u8_url:
21
+ raise ValueError(f"HDPlayerSystem: Video URL bulunamadı. {url}")
22
+
23
+ return ExtractResult(name=self.name, url=m3u8_url, referer=url)
@@ -0,0 +1,27 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
4
+ from Kekik.Sifreleme import AESManager
5
+ import contextlib
6
+
7
+ class HotStream(ExtractorBase):
8
+ name = "HotStream"
9
+ main_url = "https://hotstream.club"
10
+
11
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
12
+ self.httpx.headers.update({"Referer": referer or url})
13
+
14
+ resp = await self.httpx.get(url)
15
+ sel = HTMLHelper(resp.text)
16
+
17
+ m3u8_url = None
18
+ if match := sel.regex_first(r"bePlayer\('([^']+)',\s*'(\{[^']+\})'\)", group=None):
19
+ pass_val, data_val = match
20
+ with contextlib.suppress(Exception):
21
+ decrypted = AESManager.decrypt(data_val, pass_val)
22
+ m3u8_url = HTMLHelper(decrypted).regex_first(r'"video_location":"([^"]+)"')
23
+
24
+ if not m3u8_url:
25
+ raise ValueError(f"HotStream: Video linki bulunamadı. {url}")
26
+
27
+ return ExtractResult(name=self.name, url=self.fix_url(m3u8_url), referer=url)
@@ -0,0 +1,19 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult
4
+
5
+ class JFVid(ExtractorBase):
6
+ name = "JFVid"
7
+ main_url = "https://jfvid.com"
8
+
9
+ # Birden fazla domain destekle
10
+ supported_domains = ["jfvid.com"]
11
+
12
+ def can_handle_url(self, url: str) -> bool:
13
+ return any(domain in url for domain in self.supported_domains)
14
+
15
+ async def extract(self, url: str, referer: str = None) -> ExtractResult:
16
+ base_url = self.get_base_url(url)
17
+ v_id = url.split("/play/")[-1] if "/play/" in url else url.split("/stream/")[-1]
18
+
19
+ return ExtractResult(name=self.name, url=f"{base_url}/stream/{v_id}", referer=referer or base_url)
@@ -0,0 +1,32 @@
1
+ # Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ from KekikStream.Core import ExtractorBase, ExtractResult, HTMLHelper
4
+ import contextlib
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
+ resp = await self.httpx.get(url)
12
+ sel = HTMLHelper(resp.text)
13
+
14
+ m3u8_url = None
15
+ final_ref = self.main_url
16
+
17
+ if "id=" in url:
18
+ v_id = url.split("id=")[-1]
19
+ with contextlib.suppress(Exception):
20
+ api_resp = await self.httpx.get(f"{self.main_url}/apollo/get_video.php?id={v_id}", headers={"Referer": url})
21
+ data = api_resp.json()
22
+ if data.get("success"):
23
+ m3u8_url = data.get("masterUrl")
24
+ final_ref = data.get("referrerUrl") or final_ref
25
+
26
+ if not m3u8_url:
27
+ m3u8_url = sel.regex_first(r"(?i)file: '([^']*)'")
28
+
29
+ if not m3u8_url:
30
+ raise ValueError(f"JetTv: Video URL bulunamadı. {url}")
31
+
32
+ return ExtractResult(name=self.name, url=m3u8_url, referer=final_ref)