udemy-userAPI 0.3.5__py3-none-any.whl → 0.3.7__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- udemy_userAPI/__version__.py +1 -1
- udemy_userAPI/api.py +139 -39
- udemy_userAPI/authenticate.py +1 -1
- udemy_userAPI/bultins.py +243 -56
- udemy_userAPI/sections.py +2 -1
- udemy_userAPI/udemy.py +13 -4
- {udemy_userAPI-0.3.5.dist-info → udemy_userAPI-0.3.7.dist-info}/METADATA +13 -3
- udemy_userAPI-0.3.7.dist-info/RECORD +17 -0
- {udemy_userAPI-0.3.5.dist-info → udemy_userAPI-0.3.7.dist-info}/WHEEL +1 -1
- animation_consoles/__init__.py +0 -1
- animation_consoles/animation.py +0 -64
- ffmpeg_for_python/__config__.py +0 -118
- ffmpeg_for_python/__init__.py +0 -8
- ffmpeg_for_python/__utils.py +0 -78
- ffmpeg_for_python/__version__.py +0 -6
- ffmpeg_for_python/exeptions.py +0 -91
- ffmpeg_for_python/ffmpeg.py +0 -203
- m3u8_analyzer/M3u8Analyzer.py +0 -807
- m3u8_analyzer/__init__.py +0 -7
- m3u8_analyzer/__version__.py +0 -1
- m3u8_analyzer/exeptions.py +0 -82
- udemy_userAPI-0.3.5.dist-info/RECORD +0 -29
- {udemy_userAPI-0.3.5.dist-info → udemy_userAPI-0.3.7.dist-info}/LICENSE +0 -0
- {udemy_userAPI-0.3.5.dist-info → udemy_userAPI-0.3.7.dist-info}/top_level.txt +0 -0
udemy_userAPI/bultins.py
CHANGED
@@ -1,11 +1,8 @@
|
|
1
|
-
import json
|
2
1
|
from typing import Any
|
3
|
-
import
|
4
|
-
from .api import get_links, remove_tag, parser_chapers, extract_files, HEADERS_USER, assets_infor, get_add_files, \
|
5
|
-
get_files_aule, get_external_liks, extract, get_pssh, organize_streams, get_mpd_file, get_highest_resolution
|
6
|
-
from .sections import get_course_infor
|
7
|
-
from .mpd_analyzer import MPDParser
|
2
|
+
from .api import *
|
8
3
|
from .exeptions import LoginException
|
4
|
+
from .mpd_analyzer import MPDParser
|
5
|
+
from .sections import get_course_infor
|
9
6
|
|
10
7
|
|
11
8
|
class DRM:
|
@@ -108,6 +105,171 @@ class Files:
|
|
108
105
|
download_urls.append(dt_file)
|
109
106
|
return download_urls
|
110
107
|
|
108
|
+
class Quiz:
|
109
|
+
"""Representa um quiz.
|
110
|
+
"""
|
111
|
+
|
112
|
+
def __init__(self, quiz_data: dict):
|
113
|
+
"""
|
114
|
+
Inicializa uma instância de Quiz.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
quiz_data (dict): Dados do quiz.
|
118
|
+
"""
|
119
|
+
self._data = quiz_data
|
120
|
+
|
121
|
+
@property
|
122
|
+
def id(self) -> int:
|
123
|
+
"""Retorna o ID do quiz."""
|
124
|
+
return self._data.get('id', 0)
|
125
|
+
|
126
|
+
@property
|
127
|
+
def title(self) -> str:
|
128
|
+
"""Retorna o título do quiz."""
|
129
|
+
return self._data.get('title', '')
|
130
|
+
|
131
|
+
@property
|
132
|
+
def type_quiz(self) -> str:
|
133
|
+
"""Retorna o tipo de quiz (exame ou prática)."""
|
134
|
+
return self._data.get('type', '')
|
135
|
+
|
136
|
+
@property
|
137
|
+
def description(self) -> str:
|
138
|
+
"""Retorna a descrição do quiz."""
|
139
|
+
return remove_tag(self._data.get('description', ''))
|
140
|
+
|
141
|
+
@property
|
142
|
+
def duration(self) -> int:
|
143
|
+
"""Retorna a duração do quiz em minutos, se aplicável."""
|
144
|
+
duration: int = self._data.get('duration', 1)
|
145
|
+
if duration > 1:
|
146
|
+
return int(duration / 60)
|
147
|
+
else:
|
148
|
+
return 0
|
149
|
+
|
150
|
+
@property
|
151
|
+
def pass_percent(self) -> int:
|
152
|
+
"""Retorna a porcentagem necessária para passar."""
|
153
|
+
return self._data.get('pass_percent', 0)
|
154
|
+
|
155
|
+
@property
|
156
|
+
def num_assessments(self) -> int:
|
157
|
+
"""Retorna o número de perguntas do quiz."""
|
158
|
+
return self._data.get('num_assessments', 0)
|
159
|
+
|
160
|
+
def content(self) -> dict:
|
161
|
+
"""Obtém o conteúdo do quiz."""
|
162
|
+
htmls = get_quizzes(lecture_id=self.id)
|
163
|
+
return htmls
|
164
|
+
|
165
|
+
|
166
|
+
class Caption:
|
167
|
+
"""Representa uma legenda."""
|
168
|
+
|
169
|
+
def __init__(self, caption: dict):
|
170
|
+
"""
|
171
|
+
Inicializa uma instância de Caption.
|
172
|
+
|
173
|
+
Args:
|
174
|
+
caption (dict): Dados da legenda.
|
175
|
+
"""
|
176
|
+
self._caption = caption
|
177
|
+
|
178
|
+
@property
|
179
|
+
def locale(self) -> str:
|
180
|
+
"""Retorna o idioma."""
|
181
|
+
return self._caption.get('video_label', '')
|
182
|
+
|
183
|
+
@property
|
184
|
+
def status(self) -> str:
|
185
|
+
"""Retorna o status da legenda 1 ou 0"""
|
186
|
+
return self._caption.get('status')
|
187
|
+
|
188
|
+
@property
|
189
|
+
def title(self) -> str:
|
190
|
+
"""Retorna o título da legenda."""
|
191
|
+
return self._caption.get('title', '')
|
192
|
+
|
193
|
+
@property
|
194
|
+
def created(self) -> str:
|
195
|
+
"""Retorna a data de criação da legenda."""
|
196
|
+
return self._caption.get('created', '')
|
197
|
+
|
198
|
+
@property
|
199
|
+
def id(self) -> int:
|
200
|
+
"""Retorna o ID da legenda."""
|
201
|
+
return self._caption.get('id', 0)
|
202
|
+
|
203
|
+
@property
|
204
|
+
def url(self) -> str:
|
205
|
+
"""Retorna a URL da legenda."""
|
206
|
+
return self._caption.get('url', '')
|
207
|
+
|
208
|
+
@property
|
209
|
+
def content(self) -> str:
|
210
|
+
"""Obtém o conteúdo da legenda."""
|
211
|
+
if self.url:
|
212
|
+
r = requests.get(headers=HEADERS_USER, url=self.url)
|
213
|
+
if r.status_code == 200:
|
214
|
+
return r.text
|
215
|
+
else:
|
216
|
+
raise ConnectionError(
|
217
|
+
f'status_code: {r.status_code}, Não foi possível obter o conteúdo da legenda!'
|
218
|
+
)
|
219
|
+
else:
|
220
|
+
raise FileNotFoundError(
|
221
|
+
'Não foi possível obter a URL da legenda!'
|
222
|
+
)
|
223
|
+
|
224
|
+
class Captions:
|
225
|
+
"""Gerencia as legendas de um vídeo."""
|
226
|
+
|
227
|
+
def __init__(self, caption_data: list):
|
228
|
+
"""
|
229
|
+
Inicializa uma instância de Captions.
|
230
|
+
|
231
|
+
Args:
|
232
|
+
caption_data (list): Dados das legendas.
|
233
|
+
"""
|
234
|
+
self._caption_data = caption_data
|
235
|
+
|
236
|
+
def languages(self) -> list[dict]:
|
237
|
+
"""Retorna a lista de idiomas disponíveis na aula."""
|
238
|
+
langs = []
|
239
|
+
for caption in self._caption_data:
|
240
|
+
locale_id = caption.get('locale_id', '')
|
241
|
+
video_label = caption.get('video_label','')
|
242
|
+
if locale_id:
|
243
|
+
langs.append({'locale_id': locale_id,'locale':video_label})
|
244
|
+
return langs
|
245
|
+
|
246
|
+
def get_lang(self, locale_id: str) -> Caption:
|
247
|
+
"""
|
248
|
+
Obtém a legenda para o idioma especificado.
|
249
|
+
|
250
|
+
|
251
|
+
Args:
|
252
|
+
locale_id (str): ID do idioma,pode ser obtido no método -> 'languages'
|
253
|
+
|
254
|
+
Returns:
|
255
|
+
Caption: Objeto Caption.
|
256
|
+
|
257
|
+
Raises:
|
258
|
+
FileNotFoundError: Se o idioma não estiver disponível na aula.
|
259
|
+
"""
|
260
|
+
is_t = False
|
261
|
+
cpt = {}
|
262
|
+
for caption in self._caption_data:
|
263
|
+
if locale_id == caption.get('locale_id'):
|
264
|
+
is_t = True
|
265
|
+
cpt = caption
|
266
|
+
if not is_t:
|
267
|
+
raise FileNotFoundError(
|
268
|
+
'Esse idioma não está disponível nessa aula!'
|
269
|
+
)
|
270
|
+
c = Caption(caption=cpt)
|
271
|
+
return c
|
272
|
+
|
111
273
|
|
112
274
|
class Lecture:
|
113
275
|
"""Cria objetos aula (lecture) do curso e extrai os dados."""
|
@@ -166,8 +328,8 @@ class Lecture:
|
|
166
328
|
"""
|
167
329
|
thumbnail_sprite = self.__asset.get('thumbnail_sprite', {})
|
168
330
|
return {
|
169
|
-
'thumbnail_vtt_url': thumbnail_sprite.get('vtt_url'),
|
170
|
-
'thumbnail_img_url': thumbnail_sprite.get('img_url')
|
331
|
+
'thumbnail_vtt_url': thumbnail_sprite.get('vtt_url',[]),
|
332
|
+
'thumbnail_img_url': thumbnail_sprite.get('img_url',[])
|
171
333
|
}
|
172
334
|
|
173
335
|
@property
|
@@ -178,7 +340,7 @@ class Lecture:
|
|
178
340
|
Returns:
|
179
341
|
str: O tipo de asset.
|
180
342
|
"""
|
181
|
-
return self.__asset.get('asset_type', '
|
343
|
+
return self.__asset.get('asset_type', '') or self.__data.get('_class','').replace('quiz','Quiz')
|
182
344
|
|
183
345
|
@property
|
184
346
|
def get_media_sources(self) -> list:
|
@@ -188,18 +350,23 @@ class Lecture:
|
|
188
350
|
Returns:
|
189
351
|
list: Uma lista contendo as fontes de mídia.
|
190
352
|
"""
|
191
|
-
return self.__asset.get('media_sources')
|
353
|
+
return self.__asset.get('media_sources',[])
|
192
354
|
|
193
355
|
@property
|
194
|
-
def get_captions(self) ->
|
356
|
+
def get_captions(self) -> Captions:
|
195
357
|
"""
|
196
358
|
Obtém as legendas.
|
197
359
|
|
198
360
|
Returns:
|
199
|
-
|
200
|
-
"""
|
201
|
-
|
202
|
-
|
361
|
+
Captions: Objeto para gerenciar as legendas.
|
362
|
+
"""
|
363
|
+
if self.__asset.get('captions',[]):
|
364
|
+
c = Captions(caption_data=self.__asset.get('captions',[]))
|
365
|
+
return c
|
366
|
+
else:
|
367
|
+
raise FileNotFoundError(
|
368
|
+
'Não foi encontrada legendas nessa aula!'
|
369
|
+
)
|
203
370
|
@property
|
204
371
|
def get_external_url(self) -> list:
|
205
372
|
"""
|
@@ -208,7 +375,7 @@ class Lecture:
|
|
208
375
|
Returns:
|
209
376
|
list: Uma lista contendo os links externos.
|
210
377
|
"""
|
211
|
-
return self.__asset.get('external_url')
|
378
|
+
return self.__asset.get('external_url',[])
|
212
379
|
|
213
380
|
@property
|
214
381
|
def get_media_license_token(self) -> str:
|
@@ -218,7 +385,7 @@ class Lecture:
|
|
218
385
|
Returns:
|
219
386
|
str: O token de acesso à aula.
|
220
387
|
"""
|
221
|
-
return self.__asset.get('media_license_token')
|
388
|
+
return self.__asset.get('media_license_token','')
|
222
389
|
|
223
390
|
def course_is_drmed(self) -> DRM:
|
224
391
|
"""
|
@@ -227,12 +394,19 @@ class Lecture:
|
|
227
394
|
Returns:
|
228
395
|
DRM: O objeto DRM contendo as keys da aula ou None.
|
229
396
|
"""
|
230
|
-
|
231
|
-
d = DRM(license_token=self.get_media_license_token,
|
397
|
+
d = DRM(license_token=self.get_media_license_token,
|
232
398
|
get_media_sources=self.get_media_sources)
|
233
|
-
|
234
|
-
|
235
|
-
|
399
|
+
return d
|
400
|
+
|
401
|
+
def quiz_object(self) ->Quiz:
|
402
|
+
"""se for um quiz ele retorna um objeto Quiz"""
|
403
|
+
if self.get_asset_type.lower() == 'quiz':
|
404
|
+
q =Quiz(get_assessments(lecture_id=self.get_lecture_id,course_id=self.__course_id))
|
405
|
+
return q
|
406
|
+
else:
|
407
|
+
raise UserWarning(
|
408
|
+
'Atenção essa aula não é um Quiz!'
|
409
|
+
)
|
236
410
|
|
237
411
|
@property
|
238
412
|
def get_download_urls(self) -> list:
|
@@ -242,7 +416,7 @@ class Lecture:
|
|
242
416
|
Returns:
|
243
417
|
list: Uma lista contendo as URLs de download.
|
244
418
|
"""
|
245
|
-
return self.__asset.get('download_urls')
|
419
|
+
return self.__asset.get('download_urls',[])
|
246
420
|
|
247
421
|
@property
|
248
422
|
def get_slide_urls(self) -> list:
|
@@ -252,7 +426,7 @@ class Lecture:
|
|
252
426
|
Returns:
|
253
427
|
list: Uma lista contendo as URLs de slides.
|
254
428
|
"""
|
255
|
-
return self.__asset.get('slide_urls')
|
429
|
+
return self.__asset.get('slide_urls',[])
|
256
430
|
|
257
431
|
@property
|
258
432
|
def get_slides(self) -> list:
|
@@ -262,7 +436,7 @@ class Lecture:
|
|
262
436
|
Returns:
|
263
437
|
list: Uma lista contendo os slides.
|
264
438
|
"""
|
265
|
-
return self.__asset.get('slides')
|
439
|
+
return self.__asset.get('slides',[])
|
266
440
|
|
267
441
|
@property
|
268
442
|
def get_articles(self):
|
@@ -272,8 +446,11 @@ class Lecture:
|
|
272
446
|
Returns:
|
273
447
|
Os artigos relacionados à aula.
|
274
448
|
"""
|
275
|
-
|
276
|
-
|
449
|
+
if self.__asset:
|
450
|
+
d = assets_infor(course_id=self.__course_id, id_lecture=self.get_lecture_id, assets_id=self.__asset.get("id"))
|
451
|
+
return d
|
452
|
+
else:
|
453
|
+
return []
|
277
454
|
|
278
455
|
@property
|
279
456
|
def get_resources(self):
|
@@ -283,10 +460,12 @@ class Lecture:
|
|
283
460
|
Returns:
|
284
461
|
Os recursos adicionais relacionados à aula.
|
285
462
|
"""
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
463
|
+
if self.__additional_files:
|
464
|
+
files_add = get_files_aule(lecture_id_filter=self.get_lecture_id, data=self.__additional_files)
|
465
|
+
f = Files(files=files_add, id_course=self.__course_id).get_download_url
|
466
|
+
return f
|
467
|
+
else:
|
468
|
+
return []
|
290
469
|
|
291
470
|
class Course:
|
292
471
|
"""Recebe um dicionário com os dados do curso."""
|
@@ -299,8 +478,8 @@ class Course:
|
|
299
478
|
results (dict): Um dicionário contendo os dados do curso.
|
300
479
|
course_id (int): O ID do curso.
|
301
480
|
"""
|
302
|
-
self.__parser_chapers =
|
303
|
-
self.__data = self.__parser_chapers
|
481
|
+
self.__parser_chapers = parser_chapters(results=results)
|
482
|
+
self.__data:list = self.__parser_chapers
|
304
483
|
self.__course_id = course_id
|
305
484
|
self.__results = results
|
306
485
|
self.__additional_files_data = get_add_files(course_id)
|
@@ -375,8 +554,8 @@ class Course:
|
|
375
554
|
int: O número total de lectures no curso.
|
376
555
|
"""
|
377
556
|
total_lectures = 0
|
378
|
-
for chapter in self.__data
|
379
|
-
total_lectures += len(chapter.get('
|
557
|
+
for chapter in self.__data:
|
558
|
+
total_lectures += len(chapter.get('lectures', []))
|
380
559
|
return total_lectures
|
381
560
|
|
382
561
|
@property
|
@@ -398,39 +577,35 @@ class Course:
|
|
398
577
|
list: Uma lista contendo os títulos de vídeos no curso.
|
399
578
|
"""
|
400
579
|
videos = []
|
401
|
-
for chapter in self.__data
|
580
|
+
for chapter in self.__data:
|
402
581
|
for video in chapter.get('videos_in_chapter', []):
|
403
|
-
|
404
|
-
if
|
405
|
-
|
582
|
+
asset_type = video.get('asset_type')
|
583
|
+
if asset_type == 'Video':
|
584
|
+
title = video['title']
|
585
|
+
if title != "Files":
|
586
|
+
videos.append(title)
|
406
587
|
return videos
|
407
588
|
|
408
589
|
@property
|
409
590
|
def get_lectures(self) -> list:
|
410
591
|
"""
|
411
|
-
Obtém uma lista com todas as aulas.
|
412
|
-
|
592
|
+
Obtém uma lista de dicionários com todas as aulas.
|
413
593
|
Returns:
|
414
594
|
list: Uma lista contendo todas as aulas.
|
415
595
|
"""
|
416
596
|
videos = []
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
for index, video in enumerate(chapter.get('videos_in_chapter', [])):
|
421
|
-
section = f"{chapter.get('title_chapter')}" # Adicionar numeração da Sessão
|
422
|
-
title = video.get('video_title')
|
423
|
-
id_lecture = video.get('lecture_id')
|
424
|
-
id_asset = video.get('asset_id')
|
597
|
+
|
598
|
+
for chapter in self.__data:
|
599
|
+
for video in chapter.get('lectures', []):
|
425
600
|
dt = {
|
426
|
-
'section':
|
427
|
-
'title': title,
|
428
|
-
'lecture_id':
|
429
|
-
'asset_id':
|
430
|
-
'
|
601
|
+
'section': chapter.get('title', ''),
|
602
|
+
'title': video.get('title', ''),
|
603
|
+
'lecture_id': video.get('lecture_id', ''),
|
604
|
+
'asset_id': video.get('asset_id', ''),
|
605
|
+
'asset_type': video.get('asset_type', '')
|
431
606
|
}
|
432
607
|
videos.append(dt)
|
433
|
-
|
608
|
+
|
434
609
|
return videos
|
435
610
|
|
436
611
|
def get_details_lecture(self, lecture_id: int) -> Lecture:
|
@@ -443,7 +618,19 @@ class Course:
|
|
443
618
|
Returns:
|
444
619
|
Lecture: Um objeto Lecture contendo os detalhes da aula.
|
445
620
|
"""
|
446
|
-
|
621
|
+
type_lecture = ''
|
622
|
+
links= {}
|
623
|
+
if not is_lecture_in_course(lecture_id=lecture_id,lectures=self.get_lectures):
|
624
|
+
raise FileNotFoundError(
|
625
|
+
'Essa aula não existe nesse curso!'
|
626
|
+
)
|
627
|
+
for l in self.get_lectures:
|
628
|
+
if lecture_id == l.get('lecture_id'):
|
629
|
+
type_lecture = l.get('asset_type')
|
630
|
+
if type_lecture.lower() == 'video' or type_lecture.lower() == 'article':
|
631
|
+
links = get_links(course_id=self.__course_id, id_lecture=lecture_id)
|
632
|
+
else:
|
633
|
+
links = get_assessments(course_id=self.__course_id,lecture_id=lecture_id)
|
447
634
|
additional_files = self.__load_assets()
|
448
635
|
lecture = Lecture(data=links, course_id=self.__course_id, additional_files=additional_files)
|
449
636
|
return lecture
|
udemy_userAPI/sections.py
CHANGED
@@ -84,7 +84,8 @@ def get_details_courses(course_id):
|
|
84
84
|
resposta = json.loads(response.text)
|
85
85
|
return resposta
|
86
86
|
else:
|
87
|
-
raise UdemyUserApiExceptions(
|
87
|
+
raise UdemyUserApiExceptions(
|
88
|
+
response.text)
|
88
89
|
|
89
90
|
|
90
91
|
def get_course_infor(course_id):
|
udemy_userAPI/udemy.py
CHANGED
@@ -19,9 +19,6 @@ class Udemy:
|
|
19
19
|
LoginException: Se a sessão estiver expirada.
|
20
20
|
"""
|
21
21
|
self.__headers = HEADERS_USER
|
22
|
-
if not verif_login:
|
23
|
-
raise LoginException("Sessão expirada!")
|
24
|
-
|
25
22
|
@staticmethod
|
26
23
|
def my_subscribed_courses_by_plan() -> list[dict]:
|
27
24
|
"""
|
@@ -33,6 +30,10 @@ class Udemy:
|
|
33
30
|
Raises:
|
34
31
|
UdemyUserApiExceptions: Se houver erro ao obter os cursos.
|
35
32
|
"""
|
33
|
+
if not verif_login:
|
34
|
+
raise LoginException(
|
35
|
+
"Nenhuma sessão ativa,primeiro efetue login!")
|
36
|
+
|
36
37
|
try:
|
37
38
|
courses = get_courses_plan(tipe='plan')
|
38
39
|
return courses
|
@@ -50,6 +51,10 @@ class Udemy:
|
|
50
51
|
Raises:
|
51
52
|
UdemyUserApiExceptions: Se houver erro ao obter os cursos.
|
52
53
|
"""
|
54
|
+
if not verif_login:
|
55
|
+
raise LoginException(
|
56
|
+
"Nenhuma sessão ativa,primeiro efetue login!")
|
57
|
+
|
53
58
|
try:
|
54
59
|
# Obtém os cursos
|
55
60
|
courses1 = get_courses_plan(tipe='default') # lista de cursos padrão
|
@@ -72,7 +77,7 @@ class Udemy:
|
|
72
77
|
raise UnhandledExceptions(e)
|
73
78
|
|
74
79
|
@staticmethod
|
75
|
-
def get_details_course(course_id):
|
80
|
+
def get_details_course(course_id) ->Course:
|
76
81
|
"""
|
77
82
|
Obtém detalhes de um curso através do ID.
|
78
83
|
|
@@ -85,6 +90,10 @@ class Udemy:
|
|
85
90
|
Raises:
|
86
91
|
UnhandledExceptions: Se houver erro ao obter os detalhes do curso.
|
87
92
|
"""
|
93
|
+
if not verif_login:
|
94
|
+
raise LoginException(
|
95
|
+
"Nenhuma sessão ativa,primeiro efetue login!")
|
96
|
+
|
88
97
|
try:
|
89
98
|
d = get_details_courses(course_id)
|
90
99
|
b = Course(course_id=course_id, results=d)
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: udemy_userAPI
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.7
|
4
4
|
Summary: Obtenha detalhes de cursos que o usuário esteja inscrito da plataforma Udemy,usando o EndPoint de usuário o mesmo que o navegador utiliza para acessar e redenrizar os cursos.
|
5
5
|
Author: PauloCesar-dev404
|
6
6
|
Author-email: paulocesar0073dev404@gmail.com
|
@@ -15,11 +15,21 @@ License-File: LICENSE
|
|
15
15
|
Requires-Dist: requests
|
16
16
|
Requires-Dist: cloudscraper
|
17
17
|
Requires-Dist: pywidevine
|
18
|
+
Dynamic: author
|
19
|
+
Dynamic: author-email
|
20
|
+
Dynamic: description
|
21
|
+
Dynamic: description-content-type
|
22
|
+
Dynamic: keywords
|
23
|
+
Dynamic: license
|
24
|
+
Dynamic: platform
|
25
|
+
Dynamic: project-url
|
26
|
+
Dynamic: requires-dist
|
27
|
+
Dynamic: summary
|
18
28
|
|
19
29
|
# udemy-userAPI
|
20
30
|
|
21
31
|
|
22
|
-

|
23
33
|

|
24
34
|
[](https://paulocesar-dev404.github.io/me-apoiando-online/)
|
25
35
|
[](https://github.com/PauloCesar-dev404/udemy-userAPI/blob/main/docs/iniciando.md)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
udemy_userAPI/__init__.py,sha256=BPle89xE_CMTKKe_Lw6jioYLgpH-q_Lpho2S-n1PIUA,206
|
2
|
+
udemy_userAPI/__version__.py,sha256=ZlJ4BqU7L0jruy2h0iTeuMxcPub8qGB7jbO8vbwvXbY,405
|
3
|
+
udemy_userAPI/api.py,sha256=jWWwJBYS9dJDjlozUj4Yi4uc8OCAy5PILqUiRWZMiq8,28153
|
4
|
+
udemy_userAPI/authenticate.py,sha256=IJRrCjmhe_x40CrQ2KrOMNP8VvotZf0QMWsrbcLl_rw,14225
|
5
|
+
udemy_userAPI/bultins.py,sha256=s12tXjbZgyKAnYrEtZxUzuTY-aIINXlm4mpbxR-r4Io,21932
|
6
|
+
udemy_userAPI/exeptions.py,sha256=kfnPdZpqYY8nd0gnl6_Vh-MIz-XupmmbRPIuFnyXupk,692
|
7
|
+
udemy_userAPI/sections.py,sha256=eRjUUlEApwrwlcCs9GWIk580qq1UVFqR5RmOgP6y65E,5424
|
8
|
+
udemy_userAPI/udemy.py,sha256=AAXc24iAtFTyDyVehhFeSx9CyhWMeFWsbYn1J80TgFw,3270
|
9
|
+
udemy_userAPI/.cache/.udemy_userAPI,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
+
udemy_userAPI/mpd_analyzer/__init__.py,sha256=i3JVWyvcFLaj5kPmx8c1PgjsLht7OUIQQClD4yqYbo8,102
|
11
|
+
udemy_userAPI/mpd_analyzer/bin.wvd,sha256=1rAJdCc120hQlX9qe5KUS628eY2ZHYxQSmyhGNefSzo,2956
|
12
|
+
udemy_userAPI/mpd_analyzer/mpd_parser.py,sha256=PgUkHc5x8FTuXFCuYkWPZr9TaO_nsKalb02EFYl_zeA,8926
|
13
|
+
udemy_userAPI-0.3.7.dist-info/LICENSE,sha256=l4jdKYt8gSdDFOGr09vCKnMn_Im55XIcQKqTDEtFfNs,1095
|
14
|
+
udemy_userAPI-0.3.7.dist-info/METADATA,sha256=B5tTj-p8_9DEWwgarUotK9gTteg6teb_R5oeLg7HznQ,1655
|
15
|
+
udemy_userAPI-0.3.7.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
16
|
+
udemy_userAPI-0.3.7.dist-info/top_level.txt,sha256=ijTINaSDRKhdahY_X7dmSRFTxBIwQErWv9ATCG55mog,14
|
17
|
+
udemy_userAPI-0.3.7.dist-info/RECORD,,
|
animation_consoles/__init__.py
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
from .animation import AnimationConsole
|
animation_consoles/animation.py
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
import sys
|
2
|
-
import time
|
3
|
-
import threading
|
4
|
-
from colorama import init, Fore, Style
|
5
|
-
|
6
|
-
# Inicializa o suporte a cores no Windows
|
7
|
-
init(autoreset=True)
|
8
|
-
|
9
|
-
|
10
|
-
class AnimationConsole:
|
11
|
-
def __init__(self, text="Loading", color=Fore.GREEN, color_frame=Fore.BLUE):
|
12
|
-
"""
|
13
|
-
Cria uma animação de loading com uma mensagem colorida no console.
|
14
|
-
:param text: Texto inicial da mensagem de loading.
|
15
|
-
:param color: Cor do texto, usando Fore do colorama.
|
16
|
-
"""
|
17
|
-
self._color_frame = color_frame
|
18
|
-
self._text = text
|
19
|
-
self._color = color
|
20
|
-
self._running = False
|
21
|
-
self._animation_thread = None
|
22
|
-
self._frames = ["-", "\\", "|", "/"]
|
23
|
-
self._index = 0
|
24
|
-
|
25
|
-
def start(self):
|
26
|
-
"""
|
27
|
-
Inicia a animação no console.
|
28
|
-
"""
|
29
|
-
if self._running:
|
30
|
-
return # Previne múltiplas execuções
|
31
|
-
self._running = True
|
32
|
-
self._animation_thread = threading.Thread(target=self._animate, daemon=True)
|
33
|
-
self._animation_thread.start()
|
34
|
-
|
35
|
-
def stop(self):
|
36
|
-
"""
|
37
|
-
Para a animação no console.
|
38
|
-
"""
|
39
|
-
self._running = False
|
40
|
-
if self._animation_thread:
|
41
|
-
self._animation_thread.join()
|
42
|
-
sys.stdout.write("\r" + " " * (len(self._text) + 20) + "\r") # Limpa a linha
|
43
|
-
|
44
|
-
def update_message(self, new_text, new_color=None):
|
45
|
-
"""
|
46
|
-
Atualiza a mensagem exibida junto à animação.
|
47
|
-
:param new_text: Novo texto a ser exibido.
|
48
|
-
:param new_color: Nova cor para o texto (opcional).
|
49
|
-
"""
|
50
|
-
self._text = new_text
|
51
|
-
if new_color:
|
52
|
-
self._color = new_color
|
53
|
-
|
54
|
-
def _animate(self):
|
55
|
-
"""
|
56
|
-
Animação interna do console.
|
57
|
-
"""
|
58
|
-
while self._running:
|
59
|
-
frame = self._frames[self._index]
|
60
|
-
self._index = (self._index + 1) % len(self._frames)
|
61
|
-
sys.stdout.write(
|
62
|
-
f"\r{self._color}{self._text}{Style.RESET_ALL} {self._color_frame}{frame}{Style.RESET_ALL}")
|
63
|
-
sys.stdout.flush()
|
64
|
-
time.sleep(0.1)
|