ang-music-player
Version:
Angular Music/Audio Player component for web applications. Easy and HIGHLY customisable.
267 lines (257 loc) • 19.2 kB
JavaScript
import { ɵɵdefineInjectable, Injectable, EventEmitter, Component, Output, ViewChild, Input, Pipe, NgModule } from '@angular/core';
import { Subject } from 'rxjs';
import { CommonModule } from '@angular/common';
class AngMusicPlayerService {
constructor() { }
}
AngMusicPlayerService.ɵprov = ɵɵdefineInjectable({ factory: function AngMusicPlayerService_Factory() { return new AngMusicPlayerService(); }, token: AngMusicPlayerService, providedIn: "root" });
AngMusicPlayerService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
AngMusicPlayerService.ctorParameters = () => [];
class AudioPlyerOptions {
constructor() {
this.currentAudioTime = 0;
this.isAudioLoaded = false;
this.isRepeat = false;
this.audioVolume = 100;
this.isAudioEnded = new Subject;
this.isMute = false;
this.playEvent = new EventEmitter();
this.pauseEvent = new EventEmitter();
}
//get audio player events
options() {
//emit event when playing audio
this.audioPlayer.nativeElement.addEventListener('playing', () => {
});
//emit when intial loading of audio
this.audioPlayer.nativeElement.addEventListener('loadeddata', () => {
this.isAudioLoaded = true;
this.getAudioLength();
});
//emit time on playing audio
this.audioPlayer.nativeElement.addEventListener('timeupdate', () => {
//get current audio time
this.currentAudioTime = Math.floor(this.audioPlayer.nativeElement.currentTime);
//check if audio is ended for next song and pass data to component
if (this.audioPlayer.nativeElement.ended) {
this.isAudioEnded.next(true);
}
});
this.audioPlayer.nativeElement.addEventListener('volumechange', () => {
this.audioVolume = Math.floor(this.audioPlayer.nativeElement.volume * 100);
if (this.audioVolume == 0) {
this.isMute = true;
}
else {
this.isMute = false;
}
});
}
play() {
//toggle play-pause button
this.isAudioPlaying = true;
//play when user click play button
setTimeout(() => {
this.audioPlayer.nativeElement.play();
this.playEvent.emit();
}, 0);
}
pause() {
//toggle play-pause button
this.isAudioPlaying = false;
//pause when user click pause button
setTimeout(() => {
this.audioPlayer.nativeElement.pause();
this.pauseEvent.emit();
}, 0);
}
getAudioLength() {
this.totalAudioLength = Math.floor(this.audioPlayer.nativeElement.duration);
}
}
AudioPlyerOptions.decorators = [
{ type: Component, args: [{
template: ''
},] }
];
AudioPlyerOptions.propDecorators = {
playEvent: [{ type: Output }],
pauseEvent: [{ type: Output }],
audioPlayer: [{ type: ViewChild, args: ['audioPlayer', { static: true },] }]
};
class AngMusicPlayerComponent extends AudioPlyerOptions {
constructor() {
super();
this.audioList = [];
this.next = true;
this.previous = true;
this.shuffle = true;
this.repeat = true;
this.scrollTitle = false;
this.playButtonColor = "rgb(76, 130, 175)";
this.pauseButtonColor = "rgb(76, 130, 175)";
this.nextButtonColor = "rgb(76, 130, 175)";
this.previousButtonColor = "rgb(76, 130, 175)";
this.repeatButtonColor = "rgb(76, 130, 175)";
this.activeRepeatButtonColor = "rgb(76, 130, 175)";
this.volumeButtonColor = "rgb(76, 130, 175)";
this.muteButtonColor = "rgb(76, 130, 175)";
this.nextEvent = new EventEmitter();
this.previousEvent = new EventEmitter();
this.repeatEvent = new EventEmitter();
this.shuffleEvent = new EventEmitter();
this.seekEvent = new EventEmitter();
this.currentAudioIndex = 0;
this.repeatActive = false;
this.isError = false;
this.isShuffle = false;
}
ngOnInit() {
this.options();
this.initiateAudioPlayer();
//check audio is ended for next song
this.isAudioEnded.subscribe(data => {
if (!this.isRepeat && this.audioList.length > 0) {
this.nextAudio();
}
});
}
nextAudio() {
if (this.audioList.length - 1 != this.currentAudioIndex) {
this.currentAudioIndex += 1;
this.selectedAudio = this.audioList[this.currentAudioIndex];
this.getAudioLength();
if (this.isAudioPlaying) {
this.play();
}
this.nextEvent.emit();
}
else {
this.pause();
}
}
previousAudio() {
if (this.currentAudioIndex != 0) {
this.currentAudioIndex -= 1;
this.selectedAudio = this.audioList[this.currentAudioIndex];
this.getAudioLength();
if (this.isAudioPlaying) {
this.play();
}
this.previousEvent.emit();
}
}
seekAudio(seekAudioValue) {
if (this.audioVolume != 0) {
this.isMute = false;
}
this.audioPlayer.nativeElement.currentTime = seekAudioValue.target.value;
this.seekEvent.emit();
}
repeatAudio() {
this.isRepeat = !this.isRepeat;
this.repeatActive = !this.repeatActive;
this.audioPlayer.nativeElement.loop = this.isRepeat;
this.repeatEvent.emit();
}
/* shuffleAudio() {
this.isShuffle = !this.isShuffle;
if (this.isShuffle) {
let randomItem = Math.floor(Math.random() * this.audioList.length);
console.log(randomItem);
}
this.shuffleEvent.emit();
} */
volumeChange(volume) {
this.audioPlayer.nativeElement.volume = volume.target.value / 100;
}
muteAudio() {
if (this.isMute) {
this.audioPlayer.nativeElement.volume = 0.5;
this.isMute = false;
}
else {
this.audioPlayer.nativeElement.volume = 0;
this.isMute = true;
}
}
initiateAudioPlayer() {
if (this.audioList.length <= 0) {
this.isError = true;
}
else {
this.selectedAudio = this.audioList[this.currentAudioIndex];
}
}
}
AngMusicPlayerComponent.decorators = [
{ type: Component, args: [{
selector: 'ang-music-player',
template: "<audio controls #audioPlayer [src]=\"selectedAudio?.url\"> </audio>\r\n\r\n<div class=\"wrapper\" [ngStyle]=\"{ 'background-color': backgroundColor , 'width': width, 'height': height }\">\r\n\r\n <!-- loader and error -->\r\n <div class=\"loader\" *ngIf=\"!isAudioLoaded && !isError\"> Loading....</div>\r\n <div class=\"error\" *ngIf=\"isError\">No audio Found !</div>\r\n\r\n <div *ngIf=\"selectedAudio?.cover && isAudioLoaded\" class=\"cover\">\r\n <img [src]=\"selectedAudio?.cover\">\r\n </div>\r\n\r\n <div class=\"container\" *ngIf=\"isAudioLoaded\">\r\n <div class=\"details\">\r\n <div class=\"slidecontainer\">\r\n <input type=\"range\" max=\"{{totalAudioLength}}\" value=\"{{currentAudioTime}}\"\r\n [ngStyle]=\"{ 'background': timeSliderColor }\" (input)=\"seekAudio($event)\" class=\"slider\" id=\"myRange\">\r\n <div class=\"time-line\" [ngStyle]=\"{ 'color' : audioTimeColor }\">\r\n <div>{{ currentAudioTime | timeConversion}}</div>\r\n <div>{{ totalAudioLength | timeConversion}}</div>\r\n </div>\r\n </div>\r\n <marquee *ngIf=\"scrollTitle\" [ngStyle]=\"{ 'color' : audioTitleColor }\">\r\n <p class=\"title\"> {{selectedAudio?.title}}</p>\r\n </marquee>\r\n <p class=\"title\" [ngStyle]=\"{ 'color' : audioTitleColor }\" *ngIf=\"!scrollTitle\"> {{selectedAudio?.title}}</p>\r\n </div>\r\n <div class=\"controls\">\r\n\r\n <!-- repeat button starts -->\r\n <!-- not active -->\r\n <svg id=\"Layer_1\" *ngIf=\"repeat && !isRepeat\" (click)=\"repeatAudio()\" data-name=\"Layer 1\"\r\n xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\" [ngStyle]=\"{ 'fill' : repeatButtonColor }\">\r\n <title>repeat-on</title>\r\n <path class=\"cls-1\"\r\n d=\"M13.78,64a1.14,1.14,0,0,1-.8-.3L.26,51.3a1.07,1.07,0,0,1-.1-1.4l.1-.1L13,37.5a1.06,1.06,0,0,1,1.2-.2,1.2,1.2,0,0,1,.7,1v7.6H49.72a1.84,1.84,0,0,0,1.8-1.7V35.6a.91.91,0,0,1,.3-.7l8.51-8.3a1.06,1.06,0,0,1,1.2-.2,1.2,1.2,0,0,1,.7,1V46c0,6.7-3.6,10.2-10.51,10.2H14.88v6.7a1,1,0,0,1-.7,1A.6.6,0,0,1,13.78,64Z\"\r\n transform=\"translate(0.05 0)\" />\r\n <path class=\"cls-1\"\r\n d=\"M3,37.8a.6.6,0,0,1-.4-.1,1.09,1.09,0,0,1-.7-1V18C2,11.3,5.57,7.8,12.48,7.8H49.22V1.1a1.2,1.2,0,0,1,.7-1A.94.94,0,0,1,51,.3L63.64,12.7a1.07,1.07,0,0,1,.1,1.4l-.1.1L51,26.5a1.06,1.06,0,0,1-1.2.2,1.09,1.09,0,0,1-.7-1V18.1H14.38a1.69,1.69,0,0,0-1.7,1.7v8.6a1.14,1.14,0,0,1-.3.8L3.87,37.5A3.18,3.18,0,0,1,3,37.8Z\"\r\n transform=\"translate(0.05 0)\" />\r\n </svg>\r\n\r\n <!-- active -->\r\n <svg id=\"Layer_1\" *ngIf=\"repeat && isRepeat\" (click)=\"repeatAudio()\" data-name=\"Layer 1\"\r\n xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 63.98 64.03\" [ngStyle]=\"{ 'fill' : activeRepeatButtonColor }\">\r\n <style>\r\n .cls{\r\n fill: green\r\n }\r\n </style>\r\n <title>repeat-off</title>\r\n <path class=\"cls\"\r\n d=\"M13.78,64a1.13,1.13,0,0,1-.78-.31L.31,51.34a1,1,0,0,1-.07-1.41l.07-.07L13,37.49a1.09,1.09,0,0,1,1.19-.23,1,1,0,0,1,.66,1v7.63H49.64a1.73,1.73,0,0,0,1.75-1.69V35.59a1,1,0,0,1,.31-.74l8.49-8.28a1.12,1.12,0,0,1,1.18-.23,1.07,1.07,0,0,1,.67,1V46c0,6.68-3.63,10.21-10.51,10.21H14.86V63a1,1,0,0,1-.67,1A1,1,0,0,1,13.78,64Z\"\r\n transform=\"translate(0.02 0.03)\" />\r\n <path class=\"cls\"\r\n d=\"M3.05,37.75a1,1,0,0,1-.41-.09,1,1,0,0,1-.67-1V18C2,11.3,5.59,7.78,12.48,7.78H49.15V1.05a1.08,1.08,0,0,1,.67-1A1.1,1.1,0,0,1,51,.29L63.64,12.65a1,1,0,0,1,.07,1.41l-.07.07L51,26.49a1.08,1.08,0,0,1-1.18.23,1,1,0,0,1-.67-1v-7.6H14.36a1.71,1.71,0,0,0-1.72,1.69v8.61a1,1,0,0,1-.32.75l-8.5,8.26A1.06,1.06,0,0,1,3.05,37.75Z\"\r\n transform=\"translate(0.02 0.03)\" />\r\n </svg>\r\n <!-- repeat active ends -->\r\n\r\n <!-- previous button starts -->\r\n <svg id=\"Layer_1\" *ngIf=\"previous\" (click)=\"previousAudio()\" data-name=\"Layer 1\"\r\n xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 65 65.7\" [ngStyle]=\"{ 'fill' : previousButtonColor }\">\r\n <title>previous</title>\r\n <polygon class=\"cls-1\" points=\"64.5 0.85 7.05 32.85 64.5 64.85 64.5 0.85\" />\r\n <rect class=\"cls-1\" x=\"0.5\" y=\"0.85\" width=\"13.52\" height=\"64\" />\r\n </svg>\r\n <!-- previous button ends -->\r\n\r\n <!-- play button starts -->\r\n <svg id=\"Layer_1\" (click)=\"play()\" *ngIf=\"!isAudioPlaying\" data-name=\"Layer 1\" xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 64 64\" [ngStyle]=\"{ 'fill' : playButtonColor }\">\r\n <title>play</title>\r\n <path class=\"cls-1\"\r\n d=\"M21.65,79.51A3.48,3.48,0,0,1,18,76.22V18.79a3.47,3.47,0,0,1,3.64-3.28L80.8,45s2.73,2.47,0,4.93S21.65,79.51,21.65,79.51Z\"\r\n transform=\"translate(-18.01 -15.51)\" />\r\n </svg>\r\n <!-- play button ends -->\r\n\r\n <!-- pause button starts -->\r\n <svg id=\"Layer_1\" (click)=\"pause()\" *ngIf=\"isAudioPlaying\" data-name=\"Layer 1\" xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 64 64\" [ngStyle]=\"{ 'fill' : pauseButtonColor }\">\r\n <title>pause</title>\r\n <g id=\"pause\">\r\n <path class=\"cls-1\" d=\"M16,82H37.33V18H16ZM58.67,18V82H80V18Z\" transform=\"translate(-16 -18)\" />\r\n </g>\r\n </svg>\r\n <!-- pause button ends -->\r\n\r\n <!-- next button starts -->\r\n <svg id=\"Layer_1\" *ngIf=\"next\" (click)=\"nextAudio()\" data-name=\"Layer 1\" xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 64 64\" [ngStyle]=\"{ 'fill' : nextButtonColor }\">\r\n <title>next</title>\r\n <polygon class=\"cls-1\" points=\"0 64 57.45 32 0 0 0 64\" />\r\n <rect class=\"cls-1\" x=\"50.48\" width=\"13.52\" height=\"64\" />\r\n </svg>\r\n <!-- next button ends -->\r\n\r\n <!-- <div class=\"shuffle\" *ngIf=\"shuffle\" [ngClass]=\"{'shuffle-active': isShuffle}\" (click)=\"shuffleAudio()\"></div> -->\r\n\r\n <!-- volume button starts -->\r\n <div class=\"volume-container\">\r\n <!-- with volume -->\r\n <svg id=\"volume\" *ngIf=\"!isMute\" (click)=\"muteAudio()\" data-name=\"Layer 1\" xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 64 64\" [ngStyle]=\"{ 'fill' : pauseButtonColor }\">\r\n <defs>\r\n <style>\r\n .cls-2 {\r\n fill: none;\r\n }\r\n\r\n </style>\r\n </defs>\r\n <title>volume-high</title>\r\n <path class=\"cls-1\"\r\n d=\"M2.67,22.39V44.28H16.89L34.67,62.52V4.14L16.89,22.39Zm48,10.94a16.46,16.46,0,0,0-8.89-14.7V48A16.37,16.37,0,0,0,50.67,33.33Zm-8.89-32V8.85A25.49,25.49,0,0,1,59.56,33.33,25.5,25.5,0,0,1,41.78,57.82v7.51a32.65,32.65,0,0,0,24.89-32A32.66,32.66,0,0,0,41.78,1.33Z\"\r\n transform=\"translate(-2.67 -1.33)\" />\r\n <path class=\"cls-2\" d=\"M22.67,21.33h24v24h-24Z\" transform=\"translate(-2.67 -1.33)\" />\r\n </svg>\r\n\r\n <!-- mute -->\r\n <svg id=\"Capa_1\" *ngIf=\"isMute\" (click)=\"muteAudio()\" data-name=\"Capa 1\" xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 64 64\" [ngStyle]=\"{ 'fill' : muteButtonColor }\">\r\n <title>mute</title>\r\n <g id=\"volume-off\">\r\n <path class=\"cls-1\"\r\n d=\"M59,39.83a15.35,15.35,0,0,0-8.89-14.14v7.78L59,42.31Zm8.89,0A26.37,26.37,0,0,1,66.12,49l5.34,5.3A29.71,29.71,0,0,0,75,39.48,31.94,31.94,0,0,0,50.12,8.36v7.43C60.43,19.33,67.9,28.52,67.9,39.83ZM15.63,8,11,12.61,27.72,29.23H11V50.44H25.23L43,68.12V44.43L58.3,59.64a30,30,0,0,1-8.18,4.24V71.3a30.44,30.44,0,0,0,13.16-6.36L70.39,72,75,67.41,43,35.59ZM43,11.55,35.54,19,43,26.4Z\"\r\n transform=\"translate(-11.01 -8.01)\" />\r\n </g>\r\n </svg>\r\n\r\n <!-- volume button ends -->\r\n <div class=\"volume-control\">\r\n <input type=\"range\" max=\"100\" value=\"{{audioVolume}}\" (click)=\"$event.stopPropagation();\"\r\n (input)=\"volumeChange($event)\" [ngStyle]=\"{ 'background': volumeSliderColor }\" class=\"slider\"\r\n id=\"volumeRange\">\r\n </div>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n</div>\r\n",
styles: ["@import url(\"https://fonts.googleapis.com/css?family=Raleway&display=swap\");audio{display:none}.wrapper{align-items:center;background:#000;border-radius:15px;display:flex;font-family:Raleway,sans-serif;height:130px;width:500px}.wrapper .cover img{border-radius:50%;margin-left:10px;max-height:100px;max-width:100px}.wrapper .loader{color:#fff}.wrapper .error,.wrapper .loader{margin:0 auto;position:relative;text-align:center}.wrapper .error{color:#ff3737}.wrapper .container{padding:5px;width:100%}.wrapper .container .details{text-align:center}.wrapper .container .details .title{color:#4c82af;margin:7px 0 15px}.wrapper .container .controls{display:flex;flex-basis:auto;flex-direction:row;flex-wrap:wrap;justify-content:space-around;margin-top:8px}.wrapper .container .controls svg{cursor:pointer;height:25px;margin:0 20px;width:25px}.wrapper .container .volume-container{position:relative}.wrapper .container .volume-container .volume-control{display:none;height:32px;left:-9px;position:absolute;top:-62px;transform:rotate(270deg);width:90px;z-index:1000}.wrapper .container .volume-container:hover .volume-control{display:block}.slidecontainer{align-items:center;display:flex;flex-direction:column;width:100%}.slidecontainer .time-line{color:#4c82af;display:flex;justify-content:space-between;margin:7px 0 2px;width:87%}.slider{-webkit-appearance:none;background:#fff;border-radius:5px;height:2px;opacity:1;outline:none;transition:opacity .2s;width:87%;z-index:1000}.slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;background:#4c82af;border-radius:50%;cursor:pointer;height:15px;width:15px}.slider::-moz-range-thumb{background:#4c82af;border-radius:50%;cursor:pointer;height:15px;width:15px}"]
},] }
];
AngMusicPlayerComponent.ctorParameters = () => [];
AngMusicPlayerComponent.propDecorators = {
width: [{ type: Input }],
height: [{ type: Input }],
backgroundColor: [{ type: Input }],
audioTimeColor: [{ type: Input }],
audioTitleColor: [{ type: Input }],
volumeSliderColor: [{ type: Input }],
timeSliderColor: [{ type: Input }],
audioList: [{ type: Input }],
next: [{ type: Input }],
previous: [{ type: Input }],
shuffle: [{ type: Input }],
repeat: [{ type: Input }],
scrollTitle: [{ type: Input }],
playButtonColor: [{ type: Input }],
pauseButtonColor: [{ type: Input }],
nextButtonColor: [{ type: Input }],
previousButtonColor: [{ type: Input }],
repeatButtonColor: [{ type: Input }],
activeRepeatButtonColor: [{ type: Input }],
volumeButtonColor: [{ type: Input }],
muteButtonColor: [{ type: Input }],
nextEvent: [{ type: Output }],
previousEvent: [{ type: Output }],
repeatEvent: [{ type: Output }],
shuffleEvent: [{ type: Output }],
seekEvent: [{ type: Output }]
};
class TimeConversionPipe {
transform(audioTime) {
let audioLengthMin = ("0" + Math.floor(audioTime / 60)).slice(-2);
let audioLengthSec = ("0" + audioTime % 60).slice(-2);
return `${audioLengthMin}:${audioLengthSec}`;
}
}
TimeConversionPipe.decorators = [
{ type: Pipe, args: [{
name: 'timeConversion'
},] }
];
class AngMusicPlayerModule {
}
AngMusicPlayerModule.decorators = [
{ type: NgModule, args: [{
declarations: [AngMusicPlayerComponent, TimeConversionPipe],
imports: [
CommonModule
],
exports: [AngMusicPlayerComponent]
},] }
];
/*
* Public API Surface of ang-music-player
*/
/**
* Generated bundle index. Do not edit.
*/
export { AngMusicPlayerComponent, AngMusicPlayerModule, AngMusicPlayerService, AudioPlyerOptions as ɵa, TimeConversionPipe as ɵb };
//# sourceMappingURL=ang-music-player.js.map