464 lines
15 KiB
JavaScript
464 lines
15 KiB
JavaScript
|
/* global angular */
|
|||
|
|
|||
|
/**
|
|||
|
* Created by intelWorx on 19/11/2015.
|
|||
|
*/
|
|||
|
(function() {
|
|||
|
'use strict';
|
|||
|
|
|||
|
/**
|
|||
|
* Main module, your application should depend on this
|
|||
|
* @module {mdWavesurfer}
|
|||
|
*/
|
|||
|
let app = angular.module('mdWavesurfer', ['ngMaterial']);
|
|||
|
|
|||
|
/**
|
|||
|
* @ngdoc service
|
|||
|
* @name $mdWavesurferUtils
|
|||
|
*
|
|||
|
* @description
|
|||
|
*
|
|||
|
* Utility service for this directive, exposes method:
|
|||
|
* - getLength(url), which returns a promise for the length of the audio specified by URL
|
|||
|
*
|
|||
|
* ```js
|
|||
|
* app.directive('myFancyDirective', function(mdWavesurferUtils) {
|
|||
|
* return {
|
|||
|
* restrict: 'e',
|
|||
|
* link: function(scope, el, attrs) {
|
|||
|
* mdWavesurferUtils(attrs.url)
|
|||
|
* .then(function(l){
|
|||
|
* scope.length = l;
|
|||
|
* }, function(){
|
|||
|
* someErrorhandler()
|
|||
|
* })
|
|||
|
* ;
|
|||
|
* }
|
|||
|
* };
|
|||
|
* });
|
|||
|
* ```
|
|||
|
*/
|
|||
|
app.factory('mdWavesurferUtils', [
|
|||
|
'$q',
|
|||
|
'$document',
|
|||
|
'$timeout',
|
|||
|
function($q, $document, $timeout) {
|
|||
|
return {
|
|||
|
getLength: function(object) {
|
|||
|
let deferred = $q.defer();
|
|||
|
let estimateLength = function(url) {
|
|||
|
let audio = $document[0].createElement('audio');
|
|||
|
audio.src = url;
|
|||
|
audio.addEventListener(
|
|||
|
'loadeddata',
|
|||
|
function listener() {
|
|||
|
deferred.resolve(this.duration);
|
|||
|
audio.removeEventListener(
|
|||
|
'loadeddata',
|
|||
|
listener
|
|||
|
);
|
|||
|
audio.src = 'data:audio/mpeg,0'; //destroy loading.
|
|||
|
}
|
|||
|
);
|
|||
|
|
|||
|
audio.addEventListener('error', function(e) {
|
|||
|
deferred.resolve(e.target.error);
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
if (typeof object === 'string') {
|
|||
|
//this is a URL
|
|||
|
estimateLength(object);
|
|||
|
} else {
|
|||
|
$timeout(function() {
|
|||
|
deferred.reject(
|
|||
|
new DOMError(
|
|||
|
'NotSupportedError',
|
|||
|
'Specified argument is not supported'
|
|||
|
)
|
|||
|
);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
return deferred.promise;
|
|||
|
}
|
|||
|
};
|
|||
|
}
|
|||
|
]);
|
|||
|
|
|||
|
/**
|
|||
|
* @ngdoc filter
|
|||
|
* @name mdWavesurferTimeFormat
|
|||
|
*
|
|||
|
* Simple filter to convert value in seconds to MM:SS format
|
|||
|
*
|
|||
|
* @param Number duration in seconds
|
|||
|
*/
|
|||
|
app.filter('mdWavesurferTimeFormat', function() {
|
|||
|
return function(input) {
|
|||
|
if (!input) {
|
|||
|
return '00:00';
|
|||
|
}
|
|||
|
|
|||
|
const minutes = Math.floor(input / 60);
|
|||
|
const seconds = Math.floor(input) % 60;
|
|||
|
|
|||
|
return (
|
|||
|
(minutes < 10 ? '0' : '') +
|
|||
|
minutes +
|
|||
|
':' +
|
|||
|
(seconds < 10 ? '0' : '') +
|
|||
|
seconds
|
|||
|
);
|
|||
|
};
|
|||
|
});
|
|||
|
|
|||
|
app.controller('mdWavesurferAudioController', [
|
|||
|
'$attrs',
|
|||
|
'$element',
|
|||
|
function(attributes, $element) {
|
|||
|
let audio = this;
|
|||
|
|
|||
|
audio.tracks = [];
|
|||
|
audio.selectedIndex = audio.selectedIndex || 0;
|
|||
|
audio.currentTrack = null;
|
|||
|
|
|||
|
//adds to an audio track
|
|||
|
audio.addTrack = function(trackScope) {
|
|||
|
if (audio.tracks.indexOf(trackScope) < 0) {
|
|||
|
audio.tracks.push(trackScope);
|
|||
|
}
|
|||
|
|
|||
|
if (!audio.currentTrack) {
|
|||
|
audio.currentTrack = audio.tracks[audio.selectedIndex];
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
//remove audio track
|
|||
|
audio.removeTrack = function(trackScope) {
|
|||
|
const idx = audio.tracks.indexOf(trackScope);
|
|||
|
if (idx >= 0) {
|
|||
|
audio.tracks.splice(idx, 1);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
audio.playerProperties = {};
|
|||
|
let nKey;
|
|||
|
for (let attr in attributes) {
|
|||
|
if (attr.match(/^player/)) {
|
|||
|
nKey = attr.replace(/^player([A-Z])/, function(m, $1) {
|
|||
|
return $1.toLowerCase();
|
|||
|
});
|
|||
|
audio.playerProperties[nKey] = attributes[attr];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
let getPlayer = function() {
|
|||
|
return $element
|
|||
|
.find('md-wavesurfer-player')
|
|||
|
.controller('mdWavesurferPlayer');
|
|||
|
};
|
|||
|
let setAutoPlay = function(forcePlay) {
|
|||
|
let controller = getPlayer();
|
|||
|
if (
|
|||
|
controller &&
|
|||
|
(forcePlay || controller.surfer.isPlaying())
|
|||
|
) {
|
|||
|
controller.autoPlay = true;
|
|||
|
}
|
|||
|
};
|
|||
|
audio.setTrack = function(idx, forcePlay) {
|
|||
|
if (audio.tracks.length > idx) {
|
|||
|
if (audio.selectedIndex === idx) {
|
|||
|
let ctrl = getPlayer();
|
|||
|
ctrl.surfer.playPause();
|
|||
|
} else {
|
|||
|
setAutoPlay(forcePlay);
|
|||
|
audio.currentTrack = audio.tracks[idx];
|
|||
|
audio.selectedIndex = idx;
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
audio.extraButtons = [
|
|||
|
{
|
|||
|
icon: 'zmdi zmdi-skip-previous',
|
|||
|
title: 'Previous',
|
|||
|
action: function($event) {
|
|||
|
if (audio.selectedIndex > 0) {
|
|||
|
audio.setTrack(audio.selectedIndex - 1);
|
|||
|
}
|
|||
|
},
|
|||
|
class: ''
|
|||
|
},
|
|||
|
{
|
|||
|
icon: 'zmdi zmdi-skip-next',
|
|||
|
title: 'Next',
|
|||
|
action: function($event) {
|
|||
|
if (audio.selectedIndex < audio.tracks.length - 1) {
|
|||
|
audio.setTrack(audio.selectedIndex + 1);
|
|||
|
}
|
|||
|
},
|
|||
|
class: ''
|
|||
|
}
|
|||
|
];
|
|||
|
}
|
|||
|
]);
|
|||
|
|
|||
|
/**
|
|||
|
* @ngdoc directive
|
|||
|
* @name md-wavesurfer-audio
|
|||
|
*
|
|||
|
* Directive for playing a set of audio files. This directive is analogous to `<audio>` HTML tag.
|
|||
|
* The audio files, should be specified using the `md-wavesurfer-source`
|
|||
|
*
|
|||
|
* WaveSurfer properties can be passed in using the prefix : player-* for attributes, e.g. `player-wave-color` is
|
|||
|
* equivalent to WaveSurfer's waveColor option.
|
|||
|
*
|
|||
|
* Must be used as an element.
|
|||
|
*
|
|||
|
* @usage
|
|||
|
* ```html
|
|||
|
* <md-wavesurfer-audio player-wave-color="gray" player-progress-color="black" player-backend="MediaElement">
|
|||
|
* <md-wavesurfer-source src="source1" title="Title-1"></md-wavesurfer-source>
|
|||
|
* <md-wavesurfer-source src="source2" title="Title-2"></md-wavesurfer-source>
|
|||
|
* <md-wavesurfer-source src="source3" title="Title-3"></md-wavesurfer-source>
|
|||
|
* ...
|
|||
|
* <md-wavesurfer-source src="sourceN" title="Рассказы о сновидениях"></md-wavesurfer-source>
|
|||
|
* </md-wavesurfer-audio>
|
|||
|
* ```
|
|||
|
*
|
|||
|
* @param string player-* specifies WaveSurfer properties.
|
|||
|
*
|
|||
|
*/
|
|||
|
app.directive('mdWavesurferAudio', [
|
|||
|
function() {
|
|||
|
return {
|
|||
|
restrict: 'E',
|
|||
|
templateUrl: 'md-player-audio.partial.html',
|
|||
|
transclude: true,
|
|||
|
controller: 'mdWavesurferAudioController',
|
|||
|
controllerAs: 'audio'
|
|||
|
};
|
|||
|
}
|
|||
|
]);
|
|||
|
|
|||
|
/**
|
|||
|
* @ngdoc directive
|
|||
|
*
|
|||
|
* @name md-wavesurfer-source
|
|||
|
*
|
|||
|
* This directive is used within the `md-wavesurfer-audio` directive to specify an audio file source, it is
|
|||
|
* synonymous to `<source>` tag in HTML
|
|||
|
*
|
|||
|
* The directive cannot be used as standalone.
|
|||
|
*
|
|||
|
* @usage
|
|||
|
*
|
|||
|
* ```html
|
|||
|
* <md-wavesurfer-source src="source3" title="Title-3" album-art="Album-Art-Url" duration=""></md-wavesurfer-source>
|
|||
|
* ```
|
|||
|
* @param String src the URL to the audio file, this is required.
|
|||
|
* @param String title track title
|
|||
|
* @param String album-art the album art URL
|
|||
|
* @param Number duration the length of the audio file in seconds, will be auto-detected if not specified.
|
|||
|
*
|
|||
|
*/
|
|||
|
app.directive('mdWavesurferSource', [
|
|||
|
'mdWavesurferUtils',
|
|||
|
function(mdWavesurferUtils) {
|
|||
|
return {
|
|||
|
restrict: 'E',
|
|||
|
require: '^mdWavesurferAudio',
|
|||
|
scope: {
|
|||
|
src: '@',
|
|||
|
albumArt: '@',
|
|||
|
title: '@',
|
|||
|
duration: '='
|
|||
|
},
|
|||
|
link: function(scope, element, attrs, audio) {
|
|||
|
audio.addTrack(scope);
|
|||
|
|
|||
|
if (!scope.duration) {
|
|||
|
mdWavesurferUtils.getLength(scope.src).then(
|
|||
|
function(dur) {
|
|||
|
scope.duration = dur;
|
|||
|
},
|
|||
|
function(e) {
|
|||
|
scope.duration = 0;
|
|||
|
console.log(
|
|||
|
'Failed to get audio length, reason: ',
|
|||
|
e.message
|
|||
|
);
|
|||
|
}
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
element.on('$destroy', function() {
|
|||
|
audio.removeTrack(audio);
|
|||
|
});
|
|||
|
}
|
|||
|
};
|
|||
|
}
|
|||
|
]);
|
|||
|
|
|||
|
app.controller('mdWavesurferPlayerController', [
|
|||
|
'$element',
|
|||
|
'$scope',
|
|||
|
'$attrs',
|
|||
|
'$interval',
|
|||
|
'$mdTheming',
|
|||
|
function($element, $scope, attributes, $interval, $mdTheme) {
|
|||
|
let control = this,
|
|||
|
timeInterval;
|
|||
|
|
|||
|
control.themeClass = 'md-' + $mdTheme.defaultTheme() + '-theme';
|
|||
|
control.isReady = false;
|
|||
|
control.surfer = null;
|
|||
|
|
|||
|
control.toggleMute = function() {
|
|||
|
if (control.surfer) {
|
|||
|
control.surfer.toggleMute();
|
|||
|
control.isMute = !control.isMute;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
let initWaveSurfer = function() {
|
|||
|
control.isReady = false;
|
|||
|
control.currentTime = 0;
|
|||
|
if (!control.surfer) {
|
|||
|
let options = {
|
|||
|
container: $element[0].querySelector(
|
|||
|
'.waveSurferWave'
|
|||
|
)
|
|||
|
},
|
|||
|
defaults = {
|
|||
|
scrollParent: true,
|
|||
|
waveColor: 'violet',
|
|||
|
progressColor: 'purple'
|
|||
|
};
|
|||
|
|
|||
|
options = angular.extend(
|
|||
|
defaults,
|
|||
|
attributes,
|
|||
|
control.properties || {},
|
|||
|
options
|
|||
|
);
|
|||
|
control.surfer = WaveSurfer.create(options);
|
|||
|
|
|||
|
control.surfer.on('ready', function() {
|
|||
|
control.isReady = true;
|
|||
|
if (control.autoPlay) {
|
|||
|
control.surfer.play();
|
|||
|
}
|
|||
|
$scope.$apply();
|
|||
|
});
|
|||
|
|
|||
|
control.surfer.on('pause', function() {
|
|||
|
stopInterval();
|
|||
|
});
|
|||
|
|
|||
|
control.surfer.on('finish', function() {
|
|||
|
stopInterval();
|
|||
|
});
|
|||
|
|
|||
|
control.surfer.on('play', function() {
|
|||
|
startInterval();
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
control.title = control.title || control.src.split('/').pop();
|
|||
|
control.surfer.load(control.src);
|
|||
|
};
|
|||
|
|
|||
|
let startInterval = function() {
|
|||
|
timeInterval = $interval(function() {
|
|||
|
control.currentTime = control.isReady
|
|||
|
? control.surfer.getCurrentTime()
|
|||
|
: 0;
|
|||
|
}, 1000);
|
|||
|
},
|
|||
|
stopInterval = function() {
|
|||
|
$interval.cancel(timeInterval);
|
|||
|
};
|
|||
|
|
|||
|
initWaveSurfer();
|
|||
|
|
|||
|
$scope.$watch('control.src', function(src1, src2) {
|
|||
|
if (src1 != src2) {
|
|||
|
initWaveSurfer();
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
$element.on('$destroy', function() {
|
|||
|
if (control.surfer) {
|
|||
|
control.surfer.destroy();
|
|||
|
}
|
|||
|
stopInterval();
|
|||
|
});
|
|||
|
|
|||
|
$scope.$watch(
|
|||
|
function() {
|
|||
|
let div = $element[0].querySelector('.audioPlayerWrapper');
|
|||
|
return div ? div.offsetWidth : 0;
|
|||
|
},
|
|||
|
function(width) {
|
|||
|
if (width < 1) {
|
|||
|
//hidden
|
|||
|
control.surfer.pause();
|
|||
|
}
|
|||
|
}
|
|||
|
);
|
|||
|
}
|
|||
|
]);
|
|||
|
|
|||
|
/**
|
|||
|
* @ngdoc directive
|
|||
|
*
|
|||
|
* @name md-wavesurfer-player
|
|||
|
*
|
|||
|
* @usage
|
|||
|
* This directive can be used as a stand-alone directive to display Audio WaveSurfer with a few controls, by default
|
|||
|
* this will only display play/pause, fast-forward, rewind and mute toggle buttons, however, you can add extra
|
|||
|
* buttons using the `extra-buttons` parameters.
|
|||
|
*
|
|||
|
* ```html
|
|||
|
* <md-wavesurfer-player url="trackUrl" title="Track Title"
|
|||
|
* extra-buttons="extraButtons" properties="properties">
|
|||
|
* </md-wavesurfer-player>
|
|||
|
* ```
|
|||
|
*
|
|||
|
* @param {string} url the URL of the audio file
|
|||
|
* @param {string} title title of the audio track
|
|||
|
* @param {object} properties an object specifying init options for WaveSurfer
|
|||
|
* @param {boolean} auto-play specifies if the player should start as soon as it's loaded.
|
|||
|
* @param {object[]} extra-buttons a list of extra buttons to add to the control panel
|
|||
|
* each button should be an object with the following properties:
|
|||
|
* {
|
|||
|
* title: "button title"
|
|||
|
* action: "call back to call when button is clicked, executed in parent scope",
|
|||
|
* icon: "md-font-icon parameter for the button"
|
|||
|
* class: "extra classes to add to the button."
|
|||
|
* }
|
|||
|
*
|
|||
|
* Every other attribute passed to this directive is assumed to a WaveSurver init parameter.
|
|||
|
*/
|
|||
|
app.directive('mdWavesurferPlayer', function() {
|
|||
|
return {
|
|||
|
restrict: 'E',
|
|||
|
templateUrl: 'md-player.partial.html',
|
|||
|
scope: {
|
|||
|
src: '@url',
|
|||
|
title: '@',
|
|||
|
extraButtons: '=',
|
|||
|
toolbarClass: '@',
|
|||
|
autoPlay: '=',
|
|||
|
properties: '='
|
|||
|
},
|
|||
|
controller: 'mdWavesurferPlayerController',
|
|||
|
controllerAs: 'control',
|
|||
|
bindToController: true
|
|||
|
};
|
|||
|
});
|
|||
|
})();
|