RipCD Evo
開発::util
序
RipCD EvoはCDリッピングのためのシェルスクリプトである。 リッピング自体はripitとcdrdaoを使用する。
これは私のケースにおいて最適化し、コマンド一発で通るようにしたものだ。 コード自体は大したものではないが、作業フロー的にはかなり有効なものとして機能している。
私のケースにおける前提は次のとおりである。
- CDはリッピングし、特定ディレクトリ下に
$artist/$album
ディレクトリを作り、以下にFLACで保存する - CD自体はイメージ化してアーカイブする
- アートワークはアルバムディレクトリ下に
cover.jpg
として保存する
なお、もともとはripcdというシェル関数として定義して使っていたのだが、一般化するとともにユーティリティを加え、さらにfreedbの終了に合わせてgnudbを使うようにするなどの変更を加えており、“RipCD
Evo”の名称で開発していた。
だが、公開するにあたっては過去のRipCDが公開されていたという事実がないため、ripcd
という名前で公開している。
また、このスクリプトは過去の記事で存在自体は触れたことがあるものだ。
コード
ripcdのコードは、意外と複雑である。
現行aa082ba
のコードは次の通りだ。
#!/bin/zsh
setopt EXTENDED_GLOB
setopt BARE_GLOB_QUAL
if [[ ${XDG_CONFIG_DIR:-$HOME/.config}/reasonset/ripcd.zsh ]]
then
source ${XDG_CONFIG_DIR:-$HOME/.config}/reasonset/ripcd.zsh
fi
if [[ -z $RIPCD_OUTDIR ]]
then
typeset -g RIPCD_OUTDIR=$(xdg-user-dir MUSIC)
fi
print $RIPCD_OUTDIR
cd $RIPCD_OUTDIR
# Get albums before rip.
print -l */*(/) > /tmp/ripcd.$$.current
# rip
ripit -C gnudb.org -o "$RIPCD_OUTDIR" -D '"$artist/$album"' -c 2 --quality 8
# Get albums after rip.
print -l */*(/) > /tmp/ripcd.$$.next
album_list=(${(f)"$(sort /tmp/ripcd.$$.current /tmp/ripcd.$$.next | uniq -u)"})
if (( ${#album_list} == 1 ))
then
album="${album_list[1]}"
else
select album in $album_list "Manual Input"
do
if [[ -n "$album" && -e "$album" ]]
then
perl -pi -e 'if (/^\//) { s@^.*/@@ }' $album/*.m3u
break
elif [[ "$album" == "Manual Input" ]]
then
read "album?artist/album-> "
if [[ -n "$album" && -e "$album" ]]
then
perl -pi -e 'if (/^\//) { s@^.*/@@ }' $album/*.m3u
break
else
print "NO ALBUM DIRECTORY FOUND." >&2
fi
else
print "NO ALBUM DIRECTORY FOUND." >&2
fi
done
fi
if [[ -z $RIPCD_IMGDIR ]]
then
typeset -g RIPCD_IMGDIR=$(xdg-user-dir MUSIC)/rip
fi
[[ -e "$RIPCD_IMGDIR/${album:h}" ]] || mkdir -p "$RIPCD_IMGDIR/${album:h}"
cdrdao read-cd --read-raw --datafile "$RIPCD_IMGDIR/${album}.bin" --driver generic-mmc-raw "$RIPCD_IMGDIR/${album}.toc" && eject
rm /tmp/ripcd.$$.*
手順を大まかに分けると
- リッピング
- ripitによって生成されたm3uの修正
- cdrdaoによるイメージ化
というステップになる。
問題はripitによってリッピングされたアルバムのタイトルをどうやってcdrdaoに渡すか、だ。 こうした情報は基本的に外部データベースから得られるが、その取得はripitによって行われる。 手入力ではなく、これを使いたい。
ここでは、コレクションディレクトリの差分を取る、という方法を取っている。 複数候補がある場合はインタラクティブに選択させる。
ベストとは言い難いが、それなりに良い挙動になったと思う。
ウォークマンへの変換
#!/bin/zsh
setopt EXTENDED_GLOB
export AAC_BITRATE=320k
typeset -gx SOURCE_DIR=$1
typeset -gx DEST_DIR=$2
fixm3u() {
(
print "Fixing and copy m3u"
cd $SOURCE_DIR
for i in */*/*.m3u
do
[[ -e $DEST_DIR/$i ]] && continue
print $DEST_DIR/$i
perl -p -e 'if (/^[^#]/ && ! /^$/) { tr/!?"\\<>*|:/_________/; s/(\.[a-zA-Z0-9]+)? *$/.m4a/ }' $i >| $DEST_DIR/$i
done
)
}
fdkaac() {
print "Convert AAC with libfdk_aac..."
(
cd $SOURCE_DIR
ruby -rfileutils <<EOF
sources = []
Dir.glob("*/*").each {|artalbm|
oartalbm = artalbm.split('/').map {|x| x.tr('!?\"\\<>*|:', '_') }.join("/")
if File.exist?(%Q%#{ENV['DEST_DIR']}/#{oartalbm}%)
if File.exist?(%Q%#{artalbm}/cover.jpg%) && ! File.exist?("#{ENV['DEST_DIR']}/#{oartalbm}/cover.jpg")
FileUtils.cp(%Q%#{artalbm}/cover.jpg%, %Q%#{ENV['DEST_DIR']}/#{oartalbm}/%)
end
STDERR.puts "#{artalbm} is already exist. Skipping..."
next
else
FileUtils.mkdir_p(%Q%#{ENV['DEST_DIR']}/#{oartalbm}%)
if File.exist? %Q%#{artalbm}/cover.jpg%
FileUtils.cp(%Q%#{artalbm}/cover.jpg%, %Q%#{ENV['DEST_DIR']}/#{oartalbm}/%)
end
end
songfiles = Dir.entries(artalbm).select {|i| File.extname(i) == ".flac" }
songfiles.sort.each do |songfile|
params = { source: "#{artalbm}/#{songfile}" }
params[:dest] = "#{ENV['DEST_DIR']}/#{oartalbm}/#{songfile.tr('!?\"\\<>*|:', '_').sub(/.[a-zA-Z0-9]+$/, '.m4a') }"
sources.push params
end
}
sources.each { |elm| system('ffmpeg', '-nostdin', '-i', elm[:source], '-c:a', 'libfdk_aac', '-b:a', ENV['AAC_BITRATE'], '-cutoff', '18000', elm[:dest] ) }
EOF
)
}
cover4walkman() {
(
cd $DEST_DIR
for i in *
do
print For "$i" ...
(
cd $i
for j in *
do
if [[ -e $j/cover.jpg && ! -e $j/$j.jpg ]]
then
cp -v $j/cover.jpg $j/$j.jpg
fi
done
)
done
)
}
if [[ -z $SOURCE_DIR ]]
then
print SOURCE_DIR is not set.
exit 1
fi
if [[ -z $DEST_DIR ]]
then
print DEST_DIR is not set.
exit 1
fi
print source: $SOURCE_DIR
print dest: $DEST_DIR
fdkaac
fixm3u
cover4walkman
ZshスクリプトだけどAAC変換部分はRubyというもの。
Walkman用変換としての注意点は以下の3つ
- サポート形式はMP3, AAC
- FAT系ファイルシステム
- カバーアートは
$albumtitle.jpg
形式の名前である必要がある
まず、音楽形式の変換はFFmpeg+libfdk_aacを用いている。 Manjaroの標準FFmpegはlibfdk_aacが有効になっていないのでAUR版を使う必要がある。
変換部分が複雑だが、大きいのはFAT系ファイルシステムに対応したパス変換が必要であること、それに合わせて(また拡張子が変わることにも合わせて)プレイリストを変換する必要があること、カバーアートについては変換後の名前になること、に合わせたものだ。
もっとも、これは必要以上に複雑で、もともとは別の考え方をしていたために素直にtrできなかったからこうなったのだが、この状態だと別にtrで良い。 だから、Rubyパートを外してもっとスッキリしたものになるだろう。
動作確認の都合もあるが、そのうちやりたい。