udemy-userAPI 0.3.9__tar.gz → 0.3.11__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (25) hide show
  1. {udemy_userapi-0.3.9/udemy_userAPI.egg-info → udemy_userapi-0.3.11}/PKG-INFO +2 -2
  2. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/README.md +1 -1
  3. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/README_PYPI.md +1 -1
  4. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/__version__.py +1 -1
  5. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/authenticate.py +22 -22
  6. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/bultins.py +115 -4
  7. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11/udemy_userAPI.egg-info}/PKG-INFO +2 -2
  8. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/LICENSE +0 -0
  9. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/MANIFEST.in +0 -0
  10. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/setup.cfg +0 -0
  11. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/setup.py +0 -0
  12. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/.cache/.udemy_userAPI +0 -0
  13. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/__init__.py +0 -0
  14. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/api.py +0 -0
  15. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/exeptions.py +0 -0
  16. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/mpd_analyzer/__init__.py +0 -0
  17. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/mpd_analyzer/bin.wvd +0 -0
  18. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/mpd_analyzer/mpd_parser.py +0 -0
  19. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/sections.py +0 -0
  20. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI/udemy.py +0 -0
  21. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI.egg-info/SOURCES.txt +0 -0
  22. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI.egg-info/dependency_links.txt +0 -0
  23. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI.egg-info/not-zip-safe +0 -0
  24. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI.egg-info/requires.txt +0 -0
  25. {udemy_userapi-0.3.9 → udemy_userapi-0.3.11}/udemy_userAPI.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: udemy_userAPI
