Refactors subsonic's getSongs(), adds a recursiveGetSongs()
So that the separation between subsonic-service and subsonic-controller is clear: the service makes requests and deals with $http, the controller displays, add to the player queue, etc. I've also added a recursive version of getSongs() which enables us to play all the songs in a directory recursively, e.g. all the songs of an artist regardless of their album. - Adds unit-tests to the controller and service for getSongs() and recursiveGetSongs()
This commit is contained in:
parent
7be524af00
commit
ee25f7046e
5 changed files with 326 additions and 97 deletions
|
@ -138,7 +138,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
}).then(function (subsonicResponse) {
|
||||
if(subsonicResponse.indexes !== undefined && (subsonicResponse.indexes.index !== undefined || subsonicResponse.indexes.shortcut !== undefined)) {
|
||||
// Make sure shortcut, index and each index's artist are arrays
|
||||
// because Madsonic will return objects and not arrays if there is only 1 artist
|
||||
// because Madsonic will return an object when there's only one element
|
||||
var formattedResponse = {};
|
||||
formattedResponse.shortcut = [].concat(subsonicResponse.indexes.shortcut);
|
||||
formattedResponse.index = [].concat(subsonicResponse.indexes.index);
|
||||
|
@ -164,6 +164,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
params: params
|
||||
}).then(function (subsonicResponse) {
|
||||
if(subsonicResponse.directory.child !== undefined) {
|
||||
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
|
||||
var childArray = [].concat(subsonicResponse.directory.child);
|
||||
if (childArray.length > 0) {
|
||||
content.song = [];
|
||||
|
@ -203,6 +204,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
params: params
|
||||
}).then(function (subsonicResponse) {
|
||||
if(subsonicResponse.albumList.album !== undefined) {
|
||||
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
|
||||
var albumArray = [].concat(subsonicResponse.albumList.album);
|
||||
if (albumArray.length > 0) {
|
||||
content.song = [];
|
||||
|
@ -253,52 +255,25 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
getSongs: function (id, action) {
|
||||
var exception = {reason: 'No songs found on the Subsonic server.'};
|
||||
|
||||
getSongs: function (id) {
|
||||
var exception = {reason: 'This directory is empty.'};
|
||||
var promise = subsonicService.subsonicRequest('getMusicDirectory.view', {
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
}).then(function (subsonicResponse) {
|
||||
if(subsonicResponse.directory.child !== undefined) {
|
||||
var items = [].concat(subsonicResponse.directory.child);
|
||||
if (items.length > 0) {
|
||||
content.selectedAlbum = id;
|
||||
if (action == 'add') {
|
||||
angular.forEach(items, function (item, key) {
|
||||
player.queue.push(map.mapSong(item));
|
||||
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
|
||||
var children = [].concat(subsonicResponse.directory.child);
|
||||
if (children.length > 0) {
|
||||
var allChildren = _(children).partition(function (item) {
|
||||
return item.isDir;
|
||||
});
|
||||
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
|
||||
} else if (action == 'play') {
|
||||
player.queue = [];
|
||||
angular.forEach(items, function (item, key) {
|
||||
player.queue.push(map.mapSong(item));
|
||||
});
|
||||
var next = player.queue[0];
|
||||
player.play(next);
|
||||
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
|
||||
} else {
|
||||
if (subsonicResponse.directory.id != 'undefined') {
|
||||
var albumId = subsonicResponse.directory.id;
|
||||
var albumName = subsonicResponse.directory.name;
|
||||
if (content.breadcrumb.length > 0) { content.breadcrumb.splice(1, (content.breadcrumb.length - 1)); }
|
||||
content.breadcrumb.push({ 'type': 'album', 'id': albumId, 'name': albumName });
|
||||
}
|
||||
content.song = [];
|
||||
content.album = [];
|
||||
var albums = [];
|
||||
angular.forEach(items, function (item, key) {
|
||||
if (item.isDir) {
|
||||
albums.push(map.mapAlbum(item));
|
||||
} else {
|
||||
content.song.push(map.mapSong(item));
|
||||
}
|
||||
});
|
||||
if (albums.length > 0) {
|
||||
content.album = albums;
|
||||
}
|
||||
}
|
||||
return content;
|
||||
return {
|
||||
directories: map.mapAlbums(allChildren[0]),
|
||||
songs: map.mapSongs(allChildren[1])
|
||||
};
|
||||
}
|
||||
}
|
||||
// We end up here for every else
|
||||
|
@ -307,6 +282,41 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
return promise;
|
||||
},
|
||||
|
||||
// This is used when we add or play a directory, so we recursively get all its contents
|
||||
recursiveGetSongs: function (id) {
|
||||
var deferred = $q.defer();
|
||||
// We first use getSongs() to get the contents of the root directory
|
||||
subsonicService.getSongs(id).then(function (data) {
|
||||
var directories = data.directories;
|
||||
var songs = data.songs;
|
||||
// If there are only songs, we return them immediately: this is a leaf directory and the end of the recursion
|
||||
if (directories.length === 0) {
|
||||
deferred.resolve(songs);
|
||||
} else {
|
||||
// otherwise, for each directory, we call ourselves
|
||||
var promises = [];
|
||||
angular.forEach(directories, function (dir) {
|
||||
var subdirectoryRequest = subsonicService.recursiveGetSongs(dir.id).then(function (data) {
|
||||
// This is where we join all the songs together in a single array
|
||||
return songs.concat(data);
|
||||
});
|
||||
promises.push(subdirectoryRequest);
|
||||
});
|
||||
// since all of this is asynchronous, we need to wait for all the requests to finish by using $q.all()
|
||||
var allRequestsFinished = $q.all(promises).then(function (data) {
|
||||
// and since $q.all() wraps everything in another array, we use flatten() to end up with only one array of songs
|
||||
return _(data).flatten();
|
||||
});
|
||||
deferred.resolve(allRequestsFinished);
|
||||
}
|
||||
}, function () {
|
||||
// Even if getSongs returns an error, we resolve with an empty array. Otherwise one empty directory somewhere
|
||||
// would keep us from playing all the songs of a directory recursively
|
||||
deferred.resolve([]);
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
search: function (query, type) {
|
||||
if(_([0, 1, 2]).contains(type)) {
|
||||
var promise = subsonicService.subsonicRequest('search2.view', {
|
||||
|
@ -315,6 +325,8 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
}
|
||||
}).then(function (subsonicResponse) {
|
||||
if (!_.isEmpty(subsonicResponse.searchResult2)) {
|
||||
// Make sure that song, album and artist are arrays using concat
|
||||
// because Madsonic will return an object when there's only one element
|
||||
switch (type) {
|
||||
case 0:
|
||||
if (subsonicResponse.searchResult2.song !== undefined) {
|
||||
|
@ -357,6 +369,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
params: params
|
||||
}).then(function (subsonicResponse) {
|
||||
if(subsonicResponse.randomSongs !== undefined) {
|
||||
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
|
||||
var songArray = [].concat(subsonicResponse.randomSongs.song);
|
||||
if (songArray.length > 0) {
|
||||
return map.mapSongs(songArray);
|
||||
|
@ -384,6 +397,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
var promise = subsonicService.getStarred()
|
||||
.then(function (starred) {
|
||||
if(starred.song !== undefined) {
|
||||
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
|
||||
var songArray = [].concat(starred.song);
|
||||
if (songArray.length > 0) {
|
||||
// Return random subarray of songs
|
||||
|
@ -402,6 +416,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
var promise = subsonicService.subsonicRequest('getPlaylists.view')
|
||||
.then(function (subsonicResponse) {
|
||||
if(subsonicResponse.playlists.playlist !== undefined) {
|
||||
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
|
||||
var playlistArray = [].concat(subsonicResponse.playlists.playlist);
|
||||
if (playlistArray.length > 0) {
|
||||
var allPlaylists = _(playlistArray).partition(function (item) {
|
||||
|
@ -424,6 +439,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
}
|
||||
}).then(function (subsonicResponse) {
|
||||
if (subsonicResponse.playlist.entry !== undefined) {
|
||||
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
|
||||
var entryArray = [].concat(subsonicResponse.playlist.entry);
|
||||
if (entryArray.length > 0) {
|
||||
return map.mapSongs(entryArray);
|
||||
|
@ -514,6 +530,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
})
|
||||
.then(function (subsonicResponse) {
|
||||
if (subsonicResponse.podcasts !== undefined && subsonicResponse.podcasts.channel !== undefined) {
|
||||
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
|
||||
var channelArray = [].concat(subsonicResponse.podcasts.channel);
|
||||
if (channelArray.length > 0) {
|
||||
return channelArray;
|
||||
|
@ -535,10 +552,12 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
|
|||
}).then(function (subsonicResponse) {
|
||||
var episodes = [];
|
||||
if (subsonicResponse.podcasts.channel !== undefined) {
|
||||
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
|
||||
var channelArray = [].concat(subsonicResponse.podcasts.channel);
|
||||
if (channelArray.length > 0) {
|
||||
var channel = channelArray[0];
|
||||
if (channel !== null && channel.id === id) {
|
||||
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
|
||||
var episodesArray = [].concat(channel.episode);
|
||||
episodes = _(episodesArray).filter(function (episode) {
|
||||
return episode.status === "completed";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
describe("Subsonic service -", function() {
|
||||
'use strict';
|
||||
|
||||
var subsonic, mockBackend, mockGlobals,
|
||||
response;
|
||||
var subsonic, mockBackend, mockGlobals, $q,
|
||||
response, url;
|
||||
beforeEach(function() {
|
||||
// We redefine it because in some tests we need to alter the settings
|
||||
mockGlobals = {
|
||||
|
@ -46,9 +46,10 @@ describe("Subsonic service -", function() {
|
|||
});
|
||||
});
|
||||
|
||||
inject(function (_subsonic_, $httpBackend) {
|
||||
inject(function (_subsonic_, $httpBackend, _$q_) {
|
||||
subsonic = _subsonic_;
|
||||
mockBackend = $httpBackend;
|
||||
$q = _$q_;
|
||||
});
|
||||
response = {"subsonic-response": {status: "ok", version: "1.10.2"}};
|
||||
});
|
||||
|
@ -59,7 +60,7 @@ describe("Subsonic service -", function() {
|
|||
});
|
||||
|
||||
describe("subsonicRequest() -", function() {
|
||||
var partialUrl, url;
|
||||
var partialUrl;
|
||||
beforeEach(function() {
|
||||
partialUrl = '/getStarred.view';
|
||||
url ='http://demo.subsonic.com/rest/getStarred.view?'+
|
||||
|
@ -129,7 +130,6 @@ describe("Subsonic service -", function() {
|
|||
});
|
||||
|
||||
describe("getAlbums() -", function() {
|
||||
var url;
|
||||
beforeEach(function() {
|
||||
url = 'http://demo.subsonic.com/rest/getMusicDirectory.view?'+
|
||||
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&id=21'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
|
||||
|
@ -182,62 +182,127 @@ describe("Subsonic service -", function() {
|
|||
});
|
||||
});
|
||||
|
||||
//TODO: Hyz: Rename into getDirectory(), because we don't know if there will be songs or other directories in it
|
||||
describe("getSongs() -", function() {
|
||||
var url;
|
||||
beforeEach(function() {
|
||||
var url = 'http://demo.subsonic.com/rest/getMusicDirectory.view?'+
|
||||
url = 'http://demo.subsonic.com/rest/getMusicDirectory.view?'+
|
||||
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&id=209'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
|
||||
});
|
||||
|
||||
it("Given that there were 2 songs in the given directory id in my library, when I get the songs from that directory, then a promise will be resolved with an array of 2 songs", function() {
|
||||
it("Given a directory containing 2 songs and 1 subdirectory and given its id, when I get the songs from that directory, then a promise will be resolved with an array of 2 songs and an array of 1 subdirectory", function() {
|
||||
response["subsonic-response"].directory = {
|
||||
child: [
|
||||
{ id: 778 },
|
||||
{ id: 614 }
|
||||
{ id: 614 },
|
||||
{ id: 205, isDir: true}
|
||||
]
|
||||
};
|
||||
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
|
||||
|
||||
var promise = subsonic.getSongs(209);
|
||||
//TODO: Hyz: Replace with toBeResolvedWith() when getSongs() is refactored
|
||||
var success = function (data) {
|
||||
expect(data.album).toEqual([]);
|
||||
expect(data.song).toEqual([
|
||||
{ id: 778 },
|
||||
{ id: 614 }
|
||||
]);
|
||||
};
|
||||
promise.then(success);
|
||||
|
||||
mockBackend.flush();
|
||||
|
||||
expect(promise).toBeResolved();
|
||||
expect(promise).toBeResolvedWith({
|
||||
directories: [{ id: 205, isDir: true}],
|
||||
songs: [{id: 778}, {id: 614}]
|
||||
});
|
||||
});
|
||||
|
||||
it("Given that there was only 1 song in the given directory id in my Madsonic library, when I get the songs from that directory, then a promise will be resolved with an array of 1 song", function() {
|
||||
it("Given a directory containing 1 song in my Madsonic library and given its id, when I get the songs from that directory, then a promise will be resolved with an array of 1 song and an empty array of subdirectories", function() {
|
||||
response["subsonic-response"].directory = {
|
||||
child: { id: 402 }
|
||||
};
|
||||
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
|
||||
|
||||
var promise = subsonic.getSongs(209);
|
||||
//TODO: Hyz: Replace with toBeResolvedWith() when getSongs() is refactored
|
||||
var success = function (data) {
|
||||
expect(data.album).toEqual([]);
|
||||
expect(data.song).toEqual([
|
||||
{ id: 402 }
|
||||
]);
|
||||
};
|
||||
promise.then(success);
|
||||
|
||||
mockBackend.flush();
|
||||
|
||||
expect(promise).toBeResolved();
|
||||
expect(promise).toBeResolvedWith({
|
||||
directories: [],
|
||||
songs: [{id: 402}]
|
||||
});
|
||||
});
|
||||
|
||||
it("Given a directory that didn't contain anything and given its id, when I get the songs from that directory, then a promise will be rejected with an error message", function() {
|
||||
response["subsonic-response"].directory = {};
|
||||
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
|
||||
|
||||
var promise = subsonic.getSongs(209);
|
||||
mockBackend.flush();
|
||||
|
||||
expect(promise).toBeRejectedWith({reason: 'This directory is empty.'});
|
||||
});
|
||||
});
|
||||
|
||||
describe("recursiveGetSongs() -", function() {
|
||||
var deferred;
|
||||
beforeEach(function() {
|
||||
deferred = $q.defer();
|
||||
spyOn(subsonic, 'getSongs').and.returnValue(deferred.promise);
|
||||
});
|
||||
it("Given a directory containing 2 songs and a subdirectory itself containing 2 songs and given its id, when I get the songs from that directory, then a promise will be resolved with an array of 4 songs", function() {
|
||||
// Mock getSongs so we are only testing the recursivity
|
||||
var firstDeferred = $q.defer();
|
||||
var secondDeferred = $q.defer();
|
||||
subsonic.getSongs.and.callFake(function (id) {
|
||||
// First call to getSongs
|
||||
if (id === 499) {
|
||||
return firstDeferred.promise;
|
||||
// Second call to getSongs
|
||||
} else if (id === 553) {
|
||||
return secondDeferred.promise;
|
||||
}
|
||||
});
|
||||
|
||||
var promise = subsonic.recursiveGetSongs(499);
|
||||
// On the first call to getSongs, we expect 2 songs and a subdirectory
|
||||
firstDeferred.resolve({
|
||||
directories: [{ id: 553, type: 'byfolder' }],
|
||||
songs: [
|
||||
{ id: 695 },
|
||||
{ id: 227 }
|
||||
]
|
||||
});
|
||||
// On the second call, we expect 2 songs
|
||||
secondDeferred.resolve({
|
||||
directories: [],
|
||||
songs: [
|
||||
{ id: 422 },
|
||||
{ id: 171 }
|
||||
]
|
||||
});
|
||||
|
||||
expect(promise).toBeResolvedWith([
|
||||
{ id: 695 },
|
||||
{ id: 227 },
|
||||
{ id: 422 },
|
||||
{ id: 171 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("Given a directory containing only 2 songs and given its id, when I get the songs from that directory, then a promise will be resolved with an array of 2 songs", function() {
|
||||
var promise = subsonic.recursiveGetSongs(14);
|
||||
deferred.resolve({
|
||||
directories: [],
|
||||
songs: [
|
||||
{ id: 33 }, { id: 595 }
|
||||
]
|
||||
});
|
||||
|
||||
expect(promise).toBeResolvedWith([
|
||||
{ id: 33 }, { id: 595 }
|
||||
]);
|
||||
});
|
||||
|
||||
it("Given a directory that didn't contain anything and given its id, when I get the songs from that directory, then a promise will be resolved with an empty array", function() {
|
||||
var promise = subsonic.recursiveGetSongs(710);
|
||||
deferred.reject({reason: 'This directory is empty.'});
|
||||
|
||||
expect(promise).toBeResolvedWith([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAlbumListBy() -", function() {
|
||||
var url;
|
||||
beforeEach(function() {
|
||||
url = 'http://demo.subsonic.com/rest/getAlbumList.view?'+
|
||||
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&offset=0'+'&p=enc:cGFzc3dvcmQ%3D'+'&size=3&type=newest'+'&u=Hyzual&v=1.10.2';
|
||||
|
@ -331,7 +396,6 @@ describe("Subsonic service -", function() {
|
|||
});
|
||||
|
||||
describe("getRandomStarredSongs() -", function() {
|
||||
var url;
|
||||
beforeEach(function() {
|
||||
url = 'http://demo.subsonic.com/rest/getStarred.view?'+
|
||||
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
|
||||
|
@ -393,7 +457,6 @@ describe("Subsonic service -", function() {
|
|||
});
|
||||
|
||||
describe("getRandomSongs() -", function() {
|
||||
var url;
|
||||
beforeEach(function() {
|
||||
url = 'http://demo.subsonic.com/rest/getRandomSongs.view?'+
|
||||
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D'+'&size=3'+'&u=Hyzual&v=1.10.2';
|
||||
|
@ -508,7 +571,6 @@ describe("Subsonic service -", function() {
|
|||
});
|
||||
|
||||
describe("getArtists() -", function() {
|
||||
var url;
|
||||
beforeEach(function() {
|
||||
url = 'http://demo.subsonic.com/rest/getIndexes.view?'+
|
||||
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
|
||||
|
@ -689,7 +751,6 @@ describe("Subsonic service -", function() {
|
|||
});
|
||||
|
||||
describe("getPlaylist() -", function() {
|
||||
var url;
|
||||
beforeEach(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';
|
||||
|
@ -781,7 +842,6 @@ describe("Subsonic service -", function() {
|
|||
});
|
||||
|
||||
describe("getPodcasts() -", function() {
|
||||
var url;
|
||||
beforeEach(function() {
|
||||
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';
|
||||
|
@ -833,7 +893,6 @@ describe("Subsonic service -", function() {
|
|||
});
|
||||
|
||||
describe("getPodcast() -", 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';
|
||||
|
@ -933,7 +992,6 @@ describe("Subsonic service -", function() {
|
|||
});
|
||||
|
||||
describe("search() -", function() {
|
||||
var url;
|
||||
beforeEach(function() {
|
||||
url = 'http://demo.subsonic.com/rest/search2.view?'+
|
||||
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D'+'&query=unintersetingly'+'&u=Hyzual&v=1.10.2';
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
<div id="BreadCrumbs" class="floatleft">
|
||||
<div class="crumb"><a ng-click="toggleArtists()" title="Toggle Artists">Artists</a> ></div>
|
||||
<div class="crumb" ng-repeat="o in BreadCrumbs | filter:{type:'artist'}"><a ng-click="getAlbums(o.id, o.name)">{{o.name}}</a> ></div>
|
||||
<div class="crumb" ng-repeat="o in BreadCrumbs | filter:{type:'album'}"><a ng-click="getSongs(o.id, '')">{{o.name}}</a> ></div>
|
||||
<div class="crumb" ng-repeat="o in BreadCrumbs | filter:{type:'album'}"><a ng-click="getSongs('display', o.id, o.name)">{{o.name}}</a> ></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -49,10 +49,10 @@
|
|||
<div class="clear"></div>
|
||||
<ul class="simplelist songlist noselect">
|
||||
<div class="" ng-repeat="o in album" ng-switch on="o.type">
|
||||
<li class="album" ng-switch-when="byfolder" id="{{o.id}}" ng-class="{'selected': selectedAlbum == o.id, 'albumgrid': settings.DefaultLibraryLayout.id == 'grid'}" ng-click="getSongs(o.id, '')" parentid="{{o.parentid}}">
|
||||
<li class="album" ng-switch-when="byfolder" id="{{o.id}}" ng-class="{'selected': selectedAlbum == o.id, 'albumgrid': settings.DefaultLibraryLayout.id == 'grid'}" ng-click="getSongs('display', o.id, o.name)" parentid="{{o.parentid}}">
|
||||
<div class="itemactions">
|
||||
<a class="add hover" href="" title="Add To Play Queue" ng-click="getSongs(o.id, 'add')" stop-event="click"></a>
|
||||
<a class="play hover" href="" title="Play" ng-click="getSongs(o.id, 'play')" stop-event="click"></a>
|
||||
<a class="add hover" href="" title="Add To Play Queue" ng-click="getSongs('add', o.id, o.name)" stop-event="click"></a>
|
||||
<a class="play hover" href="" title="Play" ng-click="getSongs('play', o.id, o.name)" stop-event="click"></a>
|
||||
<a class="download hover" href="" ng-click="download(o.id)" title="Download" stop-event="click"></a>
|
||||
<a title="Favorite hover" href="" ng-class="{'favorite': o.starred, 'rate': !o.starred}" ng-click="updateFavorite(o)" stop-event="click"></a>
|
||||
<a class="info hover" href="" title="{{'Created: ' + o.date}}"></a>
|
||||
|
|
|
@ -279,6 +279,7 @@ angular.module('jamstash.subsonic.controller', ['jamstash.subsonic.service', 'ja
|
|||
* @param {String} action the action to be taken with the songs. Must be 'add', 'play' or 'display'
|
||||
* @return {Promise} the original promise passed in first param. That way we can chain it further !
|
||||
*/
|
||||
//TODO: Hyz: Maybe we should move this to a service
|
||||
$scope.requestSongs = function (promise, action) {
|
||||
$scope.handleErrors(promise)
|
||||
.then(function (songs) {
|
||||
|
@ -300,18 +301,33 @@ angular.module('jamstash.subsonic.controller', ['jamstash.subsonic.service', 'ja
|
|||
return promise;
|
||||
};
|
||||
|
||||
$scope.getSongs = function (id, action) {
|
||||
subsonic.getSongs(id, action).then(function (data) {
|
||||
$scope.album = data.album;
|
||||
$scope.song = data.song;
|
||||
$scope.BreadCrumbs = data.breadcrumb;
|
||||
$scope.selectedAutoAlbum = data.selectedAutoAlbum;
|
||||
$scope.selectedArtist = data.selectedArtist;
|
||||
$scope.selectedPlaylist = data.selectedPlaylist;
|
||||
if ($scope.SelectedAlbumSort.id != "default") {
|
||||
$scope.getSongs = function (action, id, name) {
|
||||
var promise;
|
||||
if (action === 'play' || action === 'add') {
|
||||
promise = subsonic.recursiveGetSongs(id);
|
||||
$scope.requestSongs(promise, action);
|
||||
} else if (action === 'display') {
|
||||
promise = subsonic.getSongs(id);
|
||||
$scope.handleErrors(promise).then(function (data) {
|
||||
$scope.album = data.directories;
|
||||
$scope.song = data.songs;
|
||||
if ($scope.BreadCrumbs.length > 0) {
|
||||
$scope.BreadCrumbs.splice(1, ($scope.BreadCrumbs.length - 1));
|
||||
}
|
||||
$scope.BreadCrumbs.push({'type': 'album', 'id': id, 'name': name});
|
||||
$scope.selectedAutoAlbum = null;
|
||||
$scope.selectedArtist = null;
|
||||
$scope.selectedAlbum = id;
|
||||
$scope.selectedAutoPlaylist = null;
|
||||
$scope.selectedPlaylist = null;
|
||||
$scope.selectedPodcast = null;
|
||||
if ($scope.SelectedAlbumSort.id !== "default") {
|
||||
sortSubsonicAlbums($scope.SelectedAlbumSort.id);
|
||||
}
|
||||
}, function (error) {
|
||||
notifications.updateMessage(error.reason, true);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getRandomStarredSongs = function (action) {
|
||||
|
|
|
@ -22,6 +22,8 @@ describe("Subsonic controller", function() {
|
|||
|
||||
// Mock the subsonic service
|
||||
subsonic = jasmine.createSpyObj("subsonic", [
|
||||
"getSongs",
|
||||
"recursiveGetSongs",
|
||||
"getAlbums",
|
||||
"getArtists",
|
||||
"getGenres",
|
||||
|
@ -38,6 +40,8 @@ describe("Subsonic controller", function() {
|
|||
]);
|
||||
// We make them return different promises and use our deferred variable only when testing
|
||||
// a particular function, so that they stay isolated
|
||||
subsonic.getSongs.and.returnValue($q.defer().promise);
|
||||
subsonic.recursiveGetSongs.and.returnValue($q.defer().promise);
|
||||
subsonic.getAlbums.and.returnValue($q.defer().promise);
|
||||
subsonic.getArtists.and.returnValue($q.defer().promise);
|
||||
subsonic.getGenres.and.returnValue($q.defer().promise);
|
||||
|
@ -80,6 +84,138 @@ describe("Subsonic controller", function() {
|
|||
scope.selectedPlaylist = null;
|
||||
});
|
||||
|
||||
describe("getSongs -", function() {
|
||||
beforeEach(function() {
|
||||
scope.BreadCrumbs = [];
|
||||
scope.SelectedAlbumSort= {
|
||||
id: "default"
|
||||
};
|
||||
subsonic.getSongs.and.returnValue(deferred.promise);
|
||||
subsonic.recursiveGetSongs.and.returnValue(deferred.promise);
|
||||
spyOn(scope, "requestSongs").and.returnValue(deferred.promise);
|
||||
});
|
||||
|
||||
it("Given a music directory that contained 3 songs and given its id and name, when I display it, then subsonic-service will be called, the breadcrumbs will be updated and the songs will be published to the scope", function() {
|
||||
scope.getSongs('display', 87, 'Covetous Dadayag');
|
||||
deferred.resolve({
|
||||
directories: [],
|
||||
songs: [
|
||||
{ id: 660 },
|
||||
{ id: 859 },
|
||||
{ id: 545 }
|
||||
]
|
||||
});
|
||||
scope.$apply();
|
||||
|
||||
expect(subsonic.getSongs).toHaveBeenCalledWith(87);
|
||||
expect(scope.album).toEqual([]);
|
||||
expect(scope.song).toEqual([
|
||||
{ id: 660 },
|
||||
{ id: 859 },
|
||||
{ id: 545 }
|
||||
]);
|
||||
expect(scope.BreadCrumbs).toEqual([{
|
||||
type: 'album',
|
||||
id: 87,
|
||||
name: 'Covetous Dadayag'
|
||||
}]);
|
||||
expect(scope.selectedAutoAlbum).toBeNull();
|
||||
expect(scope.selectedArtist).toBeNull();
|
||||
expect(scope.selectedAlbum).toBe(87);
|
||||
expect(scope.selectedAutoPlaylist).toBeNull();
|
||||
expect(scope.selectedPlaylist).toBeNull();
|
||||
expect(scope.selectedPodcast).toBeNull();
|
||||
});
|
||||
|
||||
it("Given that there was a previous level in the breadcrumbs, when I display a music directory, then the album breadcrumb will be added", function() {
|
||||
scope.BreadCrumbs = [
|
||||
{
|
||||
type: 'artist',
|
||||
id: 73,
|
||||
name: 'Evan Mestanza'
|
||||
}
|
||||
];
|
||||
scope.getSongs('display', 883, 'Pitiedly preutilizable');
|
||||
deferred.resolve({
|
||||
directories: [],
|
||||
songs: []
|
||||
});
|
||||
scope.$apply();
|
||||
|
||||
expect(scope.BreadCrumbs).toEqual([
|
||||
{
|
||||
type: 'artist',
|
||||
id: 73,
|
||||
name: 'Evan Mestanza'
|
||||
}, {
|
||||
type: 'album',
|
||||
id: 883,
|
||||
name: 'Pitiedly preutilizable'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it("Given a music directory that contained 2 songs and 1 subdirectory and given its id and name, when I display it, then subsonic-service will be called, the songs and directory will be published to the scope", function() {
|
||||
scope.getSongs('display', 6, 'Potsander dormilona');
|
||||
deferred.resolve({
|
||||
directories: [{id: 387, type: 'byfolder'}],
|
||||
songs: [
|
||||
{ id: 102 },
|
||||
{ id: 340 }
|
||||
]
|
||||
});
|
||||
scope.$apply();
|
||||
|
||||
expect(scope.album).toEqual([
|
||||
{id: 387, type: 'byfolder'}
|
||||
]);
|
||||
expect(scope.song).toEqual([
|
||||
{ id: 102 },
|
||||
{ id: 340 }
|
||||
]);
|
||||
});
|
||||
|
||||
it("Given a music directory, when I display it, then handleErrors will handle HTTP and Subsonic errors", function() {
|
||||
spyOn(scope, 'handleErrors').and.returnValue(deferred.promise);
|
||||
scope.getSongs('display', 88);
|
||||
expect(scope.handleErrors).toHaveBeenCalledWith(deferred.promise);
|
||||
});
|
||||
|
||||
it("Given a music directory that didn't contain anything, when I display it, then an error notification will be displayed", function() {
|
||||
scope.getSongs('display', 214, 'Upside bunemost');
|
||||
deferred.reject({reason: 'This directory is empty.'});
|
||||
scope.$apply();
|
||||
|
||||
expect(notifications.updateMessage).toHaveBeenCalledWith('This directory is empty.', true);
|
||||
});
|
||||
|
||||
it("Given a music directory that contained 3 songs and given its id, when I add it to the playing queue, then requestSongs() will be called", function() {
|
||||
scope.getSongs('add', 720);
|
||||
deferred.resolve([
|
||||
{ id: 927 },
|
||||
{ id: 598 },
|
||||
{ id: 632 }
|
||||
]);
|
||||
scope.$apply();
|
||||
|
||||
expect(subsonic.recursiveGetSongs).toHaveBeenCalledWith(720);
|
||||
expect(scope.requestSongs).toHaveBeenCalledWith(deferred.promise, 'add');
|
||||
});
|
||||
|
||||
it("Given a music directory that contained 3 songs and given its id, when I play it, then requestSongs() will be called", function() {
|
||||
scope.getSongs('play', 542);
|
||||
deferred.resolve([
|
||||
{ id: 905 },
|
||||
{ id: 181 },
|
||||
{ id: 880 }
|
||||
]);
|
||||
scope.$apply();
|
||||
|
||||
expect(subsonic.recursiveGetSongs).toHaveBeenCalledWith(542);
|
||||
expect(scope.requestSongs).toHaveBeenCalledWith(deferred.promise, 'play');
|
||||
});
|
||||
});
|
||||
|
||||
describe("Given that my library contained 3 songs, ", function() {
|
||||
var response;
|
||||
beforeEach(function() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue