Converting FLAC file collection to ALAC in another directory with shell script

800 Views Asked by At

I have searched many forums and websites to create an ALAC collection from my FLAC collection with the same directory structure with no success. Therefore I coded my own shell script and decided to share here so others can use or improve on it.

Problems I wanted to solve:

  1. Full automation of conversion. I did not want to go and run scripts in each and every directory.
  2. Recursive file search
  3. Moving all the structure from one location to another by converting flac to alac and copying the artwork. nothing else.
  4. I did not want flac and alac files in the same directory.(which the below script I believe can do that)

Here is how the script turned out. It works for me, I hope it does for you as well. I am using Linux Mint and bash shell.

2014-12-08 - Made some changes and now it is working fine. Before it was creating multiple copies.

Usage is: ./FLACtoALAC.sh /sourcedirectory /targetdirectory

Here are some explanations:

Source: /a/b/c/d/e/   <- e has flac
              /g/f/k  <- k has artwork
                  /l  <- l has mp3

Target: /z/u/v/g/f

when the command is run : ./FLACtoALAC.sh /a/b/ /z/u/

I want the structure look like:
/z/u/v/g/f     <- f was already there
    /c/d/e/    <- e had flac, so created with the tree following source (/a/b) 
    /c/g/f/k   <- k had artwork, so created with the tree following source (/a/b) 

not created l  <- l did not have any of the png,jpg or flac files.

I do not want to create any directory that does not contain png, jpg or flac,
unless it is a parent to one of such those directories.    

Now the updated code:

#!/bin/bash
if [[ $1 ]]
                then
                if [[ ${1:0:1} = / || ${1:0:1} = ~ ]]
                                then Source_Dir=$1
                elif [[ ${1:0:1} = . ]]          
                                then Source_Dir=`pwd`
                else Source_Dir=`pwd`'/'$1
                fi
else Source_Dir=`pwd`'/'
fi
if [[ $2 ]]
                then
                if [[ ${2:0:1} = / || ${2:0:1} = ~ ]]
                                then Target_Dir=$2
                elif [[ ${2:0:1} = . ]]          
                                then Target_Dir=`pwd`
                else Target_Dir=`pwd`'/'$2
                fi
else Target_Dir=`pwd`'/'
fi
echo "Source Directory : "$Source_Dir
echo "Target Directory : "$Target_Dir
typeset -i Source_Dir_Depth
Source_Dir_Depth=`echo $Source_Dir | grep -oi "\/" | wc -l`
typeset -i Target_Dir_Depth
Target_Dir_Depth=`echo $Target_Dir | grep -oi "\/" | wc -l`
echo "Depth of the Source Directory: "$Source_Dir_Depth
echo "Depth of the Target Directory: "$Target_Dir_Depth

echo "Let's check if the Target Directory exists, if not we will create"
typeset -i Number_of_depth_checks
Number_of_depth_checks=$Target_Dir_Depth+1
for depth in `seq 2 $Number_of_depth_checks`
    do
    Target_Directory_Tree=`echo ${Target_Dir} | cut -d'/' -f-${depth}`
    if [[ -d "$Target_Directory_Tree" ]]
    then
        echo "This directory exists ("$Target_Directory_Tree"), moving on"
    else 
        Create_Directory=`echo ${Target_Dir} | cut -d'/' -f-${depth}`
        echo "Creating the directory/subdirectory $Create_Directory"
        mkdir -pv "$Create_Directory"
    fi
done

