source: gutenbach/debian/lib/gutenbach @ 509c1f6

nodebathena
Last change on this file since 509c1f6 was 509c1f6, checked in by Jessica B. Hamrick <jhamrick@…>, 14 years ago

Move gutenbach package files into a subfolder

  • Property mode set to 100755
File size: 8.5 KB
RevLine 
[d988d9d]1#!/usr/bin/perl
[b0d9dda]2# Play the data on STDIN as an audio file
3#
[2e8b589]4# $Id: gutenbach-filter,v 1.26 2009/02/20 00:27:17 geofft Exp root $
5# $Source: /usr/local/bin/RCS/gutenbach-filter,v $
[b0d9dda]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
[c711ed9]15# apparently neither does Quentin Smith <quentin@mit.edu>
[b0d9dda]16
[d2fa590]17use strict;
18use warnings;
[ba06c7f]19use Image::ExifTool qw(ImageInfo);
20use File::Spec::Functions;
[d2fa590]21use File::Temp qw{tempfile tempdir};
[908107d]22use File::Basename qw(basename);
[ba06c7f]23use LWP::UserAgent;
24use Data::Dumper;
[f6c9736]25use IPC::Open2;
[d2fa590]26use English;
[ba06c7f]27
[5a05bf2]28use vars qw/ $host $queue $mixer $channel/;
[e20354c]29
[d2fa590]30require "/usr/lib/gutenbach/config/gutenbach-filter-config.pl" or die "Unable to load configuration";
[3a900ef]31
[ba06c7f]32my $ua = new LWP::UserAgent;
33
[0237078]34# This variable contains the pid of the child process (which runs
35# mplayer) once it has been forked, so that we can kill it on SIGTERM
36my $pid;
37
[d2fa590]38# Replace STDERR with a log file in /tmp.
39open(CUPS, ">&STDERR") or die "Unable to copy CUPS filehandle";
[ba06c7f]40close(STDERR);
[2e8b589]41open(STDERR, ">>", "/tmp/gutenbach.log") or warn "Couldn't open log: $!";
[ba06c7f]42
[d2fa590]43# Set the TERM environment (for the benefit of mplayer?)
44# I don't know why we do this --quentin
[ba06c7f]45$ENV{"TERM"}="vt100";
[b0d9dda]46
[148111e]47print STDERR "STDERR FROM SPOOL FILTER\n";
48
[d2fa590]49# CUPS provides us with these arguments:
50#
51# argv[1]
52# The job ID
53# argv[2]
54# The user printing the job
55# argv[3]
56# The job name/title
57# argv[4]
58# The number of copies to print
59# argv[5]
60# The options that were provided when the job was submitted
61# argv[6]
62# The file to print (first program only)
63#
64# The scheduler runs one or more of these programs to print any given
65# job. The first filter reads from the print file and writes to the
66# standard output, while the remaining filters read from the standard
67# input and write to the standard output. The backend is the last
68# filter in the chain and writes to the device.
[b0d9dda]69
[06133dc]70printf(STDERR "Got \@ARGV: %s\n", Dumper(\@ARGV));
71
[d2fa590]72my %arguments = (
[e20354c]73                 "job-id" => $ARGV[0],
[d2fa590]74                 user => $ARGV[1],
[e20354c]75                 "job-title" => $ARGV[2],
[d2fa590]76                 copies => $ARGV[3],
77                 options => {split(/[= ]/, $ARGV[4])},
78                 file => $ARGV[5],
79                );
80
81# If we weren't given a filename, we need to read from stdin. Since
82# mplayer really wants a file, let's write out to a temporary file
83# first.
84if (!$arguments{"file"}) {
85  my ($fh, $file) = tempfile("gutenbachXXXXX", UNLINK => 1); # Ask File::Temp for a safe temporary file
86  my $buf;
87  while (read(STDIN, $buf, 1024*1024)) { # Read 1M at a time and put it in the temporary file
88    print $fh $buf;
[06133dc]89  }
[d2fa590]90  close($fh);
[e20354c]91  $arguments{"file"} = $file;
[06133dc]92}
93
[d2fa590]94printf(STDERR "Got \%arguments: %s\n", Dumper(\%arguments));
[b011fb0]95
[80bbeea]96
[b0d9dda]97
[3778176]98my $status;
99if (exists($arguments{"options"}{"job-originating-host-name"})) {
[d1a3d62]100   
[3778176]101    $status = "User: ".$arguments{"user"}."\@".$arguments{"options"}{"job-originating-host-name"};
102} else {
[d1a3d62]103   
[3778176]104    $status = "User: ".$arguments{"user"};
105}
[2e8b589]106
[d2fa590]107# SIGHUP handler, in case we were aborted
[f014bc4]108sub clear_status {
[0237078]109  kill 15, $pid if $pid;
[d2fa590]110  die;
[f014bc4]111}
[0237078]112
[f014bc4]113$SIG{HUP} = \&clear_status;
[0237078]114$SIG{TERM} = \&clear_status;
[f014bc4]115
[ba06c7f]116# Read the metadata information from the file.
[e20354c]117my ($filepath) = $arguments{"file"};
[ba06c7f]118my ($fileinfo) = ImageInfo($filepath);
119my ($magic) = $fileinfo->{FileType};
[e20354c]120my ($tempdir);
121my ($newpath);
[b0d9dda]122
[ba06c7f]123if ($magic) {
[d2fa590]124  # $magic means that Image::ExifTool was able to identify the type of file
125  $status .= sprintf(" Filetype: %s.", $magic);
126  $status .= sprintf(" Filename: %s.", $arguments{"job-title"});
127
128  if (exists $fileinfo->{'Title'}) {
[d1a3d62]129   
[d2fa590]130    $status .= sprintf(" Title: %s.", $fileinfo->{'Title'});
131  }
132  foreach my $key (qw/Artist Album AlbumArtist/) {
133    if (exists $fileinfo->{$key}) {
[d1a3d62]134     
[d2fa590]135      $status .= sprintf(" %s: %s\n", $key, $fileinfo->{$key});
[ba06c7f]136    }
[d2fa590]137  }
138
[e20354c]139  $tempdir = tempdir();
[d2fa590]140  #awful hack -- geofft
141  #== -- quentin
142  # This code appears to create a new temporary directory and symlink
143  # the job file into the temporary directory under the original
144  # filename. I think this is because mplayer sometimes uses the file
145  # extension to identify a filetype.
[e20354c]146  $newpath = $tempdir . '/' . basename($arguments{"job-title"});
[d2fa590]147  symlink($filepath, $newpath);
148  $filepath = $newpath;
[ba06c7f]149}
[d2fa590]150elsif ($arguments{copies} == 42) {
151  # This is a flag that is set by jobs queued by split_playlist(); it tells us to not try to split the playlist again.
152  # Call resolve_external_reference to apply some heuristics to determine the filetype.
153  $filepath = resolve_external_reference($filepath, \%arguments);
154  if ($filepath =~ m|http://www\.youtube\.com/watch\?v=|) {
155    # YouTube URLs are resolved by the youtube-dl command.
156    # Launch youtube-dl
157    open(YTDL, "-|", "youtube-dl", "-g", $filepath) or die "Unable to invoke youtube-dl";
158    # Read the title (currently not doing so because youtube-dl doesn't know how to get the title.
159    my $title = ""; # <YTDL>
[d1a3d62]160    # Print the title to the status string.
161   
[d2fa590]162    $status .= " YouTube video $filepath. $title.";
163    # youtube-dl prints the URL of the flash video, which we pass to mplayer as a filename.
164    $filepath = <YTDL>;
165    chomp $filepath;
166  } else { # Doesn't appear to be a YouTube URL.
167    print STDERR "Resolved external reference to $filepath\n";
[d1a3d62]168   
[d2fa590]169    $status .= sprintf(" External: %s\n", $filepath);
170  }
[ba06c7f]171}
[d2fa590]172elsif (-T $filepath) { # If the file appears to be a text file, treat it as a playlist.
173  split_playlist($filepath, \%arguments);
[d1a3d62]174 
[d2fa590]175  # See http://www.cups.org/documentation.php/api-filter.html#MESSAGES
176  print CUPS "NOTICE: $status\n";
177  exit 0;
[7e27cc3]178}
[a041c73]179
[d1a3d62]180
[d2fa590]181print CUPS "NOTICE: $status\n";
182play_mplayer_audio($filepath, \%arguments);
[b0d9dda]183
[d2fa590]184# Remove the symlink we made earlier for the filetype.
[a041c73]185if ($magic) {
[d2fa590]186  unlink($newpath);
187  rmdir($tempdir);
[a041c73]188}
189
[b0d9dda]190# Play an external stream reference
[ba06c7f]191sub resolve_external_reference {
[d2fa590]192  # Retrieve those command line opts.
193  my ($filepath, $arguments) = @_;
194
[e20354c]195  my ($format, $uri, $userpass);
[d2fa590]196
197  open(FILE, "<", $filepath) or die "Couldn't open spool file";
198  if (<FILE> =~ /^(\S+)/) {
199    # Take the leading non-whitespace as a URL
200    $uri=$1;
201
202    if ($uri =~ m|http://www\.youtube\.com/watch\?v=|) {
203      return $uri;
[b281379]204    }
[b0d9dda]205
[d2fa590]206    # Fetch the URL with a HEAD request to get the content type
207    my $response = $ua->head($uri);
208
209    my $contenttype=($response->content_type() or "unknown");
210
211    if ($contenttype eq "audio/mpeg") { $format="MP3" }
212    elsif ($contenttype eq "application/x-ogg") { $format="OGG" }
213    elsif ($contenttype eq "application/ogg") { $format="OGG" }
214    elsif ($contenttype eq "audio/x-scpls") { $format="SHOUTCAST" }
215    else {
[d1a3d62]216   
[b0d9dda]217    }
[d2fa590]218  } else { # Unable to match the URL regex
[d1a3d62]219   
[d2fa590]220    # Return the existing path, in the hopes that mplayer knows what to do with it.
221    return $filepath;
222  }
223
224  if ($format eq "SHOUTCAST") {
[d1a3d62]225   
[d2fa590]226    return get_shoutcast($uri);
227  } elsif ($format eq "MP3") {
228  } elsif ($format eq "OGG") {
229  } else {
[d1a3d62]230   
[d2fa590]231  }
232  return $uri;
[b0d9dda]233}
234
[7e27cc3]235sub split_playlist {
[d2fa590]236  my ($file, $arguments) = @_;
237
238  my $i = 0;
239
240  open(FILE, "<", $filepath) or die "Couldn't open spool file";
241  while (<FILE>) {
242    chomp;
243    if (/^([^#]\S+)/) {
244      printf (STDERR "Found playlist line: %s\n", $_);
245      open(LPR, "|-", 'lpr', '-P'.$queue.'@localhost', '-#', '42', '-J', $arguments->{"job-title"}, '-o', 'job-priority=100');
246      print LPR $1;
247      close(LPR);
248      $i++;
[7e27cc3]249    }
[d2fa590]250  }
[d1a3d62]251 
[7e27cc3]252}
253
[53d7625]254# Process a Shoutcast playlist
255# get_shoutcast(URI)
256sub get_shoutcast {
257  my $uri = shift(@_);
[d2fa590]258
[ba06c7f]259  my $response = $ua->get($uri);
[e20354c]260  my (@titles, @uris);
[ba06c7f]261
262  foreach (split("\n", $response->content())) {
[53d7625]263      if (/^File\d+=(\S+)/) {
264          push(@uris, $1);
265      }
266      if (/^Title\d+=(.+)$/) {
267          push(@titles, $1);
268      }
269  }
[d2fa590]270
[53d7625]271  # choose a random server
[e20354c]272  my $server = int(rand scalar(@uris));
[53d7625]273  # print the name of the stream if available
[d1a3d62]274 
[53d7625]275  return $uris[$server];
276}
[b0d9dda]277
[ba06c7f]278sub play_mplayer_audio {
[d2fa590]279  my ($filepath, $opts) = @_;
[b0d9dda]280
[d2fa590]281
282  # fork for mplayer
[0237078]283  $pid = open(MP3STATUS, "-|");
[d2fa590]284  unless (defined $pid) {
285    return;
[b0d9dda]286  }
287
[d2fa590]288  if ($pid) { #parent
289    # Check if there were any errors
290    if ($_ = <MP3STATUS>) {
[d1a3d62]291     
[d2fa590]292      while (<MP3STATUS>) {
[d1a3d62]293       
[d2fa590]294      }
[d1a3d62]295     
[d2fa590]296    } else {
[d1a3d62]297     
[d2fa590]298    }
[d1a3d62]299    close(MP3STATUS);
[d2fa590]300  }
301  else { # child
302    # redirect STDERR to STDOUT
303    open STDERR, '>&STDOUT';
304    # make sure that mplayer doesn't try to intepret the file as keyboard input
305    close(STDIN);
306    open(STDIN, "/dev/null");
307
[4ac44b2]308    my @args = (qw|/usr/bin/mplayer -vo fbdev2 -zoom -x 1024 -y 768 -framedrop -nolirc -cache 512 -ao alsa -really-quiet |, $filepath);
[d2fa590]309    #print STDERR "About to exec: ", Dumper([@args]);
310    exec(@args) ||
311      die "Couldn't exec";
312  }
313}
Note: See TracBrowser for help on using the repository browser.