Renomeador Automático de Arquivos de Mídia

Publicado por Felipe Rafailov 15/07/2009

[ Hits: 6.115 ]

Download nconv.sh




Este script foi criado para renomear arquivos de mídia, como Vídeos e Fotos, porém pode ser utilizado com qualquer tipo de arquivo. É um script bem completo que aceita uma grande variedade de opções de linha de comando.

Para visualizar o resultado sem afetar os arquivos e/ou diretórios, utilize a opção --dummy. Importante usar esta opção para saber como ficariam os arquivos após as transações. Mais informações, incluindo um manual completo com exemplos, pode ser encontrado aqui:

http://maisena.net/scripts/nconv/

  



Esconder código-fonte

#!/bin/bash
# Desenvolvido por Felipe Rafailov
# E-Mail e GTalk: felipe.rafailov@gmail.com

# Quantos diretórios o script irá criar
# 0 - Não cria nenhum diretório (ex. Lost S01E01.avi)
# 1 - Cria um diretório para o seriado (ex. Lost/S01E01.avi)
# 2 - Cria diretórios para o seriado e a temporada (ex. Lost/S01/01.avi)
# 3 - Cria um diretório para cada episódio (ex. Lost/S01/S01E01/S01E01.avi)
DIRLEVEL=1
#DIRLEVEL=0

# Separador usado para o nome do arquivo e/ou diretório
EOW=' '
#EOW='.'
#EOW='_'

# Cria os diretórios (se necessário) e move o arquivo
# 0 - Somente exibe o nome na tela (para visualizar)
# 1 - Efetua a transação, movendo o arquivo (padrão)
MOVE=1

# Quando habilitado, efetua a cópia do arquivo original para seu novo
# destino.
COPY=0

# Habilita o modo verboso
# 0 - Desabilitado (padrão)
# 1 - Habilitado
VERBOSE=0

# No modo interativo, em cada alteração o usuário pode aceitar a sugestão,
# rejeitar, ou propor outro nome para o arquivo.
INTERACTIVE=0

# Os arquivos a serem movidos são armazenados nesta variável
FILES=

# Quando habilitado, o nome completo será atribuído ao arquivo.
# Senão, somente a parte referente ao número do episódio é colocado.
# 0 - Nomes curtos, ex.:    S01E01.avi
# 1 - Nomes compridos, ex.: Lost S01E01.avi
FULLNAME=1

# Permite especificar o diretório de saída, onde os arquivos serão
# criados. Por padrão, usa-se o diretório atual.
#OUTPUT=$PWD
OUTPUT=

# Permite adicionar um nome (inicial) a todos os arquivos alterados.
# Permite também adicionar um contador ao nome, utilizando o caracter
# especial '%'
BASENAME=
COUNT=0

# Esta variável contém uma lista, separada por vírgulas, de todos os termos
# a serem ignorados no nome do arquivo. Outra opção, --ignoreall, permite
# ignorar completamente o nome original do arquivo. O novo nome será formado
# pelo valor de --basedir e --basename.
IGNORE=
IGNOREALL=0

# Contém o nome do arquivo usado como fonte dos nomes dos episódios,
# quando o parâmetro --source é utilizado. O arquivo deve possuir uma
# estrutura específica, ex.:
# S01E01 Nome do Episódio 1 da 1a temporada
SOURCE=

# Função responsável por unir duas strings, utilizada para anexara
# duas strings consecutivamente, separadas por um caractere ou string
#
append(){
  if [ -n "$1" ]; then
    if [[ $1 =~ $2$ ]]; then
      echo "$1"
    else
      echo "${1}${3}${2}"
    fi
  else
    echo "$2"
  fi
}

