Авторы страницы

  • Andrey Sharoyko
    Май 27, 2010

Сортируем MP3

При скачивании MP3 из сети я обычно складываю их в одну папку, поскольку качает их в фоновом режиме менеджер закачек. Так скачивать удобно тем, что определив в его настройках скачивать не больше одного-двух файлов одновременно, можно при этом параллельно работать в сети.
Поскольку при этом MP3-файлов накапливается в одной папке довольно много, то я решил, что стоит написать shell-скрипт, который бы их рассортировывал по папкам согласно классической схеме: верхний каталог - по тегу Artist, а вложенные - по тегу Album. И так как у большинства артистов набирается всего по несколько файлов от каждого, то раскладывать файлы по альбомам я решил сделать опцией.
Вот, что получилось в результате.

Сперва я написал функцию has_option(), которая проверяет, есть ли в параметрах вызова скрипта та или иная опция:

function has_option()                           # locate script argument
{
        result=no
        for op in $1 ; do
                if [ "x$op" = "x$2" ] ; then            # short option
                        result=yes
                        return
                fi
                if [ "x$op" = "x$3" ] ; then            # long option
                        result=yes
                        return
                fi
        done
}

Функция принимает три аргумента - строку с ключами ($1), короткий ключ ($2) и длинный ключ ($3). Например, стандартный ключ запроса подсказки может быть представлен как коротким ключом -h, так и длинным --help.  Вот функция has_option() может проверять как наличие короткого ключа, так и длинного. При этом она присваивает переменной $result значение либо yes, либо no.

Следующая функция, opts_albums(), проверяет, задан ли ключ (сортировать по альбомам), и если да, то присваивает переменной $albums значение либо yes, либо no:

function opts_albums()
{
    albums=no
        has_option "$*" -a
        if [ $result = "yes" ] ; then
                albums=yes
        fi
}

Далее я просто вызвал эту функцию:

opts_albums "$*"

Чтобы особо не утруждать себя, какой программой читать теги в MP3-файлах, я решил брать эту информацию из вывода mpg123, и обрабатывать с помощью awk. Чтобы mpg123 не проигрывал файл, я запускаю его с ключом -t (тестирование), а вывод с stderr перенаправляю в файл, чтобы не декодировать MP3-шник два раза:

mpg123 -t $fn 2>${fn}'.txt'

Затем я составил два почти идентичных скрипта для awk с той лишь разницей, что один выполняется для тега Artist, а другой - для тега Album:

artist_script='/Artist:/ {
    str = $0;
    n   = index(str, "Artist:");
    an  = substr(str, n+7);
    sub(/^[ \t]+/, "", an);
    sub(/[ \t]+$/, "", an);
    gsub(/[ \t\n\+]/, "_", an);
    gsub(/[^0-9A-Za-z_\-]/, "q", an);
    print an
  }'

album_script='/Album:/ {
    str = $0;
    n   = index(str, "Album:");
    an  = substr(str, n+6);
    sub(/^[ \t]+/, "", an);
    sub(/[ \t]+$/, "", an);
    gsub(/[ \t\n\+]/, "_", an);
    gsub(/[^0-9A-Za-z_\-]/, "q", an);
    print an
  }'

Делают они следующее. Находят в строке текст, вырезают текст за ним, удаляют пробелы и табуляции в начале и конце этих текстов, заменяют некоторые символы символом подчёркивания, а затем все прочие символы, которые могут вызвать вопросы при использовании этих литералов в качестве имён файлов, заменяются буквой q.

После этого я написал простой цикл, в котором обрабатывается вывод команды ls *.mp3.
Для использования скрипта я поместил его в каталог $HOME/bin. Чтобы рассортировать MP3-шки с его помощью, я перехожу в папку с MP3-шками, открываю эмулятор терминала и вызываю в ней свой скрипт. Скрипт создаёт подкаталоги по именам артистов, а если требуется, то и по названиям альбомов, и копирует в эти каталоги соответствующие файлы.

Таким образом, на каждой итерации цикла получается имя каталога для артиста, проверяется, есть ли такой файл, и если есть, то каталог ли это, и если да, то задан ли ключ -a, и если да, то тоже самое - для названия альбома:

for fn in `ls *.mp3` ; do
      echo "File: $fn"
      dfn=$fn'.txt'
      mpg123 -t $fn 2>$dfn
      dn=`cat $dfn | awk "$artist_script"`
      if [ "x$dn" != "x" ] ; then
            echo "  Artist directory: $dn"
            if [ ! -e $dn ] ; then
                  mkdir $dn ;
            fi
            if [ -d $dn ] ; then
                  nfn=${dn}/$fn
                  if [ $albums = "yes" ] ; then
                       adn=`cat $dfn | awk "$album_script"`
                        if [ "x$adn" != "x" ] ; then
                              echo "  Album directory: $adn"
                              if [ ! -e ${dn}/$adn ] ; then
                                    mkdir ${dn}/$adn ;
                               fi
                               if [ -d ${dn}/$adn ] ; then
                                    nfn=${dn}/${adn}/$fn ;
                               fi
                        fi
                  fi
                  mv $fn $nfn ;
            fi
      fi
      rm $dfn
done

Выглядит жутковато, но это потому, что лень было дополнительные переменные объявлять в целях повышения читаемости кода.  Скачать скрипт.

Vanyamba music at Last.FM


Автор: Андрей Шаройко <vanyamboe@gmail.com>

Comments