Comandos Essenciais do FFmpeg
Para automatizar a edição de vídeos, como a remoção de introduções e encerramentos, o ffmpeg e o ffprobe são ferramentas fundamentais. Abaixo estão os comandos base que serão integrados ao script Python.
1. Obtenção da Duração do Vídeo
Utilizamos o ffprobe para extrair a duração exata do arquivo. Para facilitar o parsing no Python, configuramos a saída para retornar apenas o valor numérico, sem formatação extra.
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "caminho_do_video.mp4"
2. Corte do Vídeo sem Recodificação
O comando abaixo extrai um segemnto do vídeo copiando os streams de áudio e vídeo diretamente (-c copy), o que garante um processamento extremamente rápido.
ffmpeg -i "entrada.mp4" -ss 00:02:35 -t 900 -c copy -y "saida.mp4"
-ss: Ponto de início do corte (formatohh:mm:ssou segundos).-t: Duração do segmento a ser extraído a partir do ponto de início.-c copy: Copia os codecs originais, evitando a recodificação.-y: Sobrescreve o arquivo de saída caso já exista.
Integração Python com Comandos de Sistema
A biblioteca nativa subprocess é a abordagem recomendada para executar binários externos. Passar os argumentos como uma lista, em vez de uma string única, previne erros de injeção e problemas com espaços nos nomes de arquivos.
import subprocess
def executar_comando(cmd_list):
try:
resultado = subprocess.run(
cmd_list,
check=True,
capture_output=True,
text=True,
encoding='utf-8'
)
return resultado.stdout.strip()
except subprocess.CalledProcessError as erro:
print(f"Erro na execução: {erro.stderr}")
return None
Implementação do Processamento em Lote
Módulo de Utilitários
Centralizamos as funções de filtragem de arquivos e conversão de tempo em um módulo auxiliar para manter o código principal limpo.
# utils.py
import re
from pathlib import Path
from typing import List, Optional
def filtrar_arquivos_midia(diretorio: str, extensoes: List[str],
ids_incluir: Optional[List[int]] = None,
ids_excluir: Optional[List[int]] = None) -> List[Path]:
"""Retorna uma lista de objetos Path filtrados por extensão e padrões numéricos no nome."""
caminho_base = Path(diretorio)
arquivos_validos = []
for ext in extensoes:
for arquivo in caminho_base.glob(f"*{ext}"):
if not arquivo.is_file():
continue
# Extrai o primeiro número do nome do arquivo como identificador
numeros = re.findall(r'\d+', arquivo.name)
if not numeros:
continue
arquivo_id = int(numeros[0])
if ids_excluir and arquivo_id in ids_excluir:
continue
if ids_incluir and arquivo_id not in ids_incluir:
continue
arquivos_validos.append(arquivo)
return arquivos_validos
def converter_tempo_para_segundos(timestamp: str) -> int:
"""Converte string de tempo (mm:ss ou hh:mm:ss) para segundos totais."""
partes = list(map(int, timestamp.split(':')))
if len(partes) == 2:
return partes[0] * 60 + partes[1]
elif len(partes) == 3:
return partes[0] * 3600 + partes[1] * 60 + partes[2]
return 0
Corte em Lote com Timestamps Fixos
Este script aplica o mesmo corte de tempo para todos os vídeos encontrados no diretório.
import subprocess
from pathlib import Path
from utils import filtrar_arquivos_midia, converter_tempo_para_segundos
DIRETORIO_ORIGEM = "./videos_originais"
DIRETORIO_SAIDA = "./videos_cortados"
EXTENSOES = [".mp4", ".mkv"]
# Configurações de tempo fixo
INICIO_CORTE = "00:02:35"
FIM_CORTE = "00:17:42"
def processar_lote_fixo():
Path(DIRETORIO_SAIDA).mkdir(parents=True, exist_ok=True)
videos = filtrar_arquivos_midia(DIRETORIO_ORIGEM, EXTENSOES)
duracao_corte = converter_tempo_para_segundos(FIM_CORTE) - converter_tempo_para_segundos(INICIO_CORTE)
for video in videos:
caminho_saida = Path(DIRETORIO_SAIDA) / video.name
comando_ffmpeg = [
"ffmpeg", "-i", str(video),
"-ss", INICIO_CORTE,
"-t", str(duracao_corte),
"-c", "copy", "-y",
str(caminho_saida)
]
print(f"Processando: {video.name}...")
subprocess.run(comando_ffmpeg, check=True, capture_output=True)
print(f"Concluído: {caminho_saida.name}")
if __name__ == "__main__":
processar_lote_fixo()
Processamento Dinâmico Baseado em Configuração CSV
Quando os tempos de introdução e encerramento variam entre os episódios, utilizamos um arquivo CSV para mapear os timestamps individualmente.
Estrutura do arquivo config_cortes.csv:
id_episodio,inicio,fim
120,00:02:16,00:18:28
122,00:02:16,00:17:25
123,00:02:16,00:19:11
Script de processamento dinâmico:
import csv
import subprocess
from pathlib import Path
from utils import filtrar_arquivos_midia, converter_tempo_para_segundos
def processar_lote_dinamico():
diretorio_saida = Path("./videos_cortados_dinamico")
diretorio_saida.mkdir(parents=True, exist_ok=True)
# Carregar configurações do CSV
configuracoes = {}
with open("config_cortes.csv", mode='r', encoding='utf-8') as arquivo_csv:
leitor = csv.DictReader(arquivo_csv)
for linha in leitor:
configuracoes[int(linha['id_episodio'])] = {
'inicio': linha['inicio'],
'fim': linha['fim']
}
# Filtrar apenas os vídeos que possuem entrada no CSV
videos = filtrar_arquivos_midia("./videos_originais", [".mp4", ".mkv"], ids_incluir=list(configuracoes.keys()))
for video in videos:
# Extrair o ID do vídeo para buscar a configuração correspondente
import re
video_id = int(re.findall(r'\d+', video.name)[0])
config = configuracoes[video_id]
inicio_seg = converter_tempo_para_segundos(config['inicio'])
fim_seg = converter_tempo_para_segundos(config['fim'])
duracao = fim_seg - inicio_seg
caminho_saida = diretorio_saida / video.name
comando = [
"ffmpeg", "-i", str(video),
"-ss", str(inicio_seg),
"-t", str(duracao),
"-c", "copy", "-y",
str(caminho_saida)
]
subprocess.run(comando, check=True, capture_output=True)
print(f"Corte dinâmico aplicado com sucesso em: {video.name}")
if __name__ == "__main__":
processar_lote_dinamico()
Consolidação de Múltiplos Segmentos
Em cenários onde o conteúdo útil está fragmentado (ex: cenas pós-créditos), é necessário extrair múltiplas partes e concatená-las. O método mais eficiente é utilizar o demuxer concat do FFmpeg.
Primeiro, gere um arquivo de texto (ex: lista_segmentos.txt) contendo os caminhos dos arquivos temporários:
file 'parte_intro.mp4'
file 'parte_principal.mp4'
file 'parte_pos_creditos.mp4'
Em seguida, execute o comando de mesclagem via Python:
def mesclar_segmentos(arquivo_lista: str, arquivo_saida: str):
comando_concat = [
"ffmpeg", "-f", "concat",
"-safe", "0",
"-i", arquivo_lista,
"-c", "copy",
arquivo_saida
]
subprocess.run(comando_concat, check=True)