| @@ -13,3 +13,5 @@ doc/api/ | |||||
| .idea | .idea | ||||
| .dart_tool | .dart_tool | ||||
| .vscode/ | |||||
| @@ -1,17 +1,22 @@ | |||||
| import 'package:http/http.dart' as http; | |||||
| import 'dart:io'; | |||||
| import 'package:http/io_client.dart'; | |||||
| import 'package:webfeed/webfeed.dart'; | import 'package:webfeed/webfeed.dart'; | ||||
| void main() async { | void main() async { | ||||
| var client = http.Client(); | |||||
| final client = IOClient(HttpClient() | |||||
| ..badCertificateCallback = | |||||
| ((X509Certificate cert, String host, int port) => true)); | |||||
| // RSS feed | // RSS feed | ||||
| var response = await client | |||||
| .get('https://developer.apple.com/news/releases/rss/releases.rss'); | |||||
| var response = await client.get( | |||||
| Uri.parse('https://developer.apple.com/news/releases/rss/releases.rss')); | |||||
| var channel = RssFeed.parse(response.body); | var channel = RssFeed.parse(response.body); | ||||
| print(channel); | print(channel); | ||||
| // Atom feed | // Atom feed | ||||
| response = await client.get('https://www.theverge.com/rss/index.xml'); | |||||
| response = | |||||
| await client.get(Uri.parse('https://www.theverge.com/rss/index.xml')); | |||||
| var feed = AtomFeed.parse(response.body); | var feed = AtomFeed.parse(response.body); | ||||
| print(feed); | print(feed); | ||||
| @@ -1,9 +1,9 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class AtomCategory { | class AtomCategory { | ||||
| final String term; | |||||
| final String scheme; | |||||
| final String label; | |||||
| final String? term; | |||||
| final String? scheme; | |||||
| final String? label; | |||||
| AtomCategory(this.term, this.scheme, this.label); | AtomCategory(this.term, this.scheme, this.label); | ||||
| @@ -4,24 +4,24 @@ import 'package:webfeed/domain/atom_item.dart'; | |||||
| import 'package:webfeed/domain/atom_link.dart'; | import 'package:webfeed/domain/atom_link.dart'; | ||||
| import 'package:webfeed/domain/atom_person.dart'; | import 'package:webfeed/domain/atom_person.dart'; | ||||
| import 'package:webfeed/util/datetime.dart'; | import 'package:webfeed/util/datetime.dart'; | ||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class AtomFeed { | class AtomFeed { | ||||
| final String id; | |||||
| final String title; | |||||
| final DateTime updated; | |||||
| final List<AtomItem> items; | |||||
| final String? id; | |||||
| final String? title; | |||||
| final DateTime? updated; | |||||
| final List<AtomItem>? items; | |||||
| final List<AtomLink> links; | |||||
| final List<AtomPerson> authors; | |||||
| final List<AtomPerson> contributors; | |||||
| final List<AtomCategory> categories; | |||||
| final AtomGenerator generator; | |||||
| final String icon; | |||||
| final String logo; | |||||
| final String rights; | |||||
| final String subtitle; | |||||
| final List<AtomLink>? links; | |||||
| final List<AtomPerson>? authors; | |||||
| final List<AtomPerson>? contributors; | |||||
| final List<AtomCategory>? categories; | |||||
| final AtomGenerator? generator; | |||||
| final String? icon; | |||||
| final String? logo; | |||||
| final String? rights; | |||||
| final String? subtitle; | |||||
| AtomFeed({ | AtomFeed({ | ||||
| this.id, | this.id, | ||||
| @@ -41,15 +41,16 @@ class AtomFeed { | |||||
| factory AtomFeed.parse(String xmlString) { | factory AtomFeed.parse(String xmlString) { | ||||
| var document = XmlDocument.parse(xmlString); | var document = XmlDocument.parse(xmlString); | ||||
| var feedElement = findFirstElement(document, 'feed'); | |||||
| var feedElement = document.findElements('feed').firstOrNull; | |||||
| if (feedElement == null) { | if (feedElement == null) { | ||||
| throw ArgumentError('feed not found'); | throw ArgumentError('feed not found'); | ||||
| } | } | ||||
| return AtomFeed( | return AtomFeed( | ||||
| id: findFirstElement(feedElement, 'id')?.text, | |||||
| title: findFirstElement(feedElement, 'title')?.text, | |||||
| updated: parseDateTime(findFirstElement(feedElement, 'updated')?.text), | |||||
| id: feedElement.findElements('id').firstOrNull?.text, | |||||
| title: feedElement.findElements('title').firstOrNull?.text, | |||||
| updated: | |||||
| parseDateTime(feedElement.findElements('updated').firstOrNull?.text), | |||||
| items: feedElement | items: feedElement | ||||
| .findElements('entry') | .findElements('entry') | ||||
| .map((e) => AtomItem.parse(e)) | .map((e) => AtomItem.parse(e)) | ||||
| @@ -70,12 +71,14 @@ class AtomFeed { | |||||
| .findElements('category') | .findElements('category') | ||||
| .map((e) => AtomCategory.parse(e)) | .map((e) => AtomCategory.parse(e)) | ||||
| .toList(), | .toList(), | ||||
| generator: | |||||
| 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, | |||||
| generator: feedElement | |||||
| .findElements('generator') | |||||
| .map((e) => AtomGenerator.parse(e)) | |||||
| .firstOrNull, | |||||
| icon: feedElement.findElements('icon').firstOrNull?.text, | |||||
| logo: feedElement.findElements('logo').firstOrNull?.text, | |||||
| rights: feedElement.findElements('rights').firstOrNull?.text, | |||||
| subtitle: feedElement.findElements('subtitle').firstOrNull?.text, | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,16 +1,13 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class AtomGenerator { | class AtomGenerator { | ||||
| final String uri; | |||||
| final String version; | |||||
| final String value; | |||||
| final String? uri; | |||||
| final String? version; | |||||
| final String? value; | |||||
| AtomGenerator(this.uri, this.version, this.value); | AtomGenerator(this.uri, this.version, this.value); | ||||
| factory AtomGenerator.parse(XmlElement element) { | factory AtomGenerator.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| var uri = element.getAttribute('uri'); | var uri = element.getAttribute('uri'); | ||||
| var version = element.getAttribute('version'); | var version = element.getAttribute('version'); | ||||
| var value = element.text; | var value = element.text; | ||||
| @@ -4,24 +4,24 @@ import 'package:webfeed/domain/atom_person.dart'; | |||||
| import 'package:webfeed/domain/atom_source.dart'; | import 'package:webfeed/domain/atom_source.dart'; | ||||
| import 'package:webfeed/domain/media/media.dart'; | import 'package:webfeed/domain/media/media.dart'; | ||||
| import 'package:webfeed/util/datetime.dart'; | import 'package:webfeed/util/datetime.dart'; | ||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class AtomItem { | class AtomItem { | ||||
| final String id; | |||||
| final String title; | |||||
| final DateTime updated; | |||||
| final String? id; | |||||
| final String? title; | |||||
| final DateTime? updated; | |||||
| final List<AtomPerson> authors; | |||||
| final List<AtomLink> links; | |||||
| final List<AtomCategory> categories; | |||||
| final List<AtomPerson> contributors; | |||||
| final AtomSource source; | |||||
| final String published; | |||||
| final String content; | |||||
| final String summary; | |||||
| final String rights; | |||||
| final Media media; | |||||
| final List<AtomPerson>? authors; | |||||
| final List<AtomLink>? links; | |||||
| final List<AtomCategory>? categories; | |||||
| final List<AtomPerson>? contributors; | |||||
| final AtomSource? source; | |||||
| final String? published; | |||||
| final String? content; | |||||
| final String? summary; | |||||
| final String? rights; | |||||
| final Media? media; | |||||
| AtomItem({ | AtomItem({ | ||||
| this.id, | this.id, | ||||
| @@ -41,9 +41,9 @@ class AtomItem { | |||||
| factory AtomItem.parse(XmlElement element) { | factory AtomItem.parse(XmlElement element) { | ||||
| return AtomItem( | return AtomItem( | ||||
| id: findFirstElement(element, 'id')?.text, | |||||
| title: findFirstElement(element, 'title')?.text, | |||||
| updated: parseDateTime(findFirstElement(element, 'updated')?.text), | |||||
| id: element.findElements('id').firstOrNull?.text, | |||||
| title: element.findElements('title').firstOrNull?.text, | |||||
| updated: parseDateTime(element.findElements('updated').firstOrNull?.text), | |||||
| authors: element | authors: element | ||||
| .findElements('author') | .findElements('author') | ||||
| .map((e) => AtomPerson.parse(e)) | .map((e) => AtomPerson.parse(e)) | ||||
| @@ -58,11 +58,14 @@ class AtomItem { | |||||
| .findElements('contributor') | .findElements('contributor') | ||||
| .map((e) => AtomPerson.parse(e)) | .map((e) => AtomPerson.parse(e)) | ||||
| .toList(), | .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, | |||||
| source: element | |||||
| .findElements('source') | |||||
| .map((e) => AtomSource.parse(e)) | |||||
| .firstOrNull, | |||||
| published: element.findElements('published').firstOrNull?.text, | |||||
| content: element.findElements('content').firstOrNull?.text, | |||||
| summary: element.findElements('summary').firstOrNull?.text, | |||||
| rights: element.findElements('rights').firstOrNull?.text, | |||||
| media: Media.parse(element), | media: Media.parse(element), | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -1,11 +1,11 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class AtomLink { | class AtomLink { | ||||
| final String href; | |||||
| final String rel; | |||||
| final String type; | |||||
| final String hreflang; | |||||
| final String title; | |||||
| final String? href; | |||||
| final String? rel; | |||||
| final String? type; | |||||
| final String? hreflang; | |||||
| final String? title; | |||||
| final int length; | final int length; | ||||
| AtomLink( | AtomLink( | ||||
| @@ -25,7 +25,7 @@ class AtomLink { | |||||
| var hreflang = element.getAttribute('hreflang'); | var hreflang = element.getAttribute('hreflang'); | ||||
| var length = 0; | var length = 0; | ||||
| if (element.getAttribute('length') != null) { | if (element.getAttribute('length') != null) { | ||||
| length = int.parse(element.getAttribute('length')); | |||||
| length = int.parse(element.getAttribute('length')!); | |||||
| } | } | ||||
| return AtomLink(href, rel, type, hreflang, title, length); | return AtomLink(href, rel, type, hreflang, title, length); | ||||
| } | } | ||||
| @@ -1,18 +1,18 @@ | |||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class AtomPerson { | class AtomPerson { | ||||
| final String name; | |||||
| final String uri; | |||||
| final String email; | |||||
| final String? name; | |||||
| 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) { | factory AtomPerson.parse(XmlElement element) { | ||||
| return AtomPerson( | return AtomPerson( | ||||
| name: findFirstElement(element, 'name')?.text, | |||||
| uri: findFirstElement(element, 'uri')?.text, | |||||
| email: findFirstElement(element, 'email')?.text, | |||||
| name: element.findElements('name').firstOrNull?.text, | |||||
| uri: element.findElements('uri').firstOrNull?.text, | |||||
| email: element.findElements('email').firstOrNull?.text, | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,10 +1,10 @@ | |||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class AtomSource { | class AtomSource { | ||||
| final String id; | |||||
| final String title; | |||||
| final String updated; | |||||
| final String? id; | |||||
| final String? title; | |||||
| final String? updated; | |||||
| AtomSource({ | AtomSource({ | ||||
| this.id, | this.id, | ||||
| @@ -13,13 +13,10 @@ class AtomSource { | |||||
| }); | }); | ||||
| factory AtomSource.parse(XmlElement element) { | factory AtomSource.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return AtomSource( | return AtomSource( | ||||
| id: findFirstElement(element, 'id')?.text, | |||||
| title: findFirstElement(element, 'title')?.text, | |||||
| updated: findFirstElement(element, 'updated')?.text, | |||||
| id: element.findElements('id').firstOrNull?.text, | |||||
| title: element.findElements('title').firstOrNull?.text, | |||||
| updated: element.findElements('updated').firstOrNull?.text, | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,25 +1,25 @@ | |||||
| import 'package:webfeed/util/datetime.dart'; | import 'package:webfeed/util/datetime.dart'; | ||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class DublinCore { | class DublinCore { | ||||
| final String title; | |||||
| final String description; | |||||
| final String creator; | |||||
| final String subject; | |||||
| final String publisher; | |||||
| final String contributor; | |||||
| final DateTime date; | |||||
| final DateTime created; | |||||
| final DateTime modified; | |||||
| final String type; | |||||
| final String format; | |||||
| final String identifier; | |||||
| final String source; | |||||
| final String language; | |||||
| final String relation; | |||||
| final String coverage; | |||||
| final String rights; | |||||
| final String? title; | |||||
| final String? description; | |||||
| final String? creator; | |||||
| final String? subject; | |||||
| final String? publisher; | |||||
| final String? contributor; | |||||
| final DateTime? date; | |||||
| final DateTime? created; | |||||
| final DateTime? modified; | |||||
| final String? type; | |||||
| final String? format; | |||||
| final String? identifier; | |||||
| final String? source; | |||||
| final String? language; | |||||
| final String? relation; | |||||
| final String? coverage; | |||||
| final String? rights; | |||||
| DublinCore({ | DublinCore({ | ||||
| this.title, | this.title, | ||||
| @@ -42,27 +42,26 @@ class DublinCore { | |||||
| }); | }); | ||||
| factory DublinCore.parse(XmlElement element) { | factory DublinCore.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return DublinCore( | return DublinCore( | ||||
| 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, | |||||
| title: element.findElements('dc:title').firstOrNull?.text, | |||||
| description: element.findElements('dc:description').firstOrNull?.text, | |||||
| creator: element.findElements('dc:creator').firstOrNull?.text, | |||||
| subject: element.findElements('dc:subject').firstOrNull?.text, | |||||
| publisher: element.findElements('dc:publisher').firstOrNull?.text, | |||||
| contributor: element.findElements('dc:contributor').firstOrNull?.text, | |||||
| date: parseDateTime(element.findElements('dc:date').firstOrNull?.text), | |||||
| created: | |||||
| parseDateTime(element.findElements('dc:created').firstOrNull?.text), | |||||
| modified: | |||||
| parseDateTime(element.findElements('dc:modified').firstOrNull?.text), | |||||
| type: element.findElements('dc:type').firstOrNull?.text, | |||||
| format: element.findElements('dc:format').firstOrNull?.text, | |||||
| identifier: element.findElements('dc:identifier').firstOrNull?.text, | |||||
| source: element.findElements('dc:source').firstOrNull?.text, | |||||
| language: element.findElements('dc:language').firstOrNull?.text, | |||||
| relation: element.findElements('dc:relation').firstOrNull?.text, | |||||
| coverage: element.findElements('dc:coverage').firstOrNull?.text, | |||||
| rights: element.findElements('dc:rights').firstOrNull?.text, | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -4,27 +4,28 @@ import 'package:webfeed/domain/itunes/itunes_image.dart'; | |||||
| import 'package:webfeed/domain/itunes/itunes_owner.dart'; | import 'package:webfeed/domain/itunes/itunes_owner.dart'; | ||||
| import 'package:webfeed/domain/itunes/itunes_type.dart'; | import 'package:webfeed/domain/itunes/itunes_type.dart'; | ||||
| import 'package:webfeed/util/string.dart'; | import 'package:webfeed/util/string.dart'; | ||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:webfeed/util/xml.dart'; | import 'package:webfeed/util/xml.dart'; | ||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Itunes { | 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; | |||||
| 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({ | Itunes({ | ||||
| this.author, | this.author, | ||||
| @@ -47,38 +48,50 @@ class Itunes { | |||||
| }); | }); | ||||
| factory Itunes.parse(XmlElement element) { | 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; | |||||
| final episodeStr = element.findElements('itunes:episode').firstOrNull?.text; | |||||
| final seasonStr = element.findElements('itunes:season').firstOrNull?.text; | |||||
| final durationStr = | |||||
| element.findElements('itunes:duration').firstOrNull?.text; | |||||
| return Itunes( | return Itunes( | ||||
| author: findFirstElement(element, 'itunes:author')?.text, | |||||
| summary: findFirstElement(element, 'itunes:summary')?.text, | |||||
| author: element.findElements('itunes:author').firstOrNull?.text, | |||||
| summary: element.findElements('itunes:summary').firstOrNull?.text, | |||||
| explicit: parseBoolLiteral(element, 'itunes:explicit'), | 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') | |||||
| title: element.findElements('itunes:title').firstOrNull?.text, | |||||
| subtitle: element.findElements('itunes:subtitle').firstOrNull?.text, | |||||
| owner: element | |||||
| .findElements('itunes:owner') | |||||
| .map((e) => ItunesOwner.parse(e)) | |||||
| .firstOrNull, | |||||
| keywords: element | |||||
| .findElements('itunes:keywords') | |||||
| .firstOrNull | |||||
| ?.text | ?.text | ||||
| ?.split(',') | |||||
| ?.map((keyword) => keyword.trim()) | |||||
| ?.toList() ?? | |||||
| .split(',') | |||||
| .map((keyword) => keyword.trim()) | |||||
| .toList() ?? | |||||
| [], | [], | ||||
| image: ItunesImage.parse(findFirstElement(element, 'itunes:image')), | |||||
| categories: findElements(element, 'itunes:category') | |||||
| image: element | |||||
| .findElements('itunes:image') | |||||
| .map((e) => ItunesImage.parse(e)) | |||||
| .firstOrNull, | |||||
| categories: element | |||||
| .findElements('itunes:category') | |||||
| .map((e) => ItunesCategory.parse(e)) | .map((e) => ItunesCategory.parse(e)) | ||||
| .toList(), | .toList(), | ||||
| type: newItunesType(findFirstElement(element, 'itunes:type')), | |||||
| newFeedUrl: findFirstElement(element, 'itunes:new-feed-url')?.text, | |||||
| type: element | |||||
| .findElements('itunes:type') | |||||
| .map((e) => newItunesType(e)) | |||||
| .firstOrNull, | |||||
| newFeedUrl: element.findElements('itunes:new-feed-url').firstOrNull?.text, | |||||
| block: parseBoolLiteral(element, 'itunes:block'), | block: parseBoolLiteral(element, 'itunes:block'), | ||||
| complete: parseBoolLiteral(element, 'itunes:complete'), | complete: parseBoolLiteral(element, 'itunes:complete'), | ||||
| episode: isNullOrEmpty(episodeStr) ? null : int.parse(episodeStr), | |||||
| season: isNullOrEmpty(seasonStr) ? null : int.parse(seasonStr), | |||||
| episode: isNullOrEmpty(episodeStr) ? null : int.tryParse(episodeStr), | |||||
| season: isNullOrEmpty(seasonStr) ? null : int.tryParse(seasonStr), | |||||
| duration: isNullOrEmpty(durationStr) ? null : _parseDuration(durationStr), | duration: isNullOrEmpty(durationStr) ? null : _parseDuration(durationStr), | ||||
| episodeType: | |||||
| newItunesEpisodeType(findFirstElement(element, 'itunes:episodeType')), | |||||
| episodeType: element | |||||
| .findElements('itunes:episodeType') | |||||
| .map((e) => newItunesEpisodeType(e)) | |||||
| .firstOrNull, | |||||
| ); | ); | ||||
| } | } | ||||
| @@ -88,12 +101,12 @@ class Itunes { | |||||
| var seconds = 0; | var seconds = 0; | ||||
| var parts = s.split(':'); | var parts = s.split(':'); | ||||
| if (parts.length > 2) { | if (parts.length > 2) { | ||||
| hours = int.parse(parts[parts.length - 3]); | |||||
| hours = int.tryParse(parts[parts.length - 3]) ?? 0; | |||||
| } | } | ||||
| if (parts.length > 1) { | if (parts.length > 1) { | ||||
| minutes = int.parse(parts[parts.length - 2]); | |||||
| minutes = int.tryParse(parts[parts.length - 2]) ?? 0; | |||||
| } | } | ||||
| seconds = int.parse(parts[parts.length - 1]); | |||||
| seconds = int.tryParse(parts[parts.length - 1]) ?? 0; | |||||
| return Duration( | return Duration( | ||||
| hours: hours, | hours: hours, | ||||
| minutes: minutes, | minutes: minutes, | ||||
| @@ -1,20 +1,17 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class ItunesCategory { | class ItunesCategory { | ||||
| final String category; | |||||
| final List<String> subCategories; | |||||
| final String? category; | |||||
| final List<String>? subCategories; | |||||
| ItunesCategory({this.category, this.subCategories}); | ItunesCategory({this.category, this.subCategories}); | ||||
| factory ItunesCategory.parse(XmlElement element) { | factory ItunesCategory.parse(XmlElement element) { | ||||
| if (element == null) return null; | |||||
| return ItunesCategory( | return ItunesCategory( | ||||
| category: element.getAttribute('text')?.trim(), | |||||
| subCategories: element | |||||
| .findElements('itunes:category') | |||||
| ?.map((e) => e.getAttribute('text')?.trim()) | |||||
| ?.toList() ?? | |||||
| [], | |||||
| ); | |||||
| category: element.getAttribute('text')?.trim(), | |||||
| subCategories: element | |||||
| .findElements('itunes:category') | |||||
| .map((e) => e.getAttribute('text')?.trim() ?? '') | |||||
| .toList()); | |||||
| } | } | ||||
| } | } | ||||
| @@ -3,7 +3,7 @@ import 'package:xml/xml.dart'; | |||||
| enum ItunesEpisodeType { full, trailer, bonus, unknown } | enum ItunesEpisodeType { full, trailer, bonus, unknown } | ||||
| ItunesEpisodeType newItunesEpisodeType(XmlElement element) { | ItunesEpisodeType newItunesEpisodeType(XmlElement element) { | ||||
| switch (element?.text) { | |||||
| switch (element.text) { | |||||
| case 'full': | case 'full': | ||||
| return ItunesEpisodeType.full; | return ItunesEpisodeType.full; | ||||
| case 'trailer': | case 'trailer': | ||||
| @@ -1,12 +1,11 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class ItunesImage { | class ItunesImage { | ||||
| final String href; | |||||
| final String? href; | |||||
| ItunesImage({this.href}); | ItunesImage({this.href}); | ||||
| factory ItunesImage.parse(XmlElement element) { | factory ItunesImage.parse(XmlElement element) { | ||||
| if (element == null) return null; | |||||
| return ItunesImage( | return ItunesImage( | ||||
| href: element.getAttribute('href')?.trim(), | href: element.getAttribute('href')?.trim(), | ||||
| ); | ); | ||||
| @@ -1,17 +1,16 @@ | |||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class ItunesOwner { | class ItunesOwner { | ||||
| final String name; | |||||
| final String email; | |||||
| final String? name; | |||||
| final String? email; | |||||
| ItunesOwner({this.name, this.email}); | ItunesOwner({this.name, this.email}); | ||||
| factory ItunesOwner.parse(XmlElement element) { | factory ItunesOwner.parse(XmlElement element) { | ||||
| if (element == null) return null; | |||||
| return ItunesOwner( | return ItunesOwner( | ||||
| name: findFirstElement(element, 'itunes:name')?.text?.trim(), | |||||
| email: findFirstElement(element, 'itunes:email')?.text?.trim(), | |||||
| name: element.findElements('itunes:name').firstOrNull?.text.trim(), | |||||
| email: element.findElements('itunes:email').firstOrNull?.text.trim(), | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -3,7 +3,7 @@ import 'package:xml/xml.dart'; | |||||
| enum ItunesType { episodic, serial, unknown } | enum ItunesType { episodic, serial, unknown } | ||||
| ItunesType newItunesType(XmlElement element) { | ItunesType newItunesType(XmlElement element) { | ||||
| switch (element?.text) { | |||||
| switch (element.text) { | |||||
| case 'episodic': | case 'episodic': | ||||
| return ItunesType.episodic; | return ItunesType.episodic; | ||||
| case 'serial': | case 'serial': | ||||
| @@ -1,9 +1,9 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Category { | class Category { | ||||
| final String scheme; | |||||
| final String label; | |||||
| final String value; | |||||
| final String? scheme; | |||||
| final String? label; | |||||
| final String? value; | |||||
| Category({ | Category({ | ||||
| this.scheme, | this.scheme, | ||||
| @@ -12,9 +12,6 @@ class Category { | |||||
| }); | }); | ||||
| factory Category.parse(XmlElement element) { | factory Category.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Category( | return Category( | ||||
| scheme: element.getAttribute('scheme'), | scheme: element.getAttribute('scheme'), | ||||
| label: element.getAttribute('label'), | label: element.getAttribute('label'), | ||||
| @@ -1,13 +1,13 @@ | |||||
| import 'package:webfeed/domain/media/star_rating.dart'; | import 'package:webfeed/domain/media/star_rating.dart'; | ||||
| import 'package:webfeed/domain/media/statistics.dart'; | import 'package:webfeed/domain/media/statistics.dart'; | ||||
| import 'package:webfeed/domain/media/tags.dart'; | import 'package:webfeed/domain/media/tags.dart'; | ||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Community { | class Community { | ||||
| final StarRating starRating; | |||||
| final Statistics statistics; | |||||
| final Tags tags; | |||||
| final StarRating? starRating; | |||||
| final Statistics? statistics; | |||||
| final Tags? tags; | |||||
| Community({ | Community({ | ||||
| this.starRating, | this.starRating, | ||||
| @@ -16,19 +16,19 @@ class Community { | |||||
| }); | }); | ||||
| factory Community.parse(XmlElement element) { | factory Community.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Community( | return Community( | ||||
| starRating: StarRating.parse( | |||||
| findFirstElement(element, 'media:starRating'), | |||||
| ), | |||||
| statistics: Statistics.parse( | |||||
| findFirstElement(element, 'media:statistics'), | |||||
| ), | |||||
| tags: Tags.parse( | |||||
| findFirstElement(element, 'media:tags'), | |||||
| ), | |||||
| starRating: element | |||||
| .findElements('media:starRating') | |||||
| .map((e) => StarRating.parse(e)) | |||||
| .firstOrNull, | |||||
| statistics: element | |||||
| .findElements('media:statistics') | |||||
| .map((e) => Statistics.parse(e)) | |||||
| .firstOrNull, | |||||
| tags: element | |||||
| .findElements('media:tags') | |||||
| .map((e) => Tags.parse(e)) | |||||
| .firstOrNull, | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,20 +1,20 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Content { | class Content { | ||||
| final String url; | |||||
| final String type; | |||||
| final int fileSize; | |||||
| final String medium; | |||||
| final bool isDefault; | |||||
| final String expression; | |||||
| final int bitrate; | |||||
| final double framerate; | |||||
| final double samplingrate; | |||||
| final int channels; | |||||
| final int duration; | |||||
| final int height; | |||||
| final int width; | |||||
| final String lang; | |||||
| final String? url; | |||||
| final String? type; | |||||
| final int? fileSize; | |||||
| final String? medium; | |||||
| final bool? isDefault; | |||||
| final String? expression; | |||||
| final int? bitrate; | |||||
| final double? framerate; | |||||
| final double? samplingrate; | |||||
| final int? channels; | |||||
| final int? duration; | |||||
| final int? height; | |||||
| final int? width; | |||||
| final String? lang; | |||||
| Content({ | Content({ | ||||
| this.url, | this.url, | ||||
| @@ -1,8 +1,8 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Copyright { | class Copyright { | ||||
| final String url; | |||||
| final String value; | |||||
| final String? url; | |||||
| final String? value; | |||||
| Copyright({ | Copyright({ | ||||
| this.url, | this.url, | ||||
| @@ -10,9 +10,6 @@ class Copyright { | |||||
| }); | }); | ||||
| factory Copyright.parse(XmlElement element) { | factory Copyright.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Copyright( | return Copyright( | ||||
| url: element.getAttribute('url'), | url: element.getAttribute('url'), | ||||
| value: element.text, | value: element.text, | ||||
| @@ -1,9 +1,9 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Credit { | class Credit { | ||||
| final String role; | |||||
| final String scheme; | |||||
| final String value; | |||||
| final String? role; | |||||
| final String? scheme; | |||||
| final String? value; | |||||
| Credit({ | Credit({ | ||||
| this.role, | this.role, | ||||
| @@ -1,8 +1,8 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Description { | class Description { | ||||
| final String type; | |||||
| final String value; | |||||
| final String? type; | |||||
| final String? value; | |||||
| Description({ | Description({ | ||||
| this.type, | this.type, | ||||
| @@ -10,9 +10,6 @@ class Description { | |||||
| }); | }); | ||||
| factory Description.parse(XmlElement element) { | factory Description.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Description( | return Description( | ||||
| type: element.getAttribute('type'), | type: element.getAttribute('type'), | ||||
| value: element.text, | value: element.text, | ||||
| @@ -2,10 +2,10 @@ import 'package:webfeed/domain/media/param.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Embed { | class Embed { | ||||
| final String url; | |||||
| final int width; | |||||
| final int height; | |||||
| final List<Param> params; | |||||
| final String? url; | |||||
| final int? width; | |||||
| final int? height; | |||||
| final List<Param>? params; | |||||
| Embed({ | Embed({ | ||||
| this.url, | this.url, | ||||
| @@ -15,16 +15,14 @@ class Embed { | |||||
| }); | }); | ||||
| factory Embed.parse(XmlElement element) { | factory Embed.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Embed( | return Embed( | ||||
| url: element.getAttribute('url'), | url: element.getAttribute('url'), | ||||
| width: int.tryParse(element.getAttribute('width') ?? '0'), | width: int.tryParse(element.getAttribute('width') ?? '0'), | ||||
| height: int.tryParse(element.getAttribute('height') ?? '0'), | height: int.tryParse(element.getAttribute('height') ?? '0'), | ||||
| params: element.findElements('media:param').map((e) { | |||||
| return Param.parse(e); | |||||
| }).toList(), | |||||
| params: element | |||||
| .findElements('media:param') | |||||
| .map((e) => Param.parse(e)) | |||||
| .toList(), | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -2,14 +2,14 @@ import 'package:webfeed/domain/media/category.dart'; | |||||
| import 'package:webfeed/domain/media/content.dart'; | import 'package:webfeed/domain/media/content.dart'; | ||||
| import 'package:webfeed/domain/media/credit.dart'; | import 'package:webfeed/domain/media/credit.dart'; | ||||
| import 'package:webfeed/domain/media/rating.dart'; | import 'package:webfeed/domain/media/rating.dart'; | ||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Group { | class Group { | ||||
| final List<Content> contents; | |||||
| final List<Credit> credits; | |||||
| final Category category; | |||||
| final Rating rating; | |||||
| final List<Content>? contents; | |||||
| final List<Credit>? credits; | |||||
| final Category? category; | |||||
| final Rating? rating; | |||||
| Group({ | Group({ | ||||
| this.contents, | this.contents, | ||||
| @@ -19,22 +19,23 @@ class Group { | |||||
| }); | }); | ||||
| factory Group.parse(XmlElement element) { | factory Group.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Group( | return Group( | ||||
| contents: element.findElements('media:content').map((e) { | |||||
| return Content.parse(e); | |||||
| }).toList(), | |||||
| credits: element.findElements('media:credit').map((e) { | |||||
| return Credit.parse(e); | |||||
| }).toList(), | |||||
| category: Category.parse( | |||||
| findFirstElement(element, 'media:category'), | |||||
| ), | |||||
| rating: Rating.parse( | |||||
| findFirstElement(element, 'media:rating'), | |||||
| ), | |||||
| contents: element | |||||
| .findElements('media:content') | |||||
| .map((e) => Content.parse(e)) | |||||
| .toList(), | |||||
| credits: element | |||||
| .findElements('media:credit') | |||||
| .map((e) => Credit.parse(e)) | |||||
| .toList(), | |||||
| category: element | |||||
| .findElements('media:category') | |||||
| .map((e) => Category.parse(e)) | |||||
| .firstOrNull, | |||||
| rating: element | |||||
| .findElements('media:rating') | |||||
| .map((e) => Rating.parse(e)) | |||||
| .firstOrNull, | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,8 +1,8 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Hash { | class Hash { | ||||
| final String algo; | |||||
| final String value; | |||||
| final String? algo; | |||||
| final String? value; | |||||
| Hash({ | Hash({ | ||||
| this.algo, | this.algo, | ||||
| @@ -10,9 +10,6 @@ class Hash { | |||||
| }); | }); | ||||
| factory Hash.parse(XmlElement element) { | factory Hash.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Hash( | return Hash( | ||||
| algo: element.getAttribute('algo'), | algo: element.getAttribute('algo'), | ||||
| value: element.text, | value: element.text, | ||||
| @@ -1,9 +1,9 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class License { | class License { | ||||
| final String type; | |||||
| final String href; | |||||
| final String value; | |||||
| final String? type; | |||||
| final String? href; | |||||
| final String? value; | |||||
| License({ | License({ | ||||
| this.type, | this.type, | ||||
| @@ -12,9 +12,6 @@ class License { | |||||
| }); | }); | ||||
| factory License.parse(XmlElement element) { | factory License.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return License( | return License( | ||||
| type: element.getAttribute('type'), | type: element.getAttribute('type'), | ||||
| href: element.getAttribute('href'), | href: element.getAttribute('href'), | ||||
| @@ -20,34 +20,35 @@ import 'package:webfeed/domain/media/text.dart'; | |||||
| import 'package:webfeed/domain/media/thumbnail.dart'; | import 'package:webfeed/domain/media/thumbnail.dart'; | ||||
| import 'package:webfeed/domain/media/title.dart'; | import 'package:webfeed/domain/media/title.dart'; | ||||
| import 'package:webfeed/util/xml.dart'; | import 'package:webfeed/util/xml.dart'; | ||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Media { | class Media { | ||||
| final Group group; | |||||
| final List<Content> contents; | |||||
| final List<Credit> credits; | |||||
| final Category category; | |||||
| final Rating rating; | |||||
| final Title title; | |||||
| final Description description; | |||||
| final String keywords; | |||||
| final List<Thumbnail> thumbnails; | |||||
| final Hash hash; | |||||
| final Player player; | |||||
| final Copyright copyright; | |||||
| final Text text; | |||||
| final Restriction restriction; | |||||
| final Community community; | |||||
| final List<String> comments; | |||||
| final Embed embed; | |||||
| final List<String> responses; | |||||
| final List<String> backLinks; | |||||
| final Status status; | |||||
| final List<Price> prices; | |||||
| final License license; | |||||
| final PeerLink peerLink; | |||||
| final Rights rights; | |||||
| final List<Scene> scenes; | |||||
| final Group? group; | |||||
| final List<Content>? contents; | |||||
| final List<Credit>? credits; | |||||
| final Category? category; | |||||
| final Rating? rating; | |||||
| final Title? title; | |||||
| final Description? description; | |||||
| final String? keywords; | |||||
| final List<Thumbnail>? thumbnails; | |||||
| final Hash? hash; | |||||
| final Player? player; | |||||
| final Copyright? copyright; | |||||
| final Text? text; | |||||
| final Restriction? restriction; | |||||
| final Community? community; | |||||
| final List<String>? comments; | |||||
| final Embed? embed; | |||||
| final List<String>? responses; | |||||
| final List<String>? backLinks; | |||||
| final Status? status; | |||||
| final List<Price>? prices; | |||||
| final License? license; | |||||
| final PeerLink? peerLink; | |||||
| final Rights? rights; | |||||
| final List<Scene>? scenes; | |||||
| Media({ | Media({ | ||||
| this.group, | this.group, | ||||
| @@ -79,90 +80,113 @@ class Media { | |||||
| factory Media.parse(XmlElement element) { | factory Media.parse(XmlElement element) { | ||||
| return Media( | return Media( | ||||
| group: Group.parse( | |||||
| findFirstElement(element, 'media:group'), | |||||
| ), | |||||
| contents: element.findElements('media:content').map((e) { | |||||
| return Content.parse(e); | |||||
| }).toList(), | |||||
| credits: element.findElements('media:credit').map((e) { | |||||
| return Credit.parse(e); | |||||
| }).toList(), | |||||
| category: Category.parse( | |||||
| findFirstElement(element, 'media:category'), | |||||
| ), | |||||
| rating: Rating.parse( | |||||
| findFirstElement(element, 'media:rating'), | |||||
| ), | |||||
| title: Title.parse( | |||||
| findFirstElement(element, 'media:title'), | |||||
| ), | |||||
| description: Description.parse( | |||||
| findFirstElement(element, 'media:description'), | |||||
| ), | |||||
| keywords: findFirstElement(element, 'media:keywords')?.text, | |||||
| thumbnails: element.findElements('media:thumbnail').map((e) { | |||||
| return Thumbnail.parse(e); | |||||
| }).toList(), | |||||
| hash: Hash.parse( | |||||
| findFirstElement(element, 'media:hash'), | |||||
| ), | |||||
| player: Player.parse( | |||||
| findFirstElement(element, 'media:player'), | |||||
| ), | |||||
| copyright: Copyright.parse( | |||||
| findFirstElement(element, 'media:copyright'), | |||||
| ), | |||||
| text: Text.parse( | |||||
| findFirstElement(element, 'media:text'), | |||||
| ), | |||||
| restriction: Restriction.parse( | |||||
| findFirstElement(element, 'media:restriction'), | |||||
| ), | |||||
| community: Community.parse( | |||||
| findFirstElement(element, 'media:community'), | |||||
| ), | |||||
| comments: findFirstElement(element, 'media:comments') | |||||
| group: element | |||||
| .findElements('media:group') | |||||
| .map((e) => Group.parse(e)) | |||||
| .firstOrNull, | |||||
| contents: element | |||||
| .findElements('media:content') | |||||
| .map((e) => Content.parse(e)) | |||||
| .toList(), | |||||
| credits: element | |||||
| .findElements('media:credit') | |||||
| .map((e) => Credit.parse(e)) | |||||
| .toList(), | |||||
| category: element | |||||
| .findElements('media:category') | |||||
| .map((e) => Category.parse(e)) | |||||
| .firstOrNull, | |||||
| rating: element | |||||
| .findElements('media:rating') | |||||
| .map((e) => Rating.parse(e)) | |||||
| .firstOrNull, | |||||
| title: findElements(element, 'media:title') | |||||
| ?.map((e) => Title.parse(e)) | |||||
| .firstOrNull, | |||||
| description: element | |||||
| .findElements('media:description') | |||||
| .map((e) => Description.parse(e)) | |||||
| .firstOrNull, | |||||
| keywords: element.findElements('media:keywords').firstOrNull?.text, | |||||
| thumbnails: element | |||||
| .findElements('media:thumbnail') | |||||
| .map((e) => Thumbnail.parse(e)) | |||||
| .toList(), | |||||
| hash: element | |||||
| .findElements('media:hash') | |||||
| .map((e) => Hash.parse(e)) | |||||
| .firstOrNull, | |||||
| player: element | |||||
| .findElements('media:player') | |||||
| .map((e) => Player.parse(e)) | |||||
| .firstOrNull, | |||||
| copyright: element | |||||
| .findElements('media:copyright') | |||||
| .map((e) => Copyright.parse(e)) | |||||
| .firstOrNull, | |||||
| text: element | |||||
| .findElements('media:text') | |||||
| .map((e) => Text.parse(e)) | |||||
| .firstOrNull, | |||||
| restriction: element | |||||
| .findElements('media:restriction') | |||||
| .map((e) => Restriction.parse(e)) | |||||
| .firstOrNull, | |||||
| community: element | |||||
| .findElements('media:community') | |||||
| .map((e) => Community.parse(e)) | |||||
| .firstOrNull, | |||||
| comments: element | |||||
| .findElements('media:comments') | |||||
| .firstOrNull | |||||
| ?.findElements('media:comment') | ?.findElements('media:comment') | ||||
| ?.map((e) { | |||||
| return e.text; | |||||
| })?.toList() ?? | |||||
| .map((e) => e.text) | |||||
| .toList() ?? | |||||
| [], | [], | ||||
| embed: Embed.parse( | |||||
| findFirstElement(element, 'media:embed'), | |||||
| ), | |||||
| responses: findFirstElement(element, 'media:responses') | |||||
| embed: element | |||||
| .findElements('media:embed') | |||||
| .map((e) => Embed.parse(e)) | |||||
| .firstOrNull, | |||||
| responses: element | |||||
| .findElements('media:responses') | |||||
| .firstOrNull | |||||
| ?.findElements('media:response') | ?.findElements('media:response') | ||||
| ?.map((e) { | |||||
| return e.text; | |||||
| })?.toList() ?? | |||||
| .map((e) => e.text) | |||||
| .toList() ?? | |||||
| [], | [], | ||||
| backLinks: findFirstElement(element, 'media:backLinks') | |||||
| backLinks: element | |||||
| .findElements('media:backLinks') | |||||
| .firstOrNull | |||||
| ?.findElements('media:backLink') | ?.findElements('media:backLink') | ||||
| ?.map((e) { | |||||
| return e.text; | |||||
| })?.toList() ?? | |||||
| .map((e) => e.text) | |||||
| .toList() ?? | |||||
| [], | [], | ||||
| status: Status.parse( | |||||
| findFirstElement(element, 'media:status'), | |||||
| ), | |||||
| prices: element.findElements('media:price').map((e) { | |||||
| return Price.parse(e); | |||||
| }).toList(), | |||||
| license: License.parse( | |||||
| findFirstElement(element, 'media:license'), | |||||
| ), | |||||
| peerLink: PeerLink.parse( | |||||
| findFirstElement(element, 'media:peerLink'), | |||||
| ), | |||||
| rights: Rights.parse( | |||||
| findFirstElement(element, 'media:rights'), | |||||
| ), | |||||
| scenes: findFirstElement(element, 'media:scenes') | |||||
| status: element | |||||
| .findElements('media:status') | |||||
| .map((e) => Status.parse(e)) | |||||
| .firstOrNull, | |||||
| prices: element | |||||
| .findElements('media:price') | |||||
| .map((e) => Price.parse(e)) | |||||
| .toList(), | |||||
| license: element | |||||
| .findElements('media:license') | |||||
| .map((e) => License.parse(e)) | |||||
| .firstOrNull, | |||||
| peerLink: element | |||||
| .findElements('media:peerLink') | |||||
| .map((e) => PeerLink.parse(e)) | |||||
| .firstOrNull, | |||||
| rights: element | |||||
| .findElements('media:rights') | |||||
| .map((e) => Rights.parse(e)) | |||||
| .firstOrNull, | |||||
| scenes: element | |||||
| .findElements('media:scenes') | |||||
| .firstOrNull | |||||
| ?.findElements('media:scene') | ?.findElements('media:scene') | ||||
| ?.map((e) { | |||||
| return Scene.parse(e); | |||||
| })?.toList() ?? | |||||
| .map((e) => Scene.parse(e)) | |||||
| .toList() ?? | |||||
| [], | [], | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -1,8 +1,8 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Param { | class Param { | ||||
| final String name; | |||||
| final String value; | |||||
| final String? name; | |||||
| final String? value; | |||||
| Param({ | Param({ | ||||
| this.name, | this.name, | ||||
| @@ -10,9 +10,6 @@ class Param { | |||||
| }); | }); | ||||
| factory Param.parse(XmlElement element) { | factory Param.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Param( | return Param( | ||||
| name: element.getAttribute('name'), | name: element.getAttribute('name'), | ||||
| value: element.text, | value: element.text, | ||||
| @@ -1,9 +1,9 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class PeerLink { | class PeerLink { | ||||
| final String type; | |||||
| final String href; | |||||
| final String value; | |||||
| final String? type; | |||||
| final String? href; | |||||
| final String? value; | |||||
| PeerLink({ | PeerLink({ | ||||
| this.type, | this.type, | ||||
| @@ -12,9 +12,6 @@ class PeerLink { | |||||
| }); | }); | ||||
| factory PeerLink.parse(XmlElement element) { | factory PeerLink.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return PeerLink( | return PeerLink( | ||||
| type: element.getAttribute('type'), | type: element.getAttribute('type'), | ||||
| href: element.getAttribute('href'), | href: element.getAttribute('href'), | ||||
| @@ -1,10 +1,10 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Player { | class Player { | ||||
| final String url; | |||||
| final int width; | |||||
| final int height; | |||||
| final String value; | |||||
| final String? url; | |||||
| final int? width; | |||||
| final int? height; | |||||
| final String? value; | |||||
| Player({ | Player({ | ||||
| this.url, | this.url, | ||||
| @@ -14,9 +14,6 @@ class Player { | |||||
| }); | }); | ||||
| factory Player.parse(XmlElement element) { | factory Player.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Player( | return Player( | ||||
| url: element.getAttribute('url'), | url: element.getAttribute('url'), | ||||
| width: int.tryParse(element.getAttribute('width') ?? '0'), | width: int.tryParse(element.getAttribute('width') ?? '0'), | ||||
| @@ -1,10 +1,10 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Price { | class Price { | ||||
| final double price; | |||||
| final String type; | |||||
| final String info; | |||||
| final String currency; | |||||
| final double? price; | |||||
| final String? type; | |||||
| final String? info; | |||||
| final String? currency; | |||||
| Price({ | Price({ | ||||
| this.price, | this.price, | ||||
| @@ -1,8 +1,8 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Rating { | class Rating { | ||||
| final String scheme; | |||||
| final String value; | |||||
| final String? scheme; | |||||
| final String? value; | |||||
| Rating({ | Rating({ | ||||
| this.scheme, | this.scheme, | ||||
| @@ -10,9 +10,6 @@ class Rating { | |||||
| }); | }); | ||||
| factory Rating.parse(XmlElement element) { | factory Rating.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Rating( | return Rating( | ||||
| scheme: element.getAttribute('scheme'), | scheme: element.getAttribute('scheme'), | ||||
| value: element.text, | value: element.text, | ||||
| @@ -1,9 +1,9 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Restriction { | class Restriction { | ||||
| final String relationship; | |||||
| final String type; | |||||
| final String value; | |||||
| final String? relationship; | |||||
| final String? type; | |||||
| final String? value; | |||||
| Restriction({ | Restriction({ | ||||
| this.relationship, | this.relationship, | ||||
| @@ -12,9 +12,6 @@ class Restriction { | |||||
| }); | }); | ||||
| factory Restriction.parse(XmlElement element) { | factory Restriction.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Restriction( | return Restriction( | ||||
| relationship: element.getAttribute('relationship'), | relationship: element.getAttribute('relationship'), | ||||
| type: element.getAttribute('type'), | type: element.getAttribute('type'), | ||||
| @@ -1,16 +1,13 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Rights { | class Rights { | ||||
| final String status; | |||||
| final String? status; | |||||
| Rights({ | Rights({ | ||||
| this.status, | this.status, | ||||
| }); | }); | ||||
| factory Rights.parse(XmlElement element) { | factory Rights.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Rights( | return Rights( | ||||
| status: element.getAttribute('status'), | status: element.getAttribute('status'), | ||||
| ); | ); | ||||
| @@ -1,11 +1,11 @@ | |||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Scene { | class Scene { | ||||
| final String title; | |||||
| final String description; | |||||
| final String startTime; | |||||
| final String endTime; | |||||
| final String? title; | |||||
| final String? description; | |||||
| final String? startTime; | |||||
| final String? endTime; | |||||
| Scene({ | Scene({ | ||||
| this.title, | this.title, | ||||
| @@ -15,14 +15,11 @@ class Scene { | |||||
| }); | }); | ||||
| factory Scene.parse(XmlElement element) { | factory Scene.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Scene( | return Scene( | ||||
| title: findFirstElement(element, 'sceneTitle')?.text, | |||||
| description: findFirstElement(element, 'sceneDescription')?.text, | |||||
| startTime: findFirstElement(element, 'sceneStartTime')?.text, | |||||
| endTime: findFirstElement(element, 'sceneEndTime')?.text, | |||||
| title: element.findElements('sceneTitle').firstOrNull?.text, | |||||
| description: element.findElements('sceneDescription').firstOrNull?.text, | |||||
| startTime: element.findElements('sceneStartTime').firstOrNull?.text, | |||||
| endTime: element.findElements('sceneEndTime').firstOrNull?.text, | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,10 +1,10 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class StarRating { | class StarRating { | ||||
| final double average; | |||||
| final int count; | |||||
| final int min; | |||||
| final int max; | |||||
| final double? average; | |||||
| final int? count; | |||||
| final int? min; | |||||
| final int? max; | |||||
| StarRating({ | StarRating({ | ||||
| this.average, | this.average, | ||||
| @@ -13,12 +13,12 @@ class StarRating { | |||||
| this.max, | this.max, | ||||
| }); | }); | ||||
| factory StarRating.parse(XmlElement element) { | |||||
| factory StarRating.parse(XmlElement? element) { | |||||
| return StarRating( | 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'), | |||||
| 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'), | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,18 +1,18 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Statistics { | class Statistics { | ||||
| final int views; | |||||
| final int favorites; | |||||
| final int? views; | |||||
| final int? favorites; | |||||
| Statistics({ | Statistics({ | ||||
| this.views, | this.views, | ||||
| this.favorites, | this.favorites, | ||||
| }); | }); | ||||
| factory Statistics.parse(XmlElement element) { | |||||
| factory Statistics.parse(XmlElement? element) { | |||||
| return Statistics( | return Statistics( | ||||
| views: int.tryParse(element.getAttribute('views') ?? '0'), | |||||
| favorites: int.tryParse(element.getAttribute('favorites') ?? '0'), | |||||
| views: int.tryParse(element?.getAttribute('views') ?? '0'), | |||||
| favorites: int.tryParse(element?.getAttribute('favorites') ?? '0'), | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,8 +1,8 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Status { | class Status { | ||||
| final String state; | |||||
| final String reason; | |||||
| final String? state; | |||||
| final String? reason; | |||||
| Status({ | Status({ | ||||
| this.state, | this.state, | ||||
| @@ -10,9 +10,6 @@ class Status { | |||||
| }); | }); | ||||
| factory Status.parse(XmlElement element) { | factory Status.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Status( | return Status( | ||||
| state: element.getAttribute('state'), | state: element.getAttribute('state'), | ||||
| reason: element.getAttribute('reason'), | reason: element.getAttribute('reason'), | ||||
| @@ -1,8 +1,8 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Tags { | class Tags { | ||||
| final String tags; | |||||
| final int weight; | |||||
| final String? tags; | |||||
| final int? weight; | |||||
| Tags({ | Tags({ | ||||
| this.tags, | this.tags, | ||||
| @@ -10,9 +10,6 @@ class Tags { | |||||
| }); | }); | ||||
| factory Tags.parse(XmlElement element) { | factory Tags.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Tags( | return Tags( | ||||
| tags: element.text, | tags: element.text, | ||||
| weight: int.tryParse(element.getAttribute('weight') ?? '1'), | weight: int.tryParse(element.getAttribute('weight') ?? '1'), | ||||
| @@ -1,11 +1,11 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Text { | class Text { | ||||
| final String type; | |||||
| final String lang; | |||||
| final String start; | |||||
| final String end; | |||||
| final String value; | |||||
| final String? type; | |||||
| final String? lang; | |||||
| final String? start; | |||||
| final String? end; | |||||
| final String? value; | |||||
| Text({ | Text({ | ||||
| this.type, | this.type, | ||||
| @@ -16,9 +16,6 @@ class Text { | |||||
| }); | }); | ||||
| factory Text.parse(XmlElement element) { | factory Text.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Text( | return Text( | ||||
| type: element.getAttribute('type'), | type: element.getAttribute('type'), | ||||
| lang: element.getAttribute('lang'), | lang: element.getAttribute('lang'), | ||||
| @@ -1,10 +1,10 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Thumbnail { | class Thumbnail { | ||||
| final String url; | |||||
| final String width; | |||||
| final String height; | |||||
| final String time; | |||||
| final String? url; | |||||
| final String? width; | |||||
| final String? height; | |||||
| final String? time; | |||||
| Thumbnail({ | Thumbnail({ | ||||
| this.url, | this.url, | ||||
| @@ -1,8 +1,8 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class Title { | class Title { | ||||
| final String type; | |||||
| final String value; | |||||
| final String? type; | |||||
| final String? value; | |||||
| Title({ | Title({ | ||||
| this.type, | this.type, | ||||
| @@ -10,9 +10,6 @@ class Title { | |||||
| }); | }); | ||||
| factory Title.parse(XmlElement element) { | factory Title.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return Title( | return Title( | ||||
| type: element.getAttribute('type'), | type: element.getAttribute('type'), | ||||
| value: element.text, | value: element.text, | ||||
| @@ -1,15 +1,12 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class RssCategory { | class RssCategory { | ||||
| final String domain; | |||||
| final String? domain; | |||||
| final String value; | final String value; | ||||
| RssCategory(this.domain, this.value); | RssCategory(this.domain, this.value); | ||||
| factory RssCategory.parse(XmlElement element) { | factory RssCategory.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| var domain = element.getAttribute('domain'); | var domain = element.getAttribute('domain'); | ||||
| var value = element.text; | var value = element.text; | ||||
| @@ -1,11 +1,11 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class RssCloud { | class RssCloud { | ||||
| final String domain; | |||||
| final String port; | |||||
| final String path; | |||||
| final String registerProcedure; | |||||
| final String protocol; | |||||
| final String? domain; | |||||
| final String? port; | |||||
| final String? path; | |||||
| final String? registerProcedure; | |||||
| final String? protocol; | |||||
| RssCloud( | RssCloud( | ||||
| this.domain, | this.domain, | ||||
| @@ -16,9 +16,6 @@ class RssCloud { | |||||
| ); | ); | ||||
| factory RssCloud.parse(XmlElement node) { | factory RssCloud.parse(XmlElement node) { | ||||
| if (node == null) { | |||||
| return null; | |||||
| } | |||||
| var domain = node.getAttribute('domain'); | var domain = node.getAttribute('domain'); | ||||
| var port = node.getAttribute('port'); | var port = node.getAttribute('port'); | ||||
| var path = node.getAttribute('path'); | var path = node.getAttribute('path'); | ||||
| @@ -17,13 +17,10 @@ class RssContent { | |||||
| RssContent(this.value, this.images); | RssContent(this.value, this.images); | ||||
| factory RssContent.parse(XmlElement element) { | factory RssContent.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| final content = element.text; | |||||
| final dynamic? content = element.text; | |||||
| final images = <String>[]; | final images = <String>[]; | ||||
| _imagesRegExp.allMatches(content).forEach((match) { | _imagesRegExp.allMatches(content).forEach((match) { | ||||
| images.add(match.group(1)); | |||||
| images.add(match.group(1)!); | |||||
| }); | }); | ||||
| return RssContent(content, images); | return RssContent(content, images); | ||||
| } | } | ||||
| @@ -1,16 +1,13 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class RssEnclosure { | class RssEnclosure { | ||||
| final String url; | |||||
| final String type; | |||||
| final int length; | |||||
| final String? url; | |||||
| final String? type; | |||||
| final int? length; | |||||
| RssEnclosure(this.url, this.type, this.length); | RssEnclosure(this.url, this.type, this.length); | ||||
| factory RssEnclosure.parse(XmlElement element) { | factory RssEnclosure.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| var url = element.getAttribute('url'); | var url = element.getAttribute('url'); | ||||
| var type = element.getAttribute('type'); | var type = element.getAttribute('type'); | ||||
| var length = int.tryParse(element.getAttribute('length') ?? '0'); | var length = int.tryParse(element.getAttribute('length') ?? '0'); | ||||
| @@ -7,33 +7,33 @@ import 'package:webfeed/domain/rss_cloud.dart'; | |||||
| import 'package:webfeed/domain/rss_image.dart'; | import 'package:webfeed/domain/rss_image.dart'; | ||||
| import 'package:webfeed/domain/rss_item.dart'; | import 'package:webfeed/domain/rss_item.dart'; | ||||
| import 'package:webfeed/domain/syndication/syndication.dart'; | import 'package:webfeed/domain/syndication/syndication.dart'; | ||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class RssFeed { | class RssFeed { | ||||
| final String title; | |||||
| final String author; | |||||
| final String description; | |||||
| final String link; | |||||
| final List<RssItem> items; | |||||
| final String? title; | |||||
| final String? author; | |||||
| final String? description; | |||||
| final String? link; | |||||
| final List<RssItem>? items; | |||||
| final RssImage image; | |||||
| final RssCloud cloud; | |||||
| final List<RssCategory> categories; | |||||
| final List<String> skipDays; | |||||
| final List<int> skipHours; | |||||
| final String lastBuildDate; | |||||
| final String language; | |||||
| final String generator; | |||||
| final String copyright; | |||||
| final String docs; | |||||
| final String managingEditor; | |||||
| final String rating; | |||||
| final String webMaster; | |||||
| final int ttl; | |||||
| final DublinCore dc; | |||||
| final Itunes itunes; | |||||
| final Syndication syndication; | |||||
| final RssImage? image; | |||||
| final RssCloud? cloud; | |||||
| final List<RssCategory>? categories; | |||||
| final List<String>? skipDays; | |||||
| final List<int>? skipHours; | |||||
| final String? lastBuildDate; | |||||
| final String? language; | |||||
| final String? generator; | |||||
| final String? copyright; | |||||
| final String? docs; | |||||
| final String? managingEditor; | |||||
| final String? rating; | |||||
| final String? webMaster; | |||||
| final int? ttl; | |||||
| final DublinCore? dc; | |||||
| final Itunes? itunes; | |||||
| final Syndication? syndication; | |||||
| RssFeed({ | RssFeed({ | ||||
| this.title, | this.title, | ||||
| @@ -62,50 +62,62 @@ class RssFeed { | |||||
| factory RssFeed.parse(String xmlString) { | factory RssFeed.parse(String xmlString) { | ||||
| var document = XmlDocument.parse(xmlString); | var document = XmlDocument.parse(xmlString); | ||||
| var rss = findFirstElement(document, 'rss'); | |||||
| var rdf = findFirstElement(document, 'rdf:RDF'); | |||||
| var rss = document.findElements('rss').firstOrNull; | |||||
| var rdf = document.findElements('rdf:RDF').firstOrNull; | |||||
| if (rss == null && rdf == null) { | if (rss == null && rdf == null) { | ||||
| throw ArgumentError('not a rss feed'); | throw ArgumentError('not a rss feed'); | ||||
| } | } | ||||
| var channelElement = findFirstElement(rss ?? rdf, 'channel'); | |||||
| var channelElement = (rss ?? rdf)!.findElements('channel').firstOrNull; | |||||
| if (channelElement == null) { | if (channelElement == null) { | ||||
| throw ArgumentError('channel not found'); | throw ArgumentError('channel not found'); | ||||
| } | } | ||||
| return RssFeed( | return RssFeed( | ||||
| 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) | |||||
| title: channelElement.findElements('title').firstOrNull?.text, | |||||
| author: channelElement.findElements('author').firstOrNull?.text, | |||||
| description: channelElement.findElements('description').firstOrNull?.text, | |||||
| link: channelElement.findElements('link').firstOrNull?.text, | |||||
| items: (rdf ?? channelElement) | |||||
| .findElements('item') | .findElements('item') | ||||
| .map((e) => RssItem.parse(e)) | .map((e) => RssItem.parse(e)) | ||||
| .toList(), | .toList(), | ||||
| image: RssImage.parse( | |||||
| findFirstElement(rss != null ? channelElement : rdf, 'image')), | |||||
| cloud: RssCloud.parse(findFirstElement(channelElement, 'cloud')), | |||||
| image: (rdf ?? channelElement) | |||||
| .findElements('image') | |||||
| .map((e) => RssImage.parse(e)) | |||||
| .firstOrNull, | |||||
| cloud: channelElement | |||||
| .findElements('cloud') | |||||
| .map((e) => RssCloud.parse(e)) | |||||
| .firstOrNull, | |||||
| categories: channelElement | categories: channelElement | ||||
| .findElements('category') | .findElements('category') | ||||
| .map((e) => RssCategory.parse(e)) | .map((e) => RssCategory.parse(e)) | ||||
| .toList(), | .toList(), | ||||
| skipDays: findFirstElement(channelElement, 'skipDays') | |||||
| skipDays: channelElement | |||||
| .findElements('skipDays') | |||||
| .firstOrNull | |||||
| ?.findAllElements('day') | ?.findAllElements('day') | ||||
| ?.map((e) => e.text) | |||||
| ?.toList() ?? | |||||
| .map((e) => e.text) | |||||
| .toList() ?? | |||||
| [], | [], | ||||
| skipHours: findFirstElement(channelElement, 'skipHours') | |||||
| skipHours: channelElement | |||||
| .findElements('skipHours') | |||||
| .firstOrNull | |||||
| ?.findAllElements('hour') | ?.findAllElements('hour') | ||||
| ?.map((e) => int.tryParse(e.text ?? '0')) | |||||
| ?.toList() ?? | |||||
| .map((e) => int.tryParse(e.text) ?? 0) | |||||
| .toList() ?? | |||||
| [], | [], | ||||
| 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'), | |||||
| lastBuildDate: | |||||
| channelElement.findElements('lastBuildDate').firstOrNull?.text, | |||||
| language: channelElement.findElements('language').firstOrNull?.text, | |||||
| generator: channelElement.findElements('generator').firstOrNull?.text, | |||||
| copyright: channelElement.findElements('copyright').firstOrNull?.text, | |||||
| docs: channelElement.findElements('docs').firstOrNull?.text, | |||||
| managingEditor: | |||||
| channelElement.findElements('managingEditor').firstOrNull?.text, | |||||
| rating: channelElement.findElements('rating').firstOrNull?.text, | |||||
| webMaster: channelElement.findElements('webMaster').firstOrNull?.text, | |||||
| ttl: int.tryParse( | |||||
| channelElement.findElements('ttl').firstOrNull?.text ?? '0'), | |||||
| dc: DublinCore.parse(channelElement), | dc: DublinCore.parse(channelElement), | ||||
| itunes: Itunes.parse(channelElement), | itunes: Itunes.parse(channelElement), | ||||
| syndication: Syndication.parse(channelElement), | syndication: Syndication.parse(channelElement), | ||||
| @@ -1,21 +1,18 @@ | |||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class RssImage { | class RssImage { | ||||
| final String title; | |||||
| final String url; | |||||
| final String link; | |||||
| final String? title; | |||||
| 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) { | factory RssImage.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| return RssImage( | return RssImage( | ||||
| title: findFirstElement(element, 'title')?.text, | |||||
| url: findFirstElement(element, 'url')?.text, | |||||
| link: findFirstElement(element, 'link')?.text, | |||||
| title: element.findElements('title').firstOrNull?.text, | |||||
| url: element.findElements('url').firstOrNull?.text, | |||||
| link: element.findElements('link').firstOrNull?.text, | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -6,25 +6,25 @@ import 'package:webfeed/domain/rss_content.dart'; | |||||
| import 'package:webfeed/domain/rss_enclosure.dart'; | import 'package:webfeed/domain/rss_enclosure.dart'; | ||||
| import 'package:webfeed/domain/rss_source.dart'; | import 'package:webfeed/domain/rss_source.dart'; | ||||
| import 'package:webfeed/util/datetime.dart'; | import 'package:webfeed/util/datetime.dart'; | ||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class RssItem { | class RssItem { | ||||
| final String title; | |||||
| final String description; | |||||
| final String link; | |||||
| final String? title; | |||||
| final String? description; | |||||
| final String? link; | |||||
| final List<RssCategory> categories; | |||||
| final String guid; | |||||
| final DateTime pubDate; | |||||
| final String author; | |||||
| final String comments; | |||||
| final RssSource source; | |||||
| final RssContent content; | |||||
| final Media media; | |||||
| final RssEnclosure enclosure; | |||||
| final DublinCore dc; | |||||
| final Itunes itunes; | |||||
| final List<RssCategory>? categories; | |||||
| final String? guid; | |||||
| final DateTime? pubDate; | |||||
| final String? author; | |||||
| final String? comments; | |||||
| final RssSource? source; | |||||
| final RssContent? content; | |||||
| final Media? media; | |||||
| final RssEnclosure? enclosure; | |||||
| final DublinCore? dc; | |||||
| final Itunes? itunes; | |||||
| RssItem({ | RssItem({ | ||||
| this.title, | this.title, | ||||
| @@ -45,21 +45,30 @@ class RssItem { | |||||
| factory RssItem.parse(XmlElement element) { | factory RssItem.parse(XmlElement element) { | ||||
| return RssItem( | return RssItem( | ||||
| title: findFirstElement(element, 'title')?.text, | |||||
| description: findFirstElement(element, 'description')?.text, | |||||
| link: findFirstElement(element, 'link')?.text, | |||||
| title: element.findElements('title').firstOrNull?.text, | |||||
| description: element.findElements('description').firstOrNull?.text, | |||||
| link: element.findElements('link').firstOrNull?.text, | |||||
| categories: element | categories: element | ||||
| .findElements('category') | .findElements('category') | ||||
| .map((e) => RssCategory.parse(e)) | .map((e) => RssCategory.parse(e)) | ||||
| .toList(), | .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')), | |||||
| guid: element.findElements('guid').firstOrNull?.text, | |||||
| pubDate: parseDateTime(element.findElements('pubDate').firstOrNull?.text), | |||||
| author: element.findElements('author').firstOrNull?.text, | |||||
| comments: element.findElements('comments').firstOrNull?.text, | |||||
| source: element | |||||
| .findElements('source') | |||||
| .map((e) => RssSource.parse(e)) | |||||
| .firstOrNull, | |||||
| content: element | |||||
| .findElements('content:encoded') | |||||
| .map((e) => RssContent.parse(e)) | |||||
| .firstOrNull, | |||||
| media: Media.parse(element), | media: Media.parse(element), | ||||
| enclosure: RssEnclosure.parse(findFirstElement(element, 'enclosure')), | |||||
| enclosure: element | |||||
| .findElements('enclosure') | |||||
| .map((e) => RssEnclosure.parse(e)) | |||||
| .firstOrNull, | |||||
| dc: DublinCore.parse(element), | dc: DublinCore.parse(element), | ||||
| itunes: Itunes.parse(element), | itunes: Itunes.parse(element), | ||||
| ); | ); | ||||
| @@ -1,15 +1,12 @@ | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| class RssSource { | class RssSource { | ||||
| final String url; | |||||
| final String value; | |||||
| final String? url; | |||||
| final String? value; | |||||
| RssSource(this.url, this.value); | RssSource(this.url, this.value); | ||||
| factory RssSource.parse(XmlElement element) { | factory RssSource.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| var url = element.getAttribute('url'); | var url = element.getAttribute('url'); | ||||
| var value = element.text; | var value = element.text; | ||||
| @@ -1,13 +1,13 @@ | |||||
| import 'package:webfeed/util/datetime.dart'; | import 'package:webfeed/util/datetime.dart'; | ||||
| import 'package:webfeed/util/xml.dart'; | |||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| enum SyndicationUpdatePeriod { hourly, daily, weekly, monthly, yearly } | enum SyndicationUpdatePeriod { hourly, daily, weekly, monthly, yearly } | ||||
| class Syndication { | class Syndication { | ||||
| final SyndicationUpdatePeriod updatePeriod; | |||||
| final int updateFrequency; | |||||
| final DateTime updateBase; | |||||
| final SyndicationUpdatePeriod? updatePeriod; | |||||
| final int? updateFrequency; | |||||
| final DateTime? updateBase; | |||||
| Syndication({ | Syndication({ | ||||
| this.updatePeriod, | this.updatePeriod, | ||||
| @@ -16,11 +16,8 @@ class Syndication { | |||||
| }); | }); | ||||
| factory Syndication.parse(XmlElement element) { | factory Syndication.parse(XmlElement element) { | ||||
| if (element == null) { | |||||
| return null; | |||||
| } | |||||
| SyndicationUpdatePeriod updatePeriod; | SyndicationUpdatePeriod updatePeriod; | ||||
| switch (findFirstElement(element, 'sy:updatePeriod')?.text) { | |||||
| switch (element.findElements('sy:updatePeriod').firstOrNull?.text) { | |||||
| case 'hourly': | case 'hourly': | ||||
| updatePeriod = SyndicationUpdatePeriod.hourly; | updatePeriod = SyndicationUpdatePeriod.hourly; | ||||
| break; | break; | ||||
| @@ -43,9 +40,9 @@ class Syndication { | |||||
| return Syndication( | return Syndication( | ||||
| updatePeriod: updatePeriod, | updatePeriod: updatePeriod, | ||||
| updateFrequency: int.tryParse( | updateFrequency: int.tryParse( | ||||
| findFirstElement(element, 'sy:updateFrequency')?.text ?? '1'), | |||||
| updateBase: | |||||
| parseDateTime(findFirstElement(element, 'sy:updateBase')?.text), | |||||
| element.findElements('sy:updateFrequency').firstOrNull?.text ?? '1'), | |||||
| updateBase: parseDateTime( | |||||
| element.findElements('sy:updateBase').firstOrNull?.text), | |||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -2,15 +2,15 @@ import 'package:intl/intl.dart'; | |||||
| const rfc822DatePattern = 'EEE, dd MMM yyyy HH:mm:ss Z'; | const rfc822DatePattern = 'EEE, dd MMM yyyy HH:mm:ss Z'; | ||||
| DateTime parseDateTime(dateString) { | |||||
| DateTime? parseDateTime(dateString) { | |||||
| if (dateString == null) return null; | if (dateString == null) return null; | ||||
| return _parseRfc822DateTime(dateString) ?? _parseIso8601DateTime(dateString); | return _parseRfc822DateTime(dateString) ?? _parseIso8601DateTime(dateString); | ||||
| } | } | ||||
| DateTime _parseRfc822DateTime(String dateString) { | |||||
| DateTime? _parseRfc822DateTime(String dateString) { | |||||
| try { | try { | ||||
| final length = dateString?.length?.clamp(0, rfc822DatePattern.length); | |||||
| final trimmedPattern = rfc822DatePattern.substring(0, length); //Some feeds use a shortened RFC 822 date, e.g. 'Tue, 04 Aug 2020' | |||||
| final num? length = dateString.length.clamp(0, rfc822DatePattern.length); | |||||
| final trimmedPattern = rfc822DatePattern.substring(0, length as int?); //Some feeds use a shortened RFC 822 date, e.g. 'Tue, 04 Aug 2020' | |||||
| final format = DateFormat(trimmedPattern, 'en_US'); | final format = DateFormat(trimmedPattern, 'en_US'); | ||||
| return format.parse(dateString); | return format.parse(dateString); | ||||
| } on FormatException { | } on FormatException { | ||||
| @@ -18,7 +18,7 @@ DateTime _parseRfc822DateTime(String dateString) { | |||||
| } | } | ||||
| } | } | ||||
| DateTime _parseIso8601DateTime(dateString) { | |||||
| DateTime? _parseIso8601DateTime(dateString) { | |||||
| try { | try { | ||||
| return DateTime.parse(dateString); | return DateTime.parse(dateString); | ||||
| } on FormatException { | } on FormatException { | ||||
| @@ -0,0 +1,3 @@ | |||||
| extension WebFeedIterable<T> on Iterable<T> { | |||||
| T? get firstOrNull => isEmpty ? null : first; | |||||
| } | |||||
| @@ -1,32 +1,19 @@ | |||||
| import 'dart:core'; | import 'dart:core'; | ||||
| import 'package:webfeed/util/iterable.dart'; | |||||
| import 'package:xml/xml.dart'; | import 'package:xml/xml.dart'; | ||||
| XmlElement findFirstElement( | |||||
| XmlNode node, | |||||
| Iterable<XmlElement>? findElements( | |||||
| XmlNode? node, | |||||
| String name, { | String name, { | ||||
| bool recursive = false, | 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, | |||||
| String? namespace, | |||||
| }) { | }) { | ||||
| try { | try { | ||||
| if (recursive) { | if (recursive) { | ||||
| return node.findAllElements(name, namespace: namespace); | |||||
| return node?.findAllElements(name, namespace: namespace); | |||||
| } else { | } else { | ||||
| return node.findElements(name, namespace: namespace); | |||||
| return node?.findElements(name, namespace: namespace); | |||||
| } | } | ||||
| } on StateError { | } on StateError { | ||||
| return null; | return null; | ||||
| @@ -34,7 +21,7 @@ Iterable<XmlElement> findElements( | |||||
| } | } | ||||
| bool parseBoolLiteral(XmlElement element, String tagName) { | bool parseBoolLiteral(XmlElement element, String tagName) { | ||||
| var v = findFirstElement(element, tagName)?.text?.toLowerCase()?.trim(); | |||||
| var v = element.findElements(tagName).firstOrNull?.text.toLowerCase().trim(); | |||||
| if (v == null) return false; | if (v == null) return false; | ||||
| return ['yes', 'true'].contains(v); | return ['yes', 'true'].contains(v); | ||||
| } | } | ||||
| @@ -3,10 +3,10 @@ version: 0.6.0 | |||||
| description: webfeed is a dart package for parsing RSS and Atom feeds. Media, DublinCore, iTunes, Syndication namespaces are also supported. | 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 | homepage: https://github.com/witochandra/webfeed | ||||
| environment: | environment: | ||||
| sdk: ">=2.0.0 <3.0.0" | |||||
| sdk: '>=2.12.0 <3.0.0' | |||||
| dependencies: | dependencies: | ||||
| xml: "^4.2.0" | |||||
| intl: "^0.16.0" | |||||
| xml: "^5.0.2" | |||||
| intl: "^0.17.0" | |||||
| dev_dependencies: | dev_dependencies: | ||||
| test: ^1.3.0 | test: ^1.3.0 | ||||
| http: "^0.11.3+16" | |||||
| http: "^0.13.0" | |||||
| @@ -23,65 +23,65 @@ void main() { | |||||
| expect(feed.title, 'Foo bar news'); | expect(feed.title, 'Foo bar news'); | ||||
| expect(feed.updated, DateTime.utc(2018, 4, 6, 13, 2, 46)); | 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.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.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.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.generator.uri, 'http://foo.bar.news/generator'); | |||||
| expect(feed.generator.version, '1.0'); | |||||
| expect(feed.generator.value, 'Foo bar generator'); | |||||
| 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.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.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.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.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.icon, 'http://foo.bar.news/icon.png'); | ||||
| expect(feed.logo, 'http://foo.bar.news/logo.png'); | expect(feed.logo, 'http://foo.bar.news/logo.png'); | ||||
| expect(feed.subtitle, 'This is subtitle'); | expect(feed.subtitle, 'This is subtitle'); | ||||
| expect(feed.items.length, 2); | |||||
| var item = feed.items.first; | |||||
| expect(feed.items!.length, 2); | |||||
| var item = feed.items!.first; | |||||
| expect(item.id, 'foo-bar-entry-id-1'); | expect(item.id, 'foo-bar-entry-id-1'); | ||||
| expect(item.title, 'Foo bar item 1'); | expect(item.title, 'Foo bar item 1'); | ||||
| expect(item.updated, DateTime.utc(2018, 4, 6, 13, 2, 40)); | 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.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.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.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.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.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.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.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.published, '2018-04-06T13:02:49Z'); | ||||
| expect(item.summary, 'This is summary 1'); | expect(item.summary, 'This is summary 1'); | ||||
| @@ -96,16 +96,16 @@ void main() { | |||||
| expect(feed.title, 'Foo bar news'); | expect(feed.title, 'Foo bar news'); | ||||
| expect(feed.updated, DateTime.utc(2018, 4, 6, 13, 2, 46)); | expect(feed.updated, DateTime.utc(2018, 4, 6, 13, 2, 46)); | ||||
| expect(feed.items.length, 1); | |||||
| 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'); | |||||
| 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.contents.length, 2); | |||||
| var mediaContent = item.media.contents.first; | |||||
| expect(item.media!.contents!.length, 2); | |||||
| var mediaContent = item.media!.contents!.first; | |||||
| expect(mediaContent.url, 'http://www.foo.com/video.mov'); | expect(mediaContent.url, 'http://www.foo.com/video.mov'); | ||||
| expect(mediaContent.type, 'video/quicktime'); | expect(mediaContent.type, 'video/quicktime'); | ||||
| expect(mediaContent.fileSize, 2000); | expect(mediaContent.fileSize, 2000); | ||||
| @@ -117,111 +117,111 @@ void main() { | |||||
| expect(mediaContent.samplingrate, 44.1); | expect(mediaContent.samplingrate, 44.1); | ||||
| expect(mediaContent.channels, 2); | expect(mediaContent.channels, 2); | ||||
| expect(item.media.credits.length, 2); | |||||
| var mediaCredit = item.media.credits.first; | |||||
| expect(item.media!.credits!.length, 2); | |||||
| var mediaCredit = item.media!.credits!.first; | |||||
| expect(mediaCredit.role, 'owner1'); | expect(mediaCredit.role, 'owner1'); | ||||
| expect(mediaCredit.scheme, 'urn:yvs'); | expect(mediaCredit.scheme, 'urn:yvs'); | ||||
| expect(mediaCredit.value, 'copyright holder of the entity'); | expect(mediaCredit.value, 'copyright holder of the entity'); | ||||
| expect(item.media.category.scheme, | |||||
| expect(item.media!.category!.scheme, | |||||
| 'http://search.yahoo.com/mrss/category_ schema'); | '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!.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.value, "The Judy's -- The Moo Song"); | |||||
| 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, | |||||
| 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(item.media!.thumbnails!.length, 2); | |||||
| var mediaThumbnail = item.media!.thumbnails!.first; | |||||
| expect(mediaThumbnail.url, 'http://www.foo.com/keyframe1.jpg'); | expect(mediaThumbnail.url, 'http://www.foo.com/keyframe1.jpg'); | ||||
| expect(mediaThumbnail.width, '75'); | expect(mediaThumbnail.width, '75'); | ||||
| expect(mediaThumbnail.height, '50'); | expect(mediaThumbnail.height, '50'); | ||||
| expect(mediaThumbnail.time, '12:05:01.123'); | expect(mediaThumbnail.time, '12:05:01.123'); | ||||
| 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.width, 400); | |||||
| expect(item.media.player.height, 200); | |||||
| 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.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.community.starRating.average, 3.5); | |||||
| expect(item.media.community.starRating.count, 20); | |||||
| expect(item.media.community.starRating.min, 1); | |||||
| 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.weight, 1); | |||||
| expect(item.media.comments.length, 2); | |||||
| 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.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.responses.length, 2); | |||||
| 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.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.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.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!.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!.width, 400); | |||||
| expect(item.media!.player!.height, 200); | |||||
| 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!.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!.community!.starRating!.average, 3.5); | |||||
| expect(item.media!.community!.starRating!.count, 20); | |||||
| expect(item.media!.community!.starRating!.min, 1); | |||||
| 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!.weight, 1); | |||||
| expect(item.media!.comments!.length, 2); | |||||
| 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!.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!.responses!.length, 2); | |||||
| 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!.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!.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!.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'); | |||||
| }); | }); | ||||
| test('parse Atom-Empty.xml', () { | test('parse Atom-Empty.xml', () { | ||||
| @@ -232,25 +232,25 @@ void main() { | |||||
| expect(feed.id, null); | expect(feed.id, null); | ||||
| expect(feed.title, null); | expect(feed.title, null); | ||||
| expect(feed.updated, null); | expect(feed.updated, null); | ||||
| expect(feed.links.length, 0); | |||||
| expect(feed.authors.length, 0); | |||||
| expect(feed.contributors.length, 0); | |||||
| expect(feed.categories.length, 0); | |||||
| expect(feed.links!.length, 0); | |||||
| expect(feed.authors!.length, 0); | |||||
| expect(feed.contributors!.length, 0); | |||||
| expect(feed.categories!.length, 0); | |||||
| expect(feed.generator, null); | expect(feed.generator, null); | ||||
| expect(feed.icon, null); | expect(feed.icon, null); | ||||
| expect(feed.logo, null); | expect(feed.logo, null); | ||||
| expect(feed.subtitle, null); | expect(feed.subtitle, null); | ||||
| expect(feed.items.length, 1); | |||||
| var item = feed.items.first; | |||||
| expect(feed.items!.length, 1); | |||||
| var item = feed.items!.first; | |||||
| expect(item.authors.length, 0); | |||||
| expect(item.authors!.length, 0); | |||||
| expect(item.links.length, 0); | |||||
| expect(item.links!.length, 0); | |||||
| expect(item.categories.length, 0); | |||||
| expect(item.categories!.length, 0); | |||||
| expect(item.contributors.length, 0); | |||||
| expect(item.contributors!.length, 0); | |||||
| expect(item.published, null); | expect(item.published, null); | ||||
| expect(item.summary, null); | expect(item.summary, null); | ||||
| @@ -36,59 +36,59 @@ void main() { | |||||
| expect(feed.webMaster, 'webmaster@foo.bar.news'); | expect(feed.webMaster, 'webmaster@foo.bar.news'); | ||||
| expect(feed.ttl, 60); | 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.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.skipDays.length, 3); | |||||
| 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); | |||||
| expect(feed.skipHours.contains(1), true); | |||||
| expect(feed.skipHours.contains(2), true); | |||||
| expect(feed.skipHours.contains(3), true); | |||||
| expect(feed.skipHours.contains(4), true); | |||||
| expect(feed.items.length, 2); | |||||
| expect(feed.items.first.title, | |||||
| 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.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.skipDays!.length, 3); | |||||
| 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); | |||||
| expect(feed.skipHours!.contains(1), true); | |||||
| expect(feed.skipHours!.contains(2), true); | |||||
| expect(feed.skipHours!.contains(3), true); | |||||
| expect(feed.skipHours!.contains(4), true); | |||||
| 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, | |||||
| expect(feed.items!.first.description, | |||||
| 'Lorem ipsum dolor sit amet, consectetur adipiscing elit'); | '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, | |||||
| 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 | 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, | |||||
| 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!.length, 12216320); | |||||
| expect(feed.items!.first.enclosure!.type, 'audio/mpeg'); | |||||
| expect(feed.items.first.content.value, | |||||
| 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( | 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', () { | test('parse RSS-Media.xml', () { | ||||
| var xmlString = File('test/xml/RSS-Media.xml').readAsStringSync(); | var xmlString = File('test/xml/RSS-Media.xml').readAsStringSync(); | ||||
| @@ -98,21 +98,21 @@ void main() { | |||||
| expect( | 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); | |||||
| expect(feed.items!.length, 1); | |||||
| var item = feed.items.first; | |||||
| var item = feed.items!.first; | |||||
| expect(item.title, null); | expect(item.title, null); | ||||
| expect(item.link, 'http://www.foo.com'); | expect(item.link, 'http://www.foo.com'); | ||||
| expect(item.pubDate, | expect(item.pubDate, | ||||
| DateTime(2001, 08, 27, 16, 08, 56)); //Mon, 27 Aug 2001 16:08:56 PST | 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!.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.contents.length, 2); | |||||
| var mediaContent = item.media.contents.first; | |||||
| expect(item.media!.contents!.length, 2); | |||||
| var mediaContent = item.media!.contents!.first; | |||||
| expect(mediaContent.url, 'http://www.foo.com/video.mov'); | expect(mediaContent.url, 'http://www.foo.com/video.mov'); | ||||
| expect(mediaContent.type, 'video/quicktime'); | expect(mediaContent.type, 'video/quicktime'); | ||||
| expect(mediaContent.fileSize, 2000); | expect(mediaContent.fileSize, 2000); | ||||
| @@ -124,152 +124,152 @@ void main() { | |||||
| expect(mediaContent.samplingrate, 44.1); | expect(mediaContent.samplingrate, 44.1); | ||||
| expect(mediaContent.channels, 2); | expect(mediaContent.channels, 2); | ||||
| expect(item.media.credits.length, 2); | |||||
| var mediaCredit = item.media.credits.first; | |||||
| expect(item.media!.credits!.length, 2); | |||||
| var mediaCredit = item.media!.credits!.first; | |||||
| expect(mediaCredit.role, 'owner1'); | expect(mediaCredit.role, 'owner1'); | ||||
| expect(mediaCredit.scheme, 'urn:yvs'); | expect(mediaCredit.scheme, 'urn:yvs'); | ||||
| expect(mediaCredit.value, 'copyright holder of the entity'); | expect(mediaCredit.value, 'copyright holder of the entity'); | ||||
| expect(item.media.category.scheme, | |||||
| expect(item.media!.category!.scheme, | |||||
| 'http://search.yahoo.com/mrss/category_ schema'); | '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!.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.value, "The Judy's -- The Moo Song"); | |||||
| 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, | |||||
| 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(item.media!.thumbnails!.length, 2); | |||||
| var mediaThumbnail = item.media!.thumbnails!.first; | |||||
| expect(mediaThumbnail.url, 'http://www.foo.com/keyframe1.jpg'); | expect(mediaThumbnail.url, 'http://www.foo.com/keyframe1.jpg'); | ||||
| expect(mediaThumbnail.width, '75'); | expect(mediaThumbnail.width, '75'); | ||||
| expect(mediaThumbnail.height, '50'); | expect(mediaThumbnail.height, '50'); | ||||
| expect(mediaThumbnail.time, '12:05:01.123'); | expect(mediaThumbnail.time, '12:05:01.123'); | ||||
| 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.width, 400); | |||||
| expect(item.media.player.height, 200); | |||||
| 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.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.community.starRating.average, 3.5); | |||||
| expect(item.media.community.starRating.count, 20); | |||||
| expect(item.media.community.starRating.min, 1); | |||||
| 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.weight, 1); | |||||
| expect(item.media.comments.length, 2); | |||||
| 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.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.responses.length, 2); | |||||
| 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.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.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.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!.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!.width, 400); | |||||
| expect(item.media!.player!.height, 200); | |||||
| 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!.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!.community!.starRating!.average, 3.5); | |||||
| expect(item.media!.community!.starRating!.count, 20); | |||||
| expect(item.media!.community!.starRating!.min, 1); | |||||
| 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!.weight, 1); | |||||
| expect(item.media!.comments!.length, 2); | |||||
| 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!.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!.responses!.length, 2); | |||||
| 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!.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!.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!.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'); | |||||
| }); | }); | ||||
| test('parse RSS-DC.xml', () { | test('parse RSS-DC.xml', () { | ||||
| var xmlString = File('test/xml/RSS-DC.xml').readAsStringSync(); | var xmlString = File('test/xml/RSS-DC.xml').readAsStringSync(); | ||||
| var feed = RssFeed.parse(xmlString); | 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, 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'); | |||||
| 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', () { | test('parse RSS-Empty.xml', () { | ||||
| @@ -294,26 +294,26 @@ void main() { | |||||
| expect(feed.cloud, null); | expect(feed.cloud, null); | ||||
| expect(feed.categories.length, 0); | |||||
| expect(feed.categories!.length, 0); | |||||
| expect(feed.skipDays.length, 0); | |||||
| expect(feed.skipDays!.length, 0); | |||||
| expect(feed.skipHours.length, 0); | |||||
| expect(feed.skipHours!.length, 0); | |||||
| expect(feed.items.length, 1); | |||||
| expect(feed.items!.length, 1); | |||||
| expect(feed.items.first.title, null); | |||||
| expect(feed.items.first.description, null); | |||||
| expect(feed.items.first.link, null); | |||||
| expect(feed.items.first.guid, null); | |||||
| expect(feed.items.first.pubDate, null); | |||||
| expect(feed.items.first.categories.length, 0); | |||||
| expect(feed.items.first.author, null); | |||||
| expect(feed.items.first.source, null); | |||||
| expect(feed.items.first.comments, null); | |||||
| expect(feed.items.first.enclosure, null); | |||||
| expect(feed.items!.first.title, null); | |||||
| expect(feed.items!.first.description, null); | |||||
| expect(feed.items!.first.link, null); | |||||
| expect(feed.items!.first.guid, null); | |||||
| expect(feed.items!.first.pubDate, null); | |||||
| expect(feed.items!.first.categories!.length, 0); | |||||
| expect(feed.items!.first.author, null); | |||||
| expect(feed.items!.first.source, null); | |||||
| expect(feed.items!.first.comments, null); | |||||
| expect(feed.items!.first.enclosure, null); | |||||
| expect(feed.items.first.content, null); | |||||
| expect(feed.items!.first.content, null); | |||||
| }); | }); | ||||
| test('parse RSS-Itunes.xml', () { | test('parse RSS-Itunes.xml', () { | ||||
| @@ -321,22 +321,22 @@ void main() { | |||||
| var feed = RssFeed.parse(xmlString); | var feed = RssFeed.parse(xmlString); | ||||
| expect(feed.itunes.author, 'Changelog Media'); | |||||
| expect(feed.itunes.summary, 'Foo'); | |||||
| expect(feed.itunes.explicit, false); | |||||
| expect(feed.itunes.image.href, | |||||
| 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, | |||||
| expect(feed.itunes!.keywords, | |||||
| 'go,golang,open source,software,development'.split(',')); | 'go,golang,open source,software,development'.split(',')); | ||||
| expect(feed.itunes.owner.name, 'Changelog Media'); | |||||
| expect(feed.itunes.owner.email, 'editors@changelog.com'); | |||||
| expect(feed.itunes!.owner!.name, 'Changelog Media'); | |||||
| expect(feed.itunes!.owner!.email, 'editors@changelog.com'); | |||||
| expect( | expect( | ||||
| Set.from([ | Set.from([ | ||||
| feed.itunes.categories[0].category, | |||||
| feed.itunes.categories[1].category | |||||
| feed.itunes!.categories![0].category, | |||||
| feed.itunes!.categories![1].category | |||||
| ]), | ]), | ||||
| ['Technology', 'Foo']); | ['Technology', 'Foo']); | ||||
| for (var category in feed.itunes.categories) { | |||||
| for (var category in feed.itunes!.categories!) { | |||||
| switch (category.category) { | switch (category.category) { | ||||
| case 'Foo': | case 'Foo': | ||||
| expect(category.subCategories, ['Bar', 'Baz']); | expect(category.subCategories, ['Bar', 'Baz']); | ||||
| @@ -346,29 +346,29 @@ void main() { | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| 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, ItunesEpisodeType.full); | |||||
| expect(item.itunes.episode, 1); | |||||
| expect(item.itunes.season, 1); | |||||
| expect(item.itunes.image.href, | |||||
| 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, 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, | |||||
| 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(',')); | '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, | |||||
| 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.block, false); | |||||
| expect(item.itunes!.explicit, false); | |||||
| expect(item.itunes!.title, 'awesome title'); | |||||
| expect(item.itunes!.block, false); | |||||
| }); | }); | ||||
| test('parse RSS-Itunes_item_empty_field.xml with empty duration field', () { | test('parse RSS-Itunes_item_empty_field.xml with empty duration field', () { | ||||
| @@ -391,12 +391,12 @@ void main() { | |||||
| expect(feed.title, 'Mozilla Dot Org'); | expect(feed.title, 'Mozilla Dot Org'); | ||||
| expect(feed.link, 'http://www.mozilla.org'); | expect(feed.link, 'http://www.mozilla.org'); | ||||
| expect(feed.description, 'the Mozilla Organization web site'); | 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/'); | |||||
| 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', () { | test('parse RSS-Syndication.xml', () { | ||||
| @@ -407,17 +407,17 @@ void main() { | |||||
| expect(feed.title, 'Meerkat'); | expect(feed.title, 'Meerkat'); | ||||
| expect(feed.link, 'http://meerkat.oreillynet.com'); | expect(feed.link, 'http://meerkat.oreillynet.com'); | ||||
| expect(feed.description, 'Meerkat: An Open Wire Service'); | expect(feed.description, 'Meerkat: An Open Wire Service'); | ||||
| expect(feed.image.title, 'Meerkat Powered!'); | |||||
| expect(feed.image.url, | |||||
| expect(feed.image!.title, 'Meerkat Powered!'); | |||||
| expect(feed.image!.url, | |||||
| 'http://meerkat.oreillynet.com/icons/meerkat-powered.jpg'); | '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, | |||||
| 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.'); | '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'); | |||||
| expect(feed.items!.first.link, 'http://c.moreover.com/click/here.pl?r123'); | |||||
| }); | }); | ||||
| } | } | ||||