udemy-userAPI 0.3.5__tar.gz → 0.3.6__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {udemy_userapi-0.3.5/udemy_userAPI.egg-info → udemy_userapi-0.3.6}/PKG-INFO +13 -3
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/README.md +1 -1
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/README_PYPI.md +1 -1
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/__version__.py +1 -1
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/api.py +139 -39
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/authenticate.py +1 -1
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/bultins.py +127 -50
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6/udemy_userAPI.egg-info}/PKG-INFO +13 -3
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/LICENSE +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/MANIFEST.in +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/setup.cfg +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/setup.py +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/.cache/.udemy_userAPI +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/__init__.py +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/exeptions.py +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/mpd_analyzer/__init__.py +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/mpd_analyzer/bin.wvd +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/mpd_analyzer/mpd_parser.py +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/sections.py +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI/udemy.py +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI.egg-info/SOURCES.txt +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI.egg-info/dependency_links.txt +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI.egg-info/not-zip-safe +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI.egg-info/requires.txt +0 -0
- {udemy_userapi-0.3.5 → udemy_userapi-0.3.6}/udemy_userAPI.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: udemy_userAPI
|
3
|
-
Version: 0.3.
|
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
|
-

|
23
33
|

|
24
34
|
[](https://paulocesar-dev404.github.io/me-apoiando-online/)
|
25
35
|
[](https://github.com/PauloCesar-dev404/udemy-userAPI/blob/main/docs/iniciando.md)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
|
5
5
|
|
6
|
-

|
7
7
|

|
8
8
|
[](https://paulocesar-dev404.github.io/me-apoiando-online/)
|
9
9
|
[](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
|
-

|
5
5
|

|
6
6
|
[](https://paulocesar-dev404.github.io/me-apoiando-online/)
|
7
7
|
[](https://github.com/PauloCesar-dev404/udemy-userAPI/blob/main/docs/iniciando.md)
|
@@ -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(
|
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
|
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
|
-
|
200
|
-
|
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
|
-
|
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
|
-
'
|
208
|
-
'
|
207
|
+
'title': dictionary.get('title'),
|
208
|
+
'chapter_index': chapter_index,
|
209
|
+
'lectures': [] # Lista para armazenar aulas e quizzes
|
209
210
|
}
|
210
|
-
|
211
|
-
|
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
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
'
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
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(
|
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(
|
345
|
+
raise UdemyUserApiExceptions(
|
346
|
+
f"Erro de conexão: {e}")
|
329
347
|
except requests.Timeout as e:
|
330
|
-
raise UdemyUserApiExceptions(
|
348
|
+
raise UdemyUserApiExceptions(
|
349
|
+
f"Tempo de requisição excedido: {e}")
|
331
350
|
except requests.TooManyRedirects as e:
|
332
|
-
raise UdemyUserApiExceptions(
|
351
|
+
raise UdemyUserApiExceptions(
|
352
|
+
f"Limite de redirecionamentos excedido: {e}")
|
333
353
|
except requests.HTTPError as e:
|
334
|
-
raise UdemyUserApiExceptions(
|
354
|
+
raise UdemyUserApiExceptions(
|
355
|
+
f"Erro HTTP: {e}")
|
335
356
|
except Exception as e:
|
336
|
-
raise UnhandledExceptions(
|
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"
|
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
|
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', '
|
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
|
-
|
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
|
-
|
234
|
-
|
235
|
-
|
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
|
-
|
276
|
-
|
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
|
-
|
287
|
-
|
288
|
-
|
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 =
|
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
|
379
|
-
total_lectures += len(chapter.get('
|
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
|
466
|
+
for chapter in self.__data:
|
402
467
|
for video in chapter.get('videos_in_chapter', []):
|
403
|
-
|
404
|
-
if
|
405
|
-
|
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
|
-
|
418
|
-
|
419
|
-
|
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':
|
427
|
-
'title': title,
|
428
|
-
'lecture_id':
|
429
|
-
'asset_id':
|
430
|
-
'
|
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
|
-
|
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
|
-
|
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
|
+
Metadata-Version: 2.2
|
2
2
|
Name: udemy_userAPI
|
3
|
-
Version: 0.3.
|
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
|
-

|
23
33
|

|
24
34
|
[](https://paulocesar-dev404.github.io/me-apoiando-online/)
|
25
35
|
[](https://github.com/PauloCesar-dev404/udemy-userAPI/blob/main/docs/iniciando.md)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|