Podcast RSS/Atom Feed Viewer using JavaFX
PodcastFeedViewer is a JavaFX application that plays medias from Podcast Feeds ( RSS or Atom feeds). Features of the PodcastFeedViewer are
- Stores all the valid podcast feed urls using Storage API
- Navigation between peristed podcast feeds
- Navigation between the medias from the podcast feed
- Accepts RSS and Atom feeds
- User can delete the stored feeds using delete button
How to use
Press on Add button and enter the FeedURL text box .(Clicking Add button will make textbox editable). Press on Start Polling Button. You can navigate between the medias with in the feed using the lower left and right arrow buttons. Pressing delete button will remove the currently playing feed url. Pressing add button will add one more feed url to the list and all valid feed urls will be stored locally. You can navigate between feed urls using upper left and right arrows. During the startup of application all the stored feed urls will be loaded.
Podcasts are obtained from Atom or Rss feeds using the JavaFX Feed APIs (javafx.data.feed.atom.AtomTask or javafx.data.feed.rss.RssTask). Medias from RSS feeds are obtained using callback method onEntry(item:Item) by reading the enclosure object(javafx.data.rss.Enclosure) in Item(javafx.data.feed.rss.Item) and in Atom it is obtained using callback method onE link (javafx.data.feed.atom.Link).Medias are displayed using MediaPlayer (javafx.scene.media.MediaPlayer)
Understanding the Code
Entered FeedURL is send to the FeedValidator which parses and validates the feed. if the input starts with feed or rss tag it is a valid feed.
...
override var onInput = function(input:InputStream){
var parser = PullParser {input:input;}
parser.forward(2);
var e = parser.event;
if ( e.qname.name == "rss" ) {
type = PodcastParser.RSS;
}else if (e.qname.name == "feed" ){
type = PodcastParser.ATOM;
}
input.close();
podcastParser.type = type;
}
...
FeedTask is the base class for AtomTask and RssTask. Here is the code which start the polling.
...
public var feedTask:FeedTask ;
...
public var type:Integer on replace {
if ( type == ATOM ){
feedTask = AtomTask{
location:location;
interval:Main.feedInterval;
onFeed: function(feed:Feed){
Main.log("Feed - {feed}");
podcastFeed.title = feed.title.text;
}
onEntry: function(entry:Entry){
Main.log(" Entry - {entry}");
var length:Long ;
var mediaURL = null;
var apptype = null;
var media:PodcastMedia = null;
for (link in entry.links){
Main.log("Link - {link}");
if ( link.type != null and
(link.type.trim().startsWith("audio") or
link.type.trim().startsWith("video")) ) {
Main.log("link.type - {link.type}");
apptype = link.type;
}else {
continue;
}
Main.log("link.length - {link.length}");
try {
if ( link.length != null and
not link.length.trim().equals("")) {
length = Long.parseLong(link.length);
}else {
length = 0;
}
}catch (e){ length = 0}
if ( link.href != null ){
Main.log("link.href - {link.href}");
mediaURL = link.href;
insert
(media = PodcastMedia{
length:length
mediaURL:mediaURL
type:apptype })
into podcastFeed.medias;
Main.log ("Adding Media {mediaURL}");
}
if ( link.title != null and link.title != "") {
media.title = link.title;
}
}
if ( media != null ){
if ( media.title == null ){
media.title = entry.title.text;
}
media.data = entry.summary.text;
}
}
}
}else if ( type == RSS ) {
feedTask = RssTask{
location:location
interval:Main.feedInterval
onChannel:function(channel:Channel){
Main.log("Channel - {channel}");
podcastFeed.title = channel.title;
podcastFeed.description = channel.description;
}
onItem:function(item:Item){
Main.log("item - {item}");
var media:PodcastMedia;
var enclosure = item.enclosure;
if ( enclosure != null and
(enclosure.type.startsWith("audio") or
enclosure.type.startsWith("video") ) and
enclosure.url.startsWith("http")) {
media = PodcastMedia{
mediaURL: enclosure.url
type: enclosure.type
length: enclosure.length
}
}else return;
media.title = item.title;
media.data = item.description;
insert media into podcastFeed.medias;
}
}
}
if ( feedTask != null ){
//Create podcast feed for Atom/RSS.
podcastFeed = PodcastFeed{location:location feedTask:feedTask}
feedTask.start();
feedTask.onDone = function(){
isDone = true;
}
....
if the feed contains medias feed location is persisted using storage apis
public function store(location:String){
Main.log("Adding {location} to persistence store");
var loc = "{location}\n";
var storage = Storage{
source: Main.applicationTitle
};
//Append
var outputStream = storage.resource.openOutputStream(false);
outputStream.write(loc.getBytes());
outputStream.close();
}
During the starup of the application all the persisted feed urls will be loaded back
...
var locactions:String[];
var storage = Storage {
source: Main.applicationTitle
};
var inputStream = storage.resource.openInputStream();
if ( inputStream == null ){
Main.log("There is no persisted data");
return;
}
var stream = new java.io.ByteArrayOutputStream();
var char = -1;
while ((
char = inputStream.read()) != - 1){
stream.write(char);
}
stream.close();
var line = stream.toString();
...