浏览代码

Merge branch 'master' into parse_null_or_empty

master
Wito Chandra 5 年前
committed by GitHub
父节点
当前提交
04f276605f
找不到此签名对应的密钥 GPG 密钥 ID: 4AEE18F83AFDEB23
共有 58 个文件被更改,包括 993 次插入1014 次删除
  1. +2
    -0
      .gitignore
  2. +10
    -5
      example/main.dart
  3. +3
    -3
      lib/domain/atom_category.dart
  4. +27
    -24
      lib/domain/atom_feed.dart
  5. +3
    -6
      lib/domain/atom_generator.dart
  6. +25
    -22
      lib/domain/atom_item.dart
  7. +6
    -6
      lib/domain/atom_link.dart
  8. +7
    -7
      lib/domain/atom_person.dart
  9. +7
    -10
      lib/domain/atom_source.dart
  10. +37
    -38
      lib/domain/dublin_core/dublin_core.dart
  11. +56
    -43
      lib/domain/itunes/itunes.dart
  12. +7
    -10
      lib/domain/itunes/itunes_category.dart
  13. +1
    -1
      lib/domain/itunes/itunes_episode_type.dart
  14. +1
    -2
      lib/domain/itunes/itunes_image.dart
  15. +5
    -6
      lib/domain/itunes/itunes_owner.dart
  16. +1
    -1
      lib/domain/itunes/itunes_type.dart
  17. +3
    -6
      lib/domain/media/category.dart
  18. +16
    -16
      lib/domain/media/community.dart
  19. +14
    -14
      lib/domain/media/content.dart
  20. +2
    -5
      lib/domain/media/copyright.dart
  21. +3
    -3
      lib/domain/media/credit.dart
  22. +2
    -5
      lib/domain/media/description.dart
  23. +8
    -10
      lib/domain/media/embed.dart
  24. +21
    -20
      lib/domain/media/group.dart
  25. +2
    -5
      lib/domain/media/hash.dart
  26. +3
    -6
      lib/domain/media/license.dart
  27. +126
    -102
      lib/domain/media/media.dart
  28. +2
    -5
      lib/domain/media/param.dart
  29. +3
    -6
      lib/domain/media/peer_link.dart
  30. +4
    -7
      lib/domain/media/player.dart
  31. +4
    -4
      lib/domain/media/price.dart
  32. +2
    -5
      lib/domain/media/rating.dart
  33. +3
    -6
      lib/domain/media/restriction.dart
  34. +1
    -4
      lib/domain/media/rights.dart
  35. +9
    -12
      lib/domain/media/scene.dart
  36. +9
    -9
      lib/domain/media/star_rating.dart
  37. +5
    -5
      lib/domain/media/statistics.dart
  38. +2
    -5
      lib/domain/media/status.dart
  39. +2
    -5
      lib/domain/media/tags.dart
  40. +5
    -8
      lib/domain/media/text.dart
  41. +4
    -4
      lib/domain/media/thumbnail.dart
  42. +2
    -5
      lib/domain/media/title.dart
  43. +1
    -4
      lib/domain/rss_category.dart
  44. +5
    -8
      lib/domain/rss_cloud.dart
  45. +2
    -5
      lib/domain/rss_content.dart
  46. +3
    -6
      lib/domain/rss_enclosure.dart
  47. +61
    -49
      lib/domain/rss_feed.dart
  48. +7
    -10
      lib/domain/rss_image.dart
  49. +34
    -25
      lib/domain/rss_item.dart
  50. +2
    -5
      lib/domain/rss_source.dart
  51. +8
    -11
      lib/domain/syndication/syndication.dart
  52. +5
    -5
      lib/util/datetime.dart
  53. +0
    -0
      lib/util/function.dart
  54. +3
    -0
      lib/util/iterable.dart
  55. +7
    -20
      lib/util/xml.dart
  56. +4
    -4
      pubspec.yaml
  57. +157
    -157
      test/atom_test.dart
  58. +239
    -239
      test/rss_test.dart

+ 2
- 0
.gitignore 查看文件

@@ -13,3 +13,5 @@ doc/api/

.idea
.dart_tool

.vscode/

+ 10
- 5
example/main.dart 查看文件

@@ -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';

void main() async {
var client = http.Client();
final client = IOClient(HttpClient()
..badCertificateCallback =
((X509Certificate cert, String host, int port) => true));

// 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);
print(channel);

// 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);
print(feed);



+ 3
- 3
lib/domain/atom_category.dart 查看文件

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

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);



+ 27
- 24
lib/domain/atom_feed.dart 查看文件

