playlist export via Perl

Greetings:
Thought I'd share my Perl playlist exporter for Subsonic, in case anyone might find it useful. It's been a while since I wrote it, but I think I relied heavily upon the Java version posted in these discussion. It's also a bit hacky, as I hadn't programmed in Perl for quite some time, so there's likely a bunch of stuff that can be cleaned up, but here you go:
Here's the usage:
I use this in cron to run nightly to create weekly backups, so here's my cron script
Thought I'd share my Perl playlist exporter for Subsonic, in case anyone might find it useful. It's been a while since I wrote it, but I think I relied heavily upon the Java version posted in these discussion. It's also a bit hacky, as I hadn't programmed in Perl for quite some time, so there's likely a bunch of stuff that can be cleaned up, but here you go:
- Code: Select all
#!/usr/bin/perl
use HTTP::Request::Common qw(POST);
use LWP::UserAgent;
use HTML::Strip;
use Class::Struct;
use Switch;
# create the structure that will be used to store the playlist information
struct ( playlist => {
playlistID => '$', # DB ID of the playlist
playlistName => '$', # name of the playlist
playlistSongs => '@', # array of songs in the playlist
}
);
# create the structure that will be used to store the song information
# this will be used to generate the M3U format
struct ( song => {
songPath => '$', # path of the song
songArtist => '$', # artist
songTitle => '$', # title
songDuration => '$', # duration in seconds
}
);
# this will be an array containing instances of the structure we created above
my @playlists;
if ( $#ARGV + 1 != 4 ) {
print "Usage: playlistExport.pl <URL to db.view> <user:password base64 encoded> <format (bak or m3u)> <playlist path>\n\n";
exit;
}
else {
my $baseURL = $ARGV[0];
my $playlistQuery = "select ID, NAME from PLAYLIST;";
my $songQuery = "SELECT PATH, ARTIST, TITLE, DURATION_SECONDS FROM MEDIA_FILE INNER JOIN PLAYLIST_FILE ON (MEDIA_FILE.ID = PLAYLIST_FILE.MEDIA_FILE_ID) WHERE PLAYLIST_ID= XX ORDER BY PLAYLIST_FILE.ID;";
my $authEncoded = $ARGV[1];
my $playlistFormat = $ARGV[2];
my $playlistPath = $ARGV[3];
# if the user did not add the trailing slash, let's add it
if ( $playlistPath !~ /\/$/ ) {
$playlistPath .= "/";
}
my $userAgent = LWP::UserAgent->new();
# first, get the playlists
my $request = POST $baseURL,
Content_Type => form-data,
Content => [ 'query' => $playlistQuery ],
Authorization => 'Basic ' . $authEncoded;
my $playlistHTML = $userAgent->request($request)->as_string();
print "playlistHTML = $playlistHTML\n";
# now, parse through the raw playlist HTML code and store the playlist names and IDs in an array
my $currentIndex = -1;
for (split /^/, $playlistHTML) {
if ( $_ =~ 'ruleTableCell' ) {
# set up the tag stripper
my $stripper = HTML::Strip->new();
# strip the HTML tags/attributes
my $thisData = $stripper->parse($_);
# remove any leading/trailing whitespace
$thisData =~ s/^\s+|\s+$//g;
# if the remaining data is entirely numeric (the ID), set the the array index to it
if ( $thisData =~ /^\d+$/ && $currentIndex == -1 ) {
$currentIndex = $thisData;
}
# otherwise, it should be the playlist name, so push it to the array and reset the index
else {
my $thisPlaylist = playlist->new ( playlistID => $currentIndex, playlistName => $thisData );
push (@playlists, $thisPlaylist);
$currentIndex = -1;
}
}
}
# now, for each of the playlists, grab the list of songs
for ( $indexer = 0; $indexer <= $#playlists; $indexer++ ) {
# set up the query to get the songs from the playlist
my $thisQuery = $songQuery;
my $thisID = $playlists[$indexer]->playlistID;
$thisQuery =~ s/XX/$thisID/;
# now, get the songs for the given playlist
my $request = POST $baseURL,
Content_Type => form-data,
Content => [ 'query' => $thisQuery ],
Authorization => 'Basic ' . $authEncoded;
my $songsHTML = $userAgent->request($request)->as_string();
# this will be used to keep track of the current field, so we can populate the song array with the proper information
my $currentField = 0;
my $thisSong;
# just like the playlists, we need to parse through the $songsHTML to get the paths and add them to the playlist's array
for (split /^/, $songsHTML ) {
if ( $_ =~ 'ruleTableCell' ) {
# set up the tag stripper
my $stripper = HTML::Strip->new();
# strip the HTML tags/attributes
my $thisData = $stripper->parse($_);
# remove any leading/trailing whitespace
$thisData =~ s/^\s+|\s+$//g;
switch ( $currentField ) {
case 0 { $thisSong = song->new (songPath => $thisData); $currentField++; }
case 1 { $thisSong->songArtist($thisData); $currentField++; }
case 2 { $thisSong->songTitle($thisData); $currentField++; }
case 3 { $thisSong->songDuration($thisData); push (@{$playlists[$indexer]->playlistSongs}, $thisSong); $currentField = 0; }
}
}
}
# now, output the playlists to the the format and path specified by the user
# first, check to see if the path exists
if ( -d $playlistPath ) {
# the path exists, so let's create the file handler
open (THISPLAYLIST, ">" . $playlistPath . $playlists[$indexer]->playlistName . "." . $playlistFormat ) or die "ERROR: could not open the playlist file (" . $playlists[$indexer]->playlistName . ") for writing!\n";
print "processing playlist: " . $playlistPath . $playlists[$indexer]->playlistName . "." . $playlistFormat . "\n";
# if the user has specified M3U as the format, print out the header
if ( $playlistFormat eq "m3u" ) {
print THISPLAYLIST "#EXTM3U\n";
}
# now, loop through all of the songs in the playlist, and print them to the file in the specified format
foreach ( @{$playlists[$indexer]->playlistSongs} ) {
switch ($playlistFormat) {
case "m3u" { print THISPLAYLIST "#EXTINF:" . $_->songDuration . ", " . $_->songArtist . " - " . $_->songTitle . "\n" . $_->songPath . "\n"; }
case "bak" { print THISPLAYLIST $_->songPath . "\n";}
}
}
# close the file
close (THISPLAYLIST);
}
else {
print "ERROR: the specified path ($playlistPath) does not exist!\n";
exit;
}
}
}
Here's the usage:
- Code: Select all
./playlistExport.pl (URL to Subsonic's db.view) (encoded Auth ID) (playlist format) (export path)
I use this in cron to run nightly to create weekly backups, so here's my cron script
- Code: Select all
FOLDERID=`date +\%w`; /path/playlistExport.pl http://localhost/music/db.view XXXXXXXXXXXXXXXX m3u /path/$FOLDERID; touch /path/$FOLDERID