1 #!/usr/bin/perl
  2 # yargla - Yet Another Rapid Glue Last.fm App v0.02
  3 # Glues together Audio::MPD and Audio::Scrobbler
  4 # Reads current playing track from MPD daemon
  5 # and submits it to your last.fm profile
  6
  7 # put yargla somewhere in your $PATH, chmod +x it
  8 # install Audio::MPD, Audio::Scrobbler and Config::Tiny (and Readonly)
  9 # with cpan -i or your distrib/os packages
 10 # p5-Audio-MPD, p5-Audio-Scrobbler and p5-Config-Tiny are in OpenBSD ports
 11
 12 # i wrote this script because mpdscribble kept segfaulting on my boxes,
 13 # scmpc has mad dependencies, and i like perl - the glue of the internet.
 14
 15 # the mandatory licence for each little piece of code - ISC licenced
 16 #
 17 # Copyright (c) 2008 Jasper Lievisse Adriaanse <jasper@humppa.nl>
 18 # Copyright (c) 2007 Landry Breuil <gaston@gcu.info>
 19 #
 20 # Permission to use, copy, modify, and distribute this software for any
 21 # purpose with or without fee is hereby granted, provided that the above
 22 # copyright notice and this permission notice appear in all copies.
 23 #
 24 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 25 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 26 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 27 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 28 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 29 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 30 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 31 #
 32 # TODO: daemonize on -d, log into file
 33 # cache tracks and submit by batchs
 34 #
 35 # sample config file:
 36 # ; MPD-host variables
 37 # [mpd]
 38 # host = localhost
 39 # port = 6600
 40 # ; Last.FM user variables
 41 # [lastfm]
 42 # user = foo
 43 # password = secret
 44 #
 45
 46 use Audio::MPD;
 47 use Audio::Scrobbler;
 48 use Config::Tiny;
 49 use Getopt::Long;
 50 use strict;
 51
 52 use vars qw($VERSION);
 53 $VERSION = "0.02";
 54
 55 sub usage;
 56
 57 # parse cmdline options
 58 my ($as_user, $as_pass, $mpd_host, $mpd_port) = ('', '', 'localhost', 6600);
 59 my ($help, $verbose, $config, $config_fh);
 60
 61 GetOptions('verbose' => \$verbose,
 62     'help' => \$help,
 63     'file:s' => \$config,
 64     'user:s' => \$as_user,
 65     'password:s' => \$as_pass,
 66     'mpd:s' => \$mpd_host,
 67     'nport:i' => \$mpd_port);
 68
 69 if ($as_user eq '' || $as_pass eq '' || $help) {
 70         usage() if (!defined($config));
 71 }
 72
 73 # parse the configuration file
 74 unless (!defined($config)) {
 75         if (! -e $config) {
 76                 print "Configuration file $config not found.\n";
 77                 usage();
 78         }
 79         $config_fh = Config::Tiny->read("$config");
 80 }
 81
 82 $mpd_host = $config_fh->{mpd}->{host};
 83 $mpd_port = $config_fh->{mpd}->{port};
 84 $as_user = $config_fh->{lastfm}->{user};
 85 $as_pass = $config_fh->{lastfm}->{password};
 86
 87 # validate the configuration values
 88 if ((!defined($mpd_host)) or ($mpd_host eq '')) {
 89         die "No valid host found in $config\n";
 90 } elsif ((!defined($mpd_port)) or ($mpd_port eq '')) {
 91         die "No valid port found in $config\n";
 92 } elsif ((!defined($as_user)) or ($as_user eq '')) {
 93         die "No valid user found in $config\n";
 94 } elsif ((!defined($as_pass)) or ($as_pass eq '')) {
 95         die "No valid password found in $config\n";
 96 }
 97
 98 # initialize handlers on both sides
 99 print "Connecting to MPD on $mpd_host:$mpd_port\n";
100 my $mpd = Audio::MPD->new($mpd_host, $mpd_port) || die "Failed to connect to MPD";
101
102 my $as = Audio::Scrobbler->new( cfg => {
103     verbose => $verbose,
104     progname => 'mdc', # use mpdscribble id atm
105     progver => '0.2.12',
106     username => $as_user,
107     password => $as_pass} );
108
109 $as->handshake() || die "Failed to handshake() with Last.fm";
110
111 my $status = $mpd->status();
112
113 while(1)
114 {
115     my $curid = $status->songid();
116     my $cursong = $mpd->current();
117
118     my ($artist,$title,$album,$length) = ($cursong->artist,$cursong->title,$cursong->album,$cursong->time);
119     my @t = localtime();
120     my $datestr = sprintf('%04d-%02d-%02d %02d:%02d:%02d', $t[5] + 1900, $t[4] + 1, @t[3, 2, 1, 0]);
121
122     print "[$datestr] submitting $artist - $album - $title\n";
123
124     my $r = $as->submit( {title => "$title", artist => "$artist", album => "$album", 'length' => "$length"});
125
126     if (!$r)
127     {
128         print "error:".$as->err.", trying to re-handshake and re-submit in 5s...\n";
129         sleep 5;
130         $as->handshake();
131         $as->submit( {title => "$title", artist => "$artist", album => "$album", 'length' => "$length"});
132     }
133
134     while ($curid == $status->songid)
135     {
136         if ($status->state eq "play")
137         { sleep 10; }
138         else
139         { sleep 60; }
140         $status = $mpd->status();
141     }
142 }
143
144 sub usage
145 {
146     print "$0 v. $VERSION\n";
147     print "usage: $0 -v|--verbose -h|--help -f|--file [config] \n-u|--user [Last.fm user] -p|--password [Last.fm password] -m|--mpd [MPD host]\n-n|--nport [MPD port]\n\n";
148     print "-u and -p are mandatory, unless -f is specified\n";
149     print "-m and -n defaults to localhost:6600\n";
150     print "-h shows usage\n\n";
151     print "values provided in the file provided with -f, overrule command line flags\n";
152     exit;
153 }
154
155 # (c) 2007 - G.C.U.