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/bultins.py CHANGED
@@ -1,11 +1,8 @@
1
- import json
2
1
  from typing import Any
3
- import requests
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', 'Undefined')
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) -> list:
356
+ def get_captions(self) -> Captions:
195
357
  """
196
358
  Obtém as legendas.
197
359
 
198
360
  Returns:
199
- list: Uma lista contendo as legendas.
200
- """
201
- return self.__asset.get('captions')
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
- try:
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
- return d
234
- except Exception as e:
235
- DeprecationWarning(e)
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
- d = assets_infor(course_id=self.__course_id, id_lecture=self.get_lecture_id, assets_id=self.__asset.get("id"))
276
- return d
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
- files_add = get_files_aule(lecture_id_filter=self.get_lecture_id, data=self.__additional_files)
287
- f = Files(files=files_add, id_course=self.__course_id).get_download_url
288
- return f
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 = parser_chapers(results=results)
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.values():
379
- total_lectures += len(chapter.get('videos_in_chapter', []))
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.values():
580
+ for chapter in self.__data:
402
581
  for video in chapter.get('videos_in_chapter', []):
403
- title = video['video_title']
404
- if title != "Files":
405
- videos.append(title)
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
- section_order = 1 # Iniciar a numeração das seções (capítulos)
418
-
419
- for chapter in self.__data.values():
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': section,
427
- 'title': title,
428
- 'lecture_id': id_lecture,
429
- 'asset_id': id_asset,
430
- 'section_order': section_order
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
- section_order += 1 # Incrementar o número da Sessão após processar os vídeos do capítulo
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
- links = get_links(course_id=self.__course_id, id_lecture=lecture_id)
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(f"Erro ao obter detalhes do curso! Código de status: {response.status_code}")
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
1
+ Metadata-Version: 2.2
2
2
  Name: udemy_userAPI
3
- Version: 0.3.5
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
- ![Versão](https://img.shields.io/badge/version-0.3.5-orange)
32
+ ![Versão](https://img.shields.io/badge/version-0.3.7-orange)
23
33
  ![Licença](https://img.shields.io/badge/license-MIT-orange)
24
34
  [![Sponsor](https://img.shields.io/badge/💲Donate-yellow)](https://paulocesar-dev404.github.io/me-apoiando-online/)
25
35
  [![Sponsor](https://img.shields.io/badge/Documentation-green)](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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1 +0,0 @@
1
- from .animation import AnimationConsole
@@ -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)