Bunch of stuff, mostly moving player to it's own file and adding a progress bar
This commit is contained in:
parent
fd1d0e7f55
commit
558b498ded
6 changed files with 239 additions and 169 deletions
10
css/core.css
10
css/core.css
|
@ -17,6 +17,16 @@ body, .normalFont {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div.progressBarFull {
|
||||||
|
height: 3px;
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.progressBarCurrent {
|
||||||
|
height: 3px;
|
||||||
|
background-color: cornflowerblue;
|
||||||
|
}
|
||||||
|
|
||||||
.topBar, .topBarMenu {
|
.topBar, .topBarMenu {
|
||||||
background-color: #206BA4;
|
background-color: #206BA4;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- TODO: move topBar into it's own partial -->
|
<!-- TODO: move topBar into it's own partial -->
|
||||||
<div class="topBar" ng-controller="TopBarCtrl" ng-swipe-left="jumpAudio(+5)" ng-swipe-right="jumpAudio(-5)">
|
<div class="topBar" ng-controller="TopBarCtrl" ng-swipe-left="jumpAudioForward()" ng-swipe-right="jumpAudioBack()">
|
||||||
|
<div progress-bar progress-bar-current="audio.currentTime" progress-bar-duration="audio.duration"></div>
|
||||||
<div id="pageSwitcher" class="topBarItem" hold="showPageSwitchMenu()" ng-click="changePage()">
|
<div id="pageSwitcher" class="topBarItem" hold="showPageSwitchMenu()" ng-click="changePage()">
|
||||||
<i id="pageswitch-back-arrow" class="pageswitch-icon icon-chevron-left ng-cloak" ng-show="showBackLink()"></i>
|
<i id="pageswitch-back-arrow" class="pageswitch-icon icon-chevron-left ng-cloak" ng-show="showBackLink()"></i>
|
||||||
<i id="pageswitch-icon-settings" class="pageswitch-icon next icon-cogs"></i>
|
<i id="pageswitch-icon-settings" class="pageswitch-icon next icon-cogs"></i>
|
||||||
|
@ -24,7 +25,7 @@
|
||||||
<div ng-click="currentInfo()" class="topBarItem">
|
<div ng-click="currentInfo()" class="topBarItem">
|
||||||
<div id="currentInfo">
|
<div id="currentInfo">
|
||||||
<span class="title-slider">{{ nowPlaying.title }}</span>
|
<span class="title-slider">{{ nowPlaying.title }}</span>
|
||||||
<span ng-show="nowPlaying.duration > 0">
|
<span ng-show="audio.duration > 0">
|
||||||
<span id="position">{{ audio.currentTime|time:true }}</span>/<span id="duration">{{ audio.duration|time }}</span>
|
<span id="position">{{ audio.currentTime|time:true }}</span>/<span id="duration">{{ audio.duration|time }}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -56,6 +57,7 @@
|
||||||
<script src="js/database.js"></script>
|
<script src="js/database.js"></script>
|
||||||
<script src="js/models.js"></script>
|
<script src="js/models.js"></script>
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js"></script>
|
||||||
|
<script src="js/player.js"></script>
|
||||||
<script src="js/utilities.js"></script>
|
<script src="js/utilities.js"></script>
|
||||||
<script src="js/services.js"></script>
|
<script src="js/services.js"></script>
|
||||||
<script src="js/directives.js"></script>
|
<script src="js/directives.js"></script>
|
||||||
|
|
|
@ -96,7 +96,6 @@ function QueueListCtrl($scope, $rootScope, pageSwitcher, feedItems, feeds, downl
|
||||||
$scope.queue = queueList.getQueueList();
|
$scope.queue = queueList.getQueueList();
|
||||||
|
|
||||||
$scope.$on('queueListRefresh', function(event) {
|
$scope.$on('queueListRefresh', function(event) {
|
||||||
console.log('rebuilding');
|
|
||||||
$rootScope.$apply(queueList.rebuildList());
|
$rootScope.$apply(queueList.rebuildList());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -161,7 +160,7 @@ function TopBarCtrl($scope, player, pageSwitcher)
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.$on('playItem', function(event, feedItem) {
|
$scope.$on('playItem', function(event, feedItem) {
|
||||||
player.play(feedItem, $scope);
|
player.play(feedItem);
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.currentInfo = function() {
|
$scope.currentInfo = function() {
|
||||||
|
@ -188,7 +187,21 @@ function TopBarCtrl($scope, player, pageSwitcher)
|
||||||
return !!pageSwitcher.backPage;
|
return !!pageSwitcher.backPage;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.jumpAudio = function(distance) {
|
var lastForwardJump = 0,
|
||||||
|
forwardJumpCount = 1;
|
||||||
|
|
||||||
|
$scope.jumpAudioForward = function() {
|
||||||
|
var distance = 5;
|
||||||
|
if (lastForwardJump > new Date().getTime() - 2000) {
|
||||||
|
distance = forwardJumpCount * distance;
|
||||||
|
forwardJumpCount++;
|
||||||
|
} else {
|
||||||
|
forwardJumpCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.jumpAudio(distance);
|
||||||
|
};
|
||||||
|
$scope.jumpAudioBack = function(distance) {
|
||||||
player.jumpAudio(distance);
|
player.jumpAudio(distance);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
207
js/player.js
Normal file
207
js/player.js
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
angular.module('podcasts.player', [])
|
||||||
|
.run(['player', function(player) {
|
||||||
|
var acm = navigator.mozAudioChannelManager;
|
||||||
|
if (acm) {
|
||||||
|
acm.addEventListener('headphoneschange', function onheadphoneschange() {
|
||||||
|
if (!acm.headphones && player.playing()) {
|
||||||
|
player.pause();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
.directive('progressBar', function($timeout) {
|
||||||
|
// return the directive link function. (compile function not needed)
|
||||||
|
return {
|
||||||
|
link: function(scope, element, attrs)
|
||||||
|
{
|
||||||
|
var timeoutId; // timeoutId, so that we can cancel the time updates
|
||||||
|
|
||||||
|
// used to update the UI
|
||||||
|
function updateWidth() {
|
||||||
|
var duration = scope.$eval(attrs.progressBarDuration),
|
||||||
|
current = scope.$eval(attrs.progressBarCurrent);
|
||||||
|
|
||||||
|
var percentage = Math.round(current / (duration / 100));
|
||||||
|
|
||||||
|
if (percentage > 100) {
|
||||||
|
percentage = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
element.css('width', percentage + '%');
|
||||||
|
|
||||||
|
scope.$digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
// schedule update in one second
|
||||||
|
function updateLater() {
|
||||||
|
// save the timeoutId for canceling
|
||||||
|
timeoutId = $timeout(function() {
|
||||||
|
updateWidth(); // update DOM
|
||||||
|
updateLater(); // schedule another update
|
||||||
|
}, 1000, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initDom() {
|
||||||
|
var currentProgressElement = angular.element('<div class="progressBarCurrent">');
|
||||||
|
element.append(currentProgressElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// listen on DOM destroy (removal) event, and cancel the next UI update
|
||||||
|
// to prevent updating time after the DOM element was removed.
|
||||||
|
element.on('$destroy', function() {
|
||||||
|
$timeout.cancel(timeoutId);
|
||||||
|
});
|
||||||
|
|
||||||
|
initDom();
|
||||||
|
updateLater(); // kick off the UI update process.
|
||||||
|
},
|
||||||
|
scope: true
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.service('player', ['url', '$timeout', 'feedItems', '$rootScope', '$log', '$q', function(url, $timeout, feedItems, $rootScope, $log, $q) {
|
||||||
|
var audio,
|
||||||
|
currentFeedItem = null,
|
||||||
|
nowPlaying = {
|
||||||
|
position: 0,
|
||||||
|
duration: 0,
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
feed: '',
|
||||||
|
date: 0,
|
||||||
|
context: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
audio = new Audio();
|
||||||
|
audio.setAttribute("mozaudiochannel", "content");
|
||||||
|
_addOfflineErrorHandler();
|
||||||
|
_addPauseEventListener();
|
||||||
|
|
||||||
|
function _addOfflineErrorHandler()
|
||||||
|
{
|
||||||
|
audio.addEventListener("error", function(event) {
|
||||||
|
$log.info('Error when loading audio file, continuing to next file');
|
||||||
|
|
||||||
|
var nextFeedItemPromise = feedItems.getNextInQueue(currentFeedItem);
|
||||||
|
$rootScope.$apply(nextFeedItemPromise.then(function(nextFeedItem) {
|
||||||
|
play(nextFeedItem);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _addPauseEventListener()
|
||||||
|
{
|
||||||
|
audio.addEventListener("pause", function(event) {
|
||||||
|
currentFeedItem.position = Math.floor(event.target.currentTime);
|
||||||
|
feedItems.save(currentFeedItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function play(feedItem, context)
|
||||||
|
{
|
||||||
|
var delayPlay = false;
|
||||||
|
if (feedItem) {
|
||||||
|
$log.info('playing: ' + feedItem.title);
|
||||||
|
|
||||||
|
currentFeedItem = feedItem;
|
||||||
|
|
||||||
|
var audioSrc;
|
||||||
|
|
||||||
|
if (feedItem.audio) {
|
||||||
|
$log.info('Playing audio from download');
|
||||||
|
audioSrc = url.createObjectUrl(feedItem.audio);
|
||||||
|
} else {
|
||||||
|
$log.info('Playing audio from web');
|
||||||
|
audioSrc = feedItem.audioUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio.src = audioSrc;
|
||||||
|
updateSong(feedItem);
|
||||||
|
|
||||||
|
if (feedItem.position) {
|
||||||
|
delayPlay = true;
|
||||||
|
angular.element(audio).bind("canplay", function(event) {
|
||||||
|
event.target.currentTime = feedItem.position;
|
||||||
|
|
||||||
|
angular.element(this).unbind("canplay");
|
||||||
|
|
||||||
|
audio.play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!delayPlay) {
|
||||||
|
audio.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
audio.addEventListener("ended", function(event) {
|
||||||
|
continueToNextItem(feedItem)
|
||||||
|
.then(function(nextFeedItem) {
|
||||||
|
play(nextFeedItem);
|
||||||
|
|
||||||
|
unQueueFeedItem(feedItem);
|
||||||
|
}, function(error) {
|
||||||
|
$log.warn('got Errror when fetching next feed item');
|
||||||
|
|
||||||
|
unQueueFeedItem(feedItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
angular.element(this).unbind();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function continueToNextItem(feedItem)
|
||||||
|
{
|
||||||
|
var deferred = $q.defer();
|
||||||
|
feedItems.getNextInQueue(feedItem)
|
||||||
|
.then(function(nextFeedItem) {
|
||||||
|
deferred.resolve(nextFeedItem);
|
||||||
|
}, function(error) {
|
||||||
|
deferred.reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unQueueFeedItem(feedItem)
|
||||||
|
{
|
||||||
|
feedItem.queued = 0;
|
||||||
|
feedItem.position = 0;
|
||||||
|
|
||||||
|
feedItems.save(feedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pause()
|
||||||
|
{
|
||||||
|
audio.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
function playing()
|
||||||
|
{
|
||||||
|
return !audio.paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSong(feedItem)
|
||||||
|
{
|
||||||
|
nowPlaying.title = feedItem.title;
|
||||||
|
nowPlaying.currentFeedItem = feedItem;
|
||||||
|
nowPlaying.description = feedItem.description;
|
||||||
|
nowPlaying.feed = feedItem.feed;
|
||||||
|
nowPlaying.date = feedItem.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function jumpAudio(distance)
|
||||||
|
{
|
||||||
|
audio.currentTime = audio.currentTime + distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
audio: audio,
|
||||||
|
feedItem: currentFeedItem,
|
||||||
|
nowPlaying: nowPlaying,
|
||||||
|
play: play,
|
||||||
|
pause: pause,
|
||||||
|
playing: playing,
|
||||||
|
jumpAudio: jumpAudio
|
||||||
|
};
|
||||||
|
}]);
|
164
js/services.js
164
js/services.js
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/* Services */
|
/* Services */
|
||||||
angular.module('podcasts.services', ['podcasts.utilities', 'podcasts.queueList', 'podcasts.models'])
|
angular.module('podcasts.services', ['podcasts.utilities', 'podcasts.queueList', 'podcasts.models', 'podcasts.player'])
|
||||||
.service('url', ['$window', function($window) {
|
.service('url', ['$window', function($window) {
|
||||||
return {
|
return {
|
||||||
url: $window.URL || $window.webkitURL,
|
url: $window.URL || $window.webkitURL,
|
||||||
|
@ -10,168 +10,6 @@ angular.module('podcasts.services', ['podcasts.utilities', 'podcasts.queueList',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}])
|
}])
|
||||||
.service('player', ['url', '$timeout', 'feedItems', '$rootScope', function(url, $timeout, feedItems, $rootScope) {
|
|
||||||
var audio = new Audio();
|
|
||||||
audio.setAttribute("mozaudiochannel", "content");
|
|
||||||
var currentFeedItem = null;
|
|
||||||
var nowPlaying = {position: 0, duration: 0, title: '', description: '', feed: '', date: 0};
|
|
||||||
var hasOfflineErrorHandler = false;
|
|
||||||
var hasPauseEventListener = false;
|
|
||||||
|
|
||||||
var acm = navigator.mozAudioChannelManager;
|
|
||||||
|
|
||||||
if (acm) {
|
|
||||||
acm.addEventListener('headphoneschange', function onheadphoneschange() {
|
|
||||||
if (!acm.headphones && playing()) {
|
|
||||||
pause();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addOfflineErrorHandler($scope)
|
|
||||||
{
|
|
||||||
if (!hasOfflineErrorHandler) {
|
|
||||||
audio.addEventListener("error", function(event) {
|
|
||||||
console.log('Error when loading audio file, continuing to next file');
|
|
||||||
|
|
||||||
var nextFeedItemPromise = feedItems.getNextInQueue(currentFeedItem);
|
|
||||||
$rootScope.$apply(nextFeedItemPromise.then(function(nextFeedItem) {
|
|
||||||
play(nextFeedItem, $scope);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
hasOfflineErrorHandler = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addPauseEventListener()
|
|
||||||
{
|
|
||||||
if (!hasPauseEventListener) {
|
|
||||||
audio.addEventListener("pause", function(event) {
|
|
||||||
console.log('paused audio');
|
|
||||||
currentFeedItem.position = Math.floor(event.target.currentTime);
|
|
||||||
feedItems.save(currentFeedItem);
|
|
||||||
});
|
|
||||||
|
|
||||||
hasPauseEventListener = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function play(feedItem, $scope)
|
|
||||||
{
|
|
||||||
var delayPlay = false;
|
|
||||||
if (feedItem) {
|
|
||||||
console.log('playing: ' + feedItem.title);
|
|
||||||
|
|
||||||
currentFeedItem = feedItem;
|
|
||||||
var audioSrc;
|
|
||||||
|
|
||||||
if (feedItem.audio) {
|
|
||||||
console.log('Playing audio from download');
|
|
||||||
audioSrc = url.createObjectUrl(feedItem.audio);
|
|
||||||
} else {
|
|
||||||
console.log('Playing audio from web');
|
|
||||||
audioSrc = feedItem.audioUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
audio.src = audioSrc;
|
|
||||||
updateSong(feedItem, $scope);
|
|
||||||
|
|
||||||
addOfflineErrorHandler($scope);
|
|
||||||
addPauseEventListener();
|
|
||||||
|
|
||||||
if (feedItem.position) {
|
|
||||||
delayPlay = true;
|
|
||||||
angular.element(audio).bind('canplay', function(event) {
|
|
||||||
this.currentTime = feedItem.position;
|
|
||||||
|
|
||||||
angular.element(this).unbind('canplay');
|
|
||||||
|
|
||||||
audio.play();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!delayPlay) {
|
|
||||||
audio.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add something here for when audio is done to remove from queue and go to next song
|
|
||||||
audio.addEventListener("ended", function(event) {
|
|
||||||
var nextFeedItemPromise = feedItems.getNextInQueue(feedItem);
|
|
||||||
console.log('got promise for next feed item');
|
|
||||||
$rootScope.$apply(nextFeedItemPromise.then(function(nextFeedItem) {
|
|
||||||
console.log('Got next Feed Item:');
|
|
||||||
console.log(nextFeedItem.title);
|
|
||||||
play(nextFeedItem, $scope);
|
|
||||||
|
|
||||||
feedItem.queued = 0;
|
|
||||||
feedItem.position = 0;
|
|
||||||
|
|
||||||
feedItems.save(feedItem);
|
|
||||||
}, function(error) {
|
|
||||||
console.log('got Errror when fetching next feed item');
|
|
||||||
|
|
||||||
feedItem.queued = 0;
|
|
||||||
feedItem.position = 0;
|
|
||||||
|
|
||||||
feedItems.save(feedItem);
|
|
||||||
}));
|
|
||||||
|
|
||||||
angular.element(this).unbind();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function pause()
|
|
||||||
{
|
|
||||||
audio.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
function playing()
|
|
||||||
{
|
|
||||||
return !audio.paused;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSong(feedItem, $scope)
|
|
||||||
{
|
|
||||||
nowPlaying.title = feedItem.title;
|
|
||||||
nowPlaying.currentFeedItem = feedItem;
|
|
||||||
nowPlaying.description = feedItem.description;
|
|
||||||
nowPlaying.feed = feedItem.feed;
|
|
||||||
nowPlaying.date = feedItem.date;
|
|
||||||
updatePosition($scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePosition($scope)
|
|
||||||
{
|
|
||||||
$timeout(function() {
|
|
||||||
nowPlaying.duration = audio.duration;
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
setInterval(function() {
|
|
||||||
nowPlaying.position = audio.currentTime;
|
|
||||||
$scope.$apply();
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function jumpAudio(distance)
|
|
||||||
{
|
|
||||||
audio.currentTime = audio.currentTime + distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
|
||||||
audio: audio,
|
|
||||||
feedItem: currentFeedItem,
|
|
||||||
nowPlaying: nowPlaying,
|
|
||||||
play: play,
|
|
||||||
pause: pause,
|
|
||||||
playing: playing,
|
|
||||||
updateSong: updateSong,
|
|
||||||
updatePosition: updatePosition,
|
|
||||||
jumpAudio: jumpAudio
|
|
||||||
}
|
|
||||||
}])
|
|
||||||
.service('pageSwitcher', ['$location', '$route', function($location, $route) {
|
.service('pageSwitcher', ['$location', '$route', function($location, $route) {
|
||||||
return {
|
return {
|
||||||
//TODO: change these getElementById's to something else
|
//TODO: change these getElementById's to something else
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="http://angularjs.org/">AngularJS</a></li>
|
<li><a href="http://angularjs.org/">AngularJS</a></li>
|
||||||
<li><a href="https://github.com/jakemdrew/ixDbEz">ixDbEz</a></li>
|
<li><a href="https://github.com/jakemdrew/ixDbEz">ixDbEz</a></li>
|
||||||
<li><a href="http://cubiq.org/iscroll-4">iScroll4</a></li>
|
<li><a href="https://github.com/cubiq/iscroll">iScroll</a></li>
|
||||||
<li><a href="http://fortawesome.github.com/Font-Awesome/">Font Awesome</a></li>
|
<li><a href="http://fortawesome.github.com/Font-Awesome/">Font Awesome</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
Loading…
Add table
Add a link
Reference in a new issue