udemy-userAPI 0.3.11__py3-none-any.whl → 0.3.12__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.
- udemy_userAPI/__version__.py +1 -1
- udemy_userAPI/api.py +1 -1
- udemy_userAPI/authenticate.py +125 -5
- {udemy_userAPI-0.3.11.dist-info → udemy_userapi-0.3.12.dist-info}/METADATA +5 -4
- {udemy_userAPI-0.3.11.dist-info → udemy_userapi-0.3.12.dist-info}/RECORD +8 -8
- {udemy_userAPI-0.3.11.dist-info → udemy_userapi-0.3.12.dist-info}/WHEEL +1 -1
- {udemy_userAPI-0.3.11.dist-info → udemy_userapi-0.3.12.dist-info/licenses}/LICENSE +0 -0
- {udemy_userAPI-0.3.11.dist-info → udemy_userapi-0.3.12.dist-info}/top_level.txt +0 -0
udemy_userAPI/__version__.py
CHANGED
udemy_userAPI/api.py
CHANGED
udemy_userAPI/authenticate.py
CHANGED
@@ -1,14 +1,47 @@
|
|
1
|
+
import http
|
1
2
|
import json
|
2
3
|
import os
|
3
4
|
import pickle
|
4
5
|
import traceback
|
6
|
+
from http.cookies import SimpleCookie
|
7
|
+
|
8
|
+
import cloudscraper
|
5
9
|
import requests
|
10
|
+
|
6
11
|
from .exeptions import UnhandledExceptions, UdemyUserApiExceptions, LoginException, Upstreamconnecterror
|
7
|
-
import cloudscraper
|
8
12
|
|
9
13
|
DEBUG = False
|
10
14
|
|
11
15
|
|
16
|
+
def convert_cook(cookie_string):
|
17
|
+
"""
|
18
|
+
Converte uma string de cookies para um dicionário usando http.cookies.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
cookie_string (str): A string de cookies no formato "nome1=valor1; nome2=valor2".
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
dict: Um dicionário onde as chaves são os nomes dos cookies e os valores são os seus respectivos valores.
|
25
|
+
Retorna um dicionário vazio se a string for inválida ou vazia.
|
26
|
+
"""
|
27
|
+
if not isinstance(cookie_string, str) or not cookie_string.strip():
|
28
|
+
print("A string de cookies fornecida é inválida ou vazia.")
|
29
|
+
return {}
|
30
|
+
|
31
|
+
cookie = SimpleCookie()
|
32
|
+
try:
|
33
|
+
# Carrega a string de cookies
|
34
|
+
cookie.load(cookie_string)
|
35
|
+
|
36
|
+
# Converte o objeto SimpleCookie para um dicionário regular
|
37
|
+
dicionario_cookies = {k: v.value for k, v in cookie.items()}
|
38
|
+
|
39
|
+
return dicionario_cookies
|
40
|
+
except http.cookies.CookieError as e:
|
41
|
+
print(f"Erro ao analisar a string de cookies: {e}")
|
42
|
+
return {}
|
43
|
+
|
44
|
+
|
12
45
|
class UdemyAuth:
|
13
46
|
def __init__(self):
|
14
47
|
"""
|
@@ -162,7 +195,7 @@ class UdemyAuth:
|
|
162
195
|
|
163
196
|
# Verifica a resposta para determinar se o login foi bem-sucedido
|
164
197
|
if "returnUrl" in r.text:
|
165
|
-
self.
|
198
|
+
self.__save_cookies(s.cookies)
|
166
199
|
else:
|
167
200
|
login_error = r.json().get("error", {}).get("data", {}).get("formErrors", [])[0]
|
168
201
|
if login_error[0] == "Y":
|
@@ -176,14 +209,101 @@ class UdemyAuth:
|
|
176
209
|
e = traceback.format_exc()
|
177
210
|
raise LoginException(e)
|
178
211
|
|
179
|
-
def
|
212
|
+
def login_direct_cookies(self, cookies: str):
|
213
|
+
"""
|
214
|
+
Realiza login via cookies diretamente. (Não é seguro para uso em produção).
|
215
|
+
|
216
|
+
Argumentos:
|
217
|
+
cookies: Pode ser o caminho do arquivo cookies (apenas .json ou .txt)
|
218
|
+
ou a string com os cookies.
|
219
|
+
|
220
|
+
Raises:
|
221
|
+
FileNotFoundError: Se o arquivo especificado não for encontrado.
|
222
|
+
ValueError: Se o arquivo não for .json ou .txt, ou se o JSON for inválido.
|
223
|
+
UnhandledExceptions: Para outros erros inesperados durante a leitura do arquivo.
|
224
|
+
LoginException: Se os cookies estiverem expirados, inválidos ou houver falha no login.
|
225
|
+
"""
|
226
|
+
cookies_content = ""
|
227
|
+
# Verifica se a string fornecida é um caminho de arquivo existente
|
228
|
+
if os.path.isfile(cookies):
|
229
|
+
file_extension = os.path.splitext(cookies)[1].lower() # Pega a extensão do arquivo
|
230
|
+
|
231
|
+
# Verifica se a extensão é permitida
|
232
|
+
if file_extension not in ['.json', '.txt']:
|
233
|
+
raise ValueError(
|
234
|
+
f"Erro: Apenas arquivos .json ou .txt são permitidos. "
|
235
|
+
f"Extensão recebida: '{file_extension}' para o arquivo '{cookies}'."
|
236
|
+
)
|
237
|
+
|
238
|
+
try:
|
239
|
+
with open(cookies, 'r', encoding='utf-8') as f:
|
240
|
+
cookies_content = f.read()
|
241
|
+
|
242
|
+
# Se for um arquivo JSON, tenta carregá-lo para validar
|
243
|
+
if file_extension == '.json':
|
244
|
+
try:
|
245
|
+
# Tenta carregar o JSON. Se for um JSON de cookies, ele pode estar
|
246
|
+
# em um formato específico. Aqui estamos apenas validando a sintaxe JSON.
|
247
|
+
json.loads(cookies_content)
|
248
|
+
except json.JSONDecodeError as e:
|
249
|
+
raise ValueError(f"Erro: O arquivo '{cookies}' contém JSON inválido: {e}")
|
250
|
+
|
251
|
+
except FileNotFoundError:
|
252
|
+
raise FileNotFoundError(f"Erro: O arquivo '{cookies}' não foi encontrado.")
|
253
|
+
except Exception as e:
|
254
|
+
raise UnhandledExceptions(f"Erro ao ler o arquivo '{cookies}': {e}")
|
255
|
+
else:
|
256
|
+
# Se não for um arquivo, assume-se que é a string de cookies diretamente
|
257
|
+
cookies_content = cookies
|
258
|
+
|
259
|
+
headers = {
|
260
|
+
"accept": "*/*",
|
261
|
+
"accept-language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7",
|
262
|
+
"cache-control": "no-cache",
|
263
|
+
"pragma": "no-cache",
|
264
|
+
"sec-ch-ua": '"Chromium";v="118", "Google Chrome";v="118", "Not=A?Brand";v="99"',
|
265
|
+
"sec-ch-ua-mobile": "?0",
|
266
|
+
"sec-ch-ua-platform": '"Windows"',
|
267
|
+
"sec-fetch-dest": "empty",
|
268
|
+
"sec-fetch-mode": "cors",
|
269
|
+
"sec-fetch-site": "cross-site",
|
270
|
+
"Cookie": cookies_content,
|
271
|
+
"Referer": "https://www.udemy.com/"
|
272
|
+
}
|
273
|
+
url = 'https://www.udemy.com/api-2.0/contexts/me/?header=true'
|
274
|
+
|
275
|
+
try:
|
276
|
+
resp = requests.get(url=url, headers=headers)
|
277
|
+
resp.raise_for_status() # Lança um HTTPError para respostas de status de erro (4xx ou 5xx)
|
278
|
+
|
279
|
+
convert = resp.json() # Usa resp.json() para parsear diretamente o JSON
|
280
|
+
is_logged_in = convert.get('header', {}).get('isLoggedIn', None)
|
281
|
+
|
282
|
+
if not is_logged_in:
|
283
|
+
raise LoginException(
|
284
|
+
"Cookies expirados ou inválidos!")
|
285
|
+
self.__save_cookies(resp.cookies)
|
286
|
+
|
287
|
+
except requests.exceptions.HTTPError as e:
|
288
|
+
# Captura erros HTTP (ex: 401 Unauthorized, 403 Forbidden)
|
289
|
+
raise LoginException(f"Erro de HTTP durante o login: {e}. Resposta: {e.response.text}")
|
290
|
+
except requests.exceptions.ConnectionError as e:
|
291
|
+
raise UnhandledExceptions(f"Erro de conexão: {e}")
|
292
|
+
except requests.exceptions.Timeout as e:
|
293
|
+
raise UnhandledExceptions(f"Tempo limite da requisição excedido: {e}")
|
294
|
+
except json.JSONDecodeError as e:
|
295
|
+
raise UnhandledExceptions(f"Erro ao decodificar JSON da resposta da API: {e}. Resposta: {resp.text}")
|
296
|
+
except Exception as e:
|
297
|
+
raise UnhandledExceptions(f"Um erro inesperado ocorreu durante o login: {e}")
|
298
|
+
|
299
|
+
def __save_cookies(self, cookies):
|
180
300
|
try:
|
181
301
|
with open(fr'{self.__file_path}', 'wb') as f:
|
182
302
|
pickle.dump(cookies, f)
|
183
303
|
except Exception as e:
|
184
304
|
raise LoginException(e)
|
185
305
|
|
186
|
-
def
|
306
|
+
def load_cookies(self) -> str:
|
187
307
|
"""Carrega cookies e retorna-os em uma string formatada"""
|
188
308
|
try:
|
189
309
|
file = os.path.join(self.__file_path)
|
@@ -286,7 +406,7 @@ class UdemyAuth:
|
|
286
406
|
response = session.post(otp_login_url, otp_data, allow_redirects=False)
|
287
407
|
|
288
408
|
if response.status_code == 200:
|
289
|
-
self.
|
409
|
+
self.__save_cookies(session.cookies)
|
290
410
|
break # Sai do loop se o login for bem-sucedido
|
291
411
|
else:
|
292
412
|
if 'error_message' in response.text:
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: udemy_userAPI
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.12
|
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
|
@@ -21,6 +21,7 @@ Dynamic: description
|
|
21
21
|
Dynamic: description-content-type
|
22
22
|
Dynamic: keywords
|
23
23
|
Dynamic: license
|
24
|
+
Dynamic: license-file
|
24
25
|
Dynamic: platform
|
25
26
|
Dynamic: project-url
|
26
27
|
Dynamic: requires-dist
|
@@ -29,10 +30,10 @@ Dynamic: summary
|
|
29
30
|
# udemy-userAPI
|
30
31
|
|
31
32
|
|
32
|
-

|
33
34
|

|
34
35
|
[](https://paulocesar-dev404.github.io/me-apoiando-online/)
|
35
|
-
[](
|
36
|
+
[]()
|
36
37
|
|
37
38
|
|
38
39
|
Obtenha detalhes de cursos da plataforma udemy com a api de usuário,usando esta lib
|
@@ -1,7 +1,7 @@
|
|
1
1
|
udemy_userAPI/__init__.py,sha256=BPle89xE_CMTKKe_Lw6jioYLgpH-q_Lpho2S-n1PIUA,206
|
2
|
-
udemy_userAPI/__version__.py,sha256=
|
3
|
-
udemy_userAPI/api.py,sha256=
|
4
|
-
udemy_userAPI/authenticate.py,sha256=
|
2
|
+
udemy_userAPI/__version__.py,sha256=hsuWNfD58RhtiW3QQ8DEVA806KW5TK_7r4v9j6OIL2o,406
|
3
|
+
udemy_userAPI/api.py,sha256=Pxwy-UrqVCLX1tTLQQojb7nCjkHE9mLlnpO1OJuqlAI,29166
|
4
|
+
udemy_userAPI/authenticate.py,sha256=ObUh8cs-o7mC9Ovid1UoFvPS4m0nSe4fBk-E3XQqL1o,19511
|
5
5
|
udemy_userAPI/bultins.py,sha256=LZlyOjSGte6B6gNn7cjl6L2Q2T_CyXIqqfkOUzt4CV4,21996
|
6
6
|
udemy_userAPI/exeptions.py,sha256=kfnPdZpqYY8nd0gnl6_Vh-MIz-XupmmbRPIuFnyXupk,692
|
7
7
|
udemy_userAPI/sections.py,sha256=Q1PlVt2Bu5MSEP8g11-F_gilJDdhZq50TV1Bo400jcA,6389
|
@@ -10,8 +10,8 @@ udemy_userAPI/.cache/.udemy_userAPI,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
|
|
10
10
|
udemy_userAPI/mpd_analyzer/__init__.py,sha256=i3JVWyvcFLaj5kPmx8c1PgjsLht7OUIQQClD4yqYbo8,102
|
11
11
|
udemy_userAPI/mpd_analyzer/bin.wvd,sha256=1rAJdCc120hQlX9qe5KUS628eY2ZHYxQSmyhGNefSzo,2956
|
12
12
|
udemy_userAPI/mpd_analyzer/mpd_parser.py,sha256=PgUkHc5x8FTuXFCuYkWPZr9TaO_nsKalb02EFYl_zeA,8926
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
udemy_userapi-0.3.12.dist-info/licenses/LICENSE,sha256=l4jdKYt8gSdDFOGr09vCKnMn_Im55XIcQKqTDEtFfNs,1095
|
14
|
+
udemy_userapi-0.3.12.dist-info/METADATA,sha256=dkTiWdFOxfJejB4wL_ZYwuq2iy6DKRQheCcydIlzC2o,1602
|
15
|
+
udemy_userapi-0.3.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
16
|
+
udemy_userapi-0.3.12.dist-info/top_level.txt,sha256=ijTINaSDRKhdahY_X7dmSRFTxBIwQErWv9ATCG55mog,14
|
17
|
+
udemy_userapi-0.3.12.dist-info/RECORD,,
|
File without changes
|
File without changes
|