#!/usr/bin/perl # # Name: friendly_links.pl # Version: 1.1 # Date: 2002/11/04 # Author: Greg Zwart # Purpose: Queries the DEC database for all ripped tracks and creates # "friendly" symbolic links to the "unfriendly" tracks. This # does not manipulate the DEC database in any way. # # Disclaimer: This script is relatively non-intrusive, but has not been # thoroughly tested with all DEC configurations. Use at your own risk. # # Revision History: at end of file # BEGIN { @INC = (@INC, "/opt/odin/perl5/lib/site_perl/i386-linux", "/opt/odin/perl5/lib/site_perl"); } use DBI; use File::Find; use Getopt::Std; # Configuration variables $db = "/content/db"; $links = "/content/exported-music/mappings"; $music = "/content/music"; $playlist = "_playlist.m3u"; # Get command line options getopts('vtdh') or exit usage(); exit usage() if $opt_h==1; # Verify proper configuration abort("Invalid music directory!") if !-d $music; abort("Invalid mapping directory!") if !-d $links; abort("Invalid database directory!") if !-d $db; # Skip the link creation, if the delete-only option is specified goto DELETIONS if $opt_d==1; # Query the database $dbh = DBI->connect("DBI:XBase:$db") or abort("Unable to connect to database"); $sth_track = $dbh->prepare("SELECT album_id, artist_id, genre_id, title, file FROM LibraryTrack ORDER BY cd_trk_num"); $sth_album = $dbh->prepare("SELECT name FROM Album WHERE id = ?"); $sth_artist = $dbh->prepare("SELECT name FROM Artist WHERE id = ?"); $sth_genre = $dbh->prepare("SELECT genre FROM Genre WHERE id = ?"); # Query the tracks from the database and bind the columns to variables $sth_track->execute or abort("Unable to execute SQL statement"); $sth_track->bind_columns(undef, \($album_id, $artist_id, $genre_id, $title, $file)); # Loop through all of the tracks while( $sth_track->fetch() ) { # Get the track info $sth_album->execute($album_id); $sth_album->bind_columns(undef,\$album); $sth_album->fetch(); $sth_artist->execute($artist_id); $sth_artist->bind_columns(undef,\$artist); $sth_artist->fetch(); $sth_genre->execute($genre_id); $sth_genre->bind_columns(undef,\$genre); $sth_genre->fetch(); # Format the content $artist =~ s/\.//g; $album =~ s/\.//g; $artist =~ s#/#-#g; $album =~ s#/#-#g; $genre =~ s#/#-#g; $title =~ s#/#-#g; # Determine the filepath info $file =~ s/^tracks/$music/; $link = "$links/$artist/$album/$title.mp3"; # Create the artist/album hierarchy if(!-d "$links/$artist") { print "making $links/$artist\n" if $opt_v==1; mkdir "$links/$artist" unless $opt_t==1; } if(!-d "$links/$artist/$album") { print "making $links/$artist/$album\n" if $opt_v==1; mkdir "$links/$artist/$album" unless $opt_t==1; } # Create the symbolic link. Use of 'eval' is to bypass this command on # unsupported systems. if(!-f $link) { print "linking $link to $file\n" if $opt_v==1; eval{symlink($file,$link),1} unless $opt_t==1; open LIST,">>$links/$artist/$album/$playlist"; print LIST "./$title.mp3\n"; close LIST; } } # Clean up the database and statement handles $sth_track->finish(); $sth_album->finish(); $sth_artist->finish(); $sth_genre->finish(); $dbh->disconnect(); DELETIONS: # Finally, things need to stay in sync, so traverse the directory hierarchy # and remove any links/artists/albums that are no longer in the database. # This will be accomplished in a two step process: # # Step 1 - Remove symbolic links. If the -l operator indicates the file is # a symbolic link, but the -f indicates it is not a file, this means the # file that the link references has been deleted, so delete the link... # this is a hack, but it works. finddepth({wanted=>sub{unlink $_ if -l $_ && !-f $_; }},$links) unless $opt_t==1; # Step 2 - Remove the album/artist directories if they contain no links finddepth({wanted=>sub{opendir(DIR,$_);@cnt=readdir(DIR)-2;closedir(DIR);rmdir $_ if -d $_ && $cnt==0;}},$links) unless $opt_t==1; # sub: displays usage information sub usage() { print<<_EOF_; usage: $0 [-vtdh] -v verbose output. -t test-mode. Does not create links. -d delete-only. Only deletes out-dated links. -h displays usage information. _EOF_ } # sub: wrapper for 'die' sub abort($) { print "@_\n"; die; } ############################################################################ # Revision History # # v1.1 - 2002/11/04 # - modified to access database directly (eliminated a lot of module deps) # - added support for album playlist file creation # - added check for '/' characters in artist/album/track info # # v1.0 - 2002/10/29 # - initial version #