ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Audio-Play-MPG123/mpg123sh
Revision: 1.2
Committed: Tue Jul 16 14:44:04 2002 UTC (21 years, 10 months ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +7 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #!/usr/bin/perl
2    
3     # most of this file is dedicated to work around the braindead ReadLine
4     # implementation.
5    
6     #BEGIN { $ENV{PERL_RL} |= "Perl o=0" }
7     BEGIN { $ENV{PERL_RL} |= " o=0" }
8     BEGIN { eval "use Time::HiRes 'time'" }
9    
10     $RC = "$ENV{HOME}/.mpg123shrc";
11    
12     use Audio::Play::MPG123;
13     use Term::ReadLine;
14     use Fcntl; # required by MPG123 anyway
15     use Cwd;
16     use File::Basename;
17    
18     sub mglob {
19     eval { require File::Glob } ? &File::Glob::glob : glob $_[0];
20     }
21    
22     $|=1;
23    
24     do $RC;
25    
26     # contains tuples of the form [url, repeat]
27     @playlist=();
28     $p_url;
29     $p_repeat;
30    
31 root 1.2 $player = new Audio::Play::MPG123 mpg123args => ["-b4096"];
32 root 1.1
33     # do uri-style escaping PLUS escape space to · and back (sorry for that :()
34     sub uri_esc($) {
35     local $_ = shift;
36     s/([^\x21-\x24\x26-\x7e\xa0-\xb6\xb8-\xff])/sprintf "%c%02x", 0x25, ord($1)/ge;
37     s/%20/·/g;
38     $_;
39     }
40    
41     sub uri_unesc($) {
42     local $_ = shift;
43     s/·/%20/g;
44     s/%([0-9a-f][0-9a-f])/chr(hex($1))/gei;
45     $_;
46     }
47    
48     sub write_rc {
49     require Data::Dumper;
50     print "writing $RC... ";
51     open RC, ">$RC" or die "$RC: $!";
52     print RC Data::Dumper->Dump([\%conf], ['*conf']);
53     close RC;
54     print "ok\n";
55     }
56    
57     sub oconf {
58     my ($cmd, $value) = @_;
59     if ($cmd eq "log") {
60     if ($value =~ /^\s*$/) {
61     delete $conf{log};
62     } else {
63     $conf{log} = $value;
64     }
65     write_rc;
66     } elsif ($cmd eq "conf") {
67     while (my ($k, $v) = each %conf) {
68     printf "%-10s => %s\n", $k, $v;
69     }
70     } else {
71     print "unknown o command, use <log> or <conf>.\n";
72     }
73     }
74    
75     sub mp3log {
76     my ($cmd, @args) = @_;
77     if (defined $conf{log}) {
78     if (open LOG, ">>$conf{log}") {
79     print LOG "$cmd ", (join " ", map uri_esc($_), @args), "\n";
80     close LOG;
81     } else {
82     warn "$conf{log}: $!\n";
83     }
84     }
85     }
86    
87     my $current_url;
88     my $current_time;
89    
90     sub load_url {
91     my $url = shift;
92     if (defined $current_url && $current_time) {
93     mp3log "T", $current_url, sprintf ("%0.2f", time - $current_time);
94     }
95     $current_url = $url;
96     if (defined $url) {
97     $current_time = time;
98     $player->load($url);
99     }
100     }
101    
102     sub pause_url {
103     my $pause = shift;
104     if ($pause) {
105     mp3log "T", $current_url, sprintf ("%0.2f", time - $current_time) if defined $current_url;
106     $current_time = 0;
107     } else {
108     $current_time = time;
109     }
110     }
111    
112     sub next_song {
113     if ($p_repeat <= 0 && @playlist) {
114     my $p = shift @playlist;
115     push @playlist, $p if $p;
116     $p_url = $playlist[0][0];
117     $p_repeat = $playlist[0][1];
118     }
119     if ($p_url) {
120     $p_repeat--;
121     load_url($p_url);
122     mp3log("+", $p_url);
123     $player->stat;
124     }
125     }
126    
127     sub add_url($) {
128     my $url = shift;
129     push @playlist, [$url, 1];
130     next_song unless $player->state;
131     mp3log("a", $url);
132     }
133    
134     sub shuffle {
135     for (my $i = @playlist; $i--; ) {
136     my $j = int rand $i+1;
137     @playlist[$j, $i] = @playlist[$i, $j];
138     }
139     }
140    
141     END {
142     load_url();
143     }
144    
145     sub file_completion {
146     mglob "$_[0]*", GLOB_MARK|GLOB_TILDE;
147     }
148    
149     sub mfav {
150     my ($cmd, $value) = @_;
151     if ($cmd eq "f" or $cmd eq "l") {
152     if (defined $conf{log}) {
153     my %time;
154     local *LOG;
155     open LOG, "<$conf{log}" and do {
156     while (<LOG>) {
157     if (/^[al] (\S+)/) {
158     $time{$1} += 0.2;
159     } elsif (/^[ds] (\S+)/) {
160     $time{$1} -= 0.2;
161     } elsif (/^T (\S+) (\S+)/) {
162     $time{$1} += $2;
163     }
164     }
165     };
166     my @time;
167     my %base;
168     for (keys %time) {
169     next if -f uri_unesc $_;
170     my $base = basename $_;
171     $base{$base} += delete $time{$_};
172     }
173     while (my ($k, $v) = each %time) {
174     push @time, [$k, $v + $base{basename $k}];
175     }
176     $value ||= 5;
177     for (sort { $b->[1] <=> $a->[1] } @time) {
178     printf "%10.2f %s\n", $_->[1], $_->[0];
179     add_url uri_unesc $_->[0] if $cmd eq "f";
180     last if --$value <= 0;
181     }
182     } else {
183     print "this command requires a logfile\n";
184     }
185     } else {
186     print "unknown m command, use <f> or <l>.\n";
187     }
188     }
189    
190     $SIG{INT} =
191     $SIG{HUP} =
192     $SIG{PIPE} =
193     $SIG{TERM} = sub { exit };
194    
195     # terribly fool the Term::ReadLine packages..
196     sub Tk::DoOneEvent { }
197     sub Term::ReadLine::Tk::Tk_loop { &event_loop }
198     sub Term::ReadLine::Tk::register_Tk { }
199    
200     $rl = new Term::ReadLine "mpg123sh";
201     $rl->tkRunning(1);
202    
203     $rl->Attribs->{completion_function} = sub {
204     my ($word,$line,$pos) = @_;
205     $word ||= ""; $line ||= ""; $pos ||= 0;
206     $p = "";
207     $c = $word;
208     $rl->Attribs->{completer_terminator_character}="";
209     if ($pos==0) {
210     if ($word=~/^(l(?:oad)?|a(?:dd)?|cd?)(\S.*)?/) {
211     $p = $1." ";
212     $c = $2;
213     }
214     }
215     @r = file_completion uri_unesc($c);
216     if ($line =~ /^c/) {
217     @r = grep -d "$_/.", @r;
218     }
219     if (@r == 1) {
220     if (-f $r[0]) {
221     $rl->Attribs->{completer_terminator_character}=" ";
222     } elsif (-d $r[0]) {
223     $rl->Attribs->{completer_terminator_character}="/";
224     }
225     }
226     #print "\n<$word|$line|$pos> = ",join(":",@r)," #",scalar@r,"\n";
227     map $p.uri_esc($_),@r;
228     };
229    
230     my $pwr_state = 1;
231     sub pwr_change {
232     $SIG{PWR} = \&pwr_change;
233     $pwr_state = !$pwr_state;
234    
235     };
236     $SIG{PWR} = \&pwr_change;
237    
238     sub event_loop {
239     my $r;
240     my $rlin = $rl->IN;
241     # most ugly workaround for perl-readline bug
242     if ($rl->ReadLine eq "Term::ReadLine::Perl") {
243     require IO::Handle;
244     my $o = (fcntl $rlin,F_GETFL,0) & (O_APPEND | O_NONBLOCK);
245     fcntl $rlin,F_SETFL,$o | O_NONBLOCK;
246     my $eof = eof($rlin);
247     fcntl $rlin,F_SETFL,$o;
248     return unless $eof;
249     }
250     do {
251     next_song if $player->state == 0 && $pwr_state;
252     $player->stop if $player->state && !$pwr_state;
253     vec($r, fileno($rlin), 1) = 1;
254     vec($r, fileno($player->IN), 1) = 1;
255     (select $r, undef, undef, undef) < 0 and $r = "";
256     if (vec($r,fileno($player->IN),1)) {
257     $player->poll(0);
258     }
259     } until vec($r, fileno($rlin), 1);
260     }
261    
262     $player->statfreq(20);
263    
264     print "\nmpg123sh, version $Audio::Play::MPG123::VERSION\n";
265     print "enter 'help' for a command list\n\n";
266    
267     for(;;) {
268     my $prompt=fastcwd." ";
269     if ($player->state) {
270     $player->stat;
271     $prompt.=$player->title." ".$player->{frame}[2]."/".($player->{frame}[2]+$player->{frame}[3]);
272     } else {
273     $prompt.=$p_url;
274     }
275     $_=$rl->readline("$prompt> ");
276     if (/^l(?:oad)?\s*(.*?)\s*$/i) {
277     my $url = $player->canonicalize_url(uri_unesc $1);
278     load_url($url) or print "ERROR: ",$player->error,"\n";
279     mp3log("l", $url);
280     $player->stat;
281     } elsif (/^a(?:dd)?\s*(.*?)\s*$/i) {
282     for (mglob uri_unesc $1, GLOB_TILDE|GLOB_NOMAGIC) {
283     add_url $player->canonicalize_url($_);
284     }
285     } elsif (/^r(?:epeat)?\s*(\d+)\s*$/) {
286     $playlist[-1][1] = $1;
287     } elsif (/^p/i) {
288     $player->pause;
289     pause_url ($player->paused);
290 root 1.2 } elsif (/^DD$/) {
291     $p_repeat = 0;
292     $player->stop;
293     next_song;
294     unlink @{pop @playlist}[0];
295 root 1.1 } elsif (/^d(?:el)?\s*(\d*)\s*$/i) {
296     for (do {
297     if ($1) {
298     splice @playlist,$1-1,1;
299     } else {
300     $p_repeat = 0;
301     $player->stop;
302     next_song;
303     pop @playlist;
304     }
305     }) {
306     mp3log("d", $_->[0]);
307     }
308     } elsif (/^sh/i) {
309     shuffle;
310     } elsif (/^s/i) {
311     $p_repeat=0;
312     $player->stop;
313     mp3log("s", $playlist[0][0]);
314     next_song;
315     } elsif (/^c(?:d)?\s*(.*?)\s*$/i) {
316     chdir uri_unesc $1 or print "Unable to change to '$1': $!\n";
317     } elsif (/^j(?:ump)?\s*([0-9.]+)\s*$/i) {
318     eval { $player->jump(int($1/$player->tpf)) };
319     } elsif (/^o\s*([a-z]+)\s*(.*)/i) {
320     oconf($1,$2);
321     } elsif (/^m\s*([a-z]+)\s*(.*)/i) {
322     mfav($1,$2);
323     } elsif (/^q/i) {
324     last;
325     } elsif (/^i(nfo)?/i) {
326     print "\n";
327     if ($player->state) {
328     print "currently playing: ",$player->url,"\n";
329     printf "title: %-32s artist: %-30s\n",$player->title,$player->artist;
330     printf "album: %-32s year: %-30s\n",$player->album,$player->year;
331     printf "comment: %-32s genre: %-30s\n",$player->comment,$player->genre;
332     print "\n";
333     printf "MPEG %s layer %s, %d samples/s, %s, mode_extension is %d, %d bytes/frame\n".
334     "%d channels, %s, %s, emphasis is %s, %d kbit/s\n",
335     "I" x $player->type, $player->layer, $player->samplerate, $player->mode, $player->mode_extension,
336     $player->bpf, $player->channels, $player->copyrighted ? "copyrighted" : "not copyrighted",
337     $player->error_protected ? "error protection" : "no error protection", $player->emphasis ? "on" : "off",
338     $player->bitrate;
339     print "\n";
340     }
341     for (my $i=0; $i<=$#playlist; $i++) {
342     printf "%2d: %-30s repeat %d\n",$i+1,"'$playlist[$i][0]'",$playlist[$i][1];
343     }
344     print "\n";
345     } elsif (/^h(help)?/i) {
346     print <<EOF;
347    
348     load <file or url> loads the specified file and plays it immediately.
349     add <file or url> pushes the specified song to the end of the playlist
350     quit quits the program
351     info print information about the song and the playlist.
352     pause pause/unpause
353     stop stop current song (and play next)
354     shuffle shuffle currently selected songs once
355     cd <path> change current directory
356     del <num> remove song number <num> from the playlist
357     del remove the currently playing song
358 root 1.2 DD like del, but physically deleted the file(!!!)
359 root 1.1 jump <second> seek to the specified position
360     repeat <count> repeat the last recently added song <count times>
361     help this listing
362     o manipulate configuration
363     o conf show current configuration
364     o log <path> log all playing actions into file <path>
365     m playlist management (not much there, yet)
366     m f <num> add <num> favourite songs
367     m l <num> list <num> favourite songs
368    
369     - most commands can be shortened to a one-letter form
370     - most whitespace between command and arguments is optional
371    
372     EOF
373     } elsif (/\S/) {
374     print "unknown command, try 'help'\n";
375     }
376     }
377    
378