742 lines · 22.6 KB · Text
RawDownload
#!/bin/sh# Copyright (c) 2009-2013 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 CONFIG="${HOME}/.split2flac"TMPPIC="${HOME}/.split2flac_cover.jpg"FAILED="split_failed.txt" NOSUBDIRS=0NOPIC=0REMOVE=0NOCOLORS=0PIC_SIZE="192x192"REPLAY_GAIN=0FORMAT="${0##*split2}"DIR="."OUTPATTERN="@artist/{@year - }@album/@track - @title.@ext"COPYMASKS="Covers,covers,*.log,*.txt,*.jpg,*.cbr"COPYFILES=1ENCA_ARGS="" # codecs default argumentsENCARGS_flac="-8"ENCARGS_m4a="-q 500"ENCARGS_mp3="--preset extreme"ENCARGS_ogg="-q 10"ENCARGS_wav="" # load settingseval $(cat "${CONFIG}" 2>/dev/null)DRY=0SAVE=0NASK=0unset PIC INPATH CUE CHARSETFORCE=0 # do not forget to update before commitVERSION=112 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 addeach 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 withunderscores. 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 optionsevery 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}"} update_encargs () {	e="\${ENCARGS_${FORMAT}}"	ENCARGS=`eval echo "$e"`	ENCHELP=0} 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_encargsupdate_colors # parse argumentswhile [ "$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; 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	shiftdone eval "export ENCARGS_${FORMAT}=\"${ENCARGS}\"" # save configuration if neededif [ ${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 possiblewhich flake &>/dev/null && FLAC_ENCODER="flake" || FLAC_ENCODER="flac" METAFLAC="metaflac --no-utf8-convert"VORBISCOMMENT="vorbiscomment -R -a"which mid3v2 &>/dev/null && ID3TAG="mid3v2" || ID3TAG="id3tag -2"MP4TAGS="mp4tags"GETTAG="cueprint -n 1 -t"VALIDATE="sed s/[^][[:space:][:alnum:]&_#,.'\"\(\)!-]//g" # check & print output formatmsg_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 0fi $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_valueupdate_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}/${tag_value}\1/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_valueupdate_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 filesplit_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 cover.jpg front_cover.jpg folder.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=$(sed 's/REM[ \t]*GENRE[ \t]*\(.*\)/\1/;s/"\(.*\)"/\1/;q' < "${CUE}") 	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 	# 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";;

Lines 1–500 of 742.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t