# Coloca a primeira palavra em Caixa Alta, quando necessário
capitalize(){
  if [ ${#1} -ge 3 ] || [[ "$2" =~ ^$1 ]]; then
    part1="$(echo -n "${1:0:1}" | tr "[:lower:]" "[:upper:]")"
    part2="$(echo -n "${1:1}" | tr "[:upper:]" "[:lower:]")"
    echo "${part1}${part2}"
  else
    echo "$(echo "$1" | tr "[:upper:]" "[:lower:]")"
  fi
}

# Esta função substitui o caractere especial '%' por um contador. Esta função é
# utilizada para interpretar o argumento da opção --basename e, no modo
# interativo, também a opção de escolher o nome do arquivo manualmente.
replace_counter() {
  NEWCOUNT=$COUNT
  if [ -n "$1" ] && [[ $1 =~ '(\*?%+)' ]]; then
    t_mask="${BASH_REMATCH[1]}"
    if [ "${t_mask:0:1}" = "*" ]; then
      if [ $INTERACTIVE -eq 1 ]; then
        NEWCOUNT=1
      fi
      t_mask="${t_mask:1}"
    else
      NEWCOUNT=$(($NEWCOUNT+1))
    fi
    t_count=$NEWCOUNT
    while [ ${#t_count} -lt ${#t_mask} ]; do
      t_count="0$t_count"
    done
    NEWNAME="$(echo "$1" | sed "s/*\?$t_mask/$t_count/")"
  else
    NEWNAME="$1"
  fi
  echo "NEWNAME=\"$NEWNAME\""
  echo "NEWCOUNT=$NEWCOUNT"
}

show_help(){
echo "Uso: $(basename $0) [-i] [-d] '.' [-l] 3 arquivo1.avi arquivo2.avi ...
-h --help          Mostra informações resumidas sobre o uso do programa e sai.
-v --verbose       Mostra informações adicionais na saída, informando o usuário
                   sobre o estado atual da execução.
   --dummy         Mostra as operações que serão realizadas, porém não efetua
                   nenhuma mudança no disco rígido. Também implica em -v.
-s --short         Utiliza nomes curtos para os arquivos.
-c --copy          Copia o arquivo original para seu novo destino, em vez de
                   movê-lo. Útil para quando os arquivos originais estiverem
                   em mídias de somente-leitura (CD's ou DVD's).
-i --interactive   Habilita o modo interativo, que permite visualizar as
                   alterações a serem realizadas no disco rígido antes de
                   aceitá-las.
   --ignore a,b,c  Permite especificar uma lista (separada por vírgulas) de
                   termos que não serão consideradas como parte do novo nome do
                   arquivo. Esta opção não distingue maiúsculas/minúsculas.
   --ignoreall     Ignora completamente o nome original do arquivo. O novo nome
                   deverá ser baseado nos valores de --basename e --basedir.
-d --delimiter d   Altera o caractere delimitador. Por padrão, usa-se espaço.
   --basename n    Permite adicionar um nome ao início de cada arquivo a ser
                   alterado. Você pode acicionar um contador com o caractere
                   '%'. Usando-o múltiplas vezes seguidas permite adicionar
                   mais dígitos ao contador. Ex.:
                      --basename '%%' = 01, --basename '%%%' = 001, etc.
   --basedir n     Permite especificar o diretório de base onde os novos
                   diretórios e arquivos serão criados.
   --source file   Esta opção permite especificar um arquivo para servir de
                   fonte para os nomes dos episódios. O arquivo deve possuir
                   o seguinte formato:
                      S01E01 Nome do Episódio 1 da 1a Temporada
-l --level l       Altera o número de sub-diretórios que serão criados. Aceita
                   valores de 0-3. O padrão é 1. Por exemplo:
                      nível 0: Lost S01E01.avi
                      nível 1: Lost/Lost S01E01.avi
                      nível 2: Lost/S01/Lost S01E01.avi
                      nível 3: Lost/S01/S01E01/Lost S01E01.avi"
}

# Faz a leitura das opções de linha de comando do script
while [ -n "$1" ]; do
  case "$1" in
  -h | --help)
    show_help
    exit 0
    ;;
  -v | --verbose)
    VERBOSE=1
    ;;
  -i | --interactive)
    MV_OPTS="-i"
    INTERACTIVE=1
    ;;
  --dummy)
    MOVE=0
    VERBOSE=1
    ;;
  -s | --short)
    FULLNAME=0
    ;;
  -c | --copy)
    COPY=1
    ;;
  -d | --delimiter)
    shift
    if [ "$1" = '/' ]; then
      echo ERRO: Delimitador inválido
      exit 1
    fi
    EOW="$1"
    ;;
  --basedir)
    shift
# Não é necessário verificar o diretório de saída, isso é feito mais adiante
    OUTPUT="$(echo $1 | sed 's/\/$//')"
    ;;
  --basename)
    shift
    BASENAME="$1"
    ;;
  --source)
    shift
    if [ -f "$1" ] && [ -r "$1" ]; then
      SOURCE="$1"
    else
      echo "AVISO: Não pode ler do arquivo de fonte $1"
    fi
    ;;
  --ignore)
    shift
    IGNORE=",$(echo "$1" | sed 's/[[:space:]]\+/,/g'),"
    ;;
  --ignoreall)
    IGNOREALL=1
    ;;
  -l | --level)
    shift
    if [[ $1 =~ ^[[:digit:]]+$ ]] && [ $1 -ge 0 ] && [ $1 -le 3 ]; then
      DIRLEVEL=$1
    else
      echo "ERRO: Nível inválido. Utilize valores de 0 a 3."
      echo "Veja $(basename $0) --help para maiores informações."
      exit 1
    fi
    ;;
  *)
    FILES=$(append "$FILES" "$1" \;)
    ;;
  esac
  shift
done

# Verifica se foram passados arquivos como parâmetro
if [ -z "$FILES" ]; then
  echo ERRO: Nenhum arquivo fornecido para renomear
  exit 1
fi

# Caso alguma das opções, --basedir ou --basename for usada, faz
# o tratamento do nome, como espaços.
if [ -n "$OUTPUT" ]; then
  OUTPUT="$(echo $OUTPUT | sed "s/[[:space:]]\+/${EOW}/g")"
fi
if [ -n "$BASENAME" ]; then
  BASENAME="$(echo $BASENAME | sed "s/[[:space:]]\+/${EOW}/g")"
fi

# A variável IFS é alterada pois no caso da variável FILES, os arquivos
# são separados por ponto-e-vírgula ';'
IFS=';'

for file in $FILES; do

# O nível de expansão pode variar conforme o nome do arquivo. Caso não seja
# encontrado um número de episódio, um nível inferior (1) é usado.
  t_dirlevel=$DIRLEVEL
  t_short=$FULLNAME
  t_basename="$BASENAME"

# Pula arquivos não existentes e diretórios (quando não estiver usando --dummy)
  if [ -d "$file" ] || [ $MOVE -eq 1 ] && [ ! -f "$file" ]; then
    if [ -d "$file" ]; then
      echo "ERRO: \"$file\" é um diretório."
    elif [ ! -f "$file" ]; then
      echo "ERRO: Arquivo não encontrado: \"$file\""
    fi
    continue
  fi

# Verifica o diretório de saída (quando especificado) ou o diretório atual
  if [ -n "$OUTPUT" ]; then
    t_ckdir="$OUTPUT"
  else
    t_ckdir=$PWD
  fi
  while [ ! -d $t_ckdir ]; do
    t_ckdir="$(dirname "$t_ckdir")"
  done
  if [ ! -w "${t_ckdir}" ] && [ $MOVE -eq 1 ]; then
    echo "ERRO: Não posso criar arquivo(s)/diretório(s) de saída em $t_ckdir"
    exit 1
  fi

  t_filename="$(basename "$file")"
  t_extension="$(echo $t_filename | awk -F . '{print $NF}')"

# Verifica se o arquivo possui extensão
  if [ "$t_filename" = "$t_extension" ]; then
    unset t_extension
  else
    t_extension=".$t_extension"
  fi
  if [ $IGNOREALL -eq 0 ]; then
    t_filename=$(basename "$file" "$t_extension" | sed 's/[[:punct:]]\+/ /g')
  else
    unset t_filename
  fi

  unset IFS
  for t_part in $t_filename ; do
# Remove partes indesejadas do nome do arquivo, passadas pelo argumento
# --ignore
    t_check="$(echo "$t_part" | sed "s/[[:digit:]]/%/g")"
    if [ -n "$(echo $IGNORE | grep -i ",$t_check,")" ] ||
       [ -n "$(echo $IGNORE | grep -i ",$t_part,")" ]; then
      continue
# Procura pelo descritivo do número do episódio, geralmente
# SXXEYY, onde XX é a temporada e YY é o número do episódio.
# Ex.: S01E01
    elif [[ $t_part =~ ^[sS]?[[:digit:]]+[eE]?[[:digit:]]*$ ]]; then
      filename="$(append "$filename" "$(echo $t_part | tr se SE)" "$EOW")"
      break
# Caso seja um nome composto alfanumérico, do tipo arquivo01
    elif [[ $t_part =~ ^([[:alpha:]]+)([[:digit:]]+)$ ]]; then
      t_alpha_part="${BASH_REMATCH[1]}"
      t_numeric_part="${BASH_REMATCH[2]}"
      t_check="$(echo "$t_numeric_part" | sed "s/[[:digit:]]/%/g")"
      if [ -n "$(echo $IGNORE | grep -i ",$t_alpha_part,")" ] ||
         [ -n "$(echo $IGNORE | grep -i ",$t_check,")" ] ||
         [ -n "$(echo $IGNORE | grep -i ",$t_numeric_part,")" ]; then
        continue
      else
        t_part="$(capitalize "$t_alpha_part" "$t_filename")"
        dirname="$(append "$dirname" "$t_part" "$EOW")"
        filename="$(append "$filename" "$t_numeric_part" "$EOW")"
      fi
    else
      t_part="$(capitalize "$t_part" "$t_filename")"
      dirname="$(append "$dirname" "$t_part" "$EOW")"
    fi
  done

# Quando o nome do diretório for vazio, altera o t_dirlevel para
# compensar a falta do nome
  if [ -z "$dirname" ] && [ -n "$filename" ]; then
    if [ $t_dirlevel -eq 1 ]; then
      t_dirlevel=2
    elif [ $t_dirlevel -eq 2 ]; then
      t_dirlevel=3
    fi
  fi

# Caso o nome do arquivo (filename) esteja vazio, este recebe o nome do
# diretório
  if [ -z "$filename" ] && [ -n "$dirname" ]; then
    filename="$dirname"
    if [ $t_dirlevel -gt 1 ]; then
      t_dirlevel=1
    fi
  fi

# Altera o nome entre o nome curto e longo, de acordo com a
# opção --short. Também verifica se o nome do diretório e do arquivo
# são iguais, para evitar nomes repetidos.
  if [ $t_short -eq 1 ] && [ ! "$dirname" = "$filename" ]; then
    t_fullname="$(append "$dirname" "$filename" "$EOW")"
  else
    t_fullname=$filename
  fi

# Caso exista um prefixo (--basename), verificar a existencia do
# caracter de contador e substituí-lo pelo número do contador
  eval "$(replace_counter "$t_basename")"
  t_basename="$NEWNAME"
  COUNT=$NEWCOUNT

# Adiciona um prefixo a todos os arquivos alterados (usando a opção --basename)
  t_fullname="$(append "$t_basename" "$t_fullname" "$EOW")"

# Quando estiver usando a opção --source, obter o nome do episódio através do
# arquivo de fonte especificado pelo parâmetro, tratar a entrada e anexar este
# no nome do arquivo.
  if [ -n "$SOURCE" ] && [[ $filename =~ ^S?[[:digit:]]+E?[[:digit:]]*$ ]]; then
    t_episode="$(cat "$SOURCE" | grep "$filename" | cut -d' ' -f2- |
      sed "s/[[:space:]]\+/${EOW}/g")"
    t_fullname="$(append "$t_fullname" "$t_episode" "$EOW")"
  fi

# Verifica se o nome do arquivo é vazio. Caso seja, significa que o script falhou
# em determinar o nome do arquivo, logo ele será ignorado
  if [ -z "$t_fullname" ]; then
    echo "ERRO: Falha ao determinar nome do arquivo. Pulando arquivo $file"
    continue
  else
# Adiciona a extensão do arquivo no nome completo
    t_extension="$(echo $t_extension | tr "[[:upper:]]" "[[:lower:]]")"
    t_fullname="$(append "$t_fullname" "$t_extension")"
  fi

# Caso o DIRLEVEL seja maior ou igual a 2, o seguinte código descobre o nome do
# diretório intermediário, que é formado pelos primeiros dígitos numéricos do
# código do episódio, ou, no caso de somente haver números, usa-se a dezena ou
# centena.
# Ex.:
# S01E01 => S01
# 115 => 100
  if [ $t_dirlevel -ge 2 ] && [[ $filename =~ ^(S?[[:digit:]]+) ]]; then
    t_midname=${BASH_REMATCH[1]}
    if [ ! "${t_midname:0:1}" = "S" ]; then
      t_size=${#t_midname}
      if [ $t_size -le 2 ]; then
        if [ $t_size -eq 1 ]; then
          t_midname="$(append "0" "$t_midname")"
        fi
        t_size=1
      else
        t_size=$(expr ${#t_midname} - 2)
      fi
      t_xpon=$((10**$t_size))
      t_midname=$(expr $t_midname - $(expr $t_midname % $t_xpon))
    fi
  fi

# Estabelece o nível de diretórios de acordo com a variável
# DIRLEVEL (t_dirlevel)
  case $t_dirlevel in
  0)
    newname="$t_fullname"
    ;;
  1)
    newname="$(append "$dirname" "$t_fullname" \/)"
    ;;
  2)
    filename="$(append "$t_midname" "$t_fullname" \/)"
    newname="$(append "$dirname" "$filename" \/)"
    ;;
  3)
    filename="$(append "$t_midname" "$filename" \/)"
    filename="$(append "$filename" "$t_fullname" \/)"
    newname="$(append "$dirname" "$filename" \/)"
    ;;
  esac

# Permite adicionar um diretório de saída usando a opção --basedir
  newname="$(append "$OUTPUT" "$newname" \/)"

# Se no modo interativo, permite ao usuário aceitar o nome, rejeitar,
# ou propor um nome próprio. O usuário também pode cancelar o modo
# iterativo e sair do programa.
  t_exit=0
  t_skip=0
  while [ $INTERACTIVE -eq 1 ] && [ $t_exit -eq 0 ]; do
    echo
    if [ $COPY -eq 1 ]; then
      t_action=Copiar
    else
      t_action=Mover
    fi
    echo $t_action \"$file\" \=\> \"$newname\"
    echo -n "Escolha uma opção, ou ? para ajuda: "
    read t_option
    while [[ ! $t_option =~ ^[12345?arpcsARPCS]$ ]]; do
      echo "Opção Inválida: $t_option"
      read t_option
    done

    case $t_option in
    a | A | 1)
      t_exit=1
      ;;
    r | R | 2)
      unset dirname
      unset filename
      IFS=';'
      t_exit=1
      t_skip=1
      ;;
    p | P | 3)
      echo -n "Digite o nome do arquivo: "
      read t_newname
      while [ -z "$t_newname" ]; do
        echo -n "Digite o nome do arquivo: "
        read t_newname
      done
      eval "$(replace_counter "$t_newname")"
      newname="$(echo $NEWNAME | sed "s/[[:space:]]\+/${EOW}/g")"
      COUNT=$NEWCOUNT
      t_exit=1
      ;;
    c | C | 4)
      INTERACTIVE=0
      unset MV_OPTS
      t_exit=1
      ;;
    s | S | 5)
      exit 0
      ;;
    ?)
      echo Opção 1. a Aceitar nome escolhido, alterar nome do arquivo
      echo Opção 2. r Rejeitar nome escolhido, manter nome original
      echo Opção 3. p Propor outro nome para o arquivo
      echo Opção 4. c Cancelar o modo interativo, continuar no automático
      echo Opção 5. s Sair do programa
      ;;
    esac
  done
  
  if [ $t_skip -eq 1 ]; then
    continue
  fi

  t_return=0

# Move/renomeia o arquivo para seu novo destino e cria o diretório caso
# seja necessário
  if [ $MOVE -eq 1 ]; then
    t_newdir=$(dirname "$newname")
    if [ ! -d "$t_newdir" ] && [ ! -f "$t_newdir" ]; then
      mkdir -p "$t_newdir"
    fi
    if [ $COPY -eq 1 ]; then
      t_cmd=cp
    else
      t_cmd=mv
    fi
    $t_cmd $MV_OPTS "$file" "$newname"
    t_return=$?
  fi

# Habilita a saída em modo verboso
  if [ $VERBOSE -eq 1 ]; then
    [[ $t_return -ne 0 ]] && echo -n "ERRO: " || echo -n "OK: "
    echo $newname
  fi

# Limpa as variáveis utilizadas
  unset dirname
  unset filename
  IFS=';'
done

Scripts recomendados

Visualizar os processos que mais consomem memória

Mapeamento USB simples

Adição automática dos repositórios do Google Chrome, Google Talk Plugin, Google Earth, Dro no Ubuntu

Compilação do QMPlay2 no Deepin 20

Comando Executar Gerenciadores leves


  

Comentários
[1] Comentário enviado por Lisandro em 15/07/2009 - 07:39h

Muito bom, bem comentado e claro. Valeu.

[2] Comentário enviado por feraf em 15/07/2009 - 16:22h

Atualizei o Script, que em sua versão 1.1 traz vários melhoramentos na função --ignore. Esta e as próximas versões podem ser baixadas do site http://maisena.net/scripts/nconv/, a documentação foi atualizada para incorporar as novas alterações. Agora é possível usar o coringa * e ** na opção --ignore.


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts