UNPKG

cplayer

Version:

A beautiful and clean WEB Music Player by HTML5.

380 lines (350 loc) 11.3 kB
require('./polyfill') import { listloopPlaymode } from './playmode/listloop'; import { IAudioItem, Iplaymode, IplaymodeConstructor, Iplaylist } from './interfaces'; import { EventEmitter } from 'events'; import View, { ICplayerViewOption } from './view'; import { decodeLyricStr } from "./lyric"; import { singlecyclePlaymode } from "./playmode/singlecycle"; import { listrandomPlaymode } from "./playmode/listrandom"; import shallowEqual from "./helper/shallowEqual"; import { cplayerMediaSessionPlugin } from "./mediaSession"; let cplayerView:typeof View = undefined; if (!process.env.cplayer_noview) { cplayerView = require('./view').default; } export interface ICplayerOption { playlist?: Iplaylist; playmode?: string; volume?: number; point?: number; autoplay?: boolean; } const defaultOption: ICplayerOption = { playlist: [], point: 0, volume: 1, playmode: 'listloop', autoplay: false } const playmodes: { [key: string]: IplaymodeConstructor } = { listloop: listloopPlaymode, singlecycle: singlecyclePlaymode, listrandom: listrandomPlaymode } function playlistPreFilter(playlist: Iplaylist) { return playlist.map((audio, index) => { let res = { ...audio }; if (typeof audio.lyric === 'string' && audio.lyric.replace(/\n+/gi, "\n").trim()) { res.lyric = decodeLyricStr(audio.lyric) } if (typeof audio.sublyric === 'string' && audio.sublyric.replace(/\n+/gi, "\n").trim()) { res.sublyric = decodeLyricStr(audio.sublyric) } return res; }) } export default class cplayer extends EventEmitter { private __paused = true; public view: View; public audioElement: HTMLAudioElement | HTMLVideoElement; private playmode: Iplaymode; private playmodeName: string = 'listloop'; private audioElementType: string; private _volume: number = 0; set mode(playmode: string) { this.setMode(playmode); } set volume(volume: number) { this.setVolume(volume); } get volume() { return this._volume } get mode() { return this.playmodeName; } get playlist() { return this.playmode.playlist; } get nowplay() { return this.playmode && this.playmode.now(); } get nowplaypoint() { return this.playmode && this.playmode.nowpoint(); } get played() { return !this.__paused; } get paused() { return this.__paused; } get duration() { if (this.audioElement) { return this.audioElement.duration; } return 0; } get currentTime() { if (this.audioElement) { return this.audioElement.currentTime; } return 0; } constructor(options: ICplayerOption & ICplayerViewOption) { super(); options = { ...defaultOption, ...options } this.playmode = new playmodes[options.playmode](playlistPreFilter(options.playlist), options.point); if (!process.env.cplayer_noview)this.view = new cplayerView(this, options); cplayerMediaSessionPlugin(this); // 同步调用会导致,用户的事件得不到触发 setImmediate(() => { this.openAudio(); this.setVolume(options.volume); if (options.autoplay && this.playlist.length > 0) { this.play(true).catch((e) => { console.log(e); this.pause(); }); } }); } private initializeEventEmitter(element: HTMLElement) { const a = element.addEventListener.bind(element); const e = this.eventHandlers; a('timeupdate', e.handleTimeUpdate); a('canplaythrough', e.handleCanPlayThrough); a('pause', e.handlePause); a('play', e.handlePlay); a('playing', e.handlePlaying) a('ended', e.handleEnded); a('loadeddata', e.handleLoadeddata); } private removeEventEmitter(element: HTMLElement) { const r = element.removeEventListener.bind(element); const e = this.eventHandlers; r('timeupdate', e.handleTimeUpdate); r('canplaythrough', e.handleCanPlayThrough); r('pause', e.handlePause); r('play', e.handlePlay); r('playing', e.handlePlaying) r('ended', e.handleEnded); r('loadeddata', e.handleLoadeddata); } private eventHandlers: { [key: string]: (...args: any[]) => void } = { handlePlay: (...args) => { if (this.__paused) { this.pause(); } }, handlePlaying: (...args) => { if (this.audioElement.currentTime === 0) { this.emit('started'); } }, handleTimeUpdate: (...args) => { let time = this.audioElement.duration; let playedTime = this.audioElement.currentTime; this.emit('timeupdate', playedTime, time); }, handleCanPlayThrough: (...args) => { this.emit('canplaythrough', ...args); }, handlePause: (...args) => { if (!this.__paused && !this.audioElement.ended) { this.play(true).catch((e) => { console.log(e); this.pause(); }); } }, handleEnded: (...args) => { this.emit('ended', ...args); if (!this.__paused) { this.next(); } }, handlePlayListChange: (...args) => { this.emit('playlistchange', ...args); }, handlePlaymodeChange: (mode: string = this.mode) => { this.emit('playmodechange', mode); }, handleLoadeddata: (...args) => { let time = this.audioElement.duration; let playedTime = this.audioElement.currentTime; this.emit('timeupdate', playedTime, time); } } public setCurrentTime(currentTime: number | string) { if (typeof currentTime === 'string') { currentTime.trim(); if (currentTime[currentTime.length - 1] === '%') { const percentage = parseFloat(currentTime); currentTime = this.duration * (percentage / 100); } } if (this.audioElement) { this.audioElement.currentTime = parseFloat(currentTime.toString()); } } private isPlaying() { return !!this.audioElement && this.audioElement.currentTime > 0 && !this.audioElement.paused && !this.audioElement.ended && this.audioElement.readyState > 2; } public openAudio(audio: IAudioItem = this.nowplay) { if (audio) { if (audio.type === 'video') { if (!(this.audioElementType === 'HTMLVideoElement')) { if(typeof this.audioElement !== 'undefined') { this.removeEventEmitter(this.audioElement); this.audioElement.src = ''; } this.audioElement = document.createElement('video'); if (this.audioElement instanceof HTMLVideoElement) { this.audioElementType = 'HTMLVideoElement'; this.audioElement.loop = false; this.audioElement.autoplay = false; this.audioElement.poster = audio.poster; this.audioElement.setAttribute('playsinline', 'true'); this.audioElement.setAttribute('webkit-playsinline', 'true'); } this.initializeEventEmitter(this.audioElement); this.emit('audioelementchange', this.audioElement); } } else { if (!(this.audioElementType === 'HTMLAudioElement')) { if(typeof this.audioElement !== 'undefined') { this.removeEventEmitter(this.audioElement); this.audioElement.src = ''; } this.audioElement = new Audio(); this.audioElementType = 'HTMLAudioElement'; this.audioElement.loop = false; this.audioElement.autoplay = false; this.initializeEventEmitter(this.audioElement); this.emit('audioelementchange', this.audioElement); } } this.setVolume(this.volume); this.audioElement.src = this.nowplay.src; this.emit('openaudio', audio); if (!this.__paused) { this.play(); } } } public toggleMode() { switch (this.playmodeName) { case 'listloop': this.setMode('singlecycle'); break; case 'singlecycle': this.setMode('listrandom'); break; case 'listrandom': this.setMode('listloop'); break; } } public setMode(playmode: string) { if (typeof playmode === 'string') { if (this.playmodeName !== playmode) { if (playmodes[playmode]) { this.playmode = new playmodes[playmode](this.playlist, this.nowplaypoint); this.playmodeName = playmode; this.eventHandlers.handlePlaymodeChange(); } } } } public getMode() { return this.mode; } public play(Forced: boolean = false) { let isPlaying = this.isPlaying(); let res; if (!isPlaying && this.playlist.length > 0 || Forced) { res = this.audioElement.play(); } if (this.__paused) { this.__paused = false; this.emit('playstatechange', this.__paused); this.emit('play'); } return res; } public pause(Forced: boolean = false) { let isPlaying = this.isPlaying(); if (isPlaying && this.playlist.length > 0 || Forced) { this.audioElement.pause(); } if (!this.__paused) { this.__paused = true; this.emit('playstatechange', this.__paused); this.emit('pause'); } } public to(id: number) { this.playmode.to(id); this.openAudio(); } public next() { this.playmode.next(); this.openAudio(); } public prev() { this.playmode.prev(); this.openAudio(); } public togglePlayState() { if (this.__paused) { this.play(); } else { this.pause(); } } public add(item: IAudioItem) { item = (playlistPreFilter([item] as Iplaylist))[0]; this.playmode.addMusic(item); this.eventHandlers.handlePlayListChange(); if (this.playlist.length === 1) { this.to(0); } } public remove(item: IAudioItem) { let needUpdate = this.playmode.removeMusic(item); this.eventHandlers.handlePlayListChange(); if (needUpdate) { this.openAudio(); } } public setVolume(volume: number | string) { this._volume = parseFloat(volume as string); if (this.audioElement) this.audioElement.volume = Math.max(0.0, Math.min(1.0, this._volume)); this.emit('volumechange', this.volume); } public destroy() { if (this.audioElement) { this.audioElement.src = null; this.audioElement.removeEventListener("timeupdate", this.eventHandlers.handleTimeUpdate); this.removeAllListeners(); } if (this.view) this.view.destroy(); Object.getOwnPropertyNames(this).forEach((name: keyof cplayer) => delete this[name]); (this as any).__proto__ = Object; } } if (!process.env.cplayer_noview) { function parseCPlayerTag() { Array.prototype.forEach.call(document.querySelectorAll('template[cplayer]'),(element: Element) => { element.attributes.getNamedItem('loaded') || new cplayer({ generateBeforeElement: true, deleteElementAfterGenerate: true, element, ...JSON.parse(element.innerHTML) }) }) } window.addEventListener("load", parseCPlayerTag); } (window as any).cplayer = cplayer;