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