KekikStream 2.4.0__tar.gz → 2.4.8__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 (139) hide show
  1. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/Extractor/ExtractorBase.py +3 -2
  2. kekikstream-2.4.8/KekikStream/Core/HTMLHelper.py +205 -0
  3. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/Plugin/PluginBase.py +20 -8
  4. kekikstream-2.4.8/KekikStream/Extractors/CloseLoad.py +52 -0
  5. kekikstream-2.4.8/KekikStream/Extractors/ContentX.py +54 -0
  6. kekikstream-2.4.8/KekikStream/Extractors/DonilasPlay.py +42 -0
  7. kekikstream-2.4.8/KekikStream/Extractors/DzenRu.py +24 -0
  8. kekikstream-2.4.8/KekikStream/Extractors/ExPlay.py +35 -0
  9. kekikstream-2.4.8/KekikStream/Extractors/Filemoon.py +63 -0
  10. kekikstream-2.4.8/KekikStream/Extractors/HDMomPlayer.py +30 -0
  11. kekikstream-2.4.8/KekikStream/Extractors/HDPlayerSystem.py +23 -0
  12. kekikstream-2.4.8/KekikStream/Extractors/HotStream.py +27 -0
  13. kekikstream-2.4.8/KekikStream/Extractors/JFVid.py +19 -0
  14. kekikstream-2.4.8/KekikStream/Extractors/JetTv.py +32 -0
  15. kekikstream-2.4.8/KekikStream/Extractors/MailRu.py +20 -0
  16. kekikstream-2.4.8/KekikStream/Extractors/MixPlayHD.py +28 -0
  17. kekikstream-2.4.8/KekikStream/Extractors/MixTiger.py +34 -0
  18. kekikstream-2.4.8/KekikStream/Extractors/MolyStream.py +38 -0
  19. kekikstream-2.4.8/KekikStream/Extractors/Odnoklassniki.py +53 -0
  20. kekikstream-2.4.8/KekikStream/Extractors/PeaceMakerst.py +36 -0
  21. kekikstream-2.4.8/KekikStream/Extractors/PixelDrain.py +20 -0
  22. kekikstream-2.4.8/KekikStream/Extractors/PlayerFilmIzle.py +46 -0
  23. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Extractors/RapidVid.py +21 -35
  24. kekikstream-2.4.8/KekikStream/Extractors/SetPlay.py +41 -0
  25. kekikstream-2.4.8/KekikStream/Extractors/SibNet.py +17 -0
  26. kekikstream-2.4.8/KekikStream/Extractors/Sobreatsesuyp.py +37 -0
  27. kekikstream-2.4.8/KekikStream/Extractors/TRsTX.py +37 -0
  28. kekikstream-2.4.8/KekikStream/Extractors/TurboImgz.py +17 -0
  29. kekikstream-2.4.8/KekikStream/Extractors/VCTPlay.py +23 -0
  30. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Extractors/VidHide.py +10 -31
  31. kekikstream-2.4.8/KekikStream/Extractors/VidMoly.py +98 -0
  32. kekikstream-2.4.8/KekikStream/Extractors/VidMoxy.py +37 -0
  33. kekikstream-2.4.8/KekikStream/Extractors/VidPapi.py +57 -0
  34. kekikstream-2.4.8/KekikStream/Extractors/VideoSeyred.py +32 -0
  35. kekikstream-2.4.8/KekikStream/Extractors/Videostr.py +58 -0
  36. kekikstream-2.4.8/KekikStream/Extractors/Vidoza.py +18 -0
  37. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Extractors/YTDLP.py +2 -2
  38. kekikstream-2.4.8/KekikStream/Extractors/YildizKisaFilm.py +23 -0
  39. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/BelgeselX.py +97 -77
  40. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/DiziBox.py +28 -45
  41. kekikstream-2.4.8/KekikStream/Plugins/DiziMom.py +179 -0
  42. kekikstream-2.4.8/KekikStream/Plugins/DiziPal.py +194 -0
  43. kekikstream-2.4.8/KekikStream/Plugins/DiziYou.py +147 -0
  44. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/Dizilla.py +40 -61
  45. kekikstream-2.4.8/KekikStream/Plugins/FilmBip.py +201 -0
  46. kekikstream-2.4.8/KekikStream/Plugins/FilmEkseni.py +162 -0
  47. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/FilmMakinesi.py +72 -73
  48. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/FilmModu.py +25 -35
  49. kekikstream-2.4.8/KekikStream/Plugins/Filmatek.py +184 -0
  50. kekikstream-2.4.8/KekikStream/Plugins/FilmciBaba.py +155 -0
  51. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/FullHDFilmizlesene.py +16 -37
  52. kekikstream-2.4.8/KekikStream/Plugins/HDFilm.py +243 -0
  53. kekikstream-2.4.8/KekikStream/Plugins/HDFilmCehennemi.py +334 -0
  54. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/JetFilmizle.py +20 -21
  55. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/KultFilmler.py +18 -52
  56. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/RecTV.py +47 -85
  57. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/RoketDizi.py +5 -5
  58. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/SelcukFlix.py +30 -48
  59. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/SetFilmIzle.py +30 -86
  60. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/SezonlukDizi.py +29 -61
  61. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/Sinefy.py +39 -102
  62. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/SinemaCX.py +14 -20
  63. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/Sinezy.py +15 -16
  64. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/SuperFilmGeldi.py +9 -14
  65. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/UgurFilm.py +14 -14
  66. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/Watch32.py +68 -78
  67. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/YabanciDizi.py +35 -89
  68. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream.egg-info/PKG-INFO +1 -1
  69. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream.egg-info/SOURCES.txt +4 -2
  70. {kekikstream-2.4.0 → kekikstream-2.4.8}/PKG-INFO +1 -1
  71. {kekikstream-2.4.0 → kekikstream-2.4.8}/setup.py +1 -1
  72. kekikstream-2.4.0/KekikStream/Core/HTMLHelper.py +0 -134
  73. kekikstream-2.4.0/KekikStream/Extractors/CloseLoad.py +0 -76
  74. kekikstream-2.4.0/KekikStream/Extractors/ContentX.py +0 -99
  75. kekikstream-2.4.0/KekikStream/Extractors/DonilasPlay.py +0 -86
  76. kekikstream-2.4.0/KekikStream/Extractors/DzenRu.py +0 -38
  77. kekikstream-2.4.0/KekikStream/Extractors/ExPlay.py +0 -53
  78. kekikstream-2.4.0/KekikStream/Extractors/Filemoon.py +0 -89
  79. kekikstream-2.4.0/KekikStream/Extractors/HDMomPlayer.py +0 -62
  80. kekikstream-2.4.0/KekikStream/Extractors/HDPlayerSystem.py +0 -41
  81. kekikstream-2.4.0/KekikStream/Extractors/JFVid.py +0 -40
  82. kekikstream-2.4.0/KekikStream/Extractors/JetTv.py +0 -45
  83. kekikstream-2.4.0/KekikStream/Extractors/MailRu.py +0 -38
  84. kekikstream-2.4.0/KekikStream/Extractors/MixPlayHD.py +0 -41
  85. kekikstream-2.4.0/KekikStream/Extractors/MixTiger.py +0 -57
  86. kekikstream-2.4.0/KekikStream/Extractors/MolyStream.py +0 -42
  87. kekikstream-2.4.0/KekikStream/Extractors/Odnoklassniki.py +0 -117
  88. kekikstream-2.4.0/KekikStream/Extractors/PeaceMakerst.py +0 -63
  89. kekikstream-2.4.0/KekikStream/Extractors/PixelDrain.py +0 -28
  90. kekikstream-2.4.0/KekikStream/Extractors/PlayerFilmIzle.py +0 -65
  91. kekikstream-2.4.0/KekikStream/Extractors/SetPlay.py +0 -66
  92. kekikstream-2.4.0/KekikStream/Extractors/SibNet.py +0 -27
  93. kekikstream-2.4.0/KekikStream/Extractors/Sobreatsesuyp.py +0 -59
  94. kekikstream-2.4.0/KekikStream/Extractors/TRsTX.py +0 -67
  95. kekikstream-2.4.0/KekikStream/Extractors/TurboImgz.py +0 -24
  96. kekikstream-2.4.0/KekikStream/Extractors/VCTPlay.py +0 -41
  97. kekikstream-2.4.0/KekikStream/Extractors/VidMoly.py +0 -132
  98. kekikstream-2.4.0/KekikStream/Extractors/VidMoxy.py +0 -48
  99. kekikstream-2.4.0/KekikStream/Extractors/VidPapi.py +0 -87
  100. kekikstream-2.4.0/KekikStream/Extractors/VideoSeyred.py +0 -53
  101. kekikstream-2.4.0/KekikStream/Extractors/Videostr.py +0 -115
  102. kekikstream-2.4.0/KekikStream/Extractors/YildizKisaFilm.py +0 -41
  103. kekikstream-2.4.0/KekikStream/Plugins/DiziMom.py +0 -248
  104. kekikstream-2.4.0/KekikStream/Plugins/DiziPal.py +0 -260
  105. kekikstream-2.4.0/KekikStream/Plugins/DiziYou.py +0 -243
  106. kekikstream-2.4.0/KekikStream/Plugins/FilmBip.py +0 -150
  107. kekikstream-2.4.0/KekikStream/Plugins/FilmEkseni.py +0 -140
  108. kekikstream-2.4.0/KekikStream/Plugins/Filmatek.py +0 -188
  109. kekikstream-2.4.0/KekikStream/Plugins/Full4kizle.py +0 -190
  110. kekikstream-2.4.0/KekikStream/Plugins/FullHDFilm.py +0 -249
  111. kekikstream-2.4.0/KekikStream/Plugins/HDFilmCehennemi.py +0 -304
  112. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/CLI/__init__.py +0 -0
  113. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/CLI/pypi_kontrol.py +0 -0
  114. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/Extractor/ExtractorLoader.py +0 -0
  115. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/Extractor/ExtractorManager.py +0 -0
  116. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/Extractor/ExtractorModels.py +0 -0
  117. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/Extractor/YTDLPCache.py +0 -0
  118. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/Media/MediaHandler.py +0 -0
  119. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/Media/MediaManager.py +0 -0
  120. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/Plugin/PluginLoader.py +0 -0
  121. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/Plugin/PluginManager.py +0 -0
  122. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/Plugin/PluginModels.py +0 -0
  123. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/UI/UIManager.py +0 -0
  124. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Core/__init__.py +0 -0
  125. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Extractors/SetPrime.py +0 -0
  126. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Extractors/TauVideo.py +0 -0
  127. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Extractors/TurkeyPlayer.py +0 -0
  128. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/Plugins/SineWix.py +0 -0
  129. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/__init__.py +0 -0
  130. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/__main__.py +0 -0
  131. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream/requirements.txt +0 -0
  132. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream.egg-info/dependency_links.txt +0 -0
  133. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream.egg-info/entry_points.txt +0 -0
  134. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream.egg-info/requires.txt +0 -0
  135. {kekikstream-2.4.0 → kekikstream-2.4.8}/KekikStream.egg-info/top_level.txt +0 -0
  136. {kekikstream-2.4.0 → kekikstream-2.4.8}/LICENSE +0 -0
  137. {kekikstream-2.4.0 → kekikstream-2.4.8}/MANIFEST.in +0 -0
  138. {kekikstream-2.4.0 → kekikstream-2.4.8}/README.md +0 -0
  139. {kekikstream-2.4.0 → kekikstream-2.4.8}/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,205 @@
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) -> 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=True)
42
+ return val or None
43
+
44
+ def select_texts(self, selector: str, element: Node | None = None) -> list[str] | None:
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=True)
49
+ if txt:
50
+ out.append(txt)
51
+ return out or None
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) -> str | None:
75
+ """
76
+ Elementin yalnızca "kendi" düz metnini döndürür (child elementlerin text'ini katmadan).
77
+ """
78
+ el = self.select_first(selector, element)
79
+ if not el:
80
+ return None
81
+
82
+ # type: ignore[call-arg]
83
+ val = el.text(strip=True, deep=False)
84
+ return val or None
85
+
86
+ # ========================
87
+ # META (LABEL -> VALUE) İŞLEMLERİ
88
+ # ========================
89
+
90
+ def meta_value(self, label: str, container_selector: str | None = None) -> str | None:
91
+ """
92
+ Herhangi bir container içinde: LABEL metnini içeren bir elementten SONRA gelen metni döndürür.
93
+ label örn: "Oyuncular", "Yapım Yılı", "IMDB"
94
+ """
95
+ needle = label.casefold()
96
+
97
+ # Belirli bir container varsa içinde ara, yoksa tüm dökümanda
98
+ targets = self.select(container_selector) if container_selector else [self.parser.body]
99
+
100
+ for root in targets:
101
+ if not root: continue
102
+
103
+ # Kalın/vurgulu elementlerde (span, strong, b, label, dt) label'ı ara
104
+ for label_el in self.select("span, strong, b, label, dt", root):
105
+ txt = (label_el.text(strip=True) or "").casefold()
106
+ if needle not in txt:
107
+ continue
108
+
109
+ # 1) Elementin kendi içindeki text'te LABEL: VALUE formatı olabilir
110
+ # "Oyuncular: Brad Pitt" gibi. LABEL: sonrasını al.
111
+ full_txt = label_el.text(strip=True)
112
+ if ":" in full_txt and needle in full_txt.split(":")[0].casefold():
113
+ val = full_txt.split(":", 1)[1].strip()
114
+ if val: return val
115
+
116
+ # 2) Label sonrası gelen ilk text node'u veya element'i al
117
+ curr = label_el.next
118
+ while curr:
119
+ if curr.tag == "-text":
120
+ val = curr.text(strip=True).strip(" :")
121
+ if val: return val
122
+ elif curr.tag != "br":
123
+ val = curr.text(strip=True).strip(" :")
124
+ if val: return val
125
+ else: # <br> gördüysek satır bitmiştir
126
+ break
127
+ curr = curr.next
128
+
129
+ return None
130
+
131
+ def meta_list(self, label: str, container_selector: str | None = None, sep: str = ",") -> list[str]:
132
+ """meta_value(...) çıktısını veya label'ın ebeveynindeki linkleri listeye döndürür."""
133
+ needle = label.casefold()
134
+ targets = self.select(container_selector) if container_selector else [self.parser.body]
135
+
136
+ for root in targets:
137
+ if not root: continue
138
+ for label_el in self.select("span, strong, b, label, dt", root):
139
+ if needle in (label_el.text(strip=True) or "").casefold():
140
+ # Eğer elementin ebeveyninde linkler varsa (Kutucuklu yapı), onları al
141
+ links = self.select_texts("a", label_el.parent)
142
+ if links: return links
143
+
144
+ # Yoksa düz metin olarak meta_value mantığıyla al
145
+ raw = self.meta_value(label, container_selector=container_selector)
146
+ if not raw: return []
147
+ return [x.strip() for x in raw.split(sep) if x.strip()]
148
+
149
+ return []
150
+
151
+ # ========================
152
+ # REGEX İŞLEMLERİ
153
+ # ========================
154
+
155
+ def _regex_source(self, target: str | int | None) -> str:
156
+ """Regex için kaynak metni döndürür."""
157
+ return target if isinstance(target, str) else self.html
158
+
159
+ def regex_first(self, pattern: str, target: str | int | None = None, group: int | None = 1) -> str | tuple | None:
160
+ """Regex ile arama yap, istenen grubu döndür (group=None ise tüm grupları tuple olarak döndür)."""
161
+ match = re.search(pattern, self._regex_source(target))
162
+ if not match:
163
+ return None
164
+
165
+ if group is None:
166
+ return match.groups()
167
+
168
+ last_idx = match.lastindex or 0
169
+ return match.group(group) if last_idx >= group else match.group(0)
170
+
171
+ def regex_all(self, pattern: str, target: str | int | None = None) -> list[str] | list[tuple]:
172
+ """Regex ile tüm eşleşmeleri döndür."""
173
+ return re.findall(pattern, self._regex_source(target))
174
+
175
+ def regex_replace(self, pattern: str, repl: str, target: str | int | None = None) -> str:
176
+ """Regex ile replace yap."""
177
+ return re.sub(pattern, repl, self._regex_source(target))
178
+
179
+ # ========================
180
+ # ÖZEL AYIKLAYICILAR
181
+ # ========================
182
+
183
+ @staticmethod
184
+ def extract_season_episode(text: str) -> tuple[int | None, int | None]:
185
+ """Metin içinden sezon ve bölüm numarasını çıkar."""
186
+ if m := re.search(r"[Ss](\d+)[Ee](\d+)", text):
187
+ return int(m.group(1)), int(m.group(2))
188
+
189
+ s = re.search(r"(\d+)\.\s*[Ss]ezon|[Ss]ezon[- ]?(\d+)|-(\d+)-sezon|S(\d+)|(\d+)\.[Ss]", text, re.I)
190
+ e = re.search(r"(\d+)\.\s*[Bb][öo]l[üu]m|[Bb][öo]l[üu]m[- ]?(\d+)|-(\d+)-bolum|[Ee](\d+)", text, re.I)
191
+
192
+ s_val = next((int(g) for g in s.groups() if g), None) if s else None
193
+ e_val = next((int(g) for g in e.groups() if g), None) if e else None
194
+
195
+ return s_val, e_val
196
+
197
+ def extract_year(self, *selectors: str, pattern: str = r"(\d{4})") -> int | None:
198
+ """Birden fazla selector veya regex ile yıl bilgisini çıkar."""
199
+ for selector in selectors:
200
+ if text := self.select_text(selector):
201
+ if m := re.search(r"(\d{4})", text):
202
+ return int(m.group(1))
203
+
204
+ val = self.regex_first(pattern)
205
+ return int(val) if val and val.isdigit() else None
@@ -101,11 +101,18 @@ class PluginBase(ABC):
101
101
  return ""
