udemy-userAPI 0.3.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,495 @@
1
+ import json
2
+ 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
8
+ from .exeptions import LoginException
9
+
10
+
11
+ class DRM:
12
+ def __init__(self, license_token: str, get_media_sources: list):
13
+ """
14
+ Inicializa o objeto DRM.
15
+
16
+ Args:
17
+ license_token (str): O token de licença.
18
+ get_media_sources (list): A lista de fontes de mídia.
19
+ """
20
+ self.__mpd_file_path = None
21
+ self.__token = license_token
22
+ self.__dash_url = organize_streams(streams=get_media_sources).get('dash', {})
23
+ if not license_token or get_media_sources:
24
+ return
25
+
26
+ def get_key_for_lesson(self):
27
+ """
28
+ Obtém as chaves para a aula.
29
+
30
+ Returns:
31
+ As chaves da aula ou None se não for possível obtê-las.
32
+ """
33
+ try:
34
+ if self.__dash_url:
35
+ self.__mpd_file_path = get_mpd_file(mpd_url=self.__dash_url[0].get('src'))
36
+ parser = MPDParser(mpd_content=self.__mpd_file_path)
37
+ resolutions = get_highest_resolution(parser.get_all_video_resolutions())
38
+ parser.set_selected_resolution(resolution=resolutions)
39
+ init_url = parser.get_selected_video_init_url()
40
+ if init_url:
41
+ pssh = get_pssh(init_url=init_url)
42
+ if pssh:
43
+ keys = extract(pssh=pssh, license_token=self.__token)
44
+ if keys:
45
+ return keys
46
+ else:
47
+ return None
48
+ else:
49
+ return None
50
+ else:
51
+ return None
52
+ except Exception as e:
53
+ raise Exception(f"Não foi possível obter as chaves!\n{e}")
54
+
55
+
56
+ class Files:
57
+ def __init__(self, files: list[dict], id_course):
58
+ """
59
+ Inicializa o objeto Files.
60
+
61
+ Args:
62
+ files (list[dict]): Lista de dicionários contendo os dados dos arquivos.
63
+ id_course: ID do curso.
64
+ """
65
+ self.__data = files
66
+ self.__id_course = id_course
67
+
68
+ @property
69
+ def get_download_url(self) -> dict[str, Any | None] | list[dict[str, Any | None]]:
70
+ """
71
+ Obtém a URL de download de um arquivo quando disponível.
72
+
73
+ Returns:
74
+ dict[str, Any | None] | list[dict[str, Any | None]]: URL de download do arquivo.
75
+ """
76
+ from .authenticate import UdemyAuth
77
+ auth = UdemyAuth()
78
+ if not auth.verif_login():
79
+ raise LoginException("Sessão expirada!")
80
+ download_urls = []
81
+ for files in self.__data:
82
+ lecture_id = files.get('lecture_id', None)
83
+ asset_id = files.get('asset_id', None)
84
+ title = files.get("title", None)
85
+ lecture_title = files.get('lecture_title', None)
86
+ external_link = files.get('ExternalLink', None)
87
+ if external_link:
88
+ lnk = get_external_liks(course_id=self.__id_course, id_lecture=lecture_id, asset_id=asset_id)
89
+ dt_file = {'title-file': title,
90
+ 'lecture_title': lecture_title,
91
+ 'lecture_id': lecture_id,
92
+ 'external_link': external_link,
93
+ 'data-file': lnk.get('external_url', None)}
94
+ return dt_file
95
+ if asset_id and title and lecture_id and not external_link:
96
+ resp = requests.get(
97
+ f"https://www.udemy.com/api-2.0/users/me/subscribed-courses/{self.__id_course}/lectures/"
98
+ f"{lecture_id}/supplementary-assets/{asset_id}/?fields[asset]=download_urls",
99
+ headers=HEADERS_USER)
100
+ if resp.status_code == 200:
101
+ da = json.loads(resp.text)
102
+ # Para cada dict de um arquivo colocar seu título:
103
+ dt_file = {'title-file': title,
104
+ 'lecture_title': lecture_title,
105
+ 'lecture_id': lecture_id,
106
+ 'external_link': external_link,
107
+ 'data-file': da['download_urls']}
108
+ download_urls.append(dt_file)
109
+ return download_urls
110
+
111
+
112
+ class Lecture:
113
+ """Cria objetos aula (lecture) do curso e extrai os dados."""
114
+
115
+ def __init__(self, data: dict, course_id: int, additional_files):
116
+ """
117
+ Inicializa o objeto Lecture.
118
+
119
+ Args:
120
+ data (dict): Um dicionário contendo os dados da aula.
121
+ course_id (int): O ID do curso.
122
+ additional_files: Arquivos adicionais relacionados à aula.
123
+ """
124
+ self.__course_id = course_id
125
+ self.__data = data
126
+ self.__additional_files = additional_files
127
+ self.__asset = self.__data.get("asset", {})
128
+
129
+ @property
130
+ def get_lecture_id(self) -> int:
131
+ """
132
+ Obtém o ID da aula.
133
+
134
+ Returns:
135
+ int: O ID da aula.
136
+ """
137
+ return self.__data.get('id', 0)
138
+
139
+ @property
140
+ def get_description(self) -> str:
141
+ """
142
+ Obtém a descrição da aula.
143
+
144
+ Returns:
145
+ str: A descrição da aula.
146
+ """
147
+ return remove_tag(str(self.__data.get('description')))
148
+
149
+ @property
150
+ def is_free(self) -> bool:
151
+ """
152
+ Verifica se a aula é gratuita (aulas gratuitas estão disponíveis na apresentação do curso).
153
+
154
+ Returns:
155
+ bool: True se a aula for gratuita, False caso contrário.
156
+ """
157
+ return self.__data.get('is_free', False)
158
+
159
+ @property
160
+ def get_thumbnail(self) -> dict:
161
+ """
162
+ Obtém informações da miniatura (thumbnail) do vídeo.
163
+
164
+ Returns:
165
+ dict: Um dicionário contendo as URLs das miniaturas.
166
+ """
167
+ thumbnail_sprite = self.__asset.get('thumbnail_sprite', {})
168
+ return {
169
+ 'thumbnail_vtt_url': thumbnail_sprite.get('vtt_url'),
170
+ 'thumbnail_img_url': thumbnail_sprite.get('img_url')
171
+ }
172
+
173
+ @property
174
+ def get_asset_type(self) -> str:
175
+ """
176
+ Obtém o tipo de asset (Video, Article, etc.).
177
+
178
+ Returns:
179
+ str: O tipo de asset.
180
+ """
181
+ return self.__asset.get('asset_type', 'Undefined')
182
+
183
+ @property
184
+ def get_media_sources(self) -> list:
185
+ """
186
+ Obtém dados de streaming.
187
+
188
+ Returns:
189
+ list: Uma lista contendo as fontes de mídia.
190
+ """
191
+ return self.__asset.get('media_sources')
192
+
193
+ @property
194
+ def get_captions(self) -> list:
195
+ """
196
+ Obtém as legendas.
197
+
198
+ Returns:
199
+ list: Uma lista contendo as legendas.
200
+ """
201
+ return self.__asset.get('captions')
202
+
203
+ @property
204
+ def get_external_url(self) -> list:
205
+ """
206
+ Obtém links externos se houver.
207
+
208
+ Returns:
209
+ list: Uma lista contendo os links externos.
210
+ """
211
+ return self.__asset.get('external_url')
212
+
213
+ @property
214
+ def get_media_license_token(self) -> str:
215
+ """
216
+ Obtém o token de acesso à aula se houver.
217
+
218
+ Returns:
219
+ str: O token de acesso à aula.
220
+ """
221
+ return self.__asset.get('media_license_token')
222
+
223
+ def course_is_drmed(self) -> DRM:
224
+ """
225
+ Verifica se a aula possui DRM. Se sim, retorna as keys da aula.
226
+
227
+ Returns:
228
+ DRM: O objeto DRM contendo as keys da aula ou None.
229
+ """
230
+ try:
231
+ d = DRM(license_token=self.get_media_license_token,
232
+ get_media_sources=self.get_media_sources)
233
+ return d
234
+ except Exception as e:
235
+ DeprecationWarning(e)
236
+
237
+ @property
238
+ def get_download_urls(self) -> list:
239
+ """
240
+ Obtém URLs de download se houver.
241
+
242
+ Returns:
243
+ list: Uma lista contendo as URLs de download.
244
+ """
245
+ return self.__asset.get('download_urls')
246
+
247
+ @property
248
+ def get_slide_urls(self) -> list:
249
+ """
250
+ Obtém URLs de slides se houver.
251
+
252
+ Returns:
253
+ list: Uma lista contendo as URLs de slides.
254
+ """
255
+ return self.__asset.get('slide_urls')
256
+
257
+ @property
258
+ def get_slides(self) -> list:
259
+ """
260
+ Obtém slides se houver.
261
+
262
+ Returns:
263
+ list: Uma lista contendo os slides.
264
+ """
265
+ return self.__asset.get('slides')
266
+
267
+ @property
268
+ def get_articles(self):
269
+ """
270
+ Obtém os artigos relacionados à aula.
271
+
272
+ Returns:
273
+ Os artigos relacionados à aula.
274
+ """
275
+ d = assets_infor(course_id=self.__course_id, id_lecture=self.get_lecture_id, assets_id=self.__asset.get("id"))
276
+ return d
277
+
278
+ @property
279
+ def get_resources(self):
280
+ """
281
+ Obtém os recursos adicionais relacionados à aula.
282
+
283
+ Returns:
284
+ Os recursos adicionais relacionados à aula.
285
+ """
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
+
290
+
291
+ class Course:
292
+ """Recebe um dicionário com os dados do curso."""
293
+
294
+ def __init__(self, results: dict, course_id: int):
295
+ """
296
+ Inicializa o objeto Course.
297
+
298
+ Args:
299
+ results (dict): Um dicionário contendo os dados do curso.
300
+ course_id (int): O ID do curso.
301
+ """
302
+ self.__parser_chapers = parser_chapers(results=results)
303
+ self.__data = self.__parser_chapers
304
+ self.__course_id = course_id
305
+ self.__results = results
306
+ self.__additional_files_data = get_add_files(course_id)
307
+ self.__information = self.__load_infor_course()
308
+
309
+ def __load_infor_course(self) -> dict:
310
+ """
311
+ Obtém as informações do curso.
312
+
313
+ Returns:
314
+ dict: Um dicionário contendo as informações do curso.
315
+ """
316
+ data = get_course_infor(self.__course_id)
317
+ return data
318
+
319
+ @property
320
+ def title_course(self) -> str:
321
+ """
322
+ Obtém o título do curso.
323
+
324
+ Returns:
325
+ str: O título do curso.
326
+ """
327
+ return self.__information.get('title')
328
+
329
+ @property
330
+ def instructors(self) -> dict:
331
+ """
332
+ Obtém informações dos instrutores.
333
+
334
+ Returns:
335
+ dict: Um dicionário contendo as informações dos instrutores.
336
+ """
337
+ return self.__information.get("visible_instructors")
338
+
339
+ @property
340
+ def locale(self):
341
+ """
342
+ Obtém informações de localidade do curso.
343
+
344
+ Returns:
345
+ str: As informações de localidade do curso.
346
+ """
347
+ return self.__information.get('locale')
348
+
349
+ @property
350
+ def primary_category(self):
351
+ """
352
+ Obtém a categoria primária.
353
+
354
+ Returns:
355
+ str: A categoria primária.
356
+ """
357
+ return self.__information.get('primary_category')
358
+
359
+ @property
360
+ def primary_subcategory(self):
361
+ """
362
+ Obtém a subcategoria primária.
363
+
364
+ Returns:
365
+ str: A subcategoria primária.
366
+ """
367
+ return self.__information.get('primary_subcategory')
368
+
369
+ @property
370
+ def count_lectures(self) -> int:
371
+ """
372
+ Obtém o número total de lectures no curso.
373
+
374
+ Returns:
375
+ int: O número total de lectures no curso.
376
+ """
377
+ total_lectures = 0
378
+ for chapter in self.__data.values():
379
+ total_lectures += len(chapter.get('videos_in_chapter', []))
380
+ return total_lectures
381
+
382
+ @property
383
+ def count_chapters(self) -> int:
384
+ """
385
+ Obtém o número total de chapters (sections) no curso.
386
+
387
+ Returns:
388
+ int: O número total de chapters (sections) no curso.
389
+ """
390
+ return len(self.__data)
391
+
392
+ @property
393
+ def title_videos(self) -> list:
394
+ """
395
+ Obtém uma lista com todos os títulos de vídeos no curso.
396
+
397
+ Returns:
398
+ list: Uma lista contendo os títulos de vídeos no curso.
399
+ """
400
+ videos = []
401
+ for chapter in self.__data.values():
402
+ for video in chapter.get('videos_in_chapter', []):
403
+ title = video['video_title']
404
+ if title != "Files":
405
+ videos.append(title)
406
+ return videos
407
+
408
+ @property
409
+ def get_lectures(self) -> list:
410
+ """
411
+ Obtém uma lista com todas as aulas.
412
+
413
+ Returns:
414
+ list: Uma lista contendo todas as aulas.
415
+ """
416
+ 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 seção
422
+ title = video.get('video_title')
423
+ id_lecture = video.get('lecture_id')
424
+ id_asset = video.get('asset_id')
425
+ dt = {
426
+ 'section': section,
427
+ 'title': title,
428
+ 'lecture_id': id_lecture,
429
+ 'asset_id': id_asset,
430
+ 'section_order': section_order
431
+ }
432
+ videos.append(dt)
433
+ section_order += 1 # Incrementar o número da seção após processar os vídeos do capítulo
434
+ return videos
435
+
436
+ def get_details_lecture(self, lecture_id: int) -> Lecture:
437
+ """
438
+ Obtém detalhes de uma aula específica.
439
+
440
+ Args:
441
+ lecture_id (int): O ID da aula.
442
+
443
+ Returns:
444
+ Lecture: Um objeto Lecture contendo os detalhes da aula.
445
+ """
446
+ links = get_links(course_id=self.__course_id, id_lecture=lecture_id)
447
+ additional_files = self.__load_assets()
448
+ lecture = Lecture(data=links, course_id=self.__course_id, additional_files=additional_files)
449
+ return lecture
450
+
451
+ @property
452
+ def get_additional_files(self) -> list:
453
+ """
454
+ Retorna a lista de arquivos adicionais de um curso.
455
+
456
+ Returns:
457
+ list: Uma lista contendo os arquivos adicionais de um curso.
458
+ """
459
+ supplementary_assets = []
460
+ for item in self.__additional_files_data.get('results', []):
461
+ if item.get('_class') == 'lecture':
462
+ id_l = item.get('id', {})
463
+ title = item.get('title', {})
464
+ assets = item.get('supplementary_assets', [])
465
+ for asset in assets:
466
+ supplementary_assets.append({
467
+ 'lecture_id': id_l,
468
+ 'lecture_title': title,
469
+ 'asset': asset
470
+ })
471
+ files = extract_files(supplementary_assets)
472
+ files_objt = Files(files=files, id_course=self.__course_id).get_download_url
473
+ return files_objt
474
+
475
+ def __load_assets(self):
476
+ """
477
+ Retorna a lista de arquivos adicionais de um curso.
478
+
479
+ Returns:
480
+ list: Uma lista contendo os arquivos adicionais de um curso.
481
+ """
482
+ supplementary_assets = []
483
+ for item in self.__additional_files_data.get('results', []):
484
+ if item.get('_class') == 'lecture':
485
+ id_l = item.get('id')
486
+ title = item.get('title')
487
+ assets = item.get('supplementary_assets', [])
488
+ for asset in assets:
489
+ supplementary_assets.append({
490
+ 'lecture_id': id_l,
491
+ 'lecture_title': title,
492
+ 'asset': asset
493
+ })
494
+ files = extract_files(supplementary_assets)
495
+ return files
@@ -0,0 +1,22 @@
1
+ class UdemyUserApiExceptions(Exception):
2
+ def __init__(self, message="Udemy_UserApi Generic Error!"):
3
+ self.message = message
4
+ super().__init__(self.message)
5
+
6
+
7
+ class Upstreamconnecterror(Exception):
8
+ def __init__(self, message="Falha na conexão com o servidor!"):
9
+ self.message = message
10
+ super().__init__(self.message)
11
+
12
+
13
+ class UnhandledExceptions(Exception):
14
+ def __init__(self, message="Error Unhandled!"):
15
+ self.message = message
16
+ super().__init__(self.message)
17
+
18
+
19
+ class LoginException(Exception):
20
+ def __init__(self, message="Error Login!"):
21
+ self.message = message
22
+ super().__init__(self.message)
@@ -0,0 +1,3 @@
1
+ # Biblioteca para análise de arquivos MPD
2
+ from .mpd_parser import MPDParser
3
+ __version__ = '0.1.0'
Binary file