#!/bin/bash #!/bin/bash # config structures declare -A \ destinationchannels \ destinationfat32compat \ destinationcopymime \ destinationformat \ destinationfrequency \ destinationid \ destinationloss \ destinationmaxbps \ destinationnormalize \ destinationpath \ destinationquality \ destinationrename \ destinationnoresample \ destinationrenamepath \ destinationskipmime \ || { echo "Check your Bash version. You need >= 4.0" >&2 exit $EBASHVERS } declare -r \ DOCDIR=./doc \ LIBDIR=./lib \ SHAREDIR=./share declare -r \ exampleconf=$DOCDIR/example.cfg \ schema=$SHAREDIR/schema.sql \ \ oldIFS="$IFS" cffile="$HOME/.atom/atom.cfg" shopt -s extglob for function in "$LIBDIR"/*/* do source "$function" done args="$@" while [ -n "$1" ] do opt="$1" shift case $opt in '-A') show+=(albumartists) ;; '-l') show+=(albums) ;; '-a') show+=(artists) ;; '-b') show+=(bitrates) ;; '-c') show+=(composers) ;; '-C') show+=(channelss) ;; '-d') show+=(discs) ;; '-f') show+=(path) ;; '-g') show+=(genres) ;; '-m') show+=(oldtimestamp) ;; '-M') show+=(types) ;; '-N') show+=(tracktotals) ;; '-p') show+=(performers) ;; '-s') show+=(rates) ;; '-S') show+=(size) length[count]=5 ;; '-B') show+=(depths) ;; '-t') show+=(titles) ;; '-y') show+=(years) ;; '-#') show+=(count) ;; '--stats') stats=1 continue ;; '-T') timeformat="$1" shift continue ;; '-o') output="$1" shift continue ;; '-u') update=1 continue ;; '-D') (( debug++ )) continue ;; [0-9]*) length[count-1]=$opt continue ;; esac (( count++ )) done [ -z "$output" ] && { cat <<-EOHelp No output specified! -f Path -# File count -S Size -b Average bitrate -C Channels -s Sample rate -B Bits per sample -m Mofification time -M Format -A Album artist -l Album -a Artist -c Composer -d Disc -g Genre -p Performer -N Track total -t Title -y Year --stats -T : date-time format (see 'man date' for possible values) -o : output file (path relative to Source) -u : update database first -D : debug EOHelp exit 1 } getConfig openDatabase columns="${show[@]//*/-}" printPath() { for key in ${!pathparts[@]} do if [[ ${pathparts[key]} == ${oldpathparts[key]} ]] then echo -n " ${pathparts[key]//?/ }" else echo -n "${pathparts[key]}/" fi done } printline() { local print for index in ${!show[@]} do info="${show[index]}" locallength="${length[index]:=50}" path="$olddir" case $info in 'bitrates') info=$( printf %${locallength}d \ $((bitrates/count)) ) (( info )) || unset info ;; 'oldtimestamp') info=$(printDate ${!info}) ;; 'count') info=$(printf %${locallength}d $count) ;; 'path') while [[ $path =~ / ]] do pathparts+=("${path%%/*}") path=${path#*/} done pathparts+=("$path") info=$(printPath) unset oldpathparts for key in ${!pathparts[@]} do oldpathparts[key]=${pathparts[key]} done unset pathparts ;; 'size') if (( size > 1073741823 )) then info=$(( (size * 1000) / 1073741824 )) int=$(( info / 1000 )) if (( ${#int} > 2 )) then info=$(printf %4sG $int) else info=$( printf %2s.%.1sG\ $int \ ${info#int} ) fi elif (( size > 1048575 )) then info=$(( (size * 1000) / 1048576 )) int=$(( info / 1000 )) if (( ${#int} > 2 )) then info=$(printf %4sM $int) else info=$( printf %2s.%.1sM\ $int \ ${info#int} ) fi suffix=M elif (( size > 1023 )) then info=$(( (size * 1000) / 1024 )) int=$(( info / 1000 )) if (( ${#int} > 2 )) then info=$(printf %4sk $int) else info=$( printf %2s.%.1sk\ $int \ ${info#int} ) fi else info=$size info=$(printf %4s $size) fi ;; *) info="${!info}" ;; esac printtmp="${info:0:$locallength}" if [ -z "$printtmp" ] then until (( ${#printtmp} == locallength/2)) do printtmp+=' ' done printtmp+='-' fi until (( ${#printtmp} == locallength )) do printtmp+=' ' done print+=(${print+|} "$printtmp") done echo "${print[@]}" } if (( update )) then getFiles updateMimes updateTags fi echo 'SELECT IFNULL( (SELECT last_seen FROM source_files ORDER BY last_seen DESC LIMIT 1), 0);' >&3 read -u4 lastupdate if ! [[ "$output" == - ]] then exec > "$output" fi cat <<-EOBrag # Generated by AtOM's createindex toy. # https://gitorious.org/atom # (C) 2012-2013 Vincent Riquer (GPL-3) # # $0 $args # # Last database update: $(date -d @$lastupdate +'%x %X') EOBrag printDate() { date -d"@$1" +"${timeformat:-%x %X}" } for index in ${!show[@]} do info="${show[index]}" locallength="${length[index]:=50}" case $info in albumartists) info='Album artist' ;; albums) info='Album' ;; artists) info='Artist' ;; bitrates) info='Bitrate' ;; depths) info='Bit depth' ;; channelss) info='Channels' ;; composers) info='Composer' ;; count) info='#' ;; discs) info='Disc' ;; path) info='Directory name' ;; genres) info='Genre' ;; oldtimestamp) info='Last modified' ;; types) info='Format' ;; performers) info='Performer' ;; rates) info='Sample rate' ;; titles) info='Title' ;; tracktotals) info='Track total' ;; years) info='Date' ;; size) info='Size' ;; esac printtmp="${info:0:$locallength}" until (( ${#printtmp} == locallength )) do printtmp+=' ' done print+=(${print+|} "$printtmp") done echo "${print[@]}" echo "${print[@]//[^|]/=}" unset print echo ' SELECT source_files.filename, tags.bitrate, tags.channels, tags.rate, tags.depth, source_files.last_change, mime_types.mime_text, tags.albumartist, tags.album, tags.artist, tags.composer, tags.disc, tags.genre, tags.performer, tags.title, tags.track, tags.year, source_files.size FROM source_files INNER JOIN mime_types ON source_files.mime_type=mime_types.id INNER JOIN tags ON source_files.id=tags.source_file WHERE NOT mime_types.mime_text LIKE "text/%" AND NOT mime_types.mime_text LIKE "image/%" AND last_seen = '$lastupdate' ORDER BY source_files.filename COLLATE NOCASE; SELECT "AtOM:NoMoreFiles";' >&3 read -u4 line until [[ $line == AtOM:NoMoreFiles ]] do files+=("$line") read -u4 line done for line in "${files[@]}" do filename="${line%%::AtOM:SQL:Sep::*}" dir="${filename%/*}" rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::" bitrate="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" channels="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" rate="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" depth="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" timestamp="${rest%%::AtOM:SQL:Sep::*}" timestamp="${timestamp%%.*}" rest="${rest#*::AtOM:SQL:Sep::}" mimetype="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" albumartist="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" album="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" artist="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" composer="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" disc="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" genre="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" performer="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" title="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" track="${rest%%::AtOM:SQL:Sep::*}" [[ $track =~ / ]] && tracktotal=${track#*/} rest="${rest#*::AtOM:SQL:Sep::}" year="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" filesize="${rest%%::AtOM:SQL:Sep::*}" rest="${rest#*::AtOM:SQL:Sep::}" case $mimetype in application/ogg\ opus) type=Opus ;; application/ogg\ vorbis) type=Vorbis ;; audio/mp4) type=MPEG4\ Audio;; audio/mpeg) type=MPEG\ Audio;; audio/x-flac) type=FLAC ;; video/mpeg) type=MPEG\ Video;; video/webm) type=WebM ;; video/x-flv) type=Flash\ Video;; video/x-ms-asf) type=Windows\ Media;; video/x-msvideo) type=AVI\ Video ;; audio/*) type=Other\ Audio;; video/*) type=Other\ Video;; *) type=Other ;; esac if [[ $dir == $olddir ]] then (( size += filesize )) (( count++ )) (( $bitrate )) && (( bitrates+=bitrate )) (( oldtimestamp = ( timestamp > oldtimestamp ? timestamp : oldtimestamp ) )) expr1='(^|,)' expr2='(,|$)' if ! [[ $channelss =~ $expr1"$channels"$expr2 ]] then if [ -n "$channelss" ] \ && (( channels < ${channelss%%,*} )) then channelss="$channels,$channelss" else channelss+="${channelss+,}$channels" fi fi if ! [[ $rates =~ $expr1"$rate"$expr2 ]] then if [ -n "$rates" ] \ && (( rate < ${rates%%,*} )) then rates="$rate,$rates" else rates+="${rates+,}$rate" fi fi if [ -n "$depth" ] && ! [[ $depths =~ $expr1"$depth"$expr2 ]] then if [ -n "$depths" ] \ && (( depth < ${depths%%,*} )) then depths="$depth,$depths" else depths+="${depths+,}$depth" fi fi if ! [[ $types =~ $expr1"$type"$expr2 ]] then [ -z "$types" ] \ && unset types [ -n "$type" ] \ && types+="${types+,}$type" fi if ! [[ $albumartists =~ $expr1"$albumartist"$expr2 ]] then [ -z "$albumartists" ] \ && unset albumartists [ -n "$albumartist" ] \ && albumartists+="${albumartists+,}$albumartist" fi if ! [[ $albums =~ $expr1"$album"$expr2 ]] then [ -z "$albums" ] \ && unset albums [ -n "$album" ] \ && albums+="${albums+,}$album" fi if ! [[ $artists =~ $expr1"$artist"$expr2 ]] then [ -z "$artists" ] \ && unset artists [ -n "$artist" ] \ && artists+="${artists+,}$artist" fi if ! [[ $composers =~ $expr1"$composer"$expr2 ]] then [ -z "$composers" ] \ && unset composers [ -n "$composer" ] \ && composers+="${composers+,}$composer" fi if ! [[ $discs =~ $expr1"$disc"$expr2 ]] then [ -z "$discs" ] \ && unset discs [ -n "$disc" ] \ && discs+="${discs+,}$disc" fi if ! [[ $genres =~ $expr1"$genre"$expr2 ]] then [ -z "$genres" ] \ && unset genres [ -n "$genre" ] \ && genres+="${genres+,}$genre" fi if ! [[ $performers =~ $expr1"$performer"$expr2 ]] then [ -z "$performers" ] \ && unset performers [ -n "$performer" ] \ && performers+="${performers+,}$performer" fi if ! [[ $titles =~ $expr1"$title"$expr2 ]] then [ -z "$titles" ] \ && unset titles [ -n "$title" ] \ && titles+="${titles+,}$title" fi if ! [[ $tracktotals =~ $expr1"$tracktotal"$expr2 ]] then [ -z "$tracktotals" ] \ && unset tracktotals [ -n "$tracktotal" ] \ && tracktotals+="${tracktotals+,}$tracktotal" fi if ! [[ $years =~ $expr1"$year"$expr2 ]] then [ -z "$years" ] \ && unset years [ -n "$year" ] \ && years+="${years+,}$year" fi else if [ -n "$olddir" ] then printline fi unset bitrates channelss="$channels" rates="$rate" depths="$depth" types="$type" albumartists="$albumartist" albums="$album" artists="$artist" composers="$composer" discs="$disc" genres="$genre" performers="$performer" titles="$title" tracktotals="$tracktotal" years="$year" size="$filesize" count=1 (( bitrate )) && (( bitrates=bitrate )) oldmimetype=$mimetype oldrate=$rate oldtimestamp=$timestamp fi unset tracktotal olddir="$dir" done printline (( stats )) || exit 0 unset types counts counpcts sizes sizepcts echo ' ' echo ' SELECT COUNT(*),SUM(size) FROM source_files INNER JOIN mime_types ON source_files.mime_type=mime_types.id;' >&3 read -u4 line totalcount="${line%%::AtOM:SQL:Sep::*}" maxcountlen=$(printf "%'i" $totalcount) maxcountlen=${#maxcountlen} rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::" totalsize="${line%%::AtOM:SQL:Sep::*}" for format in \ audio/x-flac \ application/ogg\ vorbis \ application/ogg\ opus \ audio/mpeg \ audio/mp4 \ \ video/x-ms-asf \ video/webm \ video/mpeg \ video/x-flv \ video/x-msvideo \ \ 'image/%' \ 'text/%' \ '%' do echo ' SELECT COUNT(*),SUM(size) FROM source_files INNER JOIN mime_types ON source_files.mime_type=mime_types.id WHERE mime_text LIKE "'"$format"'";' >&3 read -u4 line count="${line%%::AtOM:SQL:Sep::*}" rest="${line#*::AtOM:SQL:Sep::}::AtOM:SQL:Sep::" size="${rest%%::AtOM:SQL:Sep::*}" (( count )) || continue case $format in application/ogg\ opus) type=Opus ;; application/ogg\ vorbis) type=Vorbis ;; audio/mp4) type=MPEG4\ Audio ;; audio/mpeg) type=MPEG\ Audio ;; audio/x-flac) type=FLAC ;; video/mpeg) type=MPEG\ Video ;; video/webm) type=WebM ;; video/x-flv) type=Flash\ Video ;; video/x-ms-asf) type=Windows\ Media ;; video/x-msvideo) type=AVI\ Video ;; audio/%) type=Total\ Audio ;; video/%) type=Total\ Video ;; image/%) type=Images ;; text/%) type=Texts ;; %) type=Total ;; esac counts+=( "$count" ) types+=( "$type" ) maxtypelen=$(( ${#type} > maxtypelen ? ${#type} : maxtypelen )) if (( size > 1073741823 )) then size=$(( (size * 1000) / 1073741824 )) int=$(( size / 1000 )) if (( ${#int} > 2 )) then sizes+=( "$(printf %4sG $int)" ) else sizes+=( "$( printf %2s.%.1sG\ $int \ ${size#int} )" ) fi elif (( size > 1048575 )) then size=$(( (size * 1000) / 1048576 )) int=$(( size / 1000 )) if (( ${#int} > 2 )) then sizes+=( "$(printf %4sM $int)" ) else sizes+=( "$( printf %2s.%.1sM\ $int \ ${size#int} )" ) fi suffix=M elif (( size > 1023 )) then size=$(( (size * 1000) / 1024 )) int=$(( size / 1000 )) if (( ${#int} > 2 )) then sizes+=( "$(printf %4sk $int)" ) else sizes+=( "$( printf %2s.%.1sk\ $int \ ${size#int} )" ) fi else size=$size sizes+=( "$(printf "%4s " $size)" ) fi done head=$( printf "| %-${maxtypelen}s | %'${maxcountlen}s | %% | Size |"\ Format Count ) sep=${head//[^|]/-} sep=${sep//\|/+} echo "$sep" echo "$head" echo "$sep" for id in ${!types[@]} do printf "| %-${maxtypelen}s | %'${maxcountlen}i | %3i%% | %5s |\n"\ "${types[id]}" \ "${counts[id]}" \ $(( ${counts[id]} * 100 / totalcount )) \ "${sizes[id]}" echo "$sep" done