diff --git a/app/common/model-service.js b/app/common/model-service.js index 1360859..78afc09 100644 --- a/app/common/model-service.js +++ b/app/common/model-service.js @@ -138,4 +138,13 @@ angular.module('jamstash.model', ['jamstash.utils']) url = globals.BaseURL() + '/stream.view?' + globals.BaseParams() + '&id=' + song.streamId + '&salt=' + salt; return new model.Song(song.streamId, song.parent, track, title, artist, song.artistId, album, song.albumId, coverartthumb, coverartfull, song.duration, song.userRating, starred, suffix, specs, url, 0, description); }; + + this.mapPodcasts = function (episodes) { + var mappedEpisodes = []; + var mapEpisode = this.mapPodcast; + angular.forEach(episodes, function (episode) { + mappedEpisodes.push(mapEpisode(episode)); + }); + return mappedEpisodes; + }; }]); diff --git a/app/common/model-service_test.js b/app/common/model-service_test.js index 26c8eb0..c861ed0 100644 --- a/app/common/model-service_test.js +++ b/app/common/model-service_test.js @@ -20,11 +20,11 @@ describe("model service", function() { expect(model.displayName).toBe("Know Your Enemy - Ghost in the Shell - Stand Alone Complex OST 3 - Yoko Kanno"); }); - it("Given multiple songs, when I map them, it calls mapSong for each song", function() { + it("Given multiple songs, when I map them, then mapSong is called for each song", function() { var songs = [ - {id: 2912}, - {id: 1450}, - {id: 6663} + { id: 2912 }, + { id: 1450 }, + { id: 6663 } ]; spyOn(map, 'mapSong').and.callFake(function (song) { return song; }); @@ -35,4 +35,20 @@ describe("model service", function() { expect(map.mapSong).toHaveBeenCalledWith({id: 6663}); expect(result).toEqual(songs); }); + + it("Given multiple podcast episodes, when I map them, then mapPodcast is called for each episode", function() { + var episodes = [ + { id: 63 }, + { id: 24 }, + { id: 80 } + ]; + spyOn(map, 'mapPodcast').and.callFake(function (episode) { return episode; }); + + var result = map.mapPodcasts(episodes); + expect(map.mapPodcast.calls.count()).toEqual(3); + expect(map.mapPodcast).toHaveBeenCalledWith({ id: 63 }); + expect(map.mapPodcast).toHaveBeenCalledWith({ id: 24 }); + expect(map.mapPodcast).toHaveBeenCalledWith({ id: 80 }); + expect(result).toEqual(episodes); + }); }); diff --git a/app/subsonic/subsonic-service.js b/app/subsonic/subsonic-service.js index 2a2b940..ebd5802 100644 --- a/app/subsonic/subsonic-service.js +++ b/app/subsonic/subsonic-service.js @@ -427,7 +427,7 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util id: id } }).then(function (subsonicResponse) { - if(subsonicResponse.playlist.entry !== undefined && subsonicResponse.playlist.entry.length > 0) { + if (subsonicResponse.playlist.entry !== undefined && subsonicResponse.playlist.entry.length > 0) { return map.mapSongs(subsonicResponse.playlist.entry); } else { return $q.reject(exception); @@ -507,7 +507,11 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util getPodcasts: function () { var exception = {reason: 'No podcast found on the Subsonic server.'}; - var promise = this.subsonicRequest('getPodcasts.view') + var promise = this.subsonicRequest('getPodcasts.view', { + params: { + includeEpisodes: false + } + }) .then(function (subsonicResponse) { if (subsonicResponse.podcasts !== undefined && subsonicResponse.podcasts.channel !== undefined && subsonicResponse.podcasts.channel.length > 0) { return subsonicResponse.podcasts.channel; @@ -518,62 +522,31 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util return promise; }, - getPodcast: function (id, action) { - var deferred = $q.defer(); - content.selectedPodcast = id; - $.ajax({ - url: globals.BaseURL() + '/getPodcasts.view?' + globals.BaseParams(), - method: 'GET', - dataType: globals.settings.Protocol, - timeout: globals.settings.Timeout, - success: function (data) { - if (data["subsonic-response"].podcasts.channel !== undefined) { - var podcasts = []; - if (data["subsonic-response"].podcasts.channel.length > 0) { - podcasts = data["subsonic-response"].podcasts.channel; - } else { - podcasts[0] = data["subsonic-response"].podcasts.channel; - } - var items = []; - $.each(podcasts, function (i, item) { - if (item.id == id) { - items = item.episode; - } + getPodcast: function (id) { + var exception = {reason: 'This podcast was not found on the Subsonic server.'}; + var promise = this.subsonicRequest('getPodcasts.view', { + params: { + id: id, + includeEpisodes: true + } + }).then(function (subsonicResponse) { + var episodes = []; + if (subsonicResponse.podcasts.channel !== undefined && subsonicResponse.podcasts.channel.length > 0) { + var channel = subsonicResponse.podcasts.channel[0]; + if (channel !== null && channel.id === id) { + episodes = _(channel.episode).filter(function (episode) { + return episode.status === "completed"; }); - - if (typeof items != 'undefined') { - if (action == 'add') { - angular.forEach(items, function (item, key) { - if (item.status != "skipped") { - player.queue.push(map.mapPodcast(item)); - } - }); - notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); - } else if (action == 'play') { - player.queue = []; - angular.forEach(items, function (item, key) { - if (item.status != "skipped") { - player.queue.push(map.mapPodcast(item)); - } - }); - var next = player.queue[0]; - player.play(next); - notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); - } else { - content.album = []; - content.song = []; - angular.forEach(items, function (item, key) { - if (item.status != "skipped") { - content.song.push(map.mapPodcast(item)); - } - }); - } + if(episodes.length > 0) { + return map.mapPodcasts(episodes); + } else { + return $q.reject({reason: 'No downloaded episode found for this podcast. Please check the podcast settings.'}); } } - deferred.resolve(content); } + return $q.reject(exception); }); - return deferred.promise; + return promise; }, scrobble: function (song) { diff --git a/app/subsonic/subsonic-service_test.js b/app/subsonic/subsonic-service_test.js index 2fcdbca..36ea61e 100644 --- a/app/subsonic/subsonic-service_test.js +++ b/app/subsonic/subsonic-service_test.js @@ -28,6 +28,9 @@ describe("Subsonic service -", function() { $delegate.mapSong = function (argument) { return argument; }; + $delegate.mapPodcast = function (argument) { + return argument; + }; return $delegate; }); // Mock utils.getValue @@ -455,6 +458,7 @@ describe("Subsonic service -", function() { url = 'http://demo.subsonic.com/rest/getPlaylist.view?'+ 'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2'; }); + it("Given a playlist with 2 songs in it, when I get it, it returns the 2 songs of the playlist", function() { url = 'http://demo.subsonic.com/rest/getPlaylist.view?'+ 'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&id=9123'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2'; @@ -528,11 +532,12 @@ describe("Subsonic service -", function() { }); describe("When I load the podcasts,", function() { - var url; + var url; beforeEach(function() { - url = url = 'http://demo.subsonic.com/rest/getPodcasts.view?'+ - 'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2'; + url = 'http://demo.subsonic.com/rest/getPodcasts.view?'+ + 'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&includeEpisodes=false'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2'; }); + it("given that there were podcasts in the library, then a promise will be resolved with an array of podcasts", function() { response["subsonic-response"].podcasts = { channel: [ @@ -553,7 +558,7 @@ describe("Subsonic service -", function() { ]); }); - it("given that there weren't any podcast in the library, then a rejected promise with an error message will be returned", function() { + it("given that there weren't any podcast in the library, then a promise will be rejected with an error message", function() { response["subsonic-response"].podcasts = {}; mockBackend.expectJSONP(url).respond(JSON.stringify(response)); @@ -563,4 +568,84 @@ describe("Subsonic service -", function() { expect(promise).toBeRejectedWith({reason: 'No podcast found on the Subsonic server.'}); }); }); + + describe("When I load a podcast,", function() { + var url; + beforeEach(function() { + url = 'http://demo.subsonic.com/rest/getPodcasts.view?'+ + 'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&id=2695&includeEpisodes=true'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2'; + }); + + it("given a podcast id, then a promise will be resolved with an array of songs from all the non-skipped episodes of that podcast", function() { + response["subsonic-response"].podcasts = { + channel: [ + { + id: 2695, + episode: [ + { + id: 691, + status: "completed" + }, { + id: 771, + status: "skipped" + }, { + id: 227, + status: "completed" + } + ] + } + ] + }; + mockBackend.expectJSONP(url).respond(JSON.stringify(response)); + + var promise = subsonic.getPodcast(2695); + mockBackend.flush(); + + expect(promise).toBeResolvedWith([ + { + id: 691, + status: "completed" + }, { + id: 227, + status: "completed" + } + ]); + }); + + it("given that the podcast I wanted to get didn't exist in the library, when I try to get it, then a promise will be rejected with an error message", function() { + response["subsonic-response"].podcasts = {}; + mockBackend.expectJSONP(url).respond(JSON.stringify(response)); + + var promise = subsonic.getPodcast(2695); + mockBackend.flush(); + + expect(promise).toBeRejectedWith({reason: 'This podcast was not found on the Subsonic server.'}); + }); + + it("given that the podcast I wanted to get was empty (0 non-skipped episode in it), when I get it, then a promise will be rejected with an error message", function() { + response["subsonic-response"].podcasts = { + channel: [ + { + id: 2695, + episode: [ + { + id: 678, + status: "skipped" + }, + { + id: 972, + status: "skipped" + } + ] + } + ] + }; + mockBackend.expectJSONP(url).respond(JSON.stringify(response)); + + var promise = subsonic.getPodcast(2695); + mockBackend.flush(); + + expect(promise).toBeRejectedWith({reason: 'No downloaded episode found for this podcast. Please check the podcast settings.'}); + }); + }); }); diff --git a/app/subsonic/subsonic.html b/app/subsonic/subsonic.html index 03cb6f5..e803238 100644 --- a/app/subsonic/subsonic.html +++ b/app/subsonic/subsonic.html @@ -181,9 +181,10 @@