Removes the player functions from the rootScope.

However, saving track's position is broken.
Some are still there because of problems I don't know how to solve, e.g. circular dependency between notifications and player.
Uses the queue controller for the sidebar queue.
Moves loadTrackPosition to the main controller.
This commit is contained in:
Hyzual 2014-11-30 18:02:15 +01:00
parent 74ac275ece
commit e5846e30f9
14 changed files with 352 additions and 278 deletions

View file

@ -1,7 +1,7 @@
/* Declare app level module */ /* Declare app level module */
angular.module('JamStash', ['ngCookies', 'ngRoute', 'ngSanitize', angular.module('JamStash', ['ngCookies', 'ngRoute', 'ngSanitize',
'jamstash.subsonic.ctrl', 'jamstash.archive.ctrl', 'jamstash.player.ctrl']) 'jamstash.subsonic.ctrl', 'jamstash.archive.ctrl', 'jamstash.player.ctrl', 'jamstash.queue.ctrl'])
.config(['$routeProvider',function($routeProvider) { .config(['$routeProvider',function($routeProvider) {
'use strict'; 'use strict';

View file

@ -3,10 +3,11 @@
* *
* Access Archive.org * Access Archive.org
*/ */
angular.module('jamstash.archive.service', ['jamstash.settings', 'jamstash.model', 'jamstash.notifications']) angular.module('jamstash.archive.service', ['jamstash.settings', 'jamstash.model', 'jamstash.notifications',
'jamstash.player.service'])
.factory('archive', ['$rootScope', '$http', '$q', '$sce', 'globals', 'model', 'utils', 'map', 'notifications', .factory('archive', ['$rootScope', '$http', '$q', '$sce', 'globals', 'model', 'utils', 'map', 'notifications', 'player',
function($rootScope, $http, $q, $sce, globals, model, utils, map, notifications) { function($rootScope, $http, $q, $sce, globals, model, utils, map, notifications, player) {
'use strict'; 'use strict';
var index = { shortcuts: [], artists: [] }; var index = { shortcuts: [], artists: [] };
@ -192,7 +193,7 @@ angular.module('jamstash.archive.service', ['jamstash.settings', 'jamstash.model
} }
}); });
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$rootScope.playSong(false, next); player.playSong(false, next);
notifications.updateMessage(Object.keys(items).length + ' Song(s) Added to Queue', true); notifications.updateMessage(Object.keys(items).length + ' Song(s) Added to Queue', true);
} else { } else {
content.album = []; content.album = [];
@ -213,4 +214,4 @@ angular.module('jamstash.archive.service', ['jamstash.settings', 'jamstash.model
return deferred.promise; return deferred.promise;
} }
}; };
}]); }]);

View file

@ -10,7 +10,7 @@
$rootScope.MusicFolders = []; $rootScope.MusicFolders = [];
$rootScope.Genres = []; $rootScope.Genres = [];
$rootScope.Messages = []; $rootScope.Messages = [];
$rootScope.SelectedMusicFolder = ""; $rootScope.SelectedMusicFolder = "";
$rootScope.unity = null; $rootScope.unity = null;
$rootScope.loggedIn = function () { $rootScope.loggedIn = function () {
@ -27,13 +27,6 @@
$rootScope.go = function (path) { $rootScope.go = function (path) {
$location.path(path); $location.path(path);
}; };
/*
$scope.playSong = function (loadonly, data) {
$scope.$apply(function () {
$rootScope.playSong(loadonly, data);
});
}
*/
// Reads cookies and sets globals.settings values // Reads cookies and sets globals.settings values
$scope.loadSettings = function () { $scope.loadSettings = function () {
@ -226,9 +219,9 @@
$('#left-component').stop().scrollTo(el, 400); $('#left-component').stop().scrollTo(el, 400);
} }
} else if (unicode == 39 || unicode == 176) { // right arrow } else if (unicode == 39 || unicode == 176) { // right arrow
$rootScope.nextTrack(); player.nextTrack();
} else if (unicode == 37 || unicode == 177) { // back arrow } else if (unicode == 37 || unicode == 177) { // back arrow
$rootScope.previousTrack(); player.previousTrack();
} else if (unicode == 32 || unicode == 179 || unicode.toString() == '0179') { // spacebar } else if (unicode == 32 || unicode == 179 || unicode.toString() == '0179') { // spacebar
player.playPauseSong(); player.playPauseSong();
return false; return false;
@ -286,7 +279,7 @@
$rootScope.selectAll(songs); $rootScope.selectAll(songs);
$rootScope.addSongsToQueue(); $rootScope.addSongsToQueue();
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$rootScope.playSong(false, next); player.playSong(false, next);
}; };
$rootScope.playFrom = function (index, songs) { $rootScope.playFrom = function (index, songs) {
var from = songs.slice(index,songs.length); var from = songs.slice(index,songs.length);
@ -299,7 +292,7 @@
$rootScope.queue = []; $rootScope.queue = [];
$rootScope.addSongsToQueue(); $rootScope.addSongsToQueue();
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$rootScope.playSong(false, next); player.playSong(false, next);
} }
}; };
$rootScope.addSongsToQueue = function () { $rootScope.addSongsToQueue = function () {
@ -417,6 +410,12 @@
} }
}); });
}; };
// Hyz: I don't know yet how to remove the circular dependency between player-service
// and notification-service... So I'll keep this one there until I know.
$rootScope.nextTrack = function (loadonly, song) {
player.nextTrack(loadonly, song);
};
$scope.updateFavorite = function (item) { $scope.updateFavorite = function (item) {
var id = item.id; var id = item.id;
var starred = item.starred; var starred = item.starred;
@ -441,7 +440,27 @@
$scope.toTrusted = function (html) { $scope.toTrusted = function (html) {
return $sce.trustAsHtml(html); return $sce.trustAsHtml(html);
}; };
function loadTrackPosition() {
if (utils.browserStorageCheck()) {
// Load Saved Song
var song = angular.fromJson(localStorage.getItem('CurrentSong'));
if (song) {
player.playSong(true, song);
// Load Saved Queue
var items = angular.fromJson(localStorage.getItem('CurrentQueue'));
if (items) {
$rootScope.queue = items;
if ($rootScope.queue.length > 0) {
notifications.updateMessage($rootScope.queue.length + ' Saved Song(s)', true);
}
if (globals.settings.Debug) { console.log('Play Queue Loaded From localStorage: ' + $rootScope.queue.length + ' song(s)'); }
}
}
} else {
if (globals.settings.Debug) { console.log('HTML5::loadStorage not supported on your browser'); }
}
}
/* Launch on Startup */ /* Launch on Startup */
$scope.loadSettings(); $scope.loadSettings();
@ -454,7 +473,7 @@
if ($scope.loggedIn()) { if ($scope.loggedIn()) {
//$scope.ping(); //$scope.ping();
if (globals.settings.SaveTrackPosition) { if (globals.settings.SaveTrackPosition) {
player.loadTrackPosition(); loadTrackPosition();
player.startSaveTrackPosition(); player.startSaveTrackPosition();
} }
} }

View file

@ -0,0 +1,30 @@
describe("Main controller", function() {
'use strict';
describe("updateFavorite -", function() {
it("when starring a song, it notifies the user that the star was saved", function() {
});
it("when starring an album, it notifies the user that the star was saved", function() {
});
it("when starring an artist, it notifies the user that the star was saved", function() {
});
it("given that the Subsonic server returns an error, when starring something, it notifies the user with the error message", function() {
//TODO: move to higher level
});
it("given that the Subsonic server is unreachable, when starring something, it notifies the user with the HTTP error code", function() {
//TODO: move to higher level
});
});
describe("toggleSetting -", function() {
});
});

View file

@ -64,4 +64,4 @@ angular.module('jamstash.notifications', [])
notifications[notification].close(); notifications[notification].close();
} }
}; };
}]); }]);

View file

@ -49,7 +49,7 @@
<div class="clear"></div> <div class="clear"></div>
</div><!-- end #content --> </div><!-- end #content -->
<div id="SideBar"> <div id="SideBar" ng-controller="QueueCtrl">
<div class="headeractions"> <div class="headeractions">
<a class="buttonimg" title="Shuffle Queue" ng-click="queueShuffle()"><img src="images/fork_gd_11x12.png"></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_Empty" title="Delete Queue" ng-click="queueEmpty()"><img src="images/trash_fill_gd_12x12.png"></a>

View file

@ -9,32 +9,7 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
var player2 = '#playdeck_2'; var player2 = '#playdeck_2';
var scrobbled = false; var scrobbled = false;
var timerid = 0; var timerid = 0;
this.defaultPlay = function (data, event) {
if (typeof $(player1).data("jPlayer") == 'undefined') {
$rootScope.nextTrack();
}
if (typeof $(player1).data("jPlayer") != 'undefined' && globals.settings.Jukebox) {
if ($(player1).data("jPlayer").status.paused) {
$rootScope.sendToJukebox('start');
} else {
$rootScope.sendToJukebox('stop');
}
}
};
this.nextTrack = function () {
var next = getNextSong();
if (next) {
$rootScope.playSong(false, next);
}
};
this.previousTrack = function () {
var next = getNextSong(true);
if (next) {
$rootScope.playSong(false, next);
} else {
$rootScope.restartSong();
}
};
function getNextSong (previous) { function getNextSong (previous) {
var song; var song;
if (globals.settings.Debug) { console.log('Getting Next Song > ' + 'Queue length: ' + $rootScope.queue.length); } if (globals.settings.Debug) { console.log('Getting Next Song > ' + 'Queue length: ' + $rootScope.queue.length); }
@ -63,6 +38,32 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
return false; return false;
} }
} }
this.nextTrack = function () {
var next = getNextSong();
if (next) {
this.playSong(false, next);
}
};
this.previousTrack = function () {
var next = getNextSong(true);
if (next) {
this.playSong(false, next);
} else {
this.restartSong();
}
};
this.defaultPlay = function (data, event) {
if (typeof $(player1).data("jPlayer") == 'undefined') {
this.nextTrack();
}
if (typeof $(player1).data("jPlayer") != 'undefined' && globals.settings.Jukebox) {
if ($(player1).data("jPlayer").status.paused) {
$rootScope.sendToJukebox('start');
} else {
$rootScope.sendToJukebox('stop');
}
}
};
function internalScrobbleSong(submission) { function internalScrobbleSong(submission) {
if ($rootScope.loggedIn && submission) { if ($rootScope.loggedIn && submission) {
var id = $rootScope.playingSong.id; var id = $rootScope.playingSong.id;
@ -85,22 +86,22 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
} }
timerid = $window.setInterval(function () { timerid = $window.setInterval(function () {
if (globals.settings.SaveTrackPosition) { if (globals.settings.SaveTrackPosition) {
this.saveTrackPosition(); $rootScope.saveTrackPosition();
} }
}, 30000); }, 30000);
} }
}; };
this.toggleMute = function () { this.toggleMute = function () {
//var audio = typeof $(player1).data("jPlayer") != 'undefined' ? true : false; //var audio = typeof $(player1).data("jPlayer") != 'undefined' ? true : false;
var audio = $(player1).data("jPlayer"); var audio = $(player1).data("jPlayer");
if (typeof audio != 'undefined') { if (typeof audio != 'undefined') {
if (audio.options.muted) { if (audio.options.muted) {
audio.options.muted = false; audio.options.muted = false;
} else { } else {
audio.options.muted = true; audio.options.muted = true;
} }
} }
} };
this.saveTrackPosition = function () { this.saveTrackPosition = function () {
//var audio = typeof $(player1).data("jPlayer") != 'undefined' ? true : false; //var audio = typeof $(player1).data("jPlayer") != 'undefined' ? true : false;
var audio = $(player1).data("jPlayer"); var audio = $(player1).data("jPlayer");
@ -146,27 +147,7 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
if (globals.settings.Debug) { console.log('Saving Queue: No Audio Loaded'); } if (globals.settings.Debug) { console.log('Saving Queue: No Audio Loaded'); }
} }
}; };
this.loadTrackPosition = function () {
if (utils.browserStorageCheck()) {
// Load Saved Song
var song = angular.fromJson(localStorage.getItem('CurrentSong'));
if (song) {
$rootScope.playSong(true, song);
// Load Saved Queue
var items = angular.fromJson(localStorage.getItem('CurrentQueue'));
if (items) {
//$rootScope.queue = [];
$rootScope.queue = items;
if ($rootScope.queue.length > 0) {
notifications.updateMessage($rootScope.queue.length + ' Saved Song(s)', true);
}
if (globals.settings.Debug) { console.log('Play Queue Loaded From localStorage: ' + $rootScope.queue.length + ' song(s)'); }
}
}
} else {
if (globals.settings.Debug) { console.log('HTML5::loadStorage not supported on your browser'); }
}
};
this.deleteCurrentQueue = function (data) { this.deleteCurrentQueue = function (data) {
if (utils.browserStorageCheck()) { if (utils.browserStorageCheck()) {
localStorage.removeItem('CurrentQueue'); localStorage.removeItem('CurrentQueue');
@ -181,6 +162,7 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
audio.play(0); audio.play(0);
}; };
this.playSong = function (loadonly, data) { this.playSong = function (loadonly, data) {
console.log('playSong');
if (globals.settings.Debug) { console.log('Play: ' + JSON.stringify(data, null, 2)); } if (globals.settings.Debug) { console.log('Play: ' + JSON.stringify(data, null, 2)); }
angular.forEach($rootScope.queue, function(item, key) { angular.forEach($rootScope.queue, function(item, key) {
item.playing = false; item.playing = false;
@ -208,32 +190,35 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
$('#playermiddle').css('visibility', 'visible'); $('#playermiddle').css('visibility', 'visible');
$('#songdetails').css('visibility', 'visible'); $('#songdetails').css('visibility', 'visible');
if (globals.settings.Jukebox) { if (globals.settings.Jukebox) {
$rootScope.addToJukebox(id); $rootScope.addToJukebox(id);
$rootScope.loadjPlayer(player1, url, suffix, true, position); this.loadjPlayer(player1, url, suffix, true, position);
} else { } else {
$rootScope.loadjPlayer(player1, url, suffix, loadonly, position); this.loadjPlayer(player1, url, suffix, loadonly, position);
} }
var spechtml = ''; var spechtml = '';
/*FIXME: var data = $(player1).data().jPlayer; var playerData = $(player1).data();
for (var i = 0; i < data.solutions.length; i++) { if(playerData !== null) {
var solution = data.solutions[i]; var jplayerData = playerData.jPlayer;
if (data[solution].used) { for (var i = 0; i < jplayerData.solutions.length; i++) {
spechtml += "<strong class=\"codesyntax\">" + solution + "</strong> is"; var solution = jplayerData.solutions[i];
spechtml += " currently being used with<strong>"; if (jplayerData[solution].used) {
angular.forEach(data[solution].support, function (format) { spechtml += "<strong class=\"codesyntax\">" + solution + "</strong> is";
if (data[solution].support[format]) { spechtml += " currently being used with<strong>";
spechtml += " <strong class=\"codesyntax\">" + format + "</strong>"; angular.forEach(jplayerData[solution].support, function (format) {
} if (jplayerData[solution].support[format]) {
}); spechtml += " <strong class=\"codesyntax\">" + format + "</strong>";
spechtml += "</strong> support"; }
} });
}*/ spechtml += "</strong> support";
$('#SMStats').html(spechtml); }
}
}
$('#SMStats').html(spechtml);
scrobbled = false; scrobbled = false;
if ($rootScope.queue.length > 0) { if ($rootScope.queue.length > 0) {
$('#queue').stop().scrollTo('#' + id, 400); $('#queue').stop().scrollTo('#' + id, 400);
} }
@ -250,7 +235,7 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
$rootScope.$apply(); $rootScope.$apply();
} }
}; };
$rootScope.loadjPlayer = function (el, url, suffix, loadonly, position) { this.loadjPlayer = function (el, url, suffix, loadonly, position) {
// jPlayer Setup // jPlayer Setup
var volume = 1; var volume = 1;
if (utils.getValue('Volume')) { if (utils.getValue('Volume')) {
@ -263,8 +248,8 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
if (globals.settings.Debug) { console.log('Setting Audio Solution: ' + audioSolution); } if (globals.settings.Debug) { console.log('Setting Audio Solution: ' + audioSolution); }
//var salt = Math.floor(Math.random() * 100000); //var salt = Math.floor(Math.random() * 100000);
//url += '&salt=' + salt; //url += '&salt=' + salt;
var muted = false; var muted = false;
if (globals.settings.Jukebox) { muted = true;} if (globals.settings.Jukebox) { muted = true;}
$(el).jPlayer("destroy"); $(el).jPlayer("destroy");
$.jPlayer.timeFormat.showHour = true; $.jPlayer.timeFormat.showHour = true;
$(el).jPlayer({ $(el).jPlayer({
@ -273,7 +258,7 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
solution: audioSolution, solution: audioSolution,
supplied: suffix, supplied: suffix,
volume: volume, volume: volume,
muted: muted, muted: muted,
errorAlerts: false, errorAlerts: false,
warningAlerts: false, warningAlerts: false,
cssSelectorAncestor: "", cssSelectorAncestor: "",
@ -294,10 +279,10 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
$(this).jPlayer("setMedia", { $(this).jPlayer("setMedia", {
oga: url oga: url
}); });
} else if (suffix == 'm4a') { } else if (suffix == 'm4a') {
$(this).jPlayer("setMedia", { $(this).jPlayer("setMedia", {
m4a: url m4a: url
}); });
} else if (suffix == 'mp3') { } else if (suffix == 'mp3') {
$(this).jPlayer("setMedia", { $(this).jPlayer("setMedia", {
mp3: url mp3: url
@ -338,13 +323,13 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
if (!getNextSong()) { // Action if we are at the last song in queue if (!getNextSong()) { // Action if we are at the last song in queue
if (globals.settings.LoopQueue) { // Loop to first track in queue if enabled if (globals.settings.LoopQueue) { // Loop to first track in queue if enabled
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$rootScope.playSong(false, next); this.playSong(false, next);
} else if (globals.settings.AutoPlay) { // Load more tracks if enabled } else if (globals.settings.AutoPlay) { // Load more tracks if enabled
$rootScope.getRandomSongs('play', '', ''); $rootScope.getRandomSongs('play', '', '');
notifications.updateMessage('Auto Play Activated...', true); notifications.updateMessage('Auto Play Activated...', true);
} }
} else { } else {
$rootScope.nextTrack(); this.nextTrack();
} }
} }
}, },
@ -413,12 +398,7 @@ angular.module('jamstash.player.service', ['jamstash.utils', 'jamstash.settings'
} }
}); });
}; };
// Hyz: have to maintain that in rootScope because I don't yet know how to call it any other way
// TODO: Hyz: Those are temporary. Remove them when no one else is calling them. // from setInterval
// The idea is to have them so while refactoring so we don't break anything $rootScope.saveTrackPosition = this.saveTrackPosition;
$rootScope.defaultPlay = this.defaultPlay;
$rootScope.nextTrack = this.nextTrack;
$rootScope.previousTrack = this.previousTrack;
$rootScope.restartSong = this.restartSong;
$rootScope.playSong = this.playSong;
}]); }]);

