From a024f82b6ab243536f747ba1d0f096aed110ba84 Mon Sep 17 00:00:00 2001 From: Faizan Hasan Date: Sat, 29 Apr 2017 05:30:01 +0500 Subject: [PATCH 1/5] Dependencies fixes --- package.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index dcb742c..71d0954 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "release": "npm run prepublish && npm publish", "test": "echo \"Error: no test specified\" && exit 1" }, - "dependencies": { + "dependencies": {}, + "devDependencies": { "@angular/common": "4.0.0", "@angular/compiler": "4.0.0", "@angular/compiler-cli": "4.0.0", @@ -26,9 +27,7 @@ "ionicons": "3.0.0", "rxjs": "5.1.1", "sw-toolbox": "3.4.0", - "zone.js": "^0.8.4" - }, - "devDependencies": { + "zone.js": "^0.8.4", "typescript": "~2.2.1" }, "repository": { From e06c6dc1b9c9f9547ad9dd0127fb92e7a7a61b32 Mon Sep 17 00:00:00 2001 From: codinronan Date: Thu, 4 May 2017 22:36:22 -0500 Subject: [PATCH 2/5] Add several useful events on IAudioTrack for its state changes --- dist/ionic-audio-cordova-track.ts | 164 ++++++++++++++------- dist/ionic-audio-interfaces.ts | 38 +++-- dist/ionic-audio-track-component.ts | 117 +++++++++------ dist/ionic-audio-track-play-component.ts | 60 ++++---- dist/ionic-audio-web-track.ts | 180 +++++++++++++++-------- tsconfig.json | 80 +++++----- 6 files changed, 407 insertions(+), 232 deletions(-) diff --git a/dist/ionic-audio-cordova-track.ts b/dist/ionic-audio-cordova-track.ts index 6c22d57..545929b 100644 --- a/dist/ionic-audio-cordova-track.ts +++ b/dist/ionic-audio-cordova-track.ts @@ -1,11 +1,11 @@ -import {IAudioTrack} from './ionic-audio-interfaces'; -import {Injectable} from '@angular/core'; +import {IAudioTrack, IAudioTrackError} from './ionic-audio-interfaces'; +import {Injectable, EventEmitter} from '@angular/core'; declare let Media: any; /** * Cordova Media audio track - * + * * @export * @class CordovaAudioTrack * @constructor @@ -16,6 +16,7 @@ export class CordovaAudioTrack implements IAudioTrack { private audio: any; public isPlaying: boolean = false; public isFinished: boolean = false; + public isLoaded: boolean = false; private _progress: number = 0; private _completed: number = 0; private _duration: number; @@ -23,91 +24,146 @@ export class CordovaAudioTrack implements IAudioTrack { private _isLoading: boolean; private _hasLoaded: boolean; private _timer: any; - + + /** + * Notifies when the track has loaded + * + * @property onPlayBegin + * @type {EventEmitter} + */ + onLoaded: EventEmitter = new EventEmitter(); + + /** + * Notifies when playback has begun + * + * @property onPlayBegin + * @type {EventEmitter} + */ + onPlaying: EventEmitter = new EventEmitter(); + + /** + * Notifies when playback has begun + * + * @property onPlayBegin + * @type {EventEmitter} + */ + onStop: EventEmitter = new EventEmitter(); + + /** + * Notifies when playback has completed + * + * @property onPlayBegin + * @type {EventEmitter} + */ + onFinished: EventEmitter = new EventEmitter(); + + /** + * Notifies when playback has begun + * + * @property onPlayBegin + * @type {EventEmitter} + */ + onProgressChange: EventEmitter = new EventEmitter(); + + /** + * Notifies when the media has experienced an error + * + * @property onPlayBegin + * @type {EventEmitter} + */ + onError: EventEmitter = new EventEmitter(); + constructor(public src: string) { if (window['cordova'] === undefined || window['Media'] === undefined) { console.log('Cordova Media is not available'); return; }; - - this.createAudio(); + + this.createAudio(); } - + private createAudio() { this.audio = new Media(this.src, () => { console.log('Finished playback'); this.stopTimer(); - this.isFinished = true; + this.isFinished = true; + this.onFinished.emit(this); this.destroy(); // TODO add parameter to control whether to release audio on stop or finished }, (err) => { - console.log(`Audio error => track ${this.src}`, err); + console.log(`Audio error => track ${this.src}`, err); + this.onError.emit({track: this, error: err}); }, (status) => { switch (status) { case Media.MEDIA_STARTING: console.log(`Loaded track ${this.src}`); this._hasLoaded = true; + this.isLoaded = true; + this.onLoaded.emit(this); break; case Media.MEDIA_RUNNING: console.log(`Playing track ${this.src}`); this.isPlaying = true; - this._isLoading = false; - break; + this._isLoading = false; + this.onPlaying.emit(this); + break; case Media.MEDIA_PAUSED: this.isPlaying = false; break case Media.MEDIA_STOPPED: this.isPlaying = false; + this.onStop.emit(this); break; } - }); + }); } - + private startTimer() { - this._timer = setInterval(() => { + this._timer = setInterval(() => { if (this._duration===undefined || this._duration < 0) { this._duration = Math.round(this.audio.getDuration()*100)/100; - } - + } + this.audio.getCurrentPosition((position) => { if (position > -1) { this._progress = Math.round(position*100)/100; - this._completed = this._duration > 0 ? Math.round(this._progress / this._duration * 100)/100 : 0; + this._completed = this._duration > 0 ? Math.round(this._progress / this._duration * 100)/100 : 0; + this.onProgressChange.emit(this); } }, (e) => { console.log("Error getting position", e); } ); - }, 1000); + }, 1000); } - + private stopTimer() { clearInterval(this._timer); } - + /** public members */ /** * Gets the track id - * + * * @property id * @type {number} */ public get id() : number { return this._id; } - + /** * Sets the track id - * + * * @property id */ public set id(v : number) { this._id = v; } - + /** * Gets the track duration, or -1 if it cannot be determined - * + * * @property duration * @readonly * @type {number} @@ -115,21 +171,21 @@ export class CordovaAudioTrack implements IAudioTrack { public get duration() : number { return this._duration; } - + /** * Gets current track time (progress) - * + * * @property progress * @readonly * @type {number} */ public get progress() : number { return this._progress; - } - + } + /** * Gets current track progress as a percentage - * + * * @property completed * @readonly * @type {number} @@ -141,17 +197,17 @@ export class CordovaAudioTrack implements IAudioTrack { /** * Gets any errors logged by HTML5 audio * - * @property error + * @property error * @readonly * @type {MediaError} */ public get error() : MediaError { return this.audio.error; } - + /** * Gets a boolean value indicating whether the current source can be played - * + * * @property canPlay * @readonly * @type {boolean} @@ -159,10 +215,10 @@ export class CordovaAudioTrack implements IAudioTrack { public get canPlay() : boolean { return true; } - + /** * Gets a boolean value indicating whether the track is in loading state - * + * * @property isLoading * @readonly * @type {boolean} @@ -170,76 +226,78 @@ export class CordovaAudioTrack implements IAudioTrack { public get isLoading() : boolean { return this._isLoading; } - + /** * Gets a boolean value indicating whether the track has finished loading * - * @property hadLoaded + * @property hadLoaded * @readonly * @type {boolean} */ public get hasLoaded() : boolean { return this._hasLoaded; } - + /** * Plays current track - * + * * @method play */ play() { if (!this.audio) { - this.createAudio(); + this.createAudio(); } - + if (!this._hasLoaded) { console.log(`Loading track ${this.src}`); this._isLoading = true; } - + this.audio.play(); this.startTimer(); } - + /** * Pauses current track * - * @method pause + * @method pause */ pause() { if (!this.isPlaying) return; console.log(`Pausing track ${this.src}`); this.audio.pause(); - this.stopTimer(); + this.stopTimer(); } - + /** * Stops current track and releases audio * - * @method stop + * @method stop */ stop() { this.audio.stop(); // calls Media onSuccess callback } - + /** * Seeks to a new position within the track * - * @method seekTo + * @method seekTo * @param {number} time the new position (milliseconds) to seek to */ seekTo(time: number) { // Cordova Media reports duration and progress as seconds, so we need to multiply by 1000 this.audio.seekTo(time*1000); } - + /** * Releases audio resources - * + * * @method destroy */ destroy() { - this.audio.release(); + this.isLoaded = false; + this._hasLoaded = false; + this.audio.release(); console.log(`Released track ${this.src}`); } -} \ No newline at end of file +} diff --git a/dist/ionic-audio-interfaces.ts b/dist/ionic-audio-interfaces.ts index 2077251..627e62c 100644 --- a/dist/ionic-audio-interfaces.ts +++ b/dist/ionic-audio-interfaces.ts @@ -1,24 +1,25 @@ +import { EventEmitter } from '@angular/core'; /** * Defines the audio provider contract - * + * * @export * @interface IAudioProvider */ export interface IAudioProvider { current: number; tracks: IAudioTrack[]; - + create(track: ITrackConstraint): IAudioTrack; add(track: IAudioTrack); play(index: number); pause(index?: number); stop(index?: number); -} +} /** * Defines the properties for JSON objects representing tracks to be played - * + * * @export * @interface ITrackConstraint */ @@ -27,13 +28,13 @@ export interface ITrackConstraint { src: string; title?: string; artist?: string; - art?: string; + art?: string; preload?: string; } /** - * Defines the audio track contract - * + * Defines the audio track contract + * * @export * @interface IAudioTrack * @extends {ITrackConstraint} @@ -41,20 +42,37 @@ export interface ITrackConstraint { export interface IAudioTrack extends ITrackConstraint { src: string; id: number; - isPlaying: boolean; + isPlaying: boolean; isLoading: boolean; isFinished: boolean; + isLoaded: boolean; duration: number; progress: number; completed: number; canPlay: boolean; error: MediaError; - + play(); pause(); stop(); seekTo(time: number); destroy(); -} + onLoaded: EventEmitter; + onPlaying: EventEmitter; + onStop: EventEmitter; + onFinished: EventEmitter; + onProgressChange: EventEmitter; + onError: EventEmitter; +} +/** + * Defines the audio track error contract + * + * @export + * @interface IAudioTrackError + */ +export interface IAudioTrackError { + track: IAudioTrack, + error: ErrorEvent, +} diff --git a/dist/ionic-audio-track-component.ts b/dist/ionic-audio-track-component.ts index 84be59e..d67dc5b 100644 --- a/dist/ionic-audio-track-component.ts +++ b/dist/ionic-audio-track-component.ts @@ -1,18 +1,18 @@ -import {ITrackConstraint, IAudioTrack} from './ionic-audio-interfaces'; -import {AudioProvider} from './ionic-audio-providers'; -import {WebAudioTrack} from './ionic-audio-web-track'; -import {CordovaAudioTrack} from './ionic-audio-cordova-track'; +import {ITrackConstraint, IAudioTrack} from './ionic-audio-interfaces'; +import {AudioProvider} from './ionic-audio-providers'; +import {WebAudioTrack} from './ionic-audio-web-track'; +import {CordovaAudioTrack} from './ionic-audio-cordova-track'; import {Component, DoCheck, EventEmitter, Output, Input} from '@angular/core'; /** - * # `````` - * + * # `````` + * * Creates a top level audio-track component - * + * * ## Usage - * + * * ```` * * ... @@ -26,7 +26,7 @@ import {Component, DoCheck, EventEmitter, Output, Input} from '@angular/core'; selector: 'audio-track', template: '' }) -export class AudioTrackComponent implements DoCheck { +export class AudioTrackComponent implements DoCheck { /** * Input property containing a JSON object with at least a src property * ```` @@ -42,114 +42,145 @@ export class AudioTrackComponent implements DoCheck { * @type {ITrackConstraint} */ @Input() track: ITrackConstraint; - + /** * Output property expects an event handler to be notified whenever playback finishes - * + * * @property onFinish * @type {EventEmitter} */ @Output() onFinish = new EventEmitter(); - + private _isFinished: boolean = false; + + /** + * Output property expects an event handler to be notified whenever playback has loaded + * + * @property onLoaded + * @type {EventEmitter} + */ + @Output() onLoaded: EventEmitter = new EventEmitter(); + + private _isLoaded: boolean = false; + private _audioTrack: IAudioTrack; - + constructor(private _audioProvider: AudioProvider) {} - + ngOnInit() { if (!(this.track instanceof WebAudioTrack) && !(this.track instanceof CordovaAudioTrack)) { - this._audioTrack = this._audioProvider.create(this.track); + this._audioTrack = this._audioProvider.create(this.track); } else { Object.assign(this._audioTrack, this.track); this._audioProvider.add(this._audioTrack); } - + // update input track parameter with track is so we pass it to WebAudioProvider if needed - this.track.id = this._audioTrack.id; + this.track.id = this._audioTrack.id; } - - play() { + + play() { this._audioTrack.play(); this._audioProvider.current = this._audioTrack.id; } - + pause() { this._audioTrack.pause(); this._audioProvider.current = undefined; } - + + stop() { + this._audioTrack.stop(); + this._audioProvider.current = undefined; + } + toggle() { if (this._audioTrack.isPlaying) { this.pause(); } else { this.play(); - } + } } - + + toggleStop() { + if (this._audioTrack.isPlaying) { + this.stop(); + } else { + this.play(); + } + } + seekTo(time:number) { - this._audioTrack.seekTo(time); + this._audioTrack.seekTo(time); } - - + + public get id() : number { return this._audioTrack.id; } - + public get art() : string { return this.track.art; } - - + + public get artist() : string { return this.track.artist; } - - + + public get title() : string { return this.track.title; } - + public get progress() : number { return this._audioTrack.progress; } - + public get isPlaying() : boolean { return this._audioTrack.isPlaying; } - + public get duration() : number { return this._audioTrack.duration; } - + public get completed() : number { return this._audioTrack.completed; } - + public get canPlay() { return this._audioTrack.canPlay; } - + public get error() { return this._audioTrack.error; } - + public get isLoading() : boolean { return this._audioTrack.isLoading; } - + public get hasLoaded() : boolean { return this.hasLoaded; } - + ngDoCheck() { if(!Object.is(this._audioTrack.isFinished, this._isFinished)) { // some logic here to react to the change this._isFinished = this._audioTrack.isFinished; - + // track has stopped, trigger finish event if (this._isFinished) { - this.onFinish.emit(this.track); + this.onFinish.emit(this.track); + } + } + + if(!Object.is(this._audioTrack.isLoaded, this._isLoaded)) { + this._isLoaded = this._audioTrack.isLoaded; + if (this._isLoaded) { + this.onLoaded.emit(this.track); } } } -} \ No newline at end of file +} diff --git a/dist/ionic-audio-track-play-component.ts b/dist/ionic-audio-track-play-component.ts index d746b38..9cb1277 100644 --- a/dist/ionic-audio-track-play-component.ts +++ b/dist/ionic-audio-track-play-component.ts @@ -1,36 +1,36 @@ -import {IAudioTrack} from './ionic-audio-interfaces'; +import {IAudioTrack} from './ionic-audio-interfaces'; import {Component, ElementRef, Input} from '@angular/core'; /** - * # `````` - * + * # `````` + * * Renders a play/pause button that optionally displays a loading spinner - * + * * ## Usage * ```` * - * + * * *

{{audio.title}}

- *
+ *
*
* ```` - * If placed within a `````` component it will render as a semi-transparent button layover (see live demo). + * If placed within a `````` component it will render as a semi-transparent button layover (see live demo). * Passing a `````` as a child element will display a loading spinner while loading. - * + * * ```` * - * + * * * - * + * * *

{{audio.title}}

- *
+ *
*
* ```` - * - * @element audio-track-play + * + * @element audio-track-play * @parents audio-track * @export * @class AudioTrackPlayComponent @@ -38,7 +38,7 @@ import {Component, ElementRef, Input} from '@angular/core'; @Component({ selector: 'audio-track-play', template: ` -