udemy-userAPI 0.3.5__tar.gz → 0.3.7__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (25) hide show
  1. {udemy_userapi-0.3.5/udemy_userAPI.egg-info → udemy_userapi-0.3.7}/PKG-INFO +13 -3
  2. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/README.md +1 -1
  3. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/README_PYPI.md +1 -1
  4. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/__version__.py +1 -1
  5. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/api.py +139 -39
  6. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/authenticate.py +1 -1
  7. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/bultins.py +243 -56
  8. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/sections.py +2 -1
  9. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/udemy.py +13 -4
  10. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7/udemy_userAPI.egg-info}/PKG-INFO +13 -3
  11. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/LICENSE +0 -0
  12. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/MANIFEST.in +0 -0
  13. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/setup.cfg +0 -0
  14. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/setup.py +0 -0
  15. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/.cache/.udemy_userAPI +0 -0
  16. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/__init__.py +0 -0
  17. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/exeptions.py +0 -0
  18. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/mpd_analyzer/__init__.py +0 -0
  19. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/mpd_analyzer/bin.wvd +0 -0
  20. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI/mpd_analyzer/mpd_parser.py +0 -0
  21. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI.egg-info/SOURCES.txt +0 -0
  22. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI.egg-info/dependency_links.txt +0 -0
  23. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI.egg-info/not-zip-safe +0 -0
  24. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI.egg-info/requires.txt +0 -0
  25. {udemy_userapi-0.3.5 → udemy_userapi-0.3.7}/udemy_userAPI.egg-info/top_level.txt +0 -0
