XML-XSPF
view release on metacpan or search on metacpan
lib/XML/XSPF.pm view on Meta::CPAN
}
# Playlists MUST have a <trackList> element (even if it's empty version 1)
if (!$parser->{'_xspf'}->{'trackListCount'}) {
Carp::confess("Error while parsing playlist - no trackList element!\n");
return undef;
}
$parser = undef;
return $self;
}
# Create a XSPF document from our in-memory version.
sub toString {
my $self = shift;
my $string = undef;
my $writer = XML::Writer->new(
'OUTPUT' => \$string,
'DATA_MODE' => 1,
'DATA_INDENT' => 4,
);
$writer->xmlDecl("UTF-8");
$writer->startTag('playlist', 'version' => $self->version, 'xmlns' => $self->xmlns);
for my $element (qw(title creator annotation info location identifier image date license)) {
if (my $value = $self->$element) {
$writer->dataElement($element, $value);
}
}
if ($self->attributions) {
$writer->startTag('attribution');
for my $attribution ($self->attributions) {
$writer->dataElement(@{$attribution});
}
$writer->endTag('attribution');
}
if ($self->trackList) {
$writer->startTag('trackList');
for my $track ($self->trackList) {
$writer->startTag('track');
for my $element (qw(location identifier)) {
for my $cdata (@{$track->get("${element}s")}) {
$writer->dataElement($element, $cdata);
}
}
for my $element (qw(link meta)) {
for my $cdata (@{$track->get("${element}s")}) {
$writer->startTag($element, 'rel' => $cdata->[0]);
$writer->characters($cdata->[1]);
$writer->endTag($element);
}
}
for my $element (qw(title creator annotation info image album trackNum duration)) {
if (my $value = $track->$element) {
$writer->dataElement($element, $value);
}
}
$writer->endTag('track');
}
$writer->endTag('trackList');
}
$writer->endTag('playlist');
$writer->end;
# Don't escape these. XML::Writer provides some basic escaping, but not all.
# http://rt.cpan.org/Ticket/Display.html?id=36778
# $string = encode_entities($string, '^\n\r\t !\#\$%\(-;=?-~<>&"');
return $string;
}
sub handleStartElement {
my ($parser, $element, %attributes) = @_;
# Poor Man's HTML checker - XML::Parser treats elements.
# So look at the previous element, and if we're a single value
# element, fail.
if ($parser->{'_xspf'}->{'path'}) {
my @parts = split(/\//, $parser->{'_xspf'}->{'path'});
my $last = pop @parts;
if (grep { /^$last$/ } @singleValueElements) {
Carp::confess("Found HTML markup in <$last>\n");
}
}
my $path = $parser->{'_xspf'}->{'path'} .= "/$element";
my $self = $parser->{'_xspf'}->{'self'};
push @{ $parser->{'_xspf'}->{'states'} }, {
'attributes' => \%attributes,
'cdata' => '',
'path' => $path,
};
# Set some default types once we encounter them.
if ($path eq '/playlist/attribution') {
if ($parser->{'_xspf'}->{'attributionCount'}) {
Carp::confess("Too many attribution elements in playlist!\n");
}
$self->set('attributions', []);
$parser->{'_xspf'}->{'attributionCount'} = 1;
}
if ($path eq '/playlist/meta' ||
$path eq '/playlist/link') {
$self->set("${element}s", []);
}
if ($path eq '/playlist/trackList') {
if ($parser->{'_xspf'}->{'trackListCount'}) {
Carp::confess("Too many trackList elements in playlist!\n");
}
$parser->{'_xspf'}->{'trackListCount'} = 1;
}
# We got a track entry - create a new object for it
if ($path eq '/playlist/trackList/track') {
$parser->{'_xspf'}->{'track'} = XML::XSPF::Track->new;
}
}
sub handleCharElement {
my ($parser, $value) = @_;
# Keep the our little state machine chugging along
my $state = pop @{ $parser->{'_xspf'}->{'states'} };
$state->{'cdata'} .= $value;
push @{ $parser->{'_xspf'}->{'states'} }, $state;
}
sub handleEndElement {
my ($parser, $element) = @_;
my $state = pop @{ $parser->{'_xspf'}->{'states'} };
my $value = $state->{'cdata'};
my $path = $parser->{'_xspf'}->{'path'};
my $self = $parser->{'_xspf'}->{'self'};
# These are all single value elements.
if ($path eq '/playlist/annotation' ||
$path eq '/playlist/creator' ||
$path eq '/playlist/date' ||
$path eq '/playlist/identifier' ||
$path eq '/playlist/image' ||
$path eq '/playlist/info' ||
$path eq '/playlist/license' ||
$path eq '/playlist/location' ||
$path eq '/playlist/title') {
# There should only be one value per track according to the spec.
if ($self->get($element)) {
Carp::confess("Element: $path has too many values!\n");
}
if (_validateLinkElement($path, 'playlist', $element, $value)) {
$self->$element($value);
}
}
if ($path eq '/playlist/attribution/identifier' ||
$path eq '/playlist/attribution/location') {
if (_validateLinkElement($path, 'playlist', $element, $value)) {
$self->append('attributions', [ $element, $value ]);
}
}
if ($path eq '/playlist/meta' ||
$path eq '/playlist/link') {
my $rel = $state->{'attributes'}->{'rel'};
# Check both the value and the rel for validity.
if (_validateLinkElement($path, 'playlist', $element, $value, $rel)) {
$self->append("${element}s", [ $rel, $value ]);
}
}
# We've hit the end of a track definition - push it onto the end of the track list.
if ($path eq '/playlist/trackList/track') {
push @{ $parser->{'_xspf'}->{'tracks'} }, $parser->{'_xspf'}->{'track'};
}
# End of the trackList - set all the tracks we've acquired.
if ($path eq '/playlist/trackList') {
$self->trackList($parser->{'_xspf'}->{'tracks'});
}
( run in 0.834 second using v1.01-cache-2.11-cpan-39bf76dae61 )