#!/usr/bin/perl
# yargla - Yet Another Rapid Glue Last.fm App v0.03
# Glues together Audio::MPD and Audio::Scrobbler
# Reads current playing track from MPD daemon
# and submits it to your last.fm profile

# put yargla somewhere in your $PATH, chmod +x it
# install Audio::MPD, Audio::Scrobbler and Config::Tiny (and Readonly)
# with cpan -i or your distrib/os packages
# p5-Audio-MPD, p5-Audio-Scrobbler and p5-Config-Tiny are in OpenBSD ports

# i wrote this script because mpdscribble kept segfaulting on my boxes,
# scmpc has mad dependencies, and i like perl - the glue of the internet.

# the mandatory licence for each little piece of code - ISC licenced
#
# Copyright (c) 2008,2010 Jasper Lievisse Adriaanse <jasper@humppa.nl>
# Copyright (c) 2007 Landry Breuil <gaston@gcu.info>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# TODO: - log into file
#	- cache tracks and submit by batches
#
# sample config file:
# ; MPD-host variables
# [mpd]
# host = localhost
# port = 6600
# ; Last.FM user variables
# [lastfm]
# user = foo
# password = secret
#

use Audio::MPD;
use Audio::Scrobbler;
use Config::Tiny;
use Getopt::Long;
use POSIX qw(setsid);
use strict;

use vars qw($VERSION);
$VERSION = "0.03";

sub usage;

# parse cmdline options
my ($as_user, $as_pass, $mpd_host, $mpd_port) = ('', '', 'localhost', 6600);
my ($help, $verbose, $config, $config_fh, $daemon);

GetOptions('daemon' => \$daemon,
    'verbose' => \$verbose,
    'help' => \$help,
    'file:s' => \$config,
    'user:s' => \$as_user,
    'password:s' => \$as_pass,
    'mpd:s' => \$mpd_host,
    'nport:i' => \$mpd_port);

if ($as_user eq '' || $as_pass eq '' || $help) {
	usage() if (!defined($config));
}

# parse the configuration file
unless (!defined($config)) {
	if (! -e $config) {
		print "Configuration file $config not found.\n";
		usage();
	}
	$config_fh = Config::Tiny->read("$config");
}

$mpd_host = $config_fh->{mpd}->{host};
$mpd_port = $config_fh->{mpd}->{port};
$as_user = $config_fh->{lastfm}->{user};
$as_pass = $config_fh->{lastfm}->{password};

# validate the configuration values
if ((!defined($mpd_host)) or ($mpd_host eq '')) {
	die "No valid host found in $config\n";
} elsif ((!defined($mpd_port)) or ($mpd_port eq '')) {
	die "No valid port found in $config\n";
} elsif ((!defined($as_user)) or ($as_user eq '')) {
	die "No valid user found in $config\n";
} elsif ((!defined($as_pass)) or ($as_pass eq '')) {
	die "No valid password found in $config\n";
}

# initialize handlers on both sides
print "Connecting to MPD on $mpd_host:$mpd_port\n";
my $mpd = Audio::MPD->new($mpd_host, $mpd_port) || die "Failed to connect to MPD";

my $as = Audio::Scrobbler->new( cfg => {
    verbose => $verbose,
    progname => 'mdc', # use mpdscribble id atm
    progver => '0.2.12',
    username => $as_user,
    password => $as_pass} );

$as->handshake() || die "Failed to handshake() with Last.fm";

my $status = $mpd->status();

# We've setup ourselves far enough, so daemonize if needed.
if ($daemon) {
    # Flush the buffer first.
    $| = 1;
    daemonize();
}

while(1)
{
    my $curid = $status->songid();
    my $cursong = $mpd->current();

    my ($artist,$title,$album,$length) = ($cursong->artist,$cursong->title,$cursong->album,$cursong->time);
    my @t = localtime();
    my $datestr = sprintf('%04d-%02d-%02d %02d:%02d:%02d', $t[5] + 1900, $t[4] + 1, @t[3, 2, 1, 0]);

    print "[$datestr] submitting $artist - $album - $title\n";

    my $r = $as->submit( {title => "$title", artist => "$artist", album => "$album", 'length' => "$length"});

    if (!$r)
    {
        print "error:".$as->err.", trying to re-handshake and re-submit in 5s...\n";
        sleep 5;
        $as->handshake();
        $as->submit( {title => "$title", artist => "$artist", album => "$album", 'length' => "$length"});
    }

    while ($curid == $status->songid)
    {
        if ($status->state eq "play")
        { sleep 10; }
        else
        { sleep 60; }
        $status = $mpd->status();
    }
}

# Setup a sane environment, then fork.
sub daemonize {
    eval {
        chdir '/';
        open STDIN, '/dev/null';
        open STDOUT, '>>/dev/null';
        open STDERR, '>>/dev/null';
        defined(my $pid = fork) or die "Can't fork: $!";
        exit if $pid;
        setsid; # Get new session ID.
        umask 0;
    };
    if ($@) {
        die "Failed to daemonize $0";
    };
}

sub usage
{
    print "$0 v. $VERSION\n";
    print "usage: $0 -d|--daemon -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";
    print "-u and -p are mandatory, unless -f is specified\n";
    print "-m and -n defaults to localhost:6600\n";
    print "-h shows usage\n";
    print "-d fork to the background\n\n";
    print "values provided in the file provided with -f, overrule command line flags\n";
    exit;
}

# (c) 2007 - G.C.U.
