udemy-userAPI 0.3.6__py3-none-any.whl → 0.3.8__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ # m3u8_analyzer/__init__.py
2
+
3
+ from .M3u8Analyzer import M3u8Analyzer, Wrapper,EncryptSuport
4
+
5
+ __all__ = ['M3u8Analyzer', 'Wrapper','EncryptSuport']
6
+ if __name__ == '__main__':
7
+ raise RuntimeError("no escope!")
@@ -0,0 +1 @@
1
+ __version__ = '1.0.4.1'
@@ -0,0 +1,82 @@
1
+ class M3u8AnalyzerExceptions(Exception):
2
+ def __init__(self, message="Erro na análise da playlist M3U8", errors=None):
3
+ """
4
+ Exceção base para erros relacionados à análise de playlists M3U8.
5
+
6
+ Args:
7
+ message (str): Mensagem descritiva do erro. Padrão é "Erro na análise da playlist M3U8".
8
+ errors (list, optional): Lista de erros adicionais ou detalhes para diagnóstico. Padrão é None.
9
+ """
10
+ super().__init__(message)
11
+ self.errors = errors
12
+
13
+ def __str__(self):
14
+ """
15
+ Retorna a representação em string da exceção.
16
+
17
+ Returns:
18
+ str: Mensagem de erro formatada com detalhes adicionais, se presentes.
19
+ """
20
+ if self.errors:
21
+ return f"{super().__str__()} | Erros adicionais: {self.errors}"
22
+ return super().__str__()
23
+
24
+
25
+ class M3u8DownloadError(M3u8AnalyzerExceptions):
26
+ def __init__(self, message="Erro durante o download da playlist M3U8", errors=None):
27
+ """
28
+ Exceção para erros específicos ocorridos durante o download de uma playlist M3U8.
29
+
30
+ Args:
31
+ message (str): Mensagem descritiva do erro. Padrão é "Erro durante o download da playlist M3U8".
32
+ errors (list, optional): Lista de erros adicionais ou detalhes para diagnóstico. Padrão é None.
33
+ """
34
+ super().__init__(message, errors)
35
+
36
+
37
+ class M3u8FfmpegDownloadError(M3u8AnalyzerExceptions):
38
+ def __init__(self, message="Erro durante o download da playlist M3U8 com ffmpeg", errors=None):
39
+ """
40
+ Exceção para erros específicos ocorridos durante o download de uma playlist M3U8 usando ffmpeg.
41
+
42
+ Args:
43
+ message (str): Mensagem descritiva do erro. Padrão é "Erro durante o download da playlist M3U8 com ffmpeg".
44
+ errors (list, optional): Lista de erros adicionais ou detalhes para diagnóstico. Padrão é None.
45
+ """
46
+ super().__init__(message, errors)
47
+
48
+
49
+ class M3u8NetworkingError(M3u8AnalyzerExceptions):
50
+ def __init__(self, message="Erro de rede durante o download da playlist M3U8", errors=None):
51
+ """
52
+ Exceção para erros relacionados à rede durante o download de uma playlist M3U8.
53
+
54
+ Args:
55
+ message (str): Mensagem descritiva do erro. Padrão é "Erro de rede durante o download da playlist M3U8".
56
+ errors (list, optional): Lista de erros adicionais ou detalhes para diagnóstico. Padrão é None.
57
+ """
58
+ super().__init__(message, errors)
59
+
60
+
61
+ class M3u8Error(M3u8AnalyzerExceptions):
62
+ def __init__(self, message="Erro inesperado na análise de playlist M3U8", errors=None):
63
+ """
64
+ Exceção para erros inesperados que não se encaixam em outras categorias.
65
+
66
+ Args:
67
+ message (str): Mensagem descritiva do erro. Padrão é "Erro inesperado na análise de playlist M3U8".
68
+ errors (list, optional): Lista de erros adicionais ou detalhes para diagnóstico. Padrão é None.
69
+ """
70
+ super().__init__(message, errors)
71
+
72
+
73
+ class M3u8FileError(M3u8AnalyzerExceptions):
74
+ def __init__(self, message="Erro ao manipular o arquivo da playlist M3U8", errors=None):
75
+ """
76
+ Exceção para erros ocorridos ao manipular arquivos relacionados a playlists M3U8.
77
+
78
+ Args:
79
+ message (str): Mensagem descritiva do erro. Padrão é "Erro ao manipular o arquivo da playlist M3U8".
80
+ errors (list, optional): Lista de erros adicionais ou detalhes para diagnóstico. Padrão é None.
81
+ """
82
+ super().__init__(message, errors)
Binary file
@@ -1,4 +1,4 @@
1
- __version__ = '0.3.6'
1
+ __version__ = '0.3.8'
2
2
  __lib_name__ = 'udemy_userAPI' # local name
3
3
  __repo_name__ = 'udemy-userAPI'
4
4
  __autor__ = 'PauloCesar-dev404'
udemy_userAPI/api.py CHANGED
@@ -186,53 +186,75 @@ def get_mpd_file(mpd_url):
186
186
 
187
187
 
188
188
  def parser_chapters(results) -> list[dict]:
189
+ """
190
+ Processa os dados do curso e retorna uma lista de capítulos com suas aulas e quizzes.
191
+
192
+ Se os resultados não contiverem capítulos (i.e. apenas aulas ou quizzes), todas as
193
+ aulas/quizzes serão agrupadas em um capítulo padrão.
194
+
195
+ Args:
196
+ results (dict): Dicionário com os resultados do curso, normalmente contendo a chave 'results'.
197
+
198
+ Returns:
199
+ list[dict]: Lista de capítulos, cada um com título, índice (se disponível) e lista de lectures/quizzes.
200
+
201
+ Raises:
202
+ UdemyUserApiExceptions: Se não for possível obter os detalhes do curso.
203
+ """
189
204
  if not results:
190
205
  raise UdemyUserApiExceptions("Não foi possível obter detalhes do curso!")
191
206
 
192
- results = results.get('results', [])
193
- chapters_dicts = [] # Lista que armazena todos os capítulos
207
+ items = results.get('results', [])
208
+ chapters_dicts = [] # Lista de capítulos
194
209
  current_chapter = None # Capítulo atual
195
210
 
196
- for dictionary in results:
211
+ # Nome padrão para o grupo quando não houver capítulos
212
+ default_chapter_title = "CourseFiles"
213
+
214
+ for dictionary in items:
197
215
  _class = dictionary.get('_class')
198
- chapter_index = dictionary.get('object_index')
216
+ chapter_index = dictionary.get('object_index', None)
199
217
 
200
- # Quando encontrar um novo capítulo
201
218
  if _class == 'chapter':
202
- if current_chapter: # Se já há um capítulo atual, adicionamos
219
+ # Se já há um capítulo em andamento, adiciona-o à lista
220
+ if current_chapter:
203
221
  chapters_dicts.append(current_chapter)
204
-
205
222
  # Inicia um novo capítulo
206
223
  current_chapter = {
207
- 'title': dictionary.get('title'),
224
+ 'title': dictionary.get('title', 'Sem Título'),
208
225
  'chapter_index': chapter_index,
209
226
  'lectures': [] # Lista para armazenar aulas e quizzes
210
227
  }
211
-
212
- # Se for uma aula, adiciona ao capítulo atual
213
- elif _class == 'lecture' and current_chapter:
214
- asset = dictionary.get('asset')
215
- if asset:
216
- lecture_data = {
217
- 'asset_type': asset.get('asset_type', ''),
218
- 'title': dictionary.get('title', 'Files'),
228
+ elif _class in ('lecture', 'quiz'):
229
+ # Se não houver um capítulo atual, cria um capítulo padrão
230
+ if current_chapter is None:
231
+ current_chapter = {
232
+ 'title': default_chapter_title,
233
+ 'chapter_index': None,
234
+ 'lectures': []
235
+ }
236
+ # Processa a aula ou quiz
237
+ if _class == 'lecture':
238
+ asset = dictionary.get('asset')
239
+ if asset:
240
+ lecture_data = {
241
+ 'asset_type': asset.get('asset_type', ''),
242
+ 'title': dictionary.get('title', 'Aula'),
243
+ 'lecture_id': dictionary.get('id', ''),
244
+ 'asset_id': asset.get('id', '')
245
+ }
246
+ current_chapter['lectures'].append(lecture_data)
247
+ elif _class == 'quiz':
248
+ quiz_data = {
249
+ 'asset_type': 'quiz',
250
+ 'title': dictionary.get('title', 'Quiz'),
219
251
  'lecture_id': dictionary.get('id', ''),
220
- 'asset_id': asset.get('id', '')
252
+ 'type': dictionary.get('type', ''),
253
+ 'asset_id': ''
221
254
  }
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)
255
+ current_chapter['lectures'].append(quiz_data)
234
256
 
