1# XMMSInfo.pm
2# this should be in a separate file...
3package XMMSInfo;
4
5# should write docs...
6
7use strict;
8use POSIX;
9use IO::File;
10use MP3::Info;
11use vars qw($PIPE $STATUS @ISA @EXPORT);
12
13@ISA = qw(Exporter);
14@EXPORT = qw($STATUS);
15
16$PIPE = '/tmp/xmms-info';
17$STATUS = {
18	-1	=> 'Fatal error',
19	0	=> 'Not running',
20	1	=> 'Stopped',
21	2	=> 'Playing',
22	3	=> 'Paused',
23};
24
25sub new {
26	my($class) = shift;
27
28	my($self) = {};
29	bless($self, $class);
30
31	$self->die("Try calling some methods first. \$obj->getInfo() is currently the only one available...");
32
33	$self;
34}
35
36sub parseArgs {
37	my($self) = shift;
38
39	$#_ % 2 || return $self->die("Invalid number of arguments");
40	for(my $i = 0; $i < $#_; $i += 2) {
41		my($k, $v) = ($_[$i], $_[$i + 1]);
42		$self->{Args}->{'.'.uc($k)} = $v;
43	}
44
45	1;
46}
47
48sub die {
49	my($self) = shift;
50	$self->setError(shift);
51	$self->setStatus(-1);
52	undef;
53}
54
55sub round {
56	my($d) = shift;
57	return $d unless $d =~ /^(\d+)\.(\d)/;
58	$d = $1;
59	$d++ if $2 >= 5;
60	$d;
61}
62
63sub getInfo {
64	my($self) = shift;
65
66	$self->parseArgs(@_) || return;
67
68	my($f) = $self->argPipe || $PIPE;
69	-r $f || return $self->setStatus(0);
70	my($fh) = IO::File->new($f) ||
71		return $self->die("Can't open $f for reading: $!");
72
73	while(<$fh>) {
74		chomp;
75		next unless /^(.+?): (.+)$/;
76		if($1 eq 'Status') {
77			$self->setStatus($2);
78		} else {
79			$self->{Info}->{'.'.uc($1)} = $2;
80		}
81	}
82	$fh->close;
83
84	return $self->die("Invalid input") unless $self->{Info}->{'.INFOPIPE PLUGIN VERSION'};
85
86	my($t) = get_mp3tag($self->infoFile) || return $self->die("Can't read ID3 tag: ". $self->infoFile);
87	my($i) = get_mp3info($self->infoFile) || return $self->die("Can't read MP3 info: ". $self->infoFile);
88
89	my($k, $v);
90	while(($k, $v) = (each(%$t), each(%$i))) {
91		$self->{Info}->{'.'.$k} = $v;
92	}
93
94	$self->getStatus;
95}
96
97sub setStatus {
98	my($self, $s) = @_;
99	if($s =~ /^*\d+$/) {
100		$self->{Status}->{'.STATUS'} = $s;
101		$self->{Status}->{'.STATUSSTRING'} = $STATUS->{$s};
102	} else {
103		foreach my $k (keys %$STATUS) {
104			my($v) = $STATUS->{$k};
105			if($s eq $v) {
106				$self->{Status}->{'.STATUS'} = $k;
107				$self->{Status}->{'.STATUSSTRING'} = $s;
108				return $self->getStatus;
109			}
110		}
111		die "HELP";
112	}
113
114	$self->getStatus;
115}
116
117sub setError {
118	shift->{Status}->{'.ERROR'} = pop;
119}
120
121sub getStatus {
122	shift->{Status}->{'.STATUS'};
123}
124
125sub getStatusString {
126	shift->{Status}->{'.STATUSSTRING'};
127}
128
129sub getError {
130	shift->{Status}->{'.ERROR'};
131}
132
133sub isFatalError {
134	shift->getStatus == -1;
135}
136
137sub isXmmsRunning {
138	shift->getStatus > 0;
139}
140
141sub isPlaying {
142	shift->getStatus == 2;
143}
144
145sub isPaused {
146	shift->getStatus == 3;
147}
148
149sub isStopped {
150	shift->getStatus == 1;
151}
152
153sub argPipe {
154	shift->{Args}->{'.PIPE'};
155}
156
157sub infoPlayListItems {
158	shift->{Info}->{'.TUNES IN PLAYLIST'};
159}
160
161sub infoCurrentItemInPlaylist {
162	shift->{Info}->{'.CURRENTLY PLAYING'};
163}
164
165sub infoTimeNow {
166	shift->{Info}->{'.POSITION'};
167}
168
169sub infoTimeTotal {
170	shift->{Info}->{'.TIME'};
171}
172
173sub infoSecondsTotal {
174	POSIX::ceil (shift->{Info}->{'.SECS'});
175}
176
177sub infoSecondsNow {
178	my($self) = shift;
179	my($s) = $self->infoTimeNow;
180	die "HELP" unless $s =~ /^(\d+):(\d+)$/;
181	$1 * 60 + $2;
182}
183
184sub infoMinutesTotal {
185	shift->{Info}->{'.MM'};
186}
187
188sub infoMinutesNow {
189	my($self) = shift;
190	my($s) = $self->infoTimeNow;
191	die "HELP" unless $s =~ /^(\d+):\d+$/;
192	$1;
193}
194
195sub infoSecondsTotalLeftover {
196	shift->{Info}->{'.SS'};
197}
198
199sub infoSecondsNowLeftover {
200	my($self) = shift;
201	$self->infoSecondsNow - ($self->infoMinutesNow * 60);
202}
203
204sub infouSecTotal {
205	shift->{Info}->{'.USECTIME'};
206}
207
208sub infouSecNow {
209	shift->{Info}->{'.USECPOSITION'};
210}
211
212sub infoPercentage {
213	my($self) = shift;
214	my($p) = ($self->infouSecNow / $self->infouSecTotal) * 100;
215	round($p);
216}
217
218sub infoTitle {
219	shift->{Info}->{'.TITLE'};
220}
221
222sub infoFile {
223	shift->{Info}->{'.FILE'};
224}
225
226sub infoArtist {
227	shift->{Info}->{'.ARTIST'};
228}
229
230sub infoAlbum {
231	shift->{Info}->{'.ALBUM'};
232}
233
234sub infoYear {
235	shift->{Info}->{'.YEAR'};
236}
237
238sub infoComment {
239	shift->{Info}->{'.COMMENT'};
240}
241
242sub infoGenre {
243	shift->{Info}->{'.GENRE'};
244}
245
246sub infoVersion {
247	shift->{Info}->{'.VERSION'};
248}
249
250sub infoLayer {
251	shift->{Info}->{'.LAYER'};
252}
253
254sub infoIsStereo {
255	shift->{Info}->{'.STEREO'};
256}
257
258sub infoIsVbr {
259	shift->{Info}->{'.VBR'};
260}
261
262sub infoBitrate {
263	shift->{Info}->{'.BITRATE'};
264}
265
266sub infoFrequency {
267	shift->{Info}->{'.FREQUENCY'};
268}
269
270sub infoSizeBytes {
271	shift->{Info}->{'.SIZE'};
272}
273
274sub infoSize {
275	shift->infoSizeBytes;
276}
277
278sub infoSizeKiloBytes {
279	round(shift->infoSizeBytes / 1024);
280}
281
282sub infoSizeMegaBytes {
283	round(shift->infoSizeKiloBytes / 1024);
284}
285
286sub infoIsCopyright {
287	shift->{Info}->{'.COPYRIGHT'};
288}
289
290sub infoIsPadded {
291	shift->{Info}->{'.PADDING'};
292}
293
294sub infoFrames {
295	shift->{Info}->{'.FRAMES'};
296}
297
298sub infoFramesLength {
299	shift->{Info}->{'.FRAMESLENGTH'};
300}
301
302sub infoVbrScale {
303	shift->{Info}->{'.VBR_SCALE'};
304}
305
3061;
307
308# EOF
309