udemy-userAPI 0.3.7__py3-none-any.whl → 0.3.8__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ from .animation import AnimationConsole
@@ -0,0 +1,64 @@
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)
@@ -0,0 +1,118 @@
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...")
@@ -0,0 +1,8 @@
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!")
@@ -0,0 +1,78 @@
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!")
@@ -0,0 +1,6 @@
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'
@@ -0,0 +1,91 @@
1
+ import time
2
+
3
+ INPUT_ERROR = [
4
+ 'Error opening input: No such file or directory',
5
+ 'Error opening input: Permission denied',
6
+ 'Error opening input: Invalid argument',
7
+ 'Error opening input: Protocol not found',
8
+ 'Error opening input: Unsupported protocol',
9
+ 'Error opening input: File format not recognized',
10
+ 'Error opening input: Could not open file',
11
+ 'Error opening input: Invalid data found when processing input',
12
+ 'Error opening input: Input stream is empty',
13
+ 'Error opening input: Cannot open file for reading',
14
+ 'Error opening input: File is too short',
15
+ 'Error opening input: End of file while parsing input',
16
+ 'Error opening input: Codec not found',
17
+ 'Error opening input: No decoder for codec',
18
+ 'Error opening input: Stream not found',
19
+ 'Error opening input: Stream codec not found',
20
+ 'Error opening input: Stream index out of range',
21
+ 'Error opening input: Invalid timestamp',
22
+ 'Error opening input: Corrupt file',
23
+ 'Error opening input: Unsupported codec',
24
+ 'Error opening input: Failed to initialize filter',
25
+ 'Error opening input: Error while opening codec',
26
+ 'Error opening input: Device not found',
27
+ 'Error opening input: Device or resource busy',
28
+ 'Error opening input: Invalid option',
29
+ 'Error opening input: Unable to seek',
30
+ 'Error opening input: Input format not found'
31
+ ]
32
+ OUTPUT_ERROR = [
33
+ 'Error opening output file: No such file or directory',
34
+ 'Error opening output file: Permission denied',
35
+ 'Error opening output file: Invalid argument',
36
+ 'Error opening output file: Unsupported protocol',
37
+ 'Error opening output file: Protocol not found',
38
+ 'Error opening output file: File format not recognized',
39
+ 'Error opening output file: Could not open file for writing',
40
+ 'Error opening output file: Disk full or quota exceeded',
41
+ 'Error opening output file: Cannot create file',
42
+ 'Error opening output file: Invalid data found when processing output',
43
+ 'Error opening output file: Output stream not found',
44
+ 'Error opening output file: Cannot write to file',
45
+ 'Error opening output file: File already exists',
46
+ 'Error opening output file: Unsupported codec',
47
+ 'Error opening output file: Codec not found',
48
+ 'Error opening output file: Cannot open codec for writing',
49
+ 'Error opening output file: Failed to initialize filter',
50
+ 'Error opening output file: Invalid option',
51
+ 'Error opening output file: Invalid timestamp',
52
+ 'Error opening output file: Corrupt file',
53
+ 'Error opening output file: Device or resource busy',
54
+ 'Error opening output file: Cannot seek',
55
+ 'Error opening output file: Stream index out of range',
56
+ 'Error opening output file: Stream codec not found'
57
+ ]
58
+ ERROS = []
59
+ for er in INPUT_ERROR:
60
+ ERROS.append(er)
61
+ for er in OUTPUT_ERROR:
62
+ ERROS.append(er)
63
+
64
+
65
+ class FFmpegExceptions(Exception):
66
+ def __init__(self, message: str):
67
+ super().__init__(message)
68
+
69
+ def __str__(self):
70
+ """
71
+ Retorna a representação em string da exceção.
72
+
73
+ Returns:
74
+ str: Mensagem de erro formatada com detalhes adicionais, se presentes.
75
+ """
76
+
77
+ return super().__str__()
78
+
79
+
80
+ def wraper_erros(line: str):
81
+ """Verifica se a linha de saida do ffmpeg está no dict de erros e retorna sua categoria"""
82
+
83
+ if "Error" in line:
84
+ erro = line.split('Error')[1]
85
+ return erro.strip()
86
+ elif 'already exists. Overwrite? [y/N]' in line:
87
+ erro = line.split('File')[1]
88
+ return erro.strip()
89
+
90
+
91
+
@@ -0,0 +1,203 @@
1
+ import os
2
+ import platform
3
+ import subprocess
4
+ import time
5
+ from typing import List
6
+ from .__config__ import Configurate
7
+ from .exeptions import wraper_erros, FFmpegExceptions
8
+
9
+ parser = Configurate()
10
+ parser.configure()
11
+ ffmpeg_binarie = os.path.join(parser.INSTALL_DIR, parser.FFMPEG_BINARY)
12
+
13
+
14
+ def ffmpeg(cls):
15
+ """Decorador que instancia automaticamente a classe FFmpeg."""
16
+
17
+ def wrapper(*args, **kwargs):
18
+ # Cria e retorna uma instância da classe FFmpeg
19
+ instance = cls(*args, **kwargs)
20
+ return instance
21
+
22
+ return wrapper
23
+
24
+
25
+ @ffmpeg
26
+ class FFmpeg:
27
+ """Wrapper para o binário FFmpeg, permitindo o uso de comandos FFmpeg via Python."""
28
+
29
+ def __init__(self):
30
+ # Verifica se o binário do FFmpeg existe
31
+ if not self.__verify_path(bin_path=ffmpeg_binarie, path_type='file'):
32
+ parser.install_bins()
33
+ self.__ffmpeg_path = str(ffmpeg_binarie)
34
+ self.__command = [self.__ffmpeg_path]
35
+ self.__overwrite_output = False
36
+
37
+ def args(self, arguments: List[str]) -> 'FFmpeg':
38
+ """Adiciona múltiplos argumentos personalizados ao comando FFmpeg."""
39
+ if not all(isinstance(arg, str) for arg in arguments):
40
+ raise TypeError("All arguments should be provided as strings.")
41
+ self.__command.extend(arguments)
42
+ return self
43
+
44
+ def run(self, capture_output: bool = False):
45
+ """Executa o comando FFmpeg construído.
46
+
47
+ Args:
48
+ capture_output (bool): Se verdadeiro, captura a saída e printa no console.
49
+ Se False, a saída é retornada assincronamente em tempo real.
50
+
51
+ Yields:
52
+ str: Cada linha da saída filtrada do FFmpeg quando capture_output é False.
53
+ """
54
+
55
+ configs = self.__oculte_comands_your_system.get('startupinfo')
56
+
57
+ # Executa o comando utilizando subprocess
58
+ with subprocess.Popen(
59
+ self.__command,
60
+ startupinfo=configs,
61
+ stdout=subprocess.PIPE,
62
+ stderr=subprocess.PIPE,
63
+ stdin=subprocess.PIPE,
64
+ text=True,
65
+ bufsize=1, # Linha por linha
66
+ universal_newlines=True # Garante que novas linhas sejam interpretadas corretamente
67
+ ) as process:
68
+ try:
69
+ # Lê a saída de erro padrão (stderr) em tempo real
70
+ for linha in process.stderr:
71
+ time.sleep(0.01) # Simulação de latência, opcional
72
+
73
+ # Filtra a linha de erro padrão
74
+ linha_filtrada = wraper_erros(linha)
75
+
76
+ if linha_filtrada:
77
+ # Se houver um erro detectado, o processo é encerrado
78
+ process.terminate()
79
+ raise FFmpegExceptions(message=f'Erro na execução do ffmpeg: "{linha_filtrada}"')
80
+ else:
81
+ if capture_output:
82
+ # Se `capture_output` estiver ativado, imprime a saída
83
+ print(linha.strip())
84
+ else:
85
+ # Retorna a linha assincronamente quando capture_output é False
86
+ yield linha.strip()
87
+
88
+ # Aguarda a conclusão do processo
89
+ process.wait()
90
+
91
+ except Exception as e:
92
+ process.terminate()
93
+ raise FFmpegExceptions(message=f'Erro no processo FFmpeg: {str(e)}')
94
+
95
+ def input(self, file_path: str) -> 'FFmpeg':
96
+ """Define o arquivo de entrada para o FFmpeg."""
97
+ cmd = ['-i', file_path]
98
+ self.args(cmd)
99
+ return self
100
+
101
+ def output(self, output_path: str) -> 'FFmpeg':
102
+ """Define o arquivo de saída para o FFmpeg e verifica se a sobrescrita está permitida."""
103
+ if os.path.exists(output_path):
104
+ if not self.__overwrite_output:
105
+ raise FFmpegExceptions(f"O arquivo de saída '{output_path}' já existe! Use 'overwrite_output' para "
106
+ f"sobrescrevê-lo.")
107
+
108
+ # Adiciona o arquivo de saída ao comando
109
+ cmd = [output_path]
110
+ self.args(cmd)
111
+ return self
112
+
113
+ @property
114
+ def overwrite_output(self):
115
+ """
116
+ Adiciona o parâmetro '-y' ao comando FFmpeg, o que permite sobrescrever o arquivo de saída
117
+ caso ele já exista.
118
+
119
+ Importante: Esta propriedade deve ser definida antes dos parâmetros de entrada e saída
120
+ para garantir que o comando FFmpeg seja construído corretamente. Caso contrário, o comando
121
+ pode não ser executado como esperado.
122
+
123
+ Returns:
124
+ FFmpeg: Retorna a instância atual para encadeamento de métodos.
125
+ """
126
+ self.__overwrite_output = True
127
+ cmd = ['-y']
128
+ self.args(cmd)
129
+ return self
130
+
131
+ @property
132
+ def hide_banner(self) -> 'FFmpeg':
133
+ """oculta o baner do ffmpeg"""
134
+ self.__command.extend(['-hide_banner'])
135
+ return self
136
+
137
+ @property
138
+ def copy(self) -> 'FFmpeg':
139
+ """Adiciona o parâmetro '-c copy"""
140
+ self.__command.extend(['-c', 'copy'])
141
+ return self
142
+
143
+ @property
144
+ def copy_codecs(self):
145
+ """para remuxar"""
146
+ self.__command.extend(['-c:a', 'copy', '-c:v', 'copy'])
147
+ return self
148
+
149
+ def reset_ffmpeg(self) -> 'FFmpeg':
150
+ """Reseta o comando para reutilização do objeto,isso é necessário em caso de uso em loops,
151
+ a cada execução execute o reset para ter certeza que estar limpo o cache de comandos"""
152
+ self.__command = [self.__ffmpeg_path]
153
+ return self
154
+
155
+ @staticmethod
156
+ def __verify_path(bin_path, path_type: str) -> bool:
157
+ """
158
+ Verifica se um caminho de arquivo ou diretório existe.
159
+
160
+ Args:
161
+ bin_path : O caminho a ser verificado.
162
+ path_type (str): O tipo de caminho ('file' para arquivo, 'dir' para diretório).
163
+
164
+ Returns:
165
+ bool: True se o caminho existir e for do tipo especificado, False caso contrário.
166
+ """
167
+ if path_type == 'file':
168
+ return os.path.isfile(bin_path)
169
+ elif path_type == 'dir':
170
+ return os.path.isdir(bin_path)
171
+ else:
172
+ raise ValueError("Invalid path_type. Use 'file' or 'dir'.")
173
+
174
+ @property
175
+ def __oculte_comands_your_system(self) -> dict:
176
+ """Identifica o sistema do usuário e cria um dicionário de parâmetros para ocultar saídas de janelas e do
177
+ terminal."""
178
+ system_user = platform.system()
179
+ startupinfo_options = {}
180
+
181
+ if system_user == "Windows":
182
+ # Configuração específica para ocultar o terminal no Windows
183
+ startupinfo = subprocess.STARTUPINFO()
184
+ startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
185
+ startupinfo.wShowWindow = subprocess.SW_HIDE
186
+ startupinfo_options['startupinfo'] = startupinfo
187
+
188
+ # Definindo stdout, stderr e stdin para DEVNULL para esconder saídas
189
+ startupinfo_options['stdout'] = subprocess.DEVNULL
190
+ startupinfo_options['stderr'] = subprocess.DEVNULL
191
+ startupinfo_options['stdin'] = subprocess.DEVNULL
192
+
193
+ elif system_user in ["Linux", "Darwin"]:
194
+ # Para Linux e macOS, ocultar stdout, stderr e stdin
195
+ startupinfo_options['stdout'] = subprocess.DEVNULL
196
+ startupinfo_options['stderr'] = subprocess.DEVNULL
197
+ startupinfo_options['stdin'] = subprocess.DEVNULL
198
+
199
+ else:
200
+ # Exceção para sistemas não suportados
201
+ raise NotImplementedError(f"O sistema {system_user} não é suportado para ocultação de comandos.")
202
+
203
+ return startupinfo_options