List audiobooks and/or music based on directories

Alec Jacobson

August 11, 2009

weblog/

This began as a script to count up the number of books and authors in my audiobook collection. The directories looked like this:
audiobooks/
  ernest hemingway/
    for whom the bell tolls/
    the sun also rises/
  f scott fitzgerald/
    tender is the night/
[and so forth]
Each author had a folder which held subfolders for each book, in turn those book folders held the actual mp3 files (or even further subfolders for each CD). Using ls I managed to extract a count of the author folders and a count of the internal, book folders. Here's that simple bash script:
#!/bin/bash
# Usage
plain=`ls -1 "$1"/*`
#guarantee a  new line at the end of the file
plain="$plain
"
NUMTITLES=`echo "$plain" | grep -iv ":$" | grep -vc ^$`
NUMAUTHORS=`echo "$plain" | grep -c ":$"`
echo "There are $NUMTITLES book titles and $NUMAUTHORS authors."
If you save that script in a file called count.sh, then run it on the command line with ./count.sh audiobooks/ (replacing "audiobooks/" with the path to your top level folder, the one holding the author folders). So then I thought why not transform this to also print out a nice readable list of my books. The command ls -1 * already makes a nice list. Notice that the -1 tells ls to only look one level deep recursively, and * ignores things like . and .. from being searched. So I simply tacked on
  echo "$plain" > "booklist.txt"
to the end of the code above. Plain text is great for quick searching and viewing on a terminal, but occasionally I wanted to show my list to friends where a slick, clean html page would be more appropriate. So here is a full fledged command line app with argument requirements and all. Save it in a file called list.sh:
#!/bin/sh
USAGE="Usage: list [filename] [directory] 
  If filename ends with .html then output will be html,
  else output will be plain text."
if test -z "$1" || test -z "$2"
then
  echo "$USAGE"
  exit 1
fi

if [ ! -d $2 ]; then
  echo "list: $2 is not a directory"
  echo "$USAGE"
  exit 1
fi

dir=$2
plain=`ls -1 "$dir"/*`
#guarantee a  new line at the end of the file
plain="$plain
"
NUMTITLES=`echo "$plain" | grep -iv ":$" | grep -vc ^$`
NUMAUTHORS=`echo "$plain" | grep -c ":$"`
echo "There are $NUMTITLES book titles and $NUMAUTHORS authors."

if [[ "$1" != *.html ]]; then
  echo "$plain" > $1
else
  HNAME=$1
  html=`echo "$plain" \
    | sed "s/\(.*\)\([^:]\)$/      \1\2<\/br>/" \
    | sed "s/^\s*$/    <\/div>/" \
    | sed "s/.*\/\(.*\):$/    <h3>\1<\/h3> <div class='books'>/"`
  html="<html>
  <head> 
    <style type='text/css'>
      body {
        font : 10pt verdana;
        background: white;
        width: 95%
      }
      h2 {
        margin: 15px 0px 5px 0px;
      }
      h3 {
        margin: 15px 0px 5px 0px;
      }
      .books {
        border: 1px solid #dddddd;
        padding: 5px 5px 5px 10px;
        background: #eeeeff;
      }
    </style>
  </head>
  <body>
     <h2>$NUMTITLES book titles and $NUMAUTHORS authors</h2>
  $html
  </body> 
</html>" 
  echo "$pre $html $post" > $1
fi
Then on the command line run this with two arguments: the output file and the path to the directory containing the author folders. Run using: ./list.sh output.html audiobooks/ to produce an html list in output.html or ./list.sh output.txt audiobooks/ to throw the ls -1 * output into a plain text file. Check out the html listing of my audiobooks folder. Note: Change all the book/author stuff to whatever you're listing or some generic terms maybe. Update: sed stalls if you give it too much, so this script fails on large directories. Check out the new ruby version which works well on large directories.