View file

@ -13,7 +13,6 @@ describe("Player service", function() {
describe("Given that I have 3 songs in my playing queue", function() { describe("Given that I have 3 songs in my playing queue", function() {
var stubPlaySong, stubRestartSong;
beforeEach(function() { beforeEach(function() {
$rootScope.queue = [ $rootScope.queue = [
{ {
@ -33,15 +32,15 @@ describe("Player service", function() {
album: 'redux' album: 'redux'
} }
]; ];
stubPlaySong = spyOn($rootScope, "playSong").and.stub(); spyOn(player, "playSong").and.stub();
stubRestartSong = spyOn($rootScope, "restartSong").and.stub(); spyOn(player, "restartSong").and.stub();
}); });
describe("when I call nextTrack", function() { describe("when I call nextTrack", function() {
it("and no song is playing, it plays the first song", function() { it("and no song is playing, it plays the first song", function() {
player.nextTrack(); player.nextTrack();
expect(stubPlaySong).toHaveBeenCalled(); expect(player.playSong).toHaveBeenCalled();
}); });
it("and the first song is playing, it plays the second song", function() { it("and the first song is playing, it plays the second song", function() {
@ -49,7 +48,7 @@ describe("Player service", function() {
player.nextTrack(); player.nextTrack();
expect(stubPlaySong).toHaveBeenCalled(); expect(player.playSong).toHaveBeenCalled();
}); });
it("and the last song is playing, it does nothing", function() { it("and the last song is playing, it does nothing", function() {
@ -57,7 +56,7 @@ describe("Player service", function() {
player.nextTrack(); player.nextTrack();
expect(stubPlaySong).not.toHaveBeenCalled(); expect(player.playSong).not.toHaveBeenCalled();
}); });
}); });
@ -65,7 +64,7 @@ describe("Player service", function() {
it("and no song is playing, it plays the first song", function() { it("and no song is playing, it plays the first song", function() {
player.previousTrack(); player.previousTrack();
expect(stubRestartSong).toHaveBeenCalled(); expect(player.restartSong).toHaveBeenCalled();
}); });
it("and the first song is playing, it restarts the first song", function() { it("and the first song is playing, it restarts the first song", function() {
@ -73,7 +72,7 @@ describe("Player service", function() {
player.previousTrack(); player.previousTrack();
expect(stubRestartSong).toHaveBeenCalled(); expect(player.restartSong).toHaveBeenCalled();
}); });
it("and the last song is playing, it plays the seconde song", function() { it("and the last song is playing, it plays the seconde song", function() {
@ -81,7 +80,7 @@ describe("Player service", function() {
player.previousTrack(); player.previousTrack();
expect(stubPlaySong).toHaveBeenCalled(); expect(player.playSong).toHaveBeenCalled();
}); });
}); });
}); });

View file

@ -1,17 +1,16 @@
describe("Player controller", function() { describe("Player controller", function() {
'use strict'; 'use strict';
var player, scope, deferred; var player, scope;
beforeEach(function() { beforeEach(function() {
module('jamstash.player.ctrl'); module('jamstash.player.ctrl');
inject(function ($controller, $rootScope, _player_, $q) { inject(function ($controller, $rootScope, _player_) {
scope = $rootScope.$new(); scope = $rootScope.$new();
player = _player_; player = _player_;
// Mock the functions of the services // Mock the functions of the services
deferred = $q.defer();
spyOn(player, "nextTrack").and.stub(); spyOn(player, "nextTrack").and.stub();
spyOn(player, "previousTrack").and.stub(); spyOn(player, "previousTrack").and.stub();
spyOn(player, "defaultPlay").and.stub(); spyOn(player, "defaultPlay").and.stub();

View file

@ -1,10 +1,14 @@
angular.module('JamStash') angular.module('jamstash.queue.ctrl', [])
.controller('QueueCtrl', ['$scope', '$rootScope', '$routeParams', '$location', 'utils', 'globals', .controller('QueueCtrl', ['$scope', '$rootScope', '$routeParams', '$location', 'utils', 'globals', 'player',
function QueueCtrl($scope, $rootScope, $routeParams, $location, utils, globals) { function QueueCtrl($scope, $rootScope, $routeParams, $location, utils, globals, player) {
'use strict'; 'use strict';
$scope.settings = globals.settings; $scope.settings = globals.settings;
$scope.song = $rootScope.queue; $scope.song = $rootScope.queue;
//angular.copy($rootScope.queue, $scope.song); //angular.copy($rootScope.queue, $scope.song);
$scope.itemType = 'pl'; $scope.itemType = 'pl';
$scope.playSong = function (loadonly, song) {
player.playSong(loadonly, song);
};
}]); }]);

30
app/queue/queue_test.js Normal file
View file

@ -0,0 +1,30 @@
describe("Queue controller", function() {
'use strict';
var player, scope;
beforeEach(function() {
module('jamstash.queue.ctrl');
inject(function ($controller, $rootScope, _player_) {
scope = $rootScope.$new();
player = _player_;
// Mock the functions of the services
spyOn(player, "playSong").and.stub();
$controller('PlayerCtrl', {
$scope: scope,
player: player
});
});
it("When I call playSong, it calls playSong in the player service", function() {
var fakeSong = {"id": 3174};
scope.playSong(true, fakeSong);
expect(player.playSong).toHaveBeenCalledWith(true, fakeSong);
});
});
});

View file

@ -5,10 +5,10 @@
* Also offers more fine-grained functionality that is not part of Subsonic's API. * Also offers more fine-grained functionality that is not part of Subsonic's API.
*/ */
angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.utils', 'jamstash.model', angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.utils', 'jamstash.model',
'jamstash.notifications', 'angular-underscore/utils']) 'jamstash.notifications', 'jamstash.player.service', 'angular-underscore/utils'])
.factory('subsonic', ['$rootScope', '$http', '$q', 'globals', 'utils', 'map', 'notifications', .factory('subsonic', ['$rootScope', '$http', '$q', 'globals', 'utils', 'map', 'notifications', 'player',
function ($rootScope, $http, $q, globals, utils, map, notifications) { function ($rootScope, $http, $q, globals, utils, map, notifications, player) {
'use strict'; 'use strict';
var index = { shortcuts: [], artists: [] }; var index = { shortcuts: [], artists: [] };
@ -23,7 +23,7 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
selectedArtist: null, selectedArtist: null,
selectedAlbum: null, selectedAlbum: null,
selectedPlaylist: null, selectedPlaylist: null,
selectedAutoPlaylist: null, selectedAutoPlaylist: null,
selectedGenre: null, selectedGenre: null,
selectedPodcast: null selectedPodcast: null
}; };
@ -68,7 +68,7 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
} }
} }
}; };
return { return {
showIndex: $rootScope.showIndex, showIndex: $rootScope.showIndex,
showPlaylist: showPlaylist, showPlaylist: showPlaylist,
@ -82,7 +82,7 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
dataType: globals.settings.Protocol, dataType: globals.settings.Protocol,
timeout: globals.settings.Timeout, timeout: globals.settings.Timeout,
success: function (data) { success: function (data) {
} }
}); });
*/ */
@ -318,7 +318,7 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
$rootScope.queue.push(map.mapSong(item)); $rootScope.queue.push(map.mapSong(item));
}); });
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$rootScope.playSong(false, next); player.playSong(false, next);
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else { } else {
if (typeof data["subsonic-response"].directory.id != 'undefined') { if (typeof data["subsonic-response"].directory.id != 'undefined') {
@ -466,7 +466,7 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
$rootScope.queue.push(map.mapSong(item)); $rootScope.queue.push(map.mapSong(item));
}); });
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$rootScope.playSong(false, next); player.playSong(false, next);
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else { } else {
content.album = []; content.album = [];
@ -545,7 +545,7 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
$rootScope.queue.push(map.mapSong(item)); $rootScope.queue.push(map.mapSong(item));
}); });
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$rootScope.playSong(false, next); player.playSong(false, next);
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else { } else {
content.album = []; content.album = [];
@ -703,7 +703,7 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
} else { } else {
items[0] = data["subsonic-response"].genres; items[0] = data["subsonic-response"].genres;
} }
$rootScope.Genres = items; $rootScope.Genres = items;
$scope.$apply(); $scope.$apply();
} }
@ -775,7 +775,7 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
} }
}); });
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$rootScope.playSong(false, next); player.playSong(false, next);
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else { } else {
content.album = []; content.album = [];
@ -795,4 +795,4 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util
} }
// End subsonic // End subsonic
}; };
}]); }]);

