#!/usr/bin/perl -w use strict; use File::Spec::Functions qw(catfile ); use File::Basename qw(fileparse); # We're going to build .m3u playlists. # # Assume $MusicDir contains directories corresponding to ${ArtistDir}s # Each $ArtistDir directory contains symlinks with ${AlbumDir}s # Each $AlbumDir (symlinked dir) contains ${MusicFile}s to be put in a playlist. # # There are two sets of playlists to be produced: # # 1) Each album needs a playlist # 2) Each artist needs a playlist composed of the albums stuck together in order # # We need a hash of hashes of arrays: # # %music = ("artist" => \%albums) # %albums = ( "album directory 1" => \@album1, ... ) # @album1 = ("filename1","filename2","filename3"...) # # (Obviously most of these will be anonymous.) # # So each filename will be referred to as: # $music{"artist"}{"album directory"}[0] and so forth. # # Also, each album array will be known as: # @{ $music{"artist"}{"album"} } # # We then need some subroutines to construct the data structure # and subroutines to build playlists out of it. Simple. my $MusicBaseDir = '/home/tim/music'; my $PlaylistBaseDir = '/home/tim/playlists'; my %MusicCategories = ( "rock" => "Rock", "classical" => "Classical", "other" => "Other", ); use vars qw( $MusicDir $PlaylistDir %music ); if (-e $PlaylistBaseDir) { die "$PlaylistBaseDir exists and isn't a directory!" unless (-d $PlaylistBaseDir); } else { mkdir $PlaylistBaseDir } for (keys %MusicCategories) { %music = (); $MusicDir = catfile($MusicBaseDir, $MusicCategories{$_}); $PlaylistDir = catfile($PlaylistBaseDir, $_); BuildDataStructure(); CreatePlaylistDirs(); CategoryPlaylists(); } sub BuildDataStructure { opendir (MUSIC, $MusicDir) or die "Can't open $MusicDir: $!"; for (readdir MUSIC) { # Now looking at a list of artist directories my $ArtistName = $_; my $ArtistDir = catfile($MusicDir, $_); next if (/^\./ or /^Various$/); next unless (-d $ArtistDir); opendir (ARTIST, $ArtistDir) or die "Can't open $ArtistDir: $!"; for (readdir ARTIST) { # Now looking at a list of album symlinks my $AlbumDir = catfile($ArtistDir, $_); next unless (-l $AlbumDir); opendir (ALBUM, $AlbumDir) or die "Can't open $AlbumDir: $!"; for (readdir ALBUM) { # Now looking at a list of tracks # Add each one to the data structure next if (/^\./); next unless (/\.(ogg|mp3)$/); my $MusicFile = catfile($AlbumDir, $_); push @{ $music{$ArtistName}{$AlbumDir} } , $MusicFile; } } } } sub CreatePlaylistDirs { my @SubDirs = qw( artists albums ); my @Dirs = ($PlaylistDir); for (@SubDirs) { push @Dirs, catfile($PlaylistDir, $_); } for (@Dirs) { if (-e $_) { die "$_ exists and is not directory!" unless (-d $_); } else { mkdir $_; } } } sub CategoryPlaylists { for my $ArtistName (sort keys %music) { my $ArtistPlaylist = catfile($PlaylistDir, "artists", "$ArtistName.m3u"); open (ARTIST_PLAYLIST, "> $ArtistPlaylist") or die "Can't open $ArtistPlaylist: $!"; for my $AlbumDir (sort keys %{ $music{$ArtistName} }) { my $AlbumName = ExtractAlbumName($AlbumDir); my $AlbumPlaylist = catfile($PlaylistDir, "albums", "$ArtistName - $AlbumName.m3u"); open (ALBUM_PLAYLIST, "> $AlbumPlaylist") or die "Can't open $AlbumPlaylist: $!"; for my $MusicFile (sort @{ $music{$ArtistName}{$AlbumDir} }) { print ARTIST_PLAYLIST "$MusicFile\n"; print ALBUM_PLAYLIST "$MusicFile\n"; } } } } sub ExtractAlbumName { # Extract album's name from $AlbumDir $_ = shift; $_ = fileparse($_); if (/^[^-]*$/) { # No hyphens return $_; } elsif (/^\d{4} - \d - (.*)$/) { # Year, number, then title return $1; } elsif (/^\d{2,4} - (.*)$/) { # Numbered Albums (inc. Year) return $1; } elsif (/^Op\.? \d{2,3} -? (.*)$/) { # Classical Composers return $1; } elsif (/^KV \d{2,3} -? (.*)$/) { # Mozart return $1; } elsif (/^BWV \d{4} - (.*)/) { # Bach return $1; } else { return $_; # Probably right. } }