235
- # Adiciona o último capítulo processado
257
+ # Se houver um capítulo em andamento, adiciona-o à lista
236
258
  if current_chapter:
237
259
  chapters_dicts.append(current_chapter)
238
260
 
udemy_userAPI/bultins.py CHANGED
@@ -475,11 +475,7 @@ class Course:
475
475
  @property
476
476
  def get_lectures(self) -> list:
477
477
  """
478
- Obtém uma lista com todas as aulas.
479
-
480
- Args:
481
- data (list): Lista de capítulos contendo as aulas.
482
-
478
+ Obtém uma lista de dicionários com todas as aulas.
483
479
  Returns:
484
480
  list: Uma lista contendo todas as aulas.
485
481
  """
@@ -492,12 +488,12 @@ class Course:
492
488
  'title': video.get('title', ''),
493
489
  'lecture_id': video.get('lecture_id', ''),
494
490
  'asset_id': video.get('asset_id', ''),
495
- 'asset_type': video.get('asset_type', '')
491
+ 'asset_type': video.get('asset_type', ''),
492
+ 'section_order': chapter.get('chapter_index',1)
496
493
  }
497
494
  videos.append(dt)
498
495
 
499
496
  return videos
500
-
501
497
  def get_details_lecture(self, lecture_id: int) -> Lecture:
502
498
  """
503
499
  Obtém detalhes de uma aula específica.
udemy_userAPI/sections.py CHANGED
@@ -53,13 +53,13 @@ def get_courses_plan(tipe: str) -> list:
53
53
 
54
54
  def get_details_courses(course_id):
55
55
  """
56
- Obtém detalhes de um curso específico.
56
+ Obtém detalhes de um curso específico, realizando paginação caso haja múltiplas páginas.
57
57
 
58
58
  Args:
59
59
  course_id (int): ID do curso.
60
60
 
61
61
  Returns:
62
- dict: Dicionário contendo os detalhes do curso.
62
+ dict: Dicionário contendo os detalhes do curso com todos os itens concatenados.
63
63
 
64
64
  Raises:
65
65
  LoginException: Se a sessão estiver expirada.
@@ -70,21 +70,47 @@ def get_details_courses(course_id):
70
70
  auth = UdemyAuth()
71
71
  if not auth.verif_login():
72
72
  raise LoginException("Sessão expirada!")
73
- response = requests.get(
73
+
74
+ # URL base com parâmetros
75
+ base_url = (
74
76
  f"https://www.udemy.com/api-2.0/courses/{course_id}/subscriber-curriculum-items/?"
75
- f"caching_intent=True&fields%5Basset%5D=title%2Cfilename%2Casset_type%2Cstatus%2Ctime_estimation%2"
76
- f"Cis_external&fields%5Bchapter%5D=title%2Cobject_index%2Cis_published%2Csort_order&fields%5Blecture"
77
- f"%5D=title%2Cobject_index%2Cis_published%2Csort_order%2Ccreated%2Casset%2Csupplementary_assets%2"
78
- f"Cis_free&fields%5Bpractice%5D=title%2Cobject_index%2Cis_published%2Csort_order&fields%5Bquiz%5D="
79
- f"title%2Cobject_index%2Cis_published%2Csort_order%2Ctype&pages&page_size=400&fields[lecture]=asset,"
80
- f"description,download_url,is_free,last_watched_second&fields[asset]=asset_type,length,"
81
- f"media_license_token,course_is_drmed,external_url&q=0.3108014137011559",
82
- headers=HEADERS_USER)
83
- if response.status_code == 200:
84
- resposta = json.loads(response.text)
85
- return resposta
86
- else:
87
- raise UdemyUserApiExceptions(f"Erro ao obter detalhes do curso! Código de status: {response.status_code}")
77
+ f"page_size=1000&"
78
+ f"fields[lecture]=title,object_index,is_published,sort_order,created,asset,supplementary_assets,is_free&"
79
+ f"fields[quiz]=title,object_index,is_published,sort_order,type&"
80
+ f"fields[practice]=title,object_index,is_published,sort_order&"
81
+ f"fields[chapter]=title,object_index,is_published,sort_order&"
82
+ f"fields[asset]=title,filename,asset_type,status,time_estimation,is_external&"
83
+ f"caching_intent=True"
84
+ )
85
+
86
+ try:
87
+ response = requests.get(base_url, headers=HEADERS_USER)
88
+ if response.status_code != 200:
89
+ raise UdemyUserApiExceptions(
90
+ f"Erro ao obter detalhes do curso! Código de status: {response.status_code}")
91
+
92
+ data = json.loads(response.text)
93
+ all_results = data.get('results', [])
94
+ next_page = data.get('next', '')
95
+
96
+ # Enquanto houver próxima página, faz requisição e junta os resultados
97
+ while next_page:
98
+ response = requests.get(next_page, headers=HEADERS_USER)
99
+ if response.status_code != 200:
100
+ # Caso ocorra erro na próxima página, pode-se optar por interromper ou registrar o erro.....por enquanto
101
+ # irei parar..mais se por acaso futuramente não der certo mudarei esta implementação!
102
+ # @pauloCesarDev404
103
+ break
104
+ next_data = json.loads(response.text)
105
+ all_results.extend(next_data.get('results', []))
106
+ next_page = next_data.get('next', '')
107
+
108
+ # Atualiza o dicionário final com todos os itens concatenados
109
+ data['results'] = all_results
110
+ return data
111
+
112
+ except Exception as e:
113
+ raise UdemyUserApiExceptions(f"Erro ao obter detalhes do curso! {e}")
88
114
 
