webscout 8.2.7__py3-none-any.whl → 8.2.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (323) hide show
  1. webscout/AIauto.py +1 -1
  2. webscout/AIutel.py +298 -249
  3. webscout/Extra/Act.md +309 -0
  4. webscout/Extra/GitToolkit/__init__.py +10 -0
  5. webscout/Extra/GitToolkit/gitapi/README.md +110 -0
  6. webscout/Extra/GitToolkit/gitapi/__init__.py +12 -0
  7. webscout/Extra/GitToolkit/gitapi/repository.py +195 -0
  8. webscout/Extra/GitToolkit/gitapi/user.py +96 -0
  9. webscout/Extra/GitToolkit/gitapi/utils.py +62 -0
  10. webscout/Extra/YTToolkit/README.md +375 -0
  11. webscout/Extra/YTToolkit/YTdownloader.py +957 -0
  12. webscout/Extra/YTToolkit/__init__.py +3 -0
  13. webscout/Extra/YTToolkit/transcriber.py +476 -0
  14. webscout/Extra/YTToolkit/ytapi/README.md +44 -0
  15. webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
  16. webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
  17. webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
  18. webscout/Extra/YTToolkit/ytapi/extras.py +118 -0
  19. webscout/Extra/YTToolkit/ytapi/https.py +88 -0
  20. webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
  21. webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
  22. webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
  23. webscout/Extra/YTToolkit/ytapi/query.py +40 -0
  24. webscout/Extra/YTToolkit/ytapi/stream.py +63 -0
  25. webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
  26. webscout/Extra/YTToolkit/ytapi/video.py +232 -0
  27. webscout/Extra/__init__.py +7 -0
  28. webscout/Extra/autocoder/__init__.py +9 -0
  29. webscout/Extra/autocoder/autocoder.py +1105 -0
  30. webscout/Extra/autocoder/autocoder_utiles.py +332 -0
  31. webscout/Extra/gguf.md +430 -0
  32. webscout/Extra/gguf.py +684 -0
  33. webscout/Extra/tempmail/README.md +488 -0
  34. webscout/Extra/tempmail/__init__.py +28 -0
  35. webscout/Extra/tempmail/async_utils.py +141 -0
  36. webscout/Extra/tempmail/base.py +161 -0
  37. webscout/Extra/tempmail/cli.py +187 -0
  38. webscout/Extra/tempmail/emailnator.py +84 -0
  39. webscout/Extra/tempmail/mail_tm.py +361 -0
  40. webscout/Extra/tempmail/temp_mail_io.py +292 -0
  41. webscout/Extra/weather.md +281 -0
  42. webscout/Extra/weather.py +194 -0
  43. webscout/Extra/weather_ascii.py +76 -0
  44. webscout/Litlogger/Readme.md +175 -0
  45. webscout/Litlogger/__init__.py +67 -0
  46. webscout/Litlogger/core/__init__.py +6 -0
  47. webscout/Litlogger/core/level.py +23 -0
  48. webscout/Litlogger/core/logger.py +165 -0
  49. webscout/Litlogger/handlers/__init__.py +12 -0
  50. webscout/Litlogger/handlers/console.py +33 -0
  51. webscout/Litlogger/handlers/file.py +143 -0
  52. webscout/Litlogger/handlers/network.py +173 -0
  53. webscout/Litlogger/styles/__init__.py +7 -0
  54. webscout/Litlogger/styles/colors.py +249 -0
  55. webscout/Litlogger/styles/formats.py +458 -0
  56. webscout/Litlogger/styles/text.py +87 -0
  57. webscout/Litlogger/utils/__init__.py +6 -0
  58. webscout/Litlogger/utils/detectors.py +153 -0
  59. webscout/Litlogger/utils/formatters.py +200 -0
  60. webscout/Provider/AI21.py +177 -0
  61. webscout/Provider/AISEARCH/DeepFind.py +254 -0
  62. webscout/Provider/AISEARCH/Perplexity.py +359 -0
  63. webscout/Provider/AISEARCH/README.md +279 -0
  64. webscout/Provider/AISEARCH/__init__.py +9 -0
  65. webscout/Provider/AISEARCH/felo_search.py +228 -0
  66. webscout/Provider/AISEARCH/genspark_search.py +350 -0
  67. webscout/Provider/AISEARCH/hika_search.py +198 -0
  68. webscout/Provider/AISEARCH/iask_search.py +436 -0
  69. webscout/Provider/AISEARCH/monica_search.py +246 -0
  70. webscout/Provider/AISEARCH/scira_search.py +324 -0
  71. webscout/Provider/AISEARCH/webpilotai_search.py +281 -0
  72. webscout/Provider/Aitopia.py +316 -0
  73. webscout/Provider/AllenAI.py +440 -0
  74. webscout/Provider/Andi.py +228 -0
  75. webscout/Provider/Blackboxai.py +673 -0
  76. webscout/Provider/ChatGPTClone.py +237 -0
  77. webscout/Provider/ChatGPTGratis.py +194 -0
  78. webscout/Provider/ChatSandbox.py +342 -0
  79. webscout/Provider/Cloudflare.py +324 -0
  80. webscout/Provider/Cohere.py +208 -0
  81. webscout/Provider/Deepinfra.py +340 -0
  82. webscout/Provider/ExaAI.py +261 -0
  83. webscout/Provider/ExaChat.py +358 -0
  84. webscout/Provider/Flowith.py +217 -0
  85. webscout/Provider/FreeGemini.py +250 -0
  86. webscout/Provider/Gemini.py +169 -0
  87. webscout/Provider/GithubChat.py +370 -0
  88. webscout/Provider/GizAI.py +295 -0
  89. webscout/Provider/Glider.py +225 -0
  90. webscout/Provider/Groq.py +801 -0
  91. webscout/Provider/HF_space/__init__.py +0 -0
  92. webscout/Provider/HF_space/qwen_qwen2.py +206 -0
  93. webscout/Provider/HeckAI.py +285 -0
  94. webscout/Provider/HuggingFaceChat.py +469 -0
  95. webscout/Provider/Hunyuan.py +283 -0
  96. webscout/Provider/Jadve.py +291 -0
  97. webscout/Provider/Koboldai.py +384 -0
  98. webscout/Provider/LambdaChat.py +411 -0
  99. webscout/Provider/Llama3.py +259 -0
  100. webscout/Provider/MCPCore.py +315 -0
  101. webscout/Provider/Marcus.py +198 -0
  102. webscout/Provider/Nemotron.py +218 -0
  103. webscout/Provider/Netwrck.py +270 -0
  104. webscout/Provider/OLLAMA.py +396 -0
  105. webscout/Provider/OPENAI/BLACKBOXAI.py +735 -0
  106. webscout/Provider/OPENAI/Cloudflare.py +378 -0
  107. webscout/Provider/OPENAI/FreeGemini.py +282 -0
  108. webscout/Provider/OPENAI/NEMOTRON.py +244 -0
  109. webscout/Provider/OPENAI/README.md +1253 -0
  110. webscout/Provider/OPENAI/__init__.py +36 -0
  111. webscout/Provider/OPENAI/ai4chat.py +293 -0
  112. webscout/Provider/OPENAI/api.py +810 -0
  113. webscout/Provider/OPENAI/base.py +249 -0
  114. webscout/Provider/OPENAI/c4ai.py +373 -0
  115. webscout/Provider/OPENAI/chatgpt.py +556 -0
  116. webscout/Provider/OPENAI/chatgptclone.py +488 -0
  117. webscout/Provider/OPENAI/chatsandbox.py +172 -0
  118. webscout/Provider/OPENAI/deepinfra.py +319 -0
  119. webscout/Provider/OPENAI/e2b.py +1356 -0
  120. webscout/Provider/OPENAI/exaai.py +411 -0
  121. webscout/Provider/OPENAI/exachat.py +443 -0
  122. webscout/Provider/OPENAI/flowith.py +162 -0
  123. webscout/Provider/OPENAI/freeaichat.py +359 -0
  124. webscout/Provider/OPENAI/glider.py +323 -0
  125. webscout/Provider/OPENAI/groq.py +361 -0
  126. webscout/Provider/OPENAI/heckai.py +307 -0
  127. webscout/Provider/OPENAI/llmchatco.py +335 -0
  128. webscout/Provider/OPENAI/mcpcore.py +383 -0
  129. webscout/Provider/OPENAI/multichat.py +376 -0
  130. webscout/Provider/OPENAI/netwrck.py +356 -0
  131. webscout/Provider/OPENAI/opkfc.py +496 -0
  132. webscout/Provider/OPENAI/scirachat.py +471 -0
  133. webscout/Provider/OPENAI/sonus.py +303 -0
  134. webscout/Provider/OPENAI/standardinput.py +433 -0
  135. webscout/Provider/OPENAI/textpollinations.py +339 -0
  136. webscout/Provider/OPENAI/toolbaz.py +413 -0
  137. webscout/Provider/OPENAI/typefully.py +355 -0
  138. webscout/Provider/OPENAI/typegpt.py +358 -0
  139. webscout/Provider/OPENAI/uncovrAI.py +462 -0
  140. webscout/Provider/OPENAI/utils.py +307 -0
  141. webscout/Provider/OPENAI/venice.py +425 -0
  142. webscout/Provider/OPENAI/wisecat.py +381 -0
  143. webscout/Provider/OPENAI/writecream.py +163 -0
  144. webscout/Provider/OPENAI/x0gpt.py +378 -0
  145. webscout/Provider/OPENAI/yep.py +356 -0
  146. webscout/Provider/OpenGPT.py +209 -0
  147. webscout/Provider/Openai.py +496 -0
  148. webscout/Provider/PI.py +429 -0
  149. webscout/Provider/Perplexitylabs.py +415 -0
  150. webscout/Provider/QwenLM.py +254 -0
  151. webscout/Provider/Reka.py +214 -0
  152. webscout/Provider/StandardInput.py +290 -0
  153. webscout/Provider/TTI/AiForce/README.md +159 -0
  154. webscout/Provider/TTI/AiForce/__init__.py +22 -0
  155. webscout/Provider/TTI/AiForce/async_aiforce.py +224 -0
  156. webscout/Provider/TTI/AiForce/sync_aiforce.py +245 -0
  157. webscout/Provider/TTI/FreeAIPlayground/README.md +99 -0
  158. webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -0
  159. webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +181 -0
  160. webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +180 -0
  161. webscout/Provider/TTI/ImgSys/README.md +174 -0
  162. webscout/Provider/TTI/ImgSys/__init__.py +23 -0
  163. webscout/Provider/TTI/ImgSys/async_imgsys.py +202 -0
  164. webscout/Provider/TTI/ImgSys/sync_imgsys.py +195 -0
  165. webscout/Provider/TTI/MagicStudio/README.md +101 -0
  166. webscout/Provider/TTI/MagicStudio/__init__.py +2 -0
  167. webscout/Provider/TTI/MagicStudio/async_magicstudio.py +111 -0
  168. webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +109 -0
  169. webscout/Provider/TTI/Nexra/README.md +155 -0
  170. webscout/Provider/TTI/Nexra/__init__.py +22 -0
  171. webscout/Provider/TTI/Nexra/async_nexra.py +286 -0
  172. webscout/Provider/TTI/Nexra/sync_nexra.py +258 -0
  173. webscout/Provider/TTI/PollinationsAI/README.md +146 -0
  174. webscout/Provider/TTI/PollinationsAI/__init__.py +23 -0
  175. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +311 -0
  176. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +265 -0
  177. webscout/Provider/TTI/README.md +128 -0
  178. webscout/Provider/TTI/__init__.py +12 -0
  179. webscout/Provider/TTI/aiarta/README.md +134 -0
  180. webscout/Provider/TTI/aiarta/__init__.py +2 -0
  181. webscout/Provider/TTI/aiarta/async_aiarta.py +482 -0
  182. webscout/Provider/TTI/aiarta/sync_aiarta.py +440 -0
  183. webscout/Provider/TTI/artbit/README.md +100 -0
  184. webscout/Provider/TTI/artbit/__init__.py +22 -0
  185. webscout/Provider/TTI/artbit/async_artbit.py +155 -0
  186. webscout/Provider/TTI/artbit/sync_artbit.py +148 -0
  187. webscout/Provider/TTI/fastflux/README.md +129 -0
  188. webscout/Provider/TTI/fastflux/__init__.py +22 -0
  189. webscout/Provider/TTI/fastflux/async_fastflux.py +261 -0
  190. webscout/Provider/TTI/fastflux/sync_fastflux.py +252 -0
  191. webscout/Provider/TTI/huggingface/README.md +114 -0
  192. webscout/Provider/TTI/huggingface/__init__.py +22 -0
  193. webscout/Provider/TTI/huggingface/async_huggingface.py +199 -0
  194. webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -0
  195. webscout/Provider/TTI/piclumen/README.md +161 -0
  196. webscout/Provider/TTI/piclumen/__init__.py +23 -0
  197. webscout/Provider/TTI/piclumen/async_piclumen.py +268 -0
  198. webscout/Provider/TTI/piclumen/sync_piclumen.py +233 -0
  199. webscout/Provider/TTI/pixelmuse/README.md +79 -0
  200. webscout/Provider/TTI/pixelmuse/__init__.py +4 -0
  201. webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +249 -0
  202. webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +182 -0
  203. webscout/Provider/TTI/talkai/README.md +139 -0
  204. webscout/Provider/TTI/talkai/__init__.py +4 -0
  205. webscout/Provider/TTI/talkai/async_talkai.py +229 -0
  206. webscout/Provider/TTI/talkai/sync_talkai.py +207 -0
  207. webscout/Provider/TTS/README.md +192 -0
  208. webscout/Provider/TTS/__init__.py +9 -0
  209. webscout/Provider/TTS/base.py +159 -0
  210. webscout/Provider/TTS/deepgram.py +156 -0
  211. webscout/Provider/TTS/elevenlabs.py +111 -0
  212. webscout/Provider/TTS/gesserit.py +128 -0
  213. webscout/Provider/TTS/murfai.py +113 -0
  214. webscout/Provider/TTS/parler.py +111 -0
  215. webscout/Provider/TTS/speechma.py +580 -0
  216. webscout/Provider/TTS/sthir.py +94 -0
  217. webscout/Provider/TTS/streamElements.py +333 -0
  218. webscout/Provider/TTS/utils.py +280 -0
  219. webscout/Provider/TeachAnything.py +229 -0
  220. webscout/Provider/TextPollinationsAI.py +308 -0
  221. webscout/Provider/TwoAI.py +280 -0
  222. webscout/Provider/TypliAI.py +305 -0
  223. webscout/Provider/UNFINISHED/ChatHub.py +209 -0
  224. webscout/Provider/UNFINISHED/Youchat.py +330 -0
  225. webscout/Provider/UNFINISHED/liner_api_request.py +263 -0
  226. webscout/Provider/UNFINISHED/oivscode.py +351 -0
  227. webscout/Provider/UNFINISHED/test_lmarena.py +119 -0
  228. webscout/Provider/Venice.py +258 -0
  229. webscout/Provider/VercelAI.py +253 -0
  230. webscout/Provider/WiseCat.py +233 -0
  231. webscout/Provider/WrDoChat.py +370 -0
  232. webscout/Provider/Writecream.py +246 -0
  233. webscout/Provider/WritingMate.py +269 -0
  234. webscout/Provider/__init__.py +172 -0
  235. webscout/Provider/ai4chat.py +149 -0
  236. webscout/Provider/akashgpt.py +335 -0
  237. webscout/Provider/asksteve.py +220 -0
  238. webscout/Provider/cerebras.py +290 -0
  239. webscout/Provider/chatglm.py +215 -0
  240. webscout/Provider/cleeai.py +213 -0
  241. webscout/Provider/copilot.py +425 -0
  242. webscout/Provider/elmo.py +283 -0
  243. webscout/Provider/freeaichat.py +285 -0
  244. webscout/Provider/geminiapi.py +208 -0
  245. webscout/Provider/granite.py +235 -0
  246. webscout/Provider/hermes.py +266 -0
  247. webscout/Provider/julius.py +223 -0
  248. webscout/Provider/koala.py +170 -0
  249. webscout/Provider/learnfastai.py +325 -0
  250. webscout/Provider/llama3mitril.py +215 -0
  251. webscout/Provider/llmchat.py +258 -0
  252. webscout/Provider/llmchatco.py +306 -0
  253. webscout/Provider/lmarena.py +198 -0
  254. webscout/Provider/meta.py +801 -0
  255. webscout/Provider/multichat.py +364 -0
  256. webscout/Provider/samurai.py +223 -0
  257. webscout/Provider/scira_chat.py +299 -0
  258. webscout/Provider/scnet.py +243 -0
  259. webscout/Provider/searchchat.py +292 -0
  260. webscout/Provider/sonus.py +258 -0
  261. webscout/Provider/talkai.py +194 -0
  262. webscout/Provider/toolbaz.py +353 -0
  263. webscout/Provider/turboseek.py +266 -0
  264. webscout/Provider/typefully.py +202 -0
  265. webscout/Provider/typegpt.py +289 -0
  266. webscout/Provider/uncovr.py +368 -0
  267. webscout/Provider/x0gpt.py +299 -0
  268. webscout/Provider/yep.py +389 -0
  269. webscout/__init__.py +4 -2
  270. webscout/cli.py +3 -28
  271. webscout/conversation.py +35 -35
  272. webscout/litagent/Readme.md +276 -0
  273. webscout/litagent/__init__.py +29 -0
  274. webscout/litagent/agent.py +455 -0
  275. webscout/litagent/constants.py +60 -0
  276. webscout/litprinter/__init__.py +59 -0
  277. webscout/scout/README.md +402 -0
  278. webscout/scout/__init__.py +8 -0
  279. webscout/scout/core/__init__.py +7 -0
  280. webscout/scout/core/crawler.py +140 -0
  281. webscout/scout/core/scout.py +568 -0
  282. webscout/scout/core/search_result.py +96 -0
  283. webscout/scout/core/text_analyzer.py +63 -0
  284. webscout/scout/core/text_utils.py +277 -0
  285. webscout/scout/core/web_analyzer.py +52 -0
  286. webscout/scout/element.py +460 -0
  287. webscout/scout/parsers/__init__.py +69 -0
  288. webscout/scout/parsers/html5lib_parser.py +172 -0
  289. webscout/scout/parsers/html_parser.py +236 -0
  290. webscout/scout/parsers/lxml_parser.py +178 -0
  291. webscout/scout/utils.py +37 -0
  292. webscout/swiftcli/Readme.md +323 -0
  293. webscout/swiftcli/__init__.py +95 -0
  294. webscout/swiftcli/core/__init__.py +7 -0
  295. webscout/swiftcli/core/cli.py +297 -0
  296. webscout/swiftcli/core/context.py +104 -0
  297. webscout/swiftcli/core/group.py +241 -0
  298. webscout/swiftcli/decorators/__init__.py +28 -0
  299. webscout/swiftcli/decorators/command.py +221 -0
  300. webscout/swiftcli/decorators/options.py +220 -0
  301. webscout/swiftcli/decorators/output.py +252 -0
  302. webscout/swiftcli/exceptions.py +21 -0
  303. webscout/swiftcli/plugins/__init__.py +9 -0
  304. webscout/swiftcli/plugins/base.py +135 -0
  305. webscout/swiftcli/plugins/manager.py +262 -0
  306. webscout/swiftcli/utils/__init__.py +59 -0
  307. webscout/swiftcli/utils/formatting.py +252 -0
  308. webscout/swiftcli/utils/parsing.py +267 -0
  309. webscout/version.py +1 -1
  310. webscout/webscout_search.py +2 -182
  311. webscout/webscout_search_async.py +1 -179
  312. webscout/zeroart/README.md +89 -0
  313. webscout/zeroart/__init__.py +135 -0
  314. webscout/zeroart/base.py +66 -0
  315. webscout/zeroart/effects.py +101 -0
  316. webscout/zeroart/fonts.py +1239 -0
  317. {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/METADATA +115 -60
  318. webscout-8.2.8.dist-info/RECORD +334 -0
  319. {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/WHEEL +1 -1
  320. webscout-8.2.7.dist-info/RECORD +0 -26
  321. {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/entry_points.txt +0 -0
  322. {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/licenses/LICENSE.md +0 -0
  323. {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,40 @@
1
+ from .utils import dup_filter
2
+ from .video import Video
3
+ from .channel import Channel
4
+ from .playlist import Playlist
5
+ from .patterns import _QueryPatterns as Patterns
6
+ from typing import Optional, Dict, Any, List
7
+ from .https import find_videos, find_channels, find_playlists
8
+
9
+
10
+ class Search:
11
+
12
+ @staticmethod
13
+ def video(keywords: str) -> Optional[Video]:
14
+ video_ids = Patterns.video_id.findall(find_videos(keywords))
15
+ return Video(video_ids[0]) if video_ids else None
16
+
17
+ @staticmethod
18
+ def channel(keywords: str) -> Optional[Channel]:
19
+ channel_ids = Patterns.channel_id.findall(find_channels(keywords))
20
+ return Channel(channel_ids[0]) if channel_ids else None
21
+
22
+ @staticmethod
23
+ def playlist(keywords: str) -> Optional[Playlist]:
24
+ playlist_ids = Patterns.playlist_id.findall(find_playlists(keywords))
25
+ return Playlist(playlist_ids[0]) if playlist_ids else None
26
+
27
+ @staticmethod
28
+ def videos(keywords: str, limit: int = 20) -> Optional[List[str]]:
29
+ return dup_filter(Patterns.video_id.findall(find_videos(keywords)), limit)
30
+
31
+ @staticmethod
32
+ def channels(keywords: str, limit: int = 20) -> Optional[List[str]]:
33
+ return dup_filter(Patterns.channel_id.findall(find_channels(keywords)), limit)
34
+
35
+ @staticmethod
36
+ def playlists(keywords: str, limit: int = 20) -> Optional[List[str]]:
37
+ return dup_filter(Patterns.playlist_id.findall(find_playlists(keywords)), limit)
38
+
39
+ if __name__ == "__main__":
40
+ print(Search.videos("java"))
@@ -0,0 +1,63 @@
1
+ import re
2
+ from typing import Dict, Any
3
+
4
+ from .pool import collect
5
+ from .https import video_data
6
+ from .patterns import _VideoPatterns as Patterns
7
+
8
+
9
+ class Video:
10
+
11
+ _HEAD = 'https://www.youtube.com/watch?v='
12
+
13
+ def __init__(self, video_id: str):
14
+ pattern = re.compile('.be/(.*?)$|=(.*?)$|^(\w{11})$') # noqa
15
+ self._matched_id = (
16
+ pattern.search(video_id).group(1)
17
+ or pattern.search(video_id).group(2)
18
+ or pattern.search(video_id).group(3)
19
+ )
20
+ if self._matched_id:
21
+ self._url = self._HEAD + self._matched_id
22
+ self._video_data = video_data(self._matched_id)
23
+ else:
24
+ raise ValueError('invalid video id or url')
25
+
26
+ def __repr__(self):
27
+ return f'<Video {self._url}>'
28
+
29
+ @property
30
+ def metadata(self) -> Dict[str, Any]:
31
+ patterns = [
32
+ Patterns.title,
33
+ Patterns.views,
34
+ Patterns.likes,
35
+ Patterns.duration,
36
+ Patterns.author_id,
37
+ Patterns.upload_date,
38
+ Patterns.thumbnail,
39
+ Patterns.tags,
40
+ Patterns.description,
41
+ Patterns.is_streamed,
42
+ Patterns.is_premiered
43
+ ]
44
+ ext = collect(lambda x: x.findall(self._video_data) or None, patterns)
45
+ data = [i[0] if i else i for i in ext]
46
+ return {
47
+ 'title': data[0],
48
+ 'id': self._matched_id,
49
+ 'views': data[1][:-6] if data[1] else None,
50
+ 'likes': data[2],
51
+ 'streamed': data[9] is not None,
52
+ 'premiered': data[10],
53
+ 'duration': int(data[3]) / 1000 if data[3] else None,
54
+ 'author': data[4],
55
+ 'upload_date': data[5],
56
+ 'url': self._url,
57
+ 'thumbnail': data[6],
58
+ 'tags': data[7].split(',') if data[7] else None,
59
+ 'description': data[8].replace('\\n', '\n') if data[8] else None
60
+ }
61
+
62
+ if __name__ == '__main__':
63
+ print(Video('https://www.youtube.com/watch?v=9bZkp7q19f0').metadata)
@@ -0,0 +1,62 @@
1
+ from urllib.request import Request, urlopen
2
+ from collections import OrderedDict
3
+ from urllib.error import HTTPError
4
+ from .errors import TooManyRequests, InvalidURL, RequestError
5
+ from webscout.litagent import LitAgent
6
+
7
+
8
+ __all__ = ['dup_filter', 'request']
9
+
10
+
11
+ _USER_AGENT_GENERATOR = LitAgent()
12
+
13
+
14
+ def request(url: str, retry_attempts: int = 3) -> str:
15
+ """
16
+ Send a request with a random user agent and built-in retry mechanism.
17
+
18
+ Args:
19
+ url (str): The URL to request
20
+ retry_attempts (int, optional): Number of retry attempts. Defaults to 3.
21
+
22
+ Raises:
23
+ InvalidURL: If the URL cannot be found
24
+ TooManyRequests: If rate-limited
25
+ RequestError: For other request-related errors
26
+
27
+ Returns:
28
+ str: Decoded response content
29
+ """
30
+ for attempt in range(retry_attempts):
31
+ try:
32
+ headers = {
33
+ "User-Agent": _USER_AGENT_GENERATOR.random()
34
+ }
35
+
36
+ req = Request(url, headers=headers)
37
+ response = urlopen(req)
38
+ return response.read().decode('utf-8')
39
+
40
+ except HTTPError as e:
41
+ if e.code == 404:
42
+ raise InvalidURL(f'Cannot find anything with the requested URL: {url}')
43
+ if e.code == 429:
44
+ raise TooManyRequests(f'Rate-limited on attempt {attempt + 1}')
45
+
46
+ if attempt == retry_attempts - 1:
47
+ raise RequestError(f'HTTP Error {e.code}: {e.reason}') from e
48
+
49
+ except Exception as e:
50
+ if attempt == retry_attempts - 1:
51
+ raise RequestError(f'Request failed: {e!r}') from None
52
+
53
+
54
+ def dup_filter(iterable: list, limit: int = None) -> list:
55
+ if not iterable:
56
+ return []
57
+ lim = limit if limit else len(iterable)
58
+ converted = list(OrderedDict.fromkeys(iterable))
59
+ if len(converted) - lim > 0:
60
+ return converted[:-len(converted) + lim]
61
+ else:
62
+ return converted
@@ -0,0 +1,232 @@
1
+ import re
2
+ import json
3
+ from typing import Dict, Any
4
+ from .https import video_data
5
+
6
+
7
+ class Video:
8
+
9
+ _HEAD = 'https://www.youtube.com/watch?v='
10
+
11
+ def __init__(self, video_id: str):
12
+ """
13
+ Represents a YouTube video
14
+
15
+ Parameters
16
+ ----------
17
+ video_id : str
18
+ The id or url of the video
19
+ """
20
+ pattern = re.compile('.be/(.*?)$|=(.*?)$|^(\w{11})$') # noqa
21
+ match = pattern.search(video_id)
22
+
23
+ if not match:
24
+ raise ValueError('Invalid YouTube video ID or URL')
25
+
26
+ self._matched_id = (
27
+ match.group(1)
28
+ or match.group(2)
29
+ or match.group(3)
30
+ )
31
+
32
+ if self._matched_id:
33
+ self._url = self._HEAD + self._matched_id
34
+ self._video_data = video_data(self._matched_id)
35
+ # Extract basic info for fallback
36
+ title_match = re.search('<title>(.*?) - YouTube</title>', self._video_data)
37
+ self.title = title_match.group(1) if title_match else None
38
+ self.id = self._matched_id
39
+ else:
40
+ raise ValueError('Invalid YouTube video ID or URL')
41
+
42
+ def __repr__(self):
43
+ return f'<Video {self._url}>'
44
+
45
+ @property
46
+ def metadata(self) -> Dict[str, Any]:
47
+ """
48
+ Fetches video metadata in a dict format
49
+
50
+ Returns
51
+ -------
52
+ Dict
53
+ Video metadata in a dict format containing keys: title, id, views, duration, author_id,
54
+ upload_date, url, thumbnails, tags, description, likes, genre, etc.
55
+ """
56
+ # Multiple patterns to try for video details extraction for robustness
57
+ details_patterns = [
58
+ re.compile('videoDetails\":(.*?)\"isLiveContent\":.*?}'),
59
+ re.compile('videoDetails\":(.*?),\"playerConfig'),
60
+ re.compile('videoDetails\":(.*?),\"playabilityStatus')
61
+ ]
62
+
63
+ # Other metadata patterns
64
+ upload_date_pattern = re.compile("<meta itemprop=\"uploadDate\" content=\"(.*?)\">")
65
+ genre_pattern = re.compile("<meta itemprop=\"genre\" content=\"(.*?)\">")
66
+ like_count_patterns = [
67
+ re.compile("iconType\":\"LIKE\"},\"defaultText\":(.*?)}"),
68
+ re.compile('\"likeCount\":\"(\\d+)\"')
69
+ ]
70
+ channel_name_pattern = re.compile('"ownerChannelName":"(.*?)"')
71
+
72
+ # Try each pattern for video details
73
+ raw_details_match = None
74
+ for pattern in details_patterns:
75
+ match = pattern.search(self._video_data)
76
+ if match:
77
+ raw_details_match = match
78
+ break
79
+
80
+ if not raw_details_match:
81
+ # Fallback metadata for search results or incomplete video data
82
+ return {
83
+ 'title': getattr(self, 'title', None),
84
+ 'id': getattr(self, 'id', None),
85
+ 'views': getattr(self, 'views', None),
86
+ 'streamed': False,
87
+ 'duration': None,
88
+ 'author_id': None,
89
+ 'author_name': None,
90
+ 'upload_date': None,
91
+ 'url': f"https://www.youtube.com/watch?v={getattr(self, 'id', '')}" if hasattr(self, 'id') else None,
92
+ 'thumbnails': None,
93
+ 'tags': None,
94
+ 'description': None,
95
+ 'likes': None,
96
+ 'genre': None,
97
+ 'is_age_restricted': 'age-restricted' in self._video_data.lower(),
98
+ 'is_unlisted': 'unlisted' in self._video_data.lower()
99
+ }
100
+
101
+ raw_details = raw_details_match.group(0)
102
+
103
+ # Extract upload date
104
+ upload_date_match = upload_date_pattern.search(self._video_data)
105
+ upload_date = upload_date_match.group(1) if upload_date_match else None
106
+
107
+ # Extract channel name
108
+ channel_name_match = channel_name_pattern.search(self._video_data)
109
+ channel_name = channel_name_match.group(1) if channel_name_match else None
110
+
111
+ # Parse video details
112
+ try:
113
+ # Clean up the JSON string for parsing
114
+ clean_json = raw_details.replace('videoDetails\":', '')
115
+ # Handle potential JSON parsing issues
116
+ if clean_json.endswith(','):
117
+ clean_json = clean_json[:-1]
118
+ metadata = json.loads(clean_json)
119
+
120
+ data = {
121
+ 'title': metadata.get('title'),
122
+ 'id': metadata.get('videoId', self._matched_id),
123
+ 'views': metadata.get('viewCount'),
124
+ 'streamed': metadata.get('isLiveContent', False),
125
+ 'duration': metadata.get('lengthSeconds'),
126
+ 'author_id': metadata.get('channelId'),
127
+ 'author_name': channel_name or metadata.get('author'),
128
+ 'upload_date': upload_date,
129
+ 'url': f"https://www.youtube.com/watch?v={metadata.get('videoId', self._matched_id)}",
130
+ 'thumbnails': metadata.get('thumbnail', {}).get('thumbnails'),
131
+ 'tags': metadata.get('keywords'),
132
+ 'description': metadata.get('shortDescription'),
133
+ 'is_age_restricted': metadata.get('isAgeRestricted', False) or 'age-restricted' in self._video_data.lower(),
134
+ 'is_unlisted': 'unlisted' in self._video_data.lower(),
135
+ 'is_family_safe': metadata.get('isFamilySafe', True),
136
+ 'is_private': metadata.get('isPrivate', False),
137
+ 'is_live_content': metadata.get('isLiveContent', False),
138
+ 'is_crawlable': metadata.get('isCrawlable', True),
139
+ 'allow_ratings': metadata.get('allowRatings', True)
140
+ }
141
+ except (json.JSONDecodeError, KeyError, TypeError) as e:
142
+ # Fallback to basic metadata if JSON parsing fails
143
+ return {
144
+ 'title': getattr(self, 'title', None),
145
+ 'id': self._matched_id,
146
+ 'url': self._url,
147
+ 'error': f"Failed to parse video details: {str(e)}"
148
+ }
149
+
150
+ # Try to extract likes count
151
+ likes = None
152
+ for pattern in like_count_patterns:
153
+ try:
154
+ likes_match = pattern.search(self._video_data)
155
+ if likes_match:
156
+ likes_text = likes_match.group(1)
157
+ # Handle different formats of like count
158
+ if '{' in likes_text:
159
+ likes = json.loads(likes_text + '}}}')['accessibility']['accessibilityData']['label'].split(' ')[0].replace(',', '')
160
+ else:
161
+ likes = likes_text
162
+ break
163
+ except (AttributeError, KeyError, json.decoder.JSONDecodeError):
164
+ continue
165
+
166
+ data['likes'] = likes
167
+
168
+ # Try to extract genre
169
+ try:
170
+ genre_match = genre_pattern.search(self._video_data)
171
+ data['genre'] = genre_match.group(1) if genre_match else None
172
+ except AttributeError:
173
+ data['genre'] = None
174
+
175
+ return data
176
+
177
+
178
+
179
+ @property
180
+ def embed_html(self) -> str:
181
+ """
182
+ Get the embed HTML code for this video
183
+
184
+ Returns:
185
+ HTML iframe code for embedding the video
186
+ """
187
+ return f'<iframe width="560" height="315" src="https://www.youtube.com/embed/{self._matched_id}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>'
188
+
189
+ @property
190
+ def embed_url(self) -> str:
191
+ """
192
+ Get the embed URL for this video
193
+
194
+ Returns:
195
+ URL for embedding the video
196
+ """
197
+ return f'https://www.youtube.com/embed/{self._matched_id}'
198
+
199
+ @property
200
+ def thumbnail_url(self) -> str:
201
+ """
202
+ Get the thumbnail URL for this video
203
+
204
+ Returns:
205
+ URL of the video thumbnail (high quality)
206
+ """
207
+ return f'https://i.ytimg.com/vi/{self._matched_id}/hqdefault.jpg'
208
+
209
+ @property
210
+ def thumbnail_urls(self) -> Dict[str, str]:
211
+ """
212
+ Get all thumbnail URLs for this video in different qualities
213
+
214
+ Returns:
215
+ Dictionary of thumbnail URLs with quality labels
216
+ """
217
+ return {
218
+ 'default': f'https://i.ytimg.com/vi/{self._matched_id}/default.jpg',
219
+ 'medium': f'https://i.ytimg.com/vi/{self._matched_id}/mqdefault.jpg',
220
+ 'high': f'https://i.ytimg.com/vi/{self._matched_id}/hqdefault.jpg',
221
+ 'standard': f'https://i.ytimg.com/vi/{self._matched_id}/sddefault.jpg',
222
+ 'maxres': f'https://i.ytimg.com/vi/{self._matched_id}/maxresdefault.jpg'
223
+ }
224
+
225
+ if __name__ == '__main__':
226
+ video = Video('https://www.youtube.com/watch?v=9bZkp7q19f0')
227
+ print(video.metadata)
228
+
229
+ # Example of getting comments
230
+ print("\nFirst 3 comments:")
231
+ for i, comment in enumerate(video.stream_comments(3), 1):
232
+ print(f"{i}. {comment['author']}: {comment['text'][:50]}...")
@@ -0,0 +1,7 @@
1
+ from .gguf import *
2
+ from .weather import *
3
+ from .weather_ascii import *
4
+ from .autocoder import *
5
+ from .YTToolkit import *
6
+ from .GitToolkit import *
7
+ from .tempmail import *
@@ -0,0 +1,9 @@
1
+ """
2
+ AutoCoder Module - Part of Webscout
3
+ Provides automated code generation and manipulation capabilities.
4
+ """
5
+
6
+ from .autocoder import *
7
+ from .autocoder_utiles import *
8
+
9
+ # __all__ = [] # Add your public module names here