source: debian/lib/sipbmp3-filter @ 2babe17

debianmacno-cupsnodebathenaweb
Last change on this file since 2babe17 was 2babe17, checked in by Jessica Hamrick <jhamrick@…>, 16 years ago

Installing the debian package should make the printing of music files
to the lprmp3 printer work. Still need to add in support for uninstalling
the package and using debconf (so users can add in their own configuration)

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