89
115
 
90
116
  def get_course_infor(course_id):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: udemy_userAPI
3
- Version: 0.3.6
3
+ Version: 0.3.8
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
@@ -29,7 +29,7 @@ Dynamic: summary
29
29
  # udemy-userAPI
30
30
 
31
31
 
32
- ![Versão](https://img.shields.io/badge/version-0.3.6-orange)
32
+ ![Versão](https://img.shields.io/badge/version-0.3.8-orange)
33
33
  ![Licença](https://img.shields.io/badge/license-MIT-orange)
34
34
  [![Sponsor](https://img.shields.io/badge/💲Donate-yellow)](https://paulocesar-dev404.github.io/me-apoiando-online/)
35
35
  [![Sponsor](https://img.shields.io/badge/Documentation-green)](https://github.com/PauloCesar-dev404/udemy-userAPI/blob/main/docs/iniciando.md)
@@ -0,0 +1,29 @@
1
+ animation_consoles/__init__.py,sha256=5uHhe-PVZ54FHWxbF1sNvNt4fuQf3FtZWVo2Mjo11a8,40
2
+ animation_consoles/animation.py,sha256=ZreNtdD0HYeqlRx-f1d1twUU4sOFTR7vJ3S6tMQpnHM,2122
3
+ ffmpeg_for_python/__config__.py,sha256=nCPrYs1NkMnyfyg5ITw9wOar4nUJOxwONrItVpVBVBM,4719
4
+ ffmpeg_for_python/__init__.py,sha256=-BMtoX8Yof_pnHra2OzoV3faxMubpMvUedMy8TqI8dc,214
5
+ ffmpeg_for_python/__utils.py,sha256=Qy3J5f4lOIPcSNbTwiawfiHjYPdZ_tq7hafStnnqwA4,3263
6
+ ffmpeg_for_python/__version__.py,sha256=HLFuN4n_leeJE5twr7yH2AAFyfIcEHzxElLRP1FUKmQ,422
7
+ ffmpeg_for_python/exeptions.py,sha256=tg-TBdaq_NHxZOCAhkMttzwtJVILPAQPLOKqofe5PPA,3627
8
+ ffmpeg_for_python/ffmpeg.py,sha256=G2VGHOIhErsqQI4OVlUnIQGmleNCjxyFqzNAMNnoD6I,7920
9
+ m3u8_analyzer/M3u8Analyzer.py,sha256=aUgxk2jS84MFDNbjlOT8FRiJerFI_jGcKMu9uv1EwcE,36620
10
+ m3u8_analyzer/__init__.py,sha256=v7CiVqsCq2YH347C-QR1kHPJtXFFdru8qole3E9adCY,217
11
+ m3u8_analyzer/__version__.py,sha256=YP3yT87ZKrU3eARUUdQ_pg4xAXLGfBXjH4ZgEoZSq1I,25
12
+ m3u8_analyzer/exeptions.py,sha256=fK6bU3YxNSbfsPmCp4yudUvmwy_g6dj2KwIkH0dW4LI,3672
13
+ udemy_userAPI/__init__.py,sha256=BPle89xE_CMTKKe_Lw6jioYLgpH-q_Lpho2S-n1PIUA,206
14
+ udemy_userAPI/__version__.py,sha256=uwv2sjxdkP_iinuyfXK3i0Hngd7zXHyOfaCoAUNr_PY,405
15
+ udemy_userAPI/api.py,sha256=GVvbbs3vFN-rF-qLBwiuHz77sjehwk8HjAI-Dey_A6c,29167
16
+ udemy_userAPI/authenticate.py,sha256=IJRrCjmhe_x40CrQ2KrOMNP8VvotZf0QMWsrbcLl_rw,14225
17
+ udemy_userAPI/bultins.py,sha256=8L3FuEE09ySPLVUdyuy2EC7uXppDzr0KpSrNeG5AhRo,18629
18
+ udemy_userAPI/exeptions.py,sha256=kfnPdZpqYY8nd0gnl6_Vh-MIz-XupmmbRPIuFnyXupk,692
19
+ udemy_userAPI/sections.py,sha256=Q1PlVt2Bu5MSEP8g11-F_gilJDdhZq50TV1Bo400jcA,6389
20
+ udemy_userAPI/udemy.py,sha256=SpK0LI4hjO45nZDz5waw-Py-d0uulBb28TVjltyWBxM,2920
21
+ udemy_userAPI/.cache/.udemy_userAPI,sha256=J6Wp1IjPAoO8TksV4CfLUmt-7c6hcn6y1y3HnETvoFc,4139
22
+ udemy_userAPI/mpd_analyzer/__init__.py,sha256=i3JVWyvcFLaj5kPmx8c1PgjsLht7OUIQQClD4yqYbo8,102
23
+ udemy_userAPI/mpd_analyzer/bin.wvd,sha256=1rAJdCc120hQlX9qe5KUS628eY2ZHYxQSmyhGNefSzo,2956
24
+ udemy_userAPI/mpd_analyzer/mpd_parser.py,sha256=PgUkHc5x8FTuXFCuYkWPZr9TaO_nsKalb02EFYl_zeA,8926
25
+ udemy_userAPI-0.3.8.dist-info/LICENSE,sha256=l4jdKYt8gSdDFOGr09vCKnMn_Im55XIcQKqTDEtFfNs,1095
26
+ udemy_userAPI-0.3.8.dist-info/METADATA,sha256=YbJ2lhBvhSeQHQy5R8sdaGoav2fc0NEUG2VWWPSpZyc,1655
27
+ udemy_userAPI-0.3.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
28
+ udemy_userAPI-0.3.8.dist-info/top_level.txt,sha256=ijTINaSDRKhdahY_X7dmSRFTxBIwQErWv9ATCG55mog,14
29
+ udemy_userAPI-0.3.8.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- udemy_userAPI/__init__.py,sha256=BPle89xE_CMTKKe_Lw6jioYLgpH-q_Lpho2S-n1PIUA,206
2
- udemy_userAPI/__version__.py,sha256=EcE9Js6ZcOzm6ll-14ArSvYXxNzDsRtEfpRK27AHgtA,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=I2jKaOL6ExSQg7zMaGvaJGLym4L7GXo9WWvNeDbrm2I,18629
6
- udemy_userAPI/exeptions.py,sha256=kfnPdZpqYY8nd0gnl6_Vh-MIz-XupmmbRPIuFnyXupk,692
7
- udemy_userAPI/sections.py,sha256=oP3jvbsWocemqhzzOAOoeL7ICF1f4gNvjL4FJBt47pE,5474
8
- udemy_userAPI/udemy.py,sha256=SpK0LI4hjO45nZDz5waw-Py-d0uulBb28TVjltyWBxM,2920
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.6.dist-info/LICENSE,sha256=l4jdKYt8gSdDFOGr09vCKnMn_Im55XIcQKqTDEtFfNs,1095
14
- udemy_userAPI-0.3.6.dist-info/METADATA,sha256=LUarfagN6sbQ5BgJDvHSJdFAEFhi18vCSDF5DKOMw70,1655
15
- udemy_userAPI-0.3.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
16
- udemy_userAPI-0.3.6.dist-info/top_level.txt,sha256=ijTINaSDRKhdahY_X7dmSRFTxBIwQErWv9ATCG55mog,14
17
- udemy_userAPI-0.3.6.dist-info/RECORD,,