#!/bin/bash if [[ "${MUSIC_BASH}" != "" ]] then return fi # Includes #----------------------------------------------------------------------------------------------------------------------------------- . /usr/global/lib/default.bash # Global Variable #----------------------------------------------------------------------------------------------------------------------------------- declare -g MUSIC_BASH=1 declare -Ag FLAC_2_MP3_TAB=( [TRACKNUMBER]=TRCK [TRACKTOTAL]=TRCK [TITLE]=TIT2 [ALBUM]=TALB [ARTIST]=TPE1 [COMPOSER]=TCOM [ARRANGER]=IPLS [CONDUCTOR]=TPE3 [REMIXER]=TPE4 [AUTHOR]=TEXT [WRITER]=TEXT [LYRICIST]=TEXT [ALBUMARTIST]=TPE2 [DATE]=TDRC [GENRE]=TCON [COMPILATION]=TCMP [DISCTOTAL]=TPOS [DISCNUMBER]=TPOS [LABEL]=XXXX [PUBLISHER]=TPUB [ISRC]=TSRC [BARCODE]=TXXX [CATALOGNUMBER]=TXXX [COPYRIGHT]=TCOP [LICENSE]=USER [COMMENT]=COMM [DESCRIPTION]=TIT3 [ENCODER]=TSSE [MEDIA]=TMED [LANGUAGE]=TLAN [LYRICS]=USLT ) declare -Ag MP3_2_FLAC_TAB=( [TRCK]=TRACKNUMBER:TRACKTOTAL [TIT2]=TITLE [TALB]=ALBUM [TPE1]=ARTIST [TCOM]=COMPOSER [IPLS]=ARRANGER [TPE3]=CONDUCTOR [TPE4]=REMIXER [TEXT]=AUTHOR:WRITER:LYRICIST [TPE2]=ALBUMARTIST [TDRC]=DATE [TCON]=GENRE [TCMP]=COMPILATION [TPOS]=DISCNUMBER:DISCTOTAL [TPUB]=PUBLISHER [TSRC]=ISRC [TXXX]=BARCODE:CATALOGNUMBER [TCOP]=COPYRIGHT [USER]=LICENSE [COMM]=COMMENT [TIT3]=DESCRIPTION [TSSE]=ENCODER [TMED]=MEDIA [TLAN]=LANGUAGE [USLT]=LYRICS ) declare -g FLAC_TAG_LIST="TRACKNUMBER TRACKTOTAL TITLE ALBUM ARTIST COMPOSER ARRANGER CONDUCTOR REMIXER AUTHOR WRITER LYRICIST ALBUMARTIST DATE GENRE COMPILATION LABEL PUBLISHER ISRC BARCODE UPC COPYRIGHT LICENSE COMMENT DESCRIPTION ENCODER MEDIA LANGUAGE LYRICS" declare -g FLAC_TAG_COND=$(echo '('${FLAC_TAG_LIST}')' | sed -e 's/ /|/g') declare -g NODEF_TAG_LIST="TRACKNUMBER TITLE ISRC LYRICS" declare -g NODEF_TAG_COND=$(echo '('${NODEF_TAG_LIST}')' | sed -e 's/ /|/g') #----------------------------------------------------------------------------------------------------------------------------------- # Fix Name #----------------------------------------------------------------------------------------------------------------------------------- # Standardise Name #----------------------------------------------------------------------------------------------------------------------------------- # $1: Input Name # $2: SED Substitute Patern # Out: Output Name #----------------------------------------------------------------------------------------------------------------------------------- function fix_name { input_name="$1" ssp="$2" if [[ "${ssp}" != "" ]] then sse="s/${ssp}" else sse="s/^//" fi echo ${input_name} | sed -e 's/[ ,!/?:]/_/g' \ -e "s/['’´]/_/g" \ -e 's/\[/-/g' -e 's/\]//g' \ -e 's/(/-/g' -e 's/)//g' \ -e 's/__/_/g' -e 's/_\././g' -e 's/_-/-/g' -e 's/--/-/g' -e 's/-_/-/g' \ -e 's/à/a/g' -e 's/á/a/g' -e 's/ä/a/g' -e 's/â/a/g' -e 's/ã/a/g' -e 's/é/e/g' -e 's/è/e/g' -e 's/ê/e/g' -e 's/ë/e/g' -e 's/ï/i/g' -e 's/î/i/g' -e 's/í/i/g' -e 's/ô/o/g' -e 's/ö/o/g' -e 's/ó/o/g' -e 's/ù/u/g' -e 's/ü/u/g' -e 's/û/u/g' -e 's/ú/u/g' -e 's/ç/c/g' -e 's/ñ/n/g' \ -e 's/À/a/g' -e 's/Á/a/g' -e 's/Ä/a/g' -e 's/Â/a/g' -e 's/Ã/a/g' -e 's/É/e/g' -e 's/È/e/g' -e 's/Ê/e/g' -e 's/Ë/e/g' -e 's/Ï/i/g' -e 's/Î/i/g' -e 's/Í/i/g' -e 's/Ô/o/g' -e 's/Ö/o/g' -e 's/Ó/o/g' -e 's/Ù/u/g' -e 's/Ü/u/g' -e 's/Û/u/g' -e 's/Ú/u/g' -e 's/Ç/c/g' -e 's/Ñ/n/g' \ -e 's/¿//g' \ -e 's/[+&]/and/g' -e 's/"/_inch/g' \ -e 's/#//g' \ -e 's/^[_-]*//' \ -e 's/[_-]*$//' \ -e "${sse}" | tr '[:upper:]' '[:lower:]' } #----------------------------------------------------------------------------------------------------------------------------------- # Fix File Name #----------------------------------------------------------------------------------------------------------------------------------- # Standardise File Name #----------------------------------------------------------------------------------------------------------------------------------- # $1: Input File Name # $2: SED Substitute Patern # Out: Output File Name #----------------------------------------------------------------------------------------------------------------------------------- function fix_file_name { input_file_name="$1" ssp="$2" fix_name "${input_file_name}" "${ssp}" | sed -e 's/\(^[0-9]*\)./\1-/' } # TagTab Alloc #----------------------------------------------------------------------------------------------------------------------------------- function tagtab_alloc { unset tagtab declare -Ag tagtab declare -g taglist taglist=":" } # Tag Exist #----------------------------------------------------------------------------------------------------------------------------------- function tag_exist { tag="$1" echo ${taglist} | grep -e ":${tag}:" } # TagTab Read #----------------------------------------------------------------------------------------------------------------------------------- function tagtab_read { track_id="$1" track_file="$2" mode="$3" tmp_file=$(mktemp) case "${track_file}" in *.flac) metaflac --export-tags-to=- "${track_file}" | sed -e 's/^[^=]*=/\U&\E/' > ${tmp_file} while read line do tag="${line/=*}" if [[ ${line/*=*/=} == "=" ]] && [[ "${tag}" != "" ]] then value="$(tag_read "${track_file}" "${tag}")" if [[ "$(tag_exist ${tag})" == "" ]] then taglist="${taglist}${tag}:" fi if [[ ( "${mode}" != "FACTORING" ) || ( "${tag}" =~ ^${NODEF_TAG_COND}$ ) || ( "${tagtab["0,${tag}"]}" != "" ) ]] then if [[ "${tagtab["0,${tag}"]}" != "${value}" ]] then tagtab["${track_id},${tag}"]="$value" fi else tagtab["0,${tag}"]="${value}" fi fi done < ${tmp_file} ;; *.mp3) mid3v2 --list "${track_file}" | tail -n +2 | sed -e 's/^[^=]*=/\U&\E/' > ${tmp_file} while read line do mp3tag="${line/=*}" value="$(tag_read "${track_file}" "${mp3tag}")" if [[ "${line/*=*/=}" == "=" ]] && [[ "${mp3tag}" != "" ]] && [[ "${MP3_2_FLAC_TAB["${mp3tag}"]}" != "" ]] then tag="${MP3_2_FLAC_TAB["${mp3tag}"]}" if [[ "$(tag_exist ${tag})" == "" ]] then taglist="${taglist}${tag}:" fi if [[ ( "${mode}" != "FACTORING" ) || ( "${tag}" =~ ^${NODEF_TAG_COND}$ ) || ( "${tagtab["0,${tag}"]}" != "" ) ]] then if [[ "${tagtab["0,${tag}"]}" != "${value}" ]] then tagtab["${track_id},${tag}"]="$value" fi else tagtab["0,${tag}"]="${value}" fi fi done < ${tmp_file} ;; *) err_echo "tagtab_read: Unknown file format: ${track_file}" ;; esac if [[ ${taglist} == ":" ]] then taglist="" fi \rm -f ${tmp_file} } # TagTab Load #----------------------------------------------------------------------------------------------------------------------------------- function tagtab_load { metadata_file="$1" mode="$2" track_id=0 eoa="false" while read -r line && [[ "${eoa}" != "true" ]] do tag="${line/=*}" value="${line/${tag}=}" case "${tag}" in @${FLAC_TAG_COND}) tagtab["${track_id},${tag}"]="$value" ;; DEF) ;; SOT) track_id=$((${track_id} + 1)) ;; EOT) ;; EOA) eoa="true" ;; *) err_echo "tagtab_load: Unkwown tag: '${tag}'" ;; esac done < "${metadata_file}" } # TagTab Dump #----------------------------------------------------------------------------------------------------------------------------------- function tagtab_dump { track_id="$1" dump_mode="$2" case "${dump_mode}" in "STANDARD") dump_list="${FLAC_TAG_LIST}" ;; "PASSTHROUGH") dump_list="${taglist//:/ }" ;; "CUSTOM") dump_list="$3" ;; esac for tag in ${dump_list} do if [[ "${tagtab["${track_id},${tag}"]}" != "" ]] then echo "${tag}=${tagtab["${track_id},${tag}"]}" fi done } # Tag Get #----------------------------------------------------------------------------------------------------------------------------------- function tag_get { declare -n return=$1 track_id=$2 tag=$3 return="${tagtab["${track_id},${tag}"]}" if [[ "${return}" == "" ]] then return="${tagtab["0,${tag}"]}" if [[ "${return}" == "" ]] && [[ "${tag}" == "TRACKNUMBER" ]] then return="$(printf "%02d" ${track_id})" fi fi } # Tag Save #----------------------------------------------------------------------------------------------------------------------------------- function tag_save { target_file=$1 track_id=$2 mode=$3 if [[ "${mode}" == "CLEAN" ]] then tag_all_delete "${target_file}" "" fi for tag in ${FLAC_TAG_LIST} do # if [[ "${tagtab["${track_id},${tag}"]}" == "" ]] # then # value="${tagtab["0,${tag}"]}" # # if [[ "${value}" == "" ]] && [[ "${tag}" == "TRACKNUMBER" ]] # then # value="$(printf "%02d" ${track_id})" # fi # else # value="${tagtab["${track_id},${tag}"]}" # fi tag_get value "${track_id}" "${tag}" tag_delete "${target_file}" "${tag}" if [[ "${value}" != "" ]] then tag_write "${target_file}" "${tag}" "${value}" fi done } # Tag Read #----------------------------------------------------------------------------------------------------------------------------------- function tag_read { track_file="$1" tag="$2" case "${track_file}" in *.flac) metaflac --show-tag="${tag}" "${track_file}" | sed -e 's/^[^=]*=//' -e ':a' -e 'N' -e '$!ba' -e 's/\n/\\n/g' ;; *.mp3) # mid3v2 --list "${track_file}" | tail -n +2 | sed -e 's/^[^=]*=/\U&\E/' | grep -e "^${FLAC_2_MP3_TAB["${tag}"]}" | sed -e 's/^[^=]*=//' -e ':a' -e 'N' -e '$!ba' -e 's/\n/\\n/g' mid3v2 --list "${track_file}" | tail -n +2 | grep -e "^${tag}" | sed -e 's/^[^=]*=//' -e ':a' -e 'N' -e '$!ba' -e 's/\n/\\n/g' ;; *) err_echo "tag_read: Unknown file format: ${track_file}" ;; esac } # Tag Write #----------------------------------------------------------------------------------------------------------------------------------- function tag_write { track_file="$1" tag="$2" value="$3" value="$(echo -e "$(escape_str "${value}")")" case "${track_file}" in *.flac) cmd="metaflac --set-tag=\"${tag}=${value}\" \"${track_file}\"" exec_cmd "${cmd}" ;; *.mp3) cmd="mid3v2 --\"${FLAC_2_MP3_TAB["${tag}"]}\" \"${value}\" \"${track_file}\"" exec_cmd "${cmd}" ;; *) err_echo "tag_write: Unknown file format: ${track_file}" ;; esac } # Tag Delete #----------------------------------------------------------------------------------------------------------------------------------- function tag_delete { track_file="$1" tag="$2" case "${track_file}" in *.flac) cmd="metaflac --remove-tag=\"${tag}\" \"${track_file}\"" exec_cmd "${cmd}" ;; *.mp3) cmd="mid3v2 --delete-frames=\"${FLAC_2_MP3_TAB["${tag}"]}\" \"${track_file}\"" exec_cmd "${cmd}" ;; *) err_echo "tag_delete: Unknown file format: ${track_file}" ;; esac } # Tag All Delete #----------------------------------------------------------------------------------------------------------------------------------- function tag_all_delete { track_file="$1" header_type="$2" if [[ "${header_type}" == "" ]] then case "${track_file##*.}" in flac) header_type="ogg" ;; mp3) header_type="id3" ;; esac fi if [[ ( "${header_type}" == "ogg") && ( "${track_file##*.}" == "mp3") ]] then err_echo "tag_all_delete: Can't remove ogg tags from mp3 file!" else case "${header_type}" in ogg) cmd="metaflac --remove-all-tags \"${track_file}\"" exec_cmd "${cmd}" ;; id3) cmd="mid3v2 --delete-all \"${track_file}\"" exec_cmd "${cmd}" ;; *) err_echo "tag_all_delete: Unknown header type: ${header_type}" ;; esac fi } # Album Type Get #----------------------------------------------------------------------------------------------------------------------------------- function album_type_get { track_file="$1" for type in various_artists tribute original_soundtrack child do if [[ ${track_file} == */${type}/* ]] then echo "${type}" fi done }