1#!/usr/bin/perl -w
2
3use 5.006;
4use strict;
5
6=head1 NAME
7
8scrobbler-helper - submit a track to AudioScrobbler
9
10=head1 SYNOPSIS
11
12 scrobbler-helper [-nv] [-e encoding] [-f configfile] -P progname
13   -V progver title artist album year comment genre length
14
15=head1 DESCRIPTION
16
17The B<scrobbler-helper> utility uses the C<Audio::Scrobbler> module to
18submit a single track's information to Last.fm's AudioScrobbler -
19http://www.audioscrobbler.com/.  It requires the program (plug-in) name
20and version to be specified on the command line, and also requires all
21seven track attributes, although some of them may be omitted by supplying
22empty strings.
23
24The following command-line options are recognized:
25
26=over 4
27
28=item -e encoding
29
30Specify the character encoding of the track info, if it is neither UTF-8
31nor the one specified via B<default_encoding> in the configuration file.
32
33=item -f configfile
34
35Specify a different configuration file, not ~/.scrobbler-helper.conf.
36
37=item -n
38
39Do not actually perform the handshake and submission
40(sets the C<Audio::Scrobbler> B<"fake"> option).
41
42=item -P progname
43
44Specify the name of the AudioScrobbler plug-in submitting the data.
45This option is B<mandatory>!
46
47=item -v
48
49Verbose operation - display diagnostic messages to the standard output
50(sets the C<Audio::Scrobbler> B<"verbose"> option).
51
52=item -V progver
53
54Specify the version of the AudioScrobbler plug-in submitting the data.
55This option is B<mandatory>!
56
57=back
58
59Besides the command line, the B<scrobbler-helper> utility also retrieves
60information from a per-user configuration file, usually
61~/.scrobbler-helper.conf; it is a INI-style file, which must contain a
62secion named B<"global">.  The following variables are recognized, with
63B<username> and B<password> being mandatory:
64
65=over 4
66
67=item * default_encoding
68
69The encoding to assume for the track info, if none is supplied with
70the B<-e> command-line option.  If neither B<-e> is given on the command
71line nor B<default_encoding> specified in the configuration file, the
72B<scrobbler-helper> utility assumes UTF-8.
73
74=item * fix_track_name
75
76A boolean flag specifying whether to do some trivial fixes on the song
77name before submitting it.  Currently, this only removes a "DD. "
78sequence at the start of the name, where 'D' is a digit.
79
80The values C<on>, C<true>, C<yes>, and C<1> are considered to be true.
81
82=item * password
83
84The password for the AudioScrobbler account.
85
86=item * username
87
88The username for the AudioScrobbler account.
89
90=back
91
92  [global]
93  username=jrandomlistener
94  password=mylittlesecret
95  # Optional (the default is UTF-8)
96  default_encoding=windows-1251
97  # Optional (the default is "no")
98  fix_track_name=yes
99
100=cut
101
102use Config::IniFiles;
103use Encode;
104use Getopt::Std;
105
106use Audio::Scrobbler;
107
108sub is_true($);
109
110my %infovars = (
111	'cmdopts'	=> [ qw/P V/ ],
112	'cmdline'	=> [ qw/title artist album year comment genre length/ ],
113	'global'	=> [ qw/username password/ ],
114	'global_nc'	=> [ qw/default_encoding fix_track_name/ ],
115);
116my $verbose = 0;
117
118MAIN:
119{
120	my %info;
121	my (%opts, %cfg) = ();
122	my ($configfname) = "$ENV{HOME}/.scrobbler-helper.conf";
123	my ($scrob);
124
125	getopts('nve:f:P:V:', \%opts) or die "Parsing options: $!\n";
126	$configfname = $opts{'f'} if $opts{'f'};
127	$verbose = 1 if $opts{'v'};
128	$info{'verbose'} = $verbose;
129	$info{'fake'} = 1 if $opts{'n'};
130
131	@info{qw/progname progver encoding/} = @opts{@{$infovars{'cmdopts'}}};
132	if (!($info{'progname'} && $info{'progver'})) {
133		die "Must specify program name (-P) and version (-V)!\n";
134	}
135
136	if (@ARGV != @{$infovars{'cmdline'}}) {
137		die 'Need '.@{$infovars{'cmdline'}}.' args: '.
138		    join(', ', @{$infovars{'cmdline'}}).".\n";
139	}
140	@info{@{$infovars{'cmdline'}}} = @ARGV;
141	map { s/^\s+//; s/\s+$//; } @info{@{$infovars{'cmdline'}}};
142
143	if (!tie %cfg, 'Config::IniFiles',
144	    (-file => $configfname, -allowcontinue => 1)) {
145		my $err = join "\n", "Could not read $configfname: $!",
146		    @Config::IniFiles::errors;
147		die "$err\n";
148	}
149
150	@info{@{$infovars{'global'}}, @{$infovars{'global_nc'}}} =
151	    @{$cfg{'global'}}{@{$infovars{'global'}},
152	    @{$infovars{'global_nc'}}};
153	for (@{$infovars{'global'}}) {
154		die 'Missing variables in the config file, need at least '.
155		    join(', ', @{$infovars{'global'}}).".\n"
156		    unless defined($info{$_});
157	}
158
159	# Recode the track info into UTF-8
160	if (defined($info{'default_encoding'}) &&
161	    (!defined($info{'encoding'}) || $info{'encoding'} eq '')) {
162		$info{'encoding'} = $info{'default_encoding'};
163	}
164	if (defined($info{'encoding'}) && $info{'encoding'} !~ /^utf-?8$/i) {
165		print "RDBG recoding track info from $info{encoding} to UTF8\n"
166		    if $verbose;
167		foreach (@{$infovars{'cmdline'}}) {
168			$info{$_} = decode($info{'encoding'}, $info{$_});
169		}
170	}
171
172	# Fix up the track name if requested
173	if (defined($info{'fix_track_name'}) &&
174	    is_true($info{'fix_track_name'})) {
175		$info{'title'} =~ s/^\d\d?\. //;
176		print "RDBG fixed up the track name to $info{title}\n"
177		    if $verbose;
178	}
179
180	# Rock'n'roll!
181	$scrob = new Audio::Scrobbler('cfg' => \%info) or
182	    die "Could not create an Audio::Scrobbler object\n";
183	$scrob->handshake() or
184	    die "Scrobbler: ".$scrob->err()."\n";
185	print "RDBG handshake successful, it seems\n" if $verbose;
186	$scrob->submit(\%info) or
187	    die "Scrobbler submit: ".$scrob->err()."\n";
188	print "RDBG submision successful, it seems\n" if $verbose;
189}
190
191sub is_true($)
192{
193	my $s = lc $_[0];
194
195	return ($s eq '1' || $s eq 'on' || $s =~ /^[ty]/);
196}
197
198=head1 TODO
199
200=over 4
201
202=item *
203
204Command-line options, so people don't have to submit everything...
205
206=item *
207
208Storing and caching of unsuccessful submissions for later retrying.
209
210=back
211
212=head1 SEE ALSO
213
214B<Audio::Scrobbler>
215
216=over 4
217
218=item * http://www.last.fm/
219
220=item * http://www.audioscrobbler.com/
221
222=item * http://www.audioscrobbler.net/
223
224=back
225
226The home site of the C<Audio::Scrobbler> module is
227http://devel.ringlet.net/audio/Audio-Scrobbler/
228
229=head1 AUTHOR
230
231Peter Pentchev, E<lt>roam@ringlet.netE<gt>
232
233=head1 COPYRIGHT AND LICENSE
234
235Copyright (C) 2005, 2006 by Peter Pentchev.
236
237This library is free software; you can redistribute it and/or modify
238it under the same terms as Perl itself, either Perl version 5.8.7 or,
239at your option, any later version of Perl 5 you may have available.
240
241$Id: scrobbler-helper 88 2006-01-02 09:16:32Z roam $
242
243=cut
244
245