Adds back AutoPlay, Loop Queue and Repeat support.
- AutoPlay still has a problem with scope apply. Since it's asynchronous, the directive needs to know when it's finisehd to use scope apply... That means more subsonic refactoring. - Adds unit tests for Repeat song and Loop Queue - Adds back a special function in the player Service to manage the end of a song.
This commit is contained in:
parent
834e67946c
commit
2e97e25f25
8 changed files with 136 additions and 34 deletions
|
@ -51,7 +51,6 @@
|
|||
</div><!-- end #content -->
|
||||
<div id="SideBar" ng-controller="QueueController">
|
||||
<div class="headeractions">
|
||||
<a ng-click="player.restart()">Restart</a>
|
||||
<a class="buttonimg" title="Shuffle Queue" ng-click="queueShuffle()"><img src="images/fork_gd_11x12.png"></a>
|
||||
<a class="buttonimg" id="action_Empty" title="Delete Queue" ng-click="queueEmpty()"><img src="images/trash_fill_gd_12x12.png"></a>
|
||||
<a class="buttonimg" id="action_DeleteSelected" title="Remove Selected From Queue" ng-click="queueRemoveSelected()"><img src="images/x_11x11.png"></a>
|
||||
|
|
|
@ -45,12 +45,16 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
|
|||
scope.revealControls();
|
||||
scope.scrobbled = false;
|
||||
},
|
||||
pause: function() {
|
||||
console.log('jplayer pause');
|
||||
},
|
||||
ended: function() {
|
||||
console.log('jplayer ended');
|
||||
playerService.nextTrack();
|
||||
// We do this here and not on the service because we cannot create
|
||||
// a circular dependency between the player and subsonic services
|
||||
if(playerService.isLastSongPlaying() && globals.settings.AutoPlay) {
|
||||
// Load more random tracks
|
||||
subsonic.getRandomSongs('play', '', '');
|
||||
} else {
|
||||
playerService.songEnded();
|
||||
}
|
||||
scope.$apply();
|
||||
},
|
||||
timeupdate: function (event) {
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
describe("jplayer directive", function() {
|
||||
'use strict';
|
||||
|
||||
var element, scope, playerService, globals, subsonic, $player, playingSong;
|
||||
var element, scope, playerService, mockGlobals, subsonic, $player, playingSong;
|
||||
|
||||
beforeEach(function() {
|
||||
playingSong = {};
|
||||
// We redefine globals because in some tests we need to alter the settings
|
||||
mockGlobals = {
|
||||
settings: {
|
||||
AutoPlay: false
|
||||
}
|
||||
};
|
||||
module('jamstash.player.directive', function($provide) {
|
||||
// Mock the player service
|
||||
$provide.decorator('player', function($delegate) {
|
||||
|
@ -12,14 +18,16 @@ describe("jplayer directive", function() {
|
|||
return playingSong;
|
||||
});
|
||||
$delegate.nextTrack = jasmine.createSpy('nextTrack');
|
||||
$delegate.songEnded = jasmine.createSpy('songEnded');
|
||||
$delegate.isLastSongPlaying = jasmine.createSpy('isLastSongPlaying');
|
||||
|
||||
return $delegate;
|
||||
});
|
||||
$provide.value('globals', mockGlobals);
|
||||
});
|
||||
|
||||
inject(function($rootScope, $compile, _player_, _globals_, _subsonic_) {
|
||||
inject(function($rootScope, $compile, _player_, _subsonic_) {
|
||||
playerService = _player_;
|
||||
globals = _globals_;
|
||||
subsonic = _subsonic_;
|
||||
// Compile the directive
|
||||
scope = $rootScope.$new();
|
||||
|
@ -74,11 +82,26 @@ describe("jplayer directive", function() {
|
|||
expect(playerService.restartSong).toBeFalsy();
|
||||
});
|
||||
|
||||
it("When jplayer has finished the current song, it asks the player service for the next track", function() {
|
||||
var e = $.jPlayer.event.ended;
|
||||
$player.trigger(e);
|
||||
describe("When jplayer has finished the current song,", function() {
|
||||
var ended;
|
||||
beforeEach(function() {
|
||||
ended = $.jPlayer.event.ended;
|
||||
});
|
||||
it("it notifies the player service that the song has ended", function() {
|
||||
$player.trigger(ended);
|
||||
expect(playerService.songEnded).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
expect(playerService.nextTrack).toHaveBeenCalled();
|
||||
it("given that the last song of the queue is playing and that the global setting AutoPlay is true, it asks subsonic for random tracks and notifies the player service that the song has ended", function() {
|
||||
mockGlobals.settings.AutoPlay = true;
|
||||
spyOn(subsonic, "getRandomSongs");
|
||||
playerService.isLastSongPlaying.and.returnValue(true);
|
||||
|
||||
$player.trigger(ended);
|
||||
|
||||
expect(playerService.isLastSongPlaying).toHaveBeenCalled();
|
||||
expect(subsonic.getRandomSongs).toHaveBeenCalledWith('play', '', '');
|
||||
});
|
||||
});
|
||||
|
||||
it("When jPlayer starts to play the current song, it displays the player controls", function() {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
angular.module('jamstash.player.service', ['jamstash.settings', 'angular-underscore/utils'])
|
||||
|
||||
.factory('player', function () {
|
||||
.factory('player', ['globals', function (globals) {
|
||||
'use strict';
|
||||
|
||||
var player = {
|
||||
|
@ -44,6 +44,22 @@ angular.module('jamstash.player.service', ['jamstash.settings', 'angular-undersc
|
|||
player.restartSong = true;
|
||||
},
|
||||
|
||||
// Called from the player directive at the end of the current song
|
||||
songEnded: function() {
|
||||
if (globals.settings.Repeat) {
|
||||
// repeat current track
|
||||
player.restart();
|
||||
} else if (player.isLastSongPlaying() === true) {
|
||||
if (globals.settings.LoopQueue) {
|
||||
// Loop to first track in queue
|
||||
player.playingIndex = -1;
|
||||
player.nextTrack();
|
||||
}
|
||||
} else {
|
||||
player.nextTrack();
|
||||
}
|
||||
},
|
||||
|
||||
nextTrack: function() {
|
||||
if((player.playingIndex + 1) < player.queue.length) {
|
||||
var nextTrack = player.queue[player.playingIndex + 1];
|
||||
|
@ -81,8 +97,12 @@ angular.module('jamstash.player.service', ['jamstash.settings', 'angular-undersc
|
|||
|
||||
getPlayingSong: function() {
|
||||
return player.playingSong;
|
||||
},
|
||||
|
||||
isLastSongPlaying: function() {
|
||||
return ((player.playingIndex +1) === player.queue.length);
|
||||
}
|
||||
};
|
||||
|
||||
return player;
|
||||
});
|
||||
}]);
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
describe("Player service -", function() {
|
||||
'use strict';
|
||||
|
||||
var player, firstSong, secondSong, thirdSong, newSong;
|
||||
var player, mockGlobals, firstSong, secondSong, thirdSong, newSong;
|
||||
beforeEach(function() {
|
||||
module('jamstash.player.service');
|
||||
|
||||
// We redefine globals because in some tests we need to alter the settings
|
||||
mockGlobals = {
|
||||
settings: {
|
||||
Repeat: false,
|
||||
LoopQueue: false
|
||||
}
|
||||
};
|
||||
module('jamstash.player.service', function ($provide) {
|
||||
$provide.value('globals', mockGlobals);
|
||||
});
|
||||
inject(function (_player_) {
|
||||
player = _player_;
|
||||
});
|
||||
|
@ -121,20 +129,71 @@ describe("Player service -", function() {
|
|||
expect(player.playingIndex).toBe(-1);
|
||||
});
|
||||
|
||||
it("When I call emptyQueue, it empties the playing queue", function() {
|
||||
it("when I call emptyQueue, it empties the playing queue", function() {
|
||||
player.emptyQueue();
|
||||
expect(player.queue).toEqual([]);
|
||||
});
|
||||
|
||||
it("When I add a song to the queue, it is appended to the end of the playing queue", function() {
|
||||
it("when I add a song to the queue, it is appended to the end of the playing queue", function() {
|
||||
player.addSong(newSong);
|
||||
expect(player.queue).toEqual([firstSong, secondSong, thirdSong, newSong]);
|
||||
});
|
||||
|
||||
it("When I remove the second song, the playing queue is now only the first and third song", function() {
|
||||
it("when I remove the second song, the playing queue is now only the first and third song", function() {
|
||||
player.removeSong(secondSong);
|
||||
expect(player.queue).toEqual([firstSong, thirdSong]);
|
||||
});
|
||||
|
||||
it("when the first song is playing, isLastSongPlaying returns false", function() {
|
||||
player.playingIndex = 0;
|
||||
expect(player.isLastSongPlaying()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("when the third song is playing, isLastSongPlaying returns true", function() {
|
||||
player.playingIndex = 2;
|
||||
expect(player.isLastSongPlaying()).toBeTruthy();
|
||||
});
|
||||
|
||||
it("and the current song is not the last, when the current song ends, it plays the next song in queue", function() {
|
||||
spyOn(player, "nextTrack");
|
||||
player.playingIndex = 0;
|
||||
|
||||
player.songEnded();
|
||||
|
||||
expect(player.nextTrack).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("and that the 'Repeat song' setting is true, when the current song ends, it restarts it", function() {
|
||||
spyOn(player, "restart");
|
||||
mockGlobals.settings.Repeat = true;
|
||||
|
||||
player.songEnded();
|
||||
|
||||
expect(player.restart).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("and the current song is the last of the queue, when the current song ends,", function() {
|
||||
beforeEach(function() {
|
||||
spyOn(player, "nextTrack").and.callThrough();
|
||||
player.playingIndex = 2;
|
||||
});
|
||||
|
||||
it("if the 'Repeat queue' setting is true, it plays the first song of the queue", function() {
|
||||
mockGlobals.settings.LoopQueue = true;
|
||||
|
||||
player.songEnded();
|
||||
|
||||
expect(player.playingIndex).toBe(0);
|
||||
expect(player.nextTrack).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("it does not play anything", function() {
|
||||
player.songEnded();
|
||||
|
||||
expect(player.playingIndex).toBe(2);
|
||||
expect(player.nextTrack).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Given a song", function() {
|
||||
|
@ -150,14 +209,14 @@ describe("Player service -", function() {
|
|||
};
|
||||
});
|
||||
|
||||
xit("When I play it, the song is marked as playing", function() {
|
||||
xit("when I play it, the song is marked as playing", function() {
|
||||
player.play(song);
|
||||
|
||||
expect(player.getPlayingSong()).toBe(song);
|
||||
expect(song.playing).toBeTruthy();
|
||||
});
|
||||
|
||||
xit("When I restart playback, the song is still marked as playing", function() {
|
||||
xit("when I restart the current song, the song is still marked as playing", function() {
|
||||
song.playing = true;
|
||||
//player.getPlayingSong() = song;
|
||||
|
||||
|
@ -167,7 +226,7 @@ describe("Player service -", function() {
|
|||
expect(song.playing).toBeTruthy();
|
||||
});
|
||||
|
||||
it("When the song was playing and I play it again, it restarts playback", function() {
|
||||
it("when the song was playing and I play it again, it restarts the current song", function() {
|
||||
spyOn(player, "restart");
|
||||
|
||||
player.play(song);
|
||||
|
@ -176,12 +235,12 @@ describe("Player service -", function() {
|
|||
expect(player.restart).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("When I restart playback, the flag for the directive is set", function() {
|
||||
it("when I restart the current song, the flag for the directive is set", function() {
|
||||
player.restart();
|
||||
expect(player.restartSong).toBeTruthy();
|
||||
});
|
||||
|
||||
it("When I load the song, the flag for the directive is set", function() {
|
||||
it("when I load the song, the flag for the directive is set", function() {
|
||||
spyOn(player, "play");
|
||||
|
||||
player.load(song);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* jamstash.player Module
|
||||
*
|
||||
* Control the HTML5 player through jplayer.js
|
||||
* Enables access to the player service through the scope
|
||||
*/
|
||||
angular.module('jamstash.player.controller', ['jamstash.player.service', 'jamstash.player.directive'])
|
||||
|
||||
|
|
|
@ -457,16 +457,15 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
|
|||
}
|
||||
if (action == 'add') {
|
||||
angular.forEach(items, function (item, key) {
|
||||
$rootScope.queue.push(map.mapSong(item));
|
||||
player.queue.push(map.mapSong(item));
|
||||
});
|
||||
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
|
||||
} else if (action == 'play') {
|
||||
$rootScope.queue = [];
|
||||
player.queue = [];
|
||||
angular.forEach(items, function (item, key) {
|
||||
$rootScope.queue.push(map.mapSong(item));
|
||||
player.queue.push(map.mapSong(item));
|
||||
});
|
||||
var next = $rootScope.queue[0];
|
||||
player.play(next);
|
||||
player.playFirstSong();
|
||||
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
|
||||
} else {
|
||||
content.album = [];
|
||||
|
@ -800,7 +799,6 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
|
|||
var exception = {reason: 'Error when contacting the Subsonic server.'};
|
||||
var deferred = $q.defer();
|
||||
var httpPromise;
|
||||
if (globals.settings.Debug) { console.log('Scrobble Song: ' + id); }
|
||||
if(globals.settings.Protocol === 'jsonp') {
|
||||
httpPromise = $http.jsonp(globals.BaseURL() + '/scrobble.view?callback=JSON_CALLBACK&' + globals.BaseParams() + '&id=' + id + '&submission=true',
|
||||
{
|
||||
|
@ -813,7 +811,6 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
|
|||
});
|
||||
}
|
||||
httpPromise.success(function (data, status) {
|
||||
console.log(data);
|
||||
if(globals.settings.Debug) { console.log('Successfully scrobbled song: ' + id); }
|
||||
}).error(function(data, status) {
|
||||
exception.httpError = status;
|
||||
|
|
|
@ -43,7 +43,7 @@ describe("Subsonic service -", function() {
|
|||
var promise = subsonic.scrobble(song);
|
||||
mockBackend.flush();
|
||||
|
||||
expect(promise).toBeResolved();
|
||||
expect(promise).toBeResolvedWith(response);
|
||||
});
|
||||
|
||||
describe("getStarred -", function() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue