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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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