tag-mp3 - Um script para editar e/ou visualizar algumas tags ID3 de arquivos MP3
Publicado por João Gabriel (última atualização em 23/12/2021)
[ Hits: 2.444 ]
tag-mp3 é um script para editar e/ou visualizar algumas tags ID3 de arquivos MP3.
Esse script suporta adição e remoção de imagens para a capa e o verso do álbum.
Eu fiz esse script para uso pessoal, por isso não estão presentes tags ID3 que não uso.
Os requerimentos são bash, ffmpeg e GNU getopt (o script funciona sem o GNU getopt se as linhas 158 - 160 forem removidas, porém com funcionalidade diferente em relação a forma de tratar as opções).
#!/usr/bin/env bash
## Script para alterar e / ou listar metadados de arquivos MP3
## v1 - por goll72
edit() {
[ "$opt_list" ] || printf '\nArquivo: %s\n\n' "$1"
printf "\nSe você quiser manter um item no seu valor original, pressione Enter. Se você quiser remover um item, digite '/rm'.\n"
printf "PS: Digitar '/rm' em um dos campos 'Imagem do álbum' removerá TODAS as streams de vídeo do arquivo.\n\n"
read -erp 'Título: ' ans_title
read -erp 'Subtítulo: ' ans_subtitle
read -erp 'Álbum: ' ans_album
read -erp 'Artista: ' ans_artist
read -erp 'Artista do Álbum: ' ans_alb_artist
read -erp 'Comentário: ' ans_comment
read -erp 'Ano: ' ans_year
until [ -z "$ans_year" ] || [[ "$ans_year" == '/rm' ]] || date +%Y -d "$ans_year-1-1" &>/dev/null; do
printf 'Ano inválido.\n'
read -erp 'Ano: ' ans_year
done
read -erp 'Gênero: ' ans_genre
read -erp 'Nº Faixa: ' ans_track
# É preciso verificar se o arquivo de imagem é válido
read -erp "Imagem da capa do álbum (ex. $HOME/album.jpg) : " ans_alb_img_front
until [ -z "$ans_alb_img_front" ] || [[ "$ans_alb_img_front" == '/rm' ]] || file -b --mime-type -- "$ans_alb_img_front" | grep 'image' &>/dev/null ; do
printf '%s: arquivo inválido!\n' "$ans_alb_img_front"
read -erp "Imagem da capa do álbum (ex. $HOME/album.jpg) : " ans_alb_img_front
done
read -erp "Imagem do verso do álbum (ex. $HOME/album-verso.jpg) : " ans_alb_img_back
until [ -z "$ans_alb_img_back" ] || [[ "$ans_alb_img_back" == '/rm' ]] || file -b --mime-type -- "$ans_alb_img_back" | grep 'image' &>/dev/null ; do
printf '%s: arquivo inválido!\n' "$ans_alb_img_back"
read -erp "Imagem do verso do álbum (ex. $HOME/album-verso.jpg) : " ans_alb_img_back
done
printf '\n'
# shellcheck disable=SC2191 disable=SC2206
build_ffmpeg_opts() {
if [[ "$1" == '/rm' ]]; then
ffmpeg_opts+=(-metadata $2="")
elif [ "$1" ]; then
ffmpeg_opts+=(-metadata $2="$1")
fi
}
# Monta a lista de argumentos do ffmpeg baseado nas respostas do usuário
build_ffmpeg_opts "$ans_title" 'title'
build_ffmpeg_opts "$ans_subtitle" 'TIT3'
build_ffmpeg_opts "$ans_album" 'album'
build_ffmpeg_opts "$ans_artist" 'artist'
build_ffmpeg_opts "$ans_alb_artist" 'album_artist'
build_ffmpeg_opts "$ans_comment" 'comment'
build_ffmpeg_opts "$ans_year" 'date'
build_ffmpeg_opts "$ans_genre" 'genre'
build_ffmpeg_opts "$ans_track" 'track'
# Cria um arquivo temporário usado pelo ffmpeg
tempfile=$(mktemp -u --suffix '.mp3')
[ "${ffmpeg_opts[*]}" ] && {
printf 'Executando ffmpeg ...\n\n'
ffmpeg -hide_banner -i "$1" "${ffmpeg_opts[@]}" -c copy -id3v2_version 3 "$tempfile"
mv "$tempfile" "$1"
}
# TODO: Não remover todas as streams de vídeo do arquivo
if [[ "$ans_alb_img_front" == '/rm' ]] || [[ "$ans_alb_img_back" == '/rm' ]]; then
printf 'Removendo TODAS as streams de vídeo do arquivo ...\n'
ffmpeg -hide_banner -i "$1" -vn -c copy -id3v2_version 3 "$tempfile"
mv "$tempfile" "$1"
fi
# Adiciona a capa do álbum
[ "$ans_alb_img_front" ] && [[ "$ans_alb_img_front" != '/rm' ]] && {
printf 'Adicionando capa do álbum ...\n'
ffmpeg -hide_banner -i "$1" -i "$ans_alb_img_front" -map 0:0 -map 1:0 -c copy -id3v2_version 3 -metadata:s:v comment="Cover (front)" "$tempfile"
mv "$tempfile" "$1"
}
# Adiciona o verso do álbum
[ "$ans_alb_img_back" ] && [[ "$ans_alb_img_back" != '/rm' ]] && {
printf 'Adicionando verso do álbum ...\n'
ffmpeg -hide_banner -i "$1" -i "$ans_alb_img_back" -map 0:0 -map 1:0 -c copy -id3v2_version 3 -metadata:s:v comment="Cover (back)" "$tempfile"
mv "$tempfile" "$1"
}
[ "$opt_list" ] && { printf 'Após a modificação:\n'; list "$1"; }
}
list() {
printf '\nArquivo: %s\n\n' "$1"
# Caso essas variáveis não fossem locais, haveria um risco dos metadados do arquivo mudarem, porém o valor da variável se manter
local info title subtitle album artist alb_artist comment year genre track alb_front alb_back
info=$(ffprobe -hide_banner -of flat -show_streams -show_format "$1" 2>/dev/null)
title=$(grep -Po '^format\.tags\.title=\K".*?"$' <<< "$info"); title=${title:-'Sem título'}
subtitle=$(grep -Po '^format\.tags\.TIT3=\K".*?"$' <<< "$info"); subtitle=${subtitle:-'Sem subtítulo'}
album=$(grep -Po '^format\.tags\.album=\K".*?"$' <<< "$info"); album=${album:-'Sem álbum'}
artist=$(grep -Po '^format\.tags\.artist=\K".*?"$' <<< "$info"); artist=${artist:-'Sem artista'}
alb_artist=$(grep -Po '^format\.tags\.album_artist=\K".*?"$' <<< "$info"); alb_artist=${alb_artist:-'Sem artista do álbum'}
comment=$(grep -Po '^format\.tags\.comment=\K".*?"$' <<< "$info"); comment=${comment:-'Sem comentário'}
year=$(grep -Po '^format\.tags\.date=\K".*?"$' <<< "$info"); year=${year:-'Sem ano'}
genre=$(grep -Po '^format\.tags\.genre=\K".*?"$' <<< "$info"); genre=${genre:-'Sem gênero'}
track=$(grep -Po '^format\.tags\.track=\K".*?"$' <<< "$info"); track=${track:-'Sem nº faixa'}
# Descobre se há imagem do álbum ou não
# TODO: Utilizar um método mais robusto; esse método falha se houver uma stream de áudio no arquivo com o comentário "Cover (front)"
# ou "Cover (back)", pois assume que se houver uma stream com esse(s) comentário(s), será sempre uma stream de vídeo
grep -Po '^streams\.stream\.\d\.tags\.comment="Cover \(front\)"' <<< "$info" &>/dev/null && alb_front=1
grep -Po '^streams\.stream\.\d\.tags\.comment="Cover \(back\)"' <<< "$info" &>/dev/null && alb_back=1
[ -n "$alb_front" ] && [ -n "$alb_back" ] && alb_img='Sim (frente e verso) '
[ -n "$alb_front" ] && [ -z "$alb_back" ] && alb_img='Sim (frente) '
[ -z "$alb_front" ] && [ -n "$alb_back" ] && alb_img='Sim (verso) '
[ -z "$alb_front" ] && [ -z "$alb_back" ] && alb_img='Não '
# Formata o texto usando o column
{
printf 'Título: ^%s ^Subtítulo: ^%s ^Álbum: ^%s\n' "$title" "$subtitle" "$album"
printf 'Artista: ^%s ^Artista do Álbum: ^%s ^Comentário: ^%s\n' "$artist" "$alb_artist" "$comment"
printf 'Ano: ^%s ^Gênero: ^%s ^Faixa: ^%s\n' "$year" "$genre" "$track"
printf 'Há imagem de álbum: ^%s\n' "$alb_img"
} | column -t -s'^' -R 2,4,6,8
}
quit() {
rm "$tempfile" &>/dev/null
exit 0
}
[ $# -eq 0 ] && printf '%s: requer opção! Veja %s --help.' "$0" "$0"
# Obtêm as opções
opts=$(getopt -o hle -l help,list,edit -- "$@")
eval set -- "$opts"
while true; do
case "$1" in
-h | --help ) printf 'Uso: %s [OPÇÕES] [--] [ARQUIVOS] [...]\n' "$0"
printf ' -h, --help Mostra esse menu de ajuda.\n'
printf ' -l, --list Lista os metadados dos arquivos MP3.\n'
printf ' -e, --edit Edita os metadados dos arquivos MP3.\n'
exit 0
;;
-l | --list ) opt_list=1; shift ;;
-e | --edit ) opt_edit=1; shift ;;
-- ) shift; break ;;
* ) break ;;
esac
done
for i in "$@"; do
file -b --mime-type -- "$i" | grep 'audio/mpeg' &>/dev/null || { printf '%s: %s: não é um arquivo MP3 válido!' "$0" "$i"; exit; }
[ "$opt_list" ] && list "$i"
[ "$opt_edit" ] && edit "$i"
done
quit
Contar o número de arquivos e subdiretórios de um diretório
Manter o servidor conectado a internet pelo rp-pppoe
Mounter - Script para montar pendrive.
Configuração do horário de verão 2006/2007 (Red Hat, Fedora)
Como extrair chaves TOTP 2FA a partir de QRCODE (Google Authenticator)
Linux em 2025: Segurança prática para o usuário
Desktop Linux em alta: novos apps, distros e privacidade marcam o sábado
IA chega ao desktop e impulsiona produtividade no mundo Linux
Novos apps de produtividade, avanços em IA e distros em ebulição agitam o universo Linux
Como instalar o repositório do DBeaver no Ubuntu
Como instalar o Plex Media Server no Ubuntu
Digitando underscore com "shift" + "barra de espaços"
Como ativar a lixeira e recuperar aquivos deletados em um servidor Linux
Como mudar o nome de dispositivos Bluetooth via linha de comando
How can I remove duplicate MSG files from my folders effectively? (0)
O programa assinador digital (0)
dpkg: erro: gatilho de arquivo duplicado chamado pelo arquivo de nome (6)









