webscout 7.0__py3-none-any.whl → 7.2__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 (147) hide show
  1. webscout/AIauto.py +191 -191
  2. webscout/AIbase.py +122 -122
  3. webscout/AIutel.py +440 -440
  4. webscout/Bard.py +343 -161
  5. webscout/DWEBS.py +489 -492
  6. webscout/Extra/YTToolkit/YTdownloader.py +995 -995
  7. webscout/Extra/YTToolkit/__init__.py +2 -2
  8. webscout/Extra/YTToolkit/transcriber.py +476 -479
  9. webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
  10. webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
  11. webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
  12. webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
  13. webscout/Extra/YTToolkit/ytapi/video.py +103 -103
  14. webscout/Extra/autocoder/__init__.py +9 -9
  15. webscout/Extra/autocoder/autocoder_utiles.py +199 -199
  16. webscout/Extra/autocoder/rawdog.py +5 -7
  17. webscout/Extra/autollama.py +230 -230
  18. webscout/Extra/gguf.py +3 -3
  19. webscout/Extra/weather.py +171 -171
  20. webscout/LLM.py +442 -442
  21. webscout/Litlogger/__init__.py +67 -681
  22. webscout/Litlogger/core/__init__.py +6 -0
  23. webscout/Litlogger/core/level.py +20 -0
  24. webscout/Litlogger/core/logger.py +123 -0
  25. webscout/Litlogger/handlers/__init__.py +12 -0
  26. webscout/Litlogger/handlers/console.py +50 -0
  27. webscout/Litlogger/handlers/file.py +143 -0
  28. webscout/Litlogger/handlers/network.py +174 -0
  29. webscout/Litlogger/styles/__init__.py +7 -0
  30. webscout/Litlogger/styles/colors.py +231 -0
  31. webscout/Litlogger/styles/formats.py +377 -0
  32. webscout/Litlogger/styles/text.py +87 -0
  33. webscout/Litlogger/utils/__init__.py +6 -0
  34. webscout/Litlogger/utils/detectors.py +154 -0
  35. webscout/Litlogger/utils/formatters.py +200 -0
  36. webscout/Provider/AISEARCH/DeepFind.py +250 -250
  37. webscout/Provider/Blackboxai.py +136 -137
  38. webscout/Provider/ChatGPTGratis.py +226 -0
  39. webscout/Provider/Cloudflare.py +91 -78
  40. webscout/Provider/DeepSeek.py +218 -0
  41. webscout/Provider/Deepinfra.py +59 -35
  42. webscout/Provider/Free2GPT.py +131 -124
  43. webscout/Provider/Gemini.py +100 -115
  44. webscout/Provider/Glider.py +74 -59
  45. webscout/Provider/Groq.py +30 -18
  46. webscout/Provider/Jadve.py +108 -77
  47. webscout/Provider/Llama3.py +117 -94
  48. webscout/Provider/Marcus.py +191 -137
  49. webscout/Provider/Netwrck.py +62 -50
  50. webscout/Provider/PI.py +79 -124
  51. webscout/Provider/PizzaGPT.py +129 -83
  52. webscout/Provider/QwenLM.py +311 -0
  53. webscout/Provider/TTI/AiForce/__init__.py +22 -22
  54. webscout/Provider/TTI/AiForce/async_aiforce.py +257 -257
  55. webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -242
  56. webscout/Provider/TTI/Nexra/__init__.py +22 -22
  57. webscout/Provider/TTI/Nexra/async_nexra.py +286 -286
  58. webscout/Provider/TTI/Nexra/sync_nexra.py +258 -258
  59. webscout/Provider/TTI/PollinationsAI/__init__.py +23 -23
  60. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -330
  61. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -285
  62. webscout/Provider/TTI/artbit/__init__.py +22 -22
  63. webscout/Provider/TTI/artbit/async_artbit.py +184 -184
  64. webscout/Provider/TTI/artbit/sync_artbit.py +176 -176
  65. webscout/Provider/TTI/blackbox/__init__.py +4 -4
  66. webscout/Provider/TTI/blackbox/async_blackbox.py +212 -212
  67. webscout/Provider/TTI/blackbox/sync_blackbox.py +199 -199
  68. webscout/Provider/TTI/deepinfra/__init__.py +4 -4
  69. webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -227
  70. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -199
  71. webscout/Provider/TTI/huggingface/__init__.py +22 -22
  72. webscout/Provider/TTI/huggingface/async_huggingface.py +199 -199
  73. webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -195
  74. webscout/Provider/TTI/imgninza/__init__.py +4 -4
  75. webscout/Provider/TTI/imgninza/async_ninza.py +214 -214
  76. webscout/Provider/TTI/imgninza/sync_ninza.py +209 -209
  77. webscout/Provider/TTI/talkai/__init__.py +4 -4
  78. webscout/Provider/TTI/talkai/async_talkai.py +229 -229
  79. webscout/Provider/TTI/talkai/sync_talkai.py +207 -207
  80. webscout/Provider/TTS/deepgram.py +182 -182
  81. webscout/Provider/TTS/elevenlabs.py +136 -136
  82. webscout/Provider/TTS/gesserit.py +150 -150
  83. webscout/Provider/TTS/murfai.py +138 -138
  84. webscout/Provider/TTS/parler.py +133 -134
  85. webscout/Provider/TTS/streamElements.py +360 -360
  86. webscout/Provider/TTS/utils.py +280 -280
  87. webscout/Provider/TTS/voicepod.py +116 -116
  88. webscout/Provider/TextPollinationsAI.py +74 -47
  89. webscout/Provider/WiseCat.py +193 -0
  90. webscout/Provider/__init__.py +144 -136
  91. webscout/Provider/cerebras.py +242 -227
  92. webscout/Provider/chatglm.py +204 -204
  93. webscout/Provider/dgaf.py +67 -39
  94. webscout/Provider/gaurish.py +105 -66
  95. webscout/Provider/geminiapi.py +208 -208
  96. webscout/Provider/granite.py +223 -0
  97. webscout/Provider/hermes.py +218 -218
  98. webscout/Provider/llama3mitril.py +179 -179
  99. webscout/Provider/llamatutor.py +72 -62
  100. webscout/Provider/llmchat.py +60 -35
  101. webscout/Provider/meta.py +794 -794
  102. webscout/Provider/multichat.py +331 -230
  103. webscout/Provider/typegpt.py +359 -356
  104. webscout/Provider/yep.py +5 -5
  105. webscout/__main__.py +5 -5
  106. webscout/cli.py +319 -319
  107. webscout/conversation.py +241 -242
  108. webscout/exceptions.py +328 -328
  109. webscout/litagent/__init__.py +28 -28
  110. webscout/litagent/agent.py +2 -3
  111. webscout/litprinter/__init__.py +0 -58
  112. webscout/scout/__init__.py +8 -8
  113. webscout/scout/core.py +884 -884
  114. webscout/scout/element.py +459 -459
  115. webscout/scout/parsers/__init__.py +69 -69
  116. webscout/scout/parsers/html5lib_parser.py +172 -172
  117. webscout/scout/parsers/html_parser.py +236 -236
  118. webscout/scout/parsers/lxml_parser.py +178 -178
  119. webscout/scout/utils.py +38 -38
  120. webscout/swiftcli/__init__.py +811 -811
  121. webscout/update_checker.py +2 -12
  122. webscout/version.py +1 -1
  123. webscout/webscout_search.py +1142 -1140
  124. webscout/webscout_search_async.py +635 -635
  125. webscout/zeroart/__init__.py +54 -54
  126. webscout/zeroart/base.py +60 -60
  127. webscout/zeroart/effects.py +99 -99
  128. webscout/zeroart/fonts.py +816 -816
  129. {webscout-7.0.dist-info → webscout-7.2.dist-info}/METADATA +21 -28
  130. webscout-7.2.dist-info/RECORD +217 -0
  131. webstoken/__init__.py +30 -30
  132. webstoken/classifier.py +189 -189
  133. webstoken/keywords.py +216 -216
  134. webstoken/language.py +128 -128
  135. webstoken/ner.py +164 -164
  136. webstoken/normalizer.py +35 -35
  137. webstoken/processor.py +77 -77
  138. webstoken/sentiment.py +206 -206
  139. webstoken/stemmer.py +73 -73
  140. webstoken/tagger.py +60 -60
  141. webstoken/tokenizer.py +158 -158
  142. webscout/Provider/RUBIKSAI.py +0 -272
  143. webscout-7.0.dist-info/RECORD +0 -199
  144. {webscout-7.0.dist-info → webscout-7.2.dist-info}/LICENSE.md +0 -0
  145. {webscout-7.0.dist-info → webscout-7.2.dist-info}/WHEEL +0 -0
  146. {webscout-7.0.dist-info → webscout-7.2.dist-info}/entry_points.txt +0 -0
  147. {webscout-7.0.dist-info → webscout-7.2.dist-info}/top_level.txt +0 -0
