SHOW:
|
|
- or go back to the newest paste.
1 | #!/bin/bash | |
2 | - | #Dependencies: usual gnu utils, ffmpeg or ffprobe |
2 | + | #Dependencies: ffmpeg or ffprobe, standard gnu utils (including bc) |
3 | # | |
4 | - | ##june 07 2012 edited to remove unwanted \ in regex expression which ###prevented ogg files from being parsed. |
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 | #december 22 2015 edited to add support for relative paths, thanks to Dave. | |
11 | # january 9 2016 removed problemaatic relative paths function. | |
12 | ||
13 | - | \t-r recurses through subdirectories and writes file names with |
13 | + | #also work with locales that use , instead of .for decimal position. |
14 | LC_NUMERIC=C LC_COLLATE=C | |
15 | ||
16 | function usage () | |
17 | { | |
18 | printf "\nUSAGE: | |
19 | \t$(basename "$0") [options] {-o out} <directory>\n | |
20 | OPTIONS: | |
21 | \t-D Don't overwrite existing playlist (default is to replace)\n | |
22 | \t-s Simple m3u8 playlist instead of extended m3u8\n | |
23 | \t-p writes the file names in .m3u with full path\n\ | |
24 | \t (default is simple file name without path)\n | |
25 | \t-d Set the recursion depth (default is 1)\n | |
26 | \t-r Recurses through subdirectories and writes file names with | |
27 | \t full path\n | |
28 | \t-c Format playlist for Sansa Clip+ Internal Memory (implies -p) | |
29 | \t | |
30 | - | ffprobe -show_format "$f">"$DATA" |
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 | - | SEC=$(printf %.2f\\n $(grep -m 1 -i -e "^duration" "$DATA" |\ |
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 | - | while getopts 'o:pr' OPTION |
48 | + | |
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 | #user options | |
71 | while getopts 'CcDHo:prs' OPTION | |
72 | do | |
73 | case $OPTION in | |
74 | o) oflag=1 | |
75 | oval=$OPTARG | |
76 | ;; | |
77 | p) pflag=1 | |
78 | ;; | |
79 | r) rflag=1 | |
80 | pflag=1 | |
81 | ;; | |
82 | R) rflag=1 | |
83 | relpathflag=1 | |
84 | ;; | |
85 | c) cflag=1 | |
86 | ;; | |
87 | C) Cflag=1 | |
88 | pflag=1 | |
89 | ;; | |
90 | H) Hflag=1 | |
91 | pflag=1 | |
92 | ;; | |
93 | s) sflag=1 | |
94 | ;; | |
95 | D) Dflag=1 | |
96 | - | #create/overwrite .m3u and write extm3u's generic 1st line |
96 | + | |
97 | - | printf "#EXTM3U">"$PL" |
97 | + | |
98 | done | |
99 | shift $(($OPTIND-1)) | |
100 | ||
101 | - | mp3\|ogg\|oga\|mka\|ac3\)$" |sort |while read f ; do |
101 | + | |
102 | usage | |
103 | exit 1 | |
104 | fi | |
105 | ||
106 | #recurse? | |
107 | if [ $rflag ]; then | |
108 | DEPTH='' | |
109 | else | |
110 | DEPTH="-maxdepth 1" | |
111 | fi | |
112 | ||
113 | for i in "$@" ; do | |
114 | #ensure .m3u name is not dot file if script run within target directory. | |
115 | PN=$(basename "$(readlink -f "$i")").m3u | |
116 | ||
117 | #some simple error checking | |
118 | if [ ! -d "$i" ]; then | |
119 | usage | |
120 | exit | |
121 | fi | |
122 | ||
123 | #set output destination. Output dir exists and writeable? | |
124 | if [ $oflag ]; then | |
125 | if [ ! -d "$oval" -o ! -w "$oval" ]; then | |
126 | usage | |
127 | exit | |
128 | fi | |
129 | PL=$oval/"$PN" | |
130 | else | |
131 | PL="$i"/"$PN" | |
132 | fi | |
133 | ||
134 | #playlist exists? overwrite? | |
135 | if [ $Dflag ] && [ -f "$PL" ]; then | |
136 | printf "\n$PL already exists.\n" | |
137 | continue | |
138 | else | |
139 | > "$PL" | |
140 | fi | |
141 | ||
142 | if [ ! $sflag ]; then | |
143 | #write mandatory extm3u8 header | |
144 | printf "#EXTM3U">"$PL" | |
145 | WRITE=xlist | |
146 | else | |
147 | WRITE=slist | |
148 | fi | |
149 | ||
150 | #find all audio files to be referenced in .m3u | |
151 | find "$i" $DEPTH -iregex ".*\(flac\|ape\|wv\|wav\|mpc\|aac\|m4a\|wma\|\ | |
152 | mp3\|ogg\|oga\|mka\|ac3\|opus\)$" |sort |while read f ; do | |
153 | ||
154 | #use full path or simple names? | |
155 | if [ $pflag ]; then | |
156 | if [ $cflag ]; then | |
157 | FILENAME=$(readlink -f "$f"|sed 's/\/media\/SANSA\ CLIPP//') | |
158 | elif [ $Cflag ]; then | |
159 | FILENAME=$(readlink -f "$f"|sed 's/media\/LEX/\<microSD1\>/') | |
160 | elif [ $Hflag ]; then | |
161 | FILENAME=$(readlink -f "$f"|sed 's/media\/H[0-9]*.//') | |
162 | else | |
163 | FILENAME=$(readlink -f "$f") | |
164 | fi | |
165 | elif [ $relpathflag ]; then | |
166 | FILENAME=$(readlink -f "$f") | |
167 | FILENAME=$(relpath $PWD $FILENAME) | |
168 | else | |
169 | FILENAME="${f##*/}" | |
170 | fi | |
171 | ||
172 | #where to dump ffprobe's file info | |
173 | DATA="${f%.*}"_data | |
174 | ||
175 | #run getdata silently (ffprobe generates much unwanted text) | |
176 | getdata>/dev/null 2>&1 | |
177 | ||
178 | #write to the playlist, clean up. | |
179 | printf "\n#EXTINF:$SEC,$PERF - $TIT\n$FILENAME">>"$PL" | |
180 | rm "$DATA" | |
181 | done | |
182 | ||
183 | #confirm to user, quit. | |
184 | echo "Wrote "$PL"" | |
185 | done | |
186 | exit 0 |