File Coverage

lib/Text/Playlist/M3U.pm
Criterion Covered Total %
statement 53 53 100.0
branch 14 20 70.0
condition 1 2 50.0
subroutine 8 8 100.0
pod 2 3 66.6
total 78 86 90.7


line stmt bran cond sub pod time code
1             package Text::Playlist::M3U;
2              
3 1     1   427 use strict;
  1         1  
  1         29  
4 1     1   3 use warnings;
  1         2  
  1         24  
5              
6 1     1   10 use base 'Text::Playlist';
  1         0  
  1         229  
7              
8             our $VERSION = '0.02';
9              
10             sub new {
11 1     1 0 281 my ($class) = @_;
12              
13 1         4 return bless({ attrs => {}, }, $class);
14             }
15              
16             sub parse {
17 1     1 1 2 my ($self, $text) = @_;
18              
19 1         8 my @lines = split /\r?\n/, $text;
20 1         2 my @items = ();
21              
22             # safeguard
23 5         9 return "Not looks like playlist"
24 1 50       1 unless grep { $_ =~ m/^#EXTM3U/o } @lines;
25              
26 1         1 my $item = undef;
27 1         2 foreach my $line (@lines) {
28             # header
29 5 100       11 if ($line =~ m/#EXTM3U(\s+\S+?=\S+)*/oi) {
30 1 50       8 return "Multiple EXTM3U lines found"
31             if (scalar keys($self->{attrs}));
32 1         2 $self->{attrs} = $self->_parse_attrs($1);
33             }
34             # standart tags
35 5 100       13 if ($line =~ m/^\s*#EXTINF:(-?\d+(?:\.\d+)?)(\s+\S+?=\S+)*,\s*(.*)/oi) {
36 2   50     6 $item //= {
37             duration => $1,
38             attrs => $self->_parse_attrs($2),
39             title => $3,
40             };
41             }
42             # extended tags
43 5 50       7 if ($line =~ m/^\s*#EXT-X-\S+?:(\s*\S+?=\S+)/oi) {
44             # ignore
45             }
46             # comments
47 5 100       11 if ($line =~ m/^\s*#/) {
48 3         3 next;
49             }
50             # path / url
51 2 50       3 if ($line) {
52 2         2 $item->{file} = $line;
53 2         3 push @items, $item;
54 2         2 undef $item;
55             }
56             }
57              
58 1 50       6 return wantarray ? @items : [ @items ];
59             }
60              
61             sub _parse_attrs {
62 3     3   5 my ($self, $str) = @_;
63              
64 3 100       9 return {} unless $str;
65 2         2 my %attrs = ();
66 2         5 $str =~ s/(^\s+|\s+$)//oi;
67 2         3 foreach my $token (split /\s+/, $str) {
68 2         4 my ($key, $value) = split("=", $token, 2);
69 2         3 $attrs{$key} = $value;
70             }
71              
72 2         7 return \%attrs;
73             }
74              
75             sub _dump_attrs {
76 3     3   4 my ($self, $attrs) = @_;
77 3         5 my @parts = ('');
78              
79 3         2 while (my ($key, $value) = each %{$attrs}) {
  5         16  
80 2         10 push @parts, sprintf("%s=%s", $key, $value);
81             }
82 3 50       16 return @parts ? join(" ", @parts) : "";
83             }
84              
85             sub dump {
86 1     1 1 988 my ($self, @items) = @_;
87 1         3 my @lines = ();
88 1         4 push @lines, sprintf('#EXTM3U%s', $self->_dump_attrs($self->{attrs}));
89              
90 1         2 foreach my $item (@items) {
91 2         10 push @lines, sprintf("#EXTINF:%s%s,%s", $item->{duration},
92             $self->_dump_attrs($item->{attrs}), $item->{title});
93 2         4 push @lines, $item->{file};
94             }
95              
96 1         2 push @lines, '';
97 1         5 return join("\n", @lines);
98             }
99              
100             1;
101              
102             __END__