UNPKG

ang-music-player

Version:

Angular Music/Audio Player component for web applications. Easy and HIGHLY customisable.

267 lines (257 loc) 19.2 kB
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