/upod.pl
#!/usr/bin/perl
use File::Basename;
use File::Copy;
use Getopt::Long;
use strict;

my $uthome = $ENV{HOME} . '/.utunes';
my $outdir = $ENV{HOME} . "/dspod";
my $name = "playlist";
my ($size, $time);

# The proc subroutine stands in for the standard symlink operation if it is
# defined.  Because the conversion might mangle the filename, the subroutine
# should return the actual output filename.  In this example, I'm using conv
# (http://bytex64.net/code/conv/), but you can use anything you'd like.
my $proc;
$proc = sub {
	my ($in, $out) = @_;
	$in  =~ s/"/\\"/g;
	$out =~ s/\.\w+$/.ogg/;
	$out =~ s/"/\\"/g;
	system(qq!conv -f ~/dspod/Convfile -o "$out" "$in"!);
	return $out;
};

GetOptions(
	"outdir=s"	=> \$outdir,
	"size=s"	=> \$size,
	"time=s"	=> \$time,
	"name=s"	=> \$name,
);

sub timeparse {
	my ($s, $m, $h) = reverse split(/:/, shift);
	return $h * 3600 + $m * 60 + $s;
}

sub songlength {
	my $filename = shift;
	my $length;

	$filename =~ s/"/\\"/g;
	open UDB, qq!udb info "$filename"|!;
	while (<UDB>) {
		if (/length\s+(.*)$/) {
			$length = timeparse($1);
			last;
		}
	}
	close UDB;
	return $length;
}

mkdir $outdir unless -d $outdir;

if ($size) {
	if ($size =~ /[Kk]$/) {
		$size *= 10**3;
	} elsif ($size =~ /[Mm]$/) {
		$size *= 10**6;
	} elsif ($size =~ /[Gg]$/) {
		$size *= 10**9;
	} elsif ($size =~ /[Tt]$/) {
		$size *= 10**12;
	}
}

if ($time) {
	$time = timeparse($time);
}

my @files;
open FILES, $ARGV[0] || "$ENV{HOME}/.utunes/playlist";
chomp(@files = <FILES>);
close FILES;

my $n = 0;
my ($c, $t);
my $residual;	# The residual is a processed file from the previous iteration
		# and is copied instead of reprocessed.
LIST: {
	$c = 0;
	$t = 0;
	my ($dir, $playlist);
	if ($size || $time) {
		$dir = "$outdir/${name}_$n";
		$playlist = "$outdir/${name}_$n/${name}_$n.m3u";
	} else {
		$dir = "$outdir/$name";
		$playlist = "$outdir/$name/$name.m3u";
	}
	mkdir $dir unless -d $dir;
	open PLAYLIST, ">$playlist";
	print PLAYLIST "# Generated by upod\n";

	my $i = 0;
	while (@files) {
		my $file = shift @files;
		my $length = songlength($file);
		next if $time && $length > $time;
		my $outfile = $dir . '/' . sprintf("%04d_", $i) . basename $file;
		print PLAYLIST "$outfile\n";
		die "$outfile already exists!" if -e $outfile;

		if ($proc) {
			if ($residual) {
				# If the residual has been processed,
				# its new $outfile will have the wrong
				# extension
				$residual =~ /(\.\w+)$/;
				my $residual_extension = $1;
				$outfile =~ s/\.\w+$/$residual_extension/;
				move $residual, $outfile;
				undef $residual;
			} else {
				# Process the file somehow
				$outfile = &$proc($file, $outfile);
			}
		} else {
			symlink $file, $outfile
			or copy $file, $outfile
			or die "Could not symlink or copy $file to $outfile";
		}
		if ($size && -s $outfile > $size) {
			unlink $outfile;
			next;
		}

		# The size of a file will change if it is processed, but
		# (unless something screwy is going on) the length will not.
		if (($size && $c + -s $outfile > $size) ||
		    ($time && $t + $length > $time)) {
			$n++;
			unshift @files, $file;
			if ($proc) {
				$residual = $outfile;
			}
			print PLAYLIST "# $c bytes, $t seconds\n";

			close PLAYLIST;	# I don't strictly have to close the
					# file (it is automatically closed when
					# PLAYLIST is reopened above), but if I
					# don't some hapless C programmer will
					# come across this and freak out.
			goto LIST;
		}
		$c += -s $outfile;
		$t += $length;
	} continue {
		$i++;
	}
}

print PLAYLIST "# $c bytes, $t seconds\n";
close PLAYLIST;