@@ -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)
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- ![Versão](https://img.shields.io/badge/version-0.3.5-orange)
6
+ ![Versão](https://img.shields.io/badge/version-0.3.7-orange)
7
7
  ![Licença](https://img.shields.io/badge/license-MIT-orange)
8
8
  [![Sponsor](https://img.shields.io/badge/💲Donate-yellow)](https://paulocesar-dev404.github.io/me-apoiando-online/)
9
9
  [![Sponsor](https://img.shields.io/badge/Documentation-green)](https://github.com/PauloCesar-dev404/udemy-userAPI/blob/main/docs/iniciando.md)
@@ -1,7 +1,7 @@
1
1
  # udemy-userAPI
2
2
 
3
3
 
4
- ![Versão](https://img.shields.io/badge/version-0.3.5-orange)
4
+ ![Versão](https://img.shields.io/badge/version-0.3.7-orange)
5
5
  ![Licença](https://img.shields.io/badge/license-MIT-orange)
6
6
  [![Sponsor](https://img.shields.io/badge/💲Donate-yellow)](https://paulocesar-dev404.github.io/me-apoiando-online/)
7
7
  [![Sponsor](https://img.shields.io/badge/Documentation-green)](https://github.com/PauloCesar-dev404/udemy-userAPI/blob/main/docs/iniciando.md)
@@ -1,4 +1,4 @@
1
- __version__ = '0.3.5'
1
+ __version__ = '0.3.7'
2
2
  __lib_name__ = 'udemy_userAPI' # local name
3
3
  __repo_name__ = 'udemy-userAPI'
4
4
  __autor__ = 'PauloCesar-dev404'
@@ -134,7 +134,8 @@ def extract(pssh, license_token):
134
134
  from .authenticate import UdemyAuth
135
135
  auth = UdemyAuth()
136
136
  if not auth.verif_login():
137
- raise LoginException("Sessão expirada!")
137
+ raise LoginException(
138
+ "Sessão expirada!")
138
139
  license_url = (f"https://www.udemy.com/api-2.0/media-license-server/validate-auth-token?drm_type=widevine"
139
140
  f"&auth_token={license_token}")
140
141
  session_id = cdm.open()
@@ -184,43 +185,58 @@ def get_mpd_file(mpd_url):
184
185
  raise UnhandledExceptions(f"Errro Ao Obter Mídias:{e}")
185
186
 
186
187
 
187
- def parser_chapers(results):
188
- """
189
- :param results:
190
- :return:
191
- """
192
- if not results:
193
- raise UdemyUserApiExceptions("Não foi possível obter detalhes do curso!")
194
- results = results.get('results', None)
188
+ def parser_chapters(results) -> list[dict]:
195
189
  if not results:
196
190
  raise UdemyUserApiExceptions("Não foi possível obter detalhes do curso!")
197
- chapters_dict = {} # Dicionário para armazenar os capítulos e seus vídeos correspondentes
198
191
 
199
- # Primeiro, construímos um dicionário de capítulos
200
- current_chapter = None
192
+ results = results.get('results', [])
193
+ chapters_dicts = [] # Lista que armazena todos os capítulos
194
+ current_chapter = None # Capítulo atual
195
+
201
196
  for dictionary in results:
202
197
  _class = dictionary.get('_class')
198
+ chapter_index = dictionary.get('object_index')
203
199
 
200
+ # Quando encontrar um novo capítulo
204
201
  if _class == 'chapter':
205
- chapter_index = dictionary.get('object_index')
202
+ if current_chapter: # Se já há um capítulo atual, adicionamos
203
+ chapters_dicts.append(current_chapter)
204
+
205
+ # Inicia um novo capítulo
206
206
  current_chapter = {
207
- 'title_chapter': dictionary.get('title'),
208
- 'videos_in_chapter': []
207
+ 'title': dictionary.get('title'),
208
+ 'chapter_index': chapter_index,
209
+ 'lectures': [] # Lista para armazenar aulas e quizzes
209
210
  }
210
- chapters_dict[f"chapter_{chapter_index}"] = current_chapter
211
- elif _class == 'lecture' and current_chapter is not None:
211
+
212
+ # Se for uma aula, adiciona ao capítulo atual
213
+ elif _class == 'lecture' and current_chapter:
212
214
  asset = dictionary.get('asset')
213
215
  if asset:
214
- video_title = dictionary.get('title', None)
215
- if not video_title:
216
- video_title = 'Files'
217
- current_chapter['videos_in_chapter'].append({
218
- 'video_title': video_title,
219
- 'title_lecture': dictionary.get('title'),
220
- 'lecture_id': dictionary.get('id'),
221
- 'asset_id': asset.get('id')
222
- })
223
- return chapters_dict
216
+ lecture_data = {
217
+ 'asset_type': asset.get('asset_type', ''),
218
+ 'title': dictionary.get('title', 'Files'),
219
+ 'lecture_id': dictionary.get('id', ''),
220
+ 'asset_id': asset.get('id', '')
221
+ }
222
+ current_chapter['lectures'].append(lecture_data)
223
+
224
+ # Se for um quiz, também adiciona ao capítulo atual
225
+ elif _class == 'quiz' and current_chapter:
226
+ quiz_data = {
227
+ 'asset_type': 'quiz',
228
+ 'title': dictionary.get('title', 'Quiz'),
229
+ 'lecture_id': dictionary.get('id', ''),
230
+ 'type': dictionary.get('type', ''),
231
+ 'asset_id': ''
232
+ }
233
+ current_chapter['lectures'].append(quiz_data)
234
+
235
+ # Adiciona o último capítulo processado
236
+ if current_chapter:
237
+ chapters_dicts.append(current_chapter)
238
+
239
+ return chapters_dicts
224
240
 
225
241
 
226
242
  def get_add_files(course_id: int):
@@ -304,11 +320,11 @@ def get_links(course_id: int, id_lecture: int):
304
320
  erro ao obter dados das aulas.
305
321
  """
306
322
  get = (f"https://www.udemy.com/api-2.0/users/me/subscribed-courses/{course_id}/lectures/{id_lecture}/?"
307
- f"fields[lecture]"
308
- f"=asset,description,download_url,is_free,last_watched_second&fields[asset]=asset_type,length,"
309
- f"media_license_token,course_is_drmed,media_sources,captions,thumbnail_sprite,slides,slide_urls,"
310
- f"download_urls,"
311
- f"external_url&q=0.3108014137011559/?fields[asset]=download_urls")
323
+ f"fields[lecture]"
324
+ f"=asset,description,download_url,is_free,last_watched_second&fields[asset]=asset_type,length,"
325
+ f"media_license_token,course_is_drmed,media_sources,captions,thumbnail_sprite,slides,slide_urls,"
326
+ f"download_urls,"
327
+ f"external_url&q=0.3108014137011559/?fields[asset]=download_urls")
312
328
  from .authenticate import UdemyAuth
313
329
  auth = UdemyAuth()
314
330
  if not auth.verif_login():
@@ -322,18 +338,96 @@ def get_links(course_id: int, id_lecture: int):
322
338
  a = json.loads(response.text)
323
339
  return a
324
340
  else:
325
- raise UnhandledExceptions(f"Erro ao obter dados de aulas! Código de status: {response.status_code}")
341
+ raise UnhandledExceptions(
342
+ f"Erro ao obter dados da aula! Código de status: {response.status_code}")
326
343
 
327
344
  except requests.ConnectionError as e:
328
- raise UdemyUserApiExceptions(f"Erro de conexão: {e}")
345
+ raise UdemyUserApiExceptions(
346
+ f"Erro de conexão: {e}")
329
347
  except requests.Timeout as e:
330
- raise UdemyUserApiExceptions(f"Tempo de requisição excedido: {e}")
348
+ raise UdemyUserApiExceptions(
349
+ f"Tempo de requisição excedido: {e}")
331
350
  except requests.TooManyRedirects as e:
332
- raise UdemyUserApiExceptions(f"Limite de redirecionamentos excedido: {e}")
351
+ raise UdemyUserApiExceptions(
352
+ f"Limite de redirecionamentos excedido: {e}")
333
353
  except requests.HTTPError as e:
334
- raise UdemyUserApiExceptions(f"Erro HTTP: {e}")
354
+ raise UdemyUserApiExceptions(
355
+ f"Erro HTTP: {e}")
335
356
  except Exception as e:
336
- raise UnhandledExceptions(f"Erro ao obter mídias: {e}")
357
+ raise UnhandledExceptions(
358
+ f"Erro ao obter mídias: {e}")
359
+
360
+ def get_assessments(course_id: int,lecture_id:int):
361
+ get = (f'https://www.udemy.com/api-2.0/users/me/subscribed-courses/{course_id}/quizzes/{lecture_id}/?draft='
362
+ f'false&fields[quiz]=id,type,title,description,object_index,num_assessments,version,duration,'
363
+ f'is_draft,pass_percent,changelog')
364
+ from .authenticate import UdemyAuth
365
+ auth = UdemyAuth()
366
+ if not auth.verif_login():
367
+ raise LoginException("Sessão expirada!")
368
+ try:
369
+ # Faz a solicitação GET com os cabeçalhos
370
+ response = requests.get(get, headers=HEADERS_USER)
371
+ data = []
372
+ # Exibe o código de status
373
+ if response.status_code == 200:
374
+ a = json.loads(response.text)
375
+ return a
376
+ else:
377
+ raise ConnectionError(
378
+ f"Erro ao obter dados da aula! Código de status: {response.status_code}\n"
379
+ f"{response.text}"
380
+ )
381
+
382
+ except requests.ConnectionError as e:
383
+ raise UdemyUserApiExceptions(
384
+ f"Erro de conexão: {e}")
385
+ except requests.Timeout as e:
386
+ raise UdemyUserApiExceptions(
387
+ f"Tempo de requisição excedido: {e}")
388
+ except requests.TooManyRedirects as e:
389
+ raise UdemyUserApiExceptions(
390
+ f"Limite de redirecionamentos excedido: {e}")
391
+ except requests.HTTPError as e:
392
+ raise UdemyUserApiExceptions(
393
+ f"Erro HTTP: {e}")
394
+
395
+
396
+ def get_quizzes(lecture_id:int):
397
+ get = (f'https://www.udemy.com/api-2.0/quizzes/{lecture_id}/assessments/?version=1&page_size=1000&fields[assessment]'
398
+ f'=id,assessment_type,prompt,correct_response,section,question_plain,related_lectures&use_remote_version=true'
399
+ )
400
+ from .authenticate import UdemyAuth
401
+ auth = UdemyAuth()
402
+ if not auth.verif_login():
403
+ raise LoginException("Sessão expirada!")
404
+ try:
405
+ # Faz a solicitação GET com os cabeçalhos
406
+ response = requests.get(get, headers=HEADERS_USER)
407
+ data = []
408
+ # Exibe o código de status
409
+ if response.status_code == 200:
410
+ a = json.loads(response.text)
411
+ return a
412
+ else:
413
+ raise UnhandledExceptions(
414
+ f"Erro ao obter dados da aula! Código de status: {response.status_code}")
415
+
416
+ except requests.ConnectionError as e:
417
+ raise UdemyUserApiExceptions(
418
+ f"Erro de conexão: {e}")
419
+ except requests.Timeout as e:
420
+ raise UdemyUserApiExceptions(
421
+ f"Tempo de requisição excedido: {e}")
422
+ except requests.TooManyRedirects as e:
423
+ raise UdemyUserApiExceptions(
424
+ f"Limite de redirecionamentos excedido: {e}")
425
+ except requests.HTTPError as e:
426
+ raise UdemyUserApiExceptions(
427
+ f"Erro HTTP: {e}")
428
+ except Exception as e:
429
+ raise UnhandledExceptions(
430
+ f"Erro ao obter mídias: {e}")
337
431
 
338
432
 
339
433
  def remove_tag(d: str):
@@ -607,6 +701,7 @@ def assets_infor(course_id: int, id_lecture: int, assets_id: int):
607
701
  f"{r.text}")
608
702
 
609
703
 
704
+
610
705
  def save_html(body, title_lecture):
611
706
  html_content = f"""<!DOCTYPE html>
612
707
  <html lang="en">
@@ -621,7 +716,6 @@ def save_html(body, title_lecture):
621
716
 
622
717
  return html_content
623
718
 
624
-
625
719
  def J(e, t):
626
720
  """
627
721
  Gera um identificador único baseado na data atual e nas funções X e ee.
@@ -689,3 +783,9 @@ def te(e, t):
689
783
  s = t[:r]
690
784
  o = ''.join(format(byte, '08b') for byte in s)
691
785
  return o.startswith('0' * e)
786
+ def is_lecture_in_course(lectures,lecture_id) -> bool:
787
+ # Verifica se o lecture_id está presente na lista de aulas
788
+ for lecture in lectures:
789
+ if lecture.get('lecture_id') == lecture_id:
790
+ return True
791
+ return False
@@ -105,7 +105,7 @@ class UdemyAuth:
105
105
  except requests.HTTPError as e:
106
106
  raise UdemyUserApiExceptions(f"Erro HTTP: {e}")
107
107
  except Exception as e:
108
- raise UnhandledExceptions(f"Unhandled-ERROR: {e}")
108
+ raise UnhandledExceptions(f"{e}")
109
109
  else:
110
110
  return False
111
111
 
@@ -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
@@ -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):
@@ -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)
File without changes
File without changes
File without changes
File without changes