Directory_List=`find "${Source_Dir}" -type d -exec sh -c 'ls -tr -1 "{}" | sort | egrep -iq "*.(jpg|png|flac)$"' ';' -print`
oIFS=$IFS
IFS=$'\n'
for directories in $Directory_List
    do
    echo "Directories coming from the source : $directories"
    typeset -i directories_depth
    directories_depth=`echo $directories | grep -oi "\/" | wc -l`
    echo "Number of sub-directories to be checked: $Source_Dir_Depth"
    typeset -i number_of_directories_depth
    number_of_directories_depth=$directories_depth+1
    for depth in `seq 2 $number_of_directories_depth`
        do
        Source_Tree=`echo ${Source_Dir} | cut -d'/' -f-${depth}`
        Subdirectory_Tree=`echo ${directories} | cut -d'/' -f-${depth}`
        Subdirectory_Remaining_Tree=`echo ${directories} | cut -d'/' -f${depth}-`
        echo "source tree : $Source_Tree"
        echo "source tree : $Subdirectory_Tree"
        if [[  $depth -le $Source_Dir_Depth && $Source_Tree = $Subdirectory_Tree ]]
        then
            echo "Common Directory, skipping ($Subdirectory_Tree)"
            continue
        else
            export Targetecho=$(echo $Target_Dir | sed -e 's/\r//g')
            export Destination_Directory=${Targetecho}${Subdirectory_Remaining_Tree}
            echo "Destination directory is : $Destination_Directory"
            export Sub_directories_depth=`echo $Destination_Directory | grep -oi "\/" | wc -l`
            echo "Total destination depth : $Sub_directories_depth"
            echo "Now we are checking target directory structure"
        fi
        break
        done
    echo "Gettin into the new loop to verify/create target structure"
    typeset -i number_of_Sub_directories_depth
    number_of_Sub_directories_depth=$Sub_directories_depth+1
    for subdepth in `seq 2 $number_of_Sub_directories_depth`
        do
        Target_Subdirectory_Tree=`echo ${Destination_Directory} | cut -d'/' -f-${subdepth}`
        if [[ $subdepth < $number_of_Sub_directories_depth && -d "$Target_Subdirectory_Tree" ]]
        then 
            echo "Directory already exists in the destination ($Target_Subdirectory_Tree)"
        elif [[ $subdepth < $number_of_Sub_directories_depth && ! -d "$Target_Subdirectory_Tree" ]]
        then
            echo "Creating the path in the destination ($Target_Subdirectory_Tree)"
            mkdir -pv "$Target_Subdirectory_Tree"
        elif [[ $subdepth -eq $number_of_Sub_directories_depth ]]
        then
            if [[  ! -d "$Destination_Directory" ]]
            then            
            echo "Creating Directory: $Destination_Directory"
            mkdir -pv "$Destination_Directory"
            fi
            echo "Directory already exists in the destination ($Destination_Directory)"
#Flac file processing starts here once the directory is found
            Flac_File_List=`(shopt -s nocaseglob ; ls -tr "${directories}"/*.flac | sort)`
            echo "List of files in $directories :"
            echo $Flac_File_List
            for flac_files in $Flac_File_List
            do
                echo "files : $flac_files"
                typeset -i flac_file_depth
                flac_file_depth=`echo $flac_files | grep -oi "\/" | wc -l`
                flac_file_depth=$flac_file_depth+1
                echo "flac_file_depth : $flac_file_depth"
                Flac_File_Name=`echo ${flac_files} | cut -d'/' -f${flac_file_depth}`
                echo "Flac_File Name : $Flac_File_Name"
                Destination_File=${Destination_Directory}'/'${Flac_File_Name}
                echo "will convert $Flac_File_Name from $flac_files to $Destination_File"
                yes | ffmpeg  -i "$flac_files" -vf "crop=((in_w/2)*2):((in_h/2)*2)" -c:a alac "${Destination_File%.flac}.m4a"
            done
#Artwork file processing starts here once the directory is found
            Art_File_List=`(shopt -s nocaseglob ; ls -tr "${directories}"/*.{png,jpg} | sort)`
            echo "List of files in $directories :"
            echo $Art_File_List
            for art_files in $Art_File_List
            do
                echo "files : $art_files"
                typeset -i art_file_depth
                art_file_depth=`echo $art_files | grep -oi "\/" | wc -l`
                art_file_depth=$art_file_depth+1
                echo "file_depth : $art_file_depth"
                Art_File_Name=`echo ${art_files} | cut -d'/' -f${art_file_depth}`
                echo "File Name : $Art_File_Name"
                Destination_File=${Destination_Directory}'/'${Art_File_Name}
                echo "will copy $Art_File_Name from $art_files to $Destination_File"
                cp "$art_files" "$Destination_File"
            done
        else
            echo "did nothing!!!"
        fi
        done
done
IFS=$oIFS

feel free to change, improve, distribute. Caglar

1

There are 1 best solutions below

4
On

Try this out:

#!/bin/bash

src_dir="in"
dst_dir="out"

find ${src_dir} -type f -print0|while IFS= read -r -d '' src_file; do
        dst_file=${src_file/$src_dir/$dst_dir}

        echo "src_file=${src_file} dst_file=${dst_file}"
        mkdir -pv `dirname $dst_file`
        # use above variables and run convert command with it here
done

To test how it works:

mkdir in out
cd in
mkdir 1 2 3
find . -type d -exec touch {}/foo {}/bar {}/baz \;
cd ..
./run_my_script.sh

Now you only need to attach your convert function/script/command/whatever and improve it to read src_dir and dst_dir from the command line (I would recommend man bash - > getopts)