File Coverage

blib/lib/Podcast/ESLPodcast/Splitter.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Podcast::ESLPodcast::Splitter;
2              
3 1     1   64464 use warnings;
  1         2  
  1         27  
4 1     1   5 use strict;
  1         1  
  1         30  
5 1     1   4 use Carp;
  1         5  
  1         79  
6              
7 1     1   951 use LWP::UserAgent;
  1         86928  
  1         43  
8 1     1   12 use File::Basename;
  1         3  
  1         137  
9 1     1   446 use XML::FeedPP;
  0            
  0            
10             use MP3::Splitter;
11             use Data::Dumper;
12             use Time::Piece;
13             use List::MoreUtils qw(any all);
14              
15             use version; our $VERSION = qv('0.0.1');
16              
17             use constant FEEDURI =>
18             'http://feeds.feedburner.com/EnglishAsASecondLanguagePodcast?format=xml';
19              
20             # constructor
21             sub new {
22             my ( $class, @args ) = @_;
23             my %args = ref $args[0] eq 'HASH' ? %{ $args[0] } : @args;
24             my $self = {%args};
25             $self->{n} ||= 5;
26             bless $self, $class;
27             }
28              
29             # fetch a feed information for the ESLPodcast
30             sub fetch_feed {
31             my $feed = XML::FeedPP->new(FEEDURI);
32             printf STDERR "===============FEED INFO================\n";
33             printf STDERR "Title: %s\n", $feed->title;
34             printf STDERR "Date: %s\n", $feed->pubDate;
35             printf STDERR "Copyright: %s\n", $feed->copyright;
36             printf STDERR "Link: %s\n", $feed->link;
37             printf STDERR "Language: %s\n", $feed->language;
38             printf STDERR "========================================\n";
39             return $feed;
40             }
41              
42             sub run {
43             my $self = shift;
44             my $feed = fetch_feed();
45             my $num_mp3s = 1;
46             for my $item ( $feed->get_item() ) {
47             if ( $item->title =~ /English Cafe/ ) {
48             printf STDERR "skip: %s\n", $item->title;
49             next;
50             }
51             last if ( $num_mp3s > $self->{n} );
52              
53             printf STDERR "\tURL: %s\n\tTitle: %s\n\tguid: %s\n\tDescription: %s\n",
54             $item->link, $item->title, $item->guid, ""; # $item->description;
55              
56             my $durations = extract_durations( $item->description );
57             next if ( !$durations );
58              
59             my $uri = $item->guid();
60             my $mp3_file = basename($uri);
61             if ( !download_mp3( \$uri, \$mp3_file ) ) {
62             printf STDERR "download failure: %s\n", $mp3_file;
63             next;
64             }
65             if ( split_mp3( \$mp3_file, $durations ) ) {
66             printf STDERR "split failure: %s\n", $mp3_file;
67             }
68              
69             ++$num_mp3s;
70             }
71             printf STDERR "DONE!";
72             }
73              
74             # extract durations for each dialog
75             sub extract_durations {
76             my ($desc) = @_;
77              
78             # find started at
79             my $started_at = { slow => undef, explanation => undef, fast => undef };
80             for ( split( /[\n|\r\n]/, $desc ) ) {
81             chomp;
82             if (/Slow (dialog|dialogue):\s*(\d+:\d+)/) {
83             $started_at->{slow} = $2;
84             }
85             if (/Explanations:\s*(\d+:\d+)/) {
86             $started_at->{explanation} = $1;
87             }
88             if (/Fast (dialog|dialogue):\s*(\d+:\d+)/) {
89             $started_at->{fast} = $2;
90             }
91             }
92              
93             # print Dumper($started_at);
94             if ( !( all { defined($_) } values %$started_at ) ) {
95             printf STDERR "something wrong in this feed\n";
96             return 0;
97             }
98              
99             # find durations
100             my $durations = { slow => undef, explanation => undef, fast => undef };
101             $durations->{slow} = [
102             $started_at->{slow},
103             find_duration( $started_at->{explanation}, $started_at->{slow} )
104             ];
105             $durations->{explanation} = [
106             $started_at->{explanation},
107             find_duration( $started_at->{fast}, $started_at->{explanation} )
108             ];
109             $durations->{fast} = [ $started_at->{fast}, "=INF" ];
110             return $durations;
111             }
112              
113             # find a duration
114             sub find_duration {
115             my ( $from, $to ) = @_;
116             my $t1 = Time::Piece->strptime( $from, "%M:%S" );
117             my $t2 = Time::Piece->strptime( $to, "%M:%S" );
118             my $diff = $t2 - $t1;
119             return $diff->seconds;
120             }
121              
122             # split a mp3 file into three files
123             sub split_mp3 {
124             my ( $mp3_file, $durations ) = @_;
125             mp3split(
126             $$mp3_file, { verbose => 1 },
127             $durations->{start}, $durations->{explanation},
128             $durations->{fast}
129             );
130             return 1;
131             }
132              
133             # download a mp3 file
134             sub download_mp3 {
135             my ( $uri, $mp3_file ) = @_;
136             open( my $fh, '>', $$mp3_file ) or die "$$mp3_file: $!";
137             my $res = LWP::UserAgent->new->get(
138             $$uri,
139             ':content_cb' => sub {
140             my ( $chunk, $res, $proto ) = @_;
141             print $fh $chunk;
142             my $size = tell $fh;
143             if ( my $total = $res->header('Content-Length') ) {
144             printf "%d/%d (%f%%)\r", $size, $total, $size / $total * 100;
145             }
146             else {
147             printf "%d/Unknown bytes\r", $size;
148             }
149             }
150             );
151             close $fh;
152             print "\n", $res->status_line, "\n";
153             unlink $$mp3_file if ( !$res->is_success );
154             return $res->is_success;
155             }
156              
157             1; # Magic true value required at end of module
158             __END__