~ubuntu-branches/ubuntu/jaunty/icecream/jaunty

« back to all changes in this revision

Viewing changes to icecream

  • Committer: Bazaar Package Importer
  • Author(s): Cristian Greco
  • Date: 2008-04-29 09:43:16 UTC
  • mfrom: (1.1.2 upstream) (3.1.1 hardy)
  • Revision ID: james.westby@ubuntu.com-20080429094316-srcxihd3j14nb6tr
Tags: 1.3-1
* New Maintainer (Closes: #431684)
* New upstream release (including a patch of mine)
  - Parse .m3u playlists starting with a comment (Closes: #396892)
  - Fix wrong requests to server (Closes: #396893)
  - Support direct stream URLs (Closes: #319342)
  - New features and bugfixes.
  - Fix manpage mistake (now use section 1 instead of 8)
* debian/control:
  - Bump Standards-Version to 3.7.3.
  - Moved the "Homepage:" pseudo-header from the extended description
    to the new field in the source package general paragraph
  - Modified short and extended Description
* debian/copyright:
  - Modified homepage
  - Updated years for the copyright
  - Included GPL headers
  - Included a license statement for Debian packaging
* debian/dirs: deleted 'usr/share/man/man8'
* debian/manpages: list new manpage
* debian/patches:
  - remove 10-manpage-fix.dpatch, fixed upstream
  - add description to 20-makefile-debianlike.dpatch
* debian/rules: binary-arch must exists, even if it is empty (policy 4.9)
* debian/watch: added

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/perl -w 
2
2
#
3
 
# icecream 1.2
4
 
# Copyright (c) 2003-2006 Gil Megidish
 
3
# icecream 1.3
 
4
# Copyright (c) 2003-2008 Gil Megidish
5
5
#
6
6
# Formatted filename patch by Sean Dague (13 Dec 2005)
 
7
# Modified by Cristian Greco 2008 (release 1.3)
7
8
#
8
 
# This program is free software; you can redistribute it and/or
9
 
# modify it under the terms of the GNU General Public License
10
 
# as published by the Free Software Foundation; either version 2
11
 
# of the License, or (at your option) any later version.
 
9
# This program is free software; you can redistribute it and/or modify
 
10
# it under the terms of the GNU General Public License as published by
 
11
# the Free Software Foundation; either version 2 of the License, or
 
12
# (at your option) any later version.
12
13
#
13
14
# This program is distributed in the hope that it will be useful,
14
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
17
# GNU General Public License for more details.
17
18
#
18
 
# You should have received a copy of the GNU General Public License
19
 
# along with this program; if not, write to the Free Software
20
 
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
19
# You should have received a copy of the GNU General Public License along
 
20
# with this program; if not, write to the Free Software Foundation, Inc.,
 
21
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22
 
22
23
use strict; 
23
24
use IO::Socket; 
25
26
 
26
27
my $config = {};
27
28
 
28
 
my $def_agent = "icecream/1.2";
29
 
my $version = "icecream/1.2";
 
29
my $def_agent = "icecream/1.3";
 
30
my $version = "icecream/1.3";
30
31
my $accept_header = "audio/mpeg, audio/x-mpegurl, audio/x-scpls, */*";
31
32
 
32
33
my $def_timeout = 500;
103
104
 
104
105
        foreach my $s (@lines) 
105
106
        {
 
107
                # skip lines beginning with a comment
 
108
                if ($s =~ /^#EXT/)
 
109
                {
 
110
                        next;
 
111
                }
 
112
 
106
113
                my ($entry) = {};
107
114
                $entry->{id} = $id++;
108
115
                $entry->{file} = $s;
276
283
        debug("slurping http resource at $location");
277
284
 
278
285
        # parse location        
 
286
        $location = strip_protocol($location);
279
287
        ($host, $port, $path) = split_url($location);
280
288
                
281
289
        # parsing errors?
335
343
sub get_302_location
336
344
{
337
345
        my ($message) = shift || return undef;
338
 
        my ($headers, $body);
339
 
 
340
 
        ($headers, $body) = split("\r\n\r\n", $message, 2);
341
 
        return undef unless defined $headers;
342
 
 
343
 
        if ($headers =~ /\nLocation:\s*(.+)\n/i) 
 
346
 
 
347
        if ($message =~ /.*Location:\s*(.+)\n/i)
344
348
        {
345
349
                return $1;
346
350
        }
623
627
        if ($config->{stdout} == 1) 
624
628
        {
625
629
                print $chunk;
626
 
                return;
627
630
        }
628
631
 
629
632
        # allow set name support
641
644
                        $trackid = sprintf "%02d - ", $context->{id};
642
645
                }
643
646
 
644
 
                my $fn = $trackid . $context->{title} . '.mp3';
 
647
                my $fn = $trackid . $context->{title};
 
648
                $fn .= defined $context->{'is-ogg'} ? ".ogg" : ".mp3";
645
649
                return unless open_output($context, $fn);
646
650
        }
647
651
 
731
735
        my ($stream) = shift || return 0;
732
736
        my ($context) = {};
733
737
 
 
738
        debug("loop_named_stream()");
 
739
 
734
740
        my $synced = 0;
735
741
        my $huge = "";
736
742
 
738
744
        $context->{title} = '';
739
745
        $context->{length} = 0;
740
746
        $context->{output_open} = 0;
 
747
        $context->{'is-ogg'} = $stream->{'is-ogg'};
741
748
 
742
749
        if ($config->{tracks} == 0) 
743
750
        {
745
752
                $context->{title} = $stream->{'name'};
746
753
        }
747
754
 
748
 
        # load all data upto the first metaint if -t is set
749
 
        if ($config->{tracks} && $config->{stdout} == 0) 
 
755
        # load all data up to the first metaint if -t is set
 
756
        elsif ($config->{traks})
750
757
        {
751
758
                my $metablock;
752
759
                my $title;
802
809
        while (1) 
803
810
        {
804
811
                check_stop_cond();
805
 
                last if ($config->{stop} != 0);
 
812
                last if ((defined $config->{stop}) && ($config->{stop} != 0));
806
813
                        
807
814
                my $chunk = recv_chunk($sock, $stream->{'metaint'});
808
815
                if (length($chunk) < $stream->{'metaint'}) 
840
847
                set_title($context, $title) if defined $title;
841
848
        }
842
849
        
 
850
        debug("loop_named_stream ended");
843
851
        return 1;
844
852
}
845
853
 
852
860
        debug("loop_anonymous_stream()");
853
861
 
854
862
        $context->{id} = 0;
855
 
        $context->{title} = $stream->{name};
 
863
        $context->{title} = defined $stream->{name} ? $stream->{name} : 'stream-' .time;
856
864
        $context->{length} = 0;
857
865
        $context->{output_open} = 0;
 
866
        $context->{'is-ogg'} = $stream->{'is-ogg'};
858
867
 
859
868
        while (1) 
860
869
        {
861
870
                check_stop_cond();
862
 
                last if ($config->{stop} != 0);
 
871
                last if ((defined $config->{stop}) && ($config->{stop} != 0));
863
872
 
864
873
                my $chunk = recv_chunk($sock, 1024);
865
874
                last unless length($chunk) > 0;
941
950
        my ($host, $port, $path);
942
951
        my ($sock, $headers);
943
952
        my ($status);
 
953
        my ($stream_data);
944
954
 
945
955
        do
946
 
        {       
 
956
        {
947
957
                if (split_protocol($location) ne "http") 
948
958
                {
949
959
                        print STDERR "error: not an http location $location\n";
976
986
 
977
987
                my $agent = $config->{'user-agent'};
978
988
                my $request = "GET $path HTTP/1.0\r\n" .
 
989
                        "Host: $host:$port\r\n" .
 
990
                        "Accept: ${accept_header}\r\n" .
979
991
                        "Icy-MetaData:1\r\n" .
980
992
                        "User-Agent:$agent\r\n" .
981
993
                        "\r\n";
999
1011
                        return 0;
1000
1012
                }
1001
1013
 
1002
 
                if ($status == 302) 
 
1014
                elsif ($status == 302) 
1003
1015
                {
1004
1016
                        # relocated
1005
1017
                        $location = get_302_location($headers);
1006
 
                        next;
1007
1018
                }
1008
1019
 
1009
 
                if ($status == 400) 
 
1020
                elsif ($status == 400) 
1010
1021
                {
1011
1022
                        # server full
1012
1023
                        print STDERR "error: server is full (use --debug for complete response)\n";
1013
1024
                        return 0;
1014
1025
                }
1015
1026
 
1016
 
                if ($status != 200) 
 
1027
                elsif ($status != 200) 
1017
1028
                {
1018
1029
                        # nothing works fine these days
1019
1030
                        print STDERR "error: server error $status (use --debug for complete response)\n";
1022
1033
 
1023
1034
        } while ($status != 200);
1024
1035
 
1025
 
        if ($headers =~ /^HTTP/) 
1026
 
        {
1027
 
                # ICY is embedded inside an HTTP
1028
 
                $headers = slurp_headers($sock);
1029
 
                return 0 unless defined $headers;
1030
 
        }
1031
 
 
1032
 
        my $raw_stream_data = parse_stream_headers($headers);
1033
 
        if (! defined $raw_stream_data) 
1034
 
        {
1035
 
                print STDERR "error: problems parsing stream headers (please use --debug)\n";
1036
 
                return 0;
1037
 
        }
1038
 
 
1039
 
        my $stream_data = prepare_stream_data($raw_stream_data);
1040
 
        if (! defined $stream_data->{'name'}) 
1041
 
        {
1042
 
                print STDERR "error: not an icecast/shoutcast stream\n";
1043
 
                return 0;
1044
 
        }
1045
 
 
1046
 
        if ($config->{debug}) 
1047
 
        {
1048
 
                my $info = "name: $stream_data->{name}\n";
1049
 
                $info .= "genre: $stream_data->{genre}\n" if defined $stream_data->{genre};
1050
 
                $info .= "metaint: $stream_data->{metaint}\n" if defined $stream_data->{metaint};
1051
 
                debug("parsed stream headers", $info);
1052
 
        }
1053
 
        
 
1036
        # manage icy and x-audiocast headers even if they are embedded in http
 
1037
        # but skip header manipulation in other cases!
 
1038
        if (($headers =~ /.*icy.*/) or ($headers =~ /.*audiocast.*/))
 
1039
        {
 
1040
                my $raw_stream_data = parse_stream_headers($headers);
 
1041
                if (! defined $raw_stream_data) 
 
1042
                {
 
1043
                        print STDERR "error: problems parsing stream headers (please use --debug)\n";
 
1044
                        return 0;
 
1045
                }
 
1046
 
 
1047
                $stream_data = prepare_stream_data($raw_stream_data);
 
1048
                if (! defined $stream_data->{'name'}) 
 
1049
                {
 
1050
                        print STDERR "error: not an icecast/shoutcast stream\n";
 
1051
                        return 0;
 
1052
                }
 
1053
 
 
1054
                if ($config->{debug}) 
 
1055
                {
 
1056
                        my $info = "name: $stream_data->{name}\n";
 
1057
                        $info .= "genre: $stream_data->{genre}\n" if defined $stream_data->{genre};
 
1058
                        $info .= "metaint: $stream_data->{metaint}\n" if defined $stream_data->{metaint};
 
1059
                        debug("parsed stream headers", $info);
 
1060
                }
 
1061
 
 
1062
                if ($headers =~ /.*Content-Type: application\/ogg/i)
 
1063
                {
 
1064
                        # streaming url is .ogg file,
 
1065
                        # remember this when saving output file
 
1066
                        $stream_data->{'is-ogg'} = 1;
 
1067
                }
 
1068
        }
 
1069
 
1054
1070
        if (defined $stream_data->{'metaint'}) 
1055
1071
        {
1056
1072
                # server periodically sends stream title
1080
1096
        print "  -q, --quiet         no printouts\n";
1081
1097
        print "  -v, --verbose       be verbose\n";
1082
1098
        print "  -t, --tracks        split into tracks when saving\n";
1083
 
        print "  --name=NAME         output stream to file NAME. Format codes\n";
 
1099
        print "  --name=NAME         save stream to file NAME. Format codes\n";
1084
1100
        print "                      are replaced as in the date command.\n";
1085
 
        print "  --stdout            output tracks to stdout (implies quiet)\n";
 
1101
        print "  --stop=N[units]     stop download after N (kb, mb, min, songs)\n";
 
1102
        print "  --user-agent=AGENT  identify as AGENT stead of ${def_agent}\n";
 
1103
        print "  --stdout            output stream to stdout (implies quiet)\n";
1086
1104
        print "  --sync              sync mpeg audio\n";
1087
 
        print "  --stop=N[units]     stop after N (kb, mb, min, songs)\n";
1088
1105
        print "  --debug             turn on debugging\n";
1089
 
        print "  --useragent=AGENT   identify as AGENT stead of ${def_agent}\n";
1090
1106
        exit 0;
1091
1107
}
1092
1108
 
1182
1198
        my ($url) = shift || return undef;
1183
1199
        my ($config) = shift || return undef;
1184
1200
        
 
1201
        # play direct stream url (not .pls nor .m3u)
 
1202
        if (($url =~ /^http/) and (not (($url =~ /\.m3u$/) or ($url =~ /\.pls$/))))
 
1203
        {
 
1204
                start_stream($url);
 
1205
                return 1;
 
1206
        }
 
1207
 
1185
1208
        my $raw = retreive_playlist($url);
1186
1209
        if (! defined $raw) 
1187
1210
        {
1195
1218
        {
1196
1219
                @pls = parse_m3u_playlist($raw);
1197
1220
        } 
1198
 
        else 
 
1221
        elsif ($url =~ /\.pls$/)
1199
1222
        {
1200
1223
                @pls = parse_pls_playlist($raw);
1201
1224
        }
1284
1307
 
1285
1308
=head1 NAME
1286
1309
 
1287
 
icecream - listen to, or download icecast streams
 
1310
icecream - download icecast and shoutcast streams, redirecting all fetched
 
1311
content to stdout and/or to disk at the same time
1288
1312
 
1289
1313
=head1 SYNOPSIS
1290
1314
 
1292
1316
 
1293
1317
=head1 DESCRIPTION
1294
1318
 
1295
 
icecream is a non-interactive stream download utility. It connects
1296
 
to icecast and shoutcast servers and redirects all fetched content
1297
 
to an stdin-capable player or to media files on your disk. With an
1298
 
option turned on, it can save the stream into different files, each
1299
 
representing the played track. It is also possible to tee the input
1300
 
to both disk and stdout.
 
1319
icecream is a lightweight, non-interactive, stream download utility.
 
1320
It connects to icecast and shoutcast servers or direct stream URLs, and
 
1321
redirects all fetched content to stdout and/or to media files on your disk. 
 
1322
 
 
1323
Listen to the stream piping the output to a stdin-capable media player.
 
1324
Save the stream to a named file or split it into different tracks.
 
1325
It is possible to redirect the stream and save it to disk at the same time.
 
1326
 
 
1327
icecream is able to parse pls and m3u playlists, and to download mp3 and ogg
 
1328
direct stream URLs. If the stream is anonymous it will be saved as
 
1329
'stream-time.mp3', where time is actual timestamp.
1301
1330
 
1302
1331
=head1 OPTIONS
1303
1332
 
1315
1344
 
1316
1345
Be verbose
1317
1346
 
1318
 
=item B<--stdout>
1319
 
 
1320
 
Output stream to stdout (implies -q)
1321
 
 
1322
1347
=item B<-t>, B<--tracks>
1323
1348
 
1324
1349
Split stream into tracks (if possible)
1325
1350
 
1326
1351
=item B<--name=NAME>
1327
1352
 
1328
 
Output stream to file specified by NAME. Format codes starting
1329
 
with "%" will be replaced. See the date command for valid
1330
 
format codes.
 
1353
Save the stream to file specified by NAME. Format codes starting with "%" will
 
1354
be replaced. See the date command for valid format codes.
1331
1355
 
1332
1356
=item B<--stop=N[units]>
1333
1357
 
1334
 
Stop stream after N minutes, song or KB/MB transferred
 
1358
Stop downloading the stream after N kb/mb/min/songs
 
1359
 
 
1360
=item B<--user-agent=AGENT>
 
1361
 
 
1362
Set user-agent header to AGENT
 
1363
 
 
1364
=item B<--stdout>
 
1365
 
 
1366
Output stream to stdout (implies -q)
 
1367
 
 
1368
=item B<--sync>
 
1369
 
 
1370
Turn syncing on, required for some mpeg players that read from stdin
1335
1371
 
1336
1372
=item B<--debug>
1337
1373
 
1338
1374
Turn on debugging outputs
1339
1375
 
1340
 
=item B<--useragent=AGENT>
1341
 
 
1342
 
Set useragent header to AGENT
1343
 
 
1344
 
=item B<--sync>
1345
 
 
1346
 
Turn syncing on, required for some mpeg players that read from stdin
1347
1376
 
1348
1377
=back
1349
1378
 
1353
1382
 
1354
1383
=item Streaming to mpg123
1355
1384
 
1356
 
icecream -s http://radio.com/playlist.pls | mpg123 -
 
1385
icecream --stdout http://radio.com/playlist.pls | mpg123 -
1357
1386
 
1358
1387
=item Split stream into different tracks
1359
1388
 
1360
1389
icecream -t http://metal.org/radio.pls
1361
1390
 
 
1391
=item Split stream into tracks and play with vlc at the same time
 
1392
 
 
1393
icecream -t --stdout http://streaming.com/playlist.pls | vlc file:/dev/stdin
 
1394
 
1362
1395
=item Prepare a 74 minute CD
1363
1396
 
1364
1397
icecream -t --stop 74min http://trace.net/playlist.m3u 
1365
1398
 
1366
1399
=item Use a filename with today's date as output
1367
1400
 
1368
 
icecream -q --name 'radio_%Y_%m_%d' --stop 60min
1369
 
http://radio.com/playlist.pls
 
1401
icecream -q --name 'radio_%Y_%m_%d' --stop 60min http://radio.com/playlist.pls
1370
1402
 
1371
1403
=back
1372
1404
 
1373
1405
=head1 BUGS
1374
1406
 
1375
 
You are welcome to send bug reports about icecream to our mailing
1376
 
list. Feel free to visit http://icecream.sourceforge.net
 
1407
You are welcome to send bug reports about icecream to our mailing list.
 
1408
Feel free to visit http://icecream.sourceforge.net
1377
1409
 
1378
1410
=head1 AUTHOR
1379
1411