| @@ -0,0 +1,19 @@ | |||
| name: Dart CI | |||
| on: | |||
| push: | |||
| branches: [ master ] | |||
| pull_request: | |||
| branches: [ master ] | |||
| jobs: | |||
| build: | |||
| runs-on: ubuntu-latest | |||
| container: | |||
| image: google/dart:latest | |||
| steps: | |||
| - uses: actions/checkout@v2 | |||
| - name: Install dependencies | |||
| run: pub get | |||
| - name: Run tests | |||
| run: pub run test | |||
| @@ -1,10 +1,27 @@ | |||
| # Changelog | |||
| ## [0.6.0](https://pub.dartlang.org/packages/webfeed/versions/0.6.0) | |||
| - Refactor util/xml.dart | |||
| - Support RDF feed | |||
| - Support Syndication namespace | |||
| ## [0.5.2](https://pub.dartlang.org/packages/webfeed/versions/0.5.2) | |||
| - Lower the xml package version constraints | |||
| ## [0.5.1](https://pub.dartlang.org/packages/webfeed/versions/0.5.1) | |||
| - Support iTunes namespace [#19](https://github.com/witochandra/webfeed/pull/19) | |||
| - Parse date strings into DateTime [#22](https://github.com/witochandra/webfeed/pull/22) | |||
| - Add created & modified into dublin core namespace [#27](https://github.com/witochandra/webfeed/pull/27) | |||
| - Upgrade xml package [#28](https://github.com/witochandra/webfeed/issues/28) | |||
| - Fix linting warnings | |||
| ## [0.4.2](https://pub.dartlang.org/packages/webfeed/versions/0.4.2) | |||
| ### Fixed | |||
| - Bad import in `rss_content.dart` & `rss_source.dart` | |||
| ## [0.4.1](https://pub.dartlang.org/packages/webfeed/versions/0.4.1) | |||
| ### Added | |||
| - Support `author` in RssFeed | |||
| ## [0.4.0](https://pub.dartlang.org/packages/webfeed/versions/0.4.0) | |||
| ### Added | |||
| - Support for dublin core namespace | |||
| @@ -7,17 +7,19 @@ A dart package for parsing RSS and Atom feed. | |||
| ### Features | |||
| - [x] RSS | |||
| - [x] RSS (0.9, 1.0, & 2.0) | |||
| - [x] Atom | |||
| - [x] Namespaces | |||
| - [x] Media RSS | |||
| - [x] Dublin Core | |||
| - [x] iTunes | |||
| - [x] Syndication | |||
| ### Installing | |||
| Add this line into your `pubspec.yaml` | |||
| ``` | |||
| webfeed: ^0.4.2 | |||
| webfeed: ^0.6.0 | |||
| ``` | |||
| Import the package into your dart code using: | |||
| @@ -29,8 +31,8 @@ import 'package:webfeed/webfeed.dart'; | |||
| To parse string into `RssFeed` object use: | |||
| ``` | |||
| var rssFeed = new RssFeed.parse(xmlString); // for parsing RSS feed | |||
| var atomFeed = new AtomFeed.parse(xmlString); // for parsing Atom feed | |||
| var rssFeed = RssFeed.parse(xmlString); // for parsing RSS feed | |||
| var atomFeed = AtomFeed.parse(xmlString); // for parsing Atom feed | |||
| ``` | |||
| ### Preview | |||
| @@ -1,7 +1,4 @@ | |||
| analyzer: | |||
| language: | |||
| enablePreviewDart2: true | |||
| strong-mode: true | |||
| errors: | |||
| unused_import: error | |||
| unused_local_variable: error | |||
| @@ -35,3 +32,5 @@ linter: | |||
| - prefer_typing_uninitialized_variables | |||
| - slash_for_doc_comments | |||
| - type_init_formals | |||
| - unnecessary_new | |||
| - prefer_single_quotes | |||
| @@ -1,24 +1,19 @@ | |||
| import 'package:http/http.dart' as http; | |||
| import 'package:webfeed/webfeed.dart'; | |||
| void main() { | |||
| var client = new http.Client(); | |||
| void main() async { | |||
| var client = http.Client(); | |||
| // RSS feed | |||
| client.get("https://developer.apple.com/news/releases/rss/releases.rss").then((response) { | |||
| return response.body; | |||
| }).then((bodyString) { | |||
| var channel = new RssFeed.parse(bodyString); | |||
| print(channel); | |||
| return channel; | |||
| }); | |||
| var response = await client | |||
| .get('https://developer.apple.com/news/releases/rss/releases.rss'); | |||
| var channel = RssFeed.parse(response.body); | |||
| print(channel); | |||
| // Atom feed | |||
| client.get("https://www.theverge.com/rss/index.xml").then((response) { | |||
| return response.body; | |||
| }).then((bodyString) { | |||
| var feed = new AtomFeed.parse(bodyString); | |||
| print(feed); | |||
| return feed; | |||
| }); | |||
| response = await client.get('https://www.theverge.com/rss/index.xml'); | |||
| var feed = AtomFeed.parse(response.body); | |||
| print(feed); | |||
| client.close(); | |||
| } | |||
| @@ -8,9 +8,9 @@ class AtomCategory { | |||
| AtomCategory(this.term, this.scheme, this.label); | |||
| factory AtomCategory.parse(XmlElement element) { | |||
| var term = element.getAttribute("term"); | |||
| var scheme = element.getAttribute("scheme"); | |||
| var label = element.getAttribute("label"); | |||
| var term = element.getAttribute('term'); | |||
| var scheme = element.getAttribute('scheme'); | |||
| var label = element.getAttribute('label'); | |||
| return AtomCategory(term, scheme, label); | |||
| } | |||
| } | |||
| @@ -3,13 +3,14 @@ import 'package:webfeed/domain/atom_generator.dart'; | |||
| import 'package:webfeed/domain/atom_item.dart'; | |||
| import 'package:webfeed/domain/atom_link.dart'; | |||
| import 'package:webfeed/domain/atom_person.dart'; | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/util/datetime.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class AtomFeed { | |||
| final String id; | |||
| final String title; | |||
| final String updated; | |||
| final DateTime updated; | |||
| final List<AtomItem> items; | |||
| final List<AtomLink> links; | |||
| @@ -39,39 +40,42 @@ class AtomFeed { | |||
| }); | |||
| factory AtomFeed.parse(String xmlString) { | |||
| var document = parse(xmlString); | |||
| XmlElement feedElement; | |||
| try { | |||
| feedElement = document.findElements("feed").first; | |||
| } on StateError { | |||
| throw new ArgumentError("feed not found"); | |||
| var document = XmlDocument.parse(xmlString); | |||
| var feedElement = findFirstElement(document, 'feed'); | |||
| if (feedElement == null) { | |||
| throw ArgumentError('feed not found'); | |||
| } | |||
| return AtomFeed( | |||
| id: findElementOrNull(feedElement, "id")?.text, | |||
| title: findElementOrNull(feedElement, "title")?.text, | |||
| updated: findElementOrNull(feedElement, "updated")?.text, | |||
| items: feedElement.findElements("entry").map((element) { | |||
| return AtomItem.parse(element); | |||
| }).toList(), | |||
| links: feedElement.findElements("link").map((element) { | |||
| return AtomLink.parse(element); | |||
| }).toList(), | |||
| authors: feedElement.findElements("author").map((element) { | |||
| return AtomPerson.parse(element); | |||
| }).toList(), | |||
| contributors: feedElement.findElements("contributor").map((element) { | |||
| return AtomPerson.parse(element); | |||
| }).toList(), | |||
| categories: feedElement.findElements("category").map((element) { | |||
| return AtomCategory.parse(element); | |||
| }).toList(), | |||
| id: findFirstElement(feedElement, 'id')?.text, | |||
| title: findFirstElement(feedElement, 'title')?.text, | |||
| updated: parseDateTime(findFirstElement(feedElement, 'updated')?.text), | |||
| items: feedElement | |||
| .findElements('entry') | |||
| .map((e) => AtomItem.parse(e)) | |||
| .toList(), | |||
| links: feedElement | |||
| .findElements('link') | |||
| .map((e) => AtomLink.parse(e)) | |||
| .toList(), | |||
| authors: feedElement | |||
| .findElements('author') | |||
| .map((e) => AtomPerson.parse(e)) | |||
| .toList(), | |||
| contributors: feedElement | |||
| .findElements('contributor') | |||
| .map((e) => AtomPerson.parse(e)) | |||
| .toList(), | |||
| categories: feedElement | |||
| .findElements('category') | |||
| .map((e) => AtomCategory.parse(e)) | |||
| .toList(), | |||
| generator: | |||
| AtomGenerator.parse(findElementOrNull(feedElement, "generator")), | |||
| icon: findElementOrNull(feedElement, "icon")?.text, | |||
| logo: findElementOrNull(feedElement, "logo")?.text, | |||
| rights: findElementOrNull(feedElement, "rights")?.text, | |||
| subtitle: findElementOrNull(feedElement, "subtitle")?.text, | |||
| AtomGenerator.parse(findFirstElement(feedElement, 'generator')), | |||
| icon: findFirstElement(feedElement, 'icon')?.text, | |||
| logo: findFirstElement(feedElement, 'logo')?.text, | |||
| rights: findFirstElement(feedElement, 'rights')?.text, | |||
| subtitle: findFirstElement(feedElement, 'subtitle')?.text, | |||
| ); | |||
| } | |||
| } | |||
| @@ -11,9 +11,9 @@ class AtomGenerator { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| var uri = element.getAttribute("uri"); | |||
| var version = element.getAttribute("version"); | |||
| var uri = element.getAttribute('uri'); | |||
| var version = element.getAttribute('version'); | |||
| var value = element.text; | |||
| return new AtomGenerator(uri, version, value); | |||
| return AtomGenerator(uri, version, value); | |||
| } | |||
| } | |||
| @@ -3,13 +3,14 @@ import 'package:webfeed/domain/atom_link.dart'; | |||
| import 'package:webfeed/domain/atom_person.dart'; | |||
| import 'package:webfeed/domain/atom_source.dart'; | |||
| import 'package:webfeed/domain/media/media.dart'; | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/util/datetime.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class AtomItem { | |||
| final String id; | |||
| final String title; | |||
| final String updated; | |||
| final DateTime updated; | |||
| final List<AtomPerson> authors; | |||
| final List<AtomLink> links; | |||
| @@ -40,26 +41,28 @@ class AtomItem { | |||
| factory AtomItem.parse(XmlElement element) { | |||
| return AtomItem( | |||
| id: findElementOrNull(element, "id")?.text, | |||
| title: findElementOrNull(element, "title")?.text, | |||
| updated: findElementOrNull(element, "updated")?.text, | |||
| authors: element.findElements("author").map((element) { | |||
| return AtomPerson.parse(element); | |||
| }).toList(), | |||
| links: element.findElements("link").map((element) { | |||
| return AtomLink.parse(element); | |||
| }).toList(), | |||
| categories: element.findElements("category").map((element) { | |||
| return AtomCategory.parse(element); | |||
| }).toList(), | |||
| contributors: element.findElements("contributor").map((element) { | |||
| return AtomPerson.parse(element); | |||
| }).toList(), | |||
| source: AtomSource.parse(findElementOrNull(element, "source")), | |||
| published: findElementOrNull(element, "published")?.text, | |||
| content: findElementOrNull(element, "content")?.text, | |||
| summary: findElementOrNull(element, "summary")?.text, | |||
| rights: findElementOrNull(element, "rights")?.text, | |||
| id: findFirstElement(element, 'id')?.text, | |||
| title: findFirstElement(element, 'title')?.text, | |||
| updated: parseDateTime(findFirstElement(element, 'updated')?.text), | |||
| authors: element | |||
| .findElements('author') | |||
| .map((e) => AtomPerson.parse(e)) | |||
| .toList(), | |||
| links: | |||
| element.findElements('link').map((e) => AtomLink.parse(e)).toList(), | |||
| categories: element | |||
| .findElements('category') | |||
| .map((e) => AtomCategory.parse(e)) | |||
| .toList(), | |||
| contributors: element | |||
| .findElements('contributor') | |||
| .map((e) => AtomPerson.parse(e)) | |||
| .toList(), | |||
| source: AtomSource.parse(findFirstElement(element, 'source')), | |||
| published: findFirstElement(element, 'published')?.text, | |||
| content: findFirstElement(element, 'content')?.text, | |||
| summary: findFirstElement(element, 'summary')?.text, | |||
| rights: findFirstElement(element, 'rights')?.text, | |||
| media: Media.parse(element), | |||
| ); | |||
| } | |||
| @@ -18,14 +18,14 @@ class AtomLink { | |||
| ); | |||
| factory AtomLink.parse(XmlElement element) { | |||
| var href = element.getAttribute("href"); | |||
| var rel = element.getAttribute("rel"); | |||
| var type = element.getAttribute("type"); | |||
| var title = element.getAttribute("title"); | |||
| var hreflang = element.getAttribute("hreflang"); | |||
| var href = element.getAttribute('href'); | |||
| var rel = element.getAttribute('rel'); | |||
| var type = element.getAttribute('type'); | |||
| var title = element.getAttribute('title'); | |||
| var hreflang = element.getAttribute('hreflang'); | |||
| var length = 0; | |||
| if (element.getAttribute("length") != null) { | |||
| length = int.parse(element.getAttribute("length")); | |||
| if (element.getAttribute('length') != null) { | |||
| length = int.parse(element.getAttribute('length')); | |||
| } | |||
| return AtomLink(href, rel, type, hreflang, title, length); | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class AtomPerson { | |||
| @@ -6,12 +6,13 @@ class AtomPerson { | |||
| final String uri; | |||
| final String email; | |||
| AtomPerson(this.name, this.uri, this.email); | |||
| AtomPerson({this.name, this.uri, this.email}); | |||
| factory AtomPerson.parse(XmlElement element) { | |||
| var name = findElementOrNull(element, "name")?.text; | |||
| var uri = findElementOrNull(element, "uri")?.text; | |||
| var email = findElementOrNull(element, "email")?.text; | |||
| return AtomPerson(name, uri, email); | |||
| return AtomPerson( | |||
| name: findFirstElement(element, 'name')?.text, | |||
| uri: findFirstElement(element, 'uri')?.text, | |||
| email: findFirstElement(element, 'email')?.text, | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class AtomSource { | |||
| @@ -6,16 +6,20 @@ class AtomSource { | |||
| final String title; | |||
| final String updated; | |||
| AtomSource(this.id, this.title, this.updated); | |||
| AtomSource({ | |||
| this.id, | |||
| this.title, | |||
| this.updated, | |||
| }); | |||
| factory AtomSource.parse(XmlElement element) { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| var id = findElementOrNull(element, "id")?.text; | |||
| var title = findElementOrNull(element, "title")?.text; | |||
| var updated = findElementOrNull(element, "updated")?.text; | |||
| return AtomSource(id, title, updated); | |||
| return AtomSource( | |||
| id: findFirstElement(element, 'id')?.text, | |||
| title: findFirstElement(element, 'title')?.text, | |||
| updated: findFirstElement(element, 'updated')?.text, | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/util/datetime.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class DublinCore { | |||
| @@ -8,7 +9,9 @@ class DublinCore { | |||
| final String subject; | |||
| final String publisher; | |||
| final String contributor; | |||
| final String date; | |||
| final DateTime date; | |||
| final DateTime created; | |||
| final DateTime modified; | |||
| final String type; | |||
| final String format; | |||
| final String identifier; | |||
| @@ -26,6 +29,8 @@ class DublinCore { | |||
| this.publisher, | |||
| this.contributor, | |||
| this.date, | |||
| this.created, | |||
| this.modified, | |||
| this.type, | |||
| this.format, | |||
| this.identifier, | |||
| @@ -41,21 +46,23 @@ class DublinCore { | |||
| return null; | |||
| } | |||
| return DublinCore( | |||
| title: findElementOrNull(element, "dc:title")?.text, | |||
| description: findElementOrNull(element, "dc:description")?.text, | |||
| creator: findElementOrNull(element, "dc:creator")?.text, | |||
| subject: findElementOrNull(element, "dc:subject")?.text, | |||
| publisher: findElementOrNull(element, "dc:publisher")?.text, | |||
| contributor: findElementOrNull(element, "dc:contributor")?.text, | |||
| date: findElementOrNull(element, "dc:date")?.text, | |||
| type: findElementOrNull(element, "dc:type")?.text, | |||
| format: findElementOrNull(element, "dc:format")?.text, | |||
| identifier: findElementOrNull(element, "dc:identifier")?.text, | |||
| source: findElementOrNull(element, "dc:source")?.text, | |||
| language: findElementOrNull(element, "dc:language")?.text, | |||
| relation: findElementOrNull(element, "dc:relation")?.text, | |||
| coverage: findElementOrNull(element, "dc:coverage")?.text, | |||
| rights: findElementOrNull(element, "dc:rights")?.text, | |||
| title: findFirstElement(element, 'dc:title')?.text, | |||
| description: findFirstElement(element, 'dc:description')?.text, | |||
| creator: findFirstElement(element, 'dc:creator')?.text, | |||
| subject: findFirstElement(element, 'dc:subject')?.text, | |||
| publisher: findFirstElement(element, 'dc:publisher')?.text, | |||
| contributor: findFirstElement(element, 'dc:contributor')?.text, | |||
| date: parseDateTime(findFirstElement(element, 'dc:date')?.text), | |||
| created: parseDateTime(findFirstElement(element, 'dc:created')?.text), | |||
| modified: parseDateTime(findFirstElement(element, 'dc:modified')?.text), | |||
| type: findFirstElement(element, 'dc:type')?.text, | |||
| format: findFirstElement(element, 'dc:format')?.text, | |||
| identifier: findFirstElement(element, 'dc:identifier')?.text, | |||
| source: findFirstElement(element, 'dc:source')?.text, | |||
| language: findFirstElement(element, 'dc:language')?.text, | |||
| relation: findFirstElement(element, 'dc:relation')?.text, | |||
| coverage: findFirstElement(element, 'dc:coverage')?.text, | |||
| rights: findFirstElement(element, 'dc:rights')?.text, | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,102 @@ | |||
| import 'package:webfeed/domain/itunes/itunes_category.dart'; | |||
| import 'package:webfeed/domain/itunes/itunes_episode_type.dart'; | |||
| import 'package:webfeed/domain/itunes/itunes_image.dart'; | |||
| import 'package:webfeed/domain/itunes/itunes_owner.dart'; | |||
| import 'package:webfeed/domain/itunes/itunes_type.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class Itunes { | |||
| final String author; | |||
| final String summary; | |||
| final bool explicit; | |||
| final String title; | |||
| final String subtitle; | |||
| final ItunesOwner owner; | |||
| final List<String> keywords; | |||
| final ItunesImage image; | |||
| final List<ItunesCategory> categories; | |||
| final ItunesType type; | |||
| final String newFeedUrl; | |||
| final bool block; | |||
| final bool complete; | |||
| final int episode; | |||
| final int season; | |||
| final Duration duration; | |||
| final ItunesEpisodeType episodeType; | |||
| Itunes({ | |||
| this.author, | |||
| this.summary, | |||
| this.explicit, | |||
| this.title, | |||
| this.subtitle, | |||
| this.owner, | |||
| this.keywords, | |||
| this.image, | |||
| this.categories, | |||
| this.type, | |||
| this.newFeedUrl, | |||
| this.block, | |||
| this.complete, | |||
| this.episode, | |||
| this.season, | |||
| this.duration, | |||
| this.episodeType, | |||
| }); | |||
| factory Itunes.parse(XmlElement element) { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| var episodeStr = findFirstElement(element, 'itunes:episode')?.text; | |||
| var seasonStr = findFirstElement(element, 'itunes:season')?.text; | |||
| var durationStr = findFirstElement(element, 'itunes:duration')?.text; | |||
| return Itunes( | |||
| author: findFirstElement(element, 'itunes:author')?.text, | |||
| summary: findFirstElement(element, 'itunes:summary')?.text, | |||
| explicit: parseBoolLiteral(element, 'itunes:explicit'), | |||
| title: findFirstElement(element, 'itunes:title')?.text, | |||
| subtitle: findFirstElement(element, 'itunes:subtitle')?.text, | |||
| owner: ItunesOwner.parse(findFirstElement(element, 'itunes:owner')), | |||
| keywords: findFirstElement(element, 'itunes:keywords') | |||
| ?.text | |||
| ?.split(',') | |||
| ?.map((keyword) => keyword.trim()) | |||
| ?.toList() ?? | |||
| [], | |||
| image: ItunesImage.parse(findFirstElement(element, 'itunes:image')), | |||
| categories: findElements(element, 'itunes:category') | |||
| .map((e) => ItunesCategory.parse(e)) | |||
| .toList(), | |||
| type: newItunesType(findFirstElement(element, 'itunes:type')), | |||
| newFeedUrl: findFirstElement(element, 'itunes:new-feed-url')?.text, | |||
| block: parseBoolLiteral(element, 'itunes:block'), | |||
| complete: parseBoolLiteral(element, 'itunes:complete'), | |||
| episode: episodeStr == null ? null : int.parse(episodeStr), | |||
| season: seasonStr == null ? null : int.parse(seasonStr), | |||
| duration: durationStr == null ? null : _parseDuration(durationStr), | |||
| episodeType: | |||
| newItunesEpisodeType(findFirstElement(element, 'itunes:episodeType')), | |||
| ); | |||
| } | |||
| static Duration _parseDuration(String s) { | |||
| var hours = 0; | |||
| var minutes = 0; | |||
| var seconds = 0; | |||
| var parts = s.split(':'); | |||
| if (parts.length > 2) { | |||
| hours = int.parse(parts[parts.length - 3]); | |||
| } | |||
| if (parts.length > 1) { | |||
| minutes = int.parse(parts[parts.length - 2]); | |||
| } | |||
| seconds = int.parse(parts[parts.length - 1]); | |||
| return Duration( | |||
| hours: hours, | |||
| minutes: minutes, | |||
| seconds: seconds, | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| import 'package:xml/xml.dart'; | |||
| class ItunesCategory { | |||
| final String category; | |||
| final List<String> subCategories; | |||
| ItunesCategory({this.category, this.subCategories}); | |||
| factory ItunesCategory.parse(XmlElement element) { | |||
| if (element == null) return null; | |||
| return ItunesCategory( | |||
| category: element.getAttribute('text')?.trim(), | |||
| subCategories: element | |||
| .findElements('itunes:category') | |||
| ?.map((e) => e.getAttribute('text')?.trim()) | |||
| ?.toList() ?? | |||
| [], | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| import 'package:xml/xml.dart'; | |||
| enum ItunesEpisodeType { full, trailer, bonus, unknown } | |||
| ItunesEpisodeType newItunesEpisodeType(XmlElement element) { | |||
| switch (element?.text) { | |||
| case 'full': | |||
| return ItunesEpisodeType.full; | |||
| case 'trailer': | |||
| return ItunesEpisodeType.trailer; | |||
| case 'bonus': | |||
| return ItunesEpisodeType.bonus; | |||
| default: | |||
| return ItunesEpisodeType.unknown; | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| import 'package:xml/xml.dart'; | |||
| class ItunesImage { | |||
| final String href; | |||
| ItunesImage({this.href}); | |||
| factory ItunesImage.parse(XmlElement element) { | |||
| if (element == null) return null; | |||
| return ItunesImage( | |||
| href: element.getAttribute('href')?.trim(), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class ItunesOwner { | |||
| final String name; | |||
| final String email; | |||
| ItunesOwner({this.name, this.email}); | |||
| factory ItunesOwner.parse(XmlElement element) { | |||
| if (element == null) return null; | |||
| return ItunesOwner( | |||
| name: findFirstElement(element, 'itunes:name')?.text?.trim(), | |||
| email: findFirstElement(element, 'itunes:email')?.text?.trim(), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| import 'package:xml/xml.dart'; | |||
| enum ItunesType { episodic, serial, unknown } | |||
| ItunesType newItunesType(XmlElement element) { | |||
| switch (element?.text) { | |||
| case 'episodic': | |||
| return ItunesType.episodic; | |||
| case 'serial': | |||
| return ItunesType.serial; | |||
| default: | |||
| return ItunesType.unknown; | |||
| } | |||
| } | |||
| @@ -15,9 +15,9 @@ class Category { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Category( | |||
| scheme: element.getAttribute("scheme"), | |||
| label: element.getAttribute("label"), | |||
| return Category( | |||
| scheme: element.getAttribute('scheme'), | |||
| label: element.getAttribute('label'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| import 'package:webfeed/domain/media/star_rating.dart'; | |||
| import 'package:webfeed/domain/media/statistics.dart'; | |||
| import 'package:webfeed/domain/media/tags.dart'; | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class Community { | |||
| @@ -19,15 +19,15 @@ class Community { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Community( | |||
| starRating: new StarRating.parse( | |||
| findElementOrNull(element, "media:starRating"), | |||
| return Community( | |||
| starRating: StarRating.parse( | |||
| findFirstElement(element, 'media:starRating'), | |||
| ), | |||
| statistics: new Statistics.parse( | |||
| findElementOrNull(element, "media:statistics"), | |||
| statistics: Statistics.parse( | |||
| findFirstElement(element, 'media:statistics'), | |||
| ), | |||
| tags: new Tags.parse( | |||
| findElementOrNull(element, "media:tags"), | |||
| tags: Tags.parse( | |||
| findFirstElement(element, 'media:tags'), | |||
| ), | |||
| ); | |||
| } | |||
| @@ -34,23 +34,23 @@ class Content { | |||
| }); | |||
| factory Content.parse(XmlElement element) { | |||
| return new Content( | |||
| url: element.getAttribute("url"), | |||
| type: element.getAttribute("type"), | |||
| fileSize: int.tryParse(element.getAttribute("fileSize") ?? "0"), | |||
| medium: element.getAttribute("medium"), | |||
| isDefault: element.getAttribute("isDefault") == "true", | |||
| expression: element.getAttribute("expression"), | |||
| bitrate: int.tryParse(element.getAttribute("bitrate") ?? "0"), | |||
| framerate: double.tryParse(element.getAttribute("framerate") ?? "0"), | |||
| return Content( | |||
| url: element.getAttribute('url'), | |||
| type: element.getAttribute('type'), | |||
| fileSize: int.tryParse(element.getAttribute('fileSize') ?? '0'), | |||
| medium: element.getAttribute('medium'), | |||
| isDefault: element.getAttribute('isDefault') == 'true', | |||
| expression: element.getAttribute('expression'), | |||
| bitrate: int.tryParse(element.getAttribute('bitrate') ?? '0'), | |||
| framerate: double.tryParse(element.getAttribute('framerate') ?? '0'), | |||
| samplingrate: double.tryParse( | |||
| element.getAttribute("samplingrate") ?? "0", | |||
| element.getAttribute('samplingrate') ?? '0', | |||
| ), | |||
| channels: int.tryParse(element.getAttribute("channels") ?? "0"), | |||
| duration: int.tryParse(element.getAttribute("duration") ?? "0"), | |||
| height: int.tryParse(element.getAttribute("height") ?? "0"), | |||
| width: int.tryParse(element.getAttribute("width") ?? "0"), | |||
| lang: element.getAttribute("lang"), | |||
| channels: int.tryParse(element.getAttribute('channels') ?? '0'), | |||
| duration: int.tryParse(element.getAttribute('duration') ?? '0'), | |||
| height: int.tryParse(element.getAttribute('height') ?? '0'), | |||
| width: int.tryParse(element.getAttribute('width') ?? '0'), | |||
| lang: element.getAttribute('lang'), | |||
| ); | |||
| } | |||
| } | |||
| @@ -13,8 +13,8 @@ class Copyright { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Copyright( | |||
| url: element.getAttribute("url"), | |||
| return Copyright( | |||
| url: element.getAttribute('url'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -12,9 +12,9 @@ class Credit { | |||
| }); | |||
| factory Credit.parse(XmlElement element) { | |||
| return new Credit( | |||
| role: element.getAttribute("role"), | |||
| scheme: element.getAttribute("scheme"), | |||
| return Credit( | |||
| role: element.getAttribute('role'), | |||
| scheme: element.getAttribute('scheme'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -13,8 +13,8 @@ class Description { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Description( | |||
| type: element.getAttribute("type"), | |||
| return Description( | |||
| type: element.getAttribute('type'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -18,12 +18,12 @@ class Embed { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Embed( | |||
| url: element.getAttribute("url"), | |||
| width: int.tryParse(element.getAttribute("width") ?? "0"), | |||
| height: int.tryParse(element.getAttribute("height") ?? "0"), | |||
| params: element.findElements("media:param").map((e) { | |||
| return new Param.parse(e); | |||
| return Embed( | |||
| url: element.getAttribute('url'), | |||
| width: int.tryParse(element.getAttribute('width') ?? '0'), | |||
| height: int.tryParse(element.getAttribute('height') ?? '0'), | |||
| params: element.findElements('media:param').map((e) { | |||
| return Param.parse(e); | |||
| }).toList(), | |||
| ); | |||
| } | |||
| @@ -2,7 +2,7 @@ import 'package:webfeed/domain/media/category.dart'; | |||
| import 'package:webfeed/domain/media/content.dart'; | |||
| import 'package:webfeed/domain/media/credit.dart'; | |||
| import 'package:webfeed/domain/media/rating.dart'; | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class Group { | |||
| @@ -22,18 +22,18 @@ class Group { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Group( | |||
| contents: element.findElements("media:content").map((e) { | |||
| return new Content.parse(e); | |||
| return Group( | |||
| contents: element.findElements('media:content').map((e) { | |||
| return Content.parse(e); | |||
| }).toList(), | |||
| credits: element.findElements("media:credit").map((e) { | |||
| return new Credit.parse(e); | |||
| credits: element.findElements('media:credit').map((e) { | |||
| return Credit.parse(e); | |||
| }).toList(), | |||
| category: new Category.parse( | |||
| findElementOrNull(element, "media:category"), | |||
| category: Category.parse( | |||
| findFirstElement(element, 'media:category'), | |||
| ), | |||
| rating: new Rating.parse( | |||
| findElementOrNull(element, "media:rating"), | |||
| rating: Rating.parse( | |||
| findFirstElement(element, 'media:rating'), | |||
| ), | |||
| ); | |||
| } | |||
| @@ -13,8 +13,8 @@ class Hash { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Hash( | |||
| algo: element.getAttribute("algo"), | |||
| return Hash( | |||
| algo: element.getAttribute('algo'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -15,9 +15,9 @@ class License { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new License( | |||
| type: element.getAttribute("type"), | |||
| href: element.getAttribute("href"), | |||
| return License( | |||
| type: element.getAttribute('type'), | |||
| href: element.getAttribute('href'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -19,7 +19,7 @@ import 'package:webfeed/domain/media/status.dart'; | |||
| import 'package:webfeed/domain/media/text.dart'; | |||
| import 'package:webfeed/domain/media/thumbnail.dart'; | |||
| import 'package:webfeed/domain/media/title.dart'; | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class Media { | |||
| @@ -78,90 +78,90 @@ class Media { | |||
| }); | |||
| factory Media.parse(XmlElement element) { | |||
| return new Media( | |||
| group: new Group.parse( | |||
| findElementOrNull(element, "media:group"), | |||
| return Media( | |||
| group: Group.parse( | |||
| findFirstElement(element, 'media:group'), | |||
| ), | |||
| contents: element.findElements("media:content").map((e) { | |||
| return new Content.parse(e); | |||
| contents: element.findElements('media:content').map((e) { | |||
| return Content.parse(e); | |||
| }).toList(), | |||
| credits: element.findElements("media:credit").map((e) { | |||
| return new Credit.parse(e); | |||
| credits: element.findElements('media:credit').map((e) { | |||
| return Credit.parse(e); | |||
| }).toList(), | |||
| category: new Category.parse( | |||
| findElementOrNull(element, "media:category"), | |||
| category: Category.parse( | |||
| findFirstElement(element, 'media:category'), | |||
| ), | |||
| rating: new Rating.parse( | |||
| findElementOrNull(element, "media:rating"), | |||
| rating: Rating.parse( | |||
| findFirstElement(element, 'media:rating'), | |||
| ), | |||
| title: new Title.parse( | |||
| findElementOrNull(element, "media:title"), | |||
| title: Title.parse( | |||
| findFirstElement(element, 'media:title'), | |||
| ), | |||
| description: new Description.parse( | |||
| findElementOrNull(element, "media:description"), | |||
| description: Description.parse( | |||
| findFirstElement(element, 'media:description'), | |||
| ), | |||
| keywords: findElementOrNull(element, "media:keywords")?.text, | |||
| thumbnails: element.findElements("media:thumbnail").map((e) { | |||
| return new Thumbnail.parse(e); | |||
| keywords: findFirstElement(element, 'media:keywords')?.text, | |||
| thumbnails: element.findElements('media:thumbnail').map((e) { | |||
| return Thumbnail.parse(e); | |||
| }).toList(), | |||
| hash: new Hash.parse( | |||
| findElementOrNull(element, "media:hash"), | |||
| hash: Hash.parse( | |||
| findFirstElement(element, 'media:hash'), | |||
| ), | |||
| player: new Player.parse( | |||
| findElementOrNull(element, "media:player"), | |||
| player: Player.parse( | |||
| findFirstElement(element, 'media:player'), | |||
| ), | |||
| copyright: new Copyright.parse( | |||
| findElementOrNull(element, "media:copyright"), | |||
| copyright: Copyright.parse( | |||
| findFirstElement(element, 'media:copyright'), | |||
| ), | |||
| text: new Text.parse( | |||
| findElementOrNull(element, "media:text"), | |||
| text: Text.parse( | |||
| findFirstElement(element, 'media:text'), | |||
| ), | |||
| restriction: new Restriction.parse( | |||
| findElementOrNull(element, "media:restriction"), | |||
| restriction: Restriction.parse( | |||
| findFirstElement(element, 'media:restriction'), | |||
| ), | |||
| community: new Community.parse( | |||
| findElementOrNull(element, "media:community"), | |||
| community: Community.parse( | |||
| findFirstElement(element, 'media:community'), | |||
| ), | |||
| comments: findElementOrNull(element, "media:comments") | |||
| ?.findElements("media:comment") | |||
| comments: findFirstElement(element, 'media:comments') | |||
| ?.findElements('media:comment') | |||
| ?.map((e) { | |||
| return e.text; | |||
| })?.toList() ?? | |||
| [], | |||
| embed: new Embed.parse( | |||
| findElementOrNull(element, "media:embed"), | |||
| embed: Embed.parse( | |||
| findFirstElement(element, 'media:embed'), | |||
| ), | |||
| responses: findElementOrNull(element, "media:responses") | |||
| ?.findElements("media:response") | |||
| responses: findFirstElement(element, 'media:responses') | |||
| ?.findElements('media:response') | |||
| ?.map((e) { | |||
| return e.text; | |||
| })?.toList() ?? | |||
| [], | |||
| backLinks: findElementOrNull(element, "media:backLinks") | |||
| ?.findElements("media:backLink") | |||
| backLinks: findFirstElement(element, 'media:backLinks') | |||
| ?.findElements('media:backLink') | |||
| ?.map((e) { | |||
| return e.text; | |||
| })?.toList() ?? | |||
| [], | |||
| status: new Status.parse( | |||
| findElementOrNull(element, "media:status"), | |||
| status: Status.parse( | |||
| findFirstElement(element, 'media:status'), | |||
| ), | |||
| prices: element.findElements("media:price").map((e) { | |||
| return new Price.parse(e); | |||
| prices: element.findElements('media:price').map((e) { | |||
| return Price.parse(e); | |||
| }).toList(), | |||
| license: new License.parse( | |||
| findElementOrNull(element, "media:license"), | |||
| license: License.parse( | |||
| findFirstElement(element, 'media:license'), | |||
| ), | |||
| peerLink: new PeerLink.parse( | |||
| findElementOrNull(element, "media:peerLink"), | |||
| peerLink: PeerLink.parse( | |||
| findFirstElement(element, 'media:peerLink'), | |||
| ), | |||
| rights: new Rights.parse( | |||
| findElementOrNull(element, "media:rights"), | |||
| rights: Rights.parse( | |||
| findFirstElement(element, 'media:rights'), | |||
| ), | |||
| scenes: findElementOrNull(element, "media:scenes") | |||
| ?.findElements("media:scene") | |||
| scenes: findFirstElement(element, 'media:scenes') | |||
| ?.findElements('media:scene') | |||
| ?.map((e) { | |||
| return new Scene.parse(e); | |||
| return Scene.parse(e); | |||
| })?.toList() ?? | |||
| [], | |||
| ); | |||
| @@ -13,8 +13,8 @@ class Param { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Param( | |||
| name: element.getAttribute("name"), | |||
| return Param( | |||
| name: element.getAttribute('name'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -15,9 +15,9 @@ class PeerLink { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new PeerLink( | |||
| type: element.getAttribute("type"), | |||
| href: element.getAttribute("href"), | |||
| return PeerLink( | |||
| type: element.getAttribute('type'), | |||
| href: element.getAttribute('href'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -17,10 +17,10 @@ class Player { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Player( | |||
| url: element.getAttribute("url"), | |||
| width: int.tryParse(element.getAttribute("width") ?? "0"), | |||
| height: int.tryParse(element.getAttribute("height") ?? "0"), | |||
| return Player( | |||
| url: element.getAttribute('url'), | |||
| width: int.tryParse(element.getAttribute('width') ?? '0'), | |||
| height: int.tryParse(element.getAttribute('height') ?? '0'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -14,11 +14,11 @@ class Price { | |||
| }); | |||
| factory Price.parse(XmlElement element) { | |||
| return new Price( | |||
| price: double.tryParse(element.getAttribute("price") ?? "0"), | |||
| type: element.getAttribute("type"), | |||
| info: element.getAttribute("info"), | |||
| currency: element.getAttribute("currency"), | |||
| return Price( | |||
| price: double.tryParse(element.getAttribute('price') ?? '0'), | |||
| type: element.getAttribute('type'), | |||
| info: element.getAttribute('info'), | |||
| currency: element.getAttribute('currency'), | |||
| ); | |||
| } | |||
| } | |||
| @@ -13,8 +13,8 @@ class Rating { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Rating( | |||
| scheme: element.getAttribute("scheme"), | |||
| return Rating( | |||
| scheme: element.getAttribute('scheme'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -15,9 +15,9 @@ class Restriction { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Restriction( | |||
| relationship: element.getAttribute("relationship"), | |||
| type: element.getAttribute("type"), | |||
| return Restriction( | |||
| relationship: element.getAttribute('relationship'), | |||
| type: element.getAttribute('type'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -11,8 +11,8 @@ class Rights { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Rights( | |||
| status: element.getAttribute("status"), | |||
| return Rights( | |||
| status: element.getAttribute('status'), | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class Scene { | |||
| @@ -18,11 +18,11 @@ class Scene { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Scene( | |||
| title: findElementOrNull(element, "sceneTitle")?.text, | |||
| description: findElementOrNull(element, "sceneDescription")?.text, | |||
| startTime: findElementOrNull(element, "sceneStartTime")?.text, | |||
| endTime: findElementOrNull(element, "sceneEndTime")?.text, | |||
| return Scene( | |||
| title: findFirstElement(element, 'sceneTitle')?.text, | |||
| description: findFirstElement(element, 'sceneDescription')?.text, | |||
| startTime: findFirstElement(element, 'sceneStartTime')?.text, | |||
| endTime: findFirstElement(element, 'sceneEndTime')?.text, | |||
| ); | |||
| } | |||
| } | |||
| @@ -14,11 +14,11 @@ class StarRating { | |||
| }); | |||
| factory StarRating.parse(XmlElement element) { | |||
| return new StarRating( | |||
| average: double.tryParse(element.getAttribute("average") ?? "0"), | |||
| count: int.tryParse(element.getAttribute("count") ?? "0"), | |||
| min: int.tryParse(element.getAttribute("min") ?? "0"), | |||
| max: int.tryParse(element.getAttribute("max") ?? "0"), | |||
| return StarRating( | |||
| average: double.tryParse(element.getAttribute('average') ?? '0'), | |||
| count: int.tryParse(element.getAttribute('count') ?? '0'), | |||
| min: int.tryParse(element.getAttribute('min') ?? '0'), | |||
| max: int.tryParse(element.getAttribute('max') ?? '0'), | |||
| ); | |||
| } | |||
| } | |||
| @@ -10,9 +10,9 @@ class Statistics { | |||
| }); | |||
| factory Statistics.parse(XmlElement element) { | |||
| return new Statistics( | |||
| views: int.tryParse(element.getAttribute("views") ?? "0"), | |||
| favorites: int.tryParse(element.getAttribute("favorites") ?? "0"), | |||
| return Statistics( | |||
| views: int.tryParse(element.getAttribute('views') ?? '0'), | |||
| favorites: int.tryParse(element.getAttribute('favorites') ?? '0'), | |||
| ); | |||
| } | |||
| } | |||
| @@ -13,9 +13,9 @@ class Status { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Status( | |||
| state: element.getAttribute("state"), | |||
| reason: element.getAttribute("reason"), | |||
| return Status( | |||
| state: element.getAttribute('state'), | |||
| reason: element.getAttribute('reason'), | |||
| ); | |||
| } | |||
| } | |||
| @@ -13,9 +13,9 @@ class Tags { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Tags( | |||
| return Tags( | |||
| tags: element.text, | |||
| weight: int.tryParse(element.getAttribute("weight") ?? "1"), | |||
| weight: int.tryParse(element.getAttribute('weight') ?? '1'), | |||
| ); | |||
| } | |||
| } | |||
| @@ -19,11 +19,11 @@ class Text { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Text( | |||
| type: element.getAttribute("type"), | |||
| lang: element.getAttribute("lang"), | |||
| start: element.getAttribute("start"), | |||
| end: element.getAttribute("end"), | |||
| return Text( | |||
| type: element.getAttribute('type'), | |||
| lang: element.getAttribute('lang'), | |||
| start: element.getAttribute('start'), | |||
| end: element.getAttribute('end'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -14,11 +14,11 @@ class Thumbnail { | |||
| }); | |||
| factory Thumbnail.parse(XmlElement element) { | |||
| return new Thumbnail( | |||
| url: element.getAttribute("url"), | |||
| width: element.getAttribute("width"), | |||
| height: element.getAttribute("height"), | |||
| time: element.getAttribute("time"), | |||
| return Thumbnail( | |||
| url: element.getAttribute('url'), | |||
| width: element.getAttribute('width'), | |||
| height: element.getAttribute('height'), | |||
| time: element.getAttribute('time'), | |||
| ); | |||
| } | |||
| } | |||
| @@ -13,8 +13,8 @@ class Title { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return new Title( | |||
| type: element.getAttribute("type"), | |||
| return Title( | |||
| type: element.getAttribute('type'), | |||
| value: element.text, | |||
| ); | |||
| } | |||
| @@ -10,7 +10,7 @@ class RssCategory { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| var domain = element.getAttribute("domain"); | |||
| var domain = element.getAttribute('domain'); | |||
| var value = element.text; | |||
| return RssCategory(domain, value); | |||
| @@ -19,11 +19,11 @@ class RssCloud { | |||
| if (node == null) { | |||
| return null; | |||
| } | |||
| var domain = node.getAttribute("domain"); | |||
| var port = node.getAttribute("port"); | |||
| var path = node.getAttribute("path"); | |||
| var registerProcedure = node.getAttribute("registerProcedure"); | |||
| var protocol = node.getAttribute("protocol"); | |||
| var domain = node.getAttribute('domain'); | |||
| var port = node.getAttribute('port'); | |||
| var path = node.getAttribute('path'); | |||
| var registerProcedure = node.getAttribute('registerProcedure'); | |||
| var protocol = node.getAttribute('protocol'); | |||
| return RssCloud(domain, port, path, registerProcedure, protocol); | |||
| } | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| import 'package:xml/xml.dart'; | |||
| final _imagesRegExp = new RegExp( | |||
| final _imagesRegExp = RegExp( | |||
| "<img\\s.*?src=(?:'|\")([^'\">]+)(?:'|\")", | |||
| multiLine: true, | |||
| caseSensitive: false, | |||
| @@ -11,9 +11,9 @@ class RssEnclosure { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| var url = element.getAttribute("url"); | |||
| var type = element.getAttribute("type"); | |||
| var length = int.tryParse(element.getAttribute("length") ?? "0"); | |||
| var url = element.getAttribute('url'); | |||
| var type = element.getAttribute('type'); | |||
| var length = int.tryParse(element.getAttribute('length') ?? '0'); | |||
| return RssEnclosure(url, type, length); | |||
| } | |||
| } | |||
| @@ -1,15 +1,15 @@ | |||
| import 'dart:core'; | |||
| import 'package:webfeed/domain/dublin_core/dublin_core.dart'; | |||
| import 'package:webfeed/domain/itunes/itunes.dart'; | |||
| import 'package:webfeed/domain/rss_category.dart'; | |||
| import 'package:webfeed/domain/rss_cloud.dart'; | |||
| import 'package:webfeed/domain/rss_image.dart'; | |||
| import 'package:webfeed/domain/rss_item.dart'; | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/domain/syndication/syndication.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| import 'rss_itunes.dart'; | |||
| class RssFeed { | |||
| final String title; | |||
| final String author; | |||
| @@ -32,7 +32,8 @@ class RssFeed { | |||
| final String webMaster; | |||
| final int ttl; | |||
| final DublinCore dc; | |||
| final RssItunes itunes; | |||
| final Itunes itunes; | |||
| final Syndication syndication; | |||
| RssFeed({ | |||
| this.title, | |||
| @@ -56,53 +57,58 @@ class RssFeed { | |||
| this.ttl, | |||
| this.dc, | |||
| this.itunes, | |||
| this.syndication, | |||
| }); | |||
| factory RssFeed.parse(String xmlString) { | |||
| var document = parse(xmlString); | |||
| XmlElement channelElement; | |||
| try { | |||
| channelElement = document.findAllElements("channel").first; | |||
| } on StateError { | |||
| throw ArgumentError("channel not found"); | |||
| var document = XmlDocument.parse(xmlString); | |||
| var rss = findFirstElement(document, 'rss'); | |||
| var rdf = findFirstElement(document, 'rdf:RDF'); | |||
| if (rss == null && rdf == null) { | |||
| throw ArgumentError('not a rss feed'); | |||
| } | |||
| var channelElement = findFirstElement(rss ?? rdf, 'channel'); | |||
| if (channelElement == null) { | |||
| throw ArgumentError('channel not found'); | |||
| } | |||
| return RssFeed( | |||
| title: findElementOrNull(channelElement, "title")?.text, | |||
| author: findElementOrNull(channelElement, "author")?.text, | |||
| description: findElementOrNull(channelElement, "description")?.text, | |||
| link: findElementOrNull(channelElement, "link")?.text, | |||
| items: channelElement.findElements("item").map((element) { | |||
| return RssItem.parse(element); | |||
| }).toList(), | |||
| image: RssImage.parse(findElementOrNull(channelElement, "image")), | |||
| cloud: RssCloud.parse(findElementOrNull(channelElement, "cloud")), | |||
| categories: channelElement.findElements("category").map((element) { | |||
| return RssCategory.parse(element); | |||
| }).toList(), | |||
| skipDays: findElementOrNull(channelElement, "skipDays") | |||
| ?.findAllElements("day") | |||
| ?.map((element) { | |||
| return element.text; | |||
| })?.toList() ?? | |||
| title: findFirstElement(channelElement, 'title')?.text, | |||
| author: findFirstElement(channelElement, 'author')?.text, | |||
| description: findFirstElement(channelElement, 'description')?.text, | |||
| link: findFirstElement(channelElement, 'link')?.text, | |||
| items: (rss != null ? channelElement : rdf) | |||
| .findElements('item') | |||
| .map((e) => RssItem.parse(e)) | |||
| .toList(), | |||
| image: RssImage.parse( | |||
| findFirstElement(rss != null ? channelElement : rdf, 'image')), | |||
| cloud: RssCloud.parse(findFirstElement(channelElement, 'cloud')), | |||
| categories: channelElement | |||
| .findElements('category') | |||
| .map((e) => RssCategory.parse(e)) | |||
| .toList(), | |||
| skipDays: findFirstElement(channelElement, 'skipDays') | |||
| ?.findAllElements('day') | |||
| ?.map((e) => e.text) | |||
| ?.toList() ?? | |||
| [], | |||
| skipHours: findElementOrNull(channelElement, "skipHours") | |||
| ?.findAllElements("hour") | |||
| ?.map((element) { | |||
| return int.tryParse(element.text ?? "0"); | |||
| })?.toList() ?? | |||
| skipHours: findFirstElement(channelElement, 'skipHours') | |||
| ?.findAllElements('hour') | |||
| ?.map((e) => int.tryParse(e.text ?? '0')) | |||
| ?.toList() ?? | |||
| [], | |||
| lastBuildDate: findElementOrNull(channelElement, "lastBuildDate")?.text, | |||
| language: findElementOrNull(channelElement, "language")?.text, | |||
| generator: findElementOrNull(channelElement, "generator")?.text, | |||
| copyright: findElementOrNull(channelElement, "copyright")?.text, | |||
| docs: findElementOrNull(channelElement, "docs")?.text, | |||
| managingEditor: findElementOrNull(channelElement, "managingEditor")?.text, | |||
| rating: findElementOrNull(channelElement, "rating")?.text, | |||
| webMaster: findElementOrNull(channelElement, "webMaster")?.text, | |||
| ttl: int.tryParse(findElementOrNull(channelElement, "ttl")?.text ?? "0"), | |||
| lastBuildDate: findFirstElement(channelElement, 'lastBuildDate')?.text, | |||
| language: findFirstElement(channelElement, 'language')?.text, | |||
| generator: findFirstElement(channelElement, 'generator')?.text, | |||
| copyright: findFirstElement(channelElement, 'copyright')?.text, | |||
| docs: findFirstElement(channelElement, 'docs')?.text, | |||
| managingEditor: findFirstElement(channelElement, 'managingEditor')?.text, | |||
| rating: findFirstElement(channelElement, 'rating')?.text, | |||
| webMaster: findFirstElement(channelElement, 'webMaster')?.text, | |||
| ttl: int.tryParse(findFirstElement(channelElement, 'ttl')?.text ?? '0'), | |||
| dc: DublinCore.parse(channelElement), | |||
| itunes: RssItunes.parse(channelElement), | |||
| itunes: Itunes.parse(channelElement), | |||
| syndication: Syndication.parse(channelElement), | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class RssImage { | |||
| @@ -6,16 +6,16 @@ class RssImage { | |||
| final String url; | |||
| final String link; | |||
| RssImage(this.title, this.url, this.link); | |||
| RssImage({this.title, this.url, this.link}); | |||
| factory RssImage.parse(XmlElement element) { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| var title = findElementOrNull(element, "title")?.text; | |||
| var url = findElementOrNull(element, "url")?.text; | |||
| var link = findElementOrNull(element, "link")?.text; | |||
| return RssImage(title, url, link); | |||
| return RssImage( | |||
| title: findFirstElement(element, 'title')?.text, | |||
| url: findFirstElement(element, 'url')?.text, | |||
| link: findFirstElement(element, 'link')?.text, | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,15 +1,14 @@ | |||
| import 'package:intl/intl.dart'; | |||
| import 'package:webfeed/domain/dublin_core/dublin_core.dart'; | |||
| import 'package:webfeed/domain/itunes/itunes.dart'; | |||
| import 'package:webfeed/domain/media/media.dart'; | |||
| import 'package:webfeed/domain/rss_category.dart'; | |||
| import 'package:webfeed/domain/rss_content.dart'; | |||
| import 'package:webfeed/domain/rss_enclosure.dart'; | |||
| import 'package:webfeed/domain/rss_source.dart'; | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:webfeed/util/datetime.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| import 'rss_item_itunes.dart'; | |||
| class RssItem { | |||
| final String title; | |||
| final String description; | |||
| @@ -25,7 +24,7 @@ class RssItem { | |||
| final Media media; | |||
| final RssEnclosure enclosure; | |||
| final DublinCore dc; | |||
| final RssItemItunes itunes; | |||
| final Itunes itunes; | |||
| RssItem({ | |||
| this.title, | |||
| @@ -33,7 +32,7 @@ class RssItem { | |||
| this.link, | |||
| this.categories, | |||
| this.guid, | |||
| String pubDate, | |||
| this.pubDate, | |||
| this.author, | |||
| this.comments, | |||
| this.source, | |||
| @@ -42,33 +41,27 @@ class RssItem { | |||
| this.enclosure, | |||
| this.dc, | |||
| this.itunes, | |||
| }) | |||
| : this.pubDate = _parsePubDate(pubDate); | |||
| }); | |||
| factory RssItem.parse(XmlElement element) { | |||
| return RssItem( | |||
| title: findElementOrNull(element, "title")?.text, | |||
| description: findElementOrNull(element, "description")?.text, | |||
| link: findElementOrNull(element, "link")?.text, | |||
| categories: element.findElements("category").map((element) { | |||
| return RssCategory.parse(element); | |||
| }).toList(), | |||
| guid: findElementOrNull(element, "guid")?.text, | |||
| pubDate: findElementOrNull(element, "pubDate")?.text, | |||
| author: findElementOrNull(element, "author")?.text, | |||
| comments: findElementOrNull(element, "comments")?.text, | |||
| source: RssSource.parse(findElementOrNull(element, "source")), | |||
| content: RssContent.parse(findElementOrNull(element, "content:encoded")), | |||
| title: findFirstElement(element, 'title')?.text, | |||
| description: findFirstElement(element, 'description')?.text, | |||
| link: findFirstElement(element, 'link')?.text, | |||
| categories: element | |||
| .findElements('category') | |||
| .map((e) => RssCategory.parse(e)) | |||
| .toList(), | |||
| guid: findFirstElement(element, 'guid')?.text, | |||
| pubDate: parseDateTime(findFirstElement(element, 'pubDate')?.text), | |||
| author: findFirstElement(element, 'author')?.text, | |||
| comments: findFirstElement(element, 'comments')?.text, | |||
| source: RssSource.parse(findFirstElement(element, 'source')), | |||
| content: RssContent.parse(findFirstElement(element, 'content:encoded')), | |||
| media: Media.parse(element), | |||
| enclosure: RssEnclosure.parse(findElementOrNull(element, "enclosure")), | |||
| enclosure: RssEnclosure.parse(findFirstElement(element, 'enclosure')), | |||
| dc: DublinCore.parse(element), | |||
| itunes: RssItemItunes.parse(element), | |||
| itunes: Itunes.parse(element), | |||
| ); | |||
| } | |||
| static _parsePubDate(pubDate) { | |||
| if (pubDate == null) return null; | |||
| //Locale for pubDate is always en_US, regardless of device locale | |||
| return DateFormat('EEE, dd MMM yyyy HH:mm:ss Z', 'en_US').parse(pubDate); | |||
| } | |||
| } | |||
| @@ -1,83 +0,0 @@ | |||
| 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 RssItunesEpisodeType episodeType; | |||
| final String author; | |||
| final String summary; | |||
| final bool explicit; | |||
| final String subtitle; | |||
| 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, | |||
| this.summary, | |||
| this.explicit, | |||
| this.subtitle, | |||
| this.keywords, | |||
| this.image, | |||
| this.category, | |||
| this.block, | |||
| }); | |||
| factory RssItemItunes.parse(XmlElement element) { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| 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: newRssItunesEpisodeType(findElementOrNull(element, "itunes:episodeType")), | |||
| author: findElementOrNull(element, "itunes:author")?.text?.trim(), | |||
| summary: findElementOrNull(element, "itunes:summary")?.text?.trim(), | |||
| 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"), | |||
| ); | |||
| } | |||
| } | |||
| Duration parseDuration(String s) { | |||
| var hours = 0; | |||
| var minutes = 0; | |||
| var seconds = 0; | |||
| var parts = s.split(':'); | |||
| if (parts.length > 2) { | |||
| hours = int.parse(parts[parts.length - 3]); | |||
| } | |||
| if (parts.length > 1) { | |||
| minutes = int.parse(parts[parts.length - 2]); | |||
| } | |||
| seconds = int.parse(parts[parts.length - 1]); | |||
| return Duration( | |||
| hours: hours, | |||
| minutes: minutes, | |||
| seconds: seconds, | |||
| ); | |||
| } | |||
| @@ -1,69 +0,0 @@ | |||
| import 'package:xml/xml.dart'; | |||
| 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 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.categories, | |||
| this.type, | |||
| this.newFeedUrl, | |||
| this.block, | |||
| this.complete, | |||
| }); | |||
| factory RssItunes.parse(XmlElement element) { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return RssItunes( | |||
| author: findElementOrNull(element, "itunes:author")?.text?.trim(), | |||
| summary: findElementOrNull(element, "itunes:summary")?.text?.trim(), | |||
| 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(), | |||
| image: RssItunesImage.parse(findElementOrNull(element, "itunes:image")), | |||
| 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"), | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,24 +0,0 @@ | |||
| import 'package:xml/xml.dart'; | |||
| class RssItunesCategory { | |||
| final String category; | |||
| final List<String> subCategories; | |||
| RssItunesCategory({this.category, this.subCategories}); | |||
| factory RssItunesCategory.parse(XmlElement element) { | |||
| if (element == null) return null; | |||
| Iterable<XmlElement> subCategories; | |||
| try { | |||
| subCategories = element.findElements("itunes:category"); | |||
| } on StateError { | |||
| subCategories = null; | |||
| } | |||
| return RssItunesCategory( | |||
| category: element.getAttribute("text")?.trim(), | |||
| subCategories: | |||
| subCategories?.map((ele) => ele.getAttribute("text")?.trim())?.toList(), | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,19 +0,0 @@ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -1,14 +0,0 @@ | |||
| import 'package:xml/xml.dart'; | |||
| class RssItunesImage { | |||
| final String href; | |||
| RssItunesImage({this.href}); | |||
| factory RssItunesImage.parse(XmlElement element) { | |||
| if (element == null) return null; | |||
| return RssItunesImage( | |||
| href: element.getAttribute("href")?.trim(), | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,18 +0,0 @@ | |||
| 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(), | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,17 +0,0 @@ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -10,7 +10,7 @@ class RssSource { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| var url = element.getAttribute("url"); | |||
| var url = element.getAttribute('url'); | |||
| var value = element.text; | |||
| return RssSource(url, value); | |||
| @@ -0,0 +1,51 @@ | |||
| import 'package:webfeed/util/datetime.dart'; | |||
| import 'package:webfeed/util/xml.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| enum SyndicationUpdatePeriod { hourly, daily, weekly, monthly, yearly } | |||
| class Syndication { | |||
| final SyndicationUpdatePeriod updatePeriod; | |||
| final int updateFrequency; | |||
| final DateTime updateBase; | |||
| Syndication({ | |||
| this.updatePeriod, | |||
| this.updateFrequency, | |||
| this.updateBase, | |||
| }); | |||
| factory Syndication.parse(XmlElement element) { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| SyndicationUpdatePeriod updatePeriod; | |||
| switch (findFirstElement(element, 'sy:updatePeriod')?.text) { | |||
| case 'hourly': | |||
| updatePeriod = SyndicationUpdatePeriod.hourly; | |||
| break; | |||
| case 'daily': | |||
| updatePeriod = SyndicationUpdatePeriod.daily; | |||
| break; | |||
| case 'weekly': | |||
| updatePeriod = SyndicationUpdatePeriod.weekly; | |||
| break; | |||
| case 'monthly': | |||
| updatePeriod = SyndicationUpdatePeriod.monthly; | |||
| break; | |||
| case 'yearly': | |||
| updatePeriod = SyndicationUpdatePeriod.yearly; | |||
| break; | |||
| default: | |||
| updatePeriod = SyndicationUpdatePeriod.daily; | |||
| break; | |||
| } | |||
| return Syndication( | |||
| updatePeriod: updatePeriod, | |||
| updateFrequency: int.tryParse( | |||
| findFirstElement(element, 'sy:updateFrequency')?.text ?? '1'), | |||
| updateBase: | |||
| parseDateTime(findFirstElement(element, 'sy:updateBase')?.text), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| import 'package:intl/intl.dart'; | |||
| var rfc822DateFormat = DateFormat('EEE, dd MMM yyyy HH:mm:ss Z', 'en_US'); | |||
| DateTime parseDateTime(dateString) { | |||
| if (dateString == null) return null; | |||
| return _parseRfc822DateTime(dateString) ?? _parseIso8601DateTime(dateString); | |||
| } | |||
| DateTime _parseRfc822DateTime(dateString) { | |||
| try { | |||
| return rfc822DateFormat.parse(dateString); | |||
| } on FormatException { | |||
| return null; | |||
| } | |||
| } | |||
| DateTime _parseIso8601DateTime(dateString) { | |||
| try { | |||
| return DateTime.parse(dateString); | |||
| } on FormatException { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -1,28 +0,0 @@ | |||
| import 'dart:core'; | |||
| import 'package:xml/xml.dart'; | |||
| 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); | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| import 'dart:core'; | |||
| import 'package:xml/xml.dart'; | |||
| XmlElement findFirstElement( | |||
| XmlNode node, | |||
| String name, { | |||
| bool recursive = false, | |||
| String namespace, | |||
| }) { | |||
| try { | |||
| return findElements(node, name, recursive: recursive, namespace: namespace) | |||
| ?.first; | |||
| } on StateError { | |||
| return null; | |||
| } | |||
| } | |||
| Iterable<XmlElement> findElements( | |||
| XmlNode node, | |||
| String name, { | |||
| bool recursive = false, | |||
| String namespace, | |||
| }) { | |||
| try { | |||
| if (recursive) { | |||
| return node.findAllElements(name, namespace: namespace); | |||
| } else { | |||
| return node.findElements(name, namespace: namespace); | |||
| } | |||
| } on StateError { | |||
| return null; | |||
| } | |||
| } | |||
| bool parseBoolLiteral(XmlElement element, String tagName) { | |||
| var v = findFirstElement(element, tagName)?.text?.toLowerCase()?.trim(); | |||
| if (v == null) return false; | |||
| return ['yes', 'true'].contains(v); | |||
| } | |||
| @@ -1,12 +1,11 @@ | |||
| name: webfeed | |||
| version: 0.4.2 | |||
| description: webfeed is a dart package for parsing RSS and Atom feeds. Media & DublinCore namespaces are also supported. | |||
| author: Wito Chandra <wito.c.91@gmail.com> | |||
| version: 0.6.0 | |||
| description: webfeed is a dart package for parsing RSS and Atom feeds. Media, DublinCore, iTunes, Syndication namespaces are also supported. | |||
| homepage: https://github.com/witochandra/webfeed | |||
| environment: | |||
| sdk: ">=2.0.0 <3.0.0" | |||
| dependencies: | |||
| xml: "^3.0.0" | |||
| xml: "^4.2.0" | |||
| intl: "^0.16.0" | |||
| dev_dependencies: | |||
| test: ^1.3.0 | |||
| @@ -5,113 +5,113 @@ import 'package:test/test.dart'; | |||
| import 'package:webfeed/webfeed.dart'; | |||
| void main() { | |||
| test("parse Invalid.xml", () { | |||
| var xmlString = new File("test/xml/Invalid.xml").readAsStringSync(); | |||
| test('parse Invalid.xml', () { | |||
| var xmlString = File('test/xml/Invalid.xml').readAsStringSync(); | |||
| try { | |||
| new AtomFeed.parse(xmlString); | |||
| fail("Should throw Argument Error"); | |||
| AtomFeed.parse(xmlString); | |||
| fail('Should throw Argument Error'); | |||
| } on ArgumentError {} | |||
| }); | |||
| test("parse Atom.xml", () { | |||
| var xmlString = new File("test/xml/Atom.xml").readAsStringSync(); | |||
| test('parse Atom.xml', () { | |||
| var xmlString = File('test/xml/Atom.xml').readAsStringSync(); | |||
| var feed = new AtomFeed.parse(xmlString); | |||
| var feed = AtomFeed.parse(xmlString); | |||
| expect(feed.id, "foo-bar-id"); | |||
| expect(feed.title, "Foo bar news"); | |||
| expect(feed.updated, "2018-04-06T13:02:46Z"); | |||
| expect(feed.id, 'foo-bar-id'); | |||
| expect(feed.title, 'Foo bar news'); | |||
| expect(feed.updated, DateTime.utc(2018, 4, 6, 13, 2, 46)); | |||
| expect(feed.links.length, 2); | |||
| expect(feed.links.first.rel, "foo"); | |||
| expect(feed.links.first.type, "text/html"); | |||
| expect(feed.links.first.hreflang, "en"); | |||
| expect(feed.links.first.href, "http://foo.bar.news/"); | |||
| expect(feed.links.first.title, "Foo bar news html"); | |||
| expect(feed.links.first.rel, 'foo'); | |||
| expect(feed.links.first.type, 'text/html'); | |||
| expect(feed.links.first.hreflang, 'en'); | |||
| expect(feed.links.first.href, 'http://foo.bar.news/'); | |||
| expect(feed.links.first.title, 'Foo bar news html'); | |||
| expect(feed.links.first.length, 1000); | |||
| expect(feed.authors.length, 2); | |||
| expect(feed.authors.first.name, "Alice"); | |||
| expect(feed.authors.first.uri, "http://foo.bar.news/people/alice"); | |||
| expect(feed.authors.first.email, "alice@foo.bar.news"); | |||
| expect(feed.authors.first.name, 'Alice'); | |||
| expect(feed.authors.first.uri, 'http://foo.bar.news/people/alice'); | |||
| expect(feed.authors.first.email, 'alice@foo.bar.news'); | |||
| expect(feed.contributors.length, 2); | |||
| expect(feed.contributors.first.name, "Charlie"); | |||
| expect(feed.contributors.first.uri, "http://foo.bar.news/people/charlie"); | |||
| expect(feed.contributors.first.email, "charlie@foo.bar.news"); | |||
| expect(feed.contributors.first.name, 'Charlie'); | |||
| expect(feed.contributors.first.uri, 'http://foo.bar.news/people/charlie'); | |||
| expect(feed.contributors.first.email, 'charlie@foo.bar.news'); | |||
| expect(feed.categories.length, 2); | |||
| expect(feed.categories.first.term, "foo category"); | |||
| expect(feed.categories.first.scheme, "this-is-foo-scheme"); | |||
| expect(feed.categories.first.label, "this is foo label"); | |||
| expect(feed.categories.first.term, 'foo category'); | |||
| expect(feed.categories.first.scheme, 'this-is-foo-scheme'); | |||
| expect(feed.categories.first.label, 'this is foo label'); | |||
| expect(feed.generator.uri, "http://foo.bar.news/generator"); | |||
| expect(feed.generator.version, "1.0"); | |||
| expect(feed.generator.value, "Foo bar generator"); | |||
| expect(feed.generator.uri, 'http://foo.bar.news/generator'); | |||
| expect(feed.generator.version, '1.0'); | |||
| expect(feed.generator.value, 'Foo bar generator'); | |||
| expect(feed.icon, "http://foo.bar.news/icon.png"); | |||
| expect(feed.logo, "http://foo.bar.news/logo.png"); | |||
| expect(feed.subtitle, "This is subtitle"); | |||
| expect(feed.icon, 'http://foo.bar.news/icon.png'); | |||
| expect(feed.logo, 'http://foo.bar.news/logo.png'); | |||
| expect(feed.subtitle, 'This is subtitle'); | |||
| expect(feed.items.length, 2); | |||
| var item = feed.items.first; | |||
| expect(item.id, "foo-bar-entry-id-1"); | |||
| expect(item.title, "Foo bar item 1"); | |||
| expect(item.updated, "2018-04-06T13:02:47Z"); | |||
| expect(item.id, 'foo-bar-entry-id-1'); | |||
| expect(item.title, 'Foo bar item 1'); | |||
| expect(item.updated, DateTime.utc(2018, 4, 6, 13, 2, 40)); | |||
| expect(item.authors.length, 2); | |||
| expect(item.authors.first.name, "Ellie"); | |||
| expect(item.authors.first.uri, "http://foo.bar.news/people/ellie"); | |||
| expect(item.authors.first.email, "ellie@foo.bar.news"); | |||
| expect(item.authors.first.name, 'Ellie'); | |||
| expect(item.authors.first.uri, 'http://foo.bar.news/people/ellie'); | |||
| expect(item.authors.first.email, 'ellie@foo.bar.news'); | |||
| expect(item.links.length, 2); | |||
| expect(item.links.first.rel, "foo entry"); | |||
| expect(item.links.first.type, "text/html"); | |||
| expect(item.links.first.hreflang, "en"); | |||
| expect(item.links.first.href, "http://foo.bar.news/entry"); | |||
| expect(item.links.first.title, "Foo bar news html"); | |||
| expect(item.links.first.rel, 'foo entry'); | |||
| expect(item.links.first.type, 'text/html'); | |||
| expect(item.links.first.hreflang, 'en'); | |||
| expect(item.links.first.href, 'http://foo.bar.news/entry'); | |||
| expect(item.links.first.title, 'Foo bar news html'); | |||
| expect(item.links.first.length, 1000); | |||
| expect(item.categories.length, 2); | |||
| expect(item.categories.first.term, "foo entry category"); | |||
| expect(item.categories.first.scheme, "this-is-foo-entry-scheme"); | |||
| expect(item.categories.first.label, "this is foo entry label"); | |||
| expect(item.categories.first.term, 'foo entry category'); | |||
| expect(item.categories.first.scheme, 'this-is-foo-entry-scheme'); | |||
| expect(item.categories.first.label, 'this is foo entry label'); | |||
| expect(item.contributors.length, 2); | |||
| expect(item.contributors.first.name, "Gin"); | |||
| expect(item.contributors.first.uri, "http://foo.bar.news/people/gin"); | |||
| expect(item.contributors.first.email, "gin@foo.bar.news"); | |||
| expect(item.published, "2018-04-06T13:02:49Z"); | |||
| expect(item.summary, "This is summary 1"); | |||
| expect(item.content, "This is content 1"); | |||
| expect(item.rights, "This is rights 1"); | |||
| expect(item.contributors.first.name, 'Gin'); | |||
| expect(item.contributors.first.uri, 'http://foo.bar.news/people/gin'); | |||
| expect(item.contributors.first.email, 'gin@foo.bar.news'); | |||
| expect(item.published, '2018-04-06T13:02:49Z'); | |||
| expect(item.summary, 'This is summary 1'); | |||
| expect(item.content, 'This is content 1'); | |||
| expect(item.rights, 'This is rights 1'); | |||
| }); | |||
| test("parse Atom-Media.xml", (){ | |||
| var xmlString = new File("test/xml/Atom-Media.xml").readAsStringSync(); | |||
| test('parse Atom-Media.xml', () { | |||
| var xmlString = File('test/xml/Atom-Media.xml').readAsStringSync(); | |||
| var feed = new AtomFeed.parse(xmlString); | |||
| expect(feed.id, "foo-bar-id"); | |||
| expect(feed.title, "Foo bar news"); | |||
| expect(feed.updated, "2018-04-06T13:02:46Z"); | |||
| var feed = AtomFeed.parse(xmlString); | |||
| expect(feed.id, 'foo-bar-id'); | |||
| expect(feed.title, 'Foo bar news'); | |||
| expect(feed.updated, DateTime.utc(2018, 4, 6, 13, 2, 46)); | |||
| expect(feed.items.length, 1); | |||
| var item = feed.items.first; | |||
| expect(item.media.group.contents.length, 5); | |||
| expect(item.media.group.credits.length, 2); | |||
| expect(item.media.group.category.value, "music/artist name/album/song"); | |||
| expect(item.media.group.rating.value, "nonadult"); | |||
| expect(item.media.group.category.value, 'music/artist name/album/song'); | |||
| expect(item.media.group.rating.value, 'nonadult'); | |||
| expect(item.media.contents.length, 2); | |||
| var mediaContent = item.media.contents.first; | |||
| expect(mediaContent.url, "http://www.foo.com/video.mov"); | |||
| expect(mediaContent.type, "video/quicktime"); | |||
| expect(mediaContent.url, 'http://www.foo.com/video.mov'); | |||
| expect(mediaContent.type, 'video/quicktime'); | |||
| expect(mediaContent.fileSize, 2000); | |||
| expect(mediaContent.medium, "video"); | |||
| expect(mediaContent.medium, 'video'); | |||
| expect(mediaContent.isDefault, true); | |||
| expect(mediaContent.expression, "full"); | |||
| expect(mediaContent.expression, 'full'); | |||
| expect(mediaContent.bitrate, 128); | |||
| expect(mediaContent.framerate, 25); | |||
| expect(mediaContent.samplingrate, 44.1); | |||
| @@ -119,52 +119,54 @@ void main() { | |||
| expect(item.media.credits.length, 2); | |||
| var mediaCredit = item.media.credits.first; | |||
| expect(mediaCredit.role, "owner1"); | |||
| expect(mediaCredit.scheme, "urn:yvs"); | |||
| expect(mediaCredit.value, "copyright holder of the entity"); | |||
| expect(mediaCredit.role, 'owner1'); | |||
| 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.label, "Music"); | |||
| expect(item.media.category.value, "music/artist/album/song"); | |||
| 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'); | |||
| expect(item.media.rating.scheme, "urn:simple"); | |||
| expect(item.media.rating.value, "adult"); | |||
| expect(item.media.rating.scheme, 'urn:simple'); | |||
| expect(item.media.rating.value, 'adult'); | |||
| expect(item.media.title.type, "plain"); | |||
| expect(item.media.title.type, 'plain'); | |||
| 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.keywords, "kitty, cat, big dog, yarn, fluffy"); | |||
| 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.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"); | |||
| expect(mediaThumbnail.width, "75"); | |||
| expect(mediaThumbnail.height, "50"); | |||
| expect(mediaThumbnail.time, "12:05:01.123"); | |||
| expect(mediaThumbnail.url, 'http://www.foo.com/keyframe1.jpg'); | |||
| expect(mediaThumbnail.width, '75'); | |||
| expect(mediaThumbnail.height, '50'); | |||
| expect(mediaThumbnail.time, '12:05:01.123'); | |||
| expect(item.media.hash.algo, "md5"); | |||
| expect(item.media.hash.value, "dfdec888b72151965a34b4b59031290a"); | |||
| expect(item.media.hash.algo, 'md5'); | |||
| expect(item.media.hash.value, 'dfdec888b72151965a34b4b59031290a'); | |||
| expect(item.media.player.url, "http://www.foo.com/player?id=1111"); | |||
| expect(item.media.player.url, 'http://www.foo.com/player?id=1111'); | |||
| expect(item.media.player.width, 400); | |||
| expect(item.media.player.height, 200); | |||
| expect(item.media.player.value, ""); | |||
| expect(item.media.player.value, ''); | |||
| expect(item.media.copyright.url, "http://blah.com/additional-info.html"); | |||
| expect(item.media.copyright.value, "2005 FooBar Media"); | |||
| expect(item.media.copyright.url, 'http://blah.com/additional-info.html'); | |||
| expect(item.media.copyright.value, '2005 FooBar Media'); | |||
| expect(item.media.text.type, "plain"); | |||
| expect(item.media.text.lang, "en"); | |||
| expect(item.media.text.start, "00:00:03.000"); | |||
| expect(item.media.text.end, "00:00:10.000"); | |||
| expect(item.media.text.value, " Oh, say, can you see"); | |||
| expect(item.media.text.type, 'plain'); | |||
| expect(item.media.text.lang, 'en'); | |||
| expect(item.media.text.start, '00:00:03.000'); | |||
| expect(item.media.text.end, '00:00:10.000'); | |||
| expect(item.media.text.value, ' Oh, say, can you see'); | |||
| expect(item.media.restriction.relationship, "allow"); | |||
| expect(item.media.restriction.type, "country"); | |||
| expect(item.media.restriction.value, "au us"); | |||
| expect(item.media.restriction.relationship, 'allow'); | |||
| expect(item.media.restriction.type, 'country'); | |||
| expect(item.media.restriction.value, 'au us'); | |||
| expect(item.media.community.starRating.average, 3.5); | |||
| expect(item.media.community.starRating.count, 20); | |||
| @@ -172,56 +174,58 @@ void main() { | |||
| expect(item.media.community.starRating.max, 10); | |||
| expect(item.media.community.statistics.views, 5); | |||
| expect(item.media.community.statistics.favorites, 4); | |||
| expect(item.media.community.tags.tags, "news: 5, abc:3"); | |||
| expect(item.media.community.tags.tags, 'news: 5, abc:3'); | |||
| expect(item.media.community.tags.weight, 1); | |||
| expect(item.media.comments.length, 2); | |||
| expect(item.media.comments.first, "comment1"); | |||
| expect(item.media.comments.last, "comment2"); | |||
| expect(item.media.comments.first, 'comment1'); | |||
| expect(item.media.comments.last, 'comment2'); | |||
| expect(item.media.embed.url, "http://www.foo.com/player.swf"); | |||
| expect(item.media.embed.url, 'http://www.foo.com/player.swf'); | |||
| expect(item.media.embed.width, 512); | |||
| 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.name, 'type'); | |||
| 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"); | |||
| expect(item.media.responses.last, "http://www.response2.com"); | |||
| expect(item.media.responses.first, 'http://www.response1.com'); | |||
| expect(item.media.responses.last, 'http://www.response2.com'); | |||
| expect(item.media.backLinks.length, 2); | |||
| expect(item.media.backLinks.first, "http://www.backlink1.com"); | |||
| expect(item.media.backLinks.last, "http://www.backlink2.com"); | |||
| expect(item.media.backLinks.first, 'http://www.backlink1.com'); | |||
| expect(item.media.backLinks.last, 'http://www.backlink2.com'); | |||
| expect(item.media.status.state, "active"); | |||
| expect(item.media.status.state, 'active'); | |||
| expect(item.media.status.reason, null); | |||
| 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.currency, "EUR"); | |||
| 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.currency, 'EUR'); | |||
| expect(item.media.license.type, "text/html"); | |||
| expect(item.media.license.href, "http://www.licensehost.com/license"); | |||
| expect(item.media.license.value, " Sample license for a video"); | |||
| expect(item.media.license.type, 'text/html'); | |||
| expect(item.media.license.href, 'http://www.licensehost.com/license'); | |||
| expect(item.media.license.value, ' Sample license for a video'); | |||
| expect(item.media.peerLink.type, "application/x-bittorrent"); | |||
| expect(item.media.peerLink.href, "http://www.foo.org/sampleFile.torrent"); | |||
| expect(item.media.peerLink.value, ""); | |||
| expect(item.media.peerLink.type, 'application/x-bittorrent'); | |||
| expect(item.media.peerLink.href, 'http://www.foo.org/sampleFile.torrent'); | |||
| expect(item.media.peerLink.value, ''); | |||
| expect(item.media.rights.status, "official"); | |||
| expect(item.media.rights.status, 'official'); | |||
| expect(item.media.scenes.length, 2); | |||
| expect(item.media.scenes.first.title, "sceneTitle1"); | |||
| expect(item.media.scenes.first.description, "sceneDesc1"); | |||
| expect(item.media.scenes.first.startTime, "00:15"); | |||
| expect(item.media.scenes.first.endTime, "00:45"); | |||
| expect(item.media.scenes.first.title, 'sceneTitle1'); | |||
| expect(item.media.scenes.first.description, 'sceneDesc1'); | |||
| expect(item.media.scenes.first.startTime, '00:15'); | |||
| expect(item.media.scenes.first.endTime, '00:45'); | |||
| }); | |||
| test("parse Atom-Empty.xml", () { | |||
| var xmlString = File("test/xml/Atom-Empty.xml").readAsStringSync(); | |||
| test('parse Atom-Empty.xml', () { | |||
| var xmlString = File('test/xml/Atom-Empty.xml').readAsStringSync(); | |||
| var feed = AtomFeed.parse(xmlString); | |||
| @@ -2,60 +2,60 @@ import 'dart:core'; | |||
| import 'dart:io'; | |||
| import 'package:test/test.dart'; | |||
| import 'package:webfeed/domain/itunes/itunes_episode_type.dart'; | |||
| import 'package:webfeed/domain/itunes/itunes_type.dart'; | |||
| import 'package:webfeed/domain/syndication/syndication.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(); | |||
| test('parse Invalid.xml', () { | |||
| var xmlString = File('test/xml/Invalid.xml').readAsStringSync(); | |||
| try { | |||
| new RssFeed.parse(xmlString); | |||
| fail("Should throw Argument Error"); | |||
| RssFeed.parse(xmlString); | |||
| fail('Should throw Argument Error'); | |||
| } on ArgumentError {} | |||
| }); | |||
| test("parse RSS.xml", () { | |||
| var xmlString = new File("test/xml/RSS.xml").readAsStringSync(); | |||
| test('parse RSS.xml', () { | |||
| var xmlString = File('test/xml/RSS.xml').readAsStringSync(); | |||
| var feed = new RssFeed.parse(xmlString); | |||
| var feed = RssFeed.parse(xmlString); | |||
| expect(feed.title, "News - Foo bar News"); | |||
| expect(feed.title, 'News - Foo bar News'); | |||
| 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"); | |||
| expect(feed.lastBuildDate, "Mon, 26 Mar 2018 14:00:00 PDT"); | |||
| expect(feed.generator, "Custom"); | |||
| expect(feed.copyright, "Copyright 2018, Foo bar Inc."); | |||
| expect(feed.docs, "https://foo.bar.news/docs"); | |||
| expect(feed.managingEditor, "alice@foo.bar.news"); | |||
| expect(feed.rating, "The PICS rating of the feed"); | |||
| expect(feed.webMaster, "webmaster@foo.bar.news"); | |||
| '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'); | |||
| expect(feed.lastBuildDate, 'Mon, 26 Mar 2018 14:00:00 PDT'); | |||
| expect(feed.generator, 'Custom'); | |||
| expect(feed.copyright, 'Copyright 2018, Foo bar Inc.'); | |||
| expect(feed.docs, 'https://foo.bar.news/docs'); | |||
| expect(feed.managingEditor, 'alice@foo.bar.news'); | |||
| expect(feed.rating, 'The PICS rating of the feed'); | |||
| expect(feed.webMaster, 'webmaster@foo.bar.news'); | |||
| expect(feed.ttl, 60); | |||
| expect(feed.image.title, "Foo bar News"); | |||
| expect(feed.image.url, "https://foo.bar.news/logo.gif"); | |||
| expect(feed.image.link, "https://foo.bar.news/"); | |||
| expect(feed.image.title, 'Foo bar News'); | |||
| expect(feed.image.url, 'https://foo.bar.news/logo.gif'); | |||
| expect(feed.image.link, 'https://foo.bar.news/'); | |||
| expect(feed.cloud.domain, "radio.foo.bar.news"); | |||
| expect(feed.cloud.port, "80"); | |||
| expect(feed.cloud.path, "/RPC2"); | |||
| expect(feed.cloud.registerProcedure, "foo.bar.rssPleaseNotify"); | |||
| expect(feed.cloud.protocol, "xml-rpc"); | |||
| expect(feed.cloud.domain, 'radio.foo.bar.news'); | |||
| expect(feed.cloud.port, '80'); | |||
| expect(feed.cloud.path, '/RPC2'); | |||
| expect(feed.cloud.registerProcedure, 'foo.bar.rssPleaseNotify'); | |||
| expect(feed.cloud.protocol, 'xml-rpc'); | |||
| expect(feed.categories.length, 2); | |||
| expect(feed.categories[0].domain, null); | |||
| expect(feed.categories[0].value, "Ipsum"); | |||
| expect(feed.categories[1].domain, "news"); | |||
| expect(feed.categories[1].value, "Lorem Ipsum"); | |||
| expect(feed.categories[0].value, 'Ipsum'); | |||
| expect(feed.categories[1].domain, 'news'); | |||
| expect(feed.categories[1].value, 'Lorem Ipsum'); | |||
| expect(feed.skipDays.length, 3); | |||
| expect(feed.skipDays.contains("Monday"), true); | |||
| expect(feed.skipDays.contains("Tuesday"), true); | |||
| expect(feed.skipDays.contains("Sunday"), true); | |||
| expect(feed.skipDays.contains('Monday'), true); | |||
| expect(feed.skipDays.contains('Tuesday'), true); | |||
| expect(feed.skipDays.contains('Sunday'), true); | |||
| expect(feed.skipHours.length, 5); | |||
| expect(feed.skipHours.contains(0), true); | |||
| @@ -67,56 +67,58 @@ void main() { | |||
| expect(feed.items.length, 2); | |||
| expect(feed.items.first.title, | |||
| "The standard Lorem Ipsum passage, used since the 1500s"); | |||
| '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, DateTime(2018, 03, 26, 14)); //Mon, 26 Mar 2018 14:00:00 PDT | |||
| expect(feed.items.first.categories.first.domain, "news"); | |||
| expect(feed.items.first.categories.first.value, "Lorem"); | |||
| expect(feed.items.first.author, "alice@foo.bar.news"); | |||
| 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"); | |||
| '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, | |||
| DateTime(2018, 03, 26, 14)); //Mon, 26 Mar 2018 14:00:00 PDT | |||
| expect(feed.items.first.categories.first.domain, 'news'); | |||
| expect(feed.items.first.categories.first.value, 'Lorem'); | |||
| expect(feed.items.first.author, 'alice@foo.bar.news'); | |||
| 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"); | |||
| '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.enclosure.type, 'audio/mpeg'); | |||
| expect(feed.items.first.content.value, | |||
| "<img width=\"1000\" height=\"690\" src=\"https://test.com/image_link\"/> Test content<br />"); | |||
| '<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"); | |||
| feed.items.first.content.images.first, 'https://test.com/image_link'); | |||
| }); | |||
| test("parse RSS-Media.xml", () { | |||
| var xmlString = new File("test/xml/RSS-Media.xml").readAsStringSync(); | |||
| test('parse RSS-Media.xml', () { | |||
| var xmlString = File('test/xml/RSS-Media.xml').readAsStringSync(); | |||
| var feed = new RssFeed.parse(xmlString); | |||
| expect(feed.title, "Song Site"); | |||
| var feed = RssFeed.parse(xmlString); | |||
| expect(feed.title, 'Song Site'); | |||
| expect( | |||
| feed.description, "Media RSS example with new fields added in v1.5.0"); | |||
| 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"); | |||
| expect(item.pubDate, DateTime(2001, 08, 27, 16, 08, 56)); //Mon, 27 Aug 2001 16:08:56 PST | |||
| expect(item.link, 'http://www.foo.com'); | |||
| expect(item.pubDate, | |||
| DateTime(2001, 08, 27, 16, 08, 56)); //Mon, 27 Aug 2001 16:08:56 PST | |||
| expect(item.media.group.contents.length, 5); | |||
| expect(item.media.group.credits.length, 2); | |||
| expect(item.media.group.category.value, "music/artist name/album/song"); | |||
| expect(item.media.group.rating.value, "nonadult"); | |||
| expect(item.media.group.category.value, 'music/artist name/album/song'); | |||
| expect(item.media.group.rating.value, 'nonadult'); | |||
| expect(item.media.contents.length, 2); | |||
| var mediaContent = item.media.contents.first; | |||
| expect(mediaContent.url, "http://www.foo.com/video.mov"); | |||
| expect(mediaContent.type, "video/quicktime"); | |||
| expect(mediaContent.url, 'http://www.foo.com/video.mov'); | |||
| expect(mediaContent.type, 'video/quicktime'); | |||
| expect(mediaContent.fileSize, 2000); | |||
| expect(mediaContent.medium, "video"); | |||
| expect(mediaContent.medium, 'video'); | |||
| expect(mediaContent.isDefault, true); | |||
| expect(mediaContent.expression, "full"); | |||
| expect(mediaContent.expression, 'full'); | |||
| expect(mediaContent.bitrate, 128); | |||
| expect(mediaContent.framerate, 25); | |||
| expect(mediaContent.samplingrate, 44.1); | |||
| @@ -124,54 +126,54 @@ void main() { | |||
| expect(item.media.credits.length, 2); | |||
| var mediaCredit = item.media.credits.first; | |||
| expect(mediaCredit.role, "owner1"); | |||
| expect(mediaCredit.scheme, "urn:yvs"); | |||
| expect(mediaCredit.value, "copyright holder of the entity"); | |||
| expect(mediaCredit.role, 'owner1'); | |||
| 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.label, "Music"); | |||
| expect(item.media.category.value, "music/artist/album/song"); | |||
| 'http://search.yahoo.com/mrss/category_ schema'); | |||
| expect(item.media.category.label, 'Music'); | |||
| expect(item.media.category.value, 'music/artist/album/song'); | |||
| expect(item.media.rating.scheme, "urn:simple"); | |||
| expect(item.media.rating.value, "adult"); | |||
| expect(item.media.rating.scheme, 'urn:simple'); | |||
| expect(item.media.rating.value, 'adult'); | |||
| expect(item.media.title.type, "plain"); | |||
| expect(item.media.title.type, 'plain'); | |||
| expect(item.media.title.value, "The Judy's -- The Moo Song"); | |||
| expect(item.media.description.type, "plain"); | |||
| expect(item.media.description.type, 'plain'); | |||
| expect(item.media.description.value, | |||
| "This was some really bizarre band I listened to as a young lad."); | |||
| '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.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"); | |||
| expect(mediaThumbnail.width, "75"); | |||
| expect(mediaThumbnail.height, "50"); | |||
| expect(mediaThumbnail.time, "12:05:01.123"); | |||
| expect(mediaThumbnail.url, 'http://www.foo.com/keyframe1.jpg'); | |||
| expect(mediaThumbnail.width, '75'); | |||
| expect(mediaThumbnail.height, '50'); | |||
| expect(mediaThumbnail.time, '12:05:01.123'); | |||
| expect(item.media.hash.algo, "md5"); | |||
| expect(item.media.hash.value, "dfdec888b72151965a34b4b59031290a"); | |||
| expect(item.media.hash.algo, 'md5'); | |||
| expect(item.media.hash.value, 'dfdec888b72151965a34b4b59031290a'); | |||
| expect(item.media.player.url, "http://www.foo.com/player?id=1111"); | |||
| expect(item.media.player.url, 'http://www.foo.com/player?id=1111'); | |||
| expect(item.media.player.width, 400); | |||
| expect(item.media.player.height, 200); | |||
| expect(item.media.player.value, ""); | |||
| expect(item.media.player.value, ''); | |||
| expect(item.media.copyright.url, "http://blah.com/additional-info.html"); | |||
| expect(item.media.copyright.value, "2005 FooBar Media"); | |||
| expect(item.media.copyright.url, 'http://blah.com/additional-info.html'); | |||
| expect(item.media.copyright.value, '2005 FooBar Media'); | |||
| expect(item.media.text.type, "plain"); | |||
| expect(item.media.text.lang, "en"); | |||
| expect(item.media.text.start, "00:00:03.000"); | |||
| expect(item.media.text.end, "00:00:10.000"); | |||
| expect(item.media.text.value, " Oh, say, can you see"); | |||
| expect(item.media.text.type, 'plain'); | |||
| expect(item.media.text.lang, 'en'); | |||
| expect(item.media.text.start, '00:00:03.000'); | |||
| expect(item.media.text.end, '00:00:10.000'); | |||
| expect(item.media.text.value, ' Oh, say, can you see'); | |||
| expect(item.media.restriction.relationship, "allow"); | |||
| expect(item.media.restriction.type, "country"); | |||
| expect(item.media.restriction.value, "au us"); | |||
| expect(item.media.restriction.relationship, 'allow'); | |||
| expect(item.media.restriction.type, 'country'); | |||
| expect(item.media.restriction.value, 'au us'); | |||
| expect(item.media.community.starRating.average, 3.5); | |||
| expect(item.media.community.starRating.count, 20); | |||
| @@ -179,95 +181,99 @@ void main() { | |||
| expect(item.media.community.starRating.max, 10); | |||
| expect(item.media.community.statistics.views, 5); | |||
| expect(item.media.community.statistics.favorites, 4); | |||
| expect(item.media.community.tags.tags, "news: 5, abc:3"); | |||
| expect(item.media.community.tags.tags, 'news: 5, abc:3'); | |||
| expect(item.media.community.tags.weight, 1); | |||
| expect(item.media.comments.length, 2); | |||
| expect(item.media.comments.first, "comment1"); | |||
| expect(item.media.comments.last, "comment2"); | |||
| expect(item.media.comments.first, 'comment1'); | |||
| expect(item.media.comments.last, 'comment2'); | |||
| expect(item.media.embed.url, "http://www.foo.com/player.swf"); | |||
| expect(item.media.embed.url, 'http://www.foo.com/player.swf'); | |||
| expect(item.media.embed.width, 512); | |||
| 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.name, 'type'); | |||
| expect( | |||
| item.media.embed.params.first.value, "application/x-shockwave-flash"); | |||
| 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"); | |||
| expect(item.media.responses.last, "http://www.response2.com"); | |||
| expect(item.media.responses.first, 'http://www.response1.com'); | |||
| expect(item.media.responses.last, 'http://www.response2.com'); | |||
| expect(item.media.backLinks.length, 2); | |||
| expect(item.media.backLinks.first, "http://www.backlink1.com"); | |||
| expect(item.media.backLinks.last, "http://www.backlink2.com"); | |||
| expect(item.media.backLinks.first, 'http://www.backlink1.com'); | |||
| expect(item.media.backLinks.last, 'http://www.backlink2.com'); | |||
| expect(item.media.status.state, "active"); | |||
| expect(item.media.status.state, 'active'); | |||
| expect(item.media.status.reason, null); | |||
| 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.type, 'rent'); | |||
| expect( | |||
| item.media.prices.first.info, "http://www.dummy.jp/package_info.html"); | |||
| expect(item.media.prices.first.currency, "EUR"); | |||
| 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"); | |||
| expect(item.media.license.href, "http://www.licensehost.com/license"); | |||
| expect(item.media.license.value, " Sample license for a video"); | |||
| expect(item.media.license.type, 'text/html'); | |||
| expect(item.media.license.href, 'http://www.licensehost.com/license'); | |||
| expect(item.media.license.value, ' Sample license for a video'); | |||
| expect(item.media.peerLink.type, "application/x-bittorrent"); | |||
| expect(item.media.peerLink.href, "http://www.foo.org/sampleFile.torrent"); | |||
| expect(item.media.peerLink.value, ""); | |||
| expect(item.media.peerLink.type, 'application/x-bittorrent'); | |||
| expect(item.media.peerLink.href, 'http://www.foo.org/sampleFile.torrent'); | |||
| expect(item.media.peerLink.value, ''); | |||
| expect(item.media.rights.status, "official"); | |||
| expect(item.media.rights.status, 'official'); | |||
| expect(item.media.scenes.length, 2); | |||
| expect(item.media.scenes.first.title, "sceneTitle1"); | |||
| expect(item.media.scenes.first.description, "sceneDesc1"); | |||
| expect(item.media.scenes.first.startTime, "00:15"); | |||
| expect(item.media.scenes.first.endTime, "00:45"); | |||
| expect(item.media.scenes.first.title, 'sceneTitle1'); | |||
| expect(item.media.scenes.first.description, 'sceneDesc1'); | |||
| expect(item.media.scenes.first.startTime, '00:15'); | |||
| expect(item.media.scenes.first.endTime, '00:45'); | |||
| }); | |||
| test("parse RSS-DC.xml", () { | |||
| var xmlString = File("test/xml/RSS-DC.xml").readAsStringSync(); | |||
| test('parse RSS-DC.xml', () { | |||
| var xmlString = File('test/xml/RSS-DC.xml').readAsStringSync(); | |||
| var feed = RssFeed.parse(xmlString); | |||
| expect(feed.dc.title, "title"); | |||
| expect(feed.dc.creator, "creator"); | |||
| expect(feed.dc.subject, "subject"); | |||
| expect(feed.dc.description, "description"); | |||
| expect(feed.dc.publisher, "publisher"); | |||
| expect(feed.dc.contributor, "contributor"); | |||
| expect(feed.dc.date, "2000-01-01T12:00+00:00"); | |||
| expect(feed.dc.type, "type"); | |||
| expect(feed.dc.format, "format"); | |||
| expect(feed.dc.identifier, "identifier"); | |||
| expect(feed.dc.source, "source"); | |||
| expect(feed.dc.language, "language"); | |||
| expect(feed.dc.relation, "relation"); | |||
| expect(feed.dc.coverage, "coverage"); | |||
| expect(feed.dc.rights, "rights"); | |||
| expect(feed.items.first.dc.title, "title"); | |||
| expect(feed.items.first.dc.creator, "creator"); | |||
| expect(feed.items.first.dc.subject, "subject"); | |||
| expect(feed.items.first.dc.description, "description"); | |||
| expect(feed.items.first.dc.publisher, "publisher"); | |||
| expect(feed.items.first.dc.contributor, "contributor"); | |||
| expect(feed.items.first.dc.date, "2000-01-01T12:00+00:00"); | |||
| expect(feed.items.first.dc.type, "type"); | |||
| expect(feed.items.first.dc.format, "format"); | |||
| expect(feed.items.first.dc.identifier, "identifier"); | |||
| expect(feed.items.first.dc.source, "source"); | |||
| expect(feed.items.first.dc.language, "language"); | |||
| expect(feed.items.first.dc.relation, "relation"); | |||
| expect(feed.items.first.dc.coverage, "coverage"); | |||
| expect(feed.items.first.dc.rights, "rights"); | |||
| expect(feed.dc.title, 'title'); | |||
| expect(feed.dc.creator, 'creator'); | |||
| expect(feed.dc.subject, 'subject'); | |||
| expect(feed.dc.description, 'description'); | |||
| expect(feed.dc.publisher, 'publisher'); | |||
| expect(feed.dc.contributor, 'contributor'); | |||
| expect(feed.dc.date, DateTime.utc(2000, 1, 1, 12)); | |||
| expect(feed.dc.created, DateTime.utc(2000, 1, 1, 13)); | |||
| expect(feed.dc.modified, DateTime.utc(2000, 1, 1, 14)); | |||
| expect(feed.dc.type, 'type'); | |||
| expect(feed.dc.format, 'format'); | |||
| expect(feed.dc.identifier, 'identifier'); | |||
| expect(feed.dc.source, 'source'); | |||
| expect(feed.dc.language, 'language'); | |||
| expect(feed.dc.relation, 'relation'); | |||
| expect(feed.dc.coverage, 'coverage'); | |||
| expect(feed.dc.rights, 'rights'); | |||
| expect(feed.items.first.dc.title, 'title'); | |||
| expect(feed.items.first.dc.creator, 'creator'); | |||
| expect(feed.items.first.dc.subject, 'subject'); | |||
| expect(feed.items.first.dc.description, 'description'); | |||
| expect(feed.items.first.dc.publisher, 'publisher'); | |||
| expect(feed.items.first.dc.contributor, 'contributor'); | |||
| expect(feed.items.first.dc.date, DateTime.utc(2000, 1, 2, 12)); | |||
| expect(feed.items.first.dc.created, DateTime.utc(2000, 1, 2, 13)); | |||
| expect(feed.items.first.dc.modified, DateTime.utc(2000, 1, 2, 14)); | |||
| expect(feed.items.first.dc.type, 'type'); | |||
| expect(feed.items.first.dc.format, 'format'); | |||
| expect(feed.items.first.dc.identifier, 'identifier'); | |||
| expect(feed.items.first.dc.source, 'source'); | |||
| expect(feed.items.first.dc.language, 'language'); | |||
| expect(feed.items.first.dc.relation, 'relation'); | |||
| expect(feed.items.first.dc.coverage, 'coverage'); | |||
| expect(feed.items.first.dc.rights, 'rights'); | |||
| }); | |||
| test("parse RSS-Empty.xml", () { | |||
| var xmlString = File("test/xml/RSS-Empty.xml").readAsStringSync(); | |||
| test('parse RSS-Empty.xml', () { | |||
| var xmlString = File('test/xml/RSS-Empty.xml').readAsStringSync(); | |||
| var feed = RssFeed.parse(xmlString); | |||
| @@ -310,58 +316,96 @@ void main() { | |||
| expect(feed.items.first.content, null); | |||
| }); | |||
| test("parse RSS-Itunes.xml", () { | |||
| var xmlString = File("test/xml/RSS-Itunes.xml").readAsStringSync(); | |||
| test('parse RSS-Itunes.xml', () { | |||
| var xmlString = File('test/xml/RSS-Itunes.xml').readAsStringSync(); | |||
| var feed = RssFeed.parse(xmlString); | |||
| expect(feed.itunes.author, "Changelog Media"); | |||
| expect(feed.itunes.summary, "Foo"); | |||
| 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"); | |||
| '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"); | |||
| 'go,golang,open source,software,development'.split(',')); | |||
| expect(feed.itunes.owner.name, 'Changelog Media'); | |||
| expect(feed.itunes.owner.email, 'editors@changelog.com'); | |||
| expect( | |||
| Set.from([ | |||
| feed.itunes.categories[0].category, | |||
| feed.itunes.categories[1].category | |||
| ]), | |||
| ["Technology", "Foo"]); | |||
| ['Technology', 'Foo']); | |||
| for (var category in feed.itunes.categories) { | |||
| switch (category.category) { | |||
| case "Foo": | |||
| expect(category.subCategories, ["Bar", "Baz"]); | |||
| case 'Foo': | |||
| expect(category.subCategories, ['Bar', 'Baz']); | |||
| break; | |||
| case "Technology": | |||
| expect(category.subCategories, ["Software How-To", "Tech News"]); | |||
| 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.title, 'Go Time'); | |||
| expect(feed.itunes.type, ItunesType.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, RssItunesEpisodeType.full); | |||
| expect(item.itunes.episodeType, ItunesEpisodeType.full); | |||
| expect(item.itunes.episode, 1); | |||
| expect(item.itunes.season, 1); | |||
| expect(item.itunes.image.href, | |||
| "https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357"); | |||
| '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.subtitle, "with Erik, Carlisia, and Brian"); | |||
| expect(item.itunes.summary, "Foo"); | |||
| '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"); | |||
| 'Erik St. Martin, Carlisia Pinto, and Brian Ketelsen'); | |||
| expect(item.itunes.explicit, false); | |||
| expect(item.itunes.title, "awesome title"); | |||
| expect(item.itunes.title, 'awesome title'); | |||
| expect(item.itunes.block, false); | |||
| }); | |||
| test('parse RSS-RDF.xml', () { | |||
| var xmlString = File('test/xml/RSS-RDF.xml').readAsStringSync(); | |||
| var feed = RssFeed.parse(xmlString); | |||
| expect(feed.title, 'Mozilla Dot Org'); | |||
| expect(feed.link, 'http://www.mozilla.org'); | |||
| expect(feed.description, 'the Mozilla Organization web site'); | |||
| expect(feed.image.title, 'Mozilla'); | |||
| expect(feed.image.url, 'http://www.mozilla.org/images/moz.gif'); | |||
| expect(feed.image.link, 'http://www.mozilla.org'); | |||
| expect(feed.items.length, 5); | |||
| expect(feed.items.first.title, 'New Status Updates'); | |||
| expect(feed.items.first.link, 'http://www.mozilla.org/status/'); | |||
| }); | |||
| test('parse RSS-Syndication.xml', () { | |||
| var xmlString = File('test/xml/RSS-Syndication.xml').readAsStringSync(); | |||
| var feed = RssFeed.parse(xmlString); | |||
| expect(feed.title, 'Meerkat'); | |||
| expect(feed.link, 'http://meerkat.oreillynet.com'); | |||
| expect(feed.description, 'Meerkat: An Open Wire Service'); | |||
| expect(feed.image.title, 'Meerkat Powered!'); | |||
| expect(feed.image.url, | |||
| 'http://meerkat.oreillynet.com/icons/meerkat-powered.jpg'); | |||
| expect(feed.image.link, 'http://meerkat.oreillynet.com'); | |||
| expect(feed.syndication.updatePeriod, SyndicationUpdatePeriod.hourly); | |||
| expect(feed.syndication.updateFrequency, 2); | |||
| expect(feed.syndication.updateBase, DateTime.utc(2001, 1, 1, 12, 1)); | |||
| expect(feed.items.length, 1); | |||
| expect(feed.items.first.title, 'XML: A Disruptive Technology'); | |||
| expect(feed.items.first.description, | |||
| 'XML is placing increasingly heavy loads on the existing technical infrastructure of the Internet.'); | |||
| expect(feed.items.first.link, 'http://c.moreover.com/click/here.pl?r123'); | |||
| }); | |||
| } | |||
| @@ -35,7 +35,7 @@ | |||
| <entry> | |||
| <id>foo-bar-entry-id-1</id> | |||
| <title>Foo bar item 1</title> | |||
| <updated>2018-04-06T13:02:47Z</updated> | |||
| <updated>2018-04-06T13:02:40Z</updated> | |||
| <author> | |||
| <name>Ellie</name> | |||
| <uri>http://foo.bar.news/people/ellie</uri> | |||
| @@ -9,6 +9,8 @@ | |||
| <dc:publisher>publisher</dc:publisher> | |||
| <dc:contributor>contributor</dc:contributor> | |||
| <dc:date>2000-01-01T12:00+00:00</dc:date> | |||
| <dc:created>2000-01-01T13:00+00:00</dc:created> | |||
| <dc:modified>2000-01-01T14:00+00:00</dc:modified> | |||
| <dc:type>type</dc:type> | |||
| <dc:format>format</dc:format> | |||
| <dc:identifier>identifier</dc:identifier> | |||
| @@ -24,7 +26,9 @@ | |||
| <dc:description>description</dc:description> | |||
| <dc:publisher>publisher</dc:publisher> | |||
| <dc:contributor>contributor</dc:contributor> | |||
| <dc:date>2000-01-01T12:00+00:00</dc:date> | |||
| <dc:date>2000-01-02T12:00+00:00</dc:date> | |||
| <dc:created>2000-01-02T13:00+00:00</dc:created> | |||
| <dc:modified>2000-01-02T14:00+00:00</dc:modified> | |||
| <dc:type>type</dc:type> | |||
| <dc:format>format</dc:format> | |||
| <dc:identifier>identifier</dc:identifier> | |||
| @@ -45,12 +45,9 @@ | |||
| <itunes:keywords>go, golang, open source, software, development</itunes:keywords> | |||
| <itunes:subtitle>with Erik, Carlisia, and Brian</itunes:subtitle> | |||
| <itunes:summary>Foo</itunes:summary> | |||
| <dc:creator> | |||
| Erik St. Martin, Carlisia Pinto, and Brian Ketelsen | |||
| <dc:creator>Erik St. Martin, Carlisia Pinto, and Brian Ketelsen | |||
| </dc:creator> | |||
| <itunes:author> | |||
| Erik St. Martin, Carlisia Pinto, and Brian Ketelsen | |||
| </itunes:author> | |||
| <itunes:author>Erik St. Martin, Carlisia Pinto, and Brian Ketelsen</itunes:author> | |||
| </item> | |||
| </channel> | |||
| </rss> | |||
| @@ -0,0 +1,35 @@ | |||
| <?xml version="1.0"?> | |||
| <rdf:RDF | |||
| xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
| xmlns="http://channel.netscape.com/rdf/simple/0.9/"> | |||
| <channel> | |||
| <title>Mozilla Dot Org</title> | |||
| <link>http://www.mozilla.org</link> | |||
| <description>the Mozilla Organization web site</description> | |||
| </channel> | |||
| <image> | |||
| <title>Mozilla</title> | |||
| <url>http://www.mozilla.org/images/moz.gif</url> | |||
| <link>http://www.mozilla.org</link> | |||
| </image> | |||
| <item> | |||
| <title>New Status Updates</title> | |||
| <link>http://www.mozilla.org/status/</link> | |||
| </item> | |||
| <item> | |||
| <title>Bugzilla Reorganized</title> | |||
| <link>http://www.mozilla.org/bugs/</link> | |||
| </item> | |||
| <item> | |||
| <title>Mozilla Party, 2.0!</title> | |||
| <link>http://www.mozilla.org/party/1999/</link> | |||
| </item> | |||
| <item> | |||
| <title>Unix Platform Parity</title> | |||
| <link>http://www.mozilla.org/build/unix.html</link> | |||
| </item> | |||
| <item> | |||
| <title>NPL 1.0M published</title> | |||
| <link>http://www.mozilla.org/NPL/NPL-1.0M.html</link> | |||
| </item> | |||
| </rdf:RDF> | |||
| @@ -0,0 +1,24 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
| xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" | |||
| xmlns="http://purl.org/rss/1.0/"> | |||
| <channel rdf:about="http://meerkat.oreillynet.com/?_fl=rss1.0"> | |||
| <title>Meerkat</title> | |||
| <link>http://meerkat.oreillynet.com</link> | |||
| <description>Meerkat: An Open Wire Service</description> | |||
| <sy:updatePeriod>hourly</sy:updatePeriod> | |||
| <sy:updateFrequency>2</sy:updateFrequency> | |||
| <sy:updateBase>2001-01-01T12:01+00:00</sy:updateBase> | |||
| <image rdf:resource="http://meerkat.oreillynet.com/icons/meerkat-powered.jpg"/> | |||
| </channel> | |||
| <image rdf:about="http://meerkat.oreillynet.com/icons/meerkat-powered.jpg"> | |||
| <title>Meerkat Powered!</title> | |||
| <url>http://meerkat.oreillynet.com/icons/meerkat-powered.jpg</url> | |||
| <link>http://meerkat.oreillynet.com</link> | |||
| </image> | |||
| <item rdf:about="http://c.moreover.com/click/here.pl?r123"> | |||
| <title>XML: A Disruptive Technology</title> | |||
| <link>http://c.moreover.com/click/here.pl?r123</link> | |||
| <description>XML is placing increasingly heavy loads on the existing technical infrastructure of the Internet.</description> | |||
| </item> | |||
| </rdf:RDF> | |||