Rewrites the entire player component.

Not finished ! This is still work in progress and fairly broken in this commit.
The jquery jplayer is now wrapped in an angular directive.
The queue is no longer being managed in rootScope but is a part of the player service. It can be accessed like before and emptied / filled.
The player controller also uses the player service now.
This commit is contained in:
Hyzual 2014-12-15 23:08:40 +01:00
parent e51961c167
commit 88b1e6a6e6
10 changed files with 132 additions and 63 deletions

View file

@ -474,7 +474,7 @@
//$scope.ping(); //$scope.ping();
if (globals.settings.SaveTrackPosition) { if (globals.settings.SaveTrackPosition) {
loadTrackPosition(); loadTrackPosition();
player.startSaveTrackPosition(); //FIXME: player.startSaveTrackPosition();
} }
} }
/* End Startup */ /* End Startup */

View file

@ -75,7 +75,7 @@
--> -->
</div> </div>
<!-- Player --> <!-- Player -->
<jamstash-player ng-controller="PlayerController"></jamstash-player> <div ng-include src="'player/player.html'" ng-controller="PlayerController"></div>
</div> <!-- End container --> </div> <!-- End container -->
<script> <script>
(function (i, s, o, g, r, a, m) { (function (i, s, o, g, r, a, m) {

View file

@ -1,21 +1,79 @@
angular.module('jamstash.player.directive', []) angular.module('jamstash.player.directive', ['jamstash.settings'])
.directive('jamstashPlayer', function(){ .directive('jplayer', ['player', 'globals', function(playerService, globals) {
'use strict'; 'use strict';
// Runs during compile
return { return {
name: 'jamstash.player', restrict: 'EA',
// priority: 1, template: '<div></div>',
// terminal: true, link: function(scope, element, attrs) {
scope: true, // {} = isolate, true = child, false/undefined = no change
// controller: function($scope, $element, $attrs, $transclude) {}, var $player = element.children('div'),
// require: 'ngModel', // Array = multiple requires, ? = optional, ^ = check parent elements cls = 'pause';
restrict: 'E', console.log($player);
// template: '', var audioSolution = 'html,flash';
templateUrl: 'player/player.html', if (globals.settings.ForceFlash) {
replace: true, audioSolution = 'flash,html';
// transclude: true, }
// compile: function(tElement, tAttrs, function transclude(function(scope, cloneLinkingFn){ return function linking(scope, elm, attrs){}})),
// link: function($scope, iElm, iAttrs, controller) {} var updatePlayer = function() {
$player.jPlayer({
// Flash fallback for outdated browser not supporting HTML5 audio/video tags
// http://jplayer.org/download/
swfPath: 'bower_components/jplayer/jquery.jplayer/Jplayer.swf',
wmode: 'window',
solution: audioSolution,
supplied: 'mp3',
preload: 'auto',
cssSelectorAncestor: '#player',
cssSelector: {
play: '.PlayTrack',
pause: '.PauseTrack',
seekBar: '#audiocontainer .scrubber',
playBar: '#audiocontainer .progress',
mute: '#action_Mute',
unmute: '#action_UnMute',
volumeMax: '#action_VolumeMax',
currentTime: '#played',
duration: '#duration'
},
ready: function() {
$player
.jPlayer('setMedia', {
mp3: 'shot_2.mp3'
});
},
play: function() {
console.log('jplayer play');
element.addClass(cls);
},
pause: function() {
console.log('jplayer pause');
element.removeClass(cls);
},
stop: function() {
console.log('jplayer stop');
element.removeClass(cls);
},
ended: function() {
console.log('jplayer ended');
element.removeClass(cls);
}
})
.end()
.unbind('click').click(function(e) {
$player.jPlayer(element.hasClass(cls) ? 'stop' : 'play');
});
}; };
});
updatePlayer();
scope.$watch(function () {
return playerService.playingSong;
}, function (newVal) {
console.log('playingSong changed !');
$player.jPlayer('setMedia', {'mp3': newVal.url})
.jPlayer('play');
});
} //end link
};
}]);

View file

@ -10,37 +10,42 @@ angular.module('jamstash.player.service', ['jamstash.settings'])
var player = { var player = {
queue: [], queue: [],
currentlyPlayingIndex: -1, playingIndex: -1,
playingSong: {},
play: function(song) { play: function(song) {
song.playing = true; //song.playing = true;
console.log('play()'); player.playingSong = song;
console.log('player service - play()', song);
}, },
load: function(song) { load: function(song) {
console.log('player service - load()');
}, },
restart: function() { restart: function() {
console.log('restart()'); console.log('player service - restart()');
}, },
nextTrack: function() { nextTrack: function() {
if((player.currentlyPlayingIndex + 1) < player.queue.length) { console.log('player service - nextTrack()');
var nextTrack = player.queue[player.currentlyPlayingIndex + 1]; if((player.playingIndex + 1) < player.queue.length) {
player.currentlyPlayingIndex++; var nextTrack = player.queue[player.playingIndex + 1];
player.playingIndex++;
player.play(nextTrack); player.play(nextTrack);
} }
}, },
previousTrack: function() { previousTrack: function() {
if((player.currentlyPlayingIndex - 1) > 0) { console.log(('player service - previousTrack()'));
var previousTrack = player.queue[player.currentlyPlayingIndex - 1]; if((player.playingIndex - 1) > 0) {
player.currentlyPlayingIndex--; var previousTrack = player.queue[player.playingIndex - 1];
player.playingIndex--;
player.play(previousTrack); player.play(previousTrack);
} else if (player.queue.length > 0) { } else if (player.queue.length > 0) {
player.currentlyPlayingIndex = 0; player.playingIndex = 0;
player.play(player.queue[player.currentlyPlayingIndex]); var firstTrack = player.queue[0];
player.play(firstTrack);
} }
} }
}; };

View file

@ -39,25 +39,25 @@ 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.nextTrack(); player.nextTrack();
expect(player.currentlyPlayingIndex).toBe(0); expect(player.playingIndex).toBe(0);
expect(player.play).toHaveBeenCalledWith(player.queue[0]); expect(player.play).toHaveBeenCalledWith(player.queue[0]);
}); });
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() {
player.currentlyPlayingIndex = 0; player.playingIndex = 0;
player.nextTrack(); player.nextTrack();
expect(player.currentlyPlayingIndex).toBe(1); expect(player.playingIndex).toBe(1);
expect(player.play).toHaveBeenCalledWith(player.queue[1]); expect(player.play).toHaveBeenCalledWith(player.queue[1]);
}); });
it("and the last song is playing, it does nothing", function() { it("and the last song is playing, it does nothing", function() {
player.currentlyPlayingIndex = 2; player.playingIndex = 2;
player.nextTrack(); player.nextTrack();
expect(player.currentlyPlayingIndex).toBe(2); expect(player.playingIndex).toBe(2);
expect(player.play).not.toHaveBeenCalled(); expect(player.play).not.toHaveBeenCalled();
}); });
}); });
@ -66,25 +66,25 @@ 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(player.currentlyPlayingIndex).toBe(0); expect(player.playingIndex).toBe(0);
expect(player.play).toHaveBeenCalledWith(player.queue[0]); expect(player.play).toHaveBeenCalledWith(player.queue[0]);
}); });
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() {
player.currentlyPlayingIndex = 0; player.playingIndex = 0;
player.previousTrack(); player.previousTrack();
expect(player.currentlyPlayingIndex).toBe(0); expect(player.playingIndex).toBe(0);
expect(player.play).toHaveBeenCalledWith(player.queue[0]); expect(player.play).toHaveBeenCalledWith(player.queue[0]);
}); });
it("and the last song is playing, it plays the second song", function() { it("and the last song is playing, it plays the second song", function() {
player.currentlyPlayingIndex = 2; player.playingIndex = 2;
player.previousTrack(); player.previousTrack();
expect(player.currentlyPlayingIndex).toBe(1); expect(player.playingIndex).toBe(1);
expect(player.play).toHaveBeenCalledWith(player.queue[1]); expect(player.play).toHaveBeenCalledWith(player.queue[1]);
}); });
}); });
@ -106,14 +106,17 @@ describe("Player service", function() {
it("when I play it, the song is marked as playing", function() { it("when I play it, the song is marked as playing", function() {
player.play(song); player.play(song);
expect(player.playingSong).toBe(song);
expect(song.playing).toBeTruthy(); expect(song.playing).toBeTruthy();
}); });
it("when I restart playback, the song is still marked as playing", function() { it("when I restart playback, the song is still marked as playing", function() {
song.playing = true; song.playing = true;
player.playingSong = song;
player.restart(); player.restart();
expect(player.playingSong).toBe(song);
expect(song.playing).toBeTruthy(); expect(song.playing).toBeTruthy();
}); });
}); });
@ -122,20 +125,20 @@ describe("Player service", function() {
beforeEach(function() { beforeEach(function() {
player.queue = []; player.queue = [];
player.currentlyPlayingIndex = -1; player.playingIndex = -1;
spyOn(player, "play").and.stub(); spyOn(player, "play").and.stub();
}); });
it("when I call nextTrack, it does nothing", function() { it("when I call nextTrack, it does nothing", function() {
player.nextTrack(); player.nextTrack();
expect(player.play).not.toHaveBeenCalled(); expect(player.play).not.toHaveBeenCalled();
expect(player.currentlyPlayingIndex).toBe(-1); expect(player.playingIndex).toBe(-1);
}); });
it("when I call previousTrack, it does nothing", function() { it("when I call previousTrack, it does nothing", function() {
player.previousTrack(); player.previousTrack();
expect(player.play).not.toHaveBeenCalled(); expect(player.play).not.toHaveBeenCalled();
expect(player.currentlyPlayingIndex).toBe(-1); expect(player.playingIndex).toBe(-1);
}); });
}); });
}); });