@@ -4,24 +4,24 @@ import 'package:webfeed/domain/atom_item.dart';
import 'package:webfeed/domain/atom_link.dart';
import 'package:webfeed/domain/atom_person.dart';
import 'package:webfeed/util/datetime.dart';
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

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({
this.id,
@@ -41,15 +41,16 @@ class AtomFeed {

factory AtomFeed.parse(String xmlString) {
var document = XmlDocument.parse(xmlString);
var feedElement = findFirstElement(document, 'feed');
var feedElement = document.findElements('feed').firstOrNull;
if (feedElement == null) {
throw ArgumentError('feed not found');
}

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
.findElements('entry')
.map((e) => AtomItem.parse(e))
@@ -70,12 +71,14 @@ class AtomFeed {
.findElements('category')
.map((e) => AtomCategory.parse(e))
.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,
);
}
}

+ 3
- 6
lib/domain/atom_generator.dart 查看文件

@@ -1,16 +1,13 @@
import 'package:xml/xml.dart';

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);

factory AtomGenerator.parse(XmlElement element) {
if (element == null) {
return null;
}
var uri = element.getAttribute('uri');
var version = element.getAttribute('version');
var value = element.text;


+ 25
- 22
lib/domain/atom_item.dart 查看文件

@@ -4,24 +4,24 @@ import 'package:webfeed/domain/atom_person.dart';
import 'package:webfeed/domain/atom_source.dart';
import 'package:webfeed/domain/media/media.dart';
import 'package:webfeed/util/datetime.dart';
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

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({
this.id,
@@ -41,9 +41,9 @@ class AtomItem {

factory AtomItem.parse(XmlElement element) {
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
.findElements('author')
.map((e) => AtomPerson.parse(e))
@@ -58,11 +58,14 @@ class AtomItem {
.findElements('contributor')
.map((e) => AtomPerson.parse(e))
.toList(),
source: AtomSource.parse(findFirstElement(element, 'source')),
published: findFirstElement(element, 'published')?.text,
content: findFirstElement(element, 'content')?.text,
summary: findFirstElement(element, 'summary')?.text,
rights: findFirstElement(element, 'rights')?.text,
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),
);
}


+ 6
- 6
lib/domain/atom_link.dart 查看文件

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

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;

AtomLink(
@@ -25,7 +25,7 @@ class AtomLink {
var hreflang = element.getAttribute('hreflang');
var length = 0;
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);
}


+ 7
- 7
lib/domain/atom_person.dart 查看文件

@@ -1,18 +1,18 @@
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

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});

factory AtomPerson.parse(XmlElement element) {
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,
);
}
}

+ 7
- 10
lib/domain/atom_source.dart 查看文件

@@ -1,10 +1,10 @@
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

class AtomSource {
final String id;
final String title;
final String updated;
final String? id;
final String? title;
final String? updated;

AtomSource({
this.id,
@@ -13,13 +13,10 @@ class AtomSource {
});

factory AtomSource.parse(XmlElement element) {
if (element == null) {
return null;
}
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,
);
}
}

+ 37
- 38
lib/domain/dublin_core/dublin_core.dart 查看文件

@@ -1,25 +1,25 @@
import 'package:webfeed/util/datetime.dart';
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

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({
this.title,
@@ -42,27 +42,26 @@ class DublinCore {
});

factory DublinCore.parse(XmlElement element) {
if (element == null) {
return null;
}
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,
);
}
}

+ 56
- 43
lib/domain/itunes/itunes.dart 查看文件

@@ -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_type.dart';
import 'package:webfeed/util/string.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:webfeed/util/xml.dart';
import 'package:xml/xml.dart';

class Itunes {
final String author;
final String summary;
final bool explicit;
final String title;
final String subtitle;
final ItunesOwner owner;
final List<String> keywords;
final ItunesImage image;
final List<ItunesCategory> categories;
final ItunesType type;
final String newFeedUrl;
final bool block;
final bool complete;
final int episode;
final int season;
final Duration duration;
final ItunesEpisodeType episodeType;
final String? author;
final String? summary;
final bool? explicit;
final String? title;
final String? subtitle;
final ItunesOwner? owner;
final List<String>? keywords;
final ItunesImage? image;
final List<ItunesCategory>? categories;
final ItunesType? type;
final String? newFeedUrl;
final bool? block;
final bool? complete;
final int? episode;
final int? season;
final Duration? duration;
final ItunesEpisodeType? episodeType;

Itunes({
this.author,
@@ -47,38 +48,50 @@ class Itunes {
});

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(
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'),
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
?.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))
.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'),
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),
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 parts = s.split(':');
if (parts.length > 2) {
hours = int.parse(parts[parts.length - 3]);
hours = int.tryParse(parts[parts.length - 3]) ?? 0;
}
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(
hours: hours,
minutes: minutes,


+ 7
- 10
lib/domain/itunes/itunes_category.dart 查看文件

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

class ItunesCategory {
final String category;
final List<String> subCategories;
final String? category;
final List<String>? subCategories;

ItunesCategory({this.category, this.subCategories});

factory ItunesCategory.parse(XmlElement element) {
if (element == null) return null;
return ItunesCategory(
category: element.getAttribute('text')?.trim(),
subCategories: element
.findElements('itunes:category')
?.map((e) => e.getAttribute('text')?.trim())
?.toList() ??
[],
);
category: element.getAttribute('text')?.trim(),
subCategories: element
.findElements('itunes:category')
.map((e) => e.getAttribute('text')?.trim() ?? '')
.toList());
}
}

