Skip to content

Instantly share code, notes, and snippets.



Created Apr 17, 2009
What would you like to do?
=head1 NAME - Get file paths for rhythmbox playlists.
./ [pattern]
./ --list [pattern]
./ --file path/to/playlists.xml
=head1 OPTIONS
=item B<--list [pattern]>
Show available playlists, optionally restricted to those matching some regex.
=item B<--song-pattern pattern>
Restrict song URIs to those matching some pattern. Defaults to "file://".
=item B<--raw-uri>
Print a full URI, undecoded.
=item B<--file filename>
Specify a playlists.xml file.
Worse is better, baby.
This started at around 12 lines with a couple of regexen, and you could
probably get most of it down to a one-liner without much trouble. I decided to
add a couple of extra options instead.
=head1 BUGS
Are you kidding? I'd be shocked if this worked anywhere besides my laptop.
=head1 AUTHOR
Brennen Bearnes <>
No Copyright
use strict;
use warnings;
use 5.10.00;
use Rhythmbox::Playlist;
use Getopt::Long qw(:config auto_help);
# Set a few defaults:
my %options = (
file => "$ENV{HOME}/.gnome2/rhythmbox/playlists.xml",
'song_pattern' => 'file://',
my $list = undef;
my $raw_uri = undef;
'file=s' => \$options{'file'},
'song-pattern=s' => \$options{'song_pattern'},
'list' => \$list,
'raw-uri' => \$raw_uri,
my $lists = Rhythmbox::Playlist->new(%options);
# Take patterns from whatever's left in arguments:
my @patterns = @ARGV;
$patterns[0] //= '.*';
for my $pattern (@patterns) {
if ($list) {
print join "\n", $lists->lists($pattern);
} else {
if ($raw_uri) {
print join "\n", $lists->songs($pattern);
} else {
print join "\n", $lists->songs_decoded($pattern);
package Rhythmbox::Playlist;
use strict;
use warnings;
use XML::Simple;
use URI::Escape qw(uri_unescape);
sub new {
my $class = shift;
my %params = @_;
my $self = \%params;
bless $self, $class;
return $self;
sub read_file {
my $self = shift;
# Get XML data:
my $xml = new XML::Simple;
my $data = $xml->XMLin($self->{file});
$self->{playlists} = $data->{playlist};
sub lists {
my $self = shift;
my ($pattern) = @_;
my @names = keys %{ $self->{playlists} };
return sort grep { m/$pattern/ } @names;
sub songs_decoded {
my $self = shift;
my @songs = $self->songs(@_);
foreach my $song (@songs) {
$song =~ s{ (?:file|smb|ssh|sftp) :// (.*) }{$1}x;
$song = uri_unescape($song);
return @songs;
sub songs {
my $self = shift;
my ($pattern) = @_;
my @names = grep { m/$pattern/ }
keys %{ $self->{playlists} };
my @songs;
foreach my $name (@names) {
my $locations = $self->{playlists}->{$name}{'location'};
next unless defined $locations;
push @songs, (ref($locations) ? @{ $locations } : $locations);
@songs = sort grep { m/$self->{song_pattern}/ } @songs;
return @songs;
use strict;
use warnings;
use lib '..';
use Test::Simple tests => 1;
use Rhythmbox::Playlist;
use strict;
use warnings;
use Test::Harness;
runtests(glob "t/*.t");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.