View file

@ -5,10 +5,10 @@
<a class="hover" id="PreviousTrack" title="Previous Track" ng-click="previousTrack()"> <a class="hover" id="PreviousTrack" title="Previous Track" ng-click="previousTrack()">
<img src="images/first_alt_24x24.png" height="24" width="24" alt="Previous track" /> <img src="images/first_alt_24x24.png" height="24" width="24" alt="Previous track" />
</a> </a>
<a class="hover PlayTrack" title="Play/Pause" ng-click="defaultPlay()"> <a class="hover PlayTrack" title="Play/Pause" ng-click="play()">
<img src="images/play_alt_24x24.png" height="24" width="24" alt="Play" /> <img src="images/play_alt_24x24.png" height="24" width="24" alt="Play" />
</a> </a>
<a class="hover PauseTrack" title="Play/Pause" ng-click="defaultPlay()" style="display: none;"> <a class="hover PauseTrack" title="Play/Pause" ng-click="play()" style="display: none;">
<img src="images/pause_alt_24x24.png" height="24" width="24" alt="Pause" /> <img src="images/pause_alt_24x24.png" height="24" width="24" alt="Pause" />
</a> </a>
<a class="hover" id="NextTrack" title="Next Track" ng-click="nextTrack()"> <a class="hover" id="NextTrack" title="Next Track" ng-click="nextTrack()">
@ -35,7 +35,7 @@
<!--<div class="jp-volume-bar"><div class="jp-volume-bar-value"></div></div><a href="" id="action_VolumeMax" class="volume" title="Max Volume"></a>--> <!--<div class="jp-volume-bar"><div class="jp-volume-bar-value"></div></div><a href="" id="action_VolumeMax" class="volume" title="Max Volume"></a>-->
</div> </div>
</div> </div>
<div id="playdeck_1"></div> <div id="playdeck_1" jplayer></div>
<div id="playdeck_2"></div> <div id="playdeck_2"></div>
<div id="submenu_CurrentPlaylist" class="submenu shadow" style="display: none;"> <div id="submenu_CurrentPlaylist" class="submenu shadow" style="display: none;">
<table id="CurrentPlaylistPreviewContainer" class="simplelist songlist"> <table id="CurrentPlaylistPreviewContainer" class="simplelist songlist">

View file

@ -8,6 +8,8 @@ angular.module('jamstash.player.controller', ['jamstash.player.service', 'jamsta
.controller('PlayerController', ['$scope', 'player', function($scope, player){ .controller('PlayerController', ['$scope', 'player', function($scope, player){
'use strict'; 'use strict';
$scope.playingSong = player.playingSong;
$scope.previousTrack = function () { $scope.previousTrack = function () {
player.previousTrack(); player.previousTrack();
}; };

View file

@ -1,10 +1,10 @@
angular.module('jamstash.queue.controller', ['jamstash.player.service']) angular.module('jamstash.queue.controller', ['jamstash.player.service'])
.controller('QueueController', ['$scope', '$rootScope', 'globals', 'player', .controller('QueueController', ['$scope', 'globals', 'player',
function ($scope, $rootScope, globals, player) { function ($scope, globals, player) {
'use strict'; 'use strict';
$scope.settings = globals.settings; $scope.settings = globals.settings;
$scope.song = $rootScope.queue; $scope.song = player.queue;
//angular.copy($rootScope.queue, $scope.song); //angular.copy($rootScope.queue, $scope.song);
$scope.itemType = 'pl'; $scope.itemType = 'pl';

View file

@ -281,11 +281,12 @@ angular.module('jamstash.subsonic.controller', ['jamstash.subsonic.service', 'ja
mappedSongs.push(map.mapSong(randomStarredSongs[i])); mappedSongs.push(map.mapSong(randomStarredSongs[i]));
} }
if(action === 'play') { if(action === 'play') {
$rootScope.queue = [].concat(mappedSongs); player.queue = [].concat(mappedSongs);
notifications.updateMessage(mappedSongs.length + ' Song(s) Added to Queue', true); notifications.updateMessage(mappedSongs.length + ' Song(s) Added to Queue', true);
player.play($rootScope.queue[0]); console.log('subjs', player.queue);
player.play(player.queue[0]);
} else if (action === 'add') { } else if (action === 'add') {
$rootScope.queue = $rootScope.queue.concat(mappedSongs); player.queue = player.queue.concat(mappedSongs);
notifications.updateMessage(mappedSongs.length + ' Song(s) Added to Queue', true); notifications.updateMessage(mappedSongs.length + ' Song(s) Added to Queue', true);
} else if (action === 'display') { } else if (action === 'display') {
$scope.album = []; $scope.album = [];

View file

@ -24,7 +24,7 @@ describe("Subsonic controller", function() {
}); });
spyOn(notifications, 'updateMessage').and.stub(); spyOn(notifications, 'updateMessage').and.stub();
spyOn(player, 'play').and.stub(); spyOn(player, 'play').and.stub();
$rootScope.queue = []; player.queue = [];
$controller('SubsonicController', { $controller('SubsonicController', {
$scope: scope, $scope: scope,
@ -64,14 +64,14 @@ describe("Subsonic controller", function() {
$rootScope.$apply(); $rootScope.$apply();
expect(subsonic.getRandomStarredSongs).toHaveBeenCalled(); expect(subsonic.getRandomStarredSongs).toHaveBeenCalled();
expect($rootScope.queue).toEqual([ expect(player.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"}]; player.queue = [{id: "7666"}];
scope.getRandomStarredSongs('play'); scope.getRandomStarredSongs('play');
deferred.resolve(response); deferred.resolve(response);
@ -79,7 +79,7 @@ describe("Subsonic controller", function() {
expect(subsonic.getRandomStarredSongs).toHaveBeenCalled(); expect(subsonic.getRandomStarredSongs).toHaveBeenCalled();
expect(player.play).toHaveBeenCalledWith({id: "2548"}); expect(player.play).toHaveBeenCalledWith({id: "2548"});
expect($rootScope.queue).toEqual([ expect(player.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);
@ -88,7 +88,7 @@ describe("Subsonic controller", 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() { 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"}]; player.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.'});
@ -96,7 +96,7 @@ describe("Subsonic controller", function() {
expect(subsonic.getRandomStarredSongs).toHaveBeenCalled(); expect(subsonic.getRandomStarredSongs).toHaveBeenCalled();
expect(player.play).not.toHaveBeenCalled(); expect(player.play).not.toHaveBeenCalled();
expect($rootScope.queue).toEqual([{id: "7666"}]); expect(player.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);
}); });