udemy-userAPI 0.3.5__py3-none-any.whl → 0.3.7__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.
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)