View file

@ -1,13 +1,13 @@
/** /**
* jamstash.subsonic.ctrl Module * jamstash.subsonic.ctrl Module
* *
* Access and use the Subsonic Server. The Controller is in charge of relaying the Service's messages to the user through the * Access and use the Subsonic Server. The Controller is in charge of relaying the Service's messages to the user through the
* notifications. * notifications.
*/ */
angular.module('jamstash.subsonic.ctrl', ['jamstash.subsonic.service']) angular.module('jamstash.subsonic.ctrl', ['jamstash.subsonic.service', 'jamstash.player.service'])
.controller('SubsonicCtrl', ['$scope', '$rootScope', '$routeParams', 'utils', 'globals', 'map', 'subsonic', 'notifications', .controller('SubsonicCtrl', ['$scope', '$rootScope', '$routeParams', 'utils', 'globals', 'map', 'subsonic', 'notifications', 'player',
function SubsonicCtrl($scope, $rootScope, $routeParams, utils, globals, map, subsonic, notifications) { function SubsonicCtrl($scope, $rootScope, $routeParams, utils, globals, map, subsonic, notifications, player) {
'use strict'; 'use strict';
$scope.settings = globals.settings; $scope.settings = globals.settings;
@ -283,7 +283,7 @@ angular.module('jamstash.subsonic.ctrl', ['jamstash.subsonic.service'])
if(action === 'play') { if(action === 'play') {
$rootScope.queue = [].concat(mappedSongs); $rootScope.queue = [].concat(mappedSongs);
notifications.updateMessage(mappedSongs.length + ' Song(s) Added to Queue', true); notifications.updateMessage(mappedSongs.length + ' Song(s) Added to Queue', true);
$rootScope.playSong(false, $rootScope.queue[0]); player.playSong(false, $rootScope.queue[0]);
} else if (action === 'add') { } else if (action === 'add') {
$rootScope.queue = $rootScope.queue.concat(mappedSongs); $rootScope.queue = $rootScope.queue.concat(mappedSongs);
notifications.updateMessage(mappedSongs.length + ' Song(s) Added to Queue', true); notifications.updateMessage(mappedSongs.length + ' Song(s) Added to Queue', true);
@ -448,6 +448,9 @@ angular.module('jamstash.subsonic.ctrl', ['jamstash.subsonic.service'])
end = ui.item.index(); end = ui.item.index();
$scope.song.splice(end, 0, $scope.song.splice(start, 1)[0]); $scope.song.splice(end, 0, $scope.song.splice(start, 1)[0]);
}; };
$scope.playSong = function (loadonly, song) {
player.playSong(loadonly, song);
};
/* Launch on Startup */ /* Launch on Startup */
$scope.getArtists(); $scope.getArtists();

View file

@ -1,160 +1,169 @@
describe("Subsonic controller", function() { describe("Subsonic controller", function() {
'use strict'; 'use strict';
var scope, $rootScope, subsonic, notifications, deferred; var scope, $rootScope, subsonic, notifications, deferred, player;
beforeEach(function() { beforeEach(function() {
jasmine.addCustomEqualityTester(angular.equals); jasmine.addCustomEqualityTester(angular.equals);
module('jamstash.subsonic.ctrl'); module('jamstash.subsonic.ctrl');
inject(function ($controller, _$rootScope_, utils, globals, map, _subsonic_, _notifications_, $q) { inject(function ($controller, _$rootScope_, utils, globals, map, _subsonic_, _notifications_, $q, _player_) {
$rootScope = _$rootScope_; $rootScope = _$rootScope_;
scope = $rootScope.$new(); scope = $rootScope.$new();
subsonic = _subsonic_; subsonic = _subsonic_;
notifications = _notifications_; notifications = _notifications_;
player = _player_;
// Mock the functions of the services and the rootscope // Mock the functions of the services and the rootscope
deferred = $q.defer(); deferred = $q.defer();
spyOn(subsonic, 'getRandomStarredSongs').and.returnValue(deferred.promise); spyOn(subsonic, 'getRandomStarredSongs').and.returnValue(deferred.promise);
spyOn(map, 'mapSong').and.callFake(function (song) { spyOn(map, 'mapSong').and.callFake(function (song) {
return {id: song.id}; return {id: song.id};
}); });
spyOn(notifications, 'updateMessage'); spyOn(notifications, 'updateMessage').and.stub();
$rootScope.playSong = jasmine.createSpy('playSong'); spyOn(player, 'playSong').and.stub();
$rootScope.queue = []; $rootScope.queue = [];
$controller('SubsonicCtrl', { $controller('SubsonicCtrl', {
$scope: scope, $scope: scope,
$rootScope: $rootScope, $rootScope: $rootScope,
$routeParams: {}, $routeParams: {},
utils: utils, utils: utils,
globals: globals, globals: globals,
map: map, map: map,
subsonic: subsonic, subsonic: subsonic,
notifications: notifications notifications: notifications
}); });
}); });
}); });
//TODO: JMA: It should be the exact same test when getting songs from an album. We aren't testing that it's randomized, that's the service's job. //TODO: JMA: It should be the exact same test when getting songs from an album. We aren't testing that it's randomized, that's the service's job.
describe("getRandomStarred -", function() { describe("getRandomStarred -", function() {
describe("given that my library contains 3 starred songs, ", function() { describe("given that my library contains 3 starred songs, ", function() {
var response = [ var response = [
{id:"2548"}, {id:"8986"}, {id:"2986"} {id:"2548"}, {id:"8986"}, {id:"2986"}
]; ];
it("when displaying random starred songs, it sets the scope with the selected songs", function() { it("when displaying random starred songs, it sets the scope with the selected songs", function() {
scope.getRandomStarredSongs('display'); scope.getRandomStarredSongs('display');
deferred.resolve(response); deferred.resolve(response);
$rootScope.$apply(); $rootScope.$apply();
expect(subsonic.getRandomStarredSongs).toHaveBeenCalled(); expect(subsonic.getRandomStarredSongs).toHaveBeenCalled();
expect(scope.song).toEqual([ expect(scope.song).toEqual([
{id: "2548"}, {id: "8986"}, {id: "2986"} {id: "2548"}, {id: "8986"}, {id: "2986"}
]); ]);
}); });
it("when adding random starred songs, it adds the selected songs to the queue and notifies the user", function() { it("when adding random starred songs, it adds the selected songs to the queue and notifies the user", function() {
scope.getRandomStarredSongs('add'); scope.getRandomStarredSongs('add');
deferred.resolve(response); deferred.resolve(response);
$rootScope.$apply(); $rootScope.$apply();
expect(subsonic.getRandomStarredSongs).toHaveBeenCalled(); expect(subsonic.getRandomStarredSongs).toHaveBeenCalled();
expect($rootScope.queue).toEqual([ expect($rootScope.queue).toEqual([
{id: "2548"}, {id: "8986"}, {id: "2986"} {id: "2548"}, {id: "8986"}, {id: "2986"}
]); ]);
expect(notifications.updateMessage).toHaveBeenCalledWith('3 Song(s) Added to Queue', true); expect(notifications.updateMessage).toHaveBeenCalledWith('3 Song(s) Added to Queue', true);
}); });
it("when playing random starred songs, it plays the first selected song, empties the queue and fills it with the selected songs, and notifies the user", function() { it("when playing random starred songs, it plays the first selected song, empties the queue and fills it with the selected songs, and notifies the user", function() {
$rootScope.queue = [{id: "7666"}]; $rootScope.queue = [{id: "7666"}];
scope.getRandomStarredSongs('play'); scope.getRandomStarredSongs('play');
deferred.resolve(response); deferred.resolve(response);
$rootScope.$apply(); $rootScope.$apply();
expect(subsonic.getRandomStarredSongs).toHaveBeenCalled(); expect(subsonic.getRandomStarredSongs).toHaveBeenCalled();
expect($rootScope.playSong).toHaveBeenCalledWith(false, {id: "2548"}); expect(player.playSong).toHaveBeenCalledWith(false, {id: "2548"});
expect($rootScope.queue).toEqual([ expect($rootScope.queue).toEqual([
{id: "2548"}, {id: "8986"}, {id: "2986"} {id: "2548"}, {id: "8986"}, {id: "2986"}
]); ]);
expect(notifications.updateMessage).toHaveBeenCalledWith('3 Song(s) Added to Queue', true); expect(notifications.updateMessage).toHaveBeenCalledWith('3 Song(s) Added to Queue', true);
}); });
}); });
it("given that I don't have any starred song in my library, when getting random starred songs, it notifies the user with an error message, does not play a song and does not touch the queue", function() { it("given that I don't have any starred song in my library, when getting random starred songs, it notifies the user with an error message, does not play a song and does not touch the queue", function() {
$rootScope.queue = [{id: "7666"}]; $rootScope.queue = [{id: "7666"}];
scope.getRandomStarredSongs('whatever action'); scope.getRandomStarredSongs('whatever action');
deferred.reject({reason: 'No starred songs found on the Subsonic server.'}); deferred.reject({reason: 'No starred songs found on the Subsonic server.'});
$rootScope.$apply(); $rootScope.$apply();
expect(subsonic.getRandomStarredSongs).toHaveBeenCalled(); expect(subsonic.getRandomStarredSongs).toHaveBeenCalled();
expect($rootScope.playSong).not.toHaveBeenCalled(); expect(player.playSong).not.toHaveBeenCalled();
expect($rootScope.queue).toEqual([{id: "7666"}]); expect($rootScope.queue).toEqual([{id: "7666"}]);
expect(notifications.updateMessage).toHaveBeenCalledWith('No starred songs found on the Subsonic server.', true); expect(notifications.updateMessage).toHaveBeenCalledWith('No starred songs found on the Subsonic server.', true);
}); });
it("given that the Subsonic server returns an error, when getting random starred songs, it notifies the user with the error message", function() { it("given that the Subsonic server returns an error, when getting random starred songs, it notifies the user with the error message", function() {
scope.getRandomStarredSongs('whatever action'); scope.getRandomStarredSongs('whatever action');
deferred.reject({reason: 'Error when contacting the Subsonic server.', deferred.reject({reason: 'Error when contacting the Subsonic server.',
subsonicError: {code: 10, message:'Required parameter is missing.'} subsonicError: {code: 10, message:'Required parameter is missing.'}
}); });
$rootScope.$apply(); $rootScope.$apply();
expect(subsonic.getRandomStarredSongs).toHaveBeenCalled(); expect(subsonic.getRandomStarredSongs).toHaveBeenCalled();
expect(notifications.updateMessage).toHaveBeenCalledWith('Error when contacting the Subsonic server. Required parameter is missing.', true); expect(notifications.updateMessage).toHaveBeenCalledWith('Error when contacting the Subsonic server. Required parameter is missing.', true);
}); });
it("given that the Subsonic server is unreachable, when getting random starred songs, it notifies the user with the HTTP error code", function() { it("given that the Subsonic server is unreachable, when getting random starred songs, it notifies the user with the HTTP error code", function() {
scope.getRandomStarredSongs('whatever action'); scope.getRandomStarredSongs('whatever action');
deferred.reject({reason: 'Error when contacting the Subsonic server.', deferred.reject({reason: 'Error when contacting the Subsonic server.',
httpError: 404 httpError: 404
}); });
$rootScope.$apply(); $rootScope.$apply();
expect(subsonic.getRandomStarredSongs).toHaveBeenCalled(); expect(subsonic.getRandomStarredSongs).toHaveBeenCalled();
expect(notifications.updateMessage).toHaveBeenCalledWith('Error when contacting the Subsonic server. HTTP error 404', true); expect(notifications.updateMessage).toHaveBeenCalledWith('Error when contacting the Subsonic server. HTTP error 404', true);
}); });
}); });
describe("reorders playlists by drag and drop - ", function() { describe("reorders playlists by drag and drop - ", function() {
var mockUI; var mockUI;
beforeEach(function() { beforeEach(function() {
scope.song = [{id: "1084"}, {id: "6810"}, {id: "214"}]; scope.song = [{id: "1084"}, {id: "6810"}, {id: "214"}];
mockUI = { mockUI = {
item: {} item: {}
}; };
}); });
it("given a song in a list of songs, when I start dragging it, it records what its starting position in the list was", function() { it("given a song in a list of songs, when I start dragging it, it records what its starting position in the list was", function() {
mockUI.item.index = jasmine.createSpy('index').and.returnValue('1'); mockUI.item.index = jasmine.createSpy('index').and.returnValue('1');
mockUI.item.data = jasmine.createSpy('data'); mockUI.item.data = jasmine.createSpy('data');
scope.dragStart({}, mockUI); scope.dragStart({}, mockUI);
expect(mockUI.item.index).toHaveBeenCalled(); expect(mockUI.item.index).toHaveBeenCalled();
expect(mockUI.item.data).toHaveBeenCalledWith('start', '1'); expect(mockUI.item.data).toHaveBeenCalledWith('start', '1');
}); });
it("given a song in a list of songs that I started dragging, when I drop it, its position in the list of songs has changed", function() { it("given a song in a list of songs that I started dragging, when I drop it, its position in the list of songs has changed", function() {
mockUI.item.index = jasmine.createSpy('index').and.returnValue('0'); mockUI.item.index = jasmine.createSpy('index').and.returnValue('0');
mockUI.item.data = jasmine.createSpy('data').and.returnValue('1'); mockUI.item.data = jasmine.createSpy('data').and.returnValue('1');
scope.dragEnd({}, mockUI); scope.dragEnd({}, mockUI);
expect(mockUI.item.index).toHaveBeenCalled(); expect(mockUI.item.index).toHaveBeenCalled();
expect(mockUI.item.data).toHaveBeenCalledWith('start'); expect(mockUI.item.data).toHaveBeenCalledWith('start');
// The second song should now be first // The second song should now be first
expect(scope.song).toEqual([ expect(scope.song).toEqual([
{id: "6810"}, {id: "1084"}, {id: "214"} {id: "6810"}, {id: "1084"}, {id: "214"}
]); ]);
}); });
}); });
//TODO: JMA: all starred it("When I call playSong, it calls playSong in the player service", function() {
}); var fakeSong = {"id": 3572};
scope.playSong(false, fakeSong);
expect(player.playSong).toHaveBeenCalledWith(false, fakeSong);
});
//TODO: JMA: all starred
});