Advertisement
Guest User

m3u generator with relative paths

a guest
Oct 3rd, 2015
289
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/bin/bash
  2. #Dependencies: ffmpeg or ffprobe, standard gnu utils (including bc)
  3. #
  4. #april 21 2014 edited to also work with locales that
  5. #use , instead of .for decimal position (thanks to Tina Keil).
  6. #march 20 2013 edited to accommodate opus files and to fix error in fetching
  7. #durations, caused by changes in ffprobe.
  8. #june 07 2012 edited to remove unwanted \ in regex expression which
  9. #prevented ogg files from being parsed.
  10.  
  11. #also work with locales that use , instead of .for decimal position.
  12. LC_NUMERIC=C LC_COLLATE=C
  13.  
  14. function usage ()
  15. {
  16.     printf "\nUSAGE:
  17. \t$(basename "$0") [options] {-o out} <directory>\n
  18. OPTIONS:
  19. \t-D  Don't overwrite existing playlist (default is to replace)\n
  20. \t-s  Simple m3u8 playlist instead of extended m3u8\n
  21. \t-p  writes the file names in .m3u with full path\n\
  22. \t    (default is simple file name without path)\n
  23. \t-d  Set the recursion depth (default is 1)\n
  24. \t-r  Recurses through subdirectories and writes file names with
  25. \t    full path\n
  26. \t-R  Recurses through subdirectories and writes file names with
  27. \t    relative path\n
  28. \t-c  Format playlist for Sansa Clip+ Internal Memory (implies -p)
  29. \t
  30. \t-C  Format playlist for Sansa Clip+ MicroSDHC (implies -p)
  31. \t
  32. \t-H  Format playlist for iriver H10/100/300 series players (implies -p)
  33. \t    You MUST change the MicroSDHC card label and mount point in
  34. \t    the sed expressions to the ACTUAL MicroSDHC card label and
  35. \t    mount point on your OS.  The Clip internal storage and iRiver H
  36. \t    values are quite generic but Clip external storage values could be
  37. \t    anything - user must check this.\n
  38. \t-o <output directory>  (default is target directory)\n
  39. \t$(basename "$0") finds the audio files in the directory, parses
  40. \tthe metadata, then writes an extended .m3u playlist\n
  41. \t$(basename "$0") understands the same files as  your
  42. \tinstalled version of ffmpeg.\n
  43. \tIf $(basename "$0") fails please check output directory exists
  44. \tand is writeable.\n\n"
  45. }
  46.  
  47. #dump file data;grep metadata relevant to extm3u.
  48. #extm3u spec states that stated time MUST be equal to or greater than
  49. #track duration so all times are rounded UP to integer i.e. 38.00 secs
  50. #becomes 38 seconds but 38.10 secs or 38.95 seconds become 39 seconds.
  51. function getdata ()
  52. {
  53. ffprobe -v quiet -show_format -show_streams "$f">"$DATA"
  54.  
  55. PERF=$(grep -m 1 -i tag:artist "$DATA" |\
  56. awk -F "=" '{$1="";$0=substr($0,2)}1' |cleantag)
  57. TIT=$(grep -m 1 -i tag:title "$DATA" |\
  58. awk -F "=" '{$1="";$0=substr($0,2)}1' |cleantag)
  59. SEC=$(printf %.2f\\n $(grep -m 1 -i -e "^duration=" "$DATA" |\
  60. awk -F "=" '{print $2}'))
  61. SEC=$(printf %0.f\\n $(echo "$SEC+0.5"|bc))
  62. }
  63.  
  64. #remove problematic characters from metadata.
  65. function cleantag ()
  66. {
  67.     sed 's/[;]/-/g;s/[/]/-/g;s/[<]/-/g;s/[>]/-/g;s/[:]/-/g;s/[|]/-/g'
  68. }
  69.  
  70. function relpath ()
  71. {
  72.         source=$1
  73.         target=$2
  74.         common_part=$source
  75.         back=
  76.         while [ "${target#$common_part}" = "${target}" ]; do
  77.                 common_part=$(dirname $common_part)
  78.                 back="../${back}"
  79.         done
  80.         echo ${back}${target#$common_part/}
  81. }
  82.  
  83. #user options
  84. while getopts 'CcDHo:prRs' OPTION
  85. do
  86.     case $OPTION in
  87.     o)  oflag=1
  88.         oval=$OPTARG
  89.         ;;
  90.     p)  pflag=1
  91.         ;;
  92.     r)  rflag=1
  93.         pflag=1
  94.         ;;
  95.     R)  rflag=1
  96.         relpathflag=1
  97.         ;;
  98.     c)  cflag=1
  99.         ;;
  100.     C)  Cflag=1
  101.         pflag=1
  102.         ;;
  103.     H)  Hflag=1
  104.         pflag=1
  105.         ;;
  106.     s)  sflag=1
  107.         ;;
  108.     D)  Dflag=1
  109.         ;;
  110.     esac
  111. done
  112. shift $(($OPTIND-1))
  113.  
  114. if [ $# -lt 1 ]; then
  115.     usage
  116.     exit 1
  117. fi
  118.  
  119. #recurse?
  120. if [ $rflag ]; then
  121.     DEPTH=''
  122. else
  123.     DEPTH="-maxdepth 1"
  124. fi
  125.  
  126. for i in "$@" ; do
  127. #ensure .m3u name is not dot file if script run within target directory.
  128. PN=$(basename "$(readlink -f "$i")").m3u
  129.  
  130. #some simple error checking
  131. if [ ! -d "$i" ]; then
  132.     usage
  133.     exit
  134. fi
  135.  
  136. #set output destination. Output dir exists and writeable?
  137. if [ $oflag ]; then
  138.     if [ ! -d "$oval" -o ! -w "$oval" ]; then
  139.     usage
  140.     exit
  141.     fi
  142.     PL=$oval/"$PN"
  143.     else
  144.     PL="$i"/"$PN"
  145. fi
  146.  
  147. #playlist exists? overwrite?
  148. if [ $Dflag ] && [ -f "$PL" ]; then
  149.     printf "\n$PL already exists.\n"
  150.     continue
  151.     else
  152.     > "$PL"
  153. fi
  154.  
  155. if [ ! $sflag ]; then
  156.     #write mandatory extm3u8 header
  157.     printf "#EXTM3U">"$PL"
  158.     WRITE=xlist
  159.     else
  160.     WRITE=slist
  161. fi
  162.  
  163. #find all audio files to be referenced in .m3u
  164. find "$i" $DEPTH -iregex ".*\(flac\|ape\|wv\|wav\|mpc\|aac\|m4a\|wma\|\
  165. mp3\|ogg\|oga\|mka\|ac3\|opus\)$" |sort |while read f ; do
  166.  
  167. #use full path or simple names?
  168. if [ $pflag ]; then
  169.     if [ $cflag ]; then
  170.         FILENAME=$(readlink -f "$f"|sed 's/\/media\/SANSA\ CLIPP//')
  171.     elif [ $Cflag ]; then
  172.         FILENAME=$(readlink -f "$f"|sed 's/media\/LEX/\<microSD1\>/')
  173.     elif [ $Hflag ]; then
  174.         FILENAME=$(readlink -f "$f"|sed 's/media\/H[0-9]*.//')
  175.     else
  176.         FILENAME=$(readlink -f "$f")
  177.     fi
  178. elif [ $relpathflag ]; then
  179.     FILENAME=$(readlink -f "$f")
  180.     FILENAME=$(relpath $PWD $FILENAME)
  181. else
  182.     FILENAME="${f##*/}"
  183. fi
  184.  
  185. #where to dump ffprobe's file info
  186. DATA="${f%.*}"_data
  187.  
  188. #run getdata silently (ffprobe generates much unwanted text)
  189. getdata>/dev/null 2>&1
  190.  
  191. #write to the playlist, clean up.
  192. printf "\n#EXTINF:$SEC,$PERF - $TIT\n$FILENAME">>"$PL"
  193. rm "$DATA"
  194. done
  195.  
  196. #confirm to user, quit.
  197. echo "Wrote "$PL""
  198. done
  199. exit 0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement