317 lines
11 KiB
JavaScript
317 lines
11 KiB
JavaScript
'use strict';
|
|
|
|
angular.module('podcasts.models', ['podcasts.database', 'podcasts.utilities'])
|
|
.service('feeds', ['$log', '$q', 'db', 'downloaderBackend', 'xmlParser', 'feedItems', 'utilities', '$rootScope', function($log, $q, db, downloaderBackend, xmlParser, feedItems, utilities, $rootScope) {
|
|
var feeds = [];
|
|
|
|
function _add(url) {
|
|
var feedService = this;
|
|
var finishSave = function(newFeed) {
|
|
var promise = db.put("feed", newFeed)
|
|
.then(function(key) {
|
|
newFeed.id = key;
|
|
|
|
feedService.feeds.push(newFeed);
|
|
feedService.downloadItems(newFeed);
|
|
});
|
|
};
|
|
|
|
var cleanedUrl = utilities.clean_url(url);
|
|
|
|
var promise = downloaderBackend.downloadXml(cleanedUrl);
|
|
promise.then(function(xml) {
|
|
var channelChildren = xml.find('channel').children(),
|
|
newFeed = {},
|
|
imageUrl;
|
|
|
|
newFeed.url = cleanedUrl;
|
|
newFeed.title = xml.find('title').text();
|
|
newFeed.summary = xml.find('description').text();
|
|
newFeed.nrQueueItems = 1;
|
|
|
|
angular.forEach(channelChildren, function(value, key) {
|
|
if ('itunes:image' === angular.element(value)[0].nodeName.toLowerCase()) {
|
|
imageUrl = angular.element(value).attr('href');
|
|
}
|
|
|
|
if ('itunes:author' === angular.element(value)[0].nodeName.toLowerCase()) {
|
|
newFeed.author = angular.element(value).text();
|
|
}
|
|
});
|
|
|
|
|
|
var file = downloaderBackend.downloadFile(imageUrl);
|
|
file.then(function(fileBlob) {
|
|
newFeed.image = fileBlob;
|
|
finishSave(newFeed);
|
|
}, function() {
|
|
finishSave(newFeed);
|
|
});
|
|
}, function() {
|
|
console.warn('Could not fetch XML for feed, adding just URL for now');
|
|
var newFeed = {};
|
|
newFeed.url = cleanedUrl;
|
|
|
|
finishSave(newFeed);
|
|
});
|
|
}
|
|
|
|
function _get(id) {
|
|
id = parseInt(id, 10);
|
|
var deferred = $q.defer();
|
|
|
|
db.getOne("feed", id)
|
|
.then(function(feed) {
|
|
if (typeof feed.image === 'string') {
|
|
feed.image = new Blob([feed.image]);
|
|
}
|
|
|
|
feed.items = [];
|
|
db.get("feedItem", IDBKeyRange.only(feed.id), "ixFeedId")
|
|
.then(function(results) {
|
|
angular.forEach(results, function(item) {
|
|
feed.items.push(item);
|
|
});
|
|
|
|
$rootScope.$apply(deferred.resolve(feed));
|
|
});
|
|
}, function(value) {
|
|
deferred.reject();
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
function _list($scope) {
|
|
var feeds = this.feeds,
|
|
promise = db.get("feed");
|
|
|
|
promise.then(function(results) {
|
|
angular.forEach(results, function(item) {
|
|
if (typeof item.image === 'string') {
|
|
item.image = new Blob(
|
|
[item.image],
|
|
{type: 'application/octet-stream'}
|
|
);
|
|
}
|
|
|
|
feeds.push(item);
|
|
});
|
|
|
|
$scope.$apply();
|
|
});
|
|
}
|
|
|
|
function _delete(id) {
|
|
$log.info('Deleting feed with ID ' + id);
|
|
feedItems.deleteByFeedId(id);
|
|
db.delete("feed", id);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param updateStatus function that gets called for each item it goes through
|
|
* Takes the feedItem as the argument
|
|
*/
|
|
function _downloadAllItems(updateStatus) {
|
|
var feedService = this,
|
|
feeds = db.get("feed");
|
|
|
|
feeds.then(function(results) {
|
|
angular.forEach(results, function(item) {
|
|
feedService.downloadItems(item, updateStatus);
|
|
});
|
|
|
|
updateStatus();
|
|
});
|
|
}
|
|
|
|
function _downloadItems(feedItem, updateStatus) {
|
|
var promise = downloaderBackend.downloadXml(feedItem.url),
|
|
feedObjects = [];
|
|
|
|
promise.then(function(data) {
|
|
angular.forEach(
|
|
data.find('item'),
|
|
function(element, index) {
|
|
if (index < 3) { // TODO: this should be a global setting and/or a per-feed setting
|
|
feedObjects.push(feedItems.getFeedItemFromXml(element))
|
|
}
|
|
}
|
|
);
|
|
|
|
angular.forEach(feedObjects, function(feedObject, index) {
|
|
feedObject.feedId = feedItem.id;
|
|
if (0 === index) {
|
|
feedObject.queued = 1;
|
|
} else {
|
|
feedObject.queued = 0;
|
|
}
|
|
|
|
feedItems.add(feedObject)
|
|
.then(function(item) {
|
|
if (typeof updateStatus === 'function') {
|
|
updateStatus(item, feedItem);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
return {
|
|
add: _add,
|
|
get: _get,
|
|
list: _list,
|
|
delete: _delete,
|
|
downloadAllItems: _downloadAllItems,
|
|
downloadItems: _downloadItems
|
|
};
|
|
}])
|
|
.service('feedItems', ['db', '$q', '$rootScope', function(db, $q, $rootScope) {
|
|
function _get(id, onSuccess, onFailure) {
|
|
db.getOne("feedItem", id)
|
|
.then(function(value) {
|
|
onSuccess(value);
|
|
}, function() {
|
|
onFailure();
|
|
});
|
|
}
|
|
|
|
function _getFeedItemFromXml(xml) {
|
|
var newFeedItem = {},
|
|
searchableXml = angular.element(xml);
|
|
|
|
newFeedItem.guid = searchableXml.find('guid').text();
|
|
newFeedItem.title = searchableXml.find('title').text();
|
|
newFeedItem.link = searchableXml.find('link').text();
|
|
newFeedItem.date = Date.parse(searchableXml.find('pubDate').text());
|
|
newFeedItem.description = searchableXml.find('description').text();
|
|
newFeedItem.audioUrl = searchableXml.find('enclosure').attr('url');
|
|
|
|
return newFeedItem;
|
|
}
|
|
|
|
function _delete(feedItemId) {
|
|
db.delete("feedItem", feedItemId);
|
|
}
|
|
|
|
function _deleteByFeedId(feedId) {
|
|
db.get("feedItem", IDBKeyRange.only(feedId), "ixFeedId")
|
|
.then(function(results) {
|
|
angular.forEach(results, function(item) {
|
|
this._delete(item.id);
|
|
})
|
|
});
|
|
}
|
|
|
|
function _add(object) {
|
|
var deferred = $q.defer(),
|
|
newFeedItem = {
|
|
guid: object.guid,
|
|
feedId: object.feedId,
|
|
title: object.title,
|
|
link: object.link,
|
|
date: object.date,
|
|
description: object.description,
|
|
audioUrl: object.audioUrl,
|
|
queued: object.queued
|
|
},
|
|
promise = db.put("feedItem", newFeedItem);
|
|
|
|
promise.then(function() {
|
|
deferred.resolve(newFeedItem);
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
function _save(object) {
|
|
db.put("feedItem", object)
|
|
.then(function() {
|
|
$rootScope.$broadcast('queueListRefresh');
|
|
});
|
|
}
|
|
|
|
function _getNextInQueue(feedItem) {
|
|
var tempQueueList = { queue: [], addToQueue: function(item) { this.queue.push(item); } },
|
|
deferred = $q.defer();
|
|
|
|
this.listQueue(tempQueueList, function() {
|
|
var returnNextValue = false,
|
|
didReturnValue = false;
|
|
angular.forEach(tempQueueList.queue, function(value, key) {
|
|
if (returnNextValue) {
|
|
deferred.resolve(value);
|
|
returnNextValue = false;
|
|
didReturnValue = true;
|
|
}
|
|
|
|
if (feedItem.id === value.id) {
|
|
returnNextValue = true;
|
|
}
|
|
});
|
|
|
|
if (!didReturnValue) {
|
|
deferred.reject('Could not find next queue item');
|
|
}
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
/**
|
|
* Not sure if this is still used
|
|
* @param queueList
|
|
* @param done
|
|
*/
|
|
function _listQueue(queueList, done) {
|
|
db.get("feedItem", IDBKeyRange.only(1), "ixQueued")
|
|
.then(function(results) {
|
|
angular.forEach(results, function(item) {
|
|
// This additional check is necessary, since the index doesn't seem to always catch correctly
|
|
if (item.queued) {
|
|
queueList.addToQueue(item);
|
|
}
|
|
});
|
|
|
|
done();
|
|
});
|
|
}
|
|
|
|
function _unQueue(feedItemId) {
|
|
var feedItem = _get(feedItemId, function(feedItem) {
|
|
feedItem.queued = 0;
|
|
|
|
var promise = db.put("feedItem", feedItem)
|
|
.then(function() {
|
|
$rootScope.$broadcast('queueListRefresh');
|
|
});
|
|
});
|
|
}
|
|
|
|
function _addToQueue(feedItemId) {
|
|
var feedItem = _get(feedItemId, function(feedItem) {
|
|
feedItem.queued = 1;
|
|
|
|
db.put("feedItem", feedItem)
|
|
.then(function() {
|
|
$rootScope.$broadcast('queueListRefresh');
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
return {
|
|
get: _get,
|
|
getFeedItemFromXml: _getFeedItemFromXml,
|
|
delete: _delete,
|
|
deleteByFeedId: _deleteByFeedId,
|
|
add: _add,
|
|
save: _save,
|
|
getNextInQueue: _getNextInQueue,
|
|
listQueue: _listQueue,
|
|
unQueue: _unQueue,
|
|
addToQueue: _addToQueue
|
|
}
|
|
}])
|
|
;
|