udemy-userAPI 0.3.5__py3-none-any.whl → 0.3.6__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.
@@ -1,4 +1,4 @@
1
- __version__ = '0.3.5'
1
+ __version__ = '0.3.6'
2
2
  __lib_name__ = 'udemy_userAPI' # local name
3
3
  __repo_name__ = 'udemy-userAPI'
4
4
  __autor__ = 'PauloCesar-dev404'
udemy_userAPI/api.py CHANGED
@@ -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
 
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,62 @@ 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
111
164
 
112
165
  class Lecture:
113
166
  """Cria objetos aula (lecture) do curso e extrai os dados."""
@@ -166,8 +219,8 @@ class Lecture:
166
219
  """
167
220
  thumbnail_sprite = self.__asset.get('thumbnail_sprite', {})
168
221
  return {
169
- 'thumbnail_vtt_url': thumbnail_sprite.get('vtt_url'),
170
- 'thumbnail_img_url': thumbnail_sprite.get('img_url')
222
+ 'thumbnail_vtt_url': thumbnail_sprite.get('vtt_url',[]),
223
+ 'thumbnail_img_url': thumbnail_sprite.get('img_url',[])
171
224
  }
172
225
 
173
226
  @property
@@ -178,7 +231,7 @@ class Lecture:
178
231
  Returns:
179
232
  str: O tipo de asset.
180
233
  """
181
- return self.__asset.get('asset_type', 'Undefined')
234
+ return self.__asset.get('asset_type', '') or self.__data.get('_class','').replace('quiz','Quiz')
182
235
 
183
236
  @property
184
237
  def get_media_sources(self) -> list:
@@ -188,7 +241,7 @@ class Lecture:
188
241
  Returns:
189
242
  list: Uma lista contendo as fontes de mídia.
190
243
  """
191
- return self.__asset.get('media_sources')
244
+ return self.__asset.get('media_sources',[])
192
245
 
193
246
  @property
194
247
  def get_captions(self) -> list:
@@ -198,7 +251,7 @@ class Lecture:
198
251
  Returns:
199
252
  list: Uma lista contendo as legendas.
200
253
  """
201
- return self.__asset.get('captions')
254
+ return self.__asset.get('captions',[])
202
255
 
203
256
  @property
204
257
  def get_external_url(self) -> list:
@@ -208,7 +261,7 @@ class Lecture:
208
261
  Returns:
209
262
  list: Uma lista contendo os links externos.
210
263
  """
211
- return self.__asset.get('external_url')
264
+ return self.__asset.get('external_url',[])
212
265
 
213
266
  @property
214
267
  def get_media_license_token(self) -> str:
@@ -218,7 +271,7 @@ class Lecture:
218
271
  Returns:
219
272
  str: O token de acesso à aula.
220
273
  """
221
- return self.__asset.get('media_license_token')
274
+ return self.__asset.get('media_license_token','')
222
275
 
223
276
  def course_is_drmed(self) -> DRM:
224
277
  """
@@ -227,12 +280,19 @@ class Lecture:
227
280
  Returns:
228
281
  DRM: O objeto DRM contendo as keys da aula ou None.
229
282
  """
230
- try:
231
- d = DRM(license_token=self.get_media_license_token,
283
+ d = DRM(license_token=self.get_media_license_token,
232
284
  get_media_sources=self.get_media_sources)
233
- return d
234
- except Exception as e:
235
- DeprecationWarning(e)
285
+ return d
286
+
287
+ def quiz_object(self) ->Quiz:
288
+ """se for um quiz ele retorna um objeto Quiz"""
289
+ if self.get_asset_type.lower() == 'quiz':
290
+ q =Quiz(get_assessments(lecture_id=self.get_lecture_id,course_id=self.__course_id))
291
+ return q
292
+ else:
293
+ raise UserWarning(
294
+ 'Atenção essa aula não é um Quiz!'
295
+ )
236
296
 
237
297
  @property
238
298
  def get_download_urls(self) -> list:
@@ -242,7 +302,7 @@ class Lecture:
242
302
  Returns:
243
303
  list: Uma lista contendo as URLs de download.
244
304
  """
245
- return self.__asset.get('download_urls')
305
+ return self.__asset.get('download_urls',[])
246
306
 
247
307
  @property
248
308
  def get_slide_urls(self) -> list:
@@ -252,7 +312,7 @@ class Lecture:
252
312
  Returns:
253
313
  list: Uma lista contendo as URLs de slides.
254
314
  """
255
- return self.__asset.get('slide_urls')
315
+ return self.__asset.get('slide_urls',[])
256
316
 
257
317
  @property
258
318
  def get_slides(self) -> list:
@@ -262,7 +322,7 @@ class Lecture:
262
322
  Returns:
263
323
  list: Uma lista contendo os slides.
264
324
  """
265
- return self.__asset.get('slides')
325
+ return self.__asset.get('slides',[])
266
326
 
267
327
  @property
268
328
  def get_articles(self):
@@ -272,8 +332,11 @@ class Lecture:
272
332
  Returns:
273
333
  Os artigos relacionados à aula.
274
334
  """
275
- d = assets_infor(course_id=self.__course_id, id_lecture=self.get_lecture_id, assets_id=self.__asset.get("id"))
276
- return d
335
+ if self.__asset:
336
+ d = assets_infor(course_id=self.__course_id, id_lecture=self.get_lecture_id, assets_id=self.__asset.get("id"))
337
+ return d
338
+ else:
339
+ return []
277
340
 
278
341
  @property
279
342
  def get_resources(self):
@@ -283,10 +346,12 @@ class Lecture:
283
346
  Returns:
284
347
  Os recursos adicionais relacionados à aula.
285
348
  """
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
-
349
+ if self.__additional_files:
350
+ files_add = get_files_aule(lecture_id_filter=self.get_lecture_id, data=self.__additional_files)
351
+ f = Files(files=files_add, id_course=self.__course_id).get_download_url
352
+ return f
353
+ else:
354
+ return []
290
355
 
291
356
  class Course:
292
357
  """Recebe um dicionário com os dados do curso."""
@@ -299,8 +364,8 @@ class Course:
299
364
  results (dict): Um dicionário contendo os dados do curso.
300
365
  course_id (int): O ID do curso.
301
366
  """
302
- self.__parser_chapers = parser_chapers(results=results)
303
- self.__data = self.__parser_chapers
367
+ self.__parser_chapers = parser_chapters(results=results)
368
+ self.__data:list = self.__parser_chapers
304
369
  self.__course_id = course_id
305
370
  self.__results = results
306
371
  self.__additional_files_data = get_add_files(course_id)
@@ -375,8 +440,8 @@ class Course:
375
440
  int: O número total de lectures no curso.
376
441
  """
377
442
  total_lectures = 0
378
- for chapter in self.__data.values():
379
- total_lectures += len(chapter.get('videos_in_chapter', []))
443
+ for chapter in self.__data:
444
+ total_lectures += len(chapter.get('lectures', []))
380
445
  return total_lectures
381
446
 
382
447
  @property
@@ -398,11 +463,13 @@ class Course:
398
463
  list: Uma lista contendo os títulos de vídeos no curso.
399
464
  """
400
465
  videos = []
401
- for chapter in self.__data.values():
466
+ for chapter in self.__data:
402
467
  for video in chapter.get('videos_in_chapter', []):
403
- title = video['video_title']
404
- if title != "Files":
405
- videos.append(title)
468
+ asset_type = video.get('asset_type')
469
+ if asset_type == 'Video':
470
+ title = video['title']
471
+ if title != "Files":
472
+ videos.append(title)
406
473
  return videos
407
474
 
408
475
  @property
@@ -410,27 +477,25 @@ class Course:
410
477
  """
411
478
  Obtém uma lista com todas as aulas.
412
479
 
480
+ Args:
481
+ data (list): Lista de capítulos contendo as aulas.
482
+
413
483
  Returns:
414
484
  list: Uma lista contendo todas as aulas.
415
485
  """
416
486
  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')
487
+
488
+ for chapter in self.__data:
489
+ for video in chapter.get('lectures', []):
425
490
  dt = {
426
- 'section': section,
427
- 'title': title,
428
- 'lecture_id': id_lecture,
429
- 'asset_id': id_asset,
430
- 'section_order': section_order
491
+ 'section': chapter.get('title', ''),
492
+ 'title': video.get('title', ''),
493
+ 'lecture_id': video.get('lecture_id', ''),
494
+ 'asset_id': video.get('asset_id', ''),
495
+ 'asset_type': video.get('asset_type', '')
431
496
  }
432
497
  videos.append(dt)
433
- section_order += 1 # Incrementar o número da Sessão após processar os vídeos do capítulo
498
+
434
499
  return videos
435
500
 
436
501
  def get_details_lecture(self, lecture_id: int) -> Lecture:
@@ -443,7 +508,19 @@ class Course:
443
508
  Returns:
444
509
  Lecture: Um objeto Lecture contendo os detalhes da aula.
445
510
  """
446
- links = get_links(course_id=self.__course_id, id_lecture=lecture_id)
511
+ type_lecture = ''
512
+ links= {}
513
+ if not is_lecture_in_course(lecture_id=lecture_id,lectures=self.get_lectures):
514
+ raise FileNotFoundError(
515
+ 'Essa aula não existe nesse curso!'
516
+ )
517
+ for l in self.get_lectures:
518
+ if lecture_id == l.get('lecture_id'):
519
+ type_lecture = l.get('asset_type')
520
+ if type_lecture.lower() == 'video' or type_lecture.lower() == 'article':
521
+ links = get_links(course_id=self.__course_id, id_lecture=lecture_id)
522
+ else:
523
+ links = get_assessments(course_id=self.__course_id,lecture_id=lecture_id)
447
524
  additional_files = self.__load_assets()
448
525
  lecture = Lecture(data=links, course_id=self.__course_id, additional_files=additional_files)
449
526
  return lecture
@@ -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.6
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.6-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=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,,
@@ -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