Bläddra i källkod

complement itunes tag

per [official
recommendation](https://help.apple.com/itc/podcasts_connect/#/itcb54353390)
master
magodo 7 år sedan
förälder
incheckning
67f38f898c
9 ändrade filer med 195 tillägg och 59 borttagningar
  1. +14
    -7
      lib/domain/rss_item_itunes.dart
  2. +29
    -25
      lib/domain/rss_itunes.dart
  3. +1
    -1
      lib/domain/rss_itunes_category.dart
  4. +19
    -0
      lib/domain/rss_itunes_episode_type.dart
  5. +18
    -0
      lib/domain/rss_itunes_owner.dart
  6. +17
    -0
      lib/domain/rss_itunes_type.dart
  7. +18
    -1
      lib/util/helpers.dart
  8. +67
    -25
      test/rss_test.dart
  9. +12
    -0
      test/xml/RSS-Itunes.xml

+ 14
- 7
lib/domain/rss_item_itunes.dart Visa fil

@@ -2,12 +2,15 @@ import 'package:webfeed/util/helpers.dart';
import 'package:xml/xml.dart';

import 'rss_itunes_category.dart';
import 'rss_itunes_episode_type.dart';
import 'rss_itunes_image.dart';

class RssItemItunes {
final String title;
final int episode;
final int season;
final Duration duration;
final String episodeType;
final RssItunesEpisodeType episodeType;
final String author;
final String summary;
final bool explicit;
@@ -15,9 +18,12 @@ class RssItemItunes {
final List<String> keywords;
final RssItunesImage image;
final RssItunesCategory category;
final bool block;

RssItemItunes({
this.title,
this.episode,
this.season,
this.duration,
this.episodeType,
this.author,
@@ -27,31 +33,32 @@ class RssItemItunes {
this.keywords,
this.image,
this.category,
this.block,
});

factory RssItemItunes.parse(XmlElement element) {
if (element == null) {
return null;
}
var explicitStr =
findElementOrNull(element, "itunes:explicit")?.text?.toLowerCase()?.trim();
var episodeStr = findElementOrNull(element, "itunes:episode")?.text?.trim();
var seasonStr = findElementOrNull(element, "itunes:season")?.text?.trim();
var durationStr = findElementOrNull(element, "itunes:duration")?.text?.trim();

return RssItemItunes(
title: findElementOrNull(element, "itunes:title")?.text?.trim(),
episode: episodeStr == null ? null : int.parse(episodeStr),
season: seasonStr == null ? null : int.parse(seasonStr),
duration: durationStr == null ? null : parseDuration(durationStr),
episodeType: findElementOrNull(element, "itunes:episodeType")?.text?.trim(),
episodeType: newRssItunesEpisodeType(findElementOrNull(element, "itunes:episodeType")),
author: findElementOrNull(element, "itunes:author")?.text?.trim(),
summary: findElementOrNull(element, "itunes:summary")?.text?.trim(),
explicit: explicitStr == null
? null
: explicitStr == "yes" || explicitStr == "true",
explicit: parseBoolLiteral(element, "itunes:explicit"),
subtitle: findElementOrNull(element, "itunes:subtitle")?.text?.trim(),
keywords: findElementOrNull(element, "itunes:keywords")?.text?.split(",")?.map((keyword) => keyword.trim())?.toList(),
image: RssItunesImage.parse(findElementOrNull(element, "itunes:image")),
category: RssItunesCategory.parse(
findElementOrNull(element, "itunes:category")),
block: parseBoolLiteral(element, "itunes:block"),
);
}
}


+ 29
- 25
lib/domain/rss_itunes.dart Visa fil

@@ -4,62 +4,66 @@ import 'package:webfeed/util/helpers.dart';

import 'rss_itunes_category.dart';
import 'rss_itunes_image.dart';
import 'rss_itunes_owner.dart';
import 'rss_itunes_type.dart';

class RssItunes {
final String author;
final String summary;
final bool explicit;
final String title;
final String subtitle;
final RssItunesOwner owner;
final List<String> keywords;
final RssItunesImage image;
final RssItunesCategory category;
final List<RssItunesCategory> categories;
final RssItunesType type;
final String newFeedUrl;
final bool block;
final bool complete;

RssItunes({
this.author,
this.summary,
this.explicit,
this.title,
this.subtitle,
this.owner,
this.keywords,
this.image,
this.category,
this.categories,
this.type,
this.newFeedUrl,
this.block,
this.complete,
});

factory RssItunes.parse(XmlElement element) {
if (element == null) {
return null;
}
var explicitStr =
findElementOrNull(element, "itunes:explicit")?.text?.toLowerCase()?.trim();

return RssItunes(
author: findElementOrNull(element, "itunes:author")?.text?.trim(),
summary: findElementOrNull(element, "itunes:summary")?.text?.trim(),
explicit: explicitStr == null
? null
: explicitStr == "yes" || explicitStr == "true",
explicit: parseBoolLiteral(element, "itunes:explicit"),
title: findElementOrNull(element, "itunes:title")?.text?.trim(),
subtitle: findElementOrNull(element, "itunes:subtitle")?.text?.trim(),
owner: RssItunesOwner.parse(findElementOrNull(element, "itunes:owner")),
keywords: findElementOrNull(element, "itunes:keywords")?.text?.split(",")?.map((keyword) => keyword.trim())?.toList(),
keywords: findElementOrNull(element, "itunes:keywords")
?.text
?.split(",")
?.map((keyword) => keyword.trim())
?.toList(),
image: RssItunesImage.parse(findElementOrNull(element, "itunes:image")),
category: RssItunesCategory.parse(
findElementOrNull(element, "itunes:category")),
categories: findAllDirectElementsOrNull(element, "itunes:category")
.map((ele) => RssItunesCategory.parse(ele))
.toList(),
type: newRssItunesType(findElementOrNull(element, "itunes:type")),
newFeedUrl:
findElementOrNull(element, "itunes:new-feed-url")?.text?.trim(),
block: parseBoolLiteral(element, "itunes:block"),
complete: parseBoolLiteral(element, "itunes:complete"),
);
}
}

class RssItunesOwner {
final String name;
final String email;

RssItunesOwner({this.name, this.email});

factory RssItunesOwner.parse(XmlElement element) {
if (element == null) return null;
return RssItunesOwner(
name: findElementOrNull(element, "itunes:name")?.text?.trim(),
email: findElementOrNull(element, "itunes:email")?.text?.trim(),
);
}
}

+ 1
- 1
lib/domain/rss_itunes_category.dart Visa fil

@@ -11,7 +11,7 @@ class RssItunesCategory {

Iterable<XmlElement> subCategories;
try {
subCategories = element.findAllElements("itunes:category");
subCategories = element.findElements("itunes:category");
} on StateError {
subCategories = null;
}


+ 19
- 0
lib/domain/rss_itunes_episode_type.dart Visa fil

@@ -0,0 +1,19 @@
import 'package:xml/xml.dart';

enum RssItunesEpisodeType {full, trailer, bonus}

RssItunesEpisodeType newRssItunesEpisodeType(XmlElement element) {
// "full" is default type
if (element == null) return RssItunesEpisodeType.full;

switch (element.text) {
case "full":
return RssItunesEpisodeType.full;
case "trailer":
return RssItunesEpisodeType.trailer;
case "bonus":
return RssItunesEpisodeType.bonus;
default:
return null;
}
}

+ 18
- 0
lib/domain/rss_itunes_owner.dart Visa fil

@@ -0,0 +1,18 @@
import 'package:xml/xml.dart';

import '../util/helpers.dart';

class RssItunesOwner {
final String name;
final String email;

RssItunesOwner({this.name, this.email});

factory RssItunesOwner.parse(XmlElement element) {
if (element == null) return null;
return RssItunesOwner(
name: findElementOrNull(element, "itunes:name")?.text?.trim(),
email: findElementOrNull(element, "itunes:email")?.text?.trim(),
);
}
}

+ 17
- 0
lib/domain/rss_itunes_type.dart Visa fil

@@ -0,0 +1,17 @@
import 'package:xml/xml.dart';

enum RssItunesType { episodic, serial }

RssItunesType newRssItunesType(XmlElement element) {
// "episodic" is default type
if (element == null) return RssItunesType.episodic;

switch (element.text) {
case "episodic":
return RssItunesType.episodic;
case "serial":
return RssItunesType.serial;
default:
return null;
}
}

+ 18
- 1
lib/util/helpers.dart Visa fil

@@ -2,10 +2,27 @@ import 'dart:core';

import 'package:xml/xml.dart';

XmlElement findElementOrNull(XmlElement element, String name, {String namespace}) {
XmlElement findElementOrNull(XmlElement element, String name,
{String namespace}) {
try {
return element.findAllElements(name, namespace: namespace).first;
} on StateError {
return null;
}
}

List<XmlElement> findAllDirectElementsOrNull(XmlElement element, String name,
{String namespace}) {
try {
return element.findElements(name, namespace: namespace).toList();
} on StateError {
return null;
}
}

bool parseBoolLiteral(XmlElement element, String tagName) {
var v = findElementOrNull(element, tagName)?.text?.toLowerCase()?.trim();
if (v == null) return null;
return ["yes", "true"].contains(v);
}


+ 67
- 25
test/rss_test.dart Visa fil

@@ -4,6 +4,9 @@ import 'dart:io';
import 'package:test/test.dart';
import 'package:webfeed/webfeed.dart';

import 'package:webfeed/domain/rss_itunes_type.dart';
import 'package:webfeed/domain/rss_itunes_episode_type.dart';

void main() {
test("parse Invalid.xml", () {
var xmlString = new File("test/xml/Invalid.xml").readAsStringSync();
@@ -19,7 +22,8 @@ void main() {
var feed = new RssFeed.parse(xmlString);

expect(feed.title, "News - Foo bar News");
expect(feed.description, "Foo bar News and Updates feed provided by Foo bar, Inc.");
expect(feed.description,
"Foo bar News and Updates feed provided by Foo bar, Inc.");
expect(feed.link, "https://foo.bar.news/");
expect(feed.author, "hello@world.net");
expect(feed.language, "en-US");
@@ -62,8 +66,10 @@ void main() {

expect(feed.items.length, 2);

expect(feed.items.first.title, "The standard Lorem Ipsum passage, used since the 1500s");
expect(feed.items.first.description, "Lorem ipsum dolor sit amet, consectetur adipiscing elit");
expect(feed.items.first.title,
"The standard Lorem Ipsum passage, used since the 1500s");
expect(feed.items.first.description,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit");
expect(feed.items.first.link, "https://foo.bar.news/1");
expect(feed.items.first.guid, "https://foo.bar.news/1?guid");
expect(feed.items.first.pubDate, "Mon, 26 Mar 2018 14:00:00 PDT");
@@ -73,22 +79,26 @@ void main() {
expect(feed.items.first.source.url, "https://foo.bar.news/1?source");
expect(feed.items.first.source.value, "Foo Bar");
expect(feed.items.first.comments, "https://foo.bar.news/1/comments");
expect(feed.items.first.enclosure.url, "http://www.scripting.com/mp3s/weatherReportSuite.mp3");
expect(feed.items.first.enclosure.url,
"http://www.scripting.com/mp3s/weatherReportSuite.mp3");
expect(feed.items.first.enclosure.length, 12216320);
expect(feed.items.first.enclosure.type, "audio/mpeg");

expect(feed.items.first.content.value, "<img width=\"1000\" height=\"690\" src=\"https://test.com/image_link\"/> Test content<br />");
expect(feed.items.first.content.images.first, "https://test.com/image_link");
expect(feed.items.first.content.value,
"<img width=\"1000\" height=\"690\" src=\"https://test.com/image_link\"/> Test content<br />");
expect(
feed.items.first.content.images.first, "https://test.com/image_link");
});
test("parse RSS-Media.xml", (){
test("parse RSS-Media.xml", () {
var xmlString = new File("test/xml/RSS-Media.xml").readAsStringSync();

var feed = new RssFeed.parse(xmlString);
expect(feed.title, "Song Site");
expect(feed.description, "Media RSS example with new fields added in v1.5.0");
expect(
feed.description, "Media RSS example with new fields added in v1.5.0");

expect(feed.items.length, 1);
var item = feed.items.first;
expect(item.title, null);
expect(item.link, "http://www.foo.com");
@@ -118,7 +128,8 @@ void main() {
expect(mediaCredit.scheme, "urn:yvs");
expect(mediaCredit.value, "copyright holder of the entity");

expect(item.media.category.scheme, "http://search.yahoo.com/mrss/category_ schema");
expect(item.media.category.scheme,
"http://search.yahoo.com/mrss/category_ schema");
expect(item.media.category.label, "Music");
expect(item.media.category.value, "music/artist/album/song");

@@ -129,10 +140,11 @@ void main() {
expect(item.media.title.value, "The Judy's -- The Moo Song");

expect(item.media.description.type, "plain");
expect(item.media.description.value, "This was some really bizarre band I listened to as a young lad.");
expect(item.media.description.value,
"This was some really bizarre band I listened to as a young lad.");

expect(item.media.keywords, "kitty, cat, big dog, yarn, fluffy");
expect(item.media.thumbnails.length, 2);
var mediaThumbnail = item.media.thumbnails.first;
expect(mediaThumbnail.url, "http://www.foo.com/keyframe1.jpg");
@@ -179,7 +191,8 @@ void main() {
expect(item.media.embed.height, 323);
expect(item.media.embed.params.length, 5);
expect(item.media.embed.params.first.name, "type");
expect(item.media.embed.params.first.value, "application/x-shockwave-flash");
expect(
item.media.embed.params.first.value, "application/x-shockwave-flash");

expect(item.media.responses.length, 2);
expect(item.media.responses.first, "http://www.response1.com");
@@ -195,7 +208,8 @@ void main() {
expect(item.media.prices.length, 2);
expect(item.media.prices.first.price, 19.99);
expect(item.media.prices.first.type, "rent");
expect(item.media.prices.first.info, "http://www.dummy.jp/package_info.html");
expect(
item.media.prices.first.info, "http://www.dummy.jp/package_info.html");
expect(item.media.prices.first.currency, "EUR");

expect(item.media.license.type, "text/html");
@@ -214,7 +228,7 @@ void main() {
expect(item.media.scenes.first.startTime, "00:15");
expect(item.media.scenes.first.endTime, "00:45");
});
test("parse RSS-DC.xml", (){
test("parse RSS-DC.xml", () {
var xmlString = File("test/xml/RSS-DC.xml").readAsStringSync();

var feed = RssFeed.parse(xmlString);
@@ -256,7 +270,7 @@ void main() {
var xmlString = File("test/xml/RSS-Empty.xml").readAsStringSync();

var feed = RssFeed.parse(xmlString);
expect(feed.title, null);
expect(feed.description, null);
expect(feed.link, null);
@@ -304,22 +318,50 @@ void main() {
expect(feed.itunes.author, "Changelog Media");
expect(feed.itunes.summary, "Foo");
expect(feed.itunes.explicit, false);
expect(feed.itunes.image.href, "https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357");
expect(feed.itunes.keywords, "go,golang,open source,software,development".split(","));
expect(feed.itunes.image.href,
"https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357");
expect(feed.itunes.keywords,
"go,golang,open source,software,development".split(","));
expect(feed.itunes.owner.name, "Changelog Media");
expect(feed.itunes.owner.email, "editors@changelog.com");
expect(feed.itunes.category.category, "Technology");
expect(feed.itunes.category.subCategories, ["Software How-To", "Tech News"]);
expect(
Set.from([
feed.itunes.categories[0].category,
feed.itunes.categories[1].category
]),
["Technology", "Foo"]);
for (var category in feed.itunes.categories) {
switch (category.category) {
case "Foo":
expect(category.subCategories, ["Bar", "Baz"]);
break;
case "Technology":
expect(category.subCategories, ["Software How-To", "Tech News"]);
break;
}
}
expect(feed.itunes.title, "Go Time");
expect(feed.itunes.type, RssItunesType.serial);
expect(feed.itunes.newFeedUrl, "wubawuba");
expect(feed.itunes.block, true);
expect(feed.itunes.complete, true);

var item = feed.items[0];
expect(item.itunes.episodeType, "full");
expect(item.itunes.episodeType, RssItunesEpisodeType.full);
expect(item.itunes.episode, 1);
expect(item.itunes.image.href, "https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357");
expect(item.itunes.season, 1);
expect(item.itunes.image.href,
"https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357");
expect(item.itunes.duration, Duration(minutes: 32, seconds: 30));
expect(item.itunes.explicit, false);
expect(item.itunes.keywords, "go,golang,open source,software,development".split(","));
expect(item.itunes.keywords,
"go,golang,open source,software,development".split(","));
expect(item.itunes.subtitle, "with Erik, Carlisia, and Brian");
expect(item.itunes.summary, "Foo");
expect(item.itunes.author, "Erik St. Martin, Carlisia Pinto, and Brian Ketelsen");
expect(item.itunes.author,
"Erik St. Martin, Carlisia Pinto, and Brian Ketelsen");
expect(item.itunes.explicit, false);
expect(item.itunes.title, "awesome title");
expect(item.itunes.block, false);
});
}

+ 12
- 0
test/xml/RSS-Itunes.xml Visa fil

@@ -1,6 +1,11 @@
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0" nighteye="disabled">
<channel>
<title>Go Time</title>
<itunes:title>Go Time</itunes:title>
<itunes:type>serial</itunes:type>
<itunes:block>Yes</itunes:block>
<itunes:complete>TRUE</itunes:complete>
<itunes:new-feed-url>wubawuba</itunes:new-feed-url>
<copyright>All rights reserved</copyright>
<link>https://changelog.com/gotime</link>
<atom:link href="https://changelog.com/gotime/feed" rel="self" type="application/rss+xml"/>
@@ -24,9 +29,16 @@
<itunes:category text="Software How-To"/>
<itunes:category text="Tech News"/>
</itunes:category>
<itunes:category text="Foo">
<itunes:category text="Bar"/>
<itunes:category text="Baz"/>
</itunes:category>
<item>
<itunes:title>awesome title</itunes:title>
<itunes:episodeType>full</itunes:episodeType>
<itunes:episode>1</itunes:episode>
<itunes:season>1</itunes:season>
<itunes:block>xxx</itunes:block>
<itunes:image href="https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357"/>
<itunes:duration>32:30</itunes:duration>
<itunes:explicit>no</itunes:explicit>


Laddar…
Avbryt
Spara