/*
* Jampa
* Copyright (C) 2008-2009 J. Devauchelle and contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package org.jampa.model.podcasts;
import java.io.File;
import java.io.FileWriter;
import java.io.StringReader;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.jampa.logging.Log;
import org.jampa.model.IAudioItem;
import org.jampa.model.IPlaylist;
import org.jampa.net.podcast.PodcastUpdater;
import org.jampa.utils.SystemUtils;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
public class Podcast implements IPlaylist {
private static final String PODCAST_TAG = "podcast";
private static final String NAME_TAG = "name";
private static final String TITLE_TAG = "title";
private static final String DESCRIPTION_TAG = "description";
private static final String URL_TAG = "url";
private static final String POSITION_TAG = "position";
private static final String DATA_TAG = "data";
private static final String ITEMS_TAG = "items";
private static final String PODCAST_ITEM_TAG = "podcastItem";
private static final String PODCAST_ITEM_TITLE_ATTR = "title";
private static final String PODCAST_ITEM_DATE_ATTR = "date";
private static final String PODCAST_ITEM_URL_ATTR = "url";
private static final String PODCAST_ITEM_DURATION_ATTR = "duration";
private static final String PODCAST_ITEM_READ_ATTR = "read";
private static final String VERSION_ATTRIBUTE = "version";
private static final String XMLNS_ATTRIBUTE = "xmlns";
private static final String XMLNS_ATTRIBUTE_VALUE = "http://jampa.sourceforge.net/podcast/";
private static final String CDATA_OPEN_TAG = "<![CDATA[";
private static final String CDATA_CLOSE_TAG = "]]>";
private String _fileName;
private String _name;
private String _title;
private String _description;
private String _url;
private int _position;
private String _data;
private List<PodcastItem> _items;
public Podcast() {
_name = "";
_title = "";
_url = "";
_description = "";
_data = "";
_items = new ArrayList<PodcastItem>();
}
public Podcast(Podcast oldPodcast) {
_name = oldPodcast._name;
_title = oldPodcast._title;
_url = oldPodcast._url;
_description = oldPodcast._description;
_data = oldPodcast._data;
_position = oldPodcast._position;
_items = oldPodcast._items;
}
public Podcast(String name, String title, String url, String description) {
this();
setName(name);
_title = title;
_url = url;
_description = description;
}
public void download() {
PodcastUpdater downloader = new PodcastUpdater(_url);
if (downloader.download()) {
_data = downloader.getData();
} else {
_data = "";
}
}
public void loadFromDisk() {
Document document = null;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
File file = new File(_fileName);
document = builder.parse(file);
Element podcast = document.getDocumentElement();
if ((podcast == null) ||
(!podcast.getNodeName().equals(PODCAST_TAG))) {
Log.getInstance(Podcast.class).info("Empty podcast: " + _fileName); //$NON-NLS-1$
return;
}
if ((podcast.hasAttribute(XMLNS_ATTRIBUTE)) &&
(podcast.getAttribute(XMLNS_ATTRIBUTE).equals(XMLNS_ATTRIBUTE_VALUE))) {
NodeList nameList = document.getElementsByTagName(NAME_TAG);
if ((nameList != null) &&
(nameList.getLength() > 0)) {
_name = URLDecoder.decode(nameList.item(0).getTextContent(), "UTF-8");
//_name = nameList.item(0).getTextContent();
}
NodeList titleList = document.getElementsByTagName(TITLE_TAG);
if ((titleList != null) &&
(titleList.getLength() > 0)) {
_title = URLDecoder.decode(titleList.item(0).getTextContent(), "UTF-8");
//_title = titleList.item(0).getTextContent();
}
NodeList descList = document.getElementsByTagName(DESCRIPTION_TAG);
if ((descList != null) &&
(descList.getLength() > 0)) {
_description = URLDecoder.decode(descList.item(0).getTextContent(), "UTF-8");
//_description = descList.item(0).getTextContent();
}
NodeList positionList = document.getElementsByTagName(POSITION_TAG);
if ((positionList != null) &&
(positionList.getLength() > 0)) {
try {
_position = Integer.parseInt(positionList.item(0).getTextContent());
} catch (NumberFormatException e) {
_position = Integer.MAX_VALUE;
}
}
NodeList urlList = document.getElementsByTagName(URL_TAG);
if ((urlList != null) &&
(urlList.getLength() > 0)) {
_url = URLDecoder.decode(urlList.item(0).getTextContent(), "UTF-8");
//_url = urlList.item(0).getTextContent();
}
NodeList dataList = document.getElementsByTagName(DATA_TAG);
if ((dataList != null) &&
(dataList.getLength() > 0)) {
_data = URLDecoder.decode(dataList.item(0).getTextContent().replace(CDATA_OPEN_TAG, "").replace(CDATA_CLOSE_TAG, ""), "UTF-8");
//_data = dataList.item(0).getTextContent().replace(CDATA_OPEN_TAG, "").replace(CDATA_CLOSE_TAG, "");
}
NodeList podcastItemsList = document.getElementsByTagName(ITEMS_TAG);
if ((podcastItemsList != null) &&
(podcastItemsList.getLength() > 0)) {
Node item;
PodcastItem podcastItem;
NodeList podcastItems = podcastItemsList.item(0).getChildNodes();
for (int i = 0; i < podcastItems.getLength(); i++) {
String title = "";
String date = "";
String url = "";
int duration = 0;
boolean read = false;
Node tmpNode;
item = podcastItems.item(i);
if (item.getNodeName().equals(PODCAST_ITEM_TAG)) {
NamedNodeMap itemMap = item.getAttributes();
title = URLDecoder.decode(itemMap.getNamedItem(PODCAST_ITEM_TITLE_ATTR).getTextContent(), "UTF-8");
date = URLDecoder.decode(itemMap.getNamedItem(PODCAST_ITEM_DATE_ATTR).getTextContent(), "UTF-8");
url = URLDecoder.decode(itemMap.getNamedItem(PODCAST_ITEM_URL_ATTR).getTextContent(), "UTF-8");
try {
duration = Integer.parseInt(itemMap.getNamedItem(PODCAST_ITEM_DURATION_ATTR).getTextContent());
} catch (Exception e) {
duration = 0;
}
tmpNode = itemMap.getNamedItem(PODCAST_ITEM_READ_ATTR);
if (tmpNode != null) {
read = Boolean.parseBoolean(tmpNode.getTextContent());
} else {
read = false;
}
podcastItem = new PodcastItem(this, title, date, url, duration, read);
_items.add(podcastItem);
}
}
}
} else {
Log.getInstance(Podcast.class).info("Invalid podcast (xmlns missmatch): " + _fileName); //$NON-NLS-1$
}
} catch (Exception e) {
Log.getInstance(Podcast.class).error("Error while reading podcast: " + _fileName + " (" + e.getMessage() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
public void writeToDisk() {
Document document = null;
try {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
document = builder.newDocument();
document.setXmlVersion("1.0"); //$NON-NLS-1$
document.setXmlStandalone(true);
Element podcastElement = document.createElement(PODCAST_TAG);
podcastElement.setAttribute(XMLNS_ATTRIBUTE, XMLNS_ATTRIBUTE_VALUE);
podcastElement.setAttribute(VERSION_ATTRIBUTE, "1");
Element nameElement = document.createElement(NAME_TAG);
nameElement.setTextContent(URLEncoder.encode(_name, "UTF-8"));
//nameElement.setTextContent(_name);
podcastElement.appendChild(nameElement);
Element titleElement = document.createElement(TITLE_TAG);
titleElement.setTextContent(URLEncoder.encode(_title, "UTF-8"));
//titleElement.setTextContent(_title);
podcastElement.appendChild(titleElement);
Element descElement = document.createElement(DESCRIPTION_TAG);
descElement.setTextContent(URLEncoder.encode(_description, "UTF-8"));
//descElement.setTextContent(_description);
podcastElement.appendChild(descElement);
Element positionElement = document.createElement(POSITION_TAG);
positionElement.setTextContent(URLEncoder.encode(Integer.toString(_position), "UTF-8"));
podcastElement.appendChild(positionElement);
Element urlElement = document.createElement(URL_TAG);
urlElement.setTextContent(URLEncoder.encode(_url, "UTF-8"));
//urlElement.setTextContent(_url);
podcastElement.appendChild(urlElement);
Element dataElement = document.createElement(DATA_TAG);
CDATASection dataSection = document.createCDATASection(DATA_TAG);
dataSection.setData(URLEncoder.encode(_data, "UTF-8"));
//dataSection.setData(_data);
dataElement.appendChild(dataSection);
podcastElement.appendChild(dataElement);
Element podcastItems = document.createElement(ITEMS_TAG);
Element podcastItem;
PodcastItem item;
Iterator<PodcastItem> iter = _items.iterator();
while (iter.hasNext()) {
item = iter.next();
podcastItem = document.createElement(PODCAST_ITEM_TAG);
podcastItem.setAttribute(PODCAST_ITEM_TITLE_ATTR, URLEncoder.encode(item.getTitle(), "UTF-8"));
podcastItem.setAttribute(PODCAST_ITEM_DATE_ATTR, URLEncoder.encode(item.getDate(), "UTF-8"));
podcastItem.setAttribute(PODCAST_ITEM_DURATION_ATTR, URLEncoder.encode(Integer.toString(item.getDuration()), "UTF-8"));
podcastItem.setAttribute(PODCAST_ITEM_URL_ATTR, URLEncoder.encode(item.getUrl(), "UTF-8"));
podcastItem.setAttribute(PODCAST_ITEM_READ_ATTR, Boolean.toString(item.isRead()));
podcastItems.appendChild(podcastItem);
}
podcastElement.appendChild(podcastItems);
document.appendChild(podcastElement);
DOMSource domSource = new DOMSource(document);
FileWriter writer = new FileWriter(new File(_fileName));
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
//transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$
transformer.transform(domSource, result);
writer.flush();
writer.close();
} catch (Exception e) {
Log.getInstance(Podcast.class).error("Error while writting file: " + _fileName + " (" + e.getMessage() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
private PodcastItem getOldPodcastItem(List<PodcastItem> list, String url) {
PodcastItem result = null;
PodcastItem item = null;
boolean stop = false;
Iterator<PodcastItem> iter = list.iterator();
while ((!stop) &&
(iter.hasNext())) {
item = iter.next();
if (item.getUrl().equals(url)) {
stop = true;
}
}
if (stop) {
result = item;
}
return result;
}
private boolean doesItemExistsInList(List<PodcastItem> list, PodcastItem item) {
boolean result = false;
PodcastItem iterItem;
Iterator<PodcastItem> iter = list.iterator();
while ((iter.hasNext()) &&
(!result)) {
iterItem = iter.next();
if (iterItem.getUrl().equals(item.getUrl())) {
result = true;
}
}
return result;
}
private int countNewItems(List<PodcastItem> oldList) {
int result = 0;
PodcastItem item;
Iterator<PodcastItem> iter = _items.iterator();
while (iter.hasNext()) {
item = iter.next();
if (!doesItemExistsInList(oldList, item)) {
result++;
}
}
return result;
}
public int parseData() {
List<PodcastItem> oldList = _items;
_items = new ArrayList<PodcastItem>();
try {
if ((_data != null) &&
(!_data.isEmpty())) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(_data)));
Element rss = document.getDocumentElement();
if ((rss == null) ||
(!rss.getNodeName().equals(PodcastRssConstants.RSS_TAG))) {
return 0;
}
NodeList channelNodeList = rss.getElementsByTagName(PodcastRssConstants.CHANNEL_TAG);
if ((channelNodeList != null) &&
(channelNodeList.getLength() > 0)) {
Node channelNode = channelNodeList.item(0);
NodeList channelItems = channelNode.getChildNodes();
for (int i = 0; i < channelItems.getLength(); i++) {
if (channelItems.item(i).getNodeName().equals(PodcastRssConstants.CHANNEL_TITLE_TAG)) {
_title = channelItems.item(i).getTextContent();
}
if (channelItems.item(i).getNodeName().equals(PodcastRssConstants.CHANNEL_DESC_TAG)) {
_description = channelItems.item(i).getTextContent();
}
// TODO: read all
if (channelItems.item(i).getNodeName().equals(PodcastRssConstants.ITEM_TAG)) {
NodeList itemsList = channelItems.item(i).getChildNodes();
String title = "";
String date = "";
String url = "";
String type = "";
PodcastItem oldItem = null;
int duration = 0;
for (int j = 0; j < itemsList.getLength(); j++) {
if (itemsList.item(j).getNodeName().equals(PodcastRssConstants.ITEM_TITLE_TAG)) {
title = itemsList.item(j).getTextContent();
} else if (itemsList.item(j).getNodeName().equals(PodcastRssConstants.ITEM_DATE_TAG)) {
date = itemsList.item(j).getTextContent();
} else if (itemsList.item(j).getNodeName().equals(PodcastRssConstants.ITEM_ENCLOSURE_TAG)) {
NamedNodeMap enclosureMap = itemsList.item(j).getAttributes();
type = enclosureMap.getNamedItem(PodcastRssConstants.ITEM_ENCLOSURE_TYPE).getTextContent();
if ((type != null) &&
(!type.isEmpty()) &&
(type.equals(PodcastRssConstants.ITEM_ENCLOSURE_TYPE_VALUE))) {
url = enclosureMap.getNamedItem(PodcastRssConstants.ITEM_ENCLOSURE_URL).getTextContent();
duration = Integer.parseInt(enclosureMap.getNamedItem(PodcastRssConstants.ITEM_ENCLOSURE_LENGTH).getTextContent());
}
}
}
if ((url != null) &&
(!url.isEmpty())) {
PodcastItem newItem;
oldItem = getOldPodcastItem(oldList, url);
if (oldItem != null) {
newItem = oldItem;
newItem.setParent(this);
newItem.setTitle(title);
newItem.setDate(date);
newItem.setUrl(url);
newItem.setDuration(duration);
} else {
newItem = new PodcastItem(this, title, date, url, duration, false);
}
_items.add(newItem);
}
}
}
}
}
} catch (Exception e) {
Log.getInstance(Podcast.class).error("Error while reading data: (" + e.getMessage() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
return 0;
}
return countNewItems(oldList);
}
public void deletePodcast() {
File file = new File(_fileName);
if (file.exists())
file.delete();
}
public void setName(String value) {
_name = value;
_fileName = SystemUtils.podcastDirectory + _name + ".xml";
}
public String getName() {
return _name;
}
public String getTitle() {
return _title;
}
public String getDescription() {
return _description;
}
public String getUrl() {
return _url;
}
public void setUrl(String value) {
_url = value;
}
public int getPosition() {
return _position;
}
public void setPosition(int value) {
_position = value;
}
public void setData(String value) {
_data = value;
}
public List<PodcastItem> getPodcastList() {
return _items;
}
@Override
public IAudioItem getAudioItemByIndex(int index) {
return _items.get(index);
}
@Override
public int getAudioItemIndex(IAudioItem item) {
return _items.indexOf(item);
}
@Override
public int getPlaylistMaxIndex() {
return _items.size() - 1;
}
public int getItemsCount() {
return _items.size();
}
public int getUnreadItemsCount() {
int result = 0;
Iterator<PodcastItem> iter = _items.iterator();
while (iter.hasNext()) {
if (!iter.next().isRead()) {
result++;
}
}
return result;
}
public boolean hasAudioItemPlaying() {
boolean result = false;
for (int i = 0; i < _items.size(); i++) {
if (_items.get(i).isBoPlaying()) {
result = true;
break;
}
}
return result;
}
public void setReadStatus(boolean newStatus) {
Iterator<PodcastItem> iter = _items.iterator();
while (iter.hasNext()) {
iter.next().setRead(newStatus);
}
}
}