udemy-userAPI 0.2.5__tar.gz → 0.2.6__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. {udemy_userapi-0.2.5/udemy_userAPI.egg-info → udemy_userapi-0.2.6}/PKG-INFO +2 -2
  2. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/README.md +1 -1
  3. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/README_PYPI.md +1 -1
  4. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/__version__.py +1 -1
  5. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/authenticate.py +91 -7
  6. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/bultins.py +20 -12
  7. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/udemy.py +7 -6
  8. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6/udemy_userAPI.egg-info}/PKG-INFO +2 -2
  9. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI.egg-info/SOURCES.txt +0 -12
  10. udemy_userapi-0.2.6/udemy_userAPI.egg-info/top_level.txt +1 -0
  11. udemy_userapi-0.2.5/animation_consoles/__init__.py +0 -1
  12. udemy_userapi-0.2.5/animation_consoles/animation.py +0 -64
  13. udemy_userapi-0.2.5/ffmpeg_for_python/__config__.py +0 -118
  14. udemy_userapi-0.2.5/ffmpeg_for_python/__init__.py +0 -8
  15. udemy_userapi-0.2.5/ffmpeg_for_python/__utils.py +0 -78
  16. udemy_userapi-0.2.5/ffmpeg_for_python/__version__.py +0 -6
  17. udemy_userapi-0.2.5/ffmpeg_for_python/exeptions.py +0 -91
  18. udemy_userapi-0.2.5/ffmpeg_for_python/ffmpeg.py +0 -203
  19. udemy_userapi-0.2.5/m3u8_analyzer/M3u8Analyzer.py +0 -807
  20. udemy_userapi-0.2.5/m3u8_analyzer/__init__.py +0 -7
  21. udemy_userapi-0.2.5/m3u8_analyzer/__version__.py +0 -1
  22. udemy_userapi-0.2.5/m3u8_analyzer/exeptions.py +0 -82
  23. udemy_userapi-0.2.5/udemy_userAPI.egg-info/top_level.txt +0 -4
  24. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/LICENSE +0 -0
  25. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/MANIFEST.in +0 -0
  26. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/setup.cfg +0 -0
  27. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/setup.py +0 -0
  28. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/.cache/.udemy_userAPI +0 -0
  29. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/__init__.py +0 -0
  30. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/api.py +0 -0
  31. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/exeptions.py +0 -0
  32. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/mpd_analyzer/__init__.py +0 -0
  33. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/mpd_analyzer/bin.wvd +0 -0
  34. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/mpd_analyzer/mpd_parser.py +0 -0
  35. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI/sections.py +0 -0
  36. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI.egg-info/dependency_links.txt +0 -0
  37. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI.egg-info/not-zip-safe +0 -0
  38. {udemy_userapi-0.2.5 → udemy_userapi-0.2.6}/udemy_userAPI.egg-info/requires.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: udemy_userAPI
3
- Version: 0.2.5
3
+ Version: 0.2.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
@@ -19,7 +19,7 @@ Requires-Dist: pywidevine
19
19
  # udemy-userAPI
20
20
 
21
21
 
