Kaynağa Gözat

RSS feed add element with "itunes:" prefix (for podcast)

master
magodo 7 yıl önce
ebeveyn
işleme
d6f4906b73
9 değiştirilmiş dosya ile 262 ekleme ve 2 silme
  1. +5
    -0
      lib/domain/rss_feed.dart
  2. +5
    -0
      lib/domain/rss_item.dart
  3. +76
    -0
      lib/domain/rss_item_itunes.dart
  4. +65
    -0
      lib/domain/rss_itunes.dart
  5. +24
    -0
      lib/domain/rss_itunes_category.dart
  6. +14
    -0
      lib/domain/rss_itunes_image.dart
  7. +2
    -2
      lib/util/helpers.dart
  8. +27
    -0
      test/rss_test.dart
  9. +44
    -0
      test/xml/RSS-Itunes.xml

+ 5
- 0
lib/domain/rss_feed.dart Dosyayı Görüntüle

@@ -8,6 +8,8 @@ import 'package:webfeed/domain/rss_item.dart';
import 'package:webfeed/util/helpers.dart';
import 'package:xml/xml.dart';

import 'rss_itunes.dart';

class RssFeed {
final String title;
final String author;
@@ -30,6 +32,7 @@ class RssFeed {
final String webMaster;
final int ttl;
final DublinCore dc;
final RssItunes itunes;

RssFeed({
this.title,
@@ -52,6 +55,7 @@ class RssFeed {
this.webMaster,
this.ttl,
this.dc,
this.itunes,
});

factory RssFeed.parse(String xmlString) {
@@ -98,6 +102,7 @@ class RssFeed {
webMaster: findElementOrNull(channelElement, "webMaster")?.text,
ttl: int.tryParse(findElementOrNull(channelElement, "ttl")?.text ?? "0"),
dc: DublinCore.parse(channelElement),
itunes: RssItunes.parse(channelElement),
);
}
}

+ 5
- 0
lib/domain/rss_item.dart Dosyayı Görüntüle

@@ -7,6 +7,8 @@ import 'package:webfeed/domain/rss_source.dart';
import 'package:webfeed/util/helpers.dart';
import 'package:xml/xml.dart';

import 'rss_item_itunes.dart';

class RssItem {
final String title;
final String description;
@@ -22,6 +24,7 @@ class RssItem {
final Media media;
final RssEnclosure enclosure;
final DublinCore dc;
final RssItemItunes itunes;

RssItem({
this.title,
@@ -37,6 +40,7 @@ class RssItem {
this.media,
this.enclosure,
this.dc,
this.itunes,
});

factory RssItem.parse(XmlElement element) {
@@ -56,6 +60,7 @@ class RssItem {
media: Media.parse(element),
enclosure: RssEnclosure.parse(findElementOrNull(element, "enclosure")),
dc: DublinCore.parse(element),
itunes: RssItemItunes.parse(element),
);
}
}

+ 76
- 0
lib/domain/rss_item_itunes.dart Dosyayı Görüntüle

@@ -0,0 +1,76 @@
import 'package:webfeed/util/helpers.dart';
import 'package:xml/xml.dart';

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

class RssItemItunes {
final int episode;
final Duration duration;
final String episodeType;
final String author;
final String summary;
final bool explicit;
final String subtitle;
final List<String> keywords;
final RssItunesImage image;
final RssItunesCategory category;

RssItemItunes({
this.episode,
this.duration,
this.episodeType,
this.author,
this.summary,
this.explicit,
this.subtitle,
this.keywords,
this.image,
this.category,
});

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

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

Duration parseDuration(String s) {
var hours = 0;
var minutes = 0;
var seconds = 0;
var parts = s.split(':');
if (parts.length > 2) {
hours = int.parse(parts[parts.length - 3]);
}
if (parts.length > 1) {
minutes = int.parse(parts[parts.length - 2]);
}
seconds = int.parse(parts[parts.length - 1]);
return Duration(
hours: hours,
minutes: minutes,
seconds: seconds,
);
}

+ 65
- 0
lib/domain/rss_itunes.dart Dosyayı Görüntüle

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

import 'package:webfeed/util/helpers.dart';

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

class RssItunes {
final String author;
final String summary;
final bool explicit;
final String subtitle;
final RssItunesOwner owner;
final List<String> keywords;
final RssItunesImage image;
final RssItunesCategory category;

RssItunes({
this.author,
this.summary,
this.explicit,
this.subtitle,
this.owner,
this.keywords,
this.image,
this.category,
});

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

return RssItunes(
author: findElementOrNull(element, "itunes:author")?.text?.trim(),
summary: findElementOrNull(element, "itunes:summary")?.text?.trim(),
explicit: explicitStr == null
? null
: explicitStr == "yes" || explicitStr == "true",
subtitle: findElementOrNull(element, "itunes:subtitle")?.text?.trim(),
owner: RssItunesOwner.parse(findElementOrNull(element, "itunes:owner")),
keywords: findElementOrNull(element, "itunes:keywords")?.text?.split(",")?.map((keyword) => keyword.trim())?.toList(),
image: RssItunesImage.parse(findElementOrNull(element, "itunes:image")),
category: RssItunesCategory.parse(
findElementOrNull(element, "itunes:category")),
);
}
}

class RssItunesOwner {
final String name;
final String email;

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

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

+ 24
- 0
lib/domain/rss_itunes_category.dart Dosyayı Görüntüle

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

class RssItunesCategory {
final String category;
final List<String> subCategories;

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

factory RssItunesCategory.parse(XmlElement element) {
if (element == null) return null;

Iterable<XmlElement> subCategories;
try {
subCategories = element.findAllElements("itunes:category");
} on StateError {
subCategories = null;
}
return RssItunesCategory(
category: element.getAttribute("text")?.trim(),
subCategories:
subCategories?.map((ele) => ele.getAttribute("text")?.trim())?.toList(),
);
}
}

+ 14
- 0
lib/domain/rss_itunes_image.dart Dosyayı Görüntüle

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

class RssItunesImage {
final String href;

RssItunesImage({this.href});

factory RssItunesImage.parse(XmlElement element) {
if (element == null) return null;
return RssItunesImage(
href: element.getAttribute("href")?.trim(),
);
}
}

+ 2
- 2
lib/util/helpers.dart Dosyayı Görüntüle

@@ -2,9 +2,9 @@ import 'dart:core';

import 'package:xml/xml.dart';

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


+ 27
- 0
test/rss_test.dart Dosyayı Görüntüle

@@ -295,4 +295,31 @@ void main() {

expect(feed.items.first.content, null);
});

test("parse RSS-Itunes.xml", () {
var xmlString = File("test/xml/RSS-Itunes.xml").readAsStringSync();

var feed = RssFeed.parse(xmlString);

expect(feed.itunes.author, "Changelog Media");
expect(feed.itunes.summary, "Foo");
expect(feed.itunes.explicit, false);
expect(feed.itunes.image.href, "https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357");
expect(feed.itunes.keywords, "go,golang,open source,software,development".split(","));
expect(feed.itunes.owner.name, "Changelog Media");
expect(feed.itunes.owner.email, "editors@changelog.com");
expect(feed.itunes.category.category, "Technology");
expect(feed.itunes.category.subCategories, ["Software How-To", "Tech News"]);

var item = feed.items[0];
expect(item.itunes.episodeType, "full");
expect(item.itunes.episode, 1);
expect(item.itunes.image.href, "https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357");
expect(item.itunes.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, "Erik St. Martin, Carlisia Pinto, and Brian Ketelsen");
});
}

+ 44
- 0
test/xml/RSS-Itunes.xml Dosyayı Görüntüle

@@ -0,0 +1,44 @@
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0" nighteye="disabled">
<channel>
<title>Go Time</title>
<copyright>All rights reserved</copyright>
<link>https://changelog.com/gotime</link>
<atom:link href="https://changelog.com/gotime/feed" rel="self" type="application/rss+xml"/>
<atom:link href="https://changelog.com/gotime/feed" rel="first" type="application/rss+xml"/>
<atom:link href="https://changelog.com/gotime/feed?page=1" rel="last" type="application/rss+xml"/>
<atom:link href="https://changelog.com/gotime" rel="alternate" type="text/html"/>
<language>en-us</language>
<description>
A diverse panel and special guests discuss cloud infrastructure, distributed systems, microservices, Kubernetes, Docker…oh and also Go! This show records LIVE every Thursday at 3pm US Eastern. A diverse panel and special guests discuss cloud infrastructure, distributed systems, microservices, Kubernetes, Docker…oh and also Go!
</description>
<itunes:author>Changelog Media</itunes:author>
<itunes:summary>Foo</itunes:summary>
<itunes:explicit>no</itunes:explicit>
<itunes:image href="https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357"/>
<itunes:keywords>go, golang, open source, software, development</itunes:keywords>
<itunes:owner>
<itunes:name>Changelog Media</itunes:name>
<itunes:email>editors@changelog.com</itunes:email>
</itunes:owner>
<itunes:category text="Technology">
<itunes:category text="Software How-To"/>
<itunes:category text="Tech News"/>
</itunes:category>
<item>
<itunes:episodeType>full</itunes:episodeType>
<itunes:episode>1</itunes:episode>
<itunes:image href="https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357"/>
<itunes:duration>32:30</itunes:duration>
<itunes:explicit>no</itunes:explicit>
<itunes:keywords>go, golang, open source, software, development</itunes:keywords>
<itunes:subtitle>with Erik, Carlisia, and Brian</itunes:subtitle>
<itunes:summary>Foo</itunes:summary>
<dc:creator>
Erik St. Martin, Carlisia Pinto, and Brian Ketelsen
</dc:creator>
<itunes:author>
Erik St. Martin, Carlisia Pinto, and Brian Ketelsen
</itunes:author>
</item>
</channel>
</rss>

Yükleniyor…
İptal
Kaydet