102
102
 
103
103
  if url.startswith("http") or url.startswith("{\""):
104
- return url
105
-
106
- return f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
107
-
108
- async def extract(self, url: str, referer: str = None, prefix: str | None = None) -> ExtractResult | None:
104
+ return url.replace("\\", "")
105
+
106
+ url = f"https:{url}" if url.startswith("//") else urljoin(self.main_url, url)
107
+ return url.replace("\\", "")
108
+
109
+ async def extract(
110
+ self,
111
+ url: str,
112
+ referer: str = None,
113
+ prefix: str | None = None,
114
+ name_override: str | None = None
115
+ ) -> ExtractResult | list[ExtractResult] | None:
109
116
  """
110
117
  Extractor ile video URL'sini çıkarır.
111
118
 
@@ -113,6 +120,7 @@ class PluginBase(ABC):
113
120
  url: Iframe veya video URL'si
114
121
  referer: Referer header (varsayılan: plugin main_url)
115
122
  prefix: İsmin başına eklenecek opsiyonel etiket (örn: "Türkçe Dublaj")
123
+ name_override: İsmi tamamen değiştirecek opsiyonel etiket (Extractor adını ezer)
116
124
 
117
125
  Returns:
118
126
  ExtractResult: Extractor sonucu (name prefix ile birleştirilmiş) veya None
@@ -130,15 +138,19 @@ class PluginBase(ABC):
130
138
  try:
131
139
  data = await extractor.extract(url, referer=referer)
132
140
 
133
- # Liste ise her bir öğe için prefix ekle
141
+ # Liste ise her bir öğe için prefix/override ekle
134
142
  if isinstance(data, list):
135
143
  for item in data:
136
- if prefix and item.name:
144
+ if name_override:
145
+ item.name = name_override
146
+ elif prefix and item.name:
137
147
  item.name = f"{prefix} | {item.name}"
138
148
  return data
139
149
 
140
150
  # Tekil öğe ise
141
- if prefix and data.name:
151
+ if name_override:
152
+ data.name = name_override
153
+ elif prefix and data.name:
142
154
  data.name = f"{prefix} | {data.name}"
143
155
 
144
156
  return data
@@ -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)