#!/usr/bin/perl # # Connect to a shoutcast stream and pull out the metadata, pass the mp3 data out to stdout. # metadata is printed to stderr use IO::Socket; use strict; use warnings; use URI; my $buf; stream: while (@ARGV > 0) { my $u = URI->new(shift(@ARGV)); my ($host, $port, $path) = ($u->host, $u->port, $u->path || "/"); my $s = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $port, Proto => 'tcp'); if ($s->connected) { print STDERR "Connected to http://$host:$port (Requesting $path)\n"; print $s "GET $path HTTP/1.0\r\n"; print $s "Icy-Metadata: 1\r\n"; print $s "Host: $host\r\n\r\n"; $s->flush(); # Parse the IceCast/HTTP Response Header my %headers; my $buf = ""; my $n = 0; until ($buf =~ m/\r\n\r\n/s) { my $m = sysread($s, $buf, 4096, $n); unless ($m) { warn("stream pooped: $!"); $s->close(); next stream; } $n += $m; } while ($buf =~ s/^(.*?)\r?\n//s) { last if $1 eq ''; my ($k,$v) = split(/: */, $1); $headers{lc($k)} = $v; } if (!exists($headers{"icy-metaint"})) { my $n; print STDERR "WARNING: No metadata available?\n"; syswrite(STDOUT, $buf, 8192); syswrite(STDOUT, $buf, $n) while ($n = sysread($s,$buf,8192)); } else { my ($b); my $int = $headers{"icy-metaint"}; #print STDERR "Len: $int\n"; while ($n = sysread($s,$b,8192)) { if ($buf) { $b = $buf . $b; $n += length($buf); $buf = undef; } $int -= $n; if($int < 0) { # need atleast 1 byte of data to find length of metadata my $metalen = ord(substr($b,$n + $int, 1)) * 16; die "unexpectedly long metadata length: $metalen" if $metalen > 800; #print STDERR "Length: $metalen ($n)\n"; if (($metalen > 0) && (($int * -1) > $metalen)) { # XXX: " + 1" so we skip the length byte my $metadata = substr($b, $n + $int + 1, $metalen); my %song; #print STDERR "m: *$metadata*\n"; foreach (split(";", $metadata)) { my ($k,$v) = split("=", $_, 2); next unless $k && $v; $song{lc($k)} = substr($v,1,-1); } if (exists($song{"streamtitle"})) { printf STDERR "[%s] %s\n", $song{streamurl} || "unknown source", $song{streamtitle}; } #print STDERR "Song2: $metadata"; } # delete metadata from stream substr($b, $n + $int, $metalen + 1) = ''; $int += $headers{"icy-metaint"} + $metalen + 1; } syswrite(STDOUT, $b); } } } last; }