22
- ![Versão](https://img.shields.io/badge/version-0.2.4-orange)
22
+ ![Versão](https://img.shields.io/badge/version-0.2.6-orange)
23
23
  ![Licença](https://img.shields.io/badge/license-MIT-orange)
24
24
  [![Sponsor](https://img.shields.io/badge/💲Donate-yellow)](https://apoia.se/paulocesar-dev404)
25
25
  [![Sponsor](https://img.shields.io/badge/Documentation-green)](https://github.com/PauloCesar-dev404/udemy-userAPI/wiki)
@@ -2,7 +2,7 @@
2
2
  <img src="assets/udemy_userAPI-logo.png" alt="udemy_userAPI-logo" width="200"/>
3
3
 
4
4
 
5
- ![Versão](https://img.shields.io/badge/version-0.2.4-orange)
5
+ ![Versão](https://img.shields.io/badge/version-0.2.6-orange)
6
6
  ![Licença](https://img.shields.io/badge/license-MIT-orange)
7
7
  [![Sponsor](https://img.shields.io/badge/💲Donate-yellow)](https://apoia.se/paulocesar-dev404)
8
8
  [![Sponsor](https://img.shields.io/badge/Documentation-green)](https://github.com/PauloCesar-dev404/udemy-userAPI/wiki)
@@ -1,7 +1,7 @@
1
1
  # udemy-userAPI
2
2
 
3
3
 
4
- ![Versão](https://img.shields.io/badge/version-0.2.4-orange)
4
+ ![Versão](https://img.shields.io/badge/version-0.2.6-orange)
5
5
  ![Licença](https://img.shields.io/badge/license-MIT-orange)
6
6
  [![Sponsor](https://img.shields.io/badge/💲Donate-yellow)](https://apoia.se/paulocesar-dev404)
7
7
  [![Sponsor](https://img.shields.io/badge/Documentation-green)](https://github.com/PauloCesar-dev404/udemy-userAPI/wiki)
@@ -1,4 +1,4 @@
1
- __version__ = '0.2.5'
1
+ __version__ = '0.2.6'
2
2
  __lib_name__ = 'udemy_userAPI' # local name
3
3
  __repo_name__ = 'udemy-userAPI'
4
4
  __autor__ = 'PauloCesar-dev404'
@@ -2,7 +2,7 @@ import json
2
2
  import os
3
3
  import pickle
4
4
  import traceback
5
-
5
+ from datetime import datetime
6
6
  import requests
7
7
  from .exeptions import UnhandledExceptions, UdemyUserApiExceptions, LoginException
8
8
  import cloudscraper
@@ -13,7 +13,7 @@ DEBUG = False
13
13
  class UdemyAuth:
14
14
  def __init__(self):
15
15
  """Autenticação na plataforma udemy de maneira segura, atencao ao limite de logins,recomendo que apos logar
16
- nao use novamnete o metodo login use apenas o verifcador de login para evitar bloqueios temporários..."""
16
+ nao use novamente o metodo login use apenas o verifcador de login para evitar bloqueios temporários..."""
17
17
  self.__cookie_dict = {}
18
18
  # Diretório do arquivo atual
19
19
  current_directory = os.path.dirname(__file__)
@@ -142,7 +142,7 @@ class UdemyAuth:
142
142
 
143
143
  # Verifica a resposta para determinar se o login foi bem-sucedido
144
144
  if "returnUrl" in r.text:
145
- self.__save_cookies(s.cookies)
145
+ self._save_cookies(s.cookies)
146
146
  else:
147
147
  login_error = r.json().get("error", {}).get("data", {}).get("formErrors", [])[0]
148
148
  if login_error[0] == "Y":
@@ -151,14 +151,12 @@ class UdemyAuth:
151
151
  raise LoginException("Email ou senha incorretos")
152
152
  else:
153
153
  raise UnhandledExceptions(login_error)
154
-
155
- return s
156
154
  except Exception as e:
157
155
  if DEBUG:
158
156
  e = traceback.format_exc()
159
157
  raise LoginException(e)
160
158
 
161
- def __save_cookies(self, cookies):
159
+ def _save_cookies(self, cookies):
162
160
  try:
163
161
  with open(fr'{self.__file_path}', 'wb') as f:
164
162
  pickle.dump(cookies, f)
@@ -187,5 +185,91 @@ class UdemyAuth:
187
185
  raise LoginException(f"Erro ao carregar cookies: {e}")
188
186
 
189
187
  def remove_cookies(self):
188
+ """remove os cookies salvos"""
190
189
  if os.path.exists(self.__file_path):
191
- os.remove(self.__file_path)
190
+ with open(self.__file_path, 'wb') as f:
191
+ f.write(b'')
192
+
193
+ def login_passwordless(self, email: str, locale: str = 'pt-BR'):
194
+ """
195
+ Realiza login na Udemy usando autenticação de dois fatores (2FA).
196
+
197
+ Este método utiliza o fluxo de autenticação OAuth da Udemy para enviar um
198
+ código de verificação por e-mail ao usuário. Após inserir o código recebido,
199
+ o login é concluído.
200
+
201
+ :param email: Email do usuário.
202
+ :param locale: Localização do usuário (recomendado para receber mensagens no idioma local).
203
+ :raises LoginException: Em caso de falha no processo de login.
204
+ """
205
+ try:
206
+ # Inicializa uma sessão com proteção contra Cloudflare
207
+ session = cloudscraper.create_scraper()
208
+
209
+ # Requisita a página de inscrição para obter o token CSRF
210
+ signup_url = "https://www.udemy.com/join/signup-popup/"
211
+ headers = {"User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)"}
212
+ response = session.get(signup_url, headers=headers)
213
+
214
+ # Obtém o token CSRF dos cookies retornados
215
+ csrf_token = response.cookies.get("csrftoken")
216
+ if not csrf_token:
217
+ raise LoginException("Não foi possível obter o token CSRF.")
218
+
219
+ # Prepara os dados do login
220
+ data = {"email": email, "fullname": ""}
221
+
222
+ # Atualiza os cookies e cabeçalhos da sessão
223
+ session.cookies.update(response.cookies)
224
+ session.headers.update({
225
+ "User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)",
226
+ "Accept": "application/json, text/plain, */*",
227
+ "Accept-Language": locale,
228
+ "Referer": f"https://www.udemy.com/join/passwordless-auth/?locale={locale.replace('-', '_')}&next="
229
+ f"https%3A%2F%2Fwww.udemy.com%2Fmobile%2Fipad%2F&response_type=html",
230
+ "Origin": "https://www.udemy.com",
231
+ "DNT": "1",
232
+ "Connection": "keep-alive",
233
+ "Sec-Fetch-Dest": "empty",
234
+ "Sec-Fetch-Mode": "cors",
235
+ "Sec-Fetch-Site": "same-origin",
236
+ "Pragma": "no-cache",
237
+ "Cache-Control": "no-cache",
238
+ })
239
+
240
+ # Faz a requisição para iniciar o login
241
+ login_url = "https://www.udemy.com/api-2.0/auth/code-generation/login/4.0/"
242
+ response = session.post(login_url, data=data, allow_redirects=False)
243
+ if 'error_message' in response.text:
244
+ raise LoginException(f"Erro no login inicial: {response.text}")
245
+
246
+ # Solicita o código OTP ao usuário
247
+ otp = input("Digite o código de 6 dígitos enviado ao seu e-mail: ")
248
+ upow = datetime.now().strftime("%Y-%m-%d")
249
+ # Realiza o login com o código OTP
250
+ otp_login_url = "https://www.udemy.com/api-2.0/auth/udemy-passwordless/login/4.0/"
251
+ otp_data = {
252
+ "email": email,
253
+ "fullname": "",
254
+ "otp": otp,
255
+ "subscribeToEmails": "false",
256
+ "upow": f"{upow}XBY",
257
+ }
258
+ session.headers.update({
259
+ "Referer": f"https://www.udemy.com/join/passwordless-auth/?locale={locale}&next="
260
+ f"https%3A%2F%2Fwww.udemy.com%2Fmobile%2Fipad%2F&response_type=html"
261
+ })
262
+ response = session.post(otp_login_url, otp_data, allow_redirects=False)
263
+
264
+ if response.status_code == 200:
265
+ self._save_cookies(session.cookies)
266
+ else:
267
+ raise LoginException(f"Falha no login OTP: {response.text}")
268
+ except Exception as e:
269
+ if DEBUG:
270
+ error_details = traceback.format_exc()
271
+ else:
272
+ error_details = str(e)
273
+ raise LoginException(error_details)
274
+
275
+
@@ -10,9 +10,10 @@ from .mpd_analyzer import MPDParser
10
10
  class DRM:
11
11
  def __init__(self, license_token: str, get_media_sources: list):
12
12
  self.__mpd_content = None
13
- self.__dash_url = organize_streams(streams=get_media_sources).get('dash')
14
13
  self.__token = license_token
15
- self.get_key_for_lesson()
14
+ self.__dash_url = organize_streams(streams=get_media_sources).get('dash', {})
15
+ if not license_token or not get_media_sources or not self.__dash_url:
16
+ return
16
17
 
17
18
  def get_key_for_lesson(self):
18
19
  """get keys for lesson"""
@@ -28,6 +29,12 @@ class DRM:
28
29
  keys = extract(pssh=pssh, license_token=self.__token)
29
30
  if keys:
30
31
  return keys
32
+ else:
33
+ return None
34
+ else:
35
+ return None
36
+ else:
37
+ return None
31
38
 
32
39
 
33
40
  class Files:
@@ -44,11 +51,13 @@ class Files:
44
51
  lecture_id = files.get('lecture_id', None)
45
52
  asset_id = files.get('asset_id', None)
46
53
  title = files.get("title", None)
47
- lecture_title = files.get('lecture_title')
48
- external_link = files.get('ExternalLink')
54
+ lecture_title = files.get('lecture_title', None)
55
+ external_link = files.get('ExternalLink', None)
49
56
  if external_link:
50
57
  lnk = get_external_liks(course_id=self.__id_course, id_lecture=lecture_id, asset_id=asset_id)
51
- dt_file = {'title-file': title, 'lecture_title': lecture_title, 'lecture_id': lecture_id,
58
+ dt_file = {'title-file': title,
59
+ 'lecture_title': lecture_title,
60
+ 'lecture_id': lecture_id,
52
61
  'external_link': external_link,
53
62
  'data-file': lnk.get('external_url', None)}
54
63
  return dt_file
@@ -59,8 +68,9 @@ class Files:
59
68
  headers=HEADERS_USER)
60
69
  if resp.status_code == 200:
61
70
  da = json.loads(resp.text)
62
- ## para cdaa dict de um fle colocar seu titulo:
63
- dt_file = {'title-file': title, 'lecture_title': lecture_title, 'lecture_id': lecture_id,
71
+ dt_file = {'title-file': title,
72
+ 'lecture_title': lecture_title,
73
+ 'lecture_id': lecture_id,
64
74
  'external_link': external_link,
65
75
  'data-file': da['download_urls']}
66
76
  download_urls.append(dt_file)
@@ -128,12 +138,10 @@ class Lecture:
128
138
  def course_is_drmed(self) -> DRM:
129
139
  """verifica se a aula possui DRM se sim retorna as keys da aula...
130
140
  retorna 'kid:key' or None"""
131
- if self.__asset.get('course_is_drmed'):
141
+ if self.__asset.get('course_is_drmed', {}):
132
142
  d = DRM(license_token=self.get_media_license_token,
133
143
  get_media_sources=self.get_media_sources)
134
144
  return d
135
- else:
136
- return self.__asset.get('course_is_drmed')
137
145
 
138
146
  @property
139
147
  def get_download_urls(self) -> list:
@@ -267,8 +275,8 @@ class Course:
267
275
  for item in self.__additional_files_data.get('results', []):
268
276
  # Check if the item is a lecture with supplementary assets
269
277
  if item.get('_class') == 'lecture':
270
- id = item.get('id')
271
- title = item.get('title')
278
+ id = item.get('id', {})
279
+ title = item.get('title', {})
272
280
  assets = item.get('supplementary_assets', [])
273
281
  for asset in assets:
274
282
  supplementary_assets.append({
@@ -12,22 +12,23 @@ class Udemy:
12
12
  """wrapper para api de usuario da plataforma udemy"""
13
13
 
14
14
  def __init__(self):
15
- """
16
- cookies de seção.
17
- """
18
15
  self.__headers = HEADERS_USER
19
16
  if verif_login is None:
20
17
  raise LoginException("User Not Logged!")
21
18
 
22
- def my_subscribed_courses_by_plan(self) -> list[dict]:
23
- """obtém os cursos que o usuário esatá inscrito, obtidos atraves de planos(assinatura)"""
19
+ @staticmethod
20
+ def my_subscribed_courses_by_plan() -> list[dict]:
21
+ """obtém os cursos que o usuário esatá inscrito, obtidos atraves de planos(assinatura)
22
+ :return:
23
+ """
24
24
  try:
25
25
  courses = get_courses_plan(tipe='plan')
26
26
  return courses
27
27
  except UdemyUserApiExceptions as e:
28
28
  UnhandledExceptions(e)
29
29
 
30
- def my_subscribed_courses(self) -> list[dict]:
30
+ @staticmethod
31
+ def my_subscribed_courses() -> list[dict]:
31
32
  """Obtém os cursos que o usuário está inscrito, excluindo listas vazias ou nulas"""
32
33
  try:
33
34
  # Obtém os cursos
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: udemy_userAPI
3
- Version: 0.2.5
3
+ Version: 0.2.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
@@ -19,7 +19,7 @@ Requires-Dist: pywidevine
19
19
  # udemy-userAPI
20
20
 
21
21
 
22
- ![Versão](https://img.shields.io/badge/version-0.2.4-orange)
22
+ ![Versão](https://img.shields.io/badge/version-0.2.6-orange)
23
23
  ![Licença](https://img.shields.io/badge/license-MIT-orange)
24
24
  [![Sponsor](https://img.shields.io/badge/💲Donate-yellow)](https://apoia.se/paulocesar-dev404)
25
25
  [![Sponsor](https://img.shields.io/badge/Documentation-green)](https://github.com/PauloCesar-dev404/udemy-userAPI/wiki)
@@ -3,18 +3,6 @@ MANIFEST.in
3
3
  README.md
4
4
  README_PYPI.md
5
5
  setup.py
6
- animation_consoles/__init__.py
7
- animation_consoles/animation.py
8
- ffmpeg_for_python/__config__.py
9
- ffmpeg_for_python/__init__.py
10
- ffmpeg_for_python/__utils.py
11
- ffmpeg_for_python/__version__.py
12
- ffmpeg_for_python/exeptions.py
13
- ffmpeg_for_python/ffmpeg.py
14
- m3u8_analyzer/M3u8Analyzer.py
15
- m3u8_analyzer/__init__.py
16
- m3u8_analyzer/__version__.py
17
- m3u8_analyzer/exeptions.py
18
6
  udemy_userAPI/__init__.py
19
7
  udemy_userAPI/__version__.py
20
8
  udemy_userAPI/api.py
@@ -0,0 +1 @@
1
+ udemy_userAPI
@@ -1 +0,0 @@
1
- from .animation import AnimationConsole
@@ -1,64 +0,0 @@
1
- import sys
2
- import time
3
- import threading
4
- from colorama import init, Fore, Style
5
-
6
- # Inicializa o suporte a cores no Windows
7
- init(autoreset=True)
8
-
9
-
10
- class AnimationConsole:
11
- def __init__(self, text="Loading", color=Fore.GREEN, color_frame=Fore.BLUE):
12
- """
13
- Cria uma animação de loading com uma mensagem colorida no console.
14
- :param text: Texto inicial da mensagem de loading.
15
- :param color: Cor do texto, usando Fore do colorama.
16
- """
17
- self._color_frame = color_frame
18
- self._text = text
19
- self._color = color
20
- self._running = False
21
- self._animation_thread = None
22
- self._frames = ["-", "\\", "|", "/"]
23
- self._index = 0
24
-
25
- def start(self):
26
- """
27
- Inicia a animação no console.
28
- """
29
- if self._running:
30
- return # Previne múltiplas execuções
31
- self._running = True
32
- self._animation_thread = threading.Thread(target=self._animate, daemon=True)
33
- self._animation_thread.start()
34
-
35
- def stop(self):
36
- """
37
- Para a animação no console.
38
- """
39
- self._running = False
40
- if self._animation_thread:
41
- self._animation_thread.join()
42
- sys.stdout.write("\r" + " " * (len(self._text) + 20) + "\r") # Limpa a linha
43
-
44
- def update_message(self, new_text, new_color=None):
45
- """
46
- Atualiza a mensagem exibida junto à animação.
47
- :param new_text: Novo texto a ser exibido.
48
- :param new_color: Nova cor para o texto (opcional).
49
- """
50
- self._text = new_text
51
- if new_color:
52
- self._color = new_color
53
-
54
- def _animate(self):
55
- """
56
- Animação interna do console.
57
- """
58
- while self._running:
59
- frame = self._frames[self._index]
60
- self._index = (self._index + 1) % len(self._frames)
61
- sys.stdout.write(
62
- f"\r{self._color}{self._text}{Style.RESET_ALL} {self._color_frame}{frame}{Style.RESET_ALL}")
63
- sys.stdout.flush()
64
- time.sleep(0.1)
@@ -1,118 +0,0 @@
1
- import sys
2
- import os
3
- import requests
4
- import zipfile
5
- import shutil
6
- import stat
7
- from .exeptions import *
8
- from .__utils import URL_PLATAFOMR, system
9
-
10
- lib_name = os.path.dirname(__file__)
11
- URL_BASE_REPO = "https://raw.githubusercontent.com/PauloCesar-dev404/binarios/main/"
12
-
13
-
14
- class Configurate:
15
- """Configura variáveis de ambiente no ambiente virtual ou globalmente."""
16
-
17
- def __init__(self):
18
- self.VERSION = self.__read_version
19
- self.FFMPEG_URL = os.getenv('FFMPEG_URL')
20
- self.FFMPEG_BINARY = os.getenv('FFMPEG_BINARY')
21
- PATH = os.path.join(lib_name, 'ffmpeg-bin')
22
- os.makedirs(PATH, exist_ok=True)
23
- dirpath = PATH
24
- self.INSTALL_DIR = os.getenv('INSTALL_DIR', dirpath)
25
- self.configure()
26
-
27
- def configure(self):
28
- """Configura as variáveis de ambiente com base no sistema operacional."""
29
- if not self.FFMPEG_URL or not self.FFMPEG_BINARY:
30
- platform_name = system
31
- if platform_name == 'Windows':
32
- self.FFMPEG_URL = URL_PLATAFOMR
33
- self.FFMPEG_BINARY = 'ffmpeg.exe'
34
- elif platform_name == 'Linux':
35
- self.FFMPEG_URL = URL_PLATAFOMR
36
- self.FFMPEG_BINARY = 'ffmpeg'
37
- else:
38
- raise DeprecationWarning(f"Arquitetura '{platform_name}' ainda não suportada...\n\n"
39
- f"Versão atual da lib: {self.VERSION}")
40
- os.environ['FFMPEG_URL'] = self.FFMPEG_URL
41
- os.environ['FFMPEG_BINARY'] = self.FFMPEG_BINARY
42
-
43
- if not os.getenv('INSTALL_DIR'):
44
- os.environ['INSTALL_DIR'] = self.INSTALL_DIR
45
-
46
- @property
47
- def __read_version(self):
48
- """Lê a versão do arquivo __version__.py."""
49
- version_file = os.path.join(os.path.dirname(os.path.abspath(__file__)).split('.')[0], '__version__.py')
50
- if os.path.isfile(version_file):
51
- with open(version_file, 'r') as file:
52
- version_line = file.readline().strip()
53
- if version_line.startswith('__version__'):
54
- return version_line.split('=')[1].strip().strip("'")
55
- return 'Unknown Version'
56
-
57
- def __download_file(self, url: str, local_filename: str):
58
- """Baixa um arquivo do URL para o caminho local especificado."""
59
- try:
60
- response = requests.get(url, stream=True)
61
- response.raise_for_status()
62
- total_length = int(response.headers.get('content-length', 0))
63
-
64
- with open(local_filename, 'wb') as f:
65
- start_time = time.time()
66
- downloaded = 0
67
-
68
- for data in response.iter_content(chunk_size=4096):
69
- downloaded += len(data)
70
- f.write(data)
71
-
72
- elapsed_time = time.time() - start_time
73
- elapsed_time = max(elapsed_time, 0.001)
74
- speed_kbps = (downloaded / 1024) / elapsed_time
75
- percent_done = (downloaded / total_length) * 100
76
-
77
-
78
- except requests.RequestException as e:
79
- raise Exception(f"Erro durante o download: {e}")
80
-
81
- def __extract_zip(self, zip_path: str, extract_to: str):
82
- """Descompacta o arquivo ZIP no diretório especificado."""
83
- try:
84
- with zipfile.ZipFile(zip_path, 'r') as zip_ref:
85
- zip_ref.extractall(extract_to)
86
- except zipfile.BadZipFile as e:
87
- sys.stderr.write(f"Erro ao descompactar o arquivo: {e}\n")
88
- raise
89
- finally:
90
- os.remove(zip_path)
91
-
92
- def remove_file(self, file_path: str):
93
- """Remove o arquivo ou diretório especificado."""
94
- if os.path.exists(file_path):
95
- try:
96
- shutil.rmtree(file_path, onerror=self.handle_remove_readonly)
97
- except Exception as e:
98
- print(f"Erro ao remover {file_path}: {e}")
99
- raise
100
-
101
- def install_bins(self):
102
- """Instala o ffmpeg baixando e descompactando o binário apropriado."""
103
- zip_path = os.path.join(self.INSTALL_DIR, "ffmpeg.zip")
104
- os.makedirs(self.INSTALL_DIR, exist_ok=True)
105
- self.__download_file(self.FFMPEG_URL, zip_path)
106
- self.__extract_zip(zip_path, self.INSTALL_DIR)
107
- self.remove_file(zip_path)
108
- os.environ["PATH"] += os.pathsep + self.INSTALL_DIR
109
- return
110
-
111
- def handle_remove_readonly(self, func, path, exc_info):
112
- """Callback para lidar com arquivos somente leitura."""
113
- os.chmod(path, stat.S_IWRITE)
114
- func(path)
115
-
116
-
117
- if __name__ == "__main__":
118
- FFmpegExceptions("erro de runtime...")
@@ -1,8 +0,0 @@
1
- # ffmpeg-and-python/__init__.py
2
-
3
- from .ffmpeg import FFmpeg
4
- from .exeptions import FFmpegExceptions
5
-
6
- __all__ = ['FFmpeg', 'FFmpegExceptions']
7
- if __name__ == '__main__':
8
- raise RuntimeError("no escope!")
@@ -1,78 +0,0 @@
1
- import platform
2
- import os
3
-
4
- URL_PLATAFOMR = ''
5
-
6
-
7
- def get_processor_info():
8
- system = platform.system()
9
- architecture = platform.architecture()[0]
10
- processor = ''
11
- if system == "Windows":
12
- processor = platform.processor()
13
- elif system in ["Linux", "Darwin"]: # Darwin é o nome do sistema para macOS
14
- try:
15
- if system == "Linux":
16
- # Obtém informações detalhadas do processador no Linux
17
- with open("/proc/cpuinfo") as f:
18
- cpuinfo = f.read()
19
- if "model name" in cpuinfo:
20
- processor = cpuinfo.split("model name")[1].split(":")[1].split("\n")[0].strip()
21
- else:
22
- processor = "Unknown"
23
- elif system == "Darwin":
24
- # Obtém informações detalhadas do processador no macOS
25
- processor = os.popen("sysctl -n machdep.cpu.brand_string").read().strip()
26
- except FileNotFoundError:
27
- processor = "Unknown"
28
- d = (f"System: {system} "
29
- f"Architecture: {architecture} "
30
- f"Processor: {processor} ")
31
- return d
32
-
33
-
34
- # Processa a informação do processador e limpa a string
35
- data = (get_processor_info().replace('Architecture:', '').replace('System:', '').
36
- replace('Processor:', '').strip().split())
37
-
38
- # Remove entradas vazias e limpa espaços em branco
39
- cleaned_data = [item.strip() for item in data if item.strip()]
40
-
41
- # Garantindo que há pelo menos três elementos
42
- if len(cleaned_data) >= 2:
43
- system = cleaned_data[0]
44
- architecture = cleaned_data[1]
45
- processor = ' '.join(cleaned_data[2:]) # Junta o restante como o processador
46
-
47
- URL_BASE_REPO = "https://raw.githubusercontent.com/PauloCesar-dev404/binarios/main/"
48
- # Mapeamento para Linux
49
- linux_mapping = {
50
- "x86_64": "amd64",
51
- "i686": "i686",
52
- "arm64": "arm64",
53
- "armhf": "armhf",
54
- "armel": "armel"
55
- }
56
- # Formata a URL com base no sistema e arquitetura
57
- if system == "Linux" and ('intel' in processor.lower() or 'amd' in processor.lower()):
58
- url = f"{URL_BASE_REPO}linux/ffmpeg-7.0.2-{linux_mapping.get('x86_64')}.zip"
59
- elif system == "Linux" and 'i686' in architecture.lower():
60
- url = f"{URL_BASE_REPO}linux/ffmpeg-7.0.2-{linux_mapping.get('i686')}.zip"
61
- elif system == "Linux" and 'arm64' in architecture.lower():
62
- url = f"{URL_BASE_REPO}linux/ffmpeg-7.0.2-{linux_mapping.get('arm64')}.zip"
63
- elif system == "Linux" and 'armhf' in architecture.lower():
64
- url = f"{URL_BASE_REPO}linux/ffmpeg-7.0.2-{linux_mapping.get('armhf')}.zip"
65
- elif system == "Linux" and 'armel' in architecture.lower():
66
- url = f"{URL_BASE_REPO}linux/ffmpeg-7.0.2-{linux_mapping.get('armel')}.zip"
67
- elif system == "Windows" and architecture == '64bit':
68
- url = f"{URL_BASE_REPO}windows/win-ffmpeg-7.0.2-full-amd64-intel64.zip"
69
- else:
70
- url = f"Unsupported system or architecture"
71
-
72
- URL_PLATAFOMR = url
73
-
74
- else:
75
- raise DeprecationWarning("Não foi possível obter seu sistema ....consulte o desenvolvedor!")
76
-
77
- if __name__ == '__main__':
78
- raise RuntimeError("este é uma função interna!")
@@ -1,6 +0,0 @@
1
- __version__ = '0.3.5'
2
- __lib_name__ = 'ffmpeg_for_python'
3
- __autor__ = 'PauloCesar-dev404'
4
- __repo__ = 'https://github.com/PauloCesar-dev404/ffmpeg-for-python'
5
- __lib__ = f'https://raw.githubusercontent.com/PauloCesar-dev404/ffmpeg-for-python/main/{__lib_name__}-{__version__}-py3-none-any.whl'
6
- __source__ = f'https://raw.githubusercontent.com/PauloCesar-dev404/ffmpeg-for-python/main/{__lib_name__}-{__version__}.tar.gz'