ID3 batch editing

Some of my music had no interpreter and title tags set thus I wrote a little shell script to batch edit these ID3 tags. The filenames of the songs looked like “01 Interpret – Song Title.mp3″. In a few moments I wrote the following php script1 which split the filename into the interpreter and the title.

All the python scripts listed here are using the library pytagger.

<?php
if ($handle = opendir('.'))
{
   while (false !== ($file = readdir($handle)))
   {
	   if ($file != "." && $file != "..")
	   {
		$file_name = $file;
		$file = explode("-", $file);

		if(count($file) < 2)
		  continue;

		$interpret = trim($file[0]);
		$title = trim(str_replace(".mp3","",$file[1]));

		echo "interpret: $interpret , tite: $title \n";
		// execute the python script id3.py to set the tags
		exec("./id3.py '$file_name' '$title' '$interpret' ");
	   }
	}
}

closedir($handle);
?>

Unfortunately I forgot to strip the digits so all the interpreters had two leading digits afterwards.

I had to iterate over the files again. As id3.py creates new tag frames I had to write another script (fix_interpreters.py) that reads the old frames to get the old values.

#!/usr/bin/env python

from tagger import *
import sys, os, fnmatch, pickle, re

filename = sys.argv[1]

print "Processing '", filename, "'"

try:
  id3 = ID3v2(filename)

  if id3.tag_exists():
    interpret_frame = None
    for frame in id3.frames:
      interpretfid = 'TPE1'
      if id3.version == 2.2:
        interpretfid = 'TP1'

      if frame.fid == interpretfid:
        interpret_frame = frame
        break

    interpret = interpret_frame.strings[0]
    repaired = re.sub('^\d\d\s', '', interpret)

    print "Repairing "", interpret, "" => "",repaired,"""
    interpret_frame.set_text(repaired)

    # replace interpret frame
    id3.frames = [frame for frame in id3.frames if frame.fid != interpretfid]
    id3.frames.append(interpret_frame)
    id3.commit(pretend=0)

except ID3Exception, e:
  print("ID3 exception: %s" % str(e))

I didn’t want to rewrite the php script above because I thought that typing a loop in the shell would be easier (or at least faster).

for file in `ls -1 *.mp3`; do ./fix_interpreters.py $file; done

I tried it with this loop but ls -1 *.mp3 doesn’t list each mp3 file one per line as expected. Instead it splits the filenames after each whitespace. Thankfully to Greg Miller’s post about Handling Filenames With Spaces this wasn’t no problem anymore. I changed the loop into

find ./ -name '* *' | while read filename; do ls -ld "$filename"; ./fix_interpreters.py "$filename"; done

and ta-da, it worked.

  1. PHP scripts can be executed in a shell. Use php -f script.php to execute a file or use -r instead of -f to run inline code.
DeliciousDiggTechnorati FavoritesRedditLinkedInFacebookSpurlTwitterWebnewsYiGGMySpaceYahoo BookmarksFriendFeedGoogle BookmarksLiveJournalShare

Batch printing

I often upload some pdf files on a server to print them at my faculty. On the server I follow these steps:

1. Convert the uploaded pdf files to postscript

for file in `ls -1`; do pdftops $file; done

ls -11 lists all the files of the current directory, one file per line. pdftops actually converts the pdf files to postscript.

2. Delete the pdf files

rm *.pdf

We need to delete the pdf files to only list postscript files in the next command.

3. Put two pages on one page and print them

for file in `ls -1`; do psnup -2 -d0 $file | lpr -Pscit32411d; done

Again we walk through the file list but this time psnup prints two pages on one page and we pipe its result to the print command lpr. The argument P is the printer name of our destination.

I’m sure this can be done in only one line but I’m not a big shell scripter so I make the batch printing in three lines.

  1. The parameter is a one and not a l
DeliciousDiggTechnorati FavoritesRedditLinkedInFacebookSpurlTwitterWebnewsYiGGMySpaceYahoo BookmarksFriendFeedGoogle BookmarksLiveJournalShare