webscout 8.3.7__py3-none-any.whl → 2025.10.11__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.
- webscout/AIauto.py +250 -250
- webscout/AIbase.py +379 -379
- webscout/AIutel.py +60 -60
- webscout/Bard.py +1012 -1012
- webscout/Bing_search.py +417 -417
- webscout/DWEBS.py +529 -529
- webscout/Extra/Act.md +309 -309
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/README.md +110 -110
- webscout/Extra/GitToolkit/gitapi/__init__.py +11 -11
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -195
- webscout/Extra/GitToolkit/gitapi/user.py +96 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +61 -61
- webscout/Extra/YTToolkit/README.md +375 -375
- webscout/Extra/YTToolkit/YTdownloader.py +956 -956
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +475 -475
- webscout/Extra/YTToolkit/ytapi/README.md +44 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -118
- webscout/Extra/YTToolkit/ytapi/https.py +88 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/query.py +39 -39
- webscout/Extra/YTToolkit/ytapi/stream.py +62 -62
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +232 -232
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder.py +1105 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +332 -332
- webscout/Extra/gguf.md +429 -429
- webscout/Extra/gguf.py +1213 -1213
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +27 -27
- webscout/Extra/tempmail/async_utils.py +140 -140
- webscout/Extra/tempmail/base.py +160 -160
- webscout/Extra/tempmail/cli.py +186 -186
- webscout/Extra/tempmail/emailnator.py +84 -84
- webscout/Extra/tempmail/mail_tm.py +360 -360
- webscout/Extra/tempmail/temp_mail_io.py +291 -291
- webscout/Extra/weather.md +281 -281
- webscout/Extra/weather.py +193 -193
- webscout/Litlogger/README.md +10 -10
- webscout/Litlogger/__init__.py +15 -15
- webscout/Litlogger/formats.py +13 -13
- webscout/Litlogger/handlers.py +121 -121
- webscout/Litlogger/levels.py +13 -13
- webscout/Litlogger/logger.py +134 -134
- webscout/Provider/AISEARCH/Perplexity.py +332 -332
- webscout/Provider/AISEARCH/README.md +279 -279
- webscout/Provider/AISEARCH/__init__.py +16 -1
- webscout/Provider/AISEARCH/felo_search.py +206 -206
- webscout/Provider/AISEARCH/genspark_search.py +323 -323
- webscout/Provider/AISEARCH/hika_search.py +185 -185
- webscout/Provider/AISEARCH/iask_search.py +410 -410
- webscout/Provider/AISEARCH/monica_search.py +219 -219
- webscout/Provider/AISEARCH/scira_search.py +316 -316
- webscout/Provider/AISEARCH/stellar_search.py +177 -177
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -255
- webscout/Provider/Aitopia.py +314 -314
- webscout/Provider/Apriel.py +306 -0
- webscout/Provider/ChatGPTClone.py +236 -236
- webscout/Provider/ChatSandbox.py +343 -343
- webscout/Provider/Cloudflare.py +324 -324
- webscout/Provider/Cohere.py +208 -208
- webscout/Provider/Deepinfra.py +370 -366
- webscout/Provider/ExaAI.py +260 -260
- webscout/Provider/ExaChat.py +308 -308
- webscout/Provider/Flowith.py +221 -221
- webscout/Provider/GMI.py +293 -0
- webscout/Provider/Gemini.py +164 -164
- webscout/Provider/GeminiProxy.py +167 -167
- webscout/Provider/GithubChat.py +371 -372
- webscout/Provider/Groq.py +800 -800
- webscout/Provider/HeckAI.py +383 -383
- webscout/Provider/Jadve.py +282 -282
- webscout/Provider/K2Think.py +307 -307
- webscout/Provider/Koboldai.py +205 -205
- webscout/Provider/LambdaChat.py +423 -423
- webscout/Provider/Nemotron.py +244 -244
- webscout/Provider/Netwrck.py +248 -248
- webscout/Provider/OLLAMA.py +395 -395
- webscout/Provider/OPENAI/Cloudflare.py +393 -393
- webscout/Provider/OPENAI/FalconH1.py +451 -451
- webscout/Provider/OPENAI/FreeGemini.py +296 -296
- webscout/Provider/OPENAI/K2Think.py +431 -431
- webscout/Provider/OPENAI/NEMOTRON.py +240 -240
- webscout/Provider/OPENAI/PI.py +427 -427
- webscout/Provider/OPENAI/README.md +959 -959
- webscout/Provider/OPENAI/TogetherAI.py +345 -345
- webscout/Provider/OPENAI/TwoAI.py +465 -465
- webscout/Provider/OPENAI/__init__.py +33 -18
- webscout/Provider/OPENAI/base.py +248 -248
- webscout/Provider/OPENAI/chatglm.py +528 -0
- webscout/Provider/OPENAI/chatgpt.py +592 -592
- webscout/Provider/OPENAI/chatgptclone.py +521 -521
- webscout/Provider/OPENAI/chatsandbox.py +202 -202
- webscout/Provider/OPENAI/deepinfra.py +318 -314
- webscout/Provider/OPENAI/e2b.py +1665 -1665
- webscout/Provider/OPENAI/exaai.py +420 -420
- webscout/Provider/OPENAI/exachat.py +452 -452
- webscout/Provider/OPENAI/friendli.py +232 -232
- webscout/Provider/OPENAI/{refact.py → gmi.py} +324 -274
- webscout/Provider/OPENAI/groq.py +364 -364
- webscout/Provider/OPENAI/heckai.py +314 -314
- webscout/Provider/OPENAI/llmchatco.py +337 -337
- webscout/Provider/OPENAI/netwrck.py +355 -355
- webscout/Provider/OPENAI/oivscode.py +290 -290
- webscout/Provider/OPENAI/opkfc.py +518 -518
- webscout/Provider/OPENAI/pydantic_imports.py +1 -1
- webscout/Provider/OPENAI/scirachat.py +535 -535
- webscout/Provider/OPENAI/sonus.py +308 -308
- webscout/Provider/OPENAI/standardinput.py +442 -442
- webscout/Provider/OPENAI/textpollinations.py +340 -340
- webscout/Provider/OPENAI/toolbaz.py +419 -416
- webscout/Provider/OPENAI/typefully.py +362 -362
- webscout/Provider/OPENAI/utils.py +295 -295
- webscout/Provider/OPENAI/venice.py +436 -436
- webscout/Provider/OPENAI/wisecat.py +387 -387
- webscout/Provider/OPENAI/writecream.py +166 -166
- webscout/Provider/OPENAI/x0gpt.py +378 -378
- webscout/Provider/OPENAI/yep.py +389 -389
- webscout/Provider/OpenGPT.py +230 -230
- webscout/Provider/Openai.py +243 -243
- webscout/Provider/PI.py +405 -405
- webscout/Provider/Perplexitylabs.py +430 -430
- webscout/Provider/QwenLM.py +272 -272
- webscout/Provider/STT/__init__.py +16 -1
- webscout/Provider/Sambanova.py +257 -257
- webscout/Provider/StandardInput.py +309 -309
- webscout/Provider/TTI/README.md +82 -82
- webscout/Provider/TTI/__init__.py +33 -18
- webscout/Provider/TTI/aiarta.py +413 -413
- webscout/Provider/TTI/base.py +136 -136
- webscout/Provider/TTI/bing.py +243 -243
- webscout/Provider/TTI/gpt1image.py +149 -149
- webscout/Provider/TTI/imagen.py +196 -196
- webscout/Provider/TTI/infip.py +211 -211
- webscout/Provider/TTI/magicstudio.py +232 -232
- webscout/Provider/TTI/monochat.py +219 -219
- webscout/Provider/TTI/piclumen.py +214 -214
- webscout/Provider/TTI/pixelmuse.py +232 -232
- webscout/Provider/TTI/pollinations.py +232 -232
- webscout/Provider/TTI/together.py +288 -288
- webscout/Provider/TTI/utils.py +12 -12
- webscout/Provider/TTI/venice.py +367 -367
- webscout/Provider/TTS/README.md +192 -192
- webscout/Provider/TTS/__init__.py +33 -18
- webscout/Provider/TTS/parler.py +110 -110
- webscout/Provider/TTS/streamElements.py +333 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TeachAnything.py +237 -237
- webscout/Provider/TextPollinationsAI.py +310 -310
- webscout/Provider/TogetherAI.py +356 -356
- webscout/Provider/TwoAI.py +312 -312
- webscout/Provider/TypliAI.py +311 -311
- webscout/Provider/UNFINISHED/ChatHub.py +208 -208
- webscout/Provider/UNFINISHED/ChutesAI.py +313 -313
- webscout/Provider/UNFINISHED/GizAI.py +294 -294
- webscout/Provider/UNFINISHED/Marcus.py +198 -198
- webscout/Provider/UNFINISHED/Qodo.py +477 -477
- webscout/Provider/UNFINISHED/VercelAIGateway.py +338 -338
- webscout/Provider/UNFINISHED/XenAI.py +324 -324
- webscout/Provider/UNFINISHED/Youchat.py +330 -330
- webscout/Provider/UNFINISHED/liner.py +334 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +262 -262
- webscout/Provider/UNFINISHED/puterjs.py +634 -634
- webscout/Provider/UNFINISHED/samurai.py +223 -223
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
- webscout/Provider/Venice.py +250 -250
- webscout/Provider/VercelAI.py +256 -256
- webscout/Provider/WiseCat.py +231 -231
- webscout/Provider/WrDoChat.py +366 -366
- webscout/Provider/__init__.py +33 -18
- webscout/Provider/ai4chat.py +174 -174
- webscout/Provider/akashgpt.py +331 -331
- webscout/Provider/cerebras.py +446 -446
- webscout/Provider/chatglm.py +394 -301
- webscout/Provider/cleeai.py +211 -211
- webscout/Provider/elmo.py +282 -282
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +261 -261
- webscout/Provider/hermes.py +263 -263
- webscout/Provider/julius.py +223 -223
- webscout/Provider/learnfastai.py +309 -309
- webscout/Provider/llama3mitril.py +214 -214
- webscout/Provider/llmchat.py +243 -243
- webscout/Provider/llmchatco.py +290 -290
- webscout/Provider/meta.py +801 -801
- webscout/Provider/oivscode.py +309 -309
- webscout/Provider/scira_chat.py +383 -383
- webscout/Provider/searchchat.py +292 -292
- webscout/Provider/sonus.py +258 -258
- webscout/Provider/toolbaz.py +370 -367
- webscout/Provider/turboseek.py +273 -273
- webscout/Provider/typefully.py +207 -207
- webscout/Provider/yep.py +372 -372
- webscout/__init__.py +30 -31
- webscout/__main__.py +5 -5
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/config.py +175 -175
- webscout/auth/models.py +185 -185
- webscout/auth/routes.py +664 -664
- webscout/auth/simple_logger.py +236 -236
- webscout/cli.py +523 -523
- webscout/conversation.py +438 -438
- webscout/exceptions.py +361 -361
- webscout/litagent/Readme.md +298 -298
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +581 -581
- webscout/litagent/constants.py +59 -59
- webscout/litprinter/__init__.py +58 -58
- webscout/models.py +181 -181
- webscout/optimizers.py +419 -419
- webscout/prompt_manager.py +288 -288
- webscout/sanitize.py +1078 -1078
- webscout/scout/README.md +401 -401
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +6 -6
- webscout/scout/core/crawler.py +297 -297
- webscout/scout/core/scout.py +706 -706
- webscout/scout/core/search_result.py +95 -95
- webscout/scout/core/text_analyzer.py +62 -62
- webscout/scout/core/text_utils.py +277 -277
- webscout/scout/core/web_analyzer.py +51 -51
- webscout/scout/element.py +599 -599
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +37 -37
- webscout/swiftcli/Readme.md +323 -323
- webscout/swiftcli/__init__.py +95 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +308 -308
- webscout/swiftcli/core/context.py +104 -104
- webscout/swiftcli/core/group.py +241 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +221 -221
- webscout/swiftcli/decorators/options.py +220 -220
- webscout/swiftcli/decorators/output.py +302 -302
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +135 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +59 -59
- webscout/swiftcli/utils/formatting.py +252 -252
- webscout/swiftcli/utils/parsing.py +267 -267
- webscout/update_checker.py +117 -117
- webscout/version.py +1 -1
- webscout/webscout_search.py +1183 -1183
- webscout/webscout_search_async.py +649 -649
- webscout/yep_search.py +346 -346
- webscout/zeroart/README.md +89 -89
- webscout/zeroart/__init__.py +134 -134
- webscout/zeroart/base.py +66 -66
- webscout/zeroart/effects.py +100 -100
- webscout/zeroart/fonts.py +1238 -1238
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/METADATA +937 -937
- webscout-2025.10.11.dist-info/RECORD +300 -0
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/OPENAI/Qwen3.py +0 -303
- webscout/Provider/OPENAI/qodo.py +0 -630
- webscout/Provider/OPENAI/xenai.py +0 -514
- webscout/tempid.py +0 -134
- webscout-8.3.7.dist-info/RECORD +0 -301
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/WHEEL +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/top_level.txt +0 -0
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
from .https import (
|
|
2
|
-
trending_videos,
|
|
3
|
-
trending_songs,
|
|
4
|
-
trending_games,
|
|
5
|
-
trending_feeds,
|
|
6
|
-
trending_streams,
|
|
7
|
-
_get_trending_learning_videos,
|
|
8
|
-
trending_sports
|
|
9
|
-
)
|
|
10
|
-
from .utils import dup_filter
|
|
11
|
-
from .patterns import _ExtraPatterns as Patterns
|
|
12
|
-
from typing import Optional, List
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Extras:
|
|
16
|
-
|
|
17
|
-
@staticmethod
|
|
18
|
-
def trending_videos(limit: int = None) -> Optional[List[str]]:
|
|
19
|
-
"""
|
|
20
|
-
Get trending videos from YouTube.
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
limit (int, optional): Maximum number of videos to return.
|
|
24
|
-
Alternatively, manual slicing can be used:
|
|
25
|
-
Extras.trending_videos()[:5]
|
|
26
|
-
|
|
27
|
-
Returns:
|
|
28
|
-
Optional[List[str]]: List of video IDs or None if no videos found
|
|
29
|
-
"""
|
|
30
|
-
data = Patterns.video_id.findall(trending_videos())
|
|
31
|
-
return dup_filter(data, limit) if data else None
|
|
32
|
-
|
|
33
|
-
@staticmethod
|
|
34
|
-
def music_videos(limit: int = None) -> Optional[List[str]]:
|
|
35
|
-
"""
|
|
36
|
-
Get trending music videos from YouTube.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
limit (int, optional): Maximum number of videos to return.
|
|
40
|
-
Alternatively, manual slicing can be used:
|
|
41
|
-
Extras.music_videos()[:5]
|
|
42
|
-
|
|
43
|
-
Returns:
|
|
44
|
-
Optional[List[str]]: List of video IDs or None if no videos found
|
|
45
|
-
"""
|
|
46
|
-
data = Patterns.video_id.findall(trending_songs())
|
|
47
|
-
return dup_filter(data, limit) if data else None @staticmethod
|
|
48
|
-
def gaming_videos(limit: int = None) -> Optional[List[str]]:
|
|
49
|
-
"""
|
|
50
|
-
Get trending gaming videos from YouTube.
|
|
51
|
-
|
|
52
|
-
Args:
|
|
53
|
-
limit (int, optional): Maximum number of videos to return.
|
|
54
|
-
Alternatively, manual slicing can be used:
|
|
55
|
-
Extras.gaming_videos()[:5]
|
|
56
|
-
|
|
57
|
-
Returns:
|
|
58
|
-
Optional[List[str]]: List of video IDs or None if no videos found
|
|
59
|
-
"""
|
|
60
|
-
return dup_filter(Patterns.video_id.findall(trending_games()), limit)
|
|
61
|
-
|
|
62
|
-
@staticmethod
|
|
63
|
-
def news_videos(limit: int = None) -> Optional[List[str]]:
|
|
64
|
-
"""
|
|
65
|
-
Get trending news videos from YouTube.
|
|
66
|
-
|
|
67
|
-
Args:
|
|
68
|
-
limit (int, optional): Maximum number of videos to return.
|
|
69
|
-
Alternatively, manual slicing can be used:
|
|
70
|
-
Extras.news_videos()[:5]
|
|
71
|
-
|
|
72
|
-
Returns:
|
|
73
|
-
Optional[List[str]]: List of video IDs or None if no videos found
|
|
74
|
-
"""
|
|
75
|
-
return dup_filter(Patterns.video_id.findall(trending_feeds()), limit) @staticmethod
|
|
76
|
-
def live_videos(limit: int = None) -> Optional[List[str]]:
|
|
77
|
-
"""
|
|
78
|
-
Get trending live videos from YouTube.
|
|
79
|
-
|
|
80
|
-
Args:
|
|
81
|
-
limit (int, optional): Maximum number of videos to return.
|
|
82
|
-
Alternatively, manual slicing can be used:
|
|
83
|
-
Extras.live_videos()[:5]
|
|
84
|
-
|
|
85
|
-
Returns:
|
|
86
|
-
Optional[List[str]]: List of video IDs or None if no videos found
|
|
87
|
-
"""
|
|
88
|
-
return dup_filter(Patterns.video_id.findall(trending_streams()), limit)
|
|
89
|
-
|
|
90
|
-
@staticmethod
|
|
91
|
-
def educational_videos(limit: int = None) -> Optional[List[str]]:
|
|
92
|
-
"""
|
|
93
|
-
Get trending educational videos from YouTube.
|
|
94
|
-
|
|
95
|
-
Args:
|
|
96
|
-
limit (int, optional): Maximum number of videos to return.
|
|
97
|
-
Alternatively, manual slicing can be used:
|
|
98
|
-
Extras.educational_videos()[:5]
|
|
99
|
-
|
|
100
|
-
Returns:
|
|
101
|
-
Optional[List[str]]: List of video IDs or None if no videos found
|
|
102
|
-
"""
|
|
103
|
-
return dup_filter(Patterns.video_id.findall(_get_trending_learning_videos()), limit)
|
|
104
|
-
|
|
105
|
-
@staticmethod
|
|
106
|
-
def sport_videos(limit: int = None) -> Optional[List[str]]:
|
|
107
|
-
"""
|
|
108
|
-
Get trending sports videos from YouTube.
|
|
109
|
-
|
|
110
|
-
Args:
|
|
111
|
-
limit (int, optional): Maximum number of videos to return.
|
|
112
|
-
Alternatively, manual slicing can be used:
|
|
113
|
-
Extras.sport_videos()[:5]
|
|
114
|
-
|
|
115
|
-
Returns:
|
|
116
|
-
Optional[List[str]]: List of video IDs or None if no videos found
|
|
117
|
-
"""
|
|
118
|
-
return dup_filter(Patterns.video_id.findall(trending_sports()), limit)
|
|
1
|
+
from .https import (
|
|
2
|
+
trending_videos,
|
|
3
|
+
trending_songs,
|
|
4
|
+
trending_games,
|
|
5
|
+
trending_feeds,
|
|
6
|
+
trending_streams,
|
|
7
|
+
_get_trending_learning_videos,
|
|
8
|
+
trending_sports
|
|
9
|
+
)
|
|
10
|
+
from .utils import dup_filter
|
|
11
|
+
from .patterns import _ExtraPatterns as Patterns
|
|
12
|
+
from typing import Optional, List
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Extras:
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def trending_videos(limit: int = None) -> Optional[List[str]]:
|
|
19
|
+
"""
|
|
20
|
+
Get trending videos from YouTube.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
limit (int, optional): Maximum number of videos to return.
|
|
24
|
+
Alternatively, manual slicing can be used:
|
|
25
|
+
Extras.trending_videos()[:5]
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Optional[List[str]]: List of video IDs or None if no videos found
|
|
29
|
+
"""
|
|
30
|
+
data = Patterns.video_id.findall(trending_videos())
|
|
31
|
+
return dup_filter(data, limit) if data else None
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def music_videos(limit: int = None) -> Optional[List[str]]:
|
|
35
|
+
"""
|
|
36
|
+
Get trending music videos from YouTube.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
limit (int, optional): Maximum number of videos to return.
|
|
40
|
+
Alternatively, manual slicing can be used:
|
|
41
|
+
Extras.music_videos()[:5]
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Optional[List[str]]: List of video IDs or None if no videos found
|
|
45
|
+
"""
|
|
46
|
+
data = Patterns.video_id.findall(trending_songs())
|
|
47
|
+
return dup_filter(data, limit) if data else None @staticmethod
|
|
48
|
+
def gaming_videos(limit: int = None) -> Optional[List[str]]:
|
|
49
|
+
"""
|
|
50
|
+
Get trending gaming videos from YouTube.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
limit (int, optional): Maximum number of videos to return.
|
|
54
|
+
Alternatively, manual slicing can be used:
|
|
55
|
+
Extras.gaming_videos()[:5]
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Optional[List[str]]: List of video IDs or None if no videos found
|
|
59
|
+
"""
|
|
60
|
+
return dup_filter(Patterns.video_id.findall(trending_games()), limit)
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def news_videos(limit: int = None) -> Optional[List[str]]:
|
|
64
|
+
"""
|
|
65
|
+
Get trending news videos from YouTube.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
limit (int, optional): Maximum number of videos to return.
|
|
69
|
+
Alternatively, manual slicing can be used:
|
|
70
|
+
Extras.news_videos()[:5]
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Optional[List[str]]: List of video IDs or None if no videos found
|
|
74
|
+
"""
|
|
75
|
+
return dup_filter(Patterns.video_id.findall(trending_feeds()), limit) @staticmethod
|
|
76
|
+
def live_videos(limit: int = None) -> Optional[List[str]]:
|
|
77
|
+
"""
|
|
78
|
+
Get trending live videos from YouTube.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
limit (int, optional): Maximum number of videos to return.
|
|
82
|
+
Alternatively, manual slicing can be used:
|
|
83
|
+
Extras.live_videos()[:5]
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Optional[List[str]]: List of video IDs or None if no videos found
|
|
87
|
+
"""
|
|
88
|
+
return dup_filter(Patterns.video_id.findall(trending_streams()), limit)
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def educational_videos(limit: int = None) -> Optional[List[str]]:
|
|
92
|
+
"""
|
|
93
|
+
Get trending educational videos from YouTube.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
limit (int, optional): Maximum number of videos to return.
|
|
97
|
+
Alternatively, manual slicing can be used:
|
|
98
|
+
Extras.educational_videos()[:5]
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Optional[List[str]]: List of video IDs or None if no videos found
|
|
102
|
+
"""
|
|
103
|
+
return dup_filter(Patterns.video_id.findall(_get_trending_learning_videos()), limit)
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def sport_videos(limit: int = None) -> Optional[List[str]]:
|
|
107
|
+
"""
|
|
108
|
+
Get trending sports videos from YouTube.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
limit (int, optional): Maximum number of videos to return.
|
|
112
|
+
Alternatively, manual slicing can be used:
|
|
113
|
+
Extras.sport_videos()[:5]
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Optional[List[str]]: List of video IDs or None if no videos found
|
|
117
|
+
"""
|
|
118
|
+
return dup_filter(Patterns.video_id.findall(trending_sports()), limit)
|
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
from urllib.parse import quote
|
|
2
|
-
from .utils import request
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def channel_about(head: str) -> str:
|
|
6
|
-
return request(head + '/about')
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def video_count(channel_id: str) -> str:
|
|
10
|
-
head = 'https://www.youtube.com/results?search_query='
|
|
11
|
-
tail = '&sp=EgIQAg%253D%253D'
|
|
12
|
-
return request(head + channel_id + tail)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def uploads_data(head: str) -> str:
|
|
16
|
-
url = head + '/videos'
|
|
17
|
-
return request(url)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def streams_data(head: str) -> str:
|
|
21
|
-
url = head + '/streams'
|
|
22
|
-
return request(url)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def channel_playlists(head: str) -> str:
|
|
26
|
-
url = head + '/playlists'
|
|
27
|
-
return request(url)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def upcoming_videos(head: str) -> str:
|
|
31
|
-
url = head + '/videos?view=2&live_view=502'
|
|
32
|
-
return request(url)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def video_data(video_id: str) -> str:
|
|
36
|
-
url = f'https://www.youtube.com/watch?v={video_id}'
|
|
37
|
-
return request(url)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def playlist_data(playlist_id: str) -> str:
|
|
41
|
-
url = 'https://www.youtube.com/playlist?list=' + playlist_id
|
|
42
|
-
return request(url)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def trending_videos() -> str:
|
|
46
|
-
return request('https://www.youtube.com/feed/trending')
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def trending_songs() -> str:
|
|
50
|
-
return request('https://www.youtube.com/feed/music')
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def trending_games() -> str:
|
|
54
|
-
return request('https://www.youtube.com/gaming')
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def trending_feeds() -> str:
|
|
58
|
-
return request('https://www.youtube.com/news')
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def trending_streams() -> str:
|
|
62
|
-
return request('https://www.youtube.com/live')
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def _get_trending_learning_videos() -> str:
|
|
66
|
-
return request('https://www.youtube.com/learning')
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def trending_sports() -> str:
|
|
70
|
-
return request('https://www.youtube.com/sports')
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def find_videos(query: str) -> str:
|
|
74
|
-
head = 'https://www.youtube.com/results?search_query='
|
|
75
|
-
tail = '&sp=EgIQAQ%253D%253D'
|
|
76
|
-
return request(head + quote(query) + tail)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def find_channels(query: str) -> str:
|
|
80
|
-
head = 'https://www.youtube.com/results?search_query='
|
|
81
|
-
tail = '&sp=EgIQAg%253D%253D'
|
|
82
|
-
return request(head + quote(query) + tail)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def find_playlists(query: str) -> str:
|
|
86
|
-
head = 'https://www.youtube.com/results?search_query='
|
|
87
|
-
tail = '&sp=EgIQAw%253D%253D'
|
|
88
|
-
return request(head + quote(query) + tail)
|
|
1
|
+
from urllib.parse import quote
|
|
2
|
+
from .utils import request
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def channel_about(head: str) -> str:
|
|
6
|
+
return request(head + '/about')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def video_count(channel_id: str) -> str:
|
|
10
|
+
head = 'https://www.youtube.com/results?search_query='
|
|
11
|
+
tail = '&sp=EgIQAg%253D%253D'
|
|
12
|
+
return request(head + channel_id + tail)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def uploads_data(head: str) -> str:
|
|
16
|
+
url = head + '/videos'
|
|
17
|
+
return request(url)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def streams_data(head: str) -> str:
|
|
21
|
+
url = head + '/streams'
|
|
22
|
+
return request(url)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def channel_playlists(head: str) -> str:
|
|
26
|
+
url = head + '/playlists'
|
|
27
|
+
return request(url)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def upcoming_videos(head: str) -> str:
|
|
31
|
+
url = head + '/videos?view=2&live_view=502'
|
|
32
|
+
return request(url)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def video_data(video_id: str) -> str:
|
|
36
|
+
url = f'https://www.youtube.com/watch?v={video_id}'
|
|
37
|
+
return request(url)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def playlist_data(playlist_id: str) -> str:
|
|
41
|
+
url = 'https://www.youtube.com/playlist?list=' + playlist_id
|
|
42
|
+
return request(url)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def trending_videos() -> str:
|
|
46
|
+
return request('https://www.youtube.com/feed/trending')
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def trending_songs() -> str:
|
|
50
|
+
return request('https://www.youtube.com/feed/music')
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def trending_games() -> str:
|
|
54
|
+
return request('https://www.youtube.com/gaming')
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def trending_feeds() -> str:
|
|
58
|
+
return request('https://www.youtube.com/news')
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def trending_streams() -> str:
|
|
62
|
+
return request('https://www.youtube.com/live')
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _get_trending_learning_videos() -> str:
|
|
66
|
+
return request('https://www.youtube.com/learning')
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def trending_sports() -> str:
|
|
70
|
+
return request('https://www.youtube.com/sports')
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def find_videos(query: str) -> str:
|
|
74
|
+
head = 'https://www.youtube.com/results?search_query='
|
|
75
|
+
tail = '&sp=EgIQAQ%253D%253D'
|
|
76
|
+
return request(head + quote(query) + tail)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def find_channels(query: str) -> str:
|
|
80
|
+
head = 'https://www.youtube.com/results?search_query='
|
|
81
|
+
tail = '&sp=EgIQAg%253D%253D'
|
|
82
|
+
return request(head + quote(query) + tail)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def find_playlists(query: str) -> str:
|
|
86
|
+
head = 'https://www.youtube.com/results?search_query='
|
|
87
|
+
tail = '&sp=EgIQAw%253D%253D'
|
|
88
|
+
return request(head + quote(query) + tail)
|
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import re
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class _ChannelPatterns:
|
|
5
|
-
name = re.compile(r'channelMetadataRenderer\":{\"title\":\"(.*?)\"')
|
|
6
|
-
id = re.compile(r'channelId\":\"(.*?)\"')
|
|
7
|
-
verified = re.compile(r'"label":"Verified"')
|
|
8
|
-
check_live = re.compile(r'{"text":"LIVE"}')
|
|
9
|
-
live = re.compile(r"thumbnailOverlays\":\[(.*?)]")
|
|
10
|
-
video_id = re.compile(r'videoId\":\"(.*?)\"')
|
|
11
|
-
uploads = re.compile(r"gridVideoRenderer\":{\"videoId\":\"(.*?)\"")
|
|
12
|
-
subscribers = re.compile(r"\"subscriberCountText\":{\"accessibility\":(.*?),")
|
|
13
|
-
views = re.compile(r"viewCountText\":{\"simpleText\":\"(.*?)\"}")
|
|
14
|
-
creation = re.compile(r"{\"text\":\"Joined \"},{\"text\":\"(.*?)\"}")
|
|
15
|
-
country = re.compile(r"country\":{\"simpleText\":\"(.*?)\"}")
|
|
16
|
-
custom_url = re.compile(r"canonicalChannelUrl\":\"(.*?)\"")
|
|
17
|
-
description = re.compile(r"{\"description\":{\"simpleText\":\"(.*?)\"}")
|
|
18
|
-
avatar = re.compile(r"height\":88},{\"url\":\"(.*?)\"")
|
|
19
|
-
banner = re.compile(r"width\":1280,\"height\":351},{\"url\":\"(.*?)\"")
|
|
20
|
-
playlists = re.compile(r"{\"url\":\"/playlist\?list=(.*?)\"")
|
|
21
|
-
video_count = re.compile(r"videoCountText\":{\"runs\":\[{\"text\":(.*?)}")
|
|
22
|
-
socials = re.compile(r"q=https%3A%2F%2F(.*?)\"")
|
|
23
|
-
upload_ids = re.compile(r"videoId\":\"(.*?)\"")
|
|
24
|
-
stream_ids = re.compile(r"videoId\":\"(.*?)\"")
|
|
25
|
-
upload_chunk = re.compile(r"gridVideoRenderer\":{(.*?)\"navigationEndpoint")
|
|
26
|
-
upload_chunk_fl_1 = re.compile(r"simpleText\":\"Streamed")
|
|
27
|
-
upload_chunk_fl_2 = re.compile(r"default_live.")
|
|
28
|
-
upcoming_check = re.compile(r"\"title\":\"Upcoming live streams\"")
|
|
29
|
-
upcoming = re.compile(r"gridVideoRenderer\":{\"videoId\":\"(.*?)\"")
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class _VideoPatterns:
|
|
33
|
-
video_id = re.compile(r'videoId\":\"(.*?)\"')
|
|
34
|
-
title = re.compile(r"title\":\"(.*?)\"")
|
|
35
|
-
duration = re.compile(r"approxDurationMs\":\"(.*?)\"")
|
|
36
|
-
upload_date = re.compile(r"uploadDate\":\"(.*?)\"")
|
|
37
|
-
author_id = re.compile(r"channelIds\":\[\"(.*?)\"")
|
|
38
|
-
description = re.compile(r"shortDescription\":\"(.*)\",\"isCrawlable")
|
|
39
|
-
tags = re.compile(r"<meta name=\"keywords\" content=\"(.*?)\">")
|
|
40
|
-
is_streamed = re.compile(r"simpleText\":\"Streamed live")
|
|
41
|
-
is_premiered = re.compile(r"dateText\":{\"simpleText\":\"Premiered")
|
|
42
|
-
views = re.compile(r"videoViewCountRenderer\":{\"viewCount\":{\"simpleText\":\"(.*?)\"")
|
|
43
|
-
likes = re.compile(r"toggledText\":{\"accessibility\":{\"accessibilityData\":{\"label\":\"(.*?) ")
|
|
44
|
-
thumbnail = re.compile(r"playerMicroformatRenderer\":{\"thumbnail\":{\"thumbnails\":\[{\"url\":\"(.*?)\"")
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class _PlaylistPatterns:
|
|
48
|
-
name = re.compile(r"{\"title\":\"(.*?)\"")
|
|
49
|
-
video_count = re.compile(r"stats\":\[{\"runs\":\[{\"text\":\"(.*?)\"")
|
|
50
|
-
video_id = re.compile(r"videoId\":\"(.*?)\"")
|
|
51
|
-
thumbnail = re.compile(r"og:image\" content=\"(.*?)\?")
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class _ExtraPatterns:
|
|
55
|
-
video_id = re.compile(r"videoId\":\"(.*?)\"")
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
class _QueryPatterns:
|
|
59
|
-
channel_id = re.compile(r"channelId\":\"(.*?)\"")
|
|
60
|
-
video_id = re.compile(r"videoId\":\"(.*?)\"")
|
|
61
|
-
playlist_id = re.compile(r"playlistId\":\"(.*?)\"")
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class _ChannelPatterns:
|
|
5
|
+
name = re.compile(r'channelMetadataRenderer\":{\"title\":\"(.*?)\"')
|
|
6
|
+
id = re.compile(r'channelId\":\"(.*?)\"')
|
|
7
|
+
verified = re.compile(r'"label":"Verified"')
|
|
8
|
+
check_live = re.compile(r'{"text":"LIVE"}')
|
|
9
|
+
live = re.compile(r"thumbnailOverlays\":\[(.*?)]")
|
|
10
|
+
video_id = re.compile(r'videoId\":\"(.*?)\"')
|
|
11
|
+
uploads = re.compile(r"gridVideoRenderer\":{\"videoId\":\"(.*?)\"")
|
|
12
|
+
subscribers = re.compile(r"\"subscriberCountText\":{\"accessibility\":(.*?),")
|
|
13
|
+
views = re.compile(r"viewCountText\":{\"simpleText\":\"(.*?)\"}")
|
|
14
|
+
creation = re.compile(r"{\"text\":\"Joined \"},{\"text\":\"(.*?)\"}")
|
|
15
|
+
country = re.compile(r"country\":{\"simpleText\":\"(.*?)\"}")
|
|
16
|
+
custom_url = re.compile(r"canonicalChannelUrl\":\"(.*?)\"")
|
|
17
|
+
description = re.compile(r"{\"description\":{\"simpleText\":\"(.*?)\"}")
|
|
18
|
+
avatar = re.compile(r"height\":88},{\"url\":\"(.*?)\"")
|
|
19
|
+
banner = re.compile(r"width\":1280,\"height\":351},{\"url\":\"(.*?)\"")
|
|
20
|
+
playlists = re.compile(r"{\"url\":\"/playlist\?list=(.*?)\"")
|
|
21
|
+
video_count = re.compile(r"videoCountText\":{\"runs\":\[{\"text\":(.*?)}")
|
|
22
|
+
socials = re.compile(r"q=https%3A%2F%2F(.*?)\"")
|
|
23
|
+
upload_ids = re.compile(r"videoId\":\"(.*?)\"")
|
|
24
|
+
stream_ids = re.compile(r"videoId\":\"(.*?)\"")
|
|
25
|
+
upload_chunk = re.compile(r"gridVideoRenderer\":{(.*?)\"navigationEndpoint")
|
|
26
|
+
upload_chunk_fl_1 = re.compile(r"simpleText\":\"Streamed")
|
|
27
|
+
upload_chunk_fl_2 = re.compile(r"default_live.")
|
|
28
|
+
upcoming_check = re.compile(r"\"title\":\"Upcoming live streams\"")
|
|
29
|
+
upcoming = re.compile(r"gridVideoRenderer\":{\"videoId\":\"(.*?)\"")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _VideoPatterns:
|
|
33
|
+
video_id = re.compile(r'videoId\":\"(.*?)\"')
|
|
34
|
+
title = re.compile(r"title\":\"(.*?)\"")
|
|
35
|
+
duration = re.compile(r"approxDurationMs\":\"(.*?)\"")
|
|
36
|
+
upload_date = re.compile(r"uploadDate\":\"(.*?)\"")
|
|
37
|
+
author_id = re.compile(r"channelIds\":\[\"(.*?)\"")
|
|
38
|
+
description = re.compile(r"shortDescription\":\"(.*)\",\"isCrawlable")
|
|
39
|
+
tags = re.compile(r"<meta name=\"keywords\" content=\"(.*?)\">")
|
|
40
|
+
is_streamed = re.compile(r"simpleText\":\"Streamed live")
|
|
41
|
+
is_premiered = re.compile(r"dateText\":{\"simpleText\":\"Premiered")
|
|
42
|
+
views = re.compile(r"videoViewCountRenderer\":{\"viewCount\":{\"simpleText\":\"(.*?)\"")
|
|
43
|
+
likes = re.compile(r"toggledText\":{\"accessibility\":{\"accessibilityData\":{\"label\":\"(.*?) ")
|
|
44
|
+
thumbnail = re.compile(r"playerMicroformatRenderer\":{\"thumbnail\":{\"thumbnails\":\[{\"url\":\"(.*?)\"")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class _PlaylistPatterns:
|
|
48
|
+
name = re.compile(r"{\"title\":\"(.*?)\"")
|
|
49
|
+
video_count = re.compile(r"stats\":\[{\"runs\":\[{\"text\":\"(.*?)\"")
|
|
50
|
+
video_id = re.compile(r"videoId\":\"(.*?)\"")
|
|
51
|
+
thumbnail = re.compile(r"og:image\" content=\"(.*?)\?")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class _ExtraPatterns:
|
|
55
|
+
video_id = re.compile(r"videoId\":\"(.*?)\"")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class _QueryPatterns:
|
|
59
|
+
channel_id = re.compile(r"channelId\":\"(.*?)\"")
|
|
60
|
+
video_id = re.compile(r"videoId\":\"(.*?)\"")
|
|
61
|
+
playlist_id = re.compile(r"playlistId\":\"(.*?)\"")
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
import re
|
|
2
|
-
from typing import Dict, Any
|
|
3
|
-
|
|
4
|
-
from .pool import collect
|
|
5
|
-
from .utils import dup_filter
|
|
6
|
-
from .https import playlist_data
|
|
7
|
-
from .patterns import _PlaylistPatterns as Patterns
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Playlist:
|
|
11
|
-
|
|
12
|
-
def __init__(self, playlist_id: str):
|
|
13
|
-
"""
|
|
14
|
-
Represents a YouTube Playlist
|
|
15
|
-
|
|
16
|
-
Parameters
|
|
17
|
-
----------
|
|
18
|
-
playlist_id : str
|
|
19
|
-
The id or url of the playlist
|
|
20
|
-
"""
|
|
21
|
-
pattern = re.compile('=(.+?)$|^PL(.+?)$')
|
|
22
|
-
match = pattern.search(playlist_id)
|
|
23
|
-
if not match:
|
|
24
|
-
raise ValueError(f'Invalid playlist id: {playlist_id}')
|
|
25
|
-
if match.group(1):
|
|
26
|
-
self.id = match.group(1)
|
|
27
|
-
elif match.group(2):
|
|
28
|
-
self.id = 'PL' + match.group(2)
|
|
29
|
-
self._playlist_data = playlist_data(self.id)
|
|
30
|
-
|
|
31
|
-
def __repr__(self):
|
|
32
|
-
return f'<Playlist {self.id}>'
|
|
33
|
-
|
|
34
|
-
@property
|
|
35
|
-
def metadata(self) -> Dict[str, Any]:
|
|
36
|
-
"""
|
|
37
|
-
Fetches playlist metadata in a dict format
|
|
38
|
-
|
|
39
|
-
Returns
|
|
40
|
-
-------
|
|
41
|
-
Dict
|
|
42
|
-
Playlist metadata in a dict format containing keys: id, url, name, video_count, thumbnail,
|
|
43
|
-
"""
|
|
44
|
-
patterns = [
|
|
45
|
-
Patterns.name,
|
|
46
|
-
Patterns.video_count,
|
|
47
|
-
Patterns.thumbnail,
|
|
48
|
-
Patterns.video_id,
|
|
49
|
-
]
|
|
50
|
-
ext = collect(lambda x: x.findall(self._playlist_data) or None, patterns)
|
|
51
|
-
data = [e[0] if e else None for e in ext]
|
|
52
|
-
return {
|
|
53
|
-
'id': self.id,
|
|
54
|
-
'url': 'https://www.youtube.com/playlist?list=' + self.id,
|
|
55
|
-
'name': data[0] if data else None,
|
|
56
|
-
'video_count': data[1] if data else None,
|
|
57
|
-
'thumbnail': data[2] if data else None,
|
|
58
|
-
'videos': dup_filter(ext[3])
|
|
1
|
+
import re
|
|
2
|
+
from typing import Dict, Any
|
|
3
|
+
|
|
4
|
+
from .pool import collect
|
|
5
|
+
from .utils import dup_filter
|
|
6
|
+
from .https import playlist_data
|
|
7
|
+
from .patterns import _PlaylistPatterns as Patterns
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Playlist:
|
|
11
|
+
|
|
12
|
+
def __init__(self, playlist_id: str):
|
|
13
|
+
"""
|
|
14
|
+
Represents a YouTube Playlist
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
playlist_id : str
|
|
19
|
+
The id or url of the playlist
|
|
20
|
+
"""
|
|
21
|
+
pattern = re.compile('=(.+?)$|^PL(.+?)$')
|
|
22
|
+
match = pattern.search(playlist_id)
|
|
23
|
+
if not match:
|
|
24
|
+
raise ValueError(f'Invalid playlist id: {playlist_id}')
|
|
25
|
+
if match.group(1):
|
|
26
|
+
self.id = match.group(1)
|
|
27
|
+
elif match.group(2):
|
|
28
|
+
self.id = 'PL' + match.group(2)
|
|
29
|
+
self._playlist_data = playlist_data(self.id)
|
|
30
|
+
|
|
31
|
+
def __repr__(self):
|
|
32
|
+
return f'<Playlist {self.id}>'
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def metadata(self) -> Dict[str, Any]:
|
|
36
|
+
"""
|
|
37
|
+
Fetches playlist metadata in a dict format
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
Dict
|
|
42
|
+
Playlist metadata in a dict format containing keys: id, url, name, video_count, thumbnail,
|
|
43
|
+
"""
|
|
44
|
+
patterns = [
|
|
45
|
+
Patterns.name,
|
|
46
|
+
Patterns.video_count,
|
|
47
|
+
Patterns.thumbnail,
|
|
48
|
+
Patterns.video_id,
|
|
49
|
+
]
|
|
50
|
+
ext = collect(lambda x: x.findall(self._playlist_data) or None, patterns)
|
|
51
|
+
data = [e[0] if e else None for e in ext]
|
|
52
|
+
return {
|
|
53
|
+
'id': self.id,
|
|
54
|
+
'url': 'https://www.youtube.com/playlist?list=' + self.id,
|
|
55
|
+
'name': data[0] if data else None,
|
|
56
|
+
'video_count': data[1] if data else None,
|
|
57
|
+
'thumbnail': data[2] if data else None,
|
|
58
|
+
'videos': dup_filter(ext[3])
|
|
59
59
|
}
|