Primeiro, reúna a lista em um array Bash. Se os arquivos estiverem no diretório atual, você pode usar
files=(prefix_????.mp3)
Alternativamente, você pode usar localizar e classificar,
IFS=$'\n' ;
files=($(find . -name 'prefix_*.mp3' printf '%p\n' | sort -d))
A configuração IFS
diz ao Bash para dividir apenas as novas linhas. Se seus nomes de arquivos e diretórios não contiverem espaços, você poderá omiti-los.
Como alternativa, você pode ler os nomes dos arquivos de um arquivo, digamos filelist
, um nome por linha e nenhuma linha vazia,
IFS=$'\n'
files=($(<filelist))
Se você tiver linhas vazias, use
IFS=$'\n'
files=($(sed -e '/$/ d' filelist))
Em seguida, decida quantos arquivos você deseja em cada fatia, o nome do arquivo acumulador temporário, bem como o nome do arquivo combinado final:
s=100
src="combined-in.mp3"
out="combined-out.mp3"
Então, só precisamos dividir a lista e processar cada sublistagem:
while (( ${#files[@]} > 0 )); do
n=${#files[@]}
# Slice files array into sub and left.
if (( n <= s )); then
sub=("${files[@]}")
left=()
else
(( n-= s ))
sub=("${files[@]:0:s}")
left=("${files[@]:s:n}")
fi
# If there is no source file, but there is
# a sum file, rename sum to source.
if [ ! -e "$src" -a -e "$out" ]; then
mv -f "$out" "$src"
fi
# If there is a source file, include it first.
if [ -e "$src" ]; then
sub=("$src" "${sub[@]}")
fi
# Run command.
if ! sox "${sub[@]}" "$out" ; then
rm -f "$out"
echo "Failed!"
break
fi
rm -f "$src"
echo "Done up to ${sub[-1]}."
files=("${left[@]}")
# rm -f "${sub[@]}"
done
Se sox
relatar uma falha, o loop será interrompido antecipadamente. Caso contrário, será emitido o sobrenome no lote processado.
Usamos um if
para o comando sox
para detectar a falha e removemos o arquivo de saída se, de fato, ocorreu uma falha. Como também adiamos modificar o files
array até depois de um comando sox
bem-sucedido, podemos editar / corrigir arquivos individuais com segurança e, em seguida, apenas executar novamente o while
loop para continuar onde paramos.
Se você estiver com pouco espaço em disco, poderá remover o comentário da segunda à última linha, rm -f "${sub[@]}"
, para remover todos os arquivos que foram combinados com êxito.
O acima processa as partes iniciais repetidas vezes.
Como expliquei em um comentário abaixo, os resultados serão muito melhores se você concatenar os arquivos primeiro usando ffmpeg
(sem recodificar usando sox
), possivelmente seguido por uma passagem de recodificação usando sox
. (Ou você pode recodificar cada primeiro, é claro).
Primeiro, você cria uma lista separada por pipe (string) dos nomes dos arquivos,
files="$(ls -1 prefix_????.mp3 | tr '\n' '|')"
remova o tubo supérfluo final,
files="${files%|}"
e alimente-os para ffmpeg
sem recodificação:
ffmpeg -i "concat:$files" -codec copy output.mp3
Note que você pode querer correr
ulimit -n hard
para aumentar o número de arquivos abertos para o máximo permitido para o processo atual ( hard limit ); você pode consultá-lo usando ulimit -n
. (Não me lembro se ffmpeg
concat:
abre as fontes sequencialmente ou todas de uma vez.)
Se você fizer isso mais de uma vez, eu colocaria tudo em um script simples:
#!/bin/bash
export LANG=C LC_ALL=C
if [ $# -le 2 -o "$1" = "-h" -o "$1" = "--help" ]; then
exec >&2
printf '\n'
printf 'Usage: %s -h | --help ]\n' "$0"
printf ' %s OUTPUT INPUT1 .. INPUTn\n' "$0"
printf '\n'
printf 'Inputs may be audio mp3 or MPEG media files.\n'
printf '\n'
exit 1
fi
output="$1"
shift 1
ulimit -n hard
inputs="$(printf '%s|' "${@}")"
inputs="${inputs%|}"
ffmpeg -i "concat:$inputs" -codec copy "$output"
retval=$?
if [ $retval -ne 0 ]; then
rm -f "$output"
echo "Failed!"
exit $retval
fi
# To remove all inputs now, uncomment the following line:
# rm -f "${@}"
echo "Success."
exit 0
Observe que, como eu uso -codec copy
em vez de -acodec copy
, o acima deve funcionar para todos os tipos de arquivos MPEG, não apenas arquivos de áudio mp3.