webscout 7.1__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 (144) 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 +3 -3
  38. webscout/Provider/ChatGPTGratis.py +226 -0
  39. webscout/Provider/Cloudflare.py +3 -4
  40. webscout/Provider/DeepSeek.py +218 -0
  41. webscout/Provider/Deepinfra.py +3 -3
  42. webscout/Provider/Free2GPT.py +131 -124
  43. webscout/Provider/Gemini.py +100 -115
  44. webscout/Provider/Glider.py +3 -3
  45. webscout/Provider/Groq.py +5 -1
  46. webscout/Provider/Jadve.py +3 -3
  47. webscout/Provider/Marcus.py +191 -192
  48. webscout/Provider/Netwrck.py +3 -3
  49. webscout/Provider/PI.py +2 -2
  50. webscout/Provider/PizzaGPT.py +2 -3
  51. webscout/Provider/QwenLM.py +311 -0
  52. webscout/Provider/TTI/AiForce/__init__.py +22 -22
  53. webscout/Provider/TTI/AiForce/async_aiforce.py +257 -257
  54. webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -242
  55. webscout/Provider/TTI/Nexra/__init__.py +22 -22
  56. webscout/Provider/TTI/Nexra/async_nexra.py +286 -286
  57. webscout/Provider/TTI/Nexra/sync_nexra.py +258 -258
  58. webscout/Provider/TTI/PollinationsAI/__init__.py +23 -23
  59. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -330
  60. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -285
  61. webscout/Provider/TTI/artbit/__init__.py +22 -22
  62. webscout/Provider/TTI/artbit/async_artbit.py +184 -184
  63. webscout/Provider/TTI/artbit/sync_artbit.py +176 -176
  64. webscout/Provider/TTI/blackbox/__init__.py +4 -4
  65. webscout/Provider/TTI/blackbox/async_blackbox.py +212 -212
  66. webscout/Provider/TTI/blackbox/sync_blackbox.py +199 -199
  67. webscout/Provider/TTI/deepinfra/__init__.py +4 -4
  68. webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -227
  69. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -199
  70. webscout/Provider/TTI/huggingface/__init__.py +22 -22
  71. webscout/Provider/TTI/huggingface/async_huggingface.py +199 -199
  72. webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -195
  73. webscout/Provider/TTI/imgninza/__init__.py +4 -4
  74. webscout/Provider/TTI/imgninza/async_ninza.py +214 -214
  75. webscout/Provider/TTI/imgninza/sync_ninza.py +209 -209
  76. webscout/Provider/TTI/talkai/__init__.py +4 -4
  77. webscout/Provider/TTI/talkai/async_talkai.py +229 -229
  78. webscout/Provider/TTI/talkai/sync_talkai.py +207 -207
  79. webscout/Provider/TTS/deepgram.py +182 -182
  80. webscout/Provider/TTS/elevenlabs.py +136 -136
  81. webscout/Provider/TTS/gesserit.py +150 -150
  82. webscout/Provider/TTS/murfai.py +138 -138
  83. webscout/Provider/TTS/parler.py +133 -134
  84. webscout/Provider/TTS/streamElements.py +360 -360
  85. webscout/Provider/TTS/utils.py +280 -280
  86. webscout/Provider/TTS/voicepod.py +116 -116
  87. webscout/Provider/TextPollinationsAI.py +2 -3
  88. webscout/Provider/WiseCat.py +193 -0
  89. webscout/Provider/__init__.py +144 -134
  90. webscout/Provider/cerebras.py +242 -227
  91. webscout/Provider/chatglm.py +204 -204
  92. webscout/Provider/dgaf.py +2 -3
  93. webscout/Provider/gaurish.py +2 -3
  94. webscout/Provider/geminiapi.py +208 -208
  95. webscout/Provider/granite.py +223 -0
  96. webscout/Provider/hermes.py +218 -218
  97. webscout/Provider/llama3mitril.py +179 -179
  98. webscout/Provider/llamatutor.py +3 -3
  99. webscout/Provider/llmchat.py +2 -3
  100. webscout/Provider/meta.py +794 -794
  101. webscout/Provider/multichat.py +331 -331
  102. webscout/Provider/typegpt.py +359 -359
  103. webscout/Provider/yep.py +2 -2
  104. webscout/__main__.py +5 -5
  105. webscout/cli.py +319 -319
  106. webscout/conversation.py +241 -242
  107. webscout/exceptions.py +328 -328
  108. webscout/litagent/__init__.py +28 -28
  109. webscout/litagent/agent.py +2 -3
  110. webscout/litprinter/__init__.py +0 -58
  111. webscout/scout/__init__.py +8 -8
  112. webscout/scout/core.py +884 -884
  113. webscout/scout/element.py +459 -459
  114. webscout/scout/parsers/__init__.py +69 -69
  115. webscout/scout/parsers/html5lib_parser.py +172 -172
  116. webscout/scout/parsers/html_parser.py +236 -236
  117. webscout/scout/parsers/lxml_parser.py +178 -178
  118. webscout/scout/utils.py +38 -38
  119. webscout/swiftcli/__init__.py +811 -811
  120. webscout/update_checker.py +2 -12
  121. webscout/version.py +1 -1
  122. webscout/webscout_search.py +5 -4
  123. webscout/zeroart/__init__.py +54 -54
  124. webscout/zeroart/base.py +60 -60
  125. webscout/zeroart/effects.py +99 -99
  126. webscout/zeroart/fonts.py +816 -816
  127. {webscout-7.1.dist-info → webscout-7.2.dist-info}/METADATA +4 -3
  128. webscout-7.2.dist-info/RECORD +217 -0
  129. webstoken/__init__.py +30 -30
  130. webstoken/classifier.py +189 -189
  131. webstoken/keywords.py +216 -216
  132. webstoken/language.py +128 -128
  133. webstoken/ner.py +164 -164
  134. webstoken/normalizer.py +35 -35
  135. webstoken/processor.py +77 -77
  136. webstoken/sentiment.py +206 -206
  137. webstoken/stemmer.py +73 -73
  138. webstoken/tagger.py +60 -60
  139. webstoken/tokenizer.py +158 -158
  140. webscout-7.1.dist-info/RECORD +0 -198
  141. {webscout-7.1.dist-info → webscout-7.2.dist-info}/LICENSE.md +0 -0
  142. {webscout-7.1.dist-info → webscout-7.2.dist-info}/WHEEL +0 -0
  143. {webscout-7.1.dist-info → webscout-7.2.dist-info}/entry_points.txt +0 -0
  144. {webscout-7.1.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)))