#!/usr/bin/perl # This is a simplistic MP3 player I use because most of the other ones I found # were either bloated GUIs, broken GUIs, or didn't have the few features I # wanted. use strict; my $VERSION = 1.00; use X::Osd; use Audio::Scrobbler; use MP3::Info; use IPC::Open2; use List::Util qw(shuffle ); use Symbol qw(gensym ); use POSIX q(:sys_wait_h); use constant LASTFM_USER => 'username'; use constant LASTFM_PASS => 'password'; use constant MP3_PLAYER => '/usr/bin/mpg321'; use constant BREAK_TIMEOUT => 2; my $pid; my $caught_interrupt = 0; $SIG{INT} = sub { exit if $caught_interrupt > 0; $caught_interrupt++; alarm(BREAK_TIMEOUT); kill 2, $pid if $pid; }; $SIG{ALRM} = sub { $caught_interrupt = 0; }; my @file_list = map { chomp; $_ } shuffle $ARGV[0] eq '-' ? : @ARGV; my $osd = X::Osd->new(5); $osd->set_font("-*-helvetica-bold-r-normal--17-120-100-100-p-*-*-1"); $osd->set_colour("Green"); $osd->set_timeout(3); $osd->set_pos(XOSD_bottom); $osd->set_align(XOSD_center); $osd->set_horizontal_offset(0); $osd->set_vertical_offset(10); $osd->set_shadow_offset(2); my $scrobbler = new Audio::Scrobbler( cfg => { progname => 'xms', progver => '0.3.8.1', username => LASTFM_USER, password => LASTFM_PASS, } ); MP3: foreach my $mp3 (@file_list) { next MP3 if !-r $mp3 || !-s $mp3 || !-f $mp3; my $tag = MP3::Info->new($mp3) || next MP3; my $string = sprintf( "%s by %s (%d:%02d)", $tag->title, $tag->artist, $tag->mm, $tag->ss ); $osd->string(0, "Playing $string"); print "Playing $string\n"; if ($ENV{TERM} =~ /[wx]term/i || $ENV{TERM} =~ /rxvt/) { print "\033]0;$string\007"; } if (!sys(MP3_PLAYER, $mp3)) { next MP3 if $? << 8 == 512; # This happens if you CTRL-C when its initialising. die sprintf("MP3 player failed with value %d\n", $? << 8); } elsif (!$caught_interrupt && $tag->title && $tag->artist) { print "Submitting to AudioScrobbler.\n"; my $submission = { title => $tag->title, artist => $tag->artist, 'length' => $tag->mm * 60 + $tag->ss, album => q{}, }; if (!$scrobbler->handshake() || !$scrobbler->submit($submission)) { print "Failed submitting to AudioScrobbler: " . $scrobbler->err() . "\n"; } } $osd->string(0, q{}); } sub sys { open my $old_stdout, '>&', \*STDOUT || die "Can't DUP STDOUT: $!\n"; open my $old_stderr, '>&', \*STDERR || die "Can't DUP STDERR: $!\n";; open STDOUT, '>>', '/dev/null' || die "Can't redirect STDOUT: $!\n"; open STDERR, '>>', '/dev/null' || die "Can't redirect STDERR: $!\n"; $pid = open2(gensym, gensym, @_) || die "Can't run @_: $!\n"; waitpid $pid, 0; open STDOUT, '>&', $old_stdout || die "Can't restore STDOUT: $!\n"; open STDERR, '>&', $old_stderr || die "Can't restore STDERR: $!\n"; return $? << 8 == 0; } __END__ =head1 NAME mp3play - A simple program for playing MP3s including AudioScrobbler, ID3 tag, xterm titlebar, and OSD support. =head1 DESCRIPTION Just a simple program that is run on the command line via either: cat mp3_playlist | mp3play - or mp3play song1.mp3 song2.mp3 song3.mp3... =head1 README This program can easily be modified to behave differently however you like. Basically, it's a program that will take a list of MP3s either from STDIN or on the command line, shuffle them, and then play them. If you CTRL-C out of a song, it will skip to the next song, and it won't submit the song to last.fm. If you CTRL-C twice in less than a default of 2 seconds, the program will exit entirely. When an mp3 plays, the MP3 tag is extracted, and the song's length is calculated. Then, the song name, artist, and length are displayed on the screen using X::Osd. Also, the xterm/rxvt titlebar is set to the song name/title/length. If the song completes without CTRL-C, the song is submitted to last.fm. It should work with just about mp3 player that supports being run as . Most things you'd want to configure are settable via constants. =head1 LICENCE This program is copyright (c) 2006 Justin Wheeler , and is free, opensource software, licenced under the same terms as Perl itself. =head1 BUGS Probably lots, but I'm unaware of them -- it's pretty simplistic. =head1 TODO This program could be made better in so many ways -- periodic audioscrobbler commits that don't block the software from doing its thing as well as queuing, curses, etc. The limits are endless! =head1 PREREQUISITES This script requires the following modules: C C C C C C C C =head1 EXAMPLES This is how I use this program: ( find /mp3/albums/ -iname \*.mp3 ; find /mp3/rock/ -iname \*.mp3 ) | mp3play - =head1 SCRIPT CATEGORIES Audio/MP3 =cut