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.
- animation_consoles/__init__.py +1 -0
- animation_consoles/animation.py +64 -0
- ffmpeg_for_python/__config__.py +118 -0
- ffmpeg_for_python/__init__.py +8 -0
- ffmpeg_for_python/__utils.py +78 -0
- ffmpeg_for_python/__version__.py +6 -0
- ffmpeg_for_python/exeptions.py +91 -0
- ffmpeg_for_python/ffmpeg.py +203 -0
- m3u8_analyzer/M3u8Analyzer.py +807 -0
- m3u8_analyzer/__init__.py +7 -0
- m3u8_analyzer/__version__.py +1 -0
- m3u8_analyzer/exeptions.py +82 -0
- udemy_userAPI/.cache/.udemy_userAPI +0 -0
- udemy_userAPI/__init__.py +7 -0
- udemy_userAPI/__version__.py +6 -0
- udemy_userAPI/api.py +691 -0
- udemy_userAPI/authenticate.py +311 -0
- udemy_userAPI/bultins.py +495 -0
- udemy_userAPI/exeptions.py +22 -0
- udemy_userAPI/mpd_analyzer/__init__.py +3 -0
- udemy_userAPI/mpd_analyzer/bin.wvd +0 -0
- udemy_userAPI/mpd_analyzer/mpd_parser.py +224 -0
- udemy_userAPI/sections.py +117 -0
- udemy_userAPI/udemy.py +93 -0
- udemy_userAPI-0.3.2.dist-info/LICENSE +21 -0
- udemy_userAPI-0.3.2.dist-info/METADATA +34 -0
- udemy_userAPI-0.3.2.dist-info/RECORD +29 -0
- udemy_userAPI-0.3.2.dist-info/WHEEL +5 -0
- udemy_userAPI-0.3.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,311 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
import pickle
|
4
|
+
import traceback
|
5
|
+
import requests
|
6
|
+
from .exeptions import UnhandledExceptions, UdemyUserApiExceptions, LoginException, Upstreamconnecterror
|
7
|
+
import cloudscraper
|
8
|
+
|
9
|
+
DEBUG = False
|
10
|
+
|
11
|
+
|
12
|
+
class UdemyAuth:
|
13
|
+
def __init__(self):
|
14
|
+
"""
|
15
|
+
Autenticação na plataforma Udemy de maneira segura.
|
16
|
+
Atenção ao limite de logins. Recomendo que após logar não use novamente o método login,
|
17
|
+
use apenas o verificador de login para evitar bloqueios temporários.
|
18
|
+
"""
|
19
|
+
self.__cookie_dict = {}
|
20
|
+
current_directory = os.path.dirname(__file__)
|
21
|
+
cache = '.cache'
|
22
|
+
cache_dir = os.path.join(current_directory, cache)
|
23
|
+
os.makedirs(cache_dir, exist_ok=True)
|
24
|
+
self.__user_dir = os.path.join(cache_dir)
|
25
|
+
file_name = '.udemy_userAPI'
|
26
|
+
file_credenntials = '.udemy_Credentials'
|
27
|
+
self.__file_path = os.path.join(self.__user_dir, file_name)
|
28
|
+
self.__credentials_path = os.path.join(self.__user_dir, file_credenntials)
|
29
|
+
|
30
|
+
def verif_login(self) -> bool:
|
31
|
+
"""
|
32
|
+
Verifica se o usuário está logado.
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
bool: True se o usuário estiver logado, False caso contrário.
|
36
|
+
"""
|
37
|
+
|
38
|
+
def verif_config():
|
39
|
+
"""
|
40
|
+
Verifica se o arquivo .userLogin existe e carrega cookies se existir.
|
41
|
+
|
42
|
+
Returns:
|
43
|
+
str: Cookies em formato de string ou False se não existir.
|
44
|
+
"""
|
45
|
+
try:
|
46
|
+
with open(fr'{self.__file_path}', 'rb') as f:
|
47
|
+
cookies = pickle.load(f)
|
48
|
+
cookies_dict = {cookie.name: cookie.value for cookie in cookies}
|
49
|
+
cookies_str = "; ".join([f"{key}={value}" for key, value in cookies_dict.items()])
|
50
|
+
return cookies_str
|
51
|
+
except Exception as e:
|
52
|
+
if DEBUG:
|
53
|
+
e = traceback.format_exc()
|
54
|
+
raise LoginException(e)
|
55
|
+
return False
|
56
|
+
|
57
|
+
log = verif_config()
|
58
|
+
|
59
|
+
if log:
|
60
|
+
cookies_de_secao = log
|
61
|
+
headers = {
|
62
|
+
"accept": "*/*",
|
63
|
+
"accept-language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7",
|
64
|
+
"cache-control": "no-cache",
|
65
|
+
"Content-Type": "text/plain",
|
66
|
+
"pragma": "no-cache",
|
67
|
+
"sec-ch-ua": "\"Chromium\";v=\"118\", \"Google Chrome\";v=\"118\", \"Not=A?Brand\";v=\"99\"",
|
68
|
+
"sec-ch-ua-mobile": "?0",
|
69
|
+
"sec-ch-ua-platform": "\"Windows\"",
|
70
|
+
"sec-fetch-dest": "empty",
|
71
|
+
"sec-fetch-mode": "cors",
|
72
|
+
"sec-fetch-site": "cross-site",
|
73
|
+
"Cookie": cookies_de_secao,
|
74
|
+
"Referer": "https://www.udemy.com/"
|
75
|
+
}
|
76
|
+
|
77
|
+
try:
|
78
|
+
url = 'https://www.udemy.com/api-2.0/contexts/me/?header=true'
|
79
|
+
resp = requests.get(url=url, headers=headers)
|
80
|
+
if resp.status_code == 200:
|
81
|
+
convert = json.loads(resp.text)
|
82
|
+
isLoggedIn = convert.get('header', {}).get('isLoggedIn', False)
|
83
|
+
if isLoggedIn:
|
84
|
+
if isLoggedIn is True:
|
85
|
+
return True
|
86
|
+
else:
|
87
|
+
return False
|
88
|
+
else:
|
89
|
+
return False
|
90
|
+
else:
|
91
|
+
if (('upstream connect error or disconnect/reset before headers.'
|
92
|
+
' reset reason: remote connection'
|
93
|
+
' failure, transport failure reason:'
|
94
|
+
' delayed connect error: 111')
|
95
|
+
in resp.text):
|
96
|
+
raise Upstreamconnecterror(message='Erro no servidor remoto!')
|
97
|
+
raise LoginException(f"Erro Ao obter login atualize a lib! -> {resp.text}")
|
98
|
+
except requests.ConnectionError as e:
|
99
|
+
raise UdemyUserApiExceptions(f"Erro de conexão: {e}")
|
100
|
+
except requests.Timeout as e:
|
101
|
+
raise UdemyUserApiExceptions(f"Tempo de requisição excedido: {e}")
|
102
|
+
except requests.TooManyRedirects as e:
|
103
|
+
raise UdemyUserApiExceptions(f"Limite de redirecionamentos excedido: {e}")
|
104
|
+
except requests.HTTPError as e:
|
105
|
+
raise UdemyUserApiExceptions(f"Erro HTTP: {e}")
|
106
|
+
except Exception as e:
|
107
|
+
raise UnhandledExceptions(f"Unhandled-ERROR: {e}")
|
108
|
+
else:
|
109
|
+
return False
|
110
|
+
|
111
|
+
def login(self, email: str, password: str, locale: str = 'pt_BR'):
|
112
|
+
"""
|
113
|
+
Efetua login na Udemy.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
email (str): Email do usuário.
|
117
|
+
password (str): Senha do usuário.
|
118
|
+
locale (str): Localidade. Padrão é 'pt_BR'.
|
119
|
+
"""
|
120
|
+
try:
|
121
|
+
if self.verif_login():
|
122
|
+
raise UserWarning("Atenção, você já possui uma sessão válida!")
|
123
|
+
s = cloudscraper.create_scraper()
|
124
|
+
r = s.get(
|
125
|
+
"https://www.udemy.com/join/signup-popup/",
|
126
|
+
headers={"User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)"},
|
127
|
+
)
|
128
|
+
csrf_token = r.cookies["csrftoken"]
|
129
|
+
data = {
|
130
|
+
"csrfmiddlewaretoken": csrf_token,
|
131
|
+
"locale": locale,
|
132
|
+
"email": email,
|
133
|
+
"password": password,
|
134
|
+
}
|
135
|
+
s.cookies.update(r.cookies)
|
136
|
+
s.headers.update(
|
137
|
+
{
|
138
|
+
"User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)",
|
139
|
+
"Accept": "application/json, text/plain, */*",
|
140
|
+
"Accept-Language": "en-GB,en;q=0.5",
|
141
|
+
"Referer": "https://www.udemy.com/join/login-popup/?locale=en_US&response_type="
|
142
|
+
"html&next=https%3A%2F"
|
143
|
+
"%2Fwww.udemy.com%2F",
|
144
|
+
"Origin": "https://www.udemy.com",
|
145
|
+
"DNT": "1",
|
146
|
+
"Connection": "keep-alive",
|
147
|
+
"Sec-Fetch-Dest": "empty",
|
148
|
+
"Sec-Fetch-Mode": "cors",
|
149
|
+
"Sec-Fetch-Site": "same-origin",
|
150
|
+
"Pragma": "no-cache",
|
151
|
+
"Cache-Control": "no-cache",
|
152
|
+
}
|
153
|
+
)
|
154
|
+
|
155
|
+
# Tenta fazer login com as credenciais fornecidas
|
156
|
+
r = s.post(
|
157
|
+
"https://www.udemy.com/join/login-popup/?response_type=json",
|
158
|
+
data=data,
|
159
|
+
allow_redirects=False,
|
160
|
+
)
|
161
|
+
|
162
|
+
# Verifica a resposta para determinar se o login foi bem-sucedido
|
163
|
+
if "returnUrl" in r.text:
|
164
|
+
self._save_cookies(s.cookies)
|
165
|
+
else:
|
166
|
+
login_error = r.json().get("error", {}).get("data", {}).get("formErrors", [])[0]
|
167
|
+
if login_error[0] == "Y":
|
168
|
+
raise LoginException("Você excedeu o número máximo de solicitações por hora.")
|
169
|
+
elif login_error[0] == "T":
|
170
|
+
raise LoginException("Email ou senha incorretos")
|
171
|
+
else:
|
172
|
+
raise UnhandledExceptions(login_error)
|
173
|
+
except Exception as e:
|
174
|
+
if DEBUG:
|
175
|
+
e = traceback.format_exc()
|
176
|
+
raise LoginException(e)
|
177
|
+
|
178
|
+
def _save_cookies(self, cookies):
|
179
|
+
try:
|
180
|
+
with open(fr'{self.__file_path}', 'wb') as f:
|
181
|
+
pickle.dump(cookies, f)
|
182
|
+
except Exception as e:
|
183
|
+
raise LoginException(e)
|
184
|
+
|
185
|
+
def _load_cookies(self) -> str:
|
186
|
+
"""Carrega cookies e retorna-os em uma string formatada"""
|
187
|
+
try:
|
188
|
+
file = os.path.join(self.__file_path)
|
189
|
+
if os.path.exists(file) and os.path.getsize(file) > 0: # Verifica se o arquivo existe e não está vazio
|
190
|
+
with open(file, 'rb') as f:
|
191
|
+
cookies = pickle.load(f)
|
192
|
+
# Converte cookies em formato de string
|
193
|
+
cookies_dict = {cookie.name: cookie.value for cookie in cookies}
|
194
|
+
cookies_str = "; ".join([f"{key}={value}" for key, value in cookies_dict.items()])
|
195
|
+
return cookies_str
|
196
|
+
else:
|
197
|
+
return "" # Retorna uma string vazia se o arquivo não existir ou estiver vazio
|
198
|
+
except (EOFError, pickle.UnpicklingError): # Trata arquivos vazios ou corrompidos
|
199
|
+
return "" # Retorna uma string vazia
|
200
|
+
except Exception as e:
|
201
|
+
if DEBUG:
|
202
|
+
e = traceback.format_exc()
|
203
|
+
raise LoginException(f"Erro ao carregar cookies: {e}")
|
204
|
+
|
205
|
+
def remove_cookies(self):
|
206
|
+
if os.path.exists(self.__file_path):
|
207
|
+
with open(self.__file_path, 'wb') as f:
|
208
|
+
f.write(b'')
|
209
|
+
|
210
|
+
def login_passwordless(self, email: str, locale: str = 'pt-BR'):
|
211
|
+
"""
|
212
|
+
Realiza login na Udemy usando autenticação de dois fatores (2FA).
|
213
|
+
|
214
|
+
Este método utiliza o fluxo de autenticação OAuth da Udemy para enviar um
|
215
|
+
código de verificação por e-mail ao usuário. Após inserir o código recebido,
|
216
|
+
o login é concluído.
|
217
|
+
|
218
|
+
Args:
|
219
|
+
email (str): Email do usuário.
|
220
|
+
locale (str): Localização do usuário (recomendado para receber mensagens no idioma local).
|
221
|
+
|
222
|
+
Raises:
|
223
|
+
LoginException: Em caso de falha no processo de login.
|
224
|
+
"""
|
225
|
+
from .api import J
|
226
|
+
try:
|
227
|
+
if self.verif_login():
|
228
|
+
raise UserWarning("Atenção, você já possui uma sessão válida!")
|
229
|
+
# Inicializa uma sessão com proteção contra Cloudflare
|
230
|
+
session = cloudscraper.create_scraper()
|
231
|
+
|
232
|
+
# Requisita a página de inscrição para obter o token CSRF
|
233
|
+
signup_url = "https://www.udemy.com/join/signup-popup/"
|
234
|
+
headers = {"User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)"}
|
235
|
+
response = session.get(signup_url, headers=headers)
|
236
|
+
|
237
|
+
# Obtém o token CSRF dos cookies retornados
|
238
|
+
csrf_token = response.cookies.get("csrftoken")
|
239
|
+
if not csrf_token:
|
240
|
+
raise LoginException("Não foi possível obter o token CSRF.")
|
241
|
+
|
242
|
+
# Prepara os dados do login
|
243
|
+
data = {"email": email, "fullname": ""}
|
244
|
+
|
245
|
+
# Atualiza os cookies e cabeçalhos da sessão
|
246
|
+
session.cookies.update(response.cookies)
|
247
|
+
session.headers.update({
|
248
|
+
"User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)",
|
249
|
+
"Accept": "application/json, text/plain, */*",
|
250
|
+
"Accept-Language": locale,
|
251
|
+
"Referer": f"https://www.udemy.com/join/passwordless-auth/?locale={locale.replace('-', '_')}&next="
|
252
|
+
f"https%3A%2F%2Fwww.udemy.com%2Fmobile%2Fipad%2F&response_type=html",
|
253
|
+
"Origin": "https://www.udemy.com",
|
254
|
+
"DNT": "1",
|
255
|
+
"Connection": "keep-alive",
|
256
|
+
"Sec-Fetch-Dest": "empty",
|
257
|
+
"Sec-Fetch-Mode": "cors",
|
258
|
+
"Sec-Fetch-Site": "same-origin",
|
259
|
+
"Pragma": "no-cache",
|
260
|
+
"Cache-Control": "no-cache",
|
261
|
+
})
|
262
|
+
|
263
|
+
# Faz a requisição para iniciar o login
|
264
|
+
login_url = "https://www.udemy.com/api-2.0/auth/code-generation/login/4.0/"
|
265
|
+
response = session.post(login_url, data=data, allow_redirects=False)
|
266
|
+
if 'error_message' in response.text:
|
267
|
+
erro_data: dict = response.json()
|
268
|
+
error_message = erro_data.get('error_message', {})
|
269
|
+
raise LoginException(error_message)
|
270
|
+
for attempt in range(3):
|
271
|
+
# Solicita o código OTP ao usuário
|
272
|
+
otp = input("Digite o código de 6 dígitos enviado ao seu e-mail: ")
|
273
|
+
# Realiza o login com o código OTP
|
274
|
+
otp_login_url = "https://www.udemy.com/api-2.0/auth/udemy-passwordless/login/4.0/"
|
275
|
+
otp_data = {
|
276
|
+
"email": email,
|
277
|
+
"fullname": "",
|
278
|
+
"otp": otp,
|
279
|
+
"subscribeToEmails": "false",
|
280
|
+
"upow": J(email, 'login')
|
281
|
+
}
|
282
|
+
session.headers.update({
|
283
|
+
"Referer": f"https://www.udemy.com/join/passwordless-auth/?locale={locale}&next="
|
284
|
+
f"https%3A%2F%2Fwww.udemy.com%2Fmobile%2Fipad%2F&response_type=html"
|
285
|
+
})
|
286
|
+
response = session.post(otp_login_url, otp_data, allow_redirects=False)
|
287
|
+
# Verifica se o login foi bem-sucedido
|
288
|
+
if response.status_code == 200:
|
289
|
+
self._save_cookies(session.cookies)
|
290
|
+
else:
|
291
|
+
if 'error_message' in response.text:
|
292
|
+
erro_data: dict = response.json()
|
293
|
+
error_message = erro_data.get('error_message', {})
|
294
|
+
error_code = erro_data.get('error_code', {})
|
295
|
+
if error_code == '1538':
|
296
|
+
raise LoginException(error_message)
|
297
|
+
elif error_code == '2550':
|
298
|
+
print(error_message)
|
299
|
+
continue
|
300
|
+
elif error_code == '1330':
|
301
|
+
raise LoginException(error_message)
|
302
|
+
elif error_code == '1149':
|
303
|
+
raise LoginException(f"Erro interno ao enviar os dados veja os detalhes: '{error_message}'")
|
304
|
+
raise LoginException(response.text)
|
305
|
+
break
|
306
|
+
except Exception as e:
|
307
|
+
if DEBUG:
|
308
|
+
error_details = traceback.format_exc()
|
309
|
+
else:
|
310
|
+
error_details = str(e)
|
311
|
+
raise LoginException(error_details)
|