fant0men

FLAC tag transfer (bash script)

Jun 1st, 2018
128
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/bin/bash
  2. # This script transfers the tags from one directory of FLAC files
  3. # to another. The only criterion is that the source and destination
  4. # directories contain the same number of FLAC files. The script will
  5. # match the tracks from each directory automatically. If there
  6. # are no TRACKNUMBER tags, it will ask the user to match the tracks
  7. # manually, by selecting them from a menu.
  8.  
  9.  
  10. #### Creates a function that will be called if something goes wrong
  11. #### when running the script. This function prints basic
  12. #### usage instructions and then exits.
  13. function usage {
  14.     echo -e "\nUsage: $(basename $0) [source dir] [destination dir]\n"
  15.     exit
  16. }
  17.  
  18. #### If 'metaflac' is not installed, exit.
  19. type -P metaflac &> /dev/null || { echo -e "\nThis script requires metaflac.\n"; exit; }
  20.  
  21. #### If no arguments were given to the script,
  22. #### or any of the arguments are NOT directories, exit.
  23. if [[ ! $1 || ! -d $1 || ! $2 || ! -d $2 ]]; then
  24.     usage
  25. fi
  26.  
  27. dir1="$1"
  28. dir2="$2"
  29.  
  30. #### Makes a backup of $IFS to $IFS_backup.
  31. IFS_backup="${IFS}"
  32.  
  33.  
  34. #### Creates an alias, which will be used by the tag_transfer function,
  35. #### to read the tags of the FLAC file given as argument.
  36. alias gettags='metaflac --export-tags-to=-'
  37.  
  38. #### Creates an alias, which will be used by the tag_transfer function,
  39. #### to set the tags of the FLAC file given as argument.
  40. alias settags='metaflac --remove-all-tags --import-tags-from=-'
  41.  
  42. #### Prints the aliases defined within the script.
  43. #### Makes the aliases usable within the script.
  44. alias
  45. shopt -s expand_aliases
  46.  
  47.  
  48. #### Creates a function that loops through the tags (contained in the
  49. #### $tags.. (1 & 2) arrays, and looks for the TRACKNUMBER tag.
  50. function tracknumber {
  51.  
  52.     local n
  53.     local n1=$1
  54.     local n2=$2
  55.  
  56.     #### Creates a local variable that will be used to count the
  57.     #### iteration of the loop below.
  58.     local -i c=0
  59.  
  60.     #### Loops through the $tags.. (1 & 2) arrays, and looks for a
  61.     #### TRACKNUMBER tag.
  62.     for tags_n in $n1 $n2; do
  63.  
  64.         #### Increase the $c variable by 1, to indicate the iteration
  65.         #### number of the loop.
  66.         let c++
  67.  
  68.         #### If this is the second iteration of the loop, and we
  69.         #### already know that the destination file doesn't contain
  70.         #### any tags, skip to the end of this function.
  71.         if [[ $c -eq 2 && $n2 -eq 0 ]]; then
  72.             return
  73.         fi
  74.  
  75.         for (( n = 0; n < tags_n; n++ )); do
  76.  
  77.             #### Sets the $tag variable, by reading the tags of
  78.             #### specified file ($c). Reads the array
  79.             #### element specified by $n.
  80.             tagref="tags${c}[${n}]"
  81.             tag="${!tagref}"
  82.             #### If there is a track number, store the number
  83.             #### in the $track.. (1 || 2) variable.
  84.             if [[ $tag =~ ^TRACKNUMBER= || $tag =~ ^tracknumber= ]]; then
  85.                 tmp=$(echo "$tag" | cut -d'=' -f2)
  86.                 local "track${c}=${tmp}"
  87.                 break
  88.             fi
  89.         done
  90.     done
  91.  
  92.     #### Echoes the two track numbers, to be captured when the function
  93.     #### is run.
  94.     echo "$track1" "$track2"
  95. }
  96.  
  97. #### Creates a function that will transfer the tags from source file to
  98. #### destination file.
  99. function tag_transfer {
  100.  
  101.     local n
  102.  
  103.     #### Sets the $fn variable to the file number, given as argument to
  104.     #### the function when run.
  105.     #### Sets $f1 and $f2 to source and destination file respectively.
  106.     local fn=$1
  107.     local f1="${files1[${fn}]}"
  108.     local f2="${files2[${fn}]}"
  109.  
  110.     #### Sets the Internal Field Separator variable to only
  111.     #### consider newlines.
  112.     IFS=$'\n'
  113.  
  114.     #### Gets the tags for source and destination file, and stores
  115.     #### the tags in an array corresponding to the file.
  116.     local tags1=( $(gettags "$f1" 2>/dev/null) )
  117.     local tags2=( $(gettags "$f2" 2>/dev/null) )
  118.  
  119.     #### Resets $IFS to its default value, as kept by $IFS_backup.
  120.     IFS="${IFS_backup}"
  121.  
  122.     #### If the FLAC files in the source directory are untagged, exit.
  123.     #if [[ -z $tags1 ]]; then
  124.     #   echo -e "\nThe FLAC files in source directory are UNTAGGED!\n"
  125.     #   exit
  126.     #fi
  127.  
  128.     #### Stores the number of elements in the $tags.. (1 & 2) arrays
  129.     #### in its own variable.
  130.     local -i tags_n_1=${#tags1[@]}
  131.     local -i tags_n_2=${#tags2[@]}
  132.  
  133.     #### Runs the tracknumber function to see if the two files
  134.     #### have a TRACKNUMBER tag.
  135.     declare -a trackn=( $(tracknumber $tags_n_1 $tags_n_2) )
  136.     declare track1="${trackn[0]}"
  137.     declare track2="${trackn[1]}"
  138.     unset -v trackn
  139.  
  140.     echo "$f1 , $track1"
  141.     echo "$f2 , $track2"
  142.  
  143.     #### If the destination file has a TRACKNUMBER tag,
  144.     #### compare that with the source file, until there's a match.
  145.     if [[ $track2 ]]; then
  146.         if [[ $track1 -eq $track2 ]]; then
  147.             for (( n = 0; n < tags_n_1; n++ )); do echo "${tags1[${n}]}"; done | settags "$f2"
  148.             echo "auto"
  149.             unset -v files2[${fn}]
  150.         else
  151.             #### Because the tracks are different, loop through
  152.             #### the $files2 array, until there either is a matching
  153.             #### track number, or the user has to select the correct
  154.             #### track from a list.
  155.             for (( n = 0; n < files_n_2; n++ )); do
  156.  
  157.                 local f2_t="${files2[${n}]}"
  158.  
  159.                 #### Sets the Internal Field Separator variable to only
  160.                 #### consider newlines.
  161.                 IFS=$'\n'
  162.  
  163.                 local tags2_t=( $(gettags "$f2_t" 2>/dev/null) )
  164.  
  165.                 #### Resets $IFS to its default value, as kept by $IFS_backup.
  166.                 IFS="${IFS_backup}"
  167.  
  168.                 local -i tags_n_2_t=${#tags2_t[@]}
  169.  
  170.                 #### Runs the tracknumber function to see if the two files
  171.                 #### have a TRACKNUMBER tag.
  172.                 declare -a trackn=( $(tracknumber $tags_n_1 $tags_n_2_t) )
  173.                 declare track1="${trackn[0]}"
  174.                 declare track2="${trackn[1]}"
  175.                 unset -v trackn
  176.  
  177.                 #### Since we've run the tracknumber function again,
  178.                 #### test once more if the TRACKNUMBER tag matches
  179.                 #### between the source and destination file.
  180.                 if [[ $track1 -eq $track2 ]]; then
  181.                     for (( n = 0; n < tags_n_1; n++ )); do echo "${tags1[${n}]}"; done | settags "$f2"
  182.                     unset -v files2[${fn}]
  183.                 fi
  184.             done
  185.            
  186.         fi
  187.     elif [[ -z $track2 ]]; then
  188.         #### Since we weren't able to match the TRACKNUMBER tags
  189.         #### between the source and destination file, now we will
  190.         #### ask the user to manually select the matching track from
  191.         #### a menu.
  192.         echo -e "\nChoose the track that matches '${f1}'!\n"
  193.  
  194.         #### Sets the Internal Field Separator variable to only
  195.         #### consider newlines.
  196.         IFS=$'\n'
  197.  
  198.         select f in ${files2[@]}; do
  199.             el_num=${files2_h[${f}]}
  200.  
  201.             echo "FILE2: $f ELEMENT#: $el_num ARRAY#: ${#files2[@]}"
  202.  
  203.             for (( n = 0; n < tags_n_1; n++ )); do echo "${tags1[${n}]}"; done | settags "$f"
  204.  
  205.             unset -v files2[${el_num}]
  206.             return
  207.         done
  208.  
  209.         #### Resets $IFS to its default value, as kept by $IFS_backup.
  210.         IFS="${IFS_backup}"
  211.     fi
  212.  
  213. }
  214.  
  215. #### Sets the Internal Field Separator variable to only
  216. #### consider newlines.
  217. IFS=$'\n'
  218.  
  219. #### Make two arrays, containing the FLAC files in $dir1 and $dir2.
  220. files1=( $(ls "${dir1}"/*.flac 2>/dev/null) )
  221. files2=( $(ls "${dir2}"/*.flac 2>/dev/null) )
  222.  
  223. #### Resets $IFS to its default value, as kept by $IFS_backup.
  224. IFS="${IFS_backup}"
  225.  
  226. #### If either of the directories don't contain any FLAC files, exit.
  227. if [[ -z $files1 || -z $files2 ]]; then
  228.     usage
  229. fi
  230.  
  231. #### Store the number of elements in each array in its own variable.
  232. declare -i files_n_1=${#files1[@]}
  233. declare -i files_n_2=${#files2[@]}
  234.  
  235. echo "* $files_n_1"
  236. echo "* $files_n_2"
  237.  
  238. #### Copy the arrays into hashes, where each key corresponds to
  239. #### the filename, and the value is the element number in the array.
  240. for a in {1..2}; do
  241.     #### Creates a reference, which points to the $files${a} array.
  242.     ref="files${a}[@]"
  243.  
  244.     #### Sets the Internal Field Separator variable to only
  245.     #### consider newlines.
  246.     IFS=$'\n'
  247.  
  248.     #### Creates a copy of the $files${a} array.
  249.     refa=( ${!ref} )
  250.  
  251.     #### Resets $IFS to its default value, as kept by $IFS_backup.
  252.     IFS="${IFS_backup}"
  253.    
  254.     echo "${refa[@]}"
  255.     #### Creates a variable, which contains the number of elements
  256.     #### in the $files${a} array.
  257.     refn=${#refa[@]}
  258.     echo "*** $refn"
  259.     for (( n = 0; n < refn; n++ )); do
  260.  
  261.         #### Creates a variable containing the name of the hash
  262.         #### to be created.
  263.         hname="files${a}_h"
  264.         #### Creates a variable containing the filename.
  265.         fname="files${a}[${n}]"
  266.         echo "fname: [${!fname}]=${n}"
  267.         echo "hname: $hname"
  268.         #### Declares a hash with the specified name, and adds
  269.         #### a key (filename), which points to the array element number.
  270.         declare -A "${hname}+=( [${!fname}]=${n} )"
  271.        
  272.     done
  273.     unset -v ref refa refn
  274. done
  275.  
  276. #### Compare the number of FLAC files in source and destination dir.
  277. #### Exit if the number is different.
  278. if [[ $files_n_1 -ne $files_n_2 ]]; then
  279.     echo -e "\nThe number of FLAC files in the source and destination directory are different!\n"
  280.     exit
  281. fi
  282.  
  283. #### Loop through the $files1 array, and run the tag_transfer function,
  284. #### which in turn calls other functions as needed.
  285. for (( n = 0; n < files_n_1; n++ )); do
  286.  
  287.     echo "FILE: ${files1[${n}]}"
  288.     tag_transfer $n
  289.  
  290. done
RAW Paste Data