source: gutenbach/debian/lib/sipbmp3-filter @ 014f817

debianmacno-cupsnodebathenaweb
Last change on this file since 014f817 was 014f817, checked in by Geoffrey Thomas <geofft@…>, 15 years ago

Don't allow video decoding termination to cut off audio at the end.

  • Property mode set to 100755
File size: 8.1 KB
Line 
1#!/usr/athena/bin/perl
2# Play the data on STDIN as an audio file
3#
4# $Id: sipbmp3-filter,v 1.26 2009/02/20 00:27:17 geofft Exp root $
5# $Source: /usr/local/bin/RCS/sipbmp3-filter,v $
6#
7# TODO
8# ----
9# Make this structured code. It's a mess.
10# Repeat what we just played for EXT files too
11# Support HTTP Auth on ogg streams
12# License, cleanup and package
13#
14# Jered Floyd <jered@mit.edu> takes very little credit for this code
15# apparently neither does Quentin Smith <quentin@mit.edu>
16
17use Image::ExifTool qw(ImageInfo);
18use File::Spec::Functions;
19use File::Temp qw{tempdir};
20use File::Basename qw(basename);
21use LWP::UserAgent;
22use Data::Dumper;
23
24my $zephyr_class = "sipb-auto";
25my $host = "zsr";
26
27# Configuration
28my $config_file = "/etc/sipbmp3-filter-config.pl";
29if (-r $config_file) {
30    # Inline the configuration file
31    local $/;
32    my $fh;
33    open $fh, $config_file;
34    eval <$fh>;
35}
36
37my $ua = new LWP::UserAgent;
38
39close(STDERR);
40open(STDERR, ">>", "/tmp/sipbmp3.log") or warn "Couldn't open log: $!";
41
42$ENV{"TERM"}="vt100";
43
44print STDERR "STDERR FROM SPOOL FILTER\n";
45
46# set real uid to be effective uid
47$< = $>;
48
49# Select the correct output device and set the volume
50#system("amixer -q set Headphone 100\% unmute");
51
52# The command line we get from lpd is (no spaces between options and args):
53#  -C lpr -C class
54#  -A LPRng internal identifier
55#  -H originating host
56#  -J lpr -J jobname (default: list of files)
57#  -L lpr -U username
58#  -P logname
59#  -Q queuename (lpr -Q)
60#  -Z random user-specified options
61#  -a printcap af (accounting file name)
62#  -d printcap sd entry (spool dir)
63#  -e print job data file name (currently being processed)
64#  -h print job originiating host (same as -H)
65#  -j job number in spool queue
66#  -k print job control file name
67#  -l printcap pl (page length)
68#  -n user name (same as -L)
69#  -s printcap sf (status file)
70#  -w printcap pw (page width)
71#  -x printcap px (page x dimension)
72#  -y printcap py (page y dimension)
73# accounting file name
74
75printf(STDERR "Got \@ARGV: %s\n", Dumper(\@ARGV));
76
77my %opts;
78
79my @NEWARGV;
80
81foreach my $arg (@ARGV) {
82  if ($arg =~ m/^-([a-zA-Z])(.*)$/) {
83    $opts{$1} = $2;
84  } else {
85    push @NEWARGV, @ARGV;
86  }
87}
88
89@ARGV = @NEWARGV;
90
91printf(STDERR Dumper(\%opts));
92
93# Status messages at start of playback
94open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c '. $zephyr_class .' -i ' .
95  'sipbmp3@'.$host.' -s "SIPB LPR music spooler"');
96
97# For the Now Playing remctl command
98open(STATUS, '>', '/var/run/sipbmp3/status') or die("Can't open status file /var/run/sipbmp3/status");
99
100print(ZEPHYR "$opts{'n'}\@$opts{'H'} is playing:\n");
101print(STATUS "User: $opts{'n'}\@$opts{'H'}\n");
102
103# SIGHUP handler
104sub clear_status {
105    # Possible race condition if the previous status is still going
106    open(STA, '>', '/var/run/sipbmp3/status');
107    close(STA);
108    open(ZEPH, '|/usr/athena/bin/zwrite -d -n -c '. $zephyr_class .' -i '.
109        'sipbmp3@'.$host.' -s "SIPB LPR music spooler"');
110    print(ZEPH "Playback aborted.\n");
111    close(ZEPH);
112    die;
113}
114$SIG{HUP} = \&clear_status;
115
116# So, the file we're currently processing is "-d/-e".
117
118# Read the metadata information from the file.
119my ($filepath) = catfile($opts{'d'}, $opts{'e'});
120my ($fileinfo) = ImageInfo($filepath);
121my ($magic) = $fileinfo->{FileType};
122
123if ($magic) {
124    printf(ZEPHYR "%s file %s\n", $magic, $opts{'J'});
125    printf(STATUS "Filetype: %s\n", $magic);
126    printf(STATUS "Filename: %s\n", $opts{'J'});
127    if (exists $fileinfo->{'Title'}) {
128        printf(ZEPHYR "\@b{%s}\n", $fileinfo->{'Title'}) if exists $fileinfo->{'Title'};
129        printf(STATUS "Title: %s\n", $fileinfo->{'Title'});
130    }
131    foreach my $key (qw/Artist Album AlbumArtist/) {
132        if (exists $fileinfo->{$key}) {
133            printf(ZEPHYR "%s\n", $fileinfo->{$key}) if exists $fileinfo->{$key};
134            printf(STATUS "%s: %s\n", $key, $fileinfo->{$key});
135        }
136    }
137    my $tempdir = tempdir();
138    $opts{'J'} =~ s/_mp3/.mp3/; #awful hack -- geofft
139    my $newpath = $tempdir . '/' . basename($opts{'J'});
140    symlink($filepath, $newpath);
141    $filepath = $newpath;
142}
143elsif ($opts{'C'} eq 'Z') {
144    $filepath = resolve_external_reference($filepath, \%opts);
145    print STDERR "Resolved external reference to $filepath\n";
146    printf(ZEPHYR "%s\n", $filepath);
147    printf(STATUS "External: %s\n", $filepath);
148}
149elsif (-T $filepath) {
150    split_playlist($filepath, \%opts);
151    close(ZEPHYR);
152    close(STATUS);
153    exit 0;
154}
155
156#printf(STDERR "Job priority %s\n", $opts{'C'}) if $opts{'C'} eq 'Z';
157#printf(ZEPHYR "Job priority %s\n", $opts{'C'}) if ($opts{'C'} && ($opts{'C'} ne 'A'));
158close(ZEPHYR);
159close(STATUS);
160play_mplayer_audio($filepath, \%opts);
161
162if ($magic) {
163    unlink($newpath);
164    rmdir($tempdir);
165}
166
167# Play an external stream reference
168sub resolve_external_reference {
169    # Retrieve those command line opts.
170    my ($filepath, $opts) = @_;
171
172    my $format, $uri, $userpass;
173
174    if (<STDIN> =~ /^(\S+)/) {
175        $uri=$1;
176
177        my $response = $ua->head($uri);
178       
179        $contenttype=($response->content_type() or "unknown");
180       
181        if ($contenttype eq "audio/mpeg") { $format="MP3" }
182        elsif ($contenttype eq "application/x-ogg") { $format="OGG" }
183        elsif ($contenttype eq "application/ogg") { $format="OGG" }
184        elsif ($contenttype eq "audio/x-scpls") { $format="SHOUTCAST" }
185        else {
186            print ZEPHYR
187                "Unknown Content-Type $contenttype for URI $uri\n";
188        }
189    } else {
190        print ZEPHYR "Couldn't read URI for external reference\n";
191        return $filepath;
192    }
193
194    if ($format eq "SHOUTCAST") {
195        print ZEPHYR "Shoutcast playlist...\n";
196        #Don't close ZEPHYR yet, will print the name of the stream if available
197        return &get_shoutcast($uri);
198    } elsif ($format eq "MP3") {
199    } elsif ($format eq "OGG") {
200    } else {
201      print ZEPHYR "Unrecognized stream format: $format\n";
202    }
203    return $uri;
204}
205
206sub split_playlist {
207    my ($file, $opts) = @_;
208
209    my $i = 0;
210   
211    while (<STDIN>) {
212        chomp;
213        if (/^([^#]\S+)/) {
214            printf (STDERR "Found line: %s\n", $_);
215            open(LPR, "|-", qw/mit-lpr -Psipbmp3@localhost -CZ/, '-J'.$opts->{J});
216            print LPR $1;
217            close(LPR);
218        $i++;
219        }
220    }
221    printf(ZEPHYR "Playlist containing %d valid entries, split into separate jobs.\n", $i);
222}
223
224# Process a Shoutcast playlist
225# get_shoutcast(URI)
226sub get_shoutcast {
227  my $uri = shift(@_);
228 
229  my $response = $ua->get($uri);
230
231  foreach (split("\n", $response->content())) {
232      if (/^File\d+=(\S+)/) {
233          push(@uris, $1);
234      }
235      if (/^Title\d+=(.+)$/) {
236          push(@titles, $1);
237      }
238  }
239 
240  # choose a random server
241  $server = int(rand scalar(@uris));
242  # print the name of the stream if available
243  print ZEPHYR "$titles[$server]\n";
244  return $uris[$server];
245}
246
247sub play_mplayer_audio {
248    my ($filepath, $opts) = @_;
249
250    # Prepare to write status:
251    open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c '.$zephyr_class.' -i ' .
252         'sipbmp3@'.$host.' -s "SIPB LPR music spooler"');
253   
254    # fork for mpg123
255    my $pid = open(MP3STATUS, "-|");
256    unless (defined $pid) {
257        print ZEPHYR "Couldn't fork: $!\n";
258        close(ZEPHYR);
259        return;
260    }
261   
262    if ($pid) { #parent
263        # Check if there were any errors
264        if ($_ = <MP3STATUS>) {
265            print ZEPHYR "Playback completed with the following errors:\n";
266            print ZEPHYR $_;
267            while (<MP3STATUS>) {
268                print ZEPHYR $_;
269            }
270        } else {
271            print ZEPHYR "Playback completed successfully.\n";
272        }
273        close(MP3STATUS) || print ZEPHYR "mplayer exited $?\n";
274       
275        close(ZEPHYR);
276        open(STATUS, '>', '/var/run/sipbmp3/status');
277        close(STATUS);
278    }
279  else { # child
280      # redirect STDERR to STDOUT
281      open STDERR, '>&STDOUT';
282      # make sure that mplayer doesn't try to intepret the file as keyboard input
283      close(STDIN);
284      open(STDIN, "/dev/null");
285      #print STDERR Dumper([qw|/usr/bin/mplayer -nolirc -ao alsa -quiet|, $filepath]);
286      my @args = (qw|/usr/bin/mplayer -novideo -vo null -nolirc -ao alsa -cache 512 |, $filepath);
287      #print STDERR "About to exec: ", Dumper([@args]);
288      exec(@args) ||
289          die "Couldn't exec";
290  }
291}
292
293# ID3 comments often have useless crap because tools like iTunes were
294# written by drooling idiots
295sub filter_comment {
296  my $comment = shift(@_);
297
298  if ($comment =~ /^engiTunes_CDDB/) {
299    return undef;
300  }
301  return $comment;
302}
303
304
Note: See TracBrowser for help on using the repository browser.