Extracts everything related to localStorage in common/persistence-service.js
That way, whenever we want to use something else (like a JSON file, or a DB) we will only have to change persistence-service.js - Migrated the unit tests there too. - Added replay in case of jPlayer error.
This commit is contained in:
parent
3c809d1903
commit
42830bbdbf
8 changed files with 262 additions and 222 deletions
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* Declare app level module */
|
/* Declare app level module */
|
||||||
angular.module('JamStash', ['ngCookies', 'ngRoute', 'ngSanitize',
|
angular.module('JamStash', ['ngCookies', 'ngRoute', 'ngSanitize',
|
||||||
'jamstash.subsonic.controller', 'jamstash.archive.controller', 'jamstash.player.controller', 'jamstash.queue.controller', 'angular-locker'])
|
'jamstash.subsonic.controller', 'jamstash.archive.controller', 'jamstash.player.controller', 'jamstash.queue.controller', 'jamstash.persistence'])
|
||||||
|
|
||||||
.config(['$routeProvider',function($routeProvider) {
|
.config(['$routeProvider',function($routeProvider) {
|
||||||
$routeProvider
|
$routeProvider
|
||||||
|
@ -48,10 +48,4 @@ angular.module('JamStash', ['ngCookies', 'ngRoute', 'ngSanitize',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
}])
|
|
||||||
|
|
||||||
.config(['lockerProvider', function (lockerProvider) {
|
|
||||||
lockerProvider.setDefaultDriver('local')
|
|
||||||
.setDefaultNamespace('jamstash')
|
|
||||||
.setEventsEnabled(false);
|
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('JamStash')
|
angular.module('JamStash')
|
||||||
.controller('AppController', ['$scope', '$rootScope', '$document', '$window', '$location', '$cookieStore', '$http', 'utils', 'globals', 'model', 'notifications', 'player', 'locker',
|
.controller('AppController', ['$scope', '$rootScope', '$document', '$window', '$location', '$cookieStore', '$http', 'utils', 'globals', 'model', 'notifications', 'player', 'persistence',
|
||||||
function($scope, $rootScope, $document, $window, $location, $cookieStore, $http, utils, globals, model, notifications, player, locker) {
|
function($scope, $rootScope, $document, $window, $location, $cookieStore, $http, utils, globals, model, notifications, player, persistence) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
$rootScope.settings = globals.settings;
|
$rootScope.settings = globals.settings;
|
||||||
|
@ -400,27 +400,6 @@ angular.module('JamStash')
|
||||||
return $sce.trustAsHtml(html);
|
return $sce.trustAsHtml(html);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.loadTrackPosition = function () {
|
|
||||||
// Load Saved Song
|
|
||||||
var song = locker.get('CurrentSong');
|
|
||||||
if (song) {
|
|
||||||
player.load(song);
|
|
||||||
}
|
|
||||||
if (globals.settings.Debug) { console.log('Current Position Loaded from localStorage: ', song); }
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.loadQueue = function () {
|
|
||||||
// load Saved queue
|
|
||||||
var queue = locker.get('CurrentQueue');
|
|
||||||
if (queue) {
|
|
||||||
player.addSongs(queue);
|
|
||||||
if (player.queue.length > 0) {
|
|
||||||
notifications.updateMessage(player.queue.length + ' Saved Song(s)', true);
|
|
||||||
}
|
|
||||||
if (globals.settings.Debug) { console.log('Play Queue Loaded From localStorage: ' + player.queue.length + ' song(s)'); }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Launch on Startup */
|
/* Launch on Startup */
|
||||||
$scope.loadSettings();
|
$scope.loadSettings();
|
||||||
utils.switchTheme(globals.settings.Theme);
|
utils.switchTheme(globals.settings.Theme);
|
||||||
|
@ -432,9 +411,8 @@ angular.module('JamStash')
|
||||||
if ($scope.loggedIn()) {
|
if ($scope.loggedIn()) {
|
||||||
//$scope.ping();
|
//$scope.ping();
|
||||||
if (globals.settings.SaveTrackPosition) {
|
if (globals.settings.SaveTrackPosition) {
|
||||||
$scope.loadQueue();
|
persistence.loadQueue();
|
||||||
$scope.loadTrackPosition();
|
persistence.loadTrackPosition();
|
||||||
//FIXME: HYZ: player.startSaveTrackPosition();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* End Startup */
|
/* End Startup */
|
||||||
|
|
|
@ -57,79 +57,4 @@ describe("Main controller", function() {
|
||||||
xdescribe("toggleSetting -", function() {
|
xdescribe("toggleSetting -", function() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("load from localStorage -", function() {
|
|
||||||
var fakeStorage;
|
|
||||||
beforeEach(function() {
|
|
||||||
fakeStorage = {};
|
|
||||||
|
|
||||||
spyOn(locker, "get").and.callFake(function(key) {
|
|
||||||
return fakeStorage[key];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("loadTrackPosition -", function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
spyOn(player, "load");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Given that we previously saved the current track's position in local Storage, it loads the song we saved into the player", function() {
|
|
||||||
var song = {
|
|
||||||
id: 8626,
|
|
||||||
name: 'Pectinatodenticulate',
|
|
||||||
artist: 'Isiah Hosfield',
|
|
||||||
album: 'Tammanyize'
|
|
||||||
};
|
|
||||||
fakeStorage = {
|
|
||||||
'CurrentSong': song
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.loadTrackPosition();
|
|
||||||
|
|
||||||
expect(locker.get).toHaveBeenCalledWith('CurrentSong');
|
|
||||||
expect(player.load).toHaveBeenCalledWith(song);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Given that we didn't save anything in local Storage, it doesn't load anything", function() {
|
|
||||||
scope.loadTrackPosition();
|
|
||||||
expect(locker.get).toHaveBeenCalledWith('CurrentSong');
|
|
||||||
expect(player.load).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("loadQueue -", function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
spyOn(notifications, "updateMessage");
|
|
||||||
spyOn(player, "addSongs").and.callFake(function (songs) {
|
|
||||||
// Update the queue length so that notifications work
|
|
||||||
player.queue.length += songs.length;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Given that we previously saved the playing queue in local Storage, it fills the player's queue with what we saved and notifies the user", function() {
|
|
||||||
var queue = [
|
|
||||||
{ id: 8705 },
|
|
||||||
{ id: 1617 },
|
|
||||||
{ id: 9812 }
|
|
||||||
];
|
|
||||||
fakeStorage = {
|
|
||||||
'CurrentQueue': queue
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.loadQueue();
|
|
||||||
|
|
||||||
expect(locker.get).toHaveBeenCalledWith('CurrentQueue');
|
|
||||||
expect(player.addSongs).toHaveBeenCalledWith(queue);
|
|
||||||
expect(notifications.updateMessage).toHaveBeenCalledWith('3 Saved Song(s)', true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Given that we didn't save anything in local Storage, it doesn't load anything", function() {
|
|
||||||
scope.loadQueue();
|
|
||||||
|
|
||||||
expect(locker.get).toHaveBeenCalledWith('CurrentQueue');
|
|
||||||
expect(player.addSongs).not.toHaveBeenCalled();
|
|
||||||
expect(notifications.updateMessage).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
48
app/common/persistence-service.js
Normal file
48
app/common/persistence-service.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
'use strict';
|
||||||
|
/**
|
||||||
|
* jamstash.persistence Module
|
||||||
|
*
|
||||||
|
* Provides load, save and delete operations for the current song and queue.
|
||||||
|
* Data storage provided by HTML5 localStorage.
|
||||||
|
*/
|
||||||
|
angular.module('jamstash.persistence', ['jamstash.settings', 'jamstash.player.service', 'jamstash.notifications', 'angular-locker'])
|
||||||
|
|
||||||
|
.config(['lockerProvider', function (lockerProvider) {
|
||||||
|
lockerProvider.setDefaultDriver('local')
|
||||||
|
.setDefaultNamespace('jamstash')
|
||||||
|
.setEventsEnabled(false);
|
||||||
|
}])
|
||||||
|
|
||||||
|
.service('persistence', ['globals', 'player', 'notifications', 'locker', function(globals, player, notifications, locker){
|
||||||
|
this.loadTrackPosition = function () {
|
||||||
|
// Load Saved Song
|
||||||
|
var song = locker.get('CurrentSong');
|
||||||
|
if (song) {
|
||||||
|
player.load(song);
|
||||||
|
}
|
||||||
|
if (globals.settings.Debug) { console.log('Current Position Loaded from localStorage: ', song); }
|
||||||
|
};
|
||||||
|
|
||||||
|
this.loadQueue = function () {
|
||||||
|
// load Saved queue
|
||||||
|
var queue = locker.get('CurrentQueue');
|
||||||
|
if (queue) {
|
||||||
|
player.addSongs(queue);
|
||||||
|
if (player.queue.length > 0) {
|
||||||
|
notifications.updateMessage(player.queue.length + ' Saved Song(s)', true);
|
||||||
|
}
|
||||||
|
if (globals.settings.Debug) { console.log('Play Queue Loaded From localStorage: ' + player.queue.length + ' song(s)'); }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.saveTrackPosition = function (song) {
|
||||||
|
locker.put('CurrentSong', song);
|
||||||
|
if (globals.settings.Debug) { console.log('Saving Current Position: ', song); }
|
||||||
|
};
|
||||||
|
|
||||||
|
this.saveQueue = function () {
|
||||||
|
locker.put('CurrentQueue', player.queue);
|
||||||
|
if (globals.settings.Debug) { console.log('Saving Queue: ' + player.queue.length + ' songs'); }
|
||||||
|
};
|
||||||
|
}]);
|
||||||
|
|
110
app/common/persistence-service_test.js
Normal file
110
app/common/persistence-service_test.js
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
describe("Persistence service", function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var persistence, player, notifications, locker;
|
||||||
|
var song;
|
||||||
|
beforeEach(function() {
|
||||||
|
module('jamstash.persistence');
|
||||||
|
|
||||||
|
inject(function (_persistence_, _player_, _notifications_, _locker_) {
|
||||||
|
persistence = _persistence_;
|
||||||
|
player = _player_;
|
||||||
|
notifications = _notifications_;
|
||||||
|
locker = _locker_;
|
||||||
|
});
|
||||||
|
|
||||||
|
song = {
|
||||||
|
id: 8626,
|
||||||
|
name: 'Pectinatodenticulate',
|
||||||
|
artist: 'Isiah Hosfield',
|
||||||
|
album: 'Tammanyize'
|
||||||
|
};
|
||||||
|
player.queue = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("load from localStorage -", function() {
|
||||||
|
var fakeStorage;
|
||||||
|
beforeEach(function() {
|
||||||
|
fakeStorage = {};
|
||||||
|
|
||||||
|
spyOn(locker, "get").and.callFake(function(key) {
|
||||||
|
return fakeStorage[key];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("loadTrackPosition -", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
spyOn(player, "load");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Given that we previously saved the current track's position in local Storage, it loads the song we saved into the player", function() {
|
||||||
|
fakeStorage = { 'CurrentSong': song };
|
||||||
|
|
||||||
|
persistence.loadTrackPosition();
|
||||||
|
|
||||||
|
expect(locker.get).toHaveBeenCalledWith('CurrentSong');
|
||||||
|
expect(player.load).toHaveBeenCalledWith(song);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Given that we didn't save anything in local Storage, it doesn't load anything", function() {
|
||||||
|
persistence.loadTrackPosition();
|
||||||
|
expect(locker.get).toHaveBeenCalledWith('CurrentSong');
|
||||||
|
expect(player.load).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("loadQueue -", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
spyOn(notifications, "updateMessage");
|
||||||
|
spyOn(player, "addSongs").and.callFake(function (songs) {
|
||||||
|
// Update the queue length so that notifications work
|
||||||
|
player.queue.length += songs.length;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Given that we previously saved the playing queue in local Storage, it fills the player's queue with what we saved and notifies the user", function() {
|
||||||
|
var queue = [
|
||||||
|
{ id: 8705 },
|
||||||
|
{ id: 1617 },
|
||||||
|
{ id: 9812 }
|
||||||
|
];
|
||||||
|
fakeStorage = { 'CurrentQueue': queue };
|
||||||
|
|
||||||
|
persistence.loadQueue();
|
||||||
|
|
||||||
|
expect(locker.get).toHaveBeenCalledWith('CurrentQueue');
|
||||||
|
expect(player.addSongs).toHaveBeenCalledWith(queue);
|
||||||
|
expect(notifications.updateMessage).toHaveBeenCalledWith('3 Saved Song(s)', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Given that we didn't save anything in local Storage, it doesn't load anything", function() {
|
||||||
|
persistence.loadQueue();
|
||||||
|
|
||||||
|
expect(locker.get).toHaveBeenCalledWith('CurrentQueue');
|
||||||
|
expect(player.addSongs).not.toHaveBeenCalled();
|
||||||
|
expect(notifications.updateMessage).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("save from localStorage -", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
spyOn(locker, "put");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("it saves the current track's position in local Storage", function() {
|
||||||
|
persistence.saveTrackPosition(song);
|
||||||
|
expect(locker.put).toHaveBeenCalledWith('CurrentSong', song);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("it saves the playing queue in local Storage", function() {
|
||||||
|
player.queue = [
|
||||||
|
{ id: 1245 },
|
||||||
|
{ id: 7465 },
|
||||||
|
{ id: 948 }
|
||||||
|
];
|
||||||
|
persistence.saveQueue();
|
||||||
|
expect(locker.put).toHaveBeenCalledWith('CurrentQueue', player.queue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -103,6 +103,7 @@
|
||||||
<script src="common/model-service.js"></script>
|
<script src="common/model-service.js"></script>
|
||||||
<script src="common/utils-service.js"></script>
|
<script src="common/utils-service.js"></script>
|
||||||
<script src="common/notification-service.js"></script>
|
<script src="common/notification-service.js"></script>
|
||||||
|
<script src="common/persistence-service.js"></script>
|
||||||
<script src="common/main-controller.js"></script>
|
<script src="common/main-controller.js"></script>
|
||||||
<script src="subsonic/subsonic.js"></script>
|
<script src="subsonic/subsonic.js"></script>
|
||||||
<script src="subsonic/subsonic-service.js"></script>
|
<script src="subsonic/subsonic-service.js"></script>
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
* Encapsulates the jPlayer plugin. It watches the player service for the song to play, load or restart.
|
* Encapsulates the jPlayer plugin. It watches the player service for the song to play, load or restart.
|
||||||
* It also enables jPlayer to attach event handlers to our UI through css Selectors.
|
* It also enables jPlayer to attach event handlers to our UI through css Selectors.
|
||||||
*/
|
*/
|
||||||
angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstash.settings', 'jamstash.subsonic.service', 'jamstash.notifications', 'jamstash.utils', 'angular-locker'])
|
angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstash.settings', 'jamstash.subsonic.service', 'jamstash.notifications', 'jamstash.utils', 'jamstash.persistence'])
|
||||||
|
|
||||||
.directive('jplayer', ['player', 'globals', 'subsonic', 'notifications', 'utils', 'locker', '$window',
|
.directive('jplayer', ['player', 'globals', 'subsonic', 'notifications', 'utils', '$window', 'persistence',
|
||||||
function(playerService, globals, subsonic, notifications, utils, locker, $window) {
|
function(playerService, globals, subsonic, notifications, utils, $window, persistence) {
|
||||||
'use strict';
|
'use strict';
|
||||||
return {
|
return {
|
||||||
restrict: 'EA',
|
restrict: 'EA',
|
||||||
|
@ -30,6 +30,8 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
|
||||||
solution: audioSolution,
|
solution: audioSolution,
|
||||||
supplied: 'mp3',
|
supplied: 'mp3',
|
||||||
preload: 'auto',
|
preload: 'auto',
|
||||||
|
errorAlerts: false,
|
||||||
|
warningAlerts: false,
|
||||||
cssSelectorAncestor: '#player',
|
cssSelectorAncestor: '#player',
|
||||||
cssSelector: {
|
cssSelector: {
|
||||||
play: '.PlayTrack',
|
play: '.PlayTrack',
|
||||||
|
@ -68,6 +70,16 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
|
||||||
subsonic.scrobble(scope.currentSong);
|
subsonic.scrobble(scope.currentSong);
|
||||||
scope.scrobbled = true;
|
scope.scrobbled = true;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
error: function (event) {
|
||||||
|
var position = event.jPlayer.status.currentTime;
|
||||||
|
if(position) {
|
||||||
|
$player.jPlayer('play', position);
|
||||||
|
}
|
||||||
|
if (globals.settings.Debug) {
|
||||||
|
console.log("jPlayer error: ", event.jPlayer.error);
|
||||||
|
console.log("Stream interrupted, retrying from position: ", position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -75,17 +87,19 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
|
||||||
scope.$watch(function () {
|
scope.$watch(function () {
|
||||||
return playerService.getPlayingSong();
|
return playerService.getPlayingSong();
|
||||||
}, function (newSong) {
|
}, function (newSong) {
|
||||||
scope.currentSong = newSong;
|
if(newSong !== undefined) {
|
||||||
$player.jPlayer('setMedia', {'mp3': newSong.url});
|
scope.currentSong = newSong;
|
||||||
if(playerService.loadSong === true) {
|
$player.jPlayer('setMedia', {'mp3': newSong.url});
|
||||||
// Do not play, only load
|
if(playerService.loadSong === true) {
|
||||||
playerService.loadSong = false;
|
// Do not play, only load
|
||||||
scope.revealControls();
|
playerService.loadSong = false;
|
||||||
$player.jPlayer('pause', newSong.position);
|
scope.revealControls();
|
||||||
} else {
|
$player.jPlayer('pause', newSong.position);
|
||||||
$player.jPlayer('play');
|
} else {
|
||||||
if(globals.settings.NotificationSong) {
|
$player.jPlayer('play');
|
||||||
notifications.showNotification(newSong.coverartthumb, utils.toHTML.un(newSong.name), utils.toHTML.un(newSong.artist + ' - ' + newSong.album), 'text', '#NextTrack');
|
if(globals.settings.NotificationSong) {
|
||||||
|
notifications.showNotification(newSong.coverartthumb, utils.toHTML.un(newSong.name), utils.toHTML.un(newSong.artist + ' - ' + newSong.album), 'text', '#NextTrack');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -104,23 +118,6 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
|
||||||
$('#songdetails').css('visibility', 'visible');
|
$('#songdetails').css('visibility', 'visible');
|
||||||
};
|
};
|
||||||
|
|
||||||
scope.saveTrackPosition = function () {
|
|
||||||
var audio = $player.data('jPlayer');
|
|
||||||
if (audio !== undefined && scope.currentSong !== undefined) {
|
|
||||||
var position = audio.status.currentTime;
|
|
||||||
if (position !== null) {
|
|
||||||
scope.currentSong.position = position;
|
|
||||||
locker.put('CurrentSong', scope.currentSong);
|
|
||||||
if (globals.settings.Debug) { console.log('Saving Current Position: ', scope.currentSong); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.saveQueue = function () {
|
|
||||||
locker.put('CurrentQueue', playerService.queue);
|
|
||||||
if (globals.settings.Debug) { console.log('Saving Queue: ' + playerService.queue.length + ' songs'); }
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.startSavePosition = function () {
|
scope.startSavePosition = function () {
|
||||||
if (globals.settings.SaveTrackPosition) {
|
if (globals.settings.SaveTrackPosition) {
|
||||||
if (timerid !== 0) {
|
if (timerid !== 0) {
|
||||||
|
@ -128,15 +125,16 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
|
||||||
}
|
}
|
||||||
timerid = $window.setInterval(function () {
|
timerid = $window.setInterval(function () {
|
||||||
var audio = $player.data('jPlayer');
|
var audio = $player.data('jPlayer');
|
||||||
if (globals.settings.SaveTrackPosition && audio.status.currentTime > 0 && audio.status.paused === false) {
|
if (globals.settings.SaveTrackPosition && scope.currentSong !== undefined &&
|
||||||
|
audio !== undefined && audio.status.currentTime > 0 && audio.status.paused === false) {
|
||||||
$('#action_SaveProgress')
|
$('#action_SaveProgress')
|
||||||
.fadeTo("slow", 0).delay(500)
|
.fadeTo("slow", 0).delay(500)
|
||||||
.fadeTo("slow", 1).delay(500)
|
.fadeTo("slow", 1).delay(500)
|
||||||
.fadeTo("slow", 0).delay(500)
|
.fadeTo("slow", 0).delay(500)
|
||||||
.fadeTo("slow", 1);
|
.fadeTo("slow", 1);
|
||||||
//TODO: Hyz: Pass position and queue as parameter and move this away in another service
|
scope.currentSong.position = audio.status.currentTime;
|
||||||
scope.saveTrackPosition();
|
persistence.saveTrackPosition(scope.currentSong);
|
||||||
scope.saveQueue();
|
persistence.saveQueue();
|
||||||
}
|
}
|
||||||
}, 30000);
|
}, 30000);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,17 @@ describe("jplayer directive", function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var element, scope, $player, playingSong,
|
var element, scope, $player, playingSong,
|
||||||
playerService, mockGlobals, subsonic, notifications, locker, $window;
|
playerService, mockGlobals, subsonic, notifications, persistence, $window;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
playingSong = {};
|
|
||||||
// We redefine globals because in some tests we need to alter the settings
|
// We redefine globals because in some tests we need to alter the settings
|
||||||
mockGlobals = {
|
mockGlobals = {
|
||||||
settings: {
|
settings: {
|
||||||
AutoPlay: false
|
AutoPlay: false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Redefined to avoid firing 'play' with a previous test song
|
||||||
|
playingSong = undefined;
|
||||||
module('jamstash.player.directive', function($provide) {
|
module('jamstash.player.directive', function($provide) {
|
||||||
// Mock the player service
|
// Mock the player service
|
||||||
$provide.decorator('player', function($delegate) {
|
$provide.decorator('player', function($delegate) {
|
||||||
|
@ -32,11 +33,12 @@ describe("jplayer directive", function() {
|
||||||
$provide.value('globals', mockGlobals);
|
$provide.value('globals', mockGlobals);
|
||||||
});
|
});
|
||||||
|
|
||||||
inject(function($rootScope, $compile, _player_, _subsonic_, _notifications_, _locker_, _$window_) {
|
spyOn($.fn, "jPlayer").and.callThrough();
|
||||||
|
inject(function($rootScope, $compile, _player_, _subsonic_, _notifications_, _persistence_, _$window_) {
|
||||||
playerService = _player_;
|
playerService = _player_;
|
||||||
subsonic = _subsonic_;
|
subsonic = _subsonic_;
|
||||||
notifications = _notifications_;
|
notifications = _notifications_;
|
||||||
locker = _locker_;
|
persistence = _persistence_;
|
||||||
$window = _$window_;
|
$window = _$window_;
|
||||||
// Compile the directive
|
// Compile the directive
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
|
@ -48,28 +50,28 @@ describe("jplayer directive", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("When the player service's current song changes,", function() {
|
describe("When the player service's current song changes,", function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
spyOn($.fn, "jPlayer").and.returnValue($.fn);
|
// To avoid errors breaking the test, we stub jPlayer
|
||||||
|
$.fn.jPlayer.and.stub();
|
||||||
playingSong = {url: 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'};
|
playingSong = {url: 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'};
|
||||||
});
|
});
|
||||||
|
|
||||||
it("it sets jPlayer's media and stores the song for future scrobbling", function() {
|
it("it sets jPlayer's media and stores the song for future scrobbling", function() {
|
||||||
scope.$apply();
|
scope.$apply();
|
||||||
|
|
||||||
expect($player.jPlayer).toHaveBeenCalledWith('setMedia', {'mp3': 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'});
|
expect($.fn.jPlayer).toHaveBeenCalledWith('setMedia', {'mp3': 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'});
|
||||||
expect(scope.currentSong).toEqual(playingSong);
|
expect(scope.currentSong).toEqual(playingSong);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("if the player service's loadSong flag is true, it does not play the song, it displays the player controls and sets the player to the song's supplied position", function() {
|
it("if the player service's loadSong flag is true, it does not play the song, it displays the player controls and sets the player to the song's supplied position", function() {
|
||||||
spyOn(scope, "revealControls");
|
spyOn(scope, "revealControls");
|
||||||
|
playerService.loadSong = true;
|
||||||
playingSong.position = 42.2784;
|
playingSong.position = 42.2784;
|
||||||
|
|
||||||
playerService.loadSong = true;
|
|
||||||
scope.$apply();
|
scope.$apply();
|
||||||
|
|
||||||
expect($player.jPlayer).not.toHaveBeenCalledWith('play');
|
expect($player.jPlayer).not.toHaveBeenCalledWith('play');
|
||||||
expect($player.jPlayer).toHaveBeenCalledWith('pause', playingSong.position);
|
expect($player.jPlayer).toHaveBeenCalledWith('pause', 42.2784);
|
||||||
expect(playerService.loadSong).toBeFalsy();
|
expect(playerService.loadSong).toBeFalsy();
|
||||||
expect(scope.revealControls).toHaveBeenCalled();
|
expect(scope.revealControls).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
@ -86,6 +88,7 @@ describe("jplayer directive", function() {
|
||||||
it("if the global setting NotificationSong is true, it displays a notification", function() {
|
it("if the global setting NotificationSong is true, it displays a notification", function() {
|
||||||
spyOn(notifications, "showNotification");
|
spyOn(notifications, "showNotification");
|
||||||
mockGlobals.settings.NotificationSong = true;
|
mockGlobals.settings.NotificationSong = true;
|
||||||
|
|
||||||
scope.$apply();
|
scope.$apply();
|
||||||
|
|
||||||
expect(notifications.showNotification).toHaveBeenCalled();
|
expect(notifications.showNotification).toHaveBeenCalled();
|
||||||
|
@ -94,8 +97,7 @@ describe("jplayer directive", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("When the player service's restartSong flag is true, it restarts the current song and resets the flag to false", function() {
|
it("When the player service's restartSong flag is true, it restarts the current song and resets the flag to false", function() {
|
||||||
spyOn($.fn, "jPlayer").and.returnValue($.fn);
|
$.fn.jPlayer.and.stub();
|
||||||
|
|
||||||
playerService.restartSong = true;
|
playerService.restartSong = true;
|
||||||
scope.$apply();
|
scope.$apply();
|
||||||
|
|
||||||
|
@ -125,15 +127,6 @@ describe("jplayer directive", function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("When jPlayer starts to play the current song, it displays the player controls", function() {
|
|
||||||
spyOn(scope, "revealControls");
|
|
||||||
|
|
||||||
var e = $.jPlayer.event.play;
|
|
||||||
$player.trigger(e);
|
|
||||||
|
|
||||||
expect(scope.revealControls).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("When jPlayer gets new media, it resets the scrobbled flag to false", function() {
|
it("When jPlayer gets new media, it resets the scrobbled flag to false", function() {
|
||||||
scope.scrobbled = true;
|
scope.scrobbled = true;
|
||||||
|
|
||||||
|
@ -143,7 +136,30 @@ describe("jplayer directive", function() {
|
||||||
expect(scope.scrobbled).toBeFalsy();
|
expect(scope.scrobbled).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("When jPlayer throws an error, it tries to restart playback at the last position", function() {
|
||||||
|
// Fake jPlayer's internal _trigger event because I can't trigger a manual error
|
||||||
|
var fakejPlayer = {
|
||||||
|
element: $player,
|
||||||
|
status: { currentTime: 10.4228 }
|
||||||
|
};
|
||||||
|
var error = $.jPlayer.event.error;
|
||||||
|
|
||||||
|
$.jPlayer.prototype._trigger.call(fakejPlayer, error);
|
||||||
|
|
||||||
|
expect($player.jPlayer).toHaveBeenCalledWith('play', 10.4228);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("When jPlayer starts to play the current song, it displays the player controls", function() {
|
||||||
|
spyOn(scope, "revealControls");
|
||||||
|
|
||||||
|
var e = $.jPlayer.event.play;
|
||||||
|
$player.trigger(e);
|
||||||
|
|
||||||
|
expect(scope.revealControls).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it("revealControls - it displays the song details and the player controls", function() {
|
it("revealControls - it displays the song details and the player controls", function() {
|
||||||
|
$.fn.jPlayer.and.stub();
|
||||||
affix('#playermiddle').css('visibility', 'hidden');
|
affix('#playermiddle').css('visibility', 'hidden');
|
||||||
affix('#songdetails').css('visibility', 'hidden');
|
affix('#songdetails').css('visibility', 'hidden');
|
||||||
|
|
||||||
|
@ -190,78 +206,48 @@ describe("jplayer directive", function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("save to localStorage -", function() {
|
describe("Given that the global setting SaveTrackPosition is true,", function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
spyOn(locker, "put");
|
mockGlobals.settings.SaveTrackPosition = true;
|
||||||
|
spyOn(persistence, "saveTrackPosition");
|
||||||
|
spyOn(persistence, "saveQueue");
|
||||||
|
jasmine.clock().install();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("it saves the current song and its position to localStorage", function() {
|
afterEach(function() {
|
||||||
var position = 48.0773;
|
jasmine.clock().uninstall();
|
||||||
$player.data('jPlayer').status.currentTime = position;
|
|
||||||
scope.currentSong = {
|
|
||||||
id: 419
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.saveTrackPosition();
|
|
||||||
|
|
||||||
expect(scope.currentSong.position).toBe(position);
|
|
||||||
expect(locker.put).toHaveBeenCalledWith('CurrentSong', scope.currentSong);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("it saves the player queue to localStorage", function() {
|
it("every 30 seconds, it saves the current song's position and the playing queue", function() {
|
||||||
var queue = [
|
scope.currentSong = { id: 419 };
|
||||||
{id: 2313},
|
$player.data('jPlayer').status.currentTime = 35.3877;
|
||||||
{id: 4268},
|
$player.data('jPlayer').status.paused = false;
|
||||||
{id: 5470}
|
|
||||||
];
|
|
||||||
playerService.queue = queue;
|
|
||||||
|
|
||||||
scope.saveQueue();
|
scope.startSavePosition();
|
||||||
|
jasmine.clock().tick(30001);
|
||||||
|
|
||||||
expect(locker.put).toHaveBeenCalledWith('CurrentQueue', queue);
|
expect(scope.currentSong.position).toBe(35.3877);
|
||||||
|
expect(persistence.saveTrackPosition).toHaveBeenCalledWith(scope.currentSong);
|
||||||
|
expect(persistence.saveQueue).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Given that the global setting SaveTrackPosition is true,", function() {
|
it("if the song is not playing, it does not save anything", function() {
|
||||||
beforeEach(function() {
|
$player.data('jPlayer').status.currentTime = 0.0;
|
||||||
jasmine.clock().install();
|
$player.data('jPlayer').status.paused = true;
|
||||||
mockGlobals.settings.SaveTrackPosition = true;
|
|
||||||
spyOn(scope, "saveTrackPosition");
|
|
||||||
spyOn(scope, "saveQueue");
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function() {
|
scope.startSavePosition();
|
||||||
jasmine.clock().uninstall();
|
jasmine.clock().tick(30001);
|
||||||
});
|
|
||||||
|
|
||||||
it("every 30 seconds, it saves the current song and queue", function() {
|
expect(persistence.saveTrackPosition).not.toHaveBeenCalled();
|
||||||
$player.data('jPlayer').status.currentTime = 35.3877;
|
expect(persistence.saveQueue).not.toHaveBeenCalled();
|
||||||
$player.data('jPlayer').status.paused = false;
|
});
|
||||||
|
|
||||||
scope.startSavePosition();
|
it("if there was already a watcher, it clears it before adding a new one", function() {
|
||||||
jasmine.clock().tick(30001);
|
spyOn($window, "clearInterval");
|
||||||
|
|
||||||
expect(scope.saveTrackPosition).toHaveBeenCalled();
|
scope.startSavePosition();
|
||||||
expect(scope.saveQueue).toHaveBeenCalled();
|
scope.startSavePosition();
|
||||||
});
|
expect($window.clearInterval).toHaveBeenCalled();
|
||||||
|
|
||||||
it("if the song is not playing, it does not save anything", function() {
|
|
||||||
$player.data('jPlayer').status.currentTime = 0.0;
|
|
||||||
$player.data('jPlayer').status.paused = true;
|
|
||||||
|
|
||||||
scope.startSavePosition();
|
|
||||||
jasmine.clock().tick(30001);
|
|
||||||
|
|
||||||
expect(scope.saveTrackPosition).not.toHaveBeenCalled();
|
|
||||||
expect(scope.saveQueue).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("if there was already a watcher, it clears it before watching", function() {
|
|
||||||
spyOn($window, "clearInterval");
|
|
||||||
|
|
||||||
scope.startSavePosition();
|
|
||||||
scope.startSavePosition();
|
|
||||||
expect($window.clearInterval).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue