From 08aa61a60172326f4ea70834cd28817c0c397421 Mon Sep 17 00:00:00 2001 From: Aaron LI Date: Fri, 3 Nov 2017 18:35:17 +0800 Subject: Move audio scripts into cli/; also delete a dumplicate --- audio/ape2id3.py | 145 ----------- audio/m4a2mp3.sh | 8 - audio/split2flac | 752 ------------------------------------------------------- audio/wma2mp3.sh | 20 -- cli/ape2id3.py | 145 +++++++++++ cli/split2flac | 752 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ cli/wma2mp3.sh | 20 ++ 7 files changed, 917 insertions(+), 925 deletions(-) delete mode 100755 audio/ape2id3.py delete mode 100755 audio/m4a2mp3.sh delete mode 100755 audio/split2flac delete mode 100755 audio/wma2mp3.sh create mode 100755 cli/ape2id3.py create mode 100755 cli/split2flac create mode 100755 cli/wma2mp3.sh diff --git a/audio/ape2id3.py b/audio/ape2id3.py deleted file mode 100755 index 8651a2f..0000000 --- a/audio/ape2id3.py +++ /dev/null @@ -1,145 +0,0 @@ -#! /usr/bin/env python -import sys -from optparse import OptionParser -import mutagen -from mutagen.apev2 import APEv2 -from mutagen.id3 import ID3, TXXX - -def convert_gain(gain): - if gain[-3:] == " dB": - gain = gain[:-3] - try: - gain = float(gain) - except ValueError: - raise ValueError, "invalid gain value" - return "%.2f dB" % gain -def convert_peak(peak): - try: - peak = float(peak) - except ValueError: - raise ValueError, "invalid peak value" - return "%.6f" % peak - -REPLAYGAIN_TAGS = ( - ("mp3gain_album_minmax", None), - ("mp3gain_minmax", None), - ("replaygain_album_gain", convert_gain), - ("replaygain_album_peak", convert_peak), - ("replaygain_track_gain", convert_gain), - ("replaygain_track_peak", convert_peak), -) - - -class Logger(object): - def __init__(self, log_level, prog_name): - self.log_level = log_level - self.prog_name = prog_name - self.filename = None - def prefix(self, msg): - if self.filename is None: - return msg - return "%s: %s" % (self.filename, msg) - def debug(self, msg): - if self.log_level >= 4: - print self.prefix(msg) - def info(self, msg): - if self.log_level >= 3: - print self.prefix(msg) - def warning(self, msg): - if self.log_level >= 2: - print self.prefix("WARNING: %s" % msg) - def error(self, msg): - if self.log_level >= 1: - sys.stderr.write("%s: %s\n" % (self.prog_name, msg)) - def critical(self, msg, retval=1): - self.error(msg) - sys.exit(retval) - -class Ape2Id3(object): - def __init__(self, logger, force=False): - self.log = logger - self.force = force - def convert_tag(self, name, value): - pass - def copy_replaygain_tag(self, apev2, id3, name, converter=None): - self.log.debug("processing '%s' tag" % name) - if name not in apev2: - self.log.info("no APEv2 '%s' tag found, skipping tag" % name) - return False - if not self.force and ("TXXX:%s" % name) in id3: - self.log.info("ID3 '%s' tag already exists, skpping tag" % name) - return False - value = str(apev2[name]) - if callable(converter): - self.log.debug("converting APEv2 '%s' tag from '%s'" % - (name, value)) - try: - value = converter(value) - except ValueError: - self.log.warning("invalid value for APEv2 '%s' tag" % name) - return False - self.log.debug("converted APEv2 '%s' tag to '%s'" % (name, value)) - id3.add(TXXX(encoding=1, desc=name, text=value)) - self.log.info("added ID3 '%s' tag with value '%s'" % (name, value)) - return True - def copy_replaygain_tags(self, filename): - self.log.filename = filename - self.log.debug("begin processing file") - try: - apev2 = APEv2(filename) - except mutagen.apev2.error: - self.log.info("no APEv2 tag found, skipping file") - return - except IOError: - e = sys.exc_info() - self.log.error("%s" % e[1]) - return - try: - id3 = ID3(filename) - except mutagen.id3.error: - self.log.info("no ID3 tag found, creating one") - id3 = ID3() - modified = False - for name, converter in REPLAYGAIN_TAGS: - copied = self.copy_replaygain_tag(apev2, id3, name, converter) - if copied: - modified = True - if modified: - self.log.debug("saving modified ID3 tag") - id3.save(filename) - self.log.debug("done processing file") - self.log.filename = None - -def main(prog_name, options, args): - logger = Logger(options.log_level, prog_name) - ape2id3 = Ape2Id3(logger, force=options.force) - for filename in args: - ape2id3.copy_replaygain_tags(filename) - -if __name__ == "__main__": - parser = OptionParser(version="0.1", usage="%prog [OPTION]... FILE...", - description="Copy APEv2 ReplayGain tags on " - "FILE(s) to ID3v2.") - parser.add_option("-q", "--quiet", dest="log_level", - action="store_const", const=0, default=1, - help="do not output error messages") - parser.add_option("-v", "--verbose", dest="log_level", - action="store_const", const=3, - help="output warnings and informational messages") - parser.add_option("-d", "--debug", dest="log_level", - action="store_const", const=4, - help="output debug messages") - parser.add_option("-f", "--force", dest="force", - action="store_true", default=False, - help="force overwriting of existing ID3v2 " - "ReplayGain tags") - prog_name = parser.get_prog_name() - options, args = parser.parse_args() - if len(args) < 1: - parser.error("no files specified") - try: - main(prog_name, options, args) - except KeyboardInterrupt: - pass - -# vim: set expandtab shiftwidth=4 softtabstop=4 textwidth=79: diff --git a/audio/m4a2mp3.sh b/audio/m4a2mp3.sh deleted file mode 100755 index 5d06cd9..0000000 --- a/audio/m4a2mp3.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -bitrate=192 - -for i in *.m4a; do - faad -o - "$i" | lame -h -b $bitrate - "${i%m4a}mp3" -done - diff --git a/audio/split2flac b/audio/split2flac deleted file mode 100755 index 6622262..0000000 --- a/audio/split2flac +++ /dev/null @@ -1,752 +0,0 @@ -#!/bin/sh -# Copyright (c) 2009-2015 Serge "ftrvxmtrx" Ziryukin -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -# Dependencies: -# shntool, cuetools -# SPLIT: flac, wavpack, mac -# CONVERT: flac/flake, faac, libmp4v2, id3lib/mutagen, lame, vorbis-tools -# ART: ImageMagick -# CHARSET: iconv, enca -# GAIN: flac, aacgain, mp3gain, vorbisgain - -# Exit codes: -# 0 - success -# 1 - error in arguments -# 2 - file or path is not accessible -# 3 - something has failed - -[ -n "${XDG_CONFIG_HOME}" ] && CONFIG="${XDG_CONFIG_HOME}/split2flac/split2flac.conf" -[ -r "${CONFIG}" ] || CONFIG="${HOME}/.split2flac" -TMPPIC="${HOME}/.split2flac_cover.jpg" -FAILED="split_failed.txt" - -NOSUBDIRS=0 -NOPIC=0 -REMOVE=0 -NOCOLORS=0 -PIC_SIZE="192x192" -REPLAY_GAIN=0 -FORMAT="${0##*split2}" -DIR="." -OUTPATTERN="@artist/{@year - }@album/@track - @title.@ext" -COPYMASKS="[Cc]overs \*.log \*.txt \*.jpg \*.cbr" -COPYFILES=1 -ENCA_ARGS="" - -# codecs default arguments -ENCARGS_flac="-8" -ENCARGS_m4a="-q 500" -ENCARGS_mp3="--preset standard" -ENCARGS_ogg="-q 5" -ENCARGS_wav="" - -# load settings -eval $(cat "${CONFIG}" 2>/dev/null) -DRY=0 -SAVE=0 -NASK=0 -unset PIC INPATH CUE CHARSET -FORCE=0 - -# do not forget to update before commit -VERSION=121 - -HELP="\${cG}split2flac version: ${VERSION} -Splits one big \${cU}APE/FLAC/WV/WAV\$cZ\$cG audio image (or a collection) into \${cU}FLAC/M4A/MP3/OGG_VORBIS/WAV\$cZ\$cG tracks with tagging and renaming. - -Usage: \${cZ}split2\${FORMAT} [\${cU}OPTIONS\$cZ] \${cU}FILE\$cZ [\${cU}OPTIONS\$cZ]\$cZ - \${cZ}split2\${FORMAT} [\${cU}OPTIONS\$cZ] \${cU}DIR\$cZ [\${cU}OPTIONS\$cZ]\$cZ - \$cG-p\$cZ - dry run - \$cG-o \${cU}DIRECTORY\$cZ \$cR*\$cZ - set output directory (current is \$cP\${DIR}\$cZ) - \$cG-of \${cU}'PATTERN'\$cZ \$cR*\$cZ - use specific output naming pattern (current is \$cP'\${OUTPATTERN}'\$cZ) - \$cG-cue \${cU}FILE\$cZ - use file as a cue sheet (does not work with \${cU}DIR\$cZ) - \$cG-cuecharset \${cU}CHARSET\$cZ - convert cue sheet from CHARSET to UTF-8 (no conversion by default) - \$cG-nask\$cZ - do not ask to enter proper charset of a cue sheet (default is to ask) - \$cG-f \${cU}FORMAT\$cZ - use specific output format (current is \$cP\${FORMAT}\$cZ) - \$cG-e \${cU}'ARG1 ARG2'\$cZ \$cR*\$cZ - encoder arguments (current is \$cP'\${ENCARGS}'\$cZ) - \$cG-eh\$cZ - show help for current encoder and exit\$cZ - \$cG-enca \${cU}'ARG1 ARG2'\$cZ \$cR*\$cZ - enca additional arguments (current is \$cP'\${ENCA_ARGS}'\$cZ) - \$cG-c \${cU}FILE\$cZ \$cR*\$cZ - use file as a cover image (does not work with \${cU}DIR\$cZ) - \$cG-nc \${cR}*\$cZ - do not set any cover images - \$cG-C \${cU}MASKS\$cZ \$cR*\$cZ - specify wildcards for files to copy over (current is \$cP'\${COPYMASKS}'\$cZ) - \$cG-nC \${cR}*\$cZ - do not copy any files - \$cG-cs \${cU}WxH\$cZ \$cR*\$cZ - set cover image size (current is \$cP\${PIC_SIZE}\$cZ) - \$cG-d \$cR*\$cZ - create artist/album subdirs (default) - \$cG-nd \$cR*\$cZ - do not create any subdirs - \$cG-D \$cR*\$cZ - delete original file - \$cG-nD \$cR*\$cZ - do not remove the original (default) - \$cG-F\$cZ - force deletion without asking - \$cG-colors\$cZ \$cR*\$cZ - colorized output (default) - \$cG-nocolors\$cZ \$cR*\$cZ - turn off colors - \$cG-g\$cZ \$cR*\$cZ - adjust audio gain - \$cG-ng\$cZ \$cR*\$cZ - do not adjust audio gain (default) - \$cG-s\$cZ - save configuration to \$cP\"\${CONFIG}\"\$cZ - \$cG-h\$cZ - print this message - \$cG-v\$cZ - print version - -\$cR*\$cZ - option affects configuration if \$cP'-s'\$cZ option passed. -\${cP}NOTE: \$cG'-c some_file.jpg -s'\$cP only \${cU}allows\$cZ\$cP cover images, it doesn't set a default one. -\${cZ}Supported \$cU\${cG}FORMATs\${cZ}: flac, m4a, mp3, ogg, wav. -Supported tags for \$cU\${cG}PATTERN\${cZ}: @artist, @album, @year, @track, @performer, @title, @genre, @ext. -@performer tag is useful with 'various artists' albums, when you want to add -each artist's name to the track filename. It works as @artist if track performer is undefined. -Special \"underscored\" tags are also supported (@_artist, @_album, etc). If used, spaces will be replaced with -underscores. It's useful if you want to have filenames without spaces. - -It's better to pass \$cP'-p'\$cZ option to see what will happen when actually splitting tracks. -You may want to pass \$cP'-s'\$cZ option for the first run to save default configuration -(output dir, cover image size, etc.) so you won't need to pass a lot of options -every time, just a filename. Script will try to find CUE sheet if it wasn't specified. -It also supports internal CUE sheets (FLAC, APE and WV).\n" - -msg="printf" - -emsg () { - $msg "${cR}$1${cZ}" -} - -SKIP_UPDATE_ENCARGS=0 - -update_encargs () { - if [ ${SKIP_UPDATE_ENCARGS} -eq 0 ]; then - e="\${ENCARGS_${FORMAT}}" - ENCARGS=`eval echo "$e"` - ENCHELP=0 - fi -} - -update_colors () { - if [ "${NOCOLORS}" -eq 0 ]; then - cR="\033[31m" - cG="\033[32m" - cC="\033[35m" - cP="\033[36m" - cU="\033[4m" - cZ="\033[0m" - else - unset cR cG cC cP cU cZ - fi -} - -update_encargs -update_colors - -# parse arguments -while [ "$1" ]; do - case "$1" in - -o) DIR=$2; shift;; - -of) OUTPATTERN=$2; shift;; - -cue) CUE=$2; shift;; - -cuecharset) CHARSET=$2; shift;; - -nask) NASK=1;; - -f) FORMAT=$2; update_encargs; shift;; - -e) ENCARGS=$2; SKIP_UPDATE_ENCARGS=1; shift;; - -eh) ENCHELP=1;; - -enca) ENCA_ARGS=$2; shift;; - -c) NOPIC=0; PIC=$2; shift;; - -nc) NOPIC=1;; - -C) COPYMASKS=$2; COPYFILES=1; shift;; - -nC) COPYFILES=0;; - -cs) PIC_SIZE=$2; shift;; - -d) NOSUBDIRS=0;; - -nd) NOSUBDIRS=1;; - -p) DRY=1;; - -D) REMOVE=1;; - -nD) REMOVE=0;; - -F) FORCE=1;; - -colors) NOCOLORS=0; update_colors;; - -nocolors) NOCOLORS=1; update_colors;; - -g) REPLAY_GAIN=1;; - -ng) REPLAY_GAIN=0;; - -s) SAVE=1;; - -h|--help|-help) eval "$msg \"${HELP}\""; exit 0;; - -v|--version) - $msg "split2${FORMAT} version: ${VERSION}\n\n"; - shntool -v 2>&1 | grep '^shntool'; - flac --version 2>/dev/null; - wavpack --help 2>&1 | grep 'Version'; - mac 2>&1 | grep '(v '; - faac -h 2>&1 | grep '^FAAC'; - oggenc --version 2>/dev/null; - lame --version | grep '^LAME'; - exit 0;; - -*) eval "$msg \"${HELP}\""; emsg "\nUnknown option $1\n"; exit 1;; - *) - if [ -n "${INPATH}" ]; then - eval "$msg \"${HELP}\"" - emsg "\nUnknown option $1\n" - exit 1 - elif [ ! -r "$1" ]; then - emsg "Unable to read $1\n" - exit 2 - else - INPATH="$1" - fi;; - esac - shift -done - -eval "export ENCARGS_${FORMAT}=\"${ENCARGS}\"" - -# save configuration if needed -if [ ${SAVE} -eq 1 ]; then - echo "DIR=\"${DIR}\"" > "${CONFIG}" - echo "OUTPATTERN=\"${OUTPATTERN}\"" >> "${CONFIG}" - echo "COPYMASKS=\"${COPYMASKS}\"" >> "${CONFIG}" - echo "COPYFILES=${COPYFILES}" >> "${CONFIG}" - echo "NOSUBDIRS=${NOSUBDIRS}" >> "${CONFIG}" - echo "NOPIC=${NOPIC}" >> "${CONFIG}" - echo "REMOVE=${REMOVE}" >> "${CONFIG}" - echo "PIC_SIZE=${PIC_SIZE}" >> "${CONFIG}" - echo "NOCOLORS=${NOCOLORS}" >> "${CONFIG}" - echo "REPLAY_GAIN=${REPLAY_GAIN}" >> "${CONFIG}" - echo "ENCARGS_flac=\"${ENCARGS_flac}\"" >> "${CONFIG}" - echo "ENCARGS_m4a=\"${ENCARGS_m4a}\"" >> "${CONFIG}" - echo "ENCARGS_mp3=\"${ENCARGS_mp3}\"" >> "${CONFIG}" - echo "ENCARGS_ogg=\"${ENCARGS_ogg}\"" >> "${CONFIG}" - echo "ENCARGS_wav=\"${ENCARGS_wav}\"" >> "${CONFIG}" - echo "ENCA_ARGS=\"${ENCA_ARGS}\"" >> "${CONFIG}" - $msg "${cP}Configuration saved$cZ\n" -fi - -# use flake if possible -command -v flake >/dev/null && FLAC_ENCODER="flake" || FLAC_ENCODER="flac" - -METAFLAC="metaflac --no-utf8-convert" -VORBISCOMMENT="vorbiscomment -R -a" -command -v mid3v2 >/dev/null && ID3TAG="mid3v2" || ID3TAG="id3tag -2" -MP4TAGS="mp4tags" -GETTAG="cueprint -n 1 -t" -VALIDATE="sed s/[^][[:space:][:alnum:]&_#,.'\"\(\)!-]//g" - -# check & print output format -msg_format="${cG}Output format :$cZ" -case ${FORMAT} in - flac) $msg "$msg_format FLAC [using ${FLAC_ENCODER} tool]"; enc_help="${FLAC_ENCODER} -h";; - m4a) $msg "$msg_format M4A"; enc_help="faac --help";; - mp3) $msg "$msg_format MP3"; enc_help="lame --help";; - ogg) $msg "$msg_format OGG VORBIS"; enc_help="oggenc --help";; - wav) $msg "$msg_format WAVE"; enc_help="echo Sorry, no arguments available for this encoder";; - *) emsg "Unknown output format \"${FORMAT}\"\n"; exit 1;; -esac - -$msg " (${ENCARGS})\n" - -if [ ${ENCHELP} -eq 1 ]; then - ${enc_help} - exit 0 -fi - -$msg "${cG}Output dir :$cZ ${DIR:?Output directory was not set}\n" - -# replaces a tag name with the value of the tag. $1=pattern $2=tag_name $3=tag_value -update_pattern_aux () { - tag_name="$2" - tag_value="$3" - expr_match="@${tag_name}" - expr_match_opt="[{]\([^}{]*\)${expr_match}\([^}]*\)[}]" - - echo "$1" | { [ "${tag_value}" ] \ - && sed "s/${expr_match_opt}/\1${tag_value}\2/g;s/${expr_match}/${tag_value}/g" \ - || sed "s/${expr_match_opt}//g;s/${expr_match}//g"; } -} - -# replaces a tag name with the value of the tag. $1=pattern $2=tag_name $3=tag_value -update_pattern () { - # replace '/' with '\' and '&' with '\&' for proper sed call - tag_name=$(echo "$2" | sed 's,/,\\\\,g;s,&,\\&,g') - tag_value=$(echo "$3" | sed 's,/,\\\\,g;s,&,\\&,g') - - v=$(update_pattern_aux "$1" "${tag_name}" "${tag_value}") - update_pattern_aux "$v" "_${tag_name}" $(echo "${tag_value}" | sed "s/ /_/g") -} - -# splits a file -split_file () { - TMPCUE="${HOME}/.split2flac_XXXXX.cue" - FILE="$1" - - if [ ! -r "${FILE}" ]; then - emsg "Can not read the file\n" - return 1 - fi - - # search for a cue sheet if not specified - if [ -z "${CUE}" ]; then - CUE="${FILE}.cue" - if [ ! -r "${CUE}" ]; then - CUE="${FILE%.*}.cue" - if [ ! -r "${CUE}" ]; then - # try to extract internal one - CUESHEET=$(${METAFLAC} --show-tag=CUESHEET "${FILE}" 2>/dev/null | sed 's/^cuesheet=//;s/^CUESHEET=//') - - # try WV internal cue sheet - [ -z "${CUESHEET}" ] && CUESHEET=$(wvunpack -q -c "${FILE}" 2>/dev/null) - - # try APE internal cue sheet (omfg!) - if [ -z "${CUESHEET}" ]; then - APETAGEX=$(tail -c 32 "$1" | cut -b 1-8 2>/dev/null) - if [ "${APETAGEX}" = "APETAGEX" ]; then - LENGTH=$(tail -c 32 "$1" | cut -b 13-16 | od -t u4 | awk '{printf $2}') 2>/dev/null - tail -c ${LENGTH} "$1" | grep -a CUESHEET >/dev/null 2>&1 - if [ $? -eq 0 ]; then - CUESHEET=$(tail -c ${LENGTH} "$1" | sed 's/.*CUESHEET.//g' 2>/dev/null) - [ $? -ne 0 ] && CUESHEET="" - fi - fi - fi - - if [ -n "${CUESHEET}" ]; then - $msg "${cP}Found internal cue sheet$cZ\n" - TMPCUE=$(mktemp "${TMPCUE}") - CUE="${TMPCUE}" - echo "${CUESHEET}" > "${CUE}" - TMPCUE="${HOME}/.split2flac_XXXXX.cue" - - if [ $? -ne 0 ]; then - emsg "Unable to save internal cue sheet\n" - return 1 - fi - else - unset CUE - fi - fi - fi - fi - - # print cue sheet filename - if [ -z "${CUE}" ]; then - emsg "No cue sheet\n" - return 1 - fi - - # cue sheet charset - [ -z "${CHARSET}" ] && CHARSET="utf-8" || $msg "${cG}Cue charset : $cP${CHARSET} -> utf-8$cZ\n" - - CUESHEET=$(iconv -f "${CHARSET}" -t utf-8 "${CUE}" 2>/dev/null) - - # try to guess the charset using enca - if [ $? -ne 0 ]; then - CUESHEET=$(enconv ${ENCA_ARGS} -x utf8 < "${CUE}" 2>/dev/null) - fi - - if [ $? -ne 0 ]; then - [ "${CHARSET}" = "utf-8" ] \ - && emsg "Cue sheet is not utf-8\n" \ - || emsg "Unable to convert cue sheet from ${CHARSET} to utf-8\n" - - if [ ${NASK} -eq 0 ]; then - while [ 1 ]; do - echo -n "Please enter the charset (or just press ENTER to ignore) > " - read CHARSET - - [ -z "${CHARSET}" ] && break - $msg "${cG}Converted cue sheet:$cZ\n" - iconv -f "${CHARSET}" -t utf-8 "${CUE}" || continue - - echo -n "Is this right? [Y/n] > " - read YEP - [ -z "${YEP}" -o "${YEP}" = "y" -o "${YEP}" = "Y" ] && break - done - - CUESHEET=$(iconv -f "${CHARSET}" -t utf-8 "${CUE}" 2>/dev/null) - fi - fi - - # save converted cue sheet - TMPCUE=$(mktemp "${TMPCUE}") - CUE="${TMPCUE}" - echo "${CUESHEET}" > "${CUE}" - - if [ $? -ne 0 ]; then - emsg "Unable to save converted cue sheet\n" - return 1 - fi - - SDIR=$(dirname "${FILE}") - - # search for a front cover image - if [ ${NOPIC} -eq 1 ]; then - unset PIC - elif [ -z "${PIC}" ]; then - # try common names - for i in *[Cc]over*.jpg *[Ff]older*.jpg */*[Cc]over*.jpg */*[Ff]older*.jpg; do - if [ -r "${SDIR}/$i" ]; then - PIC="${SDIR}/$i" - break - fi - done - - # try to extract internal one - if [ -z "${PIC}" ]; then - ${METAFLAC} --export-picture-to="${TMPPIC}" "${FILE}" 2>/dev/null - if [ $? -ne 0 ]; then - unset PIC - else - PIC="${TMPPIC}" - fi - fi - fi - - $msg "${cG}Cue sheet :$cZ ${CUE}\n" - $msg "${cG}Cover image :$cZ ${PIC:-not set}\n" - - # file removal warning - if [ ${REMOVE} -eq 1 ]; then - msg_removal="\n${cR}Also remove original" - [ ${FORCE} -eq 1 ] \ - && $msg "$msg_removal (WITHOUT ASKING)$cZ\n" \ - || $msg "$msg_removal if user says 'y'$cZ\n" - fi - - # files to copy over - if [ ${COPYFILES} -eq 1 -a -n "${COPYMASKS}" ]; then - $msg "${cG}Copy over :$cZ ${COPYMASKS}\n" - fi - - # get common tags - TAG_ARTIST=$(${GETTAG} %P "${CUE}" 2>/dev/null) - TAG_ALBUM=$(${GETTAG} %T "${CUE}" 2>/dev/null) - TRACKS_NUM=$(${GETTAG} %N "${CUE}" 2>/dev/null) - - # some cue sheets may have non-audio tracks - # we can check the difference between what cuebreakpoints and cueprint gives us - BREAKPOINTS_NUM=$(($(cuebreakpoints "${CUE}" 2>/dev/null | wc -l) + 1)) - - # too bad, we can't fix that in a _right_ way - if [ ${BREAKPOINTS_NUM} -lt ${TRACKS_NUM} ]; then - emsg "'cueprint' tool reported ${TRACKS_NUM} tracks, " - emsg "but there seem to be only ${BREAKPOINTS_NUM} audio ones\n" - emsg "Sorry, there is no any helpful options in the 'cueprint' tool for this problem.\n" - emsg "You probably remove non-audio tracks from the cue sheet (\"${CUE}\") by hand.\n" - return 1 - fi - - if [ -z "${TRACKS_NUM}" ]; then - emsg "Failed to get number of tracks from CUE sheet.\n" - emsg "There may be an error in the sheet.\n" - emsg "Running ${GETTAG} %N \"${CUE}\" produces this:\n" - ${GETTAG} %N "${CUE}" - return 1 - fi - - TAG_GENRE=$(grep 'REM[ \t]\+GENRE[ \t]\+' "${CUE}" | head -1 | sed 's/REM[ \t]\+GENRE[ \t]\+//;s/^"\(.*\)"$/\1/') - - YEAR=$(awk '{ if (/REM[ \t]+DATE/) { printf "%i", $3; exit } }' < "${CUE}") - YEAR=$(echo ${YEAR} | tr -d -c '[:digit:]') - - unset TAG_DATE - - if [ -n "${YEAR}" ]; then - [ ${YEAR} -ne 0 ] && TAG_DATE="${YEAR}" - fi - - $msg "\n${cG}Artist :$cZ ${TAG_ARTIST}\n" - $msg "${cG}Album :$cZ ${TAG_ALBUM}\n" - [ "${TAG_GENRE}" ] && $msg "${cG}Genre :$cZ ${TAG_GENRE}\n" - [ "${TAG_DATE}" ] && $msg "${cG}Year :$cZ ${TAG_DATE}\n" - $msg "${cG}Tracks :$cZ ${TRACKS_NUM}\n\n" - - # those tags won't change, so update the pattern now - DIR_ARTIST=$(echo "${TAG_ARTIST}" | ${VALIDATE}) - DIR_ALBUM=$(echo "${TAG_ALBUM}" | ${VALIDATE}) - PATTERN=$(update_pattern "${OUTPATTERN}" "artist" "${DIR_ARTIST}") - PATTERN=$(update_pattern "${PATTERN}" "album" "${DIR_ALBUM}") - PATTERN=$(update_pattern "${PATTERN}" "genre" "${TAG_GENRE}") - PATTERN=$(update_pattern "${PATTERN}" "year" "${TAG_DATE}") - PATTERN=$(update_pattern "${PATTERN}" "ext" "${FORMAT}") - - # construct output directory name - OUT="${DIR}" - - if [ ${NOSUBDIRS} -eq 0 ]; then - # add path from the pattern - path=$(dirname "${PATTERN}") - [ "${path}" != "${PATTERN}" ] && OUT="${OUT}/${path}" - fi - - # shnsplit is retarded enough to break on double slash - OUT=$(echo "${OUT}" | sed s,/[/]*,/,g) - - # remove path from the pattern - PATTERN=$(basename "${PATTERN}") - - $msg "${cP}Saving tracks to $cZ\"${OUT}\"\n" - - # split to tracks - if [ ${DRY} -ne 1 ]; then - # remove if empty and create output dir - if [ ${NOSUBDIRS} -eq 0 ]; then - rmdir "${OUT}" 2>/dev/null - mkdir -p "${OUT}" - [ $? -ne 0 ] && { emsg "Failed to create output directory ${OUT} (already split?)\n"; return 1; } - fi - - case ${FORMAT} in - flac) ENC="flac ${FLAC_ENCODER} ${ENCARGS} - -o %f"; RG="metaflac --add-replay-gain";; - m4a) ENC="cust ext=m4a faac ${ENCARGS} -o %f -"; RG="aacgain";; - mp3) ENC="cust ext=mp3 lame ${ENCARGS} - %f"; RG="mp3gain";; - ogg) ENC="cust ext=ogg oggenc ${ENCARGS} - -o %f"; RG="vorbisgain -a";; - wav) ENC="wav ${ENCARGS}"; REPLAY_GAIN=0;; - *) emsg "Unknown output format ${FORMAT}\n"; exit 1;; - esac - - # split to tracks - # sed expression is a fix for "shnsplit: error: m:ss.ff format can only be used with CD-quality files" - cuebreakpoints "${CUE}" 2>/dev/null | \ - sed 's/$/0/' | \ - shnsplit -O never -o "${ENC}" -d "${OUT}" -t "%n" "${FILE}" - if [ $? -ne 0 ]; then - emsg "Failed to split\n" - return 1 - fi - - # prepare cover image - if [ -n "${PIC}" ]; then - convert "${PIC}" -resize "${PIC_SIZE}" "${TMPPIC}" - if [ $? -eq 0 ]; then - PIC="${TMPPIC}" - else - $msg "${cR}Failed to convert cover image$cZ\n" - unset PIC - fi - fi - fi - - # set tags and rename - $msg "\n${cP}Setting tags$cZ\n" - - i=1 - while [ $i -le ${TRACKS_NUM} ]; do - TAG_TITLE=$(cueprint -n $i -t %t "${CUE}" 2>/dev/null) - FILE_TRACK="$(printf %02i $i)" - FILE_TITLE=$(echo "${TAG_TITLE}" | ${VALIDATE}) - f="${OUT}/${FILE_TRACK}.${FORMAT}" - - TAG_PERFORMER=$(cueprint -n $i -t %p "${CUE}" 2>/dev/null) - - if [ -n "${TAG_PERFORMER}" -a "${TAG_PERFORMER}" != "${TAG_ARTIST}" ]; then - $msg "$i: $cG${TAG_PERFORMER} - ${TAG_TITLE}$cZ\n" - else - TAG_PERFORMER="${TAG_ARTIST}" - $msg "$i: $cG${TAG_TITLE}$cZ\n" - fi - - FINAL=$(update_pattern "${OUT}/${PATTERN}" "title" "${FILE_TITLE}") - FINAL=$(update_pattern "${FINAL}" "performer" "${TAG_PERFORMER}") - FINAL=$(update_pattern "${FINAL}" "track" "${FILE_TRACK}") - - if [ ${DRY} -ne 1 -a "$f" != "${FINAL}" ]; then - mv "$f" "${FINAL}" - if [ $? -ne 0 ]; then - emsg "Failed to rename track file\n" - return 1 - fi - fi - - if [ ${DRY} -ne 1 ]; then - case ${FORMAT} in - flac) - ${METAFLAC} --remove-all-tags \ - --set-tag="ARTIST=${TAG_PERFORMER}" \ - --set-tag="ALBUM=${TAG_ALBUM}" \ - --set-tag="TITLE=${TAG_TITLE}" \ - --set-tag="TRACKNUMBER=$i" \ - --set-tag="TRACKTOTAL=${TRACKS_NUM}" \ - "${FINAL}" >/dev/null - RES=$? - - [ "${TAG_GENRE}" ] && { ${METAFLAC} --set-tag="GENRE=${TAG_GENRE}" "${FINAL}" >/dev/null; RES=$RES$?; } - [ "${TAG_DATE}" ] && { ${METAFLAC} --set-tag="DATE=${TAG_DATE}" "${FINAL}" >/dev/null; RES=$RES$?; } - [ "${PIC}" ] && { ${METAFLAC} --import-picture-from="${PIC}" "${FINAL}" >/dev/null; RES=$RES$?; } - ;; - - mp3) - ${ID3TAG} --artist="${TAG_PERFORMER}" \ - --album="${TAG_ALBUM}" \ - --song="${TAG_TITLE}" \ - --track="$i" \ - "${FINAL}" >/dev/null - RES=$? - - [ "${TAG_GENRE}" ] && { ${ID3TAG} --genre="${TAG_GENRE}" "${FINAL}" >/dev/null; RES=$RES$?; } - [ "${TAG_DATE}" ] && { ${ID3TAG} --year="${TAG_DATE}" "${FINAL}" >/dev/null; RES=$RES$?; } - ;; - - ogg) - ${VORBISCOMMENT} "${FINAL}" \ - -t "ARTIST=${TAG_PERFORMER}" \ - -t "ALBUM=${TAG_ALBUM}" \ - -t "TITLE=${TAG_TITLE}" \ - -t "TRACKNUMBER=$i" \ - -t "TRACKTOTAL=${TRACKS_NUM}" >/dev/null - RES=$? - - [ "${TAG_GENRE}" ] && { ${VORBISCOMMENT} "${FINAL}" -t "GENRE=${TAG_GENRE}" >/dev/null; RES=$RES$?; } - [ "${TAG_DATE}" ] && { ${VORBISCOMMENT} "${FINAL}" -t "DATE=${TAG_DATE}" >/dev/null; RES=$RES$?; } - ;; - - m4a) - ${MP4TAGS} "${FINAL}" \ - -a "${TAG_PERFORMER}" \ - -A "${TAG_ALBUM}" \ - -s "${TAG_TITLE}" \ - -t "$i" \ - -T "${TRACKS_NUM}" >/dev/null - RES=$? - - [ "${TAG_GENRE}" ] && { ${MP4TAGS} "${FINAL}" -g "${TAG_GENRE}" >/dev/null; RES=$RES$?; } - [ "${TAG_DATE}" ] && { ${MP4TAGS} "${FINAL}" -y "${TAG_DATE}" >/dev/null; RES=$RES$?; } - [ "${PIC}" ] && { ${MP4TAGS} "${FINAL}" -P "${PIC}" >/dev/null; RES=$RES$?; } - ;; - - wav) - RES=0 - ;; - - *) - emsg "Unknown output format ${FORMAT}\n" - return 1 - ;; - esac - - if [ ${RES} -ne 0 ]; then - emsg "Failed to set tags for track\n" - return 1 - fi - fi - - $msg " -> ${cP}${FINAL}$cZ\n" - - i=$(($i + 1)) - done - - # adjust gain - if [ ${REPLAY_GAIN} -ne 0 ]; then - $msg "\n${cP}Adjusting gain$cZ\n" - - if [ ${DRY} -ne 1 ]; then - ${RG} "${OUT}/"*.${FORMAT} >/dev/null - - if [ $? -ne 0 ]; then - emsg "Failed to adjust gain for track\n" - return 1 - fi - fi - fi - - # copy files - if [ ${COPYFILES} -eq 1 -a "${COPYMASKS}" ]; then - old=`pwd` - cd "${SDIR}" - $msg "\n${cG}Copying files:$cZ\n" - eval "for i in ${COPYMASKS}; do \ - test -r \"\$i\" && \ - echo \" +> \$i\" 2>/dev/null; done" - cd "${old}" - if [ ${DRY} -ne 1 ]; then - eval "for i in ${COPYMASKS}; do \ - test -r/\"${SDIR}/\$i\" && \ - cp -r \"${SDIR}/\$i\" \"\${OUT}/\"; done" - fi - fi - - rm -f "${TMPPIC}" - rm -f "${TMPCUE}" - - if [ ${DRY} -ne 1 -a ${REMOVE} -eq 1 ]; then - YEP="n" - - if [ ${FORCE} -ne 1 ]; then - echo -n "Are you sure you want to delete original? [y/N] > " - read YEP - fi - - [ "${YEP}" = "y" -o "${YEP}" = "Y" -o ${FORCE} -eq 1 ] && rm -f "${FILE}" - fi - - return 0 -} - -# searches for files in a directory and splits them -split_collection () { - rm -f "${FAILED}" - NUM_FAILED=0 - OLDIFS=${IFS} - OLDCHARSET="${CHARSET}" - # set IFS to newline. we do not use 'read' here because we may want to ask user for input - IFS=" -" - - for FILE in `find "$1" -iname '*.flac' -o -iname '*.ape' -o -iname '*.tta' -o -iname '*.wv' -o -iname '*.wav'`; do - IFS=${OLDIFS} - CHARSET=${OLDCHARSET} - $msg "$cG>> $cC\"${FILE}\"$cZ\n" - unset PIC CUE - split_file "${FILE}" - - if [ ! $? -eq 0 ]; then - emsg "Failed to split \"${FILE}\"\n" - echo "${FILE}" >> "${FAILED}" - NUM_FAILED=$((${NUM_FAILED} + 1)) - fi - - echo - done - - if [ ${NUM_FAILED} -ne 0 ]; then - emsg "${NUM_FAILED} file(s) failed to split (already split?):\n" - $msg "${cR}\n" - sort "${FAILED}" -o "${FAILED}" - cat "${FAILED}" - emsg "\nThese files are also listed in ${FAILED}.\n" - return 1 - fi - - return 0 -} - -if [ -d "${INPATH}" ]; then - if [ ! -x "${INPATH}" ]; then - emsg "Directory \"${INPATH}\" is not accessible\n" - exit 2 - fi - $msg "${cG}Input dir :$cZ ${INPATH}$cZ\n\n" - split_collection "${INPATH}" -elif [ -n "${INPATH}" ]; then - split_file "${INPATH}" -else - emsg "No input filename given. Use -h for help.\n" - exit 1 -fi - -# exit code of split_collection or split_file -STATUS=$? - -$msg "\n${cP}Finished$cZ\n" - -[ ${STATUS} -ne 0 ] && exit 3 || exit 0 - -### Local Variables: *** -### mode:sh *** -### tab-width:4 *** -### End: *** diff --git a/audio/wma2mp3.sh b/audio/wma2mp3.sh deleted file mode 100755 index db51e56..0000000 --- a/audio/wma2mp3.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# convert *.wma to *.mp3 - -current_directory=$( pwd ) - -#remove spaces -for i in *.wma; do mv "$i" `echo $i | tr ' ' '_'`; done - -#remove uppercase -for i in *.[Ww][Mm][Aa]; do mv "$i" `echo $i | tr '[A-Z]' '[a-z]'`; done - -#Rip with Mplayer / encode with LAME -for i in *.wma ; do mplayer -vo null -vc dummy -af resample=44100 -ao pcm - -waveheader $i && lame -m s audiodump.wav -o $i; done - -#convert file names -for i in *.wma; do mv "$i" "`basename "$i" .wma`.mp3"; done - -rm audiodump.wav - diff --git a/cli/ape2id3.py b/cli/ape2id3.py new file mode 100755 index 0000000..8651a2f --- /dev/null +++ b/cli/ape2id3.py @@ -0,0 +1,145 @@ +#! /usr/bin/env python +import sys +from optparse import OptionParser +import mutagen +from mutagen.apev2 import APEv2 +from mutagen.id3 import ID3, TXXX + +def convert_gain(gain): + if gain[-3:] == " dB": + gain = gain[:-3] + try: + gain = float(gain) + except ValueError: + raise ValueError, "invalid gain value" + return "%.2f dB" % gain +def convert_peak(peak): + try: + peak = float(peak) + except ValueError: + raise ValueError, "invalid peak value" + return "%.6f" % peak + +REPLAYGAIN_TAGS = ( + ("mp3gain_album_minmax", None), + ("mp3gain_minmax", None), + ("replaygain_album_gain", convert_gain), + ("replaygain_album_peak", convert_peak), + ("replaygain_track_gain", convert_gain), + ("replaygain_track_peak", convert_peak), +) + + +class Logger(object): + def __init__(self, log_level, prog_name): + self.log_level = log_level + self.prog_name = prog_name + self.filename = None + def prefix(self, msg): + if self.filename is None: + return msg + return "%s: %s" % (self.filename, msg) + def debug(self, msg): + if self.log_level >= 4: + print self.prefix(msg) + def info(self, msg): + if self.log_level >= 3: + print self.prefix(msg) + def warning(self, msg): + if self.log_level >= 2: + print self.prefix("WARNING: %s" % msg) + def error(self, msg): + if self.log_level >= 1: + sys.stderr.write("%s: %s\n" % (self.prog_name, msg)) + def critical(self, msg, retval=1): + self.error(msg) + sys.exit(retval) + +class Ape2Id3(object): + def __init__(self, logger, force=False): + self.log = logger + self.force = force + def convert_tag(self, name, value): + pass + def copy_replaygain_tag(self, apev2, id3, name, converter=None): + self.log.debug("processing '%s' tag" % name) + if name not in apev2: + self.log.info("no APEv2 '%s' tag found, skipping tag" % name) + return False + if not self.force and ("TXXX:%s" % name) in id3: + self.log.info("ID3 '%s' tag already exists, skpping tag" % name) + return False + value = str(apev2[name]) + if callable(converter): + self.log.debug("converting APEv2 '%s' tag from '%s'" % + (name, value)) + try: + value = converter(value) + except ValueError: + self.log.warning("invalid value for APEv2 '%s' tag" % name) + return False + self.log.debug("converted APEv2 '%s' tag to '%s'" % (name, value)) + id3.add(TXXX(encoding=1, desc=name, text=value)) + self.log.info("added ID3 '%s' tag with value '%s'" % (name, value)) + return True + def copy_replaygain_tags(self, filename): + self.log.filename = filename + self.log.debug("begin processing file") + try: + apev2 = APEv2(filename) + except mutagen.apev2.error: + self.log.info("no APEv2 tag found, skipping file") + return + except IOError: + e = sys.exc_info() + self.log.error("%s" % e[1]) + return + try: + id3 = ID3(filename) + except mutagen.id3.error: + self.log.info("no ID3 tag found, creating one") + id3 = ID3() + modified = False + for name, converter in REPLAYGAIN_TAGS: + copied = self.copy_replaygain_tag(apev2, id3, name, converter) + if copied: + modified = True + if modified: + self.log.debug("saving modified ID3 tag") + id3.save(filename) + self.log.debug("done processing file") + self.log.filename = None + +def main(prog_name, options, args): + logger = Logger(options.log_level, prog_name) + ape2id3 = Ape2Id3(logger, force=options.force) + for filename in args: + ape2id3.copy_replaygain_tags(filename) + +if __name__ == "__main__": + parser = OptionParser(version="0.1", usage="%prog [OPTION]... FILE...", + description="Copy APEv2 ReplayGain tags on " + "FILE(s) to ID3v2.") + parser.add_option("-q", "--quiet", dest="log_level", + action="store_const", const=0, default=1, + help="do not output error messages") + parser.add_option("-v", "--verbose", dest="log_level", + action="store_const", const=3, + help="output warnings and informational messages") + parser.add_option("-d", "--debug", dest="log_level", + action="store_const", const=4, + help="output debug messages") + parser.add_option("-f", "--force", dest="force", + action="store_true", default=False, + help="force overwriting of existing ID3v2 " + "ReplayGain tags") + prog_name = parser.get_prog_name() + options, args = parser.parse_args() + if len(args) < 1: + parser.error("no files specified") + try: + main(prog_name, options, args) + except KeyboardInterrupt: + pass + +# vim: set expandtab shiftwidth=4 softtabstop=4 textwidth=79: diff --git a/cli/split2flac b/cli/split2flac new file mode 100755 index 0000000..6622262 --- /dev/null +++ b/cli/split2flac @@ -0,0 +1,752 @@ +#!/bin/sh +# Copyright (c) 2009-2015 Serge "ftrvxmtrx" Ziryukin +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Dependencies: +# shntool, cuetools +# SPLIT: flac, wavpack, mac +# CONVERT: flac/flake, faac, libmp4v2, id3lib/mutagen, lame, vorbis-tools +# ART: ImageMagick +# CHARSET: iconv, enca +# GAIN: flac, aacgain, mp3gain, vorbisgain + +# Exit codes: +# 0 - success +# 1 - error in arguments +# 2 - file or path is not accessible +# 3 - something has failed + +[ -n "${XDG_CONFIG_HOME}" ] && CONFIG="${XDG_CONFIG_HOME}/split2flac/split2flac.conf" +[ -r "${CONFIG}" ] || CONFIG="${HOME}/.split2flac" +TMPPIC="${HOME}/.split2flac_cover.jpg" +FAILED="split_failed.txt" + +NOSUBDIRS=0 +NOPIC=0 +REMOVE=0 +NOCOLORS=0 +PIC_SIZE="192x192" +REPLAY_GAIN=0 +FORMAT="${0##*split2}" +DIR="." +OUTPATTERN="@artist/{@year - }@album/@track - @title.@ext" +COPYMASKS="[Cc]overs \*.log \*.txt \*.jpg \*.cbr" +COPYFILES=1 +ENCA_ARGS="" + +# codecs default arguments +ENCARGS_flac="-8" +ENCARGS_m4a="-q 500" +ENCARGS_mp3="--preset standard" +ENCARGS_ogg="-q 5" +ENCARGS_wav="" + +# load settings +eval $(cat "${CONFIG}" 2>/dev/null) +DRY=0 +SAVE=0 +NASK=0 +unset PIC INPATH CUE CHARSET +FORCE=0 + +# do not forget to update before commit +VERSION=121 + +HELP="\${cG}split2flac version: ${VERSION} +Splits one big \${cU}APE/FLAC/WV/WAV\$cZ\$cG audio image (or a collection) into \${cU}FLAC/M4A/MP3/OGG_VORBIS/WAV\$cZ\$cG tracks with tagging and renaming. + +Usage: \${cZ}split2\${FORMAT} [\${cU}OPTIONS\$cZ] \${cU}FILE\$cZ [\${cU}OPTIONS\$cZ]\$cZ + \${cZ}split2\${FORMAT} [\${cU}OPTIONS\$cZ] \${cU}DIR\$cZ [\${cU}OPTIONS\$cZ]\$cZ + \$cG-p\$cZ - dry run + \$cG-o \${cU}DIRECTORY\$cZ \$cR*\$cZ - set output directory (current is \$cP\${DIR}\$cZ) + \$cG-of \${cU}'PATTERN'\$cZ \$cR*\$cZ - use specific output naming pattern (current is \$cP'\${OUTPATTERN}'\$cZ) + \$cG-cue \${cU}FILE\$cZ - use file as a cue sheet (does not work with \${cU}DIR\$cZ) + \$cG-cuecharset \${cU}CHARSET\$cZ - convert cue sheet from CHARSET to UTF-8 (no conversion by default) + \$cG-nask\$cZ - do not ask to enter proper charset of a cue sheet (default is to ask) + \$cG-f \${cU}FORMAT\$cZ - use specific output format (current is \$cP\${FORMAT}\$cZ) + \$cG-e \${cU}'ARG1 ARG2'\$cZ \$cR*\$cZ - encoder arguments (current is \$cP'\${ENCARGS}'\$cZ) + \$cG-eh\$cZ - show help for current encoder and exit\$cZ + \$cG-enca \${cU}'ARG1 ARG2'\$cZ \$cR*\$cZ - enca additional arguments (current is \$cP'\${ENCA_ARGS}'\$cZ) + \$cG-c \${cU}FILE\$cZ \$cR*\$cZ - use file as a cover image (does not work with \${cU}DIR\$cZ) + \$cG-nc \${cR}*\$cZ - do not set any cover images + \$cG-C \${cU}MASKS\$cZ \$cR*\$cZ - specify wildcards for files to copy over (current is \$cP'\${COPYMASKS}'\$cZ) + \$cG-nC \${cR}*\$cZ - do not copy any files + \$cG-cs \${cU}WxH\$cZ \$cR*\$cZ - set cover image size (current is \$cP\${PIC_SIZE}\$cZ) + \$cG-d \$cR*\$cZ - create artist/album subdirs (default) + \$cG-nd \$cR*\$cZ - do not create any subdirs + \$cG-D \$cR*\$cZ - delete original file + \$cG-nD \$cR*\$cZ - do not remove the original (default) + \$cG-F\$cZ - force deletion without asking + \$cG-colors\$cZ \$cR*\$cZ - colorized output (default) + \$cG-nocolors\$cZ \$cR*\$cZ - turn off colors + \$cG-g\$cZ \$cR*\$cZ - adjust audio gain + \$cG-ng\$cZ \$cR*\$cZ - do not adjust audio gain (default) + \$cG-s\$cZ - save configuration to \$cP\"\${CONFIG}\"\$cZ + \$cG-h\$cZ - print this message + \$cG-v\$cZ - print version + +\$cR*\$cZ - option affects configuration if \$cP'-s'\$cZ option passed. +\${cP}NOTE: \$cG'-c some_file.jpg -s'\$cP only \${cU}allows\$cZ\$cP cover images, it doesn't set a default one. +\${cZ}Supported \$cU\${cG}FORMATs\${cZ}: flac, m4a, mp3, ogg, wav. +Supported tags for \$cU\${cG}PATTERN\${cZ}: @artist, @album, @year, @track, @performer, @title, @genre, @ext. +@performer tag is useful with 'various artists' albums, when you want to add +each artist's name to the track filename. It works as @artist if track performer is undefined. +Special \"underscored\" tags are also supported (@_artist, @_album, etc). If used, spaces will be replaced with +underscores. It's useful if you want to have filenames without spaces. + +It's better to pass \$cP'-p'\$cZ option to see what will happen when actually splitting tracks. +You may want to pass \$cP'-s'\$cZ option for the first run to save default configuration +(output dir, cover image size, etc.) so you won't need to pass a lot of options +every time, just a filename. Script will try to find CUE sheet if it wasn't specified. +It also supports internal CUE sheets (FLAC, APE and WV).\n" + +msg="printf" + +emsg () { + $msg "${cR}$1${cZ}" +} + +SKIP_UPDATE_ENCARGS=0 + +update_encargs () { + if [ ${SKIP_UPDATE_ENCARGS} -eq 0 ]; then + e="\${ENCARGS_${FORMAT}}" + ENCARGS=`eval echo "$e"` + ENCHELP=0 + fi +} + +update_colors () { + if [ "${NOCOLORS}" -eq 0 ]; then + cR="\033[31m" + cG="\033[32m" + cC="\033[35m" + cP="\033[36m" + cU="\033[4m" + cZ="\033[0m" + else + unset cR cG cC cP cU cZ + fi +} + +update_encargs +update_colors + +# parse arguments +while [ "$1" ]; do + case "$1" in + -o) DIR=$2; shift;; + -of) OUTPATTERN=$2; shift;; + -cue) CUE=$2; shift;; + -cuecharset) CHARSET=$2; shift;; + -nask) NASK=1;; + -f) FORMAT=$2; update_encargs; shift;; + -e) ENCARGS=$2; SKIP_UPDATE_ENCARGS=1; shift;; + -eh) ENCHELP=1;; + -enca) ENCA_ARGS=$2; shift;; + -c) NOPIC=0; PIC=$2; shift;; + -nc) NOPIC=1;; + -C) COPYMASKS=$2; COPYFILES=1; shift;; + -nC) COPYFILES=0;; + -cs) PIC_SIZE=$2; shift;; + -d) NOSUBDIRS=0;; + -nd) NOSUBDIRS=1;; + -p) DRY=1;; + -D) REMOVE=1;; + -nD) REMOVE=0;; + -F) FORCE=1;; + -colors) NOCOLORS=0; update_colors;; + -nocolors) NOCOLORS=1; update_colors;; + -g) REPLAY_GAIN=1;; + -ng) REPLAY_GAIN=0;; + -s) SAVE=1;; + -h|--help|-help) eval "$msg \"${HELP}\""; exit 0;; + -v|--version) + $msg "split2${FORMAT} version: ${VERSION}\n\n"; + shntool -v 2>&1 | grep '^shntool'; + flac --version 2>/dev/null; + wavpack --help 2>&1 | grep 'Version'; + mac 2>&1 | grep '(v '; + faac -h 2>&1 | grep '^FAAC'; + oggenc --version 2>/dev/null; + lame --version | grep '^LAME'; + exit 0;; + -*) eval "$msg \"${HELP}\""; emsg "\nUnknown option $1\n"; exit 1;; + *) + if [ -n "${INPATH}" ]; then + eval "$msg \"${HELP}\"" + emsg "\nUnknown option $1\n" + exit 1 + elif [ ! -r "$1" ]; then + emsg "Unable to read $1\n" + exit 2 + else + INPATH="$1" + fi;; + esac + shift +done + +eval "export ENCARGS_${FORMAT}=\"${ENCARGS}\"" + +# save configuration if needed +if [ ${SAVE} -eq 1 ]; then + echo "DIR=\"${DIR}\"" > "${CONFIG}" + echo "OUTPATTERN=\"${OUTPATTERN}\"" >> "${CONFIG}" + echo "COPYMASKS=\"${COPYMASKS}\"" >> "${CONFIG}" + echo "COPYFILES=${COPYFILES}" >> "${CONFIG}" + echo "NOSUBDIRS=${NOSUBDIRS}" >> "${CONFIG}" + echo "NOPIC=${NOPIC}" >> "${CONFIG}" + echo "REMOVE=${REMOVE}" >> "${CONFIG}" + echo "PIC_SIZE=${PIC_SIZE}" >> "${CONFIG}" + echo "NOCOLORS=${NOCOLORS}" >> "${CONFIG}" + echo "REPLAY_GAIN=${REPLAY_GAIN}" >> "${CONFIG}" + echo "ENCARGS_flac=\"${ENCARGS_flac}\"" >> "${CONFIG}" + echo "ENCARGS_m4a=\"${ENCARGS_m4a}\"" >> "${CONFIG}" + echo "ENCARGS_mp3=\"${ENCARGS_mp3}\"" >> "${CONFIG}" + echo "ENCARGS_ogg=\"${ENCARGS_ogg}\"" >> "${CONFIG}" + echo "ENCARGS_wav=\"${ENCARGS_wav}\"" >> "${CONFIG}" + echo "ENCA_ARGS=\"${ENCA_ARGS}\"" >> "${CONFIG}" + $msg "${cP}Configuration saved$cZ\n" +fi + +# use flake if possible +command -v flake >/dev/null && FLAC_ENCODER="flake" || FLAC_ENCODER="flac" + +METAFLAC="metaflac --no-utf8-convert" +VORBISCOMMENT="vorbiscomment -R -a" +command -v mid3v2 >/dev/null && ID3TAG="mid3v2" || ID3TAG="id3tag -2" +MP4TAGS="mp4tags" +GETTAG="cueprint -n 1 -t" +VALIDATE="sed s/[^][[:space:][:alnum:]&_#,.'\"\(\)!-]//g" + +# check & print output format +msg_format="${cG}Output format :$cZ" +case ${FORMAT} in + flac) $msg "$msg_format FLAC [using ${FLAC_ENCODER} tool]"; enc_help="${FLAC_ENCODER} -h";; + m4a) $msg "$msg_format M4A"; enc_help="faac --help";; + mp3) $msg "$msg_format MP3"; enc_help="lame --help";; + ogg) $msg "$msg_format OGG VORBIS"; enc_help="oggenc --help";; + wav) $msg "$msg_format WAVE"; enc_help="echo Sorry, no arguments available for this encoder";; + *) emsg "Unknown output format \"${FORMAT}\"\n"; exit 1;; +esac + +$msg " (${ENCARGS})\n" + +if [ ${ENCHELP} -eq 1 ]; then + ${enc_help} + exit 0 +fi + +$msg "${cG}Output dir :$cZ ${DIR:?Output directory was not set}\n" + +# replaces a tag name with the value of the tag. $1=pattern $2=tag_name $3=tag_value +update_pattern_aux () { + tag_name="$2" + tag_value="$3" + expr_match="@${tag_name}" + expr_match_opt="[{]\([^}{]*\)${expr_match}\([^}]*\)[}]" + + echo "$1" | { [ "${tag_value}" ] \ + && sed "s/${expr_match_opt}/\1${tag_value}\2/g;s/${expr_match}/${tag_value}/g" \ + || sed "s/${expr_match_opt}//g;s/${expr_match}//g"; } +} + +# replaces a tag name with the value of the tag. $1=pattern $2=tag_name $3=tag_value +update_pattern () { + # replace '/' with '\' and '&' with '\&' for proper sed call + tag_name=$(echo "$2" | sed 's,/,\\\\,g;s,&,\\&,g') + tag_value=$(echo "$3" | sed 's,/,\\\\,g;s,&,\\&,g') + + v=$(update_pattern_aux "$1" "${tag_name}" "${tag_value}") + update_pattern_aux "$v" "_${tag_name}" $(echo "${tag_value}" | sed "s/ /_/g") +} + +# splits a file +split_file () { + TMPCUE="${HOME}/.split2flac_XXXXX.cue" + FILE="$1" + + if [ ! -r "${FILE}" ]; then + emsg "Can not read the file\n" + return 1 + fi + + # search for a cue sheet if not specified + if [ -z "${CUE}" ]; then + CUE="${FILE}.cue" + if [ ! -r "${CUE}" ]; then + CUE="${FILE%.*}.cue" + if [ ! -r "${CUE}" ]; then + # try to extract internal one + CUESHEET=$(${METAFLAC} --show-tag=CUESHEET "${FILE}" 2>/dev/null | sed 's/^cuesheet=//;s/^CUESHEET=//') + + # try WV internal cue sheet + [ -z "${CUESHEET}" ] && CUESHEET=$(wvunpack -q -c "${FILE}" 2>/dev/null) + + # try APE internal cue sheet (omfg!) + if [ -z "${CUESHEET}" ]; then + APETAGEX=$(tail -c 32 "$1" | cut -b 1-8 2>/dev/null) + if [ "${APETAGEX}" = "APETAGEX" ]; then + LENGTH=$(tail -c 32 "$1" | cut -b 13-16 | od -t u4 | awk '{printf $2}') 2>/dev/null + tail -c ${LENGTH} "$1" | grep -a CUESHEET >/dev/null 2>&1 + if [ $? -eq 0 ]; then + CUESHEET=$(tail -c ${LENGTH} "$1" | sed 's/.*CUESHEET.//g' 2>/dev/null) + [ $? -ne 0 ] && CUESHEET="" + fi + fi + fi + + if [ -n "${CUESHEET}" ]; then + $msg "${cP}Found internal cue sheet$cZ\n" + TMPCUE=$(mktemp "${TMPCUE}") + CUE="${TMPCUE}" + echo "${CUESHEET}" > "${CUE}" + TMPCUE="${HOME}/.split2flac_XXXXX.cue" + + if [ $? -ne 0 ]; then + emsg "Unable to save internal cue sheet\n" + return 1 + fi + else + unset CUE + fi + fi + fi + fi + + # print cue sheet filename + if [ -z "${CUE}" ]; then + emsg "No cue sheet\n" + return 1 + fi + + # cue sheet charset + [ -z "${CHARSET}" ] && CHARSET="utf-8" || $msg "${cG}Cue charset : $cP${CHARSET} -> utf-8$cZ\n" + + CUESHEET=$(iconv -f "${CHARSET}" -t utf-8 "${CUE}" 2>/dev/null) + + # try to guess the charset using enca + if [ $? -ne 0 ]; then + CUESHEET=$(enconv ${ENCA_ARGS} -x utf8 < "${CUE}" 2>/dev/null) + fi + + if [ $? -ne 0 ]; then + [ "${CHARSET}" = "utf-8" ] \ + && emsg "Cue sheet is not utf-8\n" \ + || emsg "Unable to convert cue sheet from ${CHARSET} to utf-8\n" + + if [ ${NASK} -eq 0 ]; then + while [ 1 ]; do + echo -n "Please enter the charset (or just press ENTER to ignore) > " + read CHARSET + + [ -z "${CHARSET}" ] && break + $msg "${cG}Converted cue sheet:$cZ\n" + iconv -f "${CHARSET}" -t utf-8 "${CUE}" || continue + + echo -n "Is this right? [Y/n] > " + read YEP + [ -z "${YEP}" -o "${YEP}" = "y" -o "${YEP}" = "Y" ] && break + done + + CUESHEET=$(iconv -f "${CHARSET}" -t utf-8 "${CUE}" 2>/dev/null) + fi + fi + + # save converted cue sheet + TMPCUE=$(mktemp "${TMPCUE}") + CUE="${TMPCUE}" + echo "${CUESHEET}" > "${CUE}" + + if [ $? -ne 0 ]; then + emsg "Unable to save converted cue sheet\n" + return 1 + fi + + SDIR=$(dirname "${FILE}") + + # search for a front cover image + if [ ${NOPIC} -eq 1 ]; then + unset PIC + elif [ -z "${PIC}" ]; then + # try common names + for i in *[Cc]over*.jpg *[Ff]older*.jpg */*[Cc]over*.jpg */*[Ff]older*.jpg; do + if [ -r "${SDIR}/$i" ]; then + PIC="${SDIR}/$i" + break + fi + done + + # try to extract internal one + if [ -z "${PIC}" ]; then + ${METAFLAC} --export-picture-to="${TMPPIC}" "${FILE}" 2>/dev/null + if [ $? -ne 0 ]; then + unset PIC + else + PIC="${TMPPIC}" + fi + fi + fi + + $msg "${cG}Cue sheet :$cZ ${CUE}\n" + $msg "${cG}Cover image :$cZ ${PIC:-not set}\n" + + # file removal warning + if [ ${REMOVE} -eq 1 ]; then + msg_removal="\n${cR}Also remove original" + [ ${FORCE} -eq 1 ] \ + && $msg "$msg_removal (WITHOUT ASKING)$cZ\n" \ + || $msg "$msg_removal if user says 'y'$cZ\n" + fi + + # files to copy over + if [ ${COPYFILES} -eq 1 -a -n "${COPYMASKS}" ]; then + $msg "${cG}Copy over :$cZ ${COPYMASKS}\n" + fi + + # get common tags + TAG_ARTIST=$(${GETTAG} %P "${CUE}" 2>/dev/null) + TAG_ALBUM=$(${GETTAG} %T "${CUE}" 2>/dev/null) + TRACKS_NUM=$(${GETTAG} %N "${CUE}" 2>/dev/null) + + # some cue sheets may have non-audio tracks + # we can check the difference between what cuebreakpoints and cueprint gives us + BREAKPOINTS_NUM=$(($(cuebreakpoints "${CUE}" 2>/dev/null | wc -l) + 1)) + + # too bad, we can't fix that in a _right_ way + if [ ${BREAKPOINTS_NUM} -lt ${TRACKS_NUM} ]; then + emsg "'cueprint' tool reported ${TRACKS_NUM} tracks, " + emsg "but there seem to be only ${BREAKPOINTS_NUM} audio ones\n" + emsg "Sorry, there is no any helpful options in the 'cueprint' tool for this problem.\n" + emsg "You probably remove non-audio tracks from the cue sheet (\"${CUE}\") by hand.\n" + return 1 + fi + + if [ -z "${TRACKS_NUM}" ]; then + emsg "Failed to get number of tracks from CUE sheet.\n" + emsg "There may be an error in the sheet.\n" + emsg "Running ${GETTAG} %N \"${CUE}\" produces this:\n" + ${GETTAG} %N "${CUE}" + return 1 + fi + + TAG_GENRE=$(grep 'REM[ \t]\+GENRE[ \t]\+' "${CUE}" | head -1 | sed 's/REM[ \t]\+GENRE[ \t]\+//;s/^"\(.*\)"$/\1/') + + YEAR=$(awk '{ if (/REM[ \t]+DATE/) { printf "%i", $3; exit } }' < "${CUE}") + YEAR=$(echo ${YEAR} | tr -d -c '[:digit:]') + + unset TAG_DATE + + if [ -n "${YEAR}" ]; then + [ ${YEAR} -ne 0 ] && TAG_DATE="${YEAR}" + fi + + $msg "\n${cG}Artist :$cZ ${TAG_ARTIST}\n" + $msg "${cG}Album :$cZ ${TAG_ALBUM}\n" + [ "${TAG_GENRE}" ] && $msg "${cG}Genre :$cZ ${TAG_GENRE}\n" + [ "${TAG_DATE}" ] && $msg "${cG}Year :$cZ ${TAG_DATE}\n" + $msg "${cG}Tracks :$cZ ${TRACKS_NUM}\n\n" + + # those tags won't change, so update the pattern now + DIR_ARTIST=$(echo "${TAG_ARTIST}" | ${VALIDATE}) + DIR_ALBUM=$(echo "${TAG_ALBUM}" | ${VALIDATE}) + PATTERN=$(update_pattern "${OUTPATTERN}" "artist" "${DIR_ARTIST}") + PATTERN=$(update_pattern "${PATTERN}" "album" "${DIR_ALBUM}") + PATTERN=$(update_pattern "${PATTERN}" "genre" "${TAG_GENRE}") + PATTERN=$(update_pattern "${PATTERN}" "year" "${TAG_DATE}") + PATTERN=$(update_pattern "${PATTERN}" "ext" "${FORMAT}") + + # construct output directory name + OUT="${DIR}" + + if [ ${NOSUBDIRS} -eq 0 ]; then + # add path from the pattern + path=$(dirname "${PATTERN}") + [ "${path}" != "${PATTERN}" ] && OUT="${OUT}/${path}" + fi + + # shnsplit is retarded enough to break on double slash + OUT=$(echo "${OUT}" | sed s,/[/]*,/,g) + + # remove path from the pattern + PATTERN=$(basename "${PATTERN}") + + $msg "${cP}Saving tracks to $cZ\"${OUT}\"\n" + + # split to tracks + if [ ${DRY} -ne 1 ]; then + # remove if empty and create output dir + if [ ${NOSUBDIRS} -eq 0 ]; then + rmdir "${OUT}" 2>/dev/null + mkdir -p "${OUT}" + [ $? -ne 0 ] && { emsg "Failed to create output directory ${OUT} (already split?)\n"; return 1; } + fi + + case ${FORMAT} in + flac) ENC="flac ${FLAC_ENCODER} ${ENCARGS} - -o %f"; RG="metaflac --add-replay-gain";; + m4a) ENC="cust ext=m4a faac ${ENCARGS} -o %f -"; RG="aacgain";; + mp3) ENC="cust ext=mp3 lame ${ENCARGS} - %f"; RG="mp3gain";; + ogg) ENC="cust ext=ogg oggenc ${ENCARGS} - -o %f"; RG="vorbisgain -a";; + wav) ENC="wav ${ENCARGS}"; REPLAY_GAIN=0;; + *) emsg "Unknown output format ${FORMAT}\n"; exit 1;; + esac + + # split to tracks + # sed expression is a fix for "shnsplit: error: m:ss.ff format can only be used with CD-quality files" + cuebreakpoints "${CUE}" 2>/dev/null | \ + sed 's/$/0/' | \ + shnsplit -O never -o "${ENC}" -d "${OUT}" -t "%n" "${FILE}" + if [ $? -ne 0 ]; then + emsg "Failed to split\n" + return 1 + fi + + # prepare cover image + if [ -n "${PIC}" ]; then + convert "${PIC}" -resize "${PIC_SIZE}" "${TMPPIC}" + if [ $? -eq 0 ]; then + PIC="${TMPPIC}" + else + $msg "${cR}Failed to convert cover image$cZ\n" + unset PIC + fi + fi + fi + + # set tags and rename + $msg "\n${cP}Setting tags$cZ\n" + + i=1 + while [ $i -le ${TRACKS_NUM} ]; do + TAG_TITLE=$(cueprint -n $i -t %t "${CUE}" 2>/dev/null) + FILE_TRACK="$(printf %02i $i)" + FILE_TITLE=$(echo "${TAG_TITLE}" | ${VALIDATE}) + f="${OUT}/${FILE_TRACK}.${FORMAT}" + + TAG_PERFORMER=$(cueprint -n $i -t %p "${CUE}" 2>/dev/null) + + if [ -n "${TAG_PERFORMER}" -a "${TAG_PERFORMER}" != "${TAG_ARTIST}" ]; then + $msg "$i: $cG${TAG_PERFORMER} - ${TAG_TITLE}$cZ\n" + else + TAG_PERFORMER="${TAG_ARTIST}" + $msg "$i: $cG${TAG_TITLE}$cZ\n" + fi + + FINAL=$(update_pattern "${OUT}/${PATTERN}" "title" "${FILE_TITLE}") + FINAL=$(update_pattern "${FINAL}" "performer" "${TAG_PERFORMER}") + FINAL=$(update_pattern "${FINAL}" "track" "${FILE_TRACK}") + + if [ ${DRY} -ne 1 -a "$f" != "${FINAL}" ]; then + mv "$f" "${FINAL}" + if [ $? -ne 0 ]; then + emsg "Failed to rename track file\n" + return 1 + fi + fi + + if [ ${DRY} -ne 1 ]; then + case ${FORMAT} in + flac) + ${METAFLAC} --remove-all-tags \ + --set-tag="ARTIST=${TAG_PERFORMER}" \ + --set-tag="ALBUM=${TAG_ALBUM}" \ + --set-tag="TITLE=${TAG_TITLE}" \ + --set-tag="TRACKNUMBER=$i" \ + --set-tag="TRACKTOTAL=${TRACKS_NUM}" \ + "${FINAL}" >/dev/null + RES=$? + + [ "${TAG_GENRE}" ] && { ${METAFLAC} --set-tag="GENRE=${TAG_GENRE}" "${FINAL}" >/dev/null; RES=$RES$?; } + [ "${TAG_DATE}" ] && { ${METAFLAC} --set-tag="DATE=${TAG_DATE}" "${FINAL}" >/dev/null; RES=$RES$?; } + [ "${PIC}" ] && { ${METAFLAC} --import-picture-from="${PIC}" "${FINAL}" >/dev/null; RES=$RES$?; } + ;; + + mp3) + ${ID3TAG} --artist="${TAG_PERFORMER}" \ + --album="${TAG_ALBUM}" \ + --song="${TAG_TITLE}" \ + --track="$i" \ + "${FINAL}" >/dev/null + RES=$? + + [ "${TAG_GENRE}" ] && { ${ID3TAG} --genre="${TAG_GENRE}" "${FINAL}" >/dev/null; RES=$RES$?; } + [ "${TAG_DATE}" ] && { ${ID3TAG} --year="${TAG_DATE}" "${FINAL}" >/dev/null; RES=$RES$?; } + ;; + + ogg) + ${VORBISCOMMENT} "${FINAL}" \ + -t "ARTIST=${TAG_PERFORMER}" \ + -t "ALBUM=${TAG_ALBUM}" \ + -t "TITLE=${TAG_TITLE}" \ + -t "TRACKNUMBER=$i" \ + -t "TRACKTOTAL=${TRACKS_NUM}" >/dev/null + RES=$? + + [ "${TAG_GENRE}" ] && { ${VORBISCOMMENT} "${FINAL}" -t "GENRE=${TAG_GENRE}" >/dev/null; RES=$RES$?; } + [ "${TAG_DATE}" ] && { ${VORBISCOMMENT} "${FINAL}" -t "DATE=${TAG_DATE}" >/dev/null; RES=$RES$?; } + ;; + + m4a) + ${MP4TAGS} "${FINAL}" \ + -a "${TAG_PERFORMER}" \ + -A "${TAG_ALBUM}" \ + -s "${TAG_TITLE}" \ + -t "$i" \ + -T "${TRACKS_NUM}" >/dev/null + RES=$? + + [ "${TAG_GENRE}" ] && { ${MP4TAGS} "${FINAL}" -g "${TAG_GENRE}" >/dev/null; RES=$RES$?; } + [ "${TAG_DATE}" ] && { ${MP4TAGS} "${FINAL}" -y "${TAG_DATE}" >/dev/null; RES=$RES$?; } + [ "${PIC}" ] && { ${MP4TAGS} "${FINAL}" -P "${PIC}" >/dev/null; RES=$RES$?; } + ;; + + wav) + RES=0 + ;; + + *) + emsg "Unknown output format ${FORMAT}\n" + return 1 + ;; + esac + + if [ ${RES} -ne 0 ]; then + emsg "Failed to set tags for track\n" + return 1 + fi + fi + + $msg " -> ${cP}${FINAL}$cZ\n" + + i=$(($i + 1)) + done + + # adjust gain + if [ ${REPLAY_GAIN} -ne 0 ]; then + $msg "\n${cP}Adjusting gain$cZ\n" + + if [ ${DRY} -ne 1 ]; then + ${RG} "${OUT}/"*.${FORMAT} >/dev/null + + if [ $? -ne 0 ]; then + emsg "Failed to adjust gain for track\n" + return 1 + fi + fi + fi + + # copy files + if [ ${COPYFILES} -eq 1 -a "${COPYMASKS}" ]; then + old=`pwd` + cd "${SDIR}" + $msg "\n${cG}Copying files:$cZ\n" + eval "for i in ${COPYMASKS}; do \ + test -r \"\$i\" && \ + echo \" +> \$i\" 2>/dev/null; done" + cd "${old}" + if [ ${DRY} -ne 1 ]; then + eval "for i in ${COPYMASKS}; do \ + test -r/\"${SDIR}/\$i\" && \ + cp -r \"${SDIR}/\$i\" \"\${OUT}/\"; done" + fi + fi + + rm -f "${TMPPIC}" + rm -f "${TMPCUE}" + + if [ ${DRY} -ne 1 -a ${REMOVE} -eq 1 ]; then + YEP="n" + + if [ ${FORCE} -ne 1 ]; then + echo -n "Are you sure you want to delete original? [y/N] > " + read YEP + fi + + [ "${YEP}" = "y" -o "${YEP}" = "Y" -o ${FORCE} -eq 1 ] && rm -f "${FILE}" + fi + + return 0 +} + +# searches for files in a directory and splits them +split_collection () { + rm -f "${FAILED}" + NUM_FAILED=0 + OLDIFS=${IFS} + OLDCHARSET="${CHARSET}" + # set IFS to newline. we do not use 'read' here because we may want to ask user for input + IFS=" +" + + for FILE in `find "$1" -iname '*.flac' -o -iname '*.ape' -o -iname '*.tta' -o -iname '*.wv' -o -iname '*.wav'`; do + IFS=${OLDIFS} + CHARSET=${OLDCHARSET} + $msg "$cG>> $cC\"${FILE}\"$cZ\n" + unset PIC CUE + split_file "${FILE}" + + if [ ! $? -eq 0 ]; then + emsg "Failed to split \"${FILE}\"\n" + echo "${FILE}" >> "${FAILED}" + NUM_FAILED=$((${NUM_FAILED} + 1)) + fi + + echo + done + + if [ ${NUM_FAILED} -ne 0 ]; then + emsg "${NUM_FAILED} file(s) failed to split (already split?):\n" + $msg "${cR}\n" + sort "${FAILED}" -o "${FAILED}" + cat "${FAILED}" + emsg "\nThese files are also listed in ${FAILED}.\n" + return 1 + fi + + return 0 +} + +if [ -d "${INPATH}" ]; then + if [ ! -x "${INPATH}" ]; then + emsg "Directory \"${INPATH}\" is not accessible\n" + exit 2 + fi + $msg "${cG}Input dir :$cZ ${INPATH}$cZ\n\n" + split_collection "${INPATH}" +elif [ -n "${INPATH}" ]; then + split_file "${INPATH}" +else + emsg "No input filename given. Use -h for help.\n" + exit 1 +fi + +# exit code of split_collection or split_file +STATUS=$? + +$msg "\n${cP}Finished$cZ\n" + +[ ${STATUS} -ne 0 ] && exit 3 || exit 0 + +### Local Variables: *** +### mode:sh *** +### tab-width:4 *** +### End: *** diff --git a/cli/wma2mp3.sh b/cli/wma2mp3.sh new file mode 100755 index 0000000..db51e56 --- /dev/null +++ b/cli/wma2mp3.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# convert *.wma to *.mp3 + +current_directory=$( pwd ) + +#remove spaces +for i in *.wma; do mv "$i" `echo $i | tr ' ' '_'`; done + +#remove uppercase +for i in *.[Ww][Mm][Aa]; do mv "$i" `echo $i | tr '[A-Z]' '[a-z]'`; done + +#Rip with Mplayer / encode with LAME +for i in *.wma ; do mplayer -vo null -vc dummy -af resample=44100 -ao pcm + -waveheader $i && lame -m s audiodump.wav -o $i; done + +#convert file names +for i in *.wma; do mv "$i" "`basename "$i" .wma`.mp3"; done + +rm audiodump.wav + -- cgit v1.2.2