+ 1
- 1
lib/domain/itunes/itunes_episode_type.dart 查看文件

@@ -3,7 +3,7 @@ import 'package:xml/xml.dart';
enum ItunesEpisodeType { full, trailer, bonus, unknown }

ItunesEpisodeType newItunesEpisodeType(XmlElement element) {
switch (element?.text) {
switch (element.text) {
case 'full':
return ItunesEpisodeType.full;
case 'trailer':


+ 1
- 2
lib/domain/itunes/itunes_image.dart 查看文件

@@ -1,12 +1,11 @@
import 'package:xml/xml.dart';

class ItunesImage {
final String href;
final String? href;

ItunesImage({this.href});

factory ItunesImage.parse(XmlElement element) {
if (element == null) return null;
return ItunesImage(
href: element.getAttribute('href')?.trim(),
);


+ 5
- 6
lib/domain/itunes/itunes_owner.dart 查看文件

@@ -1,17 +1,16 @@
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

class ItunesOwner {
final String name;
final String email;
final String? name;
final String? email;

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

factory ItunesOwner.parse(XmlElement element) {
if (element == null) return null;
return ItunesOwner(
name: findFirstElement(element, 'itunes:name')?.text?.trim(),
email: findFirstElement(element, 'itunes:email')?.text?.trim(),
name: element.findElements('itunes:name').firstOrNull?.text.trim(),
email: element.findElements('itunes:email').firstOrNull?.text.trim(),
);
}
}

+ 1
- 1
lib/domain/itunes/itunes_type.dart 查看文件

@@ -3,7 +3,7 @@ import 'package:xml/xml.dart';
enum ItunesType { episodic, serial, unknown }

ItunesType newItunesType(XmlElement element) {
switch (element?.text) {
switch (element.text) {
case 'episodic':
return ItunesType.episodic;
case 'serial':


+ 3
- 6
lib/domain/media/category.dart 查看文件

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

class Category {
final String scheme;
final String label;
final String value;
final String? scheme;
final String? label;
final String? value;

Category({
this.scheme,
@@ -12,9 +12,6 @@ class Category {
});

factory Category.parse(XmlElement element) {
if (element == null) {
return null;
}
return Category(
scheme: element.getAttribute('scheme'),
label: element.getAttribute('label'),


+ 16
- 16
lib/domain/media/community.dart 查看文件

@@ -1,13 +1,13 @@
import 'package:webfeed/domain/media/star_rating.dart';
import 'package:webfeed/domain/media/statistics.dart';
import 'package:webfeed/domain/media/tags.dart';
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

class Community {
final StarRating starRating;
final Statistics statistics;
final Tags tags;
final StarRating? starRating;
final Statistics? statistics;
final Tags? tags;

Community({
this.starRating,
@@ -16,19 +16,19 @@ class Community {
});

factory Community.parse(XmlElement element) {
if (element == null) {
return null;
}
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,
);
}
}

+ 14
- 14
lib/domain/media/content.dart 查看文件

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

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({
this.url,


+ 2
- 5
lib/domain/media/copyright.dart 查看文件

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

class Copyright {
final String url;
final String value;
final String? url;
final String? value;

Copyright({
this.url,
@@ -10,9 +10,6 @@ class Copyright {
});

factory Copyright.parse(XmlElement element) {
if (element == null) {
return null;
}
return Copyright(
url: element.getAttribute('url'),
value: element.text,


+ 3
- 3
lib/domain/media/credit.dart 查看文件

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

class Credit {
final String role;
final String scheme;
final String value;
final String? role;
final String? scheme;
final String? value;

Credit({
this.role,


+ 2
- 5
lib/domain/media/description.dart 查看文件

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

class Description {
final String type;
final String value;
final String? type;
final String? value;

Description({
this.type,
@@ -10,9 +10,6 @@ class Description {
});

factory Description.parse(XmlElement element) {
if (element == null) {
return null;
}
return Description(
type: element.getAttribute('type'),
value: element.text,


+ 8
- 10
lib/domain/media/embed.dart 查看文件

@@ -2,10 +2,10 @@ import 'package:webfeed/domain/media/param.dart';
import 'package:xml/xml.dart';

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({
this.url,
@@ -15,16 +15,14 @@ class Embed {
});

factory Embed.parse(XmlElement element) {
if (element == null) {
return null;
}
return Embed(
url: element.getAttribute('url'),
width: int.tryParse(element.getAttribute('width') ?? '0'),
height: int.tryParse(element.getAttribute('height') ?? '0'),
params: element.findElements('media:param').map((e) {
return Param.parse(e);
}).toList(),
params: element
.findElements('media:param')
.map((e) => Param.parse(e))
.toList(),
);
}
}

+ 21
- 20
lib/domain/media/group.dart 查看文件

@@ -2,14 +2,14 @@ import 'package:webfeed/domain/media/category.dart';
import 'package:webfeed/domain/media/content.dart';
import 'package:webfeed/domain/media/credit.dart';
import 'package:webfeed/domain/media/rating.dart';
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

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({
this.contents,
@@ -19,22 +19,23 @@ class Group {
});

factory Group.parse(XmlElement element) {
if (element == null) {
return null;
}
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,
);
}
}

+ 2
- 5
lib/domain/media/hash.dart 查看文件

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

class Hash {
final String algo;
final String value;
final String? algo;
final String? value;

Hash({
this.algo,
@@ -10,9 +10,6 @@ class Hash {
});

factory Hash.parse(XmlElement element) {
if (element == null) {
return null;
}
return Hash(
algo: element.getAttribute('algo'),
value: element.text,


+ 3
- 6
lib/domain/media/license.dart 查看文件

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

class License {
final String type;
final String href;
final String value;
final String? type;
final String? href;
final String? value;

License({
this.type,
@@ -12,9 +12,6 @@ class License {
});

factory License.parse(XmlElement element) {
if (element == null) {
return null;
}
return License(
type: element.getAttribute('type'),
href: element.getAttribute('href'),


+ 126
- 102
lib/domain/media/media.dart 查看文件

@@ -20,34 +20,35 @@ import 'package:webfeed/domain/media/text.dart';
import 'package:webfeed/domain/media/thumbnail.dart';
import 'package:webfeed/domain/media/title.dart';
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

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({
this.group,
@@ -79,90 +80,113 @@ class Media {

factory Media.parse(XmlElement element) {
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')
?.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')
?.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')
?.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')
?.map((e) {
return Scene.parse(e);
})?.toList() ??
.map((e) => Scene.parse(e))
.toList() ??
[],
);
}


+ 2
- 5
lib/domain/media/param.dart 查看文件

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

class Param {
final String name;
final String value;
final String? name;
final String? value;

Param({
this.name,
@@ -10,9 +10,6 @@ class Param {
});

factory Param.parse(XmlElement element) {
if (element == null) {
return null;
}
return Param(
name: element.getAttribute('name'),
value: element.text,


+ 3
- 6
lib/domain/media/peer_link.dart 查看文件

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

class PeerLink {
final String type;
final String href;
final String value;
final String? type;
final String? href;
final String? value;

PeerLink({
this.type,
@@ -12,9 +12,6 @@ class PeerLink {
});

factory PeerLink.parse(XmlElement element) {
if (element == null) {
return null;
}
return PeerLink(
type: element.getAttribute('type'),
href: element.getAttribute('href'),


+ 4
- 7
lib/domain/media/player.dart 查看文件

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

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({
this.url,
@@ -14,9 +14,6 @@ class Player {
});

factory Player.parse(XmlElement element) {
if (element == null) {
return null;
}
return Player(
url: element.getAttribute('url'),
width: int.tryParse(element.getAttribute('width') ?? '0'),


+ 4
- 4
lib/domain/media/price.dart 查看文件

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

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({
this.price,


+ 2
- 5
lib/domain/media/rating.dart 查看文件

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

class Rating {
final String scheme;
final String value;
final String? scheme;
final String? value;

Rating({
this.scheme,
@@ -10,9 +10,6 @@ class Rating {
});

factory Rating.parse(XmlElement element) {
if (element == null) {
return null;
}
return Rating(
scheme: element.getAttribute('scheme'),
value: element.text,


+ 3
- 6
lib/domain/media/restriction.dart 查看文件

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

class Restriction {
final String relationship;
final String type;
final String value;
final String? relationship;
final String? type;
final String? value;

Restriction({
this.relationship,
@@ -12,9 +12,6 @@ class Restriction {
});

factory Restriction.parse(XmlElement element) {
if (element == null) {
return null;
}
return Restriction(
relationship: element.getAttribute('relationship'),
type: element.getAttribute('type'),


+ 1
- 4
lib/domain/media/rights.dart 查看文件

@@ -1,16 +1,13 @@
import 'package:xml/xml.dart';

class Rights {
final String status;
final String? status;

Rights({
this.status,
});

factory Rights.parse(XmlElement element) {
if (element == null) {
return null;
}
return Rights(
status: element.getAttribute('status'),
);


+ 9
- 12
lib/domain/media/scene.dart 查看文件

@@ -1,11 +1,11 @@
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

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({
this.title,
@@ -15,14 +15,11 @@ class Scene {
});

factory Scene.parse(XmlElement element) {
if (element == null) {
return null;
}
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,
);
}
}

+ 9
- 9
lib/domain/media/star_rating.dart 查看文件

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

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({
this.average,
@@ -13,12 +13,12 @@ class StarRating {
this.max,
});

factory StarRating.parse(XmlElement element) {
factory StarRating.parse(XmlElement? element) {
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'),
);
}
}

+ 5
- 5
lib/domain/media/statistics.dart 查看文件

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

class Statistics {
final int views;
final int favorites;
final int? views;
final int? favorites;

Statistics({
this.views,
this.favorites,
});

factory Statistics.parse(XmlElement element) {
factory Statistics.parse(XmlElement? element) {
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'),
);
}
}

+ 2
- 5
lib/domain/media/status.dart 查看文件

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

class Status {
final String state;
final String reason;
final String? state;
final String? reason;

Status({
this.state,
@@ -10,9 +10,6 @@ class Status {
});

factory Status.parse(XmlElement element) {
if (element == null) {
return null;
}
return Status(
state: element.getAttribute('state'),
reason: element.getAttribute('reason'),


+ 2
- 5
lib/domain/media/tags.dart 查看文件

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

class Tags {
final String tags;
final int weight;
final String? tags;
final int? weight;

Tags({
this.tags,
@@ -10,9 +10,6 @@ class Tags {
});

factory Tags.parse(XmlElement element) {
if (element == null) {
return null;
}
return Tags(
tags: element.text,
weight: int.tryParse(element.getAttribute('weight') ?? '1'),


+ 5
- 8
lib/domain/media/text.dart 查看文件

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

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({
this.type,
@@ -16,9 +16,6 @@ class Text {
});

factory Text.parse(XmlElement element) {
if (element == null) {
return null;
}
return Text(
type: element.getAttribute('type'),
lang: element.getAttribute('lang'),


+ 4
- 4
lib/domain/media/thumbnail.dart 查看文件

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

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({
this.url,


+ 2
- 5
lib/domain/media/title.dart 查看文件

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

class Title {
final String type;
final String value;
final String? type;
final String? value;

Title({
this.type,
@@ -10,9 +10,6 @@ class Title {
});

factory Title.parse(XmlElement element) {
if (element == null) {
return null;
}
return Title(
type: element.getAttribute('type'),
value: element.text,


+ 1
- 4
lib/domain/rss_category.dart 查看文件

@@ -1,15 +1,12 @@
import 'package:xml/xml.dart';

class RssCategory {
final String domain;
final String? domain;
final String value;

RssCategory(this.domain, this.value);

factory RssCategory.parse(XmlElement element) {
if (element == null) {
return null;
}
var domain = element.getAttribute('domain');
var value = element.text;



+ 5
- 8
lib/domain/rss_cloud.dart 查看文件

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

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(
this.domain,
@@ -16,9 +16,6 @@ class RssCloud {
);

factory RssCloud.parse(XmlElement node) {
if (node == null) {
return null;
}
var domain = node.getAttribute('domain');
var port = node.getAttribute('port');
var path = node.getAttribute('path');


+ 2
- 5
lib/domain/rss_content.dart 查看文件

@@ -17,13 +17,10 @@ class RssContent {
RssContent(this.value, this.images);

factory RssContent.parse(XmlElement element) {
if (element == null) {
return null;
}
final content = element.text;
final dynamic? content = element.text;
final images = <String>[];
_imagesRegExp.allMatches(content).forEach((match) {
images.add(match.group(1));
images.add(match.group(1)!);
});
return RssContent(content, images);
}


+ 3
- 6
lib/domain/rss_enclosure.dart 查看文件

@@ -1,16 +1,13 @@
import 'package:xml/xml.dart';

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);

factory RssEnclosure.parse(XmlElement element) {
if (element == null) {
return null;
}
var url = element.getAttribute('url');
var type = element.getAttribute('type');
var length = int.tryParse(element.getAttribute('length') ?? '0');


+ 61
- 49
lib/domain/rss_feed.dart 查看文件

@@ -7,33 +7,33 @@ import 'package:webfeed/domain/rss_cloud.dart';
import 'package:webfeed/domain/rss_image.dart';
import 'package:webfeed/domain/rss_item.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';

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({
this.title,
@@ -62,50 +62,62 @@ class RssFeed {

factory RssFeed.parse(String 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) {
throw ArgumentError('not a rss feed');
}
var channelElement = findFirstElement(rss ?? rdf, 'channel');
var channelElement = (rss ?? rdf)!.findElements('channel').firstOrNull;
if (channelElement == null) {
throw ArgumentError('channel not found');
}
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')
.map((e) => RssItem.parse(e))
.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
.findElements('category')
.map((e) => RssCategory.parse(e))
.toList(),
skipDays: findFirstElement(channelElement, 'skipDays')
skipDays: channelElement
.findElements('skipDays')
.firstOrNull
?.findAllElements('day')
?.map((e) => e.text)
?.toList() ??
.map((e) => e.text)
.toList() ??
[],
skipHours: findFirstElement(channelElement, 'skipHours')
skipHours: channelElement
.findElements('skipHours')
.firstOrNull
?.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),
itunes: Itunes.parse(channelElement),
syndication: Syndication.parse(channelElement),


+ 7
- 10
lib/domain/rss_image.dart 查看文件

@@ -1,21 +1,18 @@
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

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});

factory RssImage.parse(XmlElement element) {
if (element == null) {
return null;
}
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,
);
}
}

+ 34
- 25
lib/domain/rss_item.dart 查看文件

@@ -6,25 +6,25 @@ import 'package:webfeed/domain/rss_content.dart';
import 'package:webfeed/domain/rss_enclosure.dart';
import 'package:webfeed/domain/rss_source.dart';
import 'package:webfeed/util/datetime.dart';
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

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({
this.title,
@@ -45,21 +45,30 @@ class RssItem {

factory RssItem.parse(XmlElement element) {
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
.findElements('category')
.map((e) => RssCategory.parse(e))
.toList(),
guid: findFirstElement(element, 'guid')?.text,
pubDate: parseDateTime(findFirstElement(element, 'pubDate')?.text),
author: findFirstElement(element, 'author')?.text,
comments: findFirstElement(element, 'comments')?.text,
source: RssSource.parse(findFirstElement(element, 'source')),
content: RssContent.parse(findFirstElement(element, 'content:encoded')),
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),
enclosure: RssEnclosure.parse(findFirstElement(element, 'enclosure')),
enclosure: element
.findElements('enclosure')
.map((e) => RssEnclosure.parse(e))
.firstOrNull,
dc: DublinCore.parse(element),
itunes: Itunes.parse(element),
);


+ 2
- 5
lib/domain/rss_source.dart 查看文件

@@ -1,15 +1,12 @@
import 'package:xml/xml.dart';

class RssSource {
final String url;
final String value;
final String? url;
final String? value;

RssSource(this.url, this.value);

factory RssSource.parse(XmlElement element) {
if (element == null) {
return null;
}
var url = element.getAttribute('url');
var value = element.text;



+ 8
- 11
lib/domain/syndication/syndication.dart 查看文件

@@ -1,13 +1,13 @@
import 'package:webfeed/util/datetime.dart';
import 'package:webfeed/util/xml.dart';
import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

enum SyndicationUpdatePeriod { hourly, daily, weekly, monthly, yearly }

class Syndication {
final SyndicationUpdatePeriod updatePeriod;
final int updateFrequency;
final DateTime updateBase;
final SyndicationUpdatePeriod? updatePeriod;
final int? updateFrequency;
final DateTime? updateBase;

Syndication({
this.updatePeriod,
@@ -16,11 +16,8 @@ class Syndication {
});

factory Syndication.parse(XmlElement element) {
if (element == null) {
return null;
}
SyndicationUpdatePeriod updatePeriod;
switch (findFirstElement(element, 'sy:updatePeriod')?.text) {
switch (element.findElements('sy:updatePeriod').firstOrNull?.text) {
case 'hourly':
updatePeriod = SyndicationUpdatePeriod.hourly;
break;
@@ -43,9 +40,9 @@ class Syndication {
return Syndication(
updatePeriod: updatePeriod,
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),
);
}
}

+ 5
- 5
lib/util/datetime.dart 查看文件

@@ -2,15 +2,15 @@ import 'package:intl/intl.dart';

const rfc822DatePattern = 'EEE, dd MMM yyyy HH:mm:ss Z';

DateTime parseDateTime(dateString) {
DateTime? parseDateTime(dateString) {
if (dateString == null) return null;
return _parseRfc822DateTime(dateString) ?? _parseIso8601DateTime(dateString);
}

DateTime _parseRfc822DateTime(String dateString) {
DateTime? _parseRfc822DateTime(String dateString) {
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');
return format.parse(dateString);
} on FormatException {
@@ -18,7 +18,7 @@ DateTime _parseRfc822DateTime(String dateString) {
}
}

DateTime _parseIso8601DateTime(dateString) {
DateTime? _parseIso8601DateTime(dateString) {
try {
return DateTime.parse(dateString);
} on FormatException {


+ 0
- 0
lib/util/function.dart 查看文件


+ 3
- 0
lib/util/iterable.dart 查看文件

@@ -0,0 +1,3 @@
extension WebFeedIterable<T> on Iterable<T> {
T? get firstOrNull => isEmpty ? null : first;
}

+ 7
- 20
lib/util/xml.dart 查看文件

@@ -1,32 +1,19 @@
import 'dart:core';

import 'package:webfeed/util/iterable.dart';
import 'package:xml/xml.dart';

XmlElement findFirstElement(
XmlNode node,
Iterable<XmlElement>? findElements(
XmlNode? node,
String name, {
bool recursive = false,
String namespace,
}) {
try {
return findElements(node, name, recursive: recursive, namespace: namespace)
?.first;
} on StateError {
return null;
}
}

Iterable<XmlElement> findElements(
XmlNode node,
String name, {
bool recursive = false,
String namespace,
String? namespace,
}) {
try {
if (recursive) {
return node.findAllElements(name, namespace: namespace);
return node?.findAllElements(name, namespace: namespace);
} else {
return node.findElements(name, namespace: namespace);
return node?.findElements(name, namespace: namespace);
}
} on StateError {
return null;
@@ -34,7 +21,7 @@ Iterable<XmlElement> findElements(
}

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;
return ['yes', 'true'].contains(v);
}

+ 4
- 4
pubspec.yaml 查看文件

@@ -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.
homepage: https://github.com/witochandra/webfeed
environment:
sdk: ">=2.0.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
xml: "^4.2.0"
intl: "^0.16.0"
xml: "^5.0.2"
intl: "^0.17.0"
dev_dependencies:
test: ^1.3.0
http: "^0.11.3+16"
http: "^0.13.0"

+ 157
- 157
test/atom_test.dart 查看文件

@@ -23,65 +23,65 @@ void main() {
expect(feed.title, 'Foo bar news');
expect(feed.updated, DateTime.utc(2018, 4, 6, 13, 2, 46));

expect(feed.links.length, 2);
expect(feed.links.first.rel, 'foo');
expect(feed.links.first.type, 'text/html');
expect(feed.links.first.hreflang, 'en');
expect(feed.links.first.href, 'http://foo.bar.news/');
expect(feed.links.first.title, 'Foo bar news html');
expect(feed.links.first.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.logo, 'http://foo.bar.news/logo.png');
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.title, 'Foo bar item 1');
expect(item.updated, DateTime.utc(2018, 4, 6, 13, 2, 40));

expect(item.authors.length, 2);
expect(item.authors.first.name, 'Ellie');
expect(item.authors.first.uri, 'http://foo.bar.news/people/ellie');
expect(item.authors.first.email, 'ellie@foo.bar.news');
expect(item.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.summary, 'This is summary 1');
@@ -96,16 +96,16 @@ void main() {
expect(feed.title, 'Foo bar news');
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.type, 'video/quicktime');
expect(mediaContent.fileSize, 2000);
@@ -117,111 +117,111 @@ void main() {
expect(mediaContent.samplingrate, 44.1);
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.scheme, 'urn:yvs');
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');
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.');

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.width, '75');
expect(mediaThumbnail.height, '50');
expect(mediaThumbnail.time, '12:05:01.123');

expect(item.media.hash.algo, 'md5');
expect(item.media.hash.value, 'dfdec888b72151965a34b4b59031290a');
expect(item.media.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', () {
@@ -232,25 +232,25 @@ void main() {
expect(feed.id, null);
expect(feed.title, 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.icon, null);
expect(feed.logo, 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.summary, null);


+ 239
- 239
test/rss_test.dart 查看文件

@@ -36,59 +36,59 @@ void main() {
expect(feed.webMaster, 'webmaster@foo.bar.news');
expect(feed.ttl, 60);

expect(feed.image.title, 'Foo bar News');
expect(feed.image.url, 'https://foo.bar.news/logo.gif');
expect(feed.image.link, 'https://foo.bar.news/');
expect(feed.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');
expect(feed.items.first.description,
expect(feed.items!.first.description,
'Lorem ipsum dolor sit amet, consectetur adipiscing elit');
expect(feed.items.first.link, 'https://foo.bar.news/1');
expect(feed.items.first.guid, 'https://foo.bar.news/1?guid');
expect(feed.items.first.pubDate,
expect(feed.items!.first.link, 'https://foo.bar.news/1');
expect(feed.items!.first.guid, 'https://foo.bar.news/1?guid');
expect(feed.items!.first.pubDate,
DateTime(2018, 03, 26, 14)); //Mon, 26 Mar 2018 14:00:00 PDT
expect(feed.items.first.categories.first.domain, 'news');
expect(feed.items.first.categories.first.value, 'Lorem');
expect(feed.items.first.author, 'alice@foo.bar.news');
expect(feed.items.first.source.url, 'https://foo.bar.news/1?source');
expect(feed.items.first.source.value, 'Foo Bar');
expect(feed.items.first.comments, 'https://foo.bar.news/1/comments');
expect(feed.items.first.enclosure.url,
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');
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 />');
expect(
feed.items.first.content.images.first, 'https://test.com/image_link');
feed.items!.first.content!.images.first, 'https://test.com/image_link');
});
test('parse RSS-Media.xml', () {
var xmlString = File('test/xml/RSS-Media.xml').readAsStringSync();
@@ -98,21 +98,21 @@ void main() {
expect(
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.link, 'http://www.foo.com');
expect(item.pubDate,
DateTime(2001, 08, 27, 16, 08, 56)); //Mon, 27 Aug 2001 16:08:56 PST

expect(item.media.group.contents.length, 5);
expect(item.media.group.credits.length, 2);
expect(item.media.group.category.value, 'music/artist name/album/song');
expect(item.media.group.rating.value, 'nonadult');
expect(item.media!.group!.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.type, 'video/quicktime');
expect(mediaContent.fileSize, 2000);
@@ -124,152 +124,152 @@ void main() {
expect(mediaContent.samplingrate, 44.1);
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.scheme, 'urn:yvs');
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');
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.');

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.width, '75');
expect(mediaThumbnail.height, '50');
expect(mediaThumbnail.time, '12:05:01.123');

expect(item.media.hash.algo, 'md5');
expect(item.media.hash.value, 'dfdec888b72151965a34b4b59031290a');
expect(item.media.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', () {
var xmlString = File('test/xml/RSS-DC.xml').readAsStringSync();

var feed = RssFeed.parse(xmlString);

expect(feed.dc.title, 'title');
expect(feed.dc.creator, 'creator');
expect(feed.dc.subject, 'subject');
expect(feed.dc.description, 'description');
expect(feed.dc.publisher, 'publisher');
expect(feed.dc.contributor, 'contributor');
expect(feed.dc.date, 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', () {
@@ -294,26 +294,26 @@ void main() {

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', () {
@@ -321,22 +321,22 @@ void main() {

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');
expect(feed.itunes.keywords,
expect(feed.itunes!.keywords,
'go,golang,open source,software,development'.split(','));
expect(feed.itunes.owner.name, 'Changelog Media');
expect(feed.itunes.owner.email, 'editors@changelog.com');
expect(feed.itunes!.owner!.name, 'Changelog Media');
expect(feed.itunes!.owner!.email, 'editors@changelog.com');
expect(
Set.from([
feed.itunes.categories[0].category,
feed.itunes.categories[1].category
feed.itunes!.categories![0].category,
feed.itunes!.categories![1].category
]),
['Technology', 'Foo']);
for (var category in feed.itunes.categories) {
for (var category in feed.itunes!.categories!) {
switch (category.category) {
case 'Foo':
expect(category.subCategories, ['Bar', 'Baz']);
@@ -346,29 +346,29 @@ void main() {
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');
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(','));
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');
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', () {
@@ -391,12 +391,12 @@ void main() {
expect(feed.title, 'Mozilla Dot Org');
expect(feed.link, 'http://www.mozilla.org');
expect(feed.description, 'the Mozilla Organization web site');
expect(feed.image.title, 'Mozilla');
expect(feed.image.url, 'http://www.mozilla.org/images/moz.gif');
expect(feed.image.link, 'http://www.mozilla.org');
expect(feed.items.length, 5);
expect(feed.items.first.title, 'New Status Updates');
expect(feed.items.first.link, 'http://www.mozilla.org/status/');
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', () {
@@ -407,17 +407,17 @@ void main() {
expect(feed.title, 'Meerkat');
expect(feed.link, 'http://meerkat.oreillynet.com');
expect(feed.description, 'Meerkat: An Open Wire Service');
expect(feed.image.title, 'Meerkat Powered!');
expect(feed.image.url,
expect(feed.image!.title, 'Meerkat Powered!');
expect(feed.image!.url,
'http://meerkat.oreillynet.com/icons/meerkat-powered.jpg');
expect(feed.image.link, 'http://meerkat.oreillynet.com');
expect(feed.syndication.updatePeriod, SyndicationUpdatePeriod.hourly);
expect(feed.syndication.updateFrequency, 2);
expect(feed.syndication.updateBase, DateTime.utc(2001, 1, 1, 12, 1));
expect(feed.items.length, 1);
expect(feed.items.first.title, 'XML: A Disruptive Technology');
expect(feed.items.first.description,
expect(feed.image!.link, 'http://meerkat.oreillynet.com');
expect(feed.syndication!.updatePeriod, SyndicationUpdatePeriod.hourly);
expect(feed.syndication!.updateFrequency, 2);
expect(feed.syndication!.updateBase, DateTime.utc(2001, 1, 1, 12, 1));
expect(feed.items!.length, 1);
expect(feed.items!.first.title, 'XML: A Disruptive Technology');
expect(feed.items!.first.description,
'XML is placing increasingly heavy loads on the existing technical infrastructure of the Internet.');
expect(feed.items.first.link, 'http://c.moreover.com/click/here.pl?r123');
expect(feed.items!.first.link, 'http://c.moreover.com/click/here.pl?r123');
});
}

正在加载...
取消
保存