@@ -1,307 +1,307 @@
1
- import json
2
- import re
3
-
4
- from .https import (
5
- channel_about,
6
- streams_data,
7
- uploads_data,
8
- channel_playlists,
9
- upcoming_videos
10
- )
11
- from .video import Video
12
- from .pool import collect
13
- from .utils import dup_filter
14
- from urllib.parse import unquote
15
- from typing import List, Optional, Dict
16
- from .patterns import _ChannelPatterns as Patterns
17
-
18
-
19
- class Channel:
20
-
21
- _HEAD = 'https://www.youtube.com/channel/'
22
- _CUSTOM = 'https://www.youtube.com/c/'
23
- _USER = 'https://www.youtube.com/'
24
-
25
- def __init__(self, channel_id: str):
26
- """
27
- Represents a YouTube channel
28
-
29
- Parameters
30
- ----------
31
- channel_id : str
32
- The id or url or custom url or user id of the channel
33
- """
34
- pattern = re.compile("UC(.+)|c/(.+)|@(.+)")
35
- results = pattern.findall(channel_id)
36
- if not results:
37
- self._usable_id = channel_id
38
- self._target_url = self._CUSTOM + channel_id
39
- elif results[0][0]:
40
- self._usable_id = results[0][0]
41
- self._target_url = self._HEAD + 'UC' + results[0][0]
42
- elif results[0][1]:
43
- self._usable_id = results[0][1]
44
- self._target_url = self._CUSTOM + results[0][1]
45
- elif results[0][2]:
46
- self._usable_id = results[0][2]
47
- self._target_url = self._USER + '@' + results[0][2]
48
- self.id = None
49
- self.name = None
50
- self.subscribers = None
51
- self.views = None
52
- self.country = None
53
- self.custom_url = None
54
- self.avatar = None
55
- self.banner = None
56
- self.url = None
57
- self.description = None
58
- self.socials = None
59
- self.__meta = None
60
- self._about_page = channel_about(self._target_url)
61
- self.__populate()
62
-
63
- def __populate(self):
64
- self.__meta = self.__prepare_metadata()
65
- for k, v in self.__meta.items():
66
- setattr(self, k, v)
67
-
68
- def __repr__(self):
69
- return f'<Channel `{self._target_url}`>'
70
-
71
- def __prepare_metadata(self) -> Optional[Dict[str, any]]:
72
- """
73
- Returns channel metadata in a dict format
74
-
75
- Returns
76
- -------
77
- Dict
78
- Channel metadata containing the following keys:
79
- id, name, subscribers, views, country, custom_url, avatar, banner, url, description, socials
80
- """
81
- patterns = [
82
- Patterns.name,
83
- Patterns.avatar,
84
- Patterns.banner,
85
- Patterns.verified,
86
- Patterns.socials
87
- ]
88
- extracted = collect(lambda x: x.findall(self._about_page) or None, patterns)
89
- name, avatar, banner, verified, socials = [e[0] if e else None for e in extracted]
90
-
91
- # Add robust error handling for info extraction
92
- info_pattern = re.compile("\\[{\"aboutChannelRenderer\":(.*?)],")
93
- info_match = info_pattern.search(self._about_page)
94
-
95
- if not info_match:
96
- # Fallback metadata for search results or incomplete channel data
97
- return {
98
- "id": self._usable_id,
99
- "name": name,
100
- "url": self._target_url,
101
- "description": None,
102
- "country": None,
103
- "custom_url": None,
104
- "subscribers": None,
105
- "views": None,
106
- "created_at": None,
107
- "video_count": None,
108
- "avatar": avatar,
109
- "banner": banner,
110
- "verified": bool(verified),
111
- "socials": unquote(socials) if socials is not None else None
112
- }
113
-
114
- try:
115
- info_str = info_match.group(1) + "]}}}}"
116
- info = json.loads(info_str)["metadata"]["aboutChannelViewModel"]
117
-
118
- return {
119
- "id": info.get("channelId", self._usable_id),
120
- "name": name,
121
- "url": "https://www.youtube.com/channel/" + info.get("channelId", self._usable_id),
122
- "description": info.get("description"),
123
- "country": info.get("country"),
124
- "custom_url": info.get("canonicalChannelUrl"),
125
- "subscribers": info.get("subscriberCountText", "").split(' ')[0] if "subscriberCountText" in info else None,
126
- "views": info.get("viewCountText", "").replace(' views', '') if "viewCountText" in info else None,
127
- "created_at": info.get("joinedDateText", {}).get("content", "").replace('Joined ', '') if "joinedDateText" in info else None,
128
- "video_count": info.get("videoCountText", "").split(' ')[0] if "videoCountText" in info else None,
129
- "avatar": avatar,
130
- "banner": banner,
131
- "verified": bool(verified),
132
- "socials": unquote(socials) if socials is not None else None
133
- }
134
- except (KeyError, json.JSONDecodeError):
135
- # Fallback if JSON parsing fails
136
- return {
137
- "id": self._usable_id,
138
- "name": name,
139
- "url": self._target_url,
140
- "description": None,
141
- "country": None,
142
- "custom_url": None,
143
- "subscribers": None,
144
- "views": None,
145
- "created_at": None,
146
- "video_count": None,
147
- "avatar": avatar,
148
- "banner": banner,
149
- "verified": bool(verified),
150
- "socials": unquote(socials) if socials is not None else None
151
- }
152
-
153
- @property
154
- def metadata(self) -> Optional[Dict[str, any]]:
155
- """
156
- Returns channel metadata in a dict format
157
-
158
- Returns
159
- -------
160
- Dict
161
- Channel metadata containing the following keys:
162
- id, name, subscribers, views, country, custom_url, avatar, banner, url, description, socials etc.
163
- """
164
- return self.__meta
165
-
166
- @property
167
- def live(self) -> bool:
168
- """
169
- Checks if the channel is live
170
-
171
- Returns
172
- -------
173
- bool
174
- True if the channel is live
175
- """
176
- return bool(self.current_streams)
177
-
178
- @property
179
- def streaming_now(self) -> Optional[str]:
180
- """
181
- Fetches the id of currently streaming video
182
-
183
- Returns
184
- -------
185
- str | None
186
- The id of the currently streaming video or None
187
- """
188
- streams = self.current_streams
189
- return streams[0] if streams else None
190
-
191
- @property
192
- def current_streams(self) -> Optional[List[str]]:
193
- """
194
- Fetches the ids of all ongoing streams
195
-
196
- Returns
197
- -------
198
- List[str] | None
199
- The ids of all ongoing streams or None
200
- """
201
- raw = streams_data(self._target_url)
202
- filtered_ids = dup_filter(Patterns.stream_ids.findall(raw))
203
- if not filtered_ids:
204
- return None
205
- return [id_ for id_ in filtered_ids if f"vi/{id_}/hqdefault_live.jpg" in raw]
206
-
207
- @property
208
- def old_streams(self) -> Optional[List[str]]:
209
- """
210
- Fetches the ids of all old or completed streams
211
-
212
- Returns
213
- -------
214
- List[str] | None
215
- The ids of all old or completed streams or None
216
- """
217
- raw = streams_data(self._target_url)
218
- filtered_ids = dup_filter(Patterns.stream_ids.findall(raw))
219
- if not filtered_ids:
220
- return None
221
- return [id_ for id_ in filtered_ids if f"vi/{id_}/hqdefault_live.jpg" not in raw]
222
-
223
- @property
224
- def last_streamed(self) -> Optional[str]:
225
- """
226
- Fetches the id of the last completed livestream
227
-
228
- Returns
229
- -------
230
- str | None
231
- The id of the last livestreamed video or None
232
- """
233
- ids = self.old_streams
234
- return ids[0] if ids else None
235
-
236
- def uploads(self, limit: int = 20) -> Optional[List[str]]:
237
- """
238
- Fetches the ids of all uploaded videos
239
-
240
- Parameters
241
- ----------
242
- limit : int
243
- The number of videos to fetch, defaults to 20
244
-
245
- Returns
246
- -------
247
- List[str] | None
248
- The ids of uploaded videos or None
249
- """
250
- return dup_filter(Patterns.upload_ids.findall(uploads_data(self._target_url)), limit)
251
-
252
- @property
253
- def last_uploaded(self) -> Optional[str]:
254
- """
255
- Fetches the id of the last uploaded video
256
-
257
- Returns
258
- -------
259
- str | None
260
- The id of the last uploaded video or None
261
- """
262
- ids = self.uploads()
263
- return ids[0] if ids else None
264
-
265
- @property
266
- def upcoming(self) -> Optional[Video]:
267
- """
268
- Fetches the upcoming video
269
-
270
- Returns
271
- -------
272
- Video | None
273
- The upcoming video or None
274
- """
275
- raw = upcoming_videos(self._target_url)
276
- if not Patterns.upcoming_check.search(raw):
277
- return None
278
- upcoming = Patterns.upcoming.findall(raw)
279
- return Video(upcoming[0]) if upcoming else None
280
-
281
- @property
282
- def upcomings(self) -> Optional[List[str]]:
283
- """
284
- Fetches the upcoming videos
285
-
286
- Returns
287
- -------
288
- List[str] | None
289
- The ids of upcoming videos or None
290
- """
291
- raw = upcoming_videos(self._target_url)
292
- if not Patterns.upcoming_check.search(raw):
293
- return None
294
- video_ids = Patterns.upcoming.findall(raw)
295
- return video_ids
296
-
297
- @property
298
- def playlists(self) -> Optional[List[str]]:
299
- """
300
- Fetches the ids of all playlists
301
-
302
- Returns
303
- -------
304
- List[str] | None
305
- The ids of all playlists or None
306
- """
307
- return dup_filter(Patterns.playlists.findall(channel_playlists(self._target_url)))
1
+ import json
2
+ import re
3
+
4
+ from .https import (
5
+ channel_about,
6
+ streams_data,
7
+ uploads_data,
8
+ channel_playlists,
9
+ upcoming_videos
10
+ )
11
+ from .video import Video
12
+ from .pool import collect
13
+ from .utils import dup_filter
14
+ from urllib.parse import unquote
15
+ from typing import List, Optional, Dict
16
+ from .patterns import _ChannelPatterns as Patterns
17
+
18
+
19
+ class Channel:
20
+
21
+ _HEAD = 'https://www.youtube.com/channel/'
22
+ _CUSTOM = 'https://www.youtube.com/c/'
23
+ _USER = 'https://www.youtube.com/'
24
+
25
+ def __init__(self, channel_id: str):
26
+ """
27
+ Represents a YouTube channel
28
+
29
+ Parameters
30
+ ----------
31
+ channel_id : str
32
+ The id or url or custom url or user id of the channel
33
+ """
34
+ pattern = re.compile("UC(.+)|c/(.+)|@(.+)")
35
+ results = pattern.findall(channel_id)
36
+ if not results:
37
+ self._usable_id = channel_id
38
+ self._target_url = self._CUSTOM + channel_id
39
+ elif results[0][0]:
40
+ self._usable_id = results[0][0]
41
+ self._target_url = self._HEAD + 'UC' + results[0][0]
42
+ elif results[0][1]:
43
+ self._usable_id = results[0][1]
44
+ self._target_url = self._CUSTOM + results[0][1]
45
+ elif results[0][2]:
46
+ self._usable_id = results[0][2]
47
+ self._target_url = self._USER + '@' + results[0][2]
48
+ self.id = None
49
+ self.name = None
50
+ self.subscribers = None
51
+ self.views = None
52
+ self.country = None
53
+ self.custom_url = None
54
+ self.avatar = None
55
+ self.banner = None
56
+ self.url = None
57
+ self.description = None
58
+ self.socials = None
59
+ self.__meta = None
60
+ self._about_page = channel_about(self._target_url)
61
+ self.__populate()
62
+
63
+ def __populate(self):
64
+ self.__meta = self.__prepare_metadata()
65
+ for k, v in self.__meta.items():
66
+ setattr(self, k, v)
67
+
68
+ def __repr__(self):
69
+ return f'<Channel `{self._target_url}`>'
70
+
71
+ def __prepare_metadata(self) -> Optional[Dict[str, any]]:
72
+ """
73
+ Returns channel metadata in a dict format
74
+
75
+ Returns
76
+ -------
77
+ Dict
78
+ Channel metadata containing the following keys:
79
+ id, name, subscribers, views, country, custom_url, avatar, banner, url, description, socials
80
+ """
81
+ patterns = [
82
+ Patterns.name,
83
+ Patterns.avatar,
84
+ Patterns.banner,
85
+ Patterns.verified,
86
+ Patterns.socials
87
+ ]
88
+ extracted = collect(lambda x: x.findall(self._about_page) or None, patterns)
89
+ name, avatar, banner, verified, socials = [e[0] if e else None for e in extracted]
90
+
91
+ # Add robust error handling for info extraction
92
+ info_pattern = re.compile("\\[{\"aboutChannelRenderer\":(.*?)],")
93
+ info_match = info_pattern.search(self._about_page)
94
+
95
+ if not info_match:
96
+ # Fallback metadata for search results or incomplete channel data
97
+ return {
98
+ "id": self._usable_id,
99
+ "name": name,
100
+ "url": self._target_url,
101
+ "description": None,
102
+ "country": None,
103
+ "custom_url": None,
104
+ "subscribers": None,
105
+ "views": None,
106
+ "created_at": None,
107
+ "video_count": None,
108
+ "avatar": avatar,
109
+ "banner": banner,
110
+ "verified": bool(verified),
111
+ "socials": unquote(socials) if socials is not None else None
112
+ }
113
+
114
+ try:
115
+ info_str = info_match.group(1) + "]}}}}"
116
+ info = json.loads(info_str)["metadata"]["aboutChannelViewModel"]
117
+
118
+ return {
119
+ "id": info.get("channelId", self._usable_id),
120
+ "name": name,
121
+ "url": "https://www.youtube.com/channel/" + info.get("channelId", self._usable_id),
122
+ "description": info.get("description"),
123
+ "country": info.get("country"),
124
+ "custom_url": info.get("canonicalChannelUrl"),
125
+ "subscribers": info.get("subscriberCountText", "").split(' ')[0] if "subscriberCountText" in info else None,
126
+ "views": info.get("viewCountText", "").replace(' views', '') if "viewCountText" in info else None,
127
+ "created_at": info.get("joinedDateText", {}).get("content", "").replace('Joined ', '') if "joinedDateText" in info else None,
128
+ "video_count": info.get("videoCountText", "").split(' ')[0] if "videoCountText" in info else None,
129
+ "avatar": avatar,
130
+ "banner": banner,
131
+ "verified": bool(verified),
132
+ "socials": unquote(socials) if socials is not None else None
133
+ }
134
+ except (KeyError, json.JSONDecodeError):
135
+ # Fallback if JSON parsing fails
136
+ return {
137
+ "id": self._usable_id,
138
+ "name": name,
139
+ "url": self._target_url,
140
+ "description": None,
141
+ "country": None,
142
+ "custom_url": None,
143
+ "subscribers": None,
144
+ "views": None,
145
+ "created_at": None,
146
+ "video_count": None,
147
+ "avatar": avatar,
148
+ "banner": banner,
149
+ "verified": bool(verified),
150
+ "socials": unquote(socials) if socials is not None else None
151
+ }
152
+
153
+ @property
154
+ def metadata(self) -> Optional[Dict[str, any]]:
155
+ """
156
+ Returns channel metadata in a dict format
157
+
158
+ Returns
159
+ -------
160
+ Dict
161
+ Channel metadata containing the following keys:
162
+ id, name, subscribers, views, country, custom_url, avatar, banner, url, description, socials etc.
163
+ """
164
+ return self.__meta
165
+
166
+ @property
167
+ def live(self) -> bool:
168
+ """
169
+ Checks if the channel is live
170
+
171
+ Returns
172
+ -------
173
+ bool
174
+ True if the channel is live
175
+ """
176
+ return bool(self.current_streams)
177
+
178
+ @property
179
+ def streaming_now(self) -> Optional[str]:
180
+ """
181
+ Fetches the id of currently streaming video
182
+
183
+ Returns
184
+ -------
185
+ str | None
186
+ The id of the currently streaming video or None
187
+ """
188
+ streams = self.current_streams
189
+ return streams[0] if streams else None
190
+
191
+ @property
192
+ def current_streams(self) -> Optional[List[str]]:
193
+ """
194
+ Fetches the ids of all ongoing streams
195
+
196
+ Returns
197
+ -------
198
+ List[str] | None
199
+ The ids of all ongoing streams or None
200
+ """
201
+ raw = streams_data(self._target_url)
202
+ filtered_ids = dup_filter(Patterns.stream_ids.findall(raw))
203
+ if not filtered_ids:
204
+ return None
205
+ return [id_ for id_ in filtered_ids if f"vi/{id_}/hqdefault_live.jpg" in raw]
206
+
207
+ @property
208
+ def old_streams(self) -> Optional[List[str]]:
209
+ """
210
+ Fetches the ids of all old or completed streams
211
+
212
+ Returns
213
+ -------
214
+ List[str] | None
215
+ The ids of all old or completed streams or None
216
+ """
217
+ raw = streams_data(self._target_url)
218
+ filtered_ids = dup_filter(Patterns.stream_ids.findall(raw))
219
+ if not filtered_ids:
220
+ return None
221
+ return [id_ for id_ in filtered_ids if f"vi/{id_}/hqdefault_live.jpg" not in raw]
222
+
223
+ @property
224
+ def last_streamed(self) -> Optional[str]:
225
+ """
226
+ Fetches the id of the last completed livestream
227
+
228
+ Returns
229
+ -------
230
+ str | None
231
+ The id of the last livestreamed video or None
232
+ """
233
+ ids = self.old_streams
234
+ return ids[0] if ids else None
235
+
236
+ def uploads(self, limit: int = 20) -> Optional[List[str]]:
237
+ """
238
+ Fetches the ids of all uploaded videos
239
+
240
+ Parameters
241
+ ----------
242
+ limit : int
243
+ The number of videos to fetch, defaults to 20
244
+
245
+ Returns
246
+ -------
247
+ List[str] | None
248
+ The ids of uploaded videos or None
249
+ """
250
+ return dup_filter(Patterns.upload_ids.findall(uploads_data(self._target_url)), limit)
251
+
252
+ @property
253
+ def last_uploaded(self) -> Optional[str]:
254
+ """
255
+ Fetches the id of the last uploaded video
256
+
257
+ Returns
258
+ -------
259
+ str | None
260
+ The id of the last uploaded video or None
261
+ """
262
+ ids = self.uploads()
263
+ return ids[0] if ids else None
264
+
265
+ @property
266
+ def upcoming(self) -> Optional[Video]:
267
+ """
268
+ Fetches the upcoming video
269
+
270
+ Returns
271
+ -------
272
+ Video | None
273
+ The upcoming video or None
274
+ """
275
+ raw = upcoming_videos(self._target_url)
276
+ if not Patterns.upcoming_check.search(raw):
277
+ return None
278
+ upcoming = Patterns.upcoming.findall(raw)
279
+ return Video(upcoming[0]) if upcoming else None
280
+
281
+ @property
282
+ def upcomings(self) -> Optional[List[str]]:
283
+ """
284
+ Fetches the upcoming videos
285
+
286
+ Returns
287
+ -------
288
+ List[str] | None
289
+ The ids of upcoming videos or None
290
+ """
291
+ raw = upcoming_videos(self._target_url)
292
+ if not Patterns.upcoming_check.search(raw):
293
+ return None
294
+ video_ids = Patterns.upcoming.findall(raw)
295
+ return video_ids
296
+
297
+ @property
298
+ def playlists(self) -> Optional[List[str]]:
299
+ """
300
+ Fetches the ids of all playlists
301
+
302
+ Returns
303
+ -------
304
+ List[str] | None
305
+ The ids of all playlists or None
306
+ """
307
+ return dup_filter(Patterns.playlists.findall(channel_playlists(self._target_url)))