3
- Version: 0.3.9
3
+ Version: 0.3.11
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.8-orange)
32
+ ![Versão](https://img.shields.io/badge/version-0.3.11-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)
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- ![Versão](https://img.shields.io/badge/version-0.3.8-orange)
6
+ ![Versão](https://img.shields.io/badge/version-0.3.11-orange)
7
7
  ![Licença](https://img.shields.io/badge/license-MIT-orange)
8
8
  [![Sponsor](https://img.shields.io/badge/💲Donate-yellow)](https://paulocesar-dev404.github.io/me-apoiando-online/)
9
9
  [![Sponsor](https://img.shields.io/badge/Documentation-green)](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
- ![Versão](https://img.shields.io/badge/version-0.3.8-orange)
4
+ ![Versão](https://img.shields.io/badge/version-0.3.11-orange)
5
5
  ![Licença](https://img.shields.io/badge/license-MIT-orange)
6
6
  [![Sponsor](https://img.shields.io/badge/💲Donate-yellow)](https://paulocesar-dev404.github.io/me-apoiando-online/)
7
7
  [![Sponsor](https://img.shields.io/badge/Documentation-green)](https://github.com/PauloCesar-dev404/udemy-userAPI/blob/main/docs/iniciando.md)
@@ -1,4 +1,4 @@
1
- __version__ = '0.3.9'
1
+ __version__ = '0.3.11'
2
2
  __lib_name__ = 'udemy_userAPI' # local name
3
3
  __repo_name__ = 'udemy-userAPI'
4
4
  __autor__ = 'PauloCesar-dev404'
@@ -208,7 +208,7 @@ class UdemyAuth:
208
208
  with open(self.__file_path, 'wb') as f:
209
209
  f.write(b'')
210
210
 
211
- def login_passwordless(self, email: str, locale: str = 'pt-BR'):
211
+ def login_passwordless(self, email: str, locale: str = 'pt-BR', otp_callback=None):
212
212
  """
213
213
  Realiza login na Udemy usando autenticação de dois fatores (2FA).
214
214
 
@@ -219,6 +219,7 @@ class UdemyAuth:
219
219
  Args:
220
220
  email (str): Email do usuário.
221
221
  locale (str): Localização do usuário (recomendado para receber mensagens no idioma local).
222
+ otp_callback (callable, opcional): Função para obter o código OTP (se None, usa input padrão).
222
223
 
223
224
  Raises:
224
225
  LoginException: Em caso de falha no processo de login.
@@ -227,23 +228,16 @@ class UdemyAuth:
227
228
  try:
228
229
  if self.verif_login():
229
230
  raise UserWarning("Atenção, você já possui uma Sessão válida!")
230
- # Inicializa uma sessão com proteção contra Cloudflare
231
- session = cloudscraper.create_scraper()
232
231
 
233
- # Requisita a página de inscrição para obter o token CSRF
232
+ session = cloudscraper.create_scraper()
234
233
  signup_url = "https://www.udemy.com/join/signup-popup/"
235
234
  headers = {"User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)"}
236
235
  response = session.get(signup_url, headers=headers)
237
-
238
- # Obtém o token CSRF dos cookies retornados
239
236
  csrf_token = response.cookies.get("csrftoken")
240
237
  if not csrf_token:
241
238
  raise LoginException("Não foi possível obter o token CSRF.")
242
239
 
243
- # Prepara os dados do login
244
240
  data = {"email": email, "fullname": ""}
245
-
246
- # Atualiza os cookies e cabeçalhos da sessão
247
241
  session.cookies.update(response.cookies)
248
242
  session.headers.update({
249
243
  "User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)",
@@ -261,17 +255,20 @@ class UdemyAuth:
261
255
  "Cache-Control": "no-cache",
262
256
  })
263
257
 
264
- # Faz a requisição para iniciar o login
265
258
  login_url = "https://www.udemy.com/api-2.0/auth/code-generation/login/4.0/"
266
259
  response = session.post(login_url, data=data, allow_redirects=False)
260
+
267
261
  if 'error_message' in response.text:
268
262
  erro_data: dict = response.json()
269
263
  error_message = erro_data.get('error_message', {})
270
264
  raise LoginException(error_message)
265
+
271
266
  for attempt in range(3):
272
- # Solicita o código OTP ao usuário
273
- otp = input("Digite o código de 6 dígitos enviado ao seu e-mail: ")
274
- # Realiza o login com o código OTP
267
+ # Obtém o código OTP via callback ou terminal
268
+ if otp_callback and callable(otp_callback):
269
+ otp = otp_callback()
270
+ else:
271
+ otp = input("Digite o código de 6 dígitos enviado ao seu e-mail: ")
275
272
  otp_login_url = "https://www.udemy.com/api-2.0/auth/udemy-passwordless/login/4.0/"
276
273
  otp_data = {
277
274
  "email": email,
@@ -280,33 +277,36 @@ class UdemyAuth:
280
277
  "subscribeToEmails": "false",
281
278
  "upow": J(email, 'login')
282
279
  }
280
+
283
281
  session.headers.update({
284
282
  "Referer": f"https://www.udemy.com/join/passwordless-auth/?locale={locale}&next="
285
283
  f"https%3A%2F%2Fwww.udemy.com%2Fmobile%2Fipad%2F&response_type=html"
286
284
  })
285
+
287
286
  response = session.post(otp_login_url, otp_data, allow_redirects=False)
288
- # Verifica se o login foi bem-sucedido
287
+
289
288
  if response.status_code == 200:
290
289
  self._save_cookies(session.cookies)
290
+ break # Sai do loop se o login for bem-sucedido
291
291
  else:
292
292
  if 'error_message' in response.text:
293
293
  erro_data: dict = response.json()
294
294
  error_message = erro_data.get('error_message', {})
295
295
  error_code = erro_data.get('error_code', {})
296
+
296
297
  if error_code == '1538':
297
298
  raise LoginException(error_message)
298
299
  elif error_code == '2550':
299
- print(error_message)
300
- continue
300
+ ### codigo errado....
301
+ raise LoginException(error_message)
301
302
  elif error_code == '1330':
302
303
  raise LoginException(error_message)
303
304
  elif error_code == '1149':
304
- raise LoginException(f"Erro interno ao enviar os dados veja os detalhes: '{error_message}'")
305
+ raise LoginException(
306
+ f"Erro interno ao enviar os dados, veja os detalhes: '{error_message}'")
307
+
305
308
  raise LoginException(response.text)
306
- break
309
+
307
310
  except Exception as e:
308
- if DEBUG:
309
- error_details = traceback.format_exc()
310
- else:
311
- error_details = str(e)
311
+ error_details = traceback.format_exc() if DEBUG else str(e)
312
312
  raise LoginException(error_details)
@@ -49,7 +49,6 @@ class DRM:
49
49
  except Exception as e:
50
50
  raise Exception(f"Não foi possível obter as chaves!\n{e}")
51
51
 
52
-
53
52
  class Files:
54
53
  def __init__(self, files: list[dict], id_course):
55
54
  """
@@ -104,6 +103,112 @@ class Files:
104
103
  'data-file': da['download_urls']}
105
104
  download_urls.append(dt_file)
106
105
  return download_urls
106
+ class Caption:
107
+ """Representa uma legenda."""
108
+
109
+ def __init__(self, caption: dict):
110
+ """
111
+ Inicializa uma instância de Caption.
112
+
113
+ Args:
114
+ caption (dict): Dados da legenda.
115
+ """
116
+ self._caption = caption
117
+
118
+ @property
119
+ def locale(self) -> str:
120
+ """Retorna o idioma."""
121
+ return self._caption.get('video_label', '')
122
+
123
+ @property
124
+ def status(self) -> str:
125
+ """Retorna o status da legenda 1 ou 0"""
126
+ return self._caption.get('status')
127
+
128
+ @property
129
+ def title(self) -> str:
130
+ """Retorna o título da legenda."""
131
+ return self._caption.get('title', '')
132
+
133
+ @property
134
+ def created(self) -> str:
135
+ """Retorna a data de criação da legenda."""
136
+ return self._caption.get('created', '')
137
+
138
+ @property
139
+ def id(self) -> int:
140
+ """Retorna o ID da legenda."""
141
+ return self._caption.get('id', 0)
142
+
143
+ @property
144
+ def url(self) -> str:
145
+ """Retorna a URL da legenda."""
146
+ return self._caption.get('url', '')
147
+
148
+ @property
149
+ def content(self) -> str:
150
+ """Obtém o conteúdo da legenda."""
151
+ if self.url:
152
+ r = requests.get(headers=HEADERS_USER, url=self.url)
153
+ if r.status_code == 200:
154
+ return r.text
155
+ else:
156
+ raise ConnectionError(
157
+ f'status_code: {r.status_code}, Não foi possível obter o conteúdo da legenda!'
158
+ )
159
+ else:
160
+ raise FileNotFoundError(
161
+ 'Não foi possível obter a URL da legenda!'
162
+ )
163
+
164
+ class Captions:
165
+ """Gerencia as legendas de um vídeo."""
166
+
167
+ def __init__(self, caption_data: list):
168
+ """
169
+ Inicializa uma instância de Captions.
170
+
171
+ Args:
172
+ caption_data (list): Dados das legendas.
173
+ """
174
+ self._caption_data = caption_data
175
+
176
+ def languages(self) -> list[dict]:
177
+ """Retorna a lista de idiomas disponíveis na aula."""
178
+ langs = []
179
+ for caption in self._caption_data:
180
+ locale_id = caption.get('locale_id', '')
181
+ video_label = caption.get('video_label','')
182
+ if locale_id:
183
+ langs.append({'locale_id': locale_id,'locale':video_label})
184
+ return langs
185
+
186
+ def get_lang(self, locale_id: str) -> Caption:
187
+ """
188
+ Obtém a legenda para o idioma especificado.
189
+
190
+
191
+ Args:
192
+ locale_id (str): ID do idioma,pode ser obtido no método -> 'languages'
193
+
194
+ Returns:
195
+ Caption: Objeto Caption.
196
+
197
+ Raises:
198
+ FileNotFoundError: Se o idioma não estiver disponível na aula.
199
+ """
200
+ is_t = False
201
+ cpt = {}
202
+ for caption in self._caption_data:
203
+ if locale_id == caption.get('locale_id'):
204
+ is_t = True
205
+ cpt = caption
206
+ if not is_t:
207
+ raise FileNotFoundError(
208
+ 'Esse idioma não está disponível nessa aula!'
209
+ )
210
+ c = Caption(caption=cpt)
211
+ return c
107
212
 
108
213
  class Quiz:
109
214
  """Representa um quiz.
@@ -244,14 +349,20 @@ class Lecture:
244
349
  return self.__asset.get('media_sources',[])
245
350
 
246
351
  @property
247
- def get_captions(self) -> list:
352
+ def get_captions(self) -> Captions:
248
353
  """
249
354
  Obtém as legendas.
250
355
 
251
356
  Returns:
252
- list: Uma lista contendo as legendas.
357
+ Captions: Objeto para gerenciar as legendas.
253
358
  """
254
- return self.__asset.get('captions',[])
359
+ if self.__asset.get('captions', []):
360
+ c = Captions(caption_data=self.__asset.get('captions', []))
361
+ return c
362
+ else:
363
+ raise FileNotFoundError(
364
+ 'Não foi encontrada legendas nessa aula!'
365
+ )
255
366
 
256
367
  @property
257
368
  def get_external_url(self) -> list:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: udemy_userAPI
3
- Version: 0.3.9
3
+ Version: 0.3.11
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.8-orange)
32
+ ![Versão](https://img.shields.io/badge/version-0.3.11-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)
File without changes
File without changes
File without changes