summaryrefslogblamecommitdiff
path: root/libs/music/player.d/main_static.js
blob: 311aa99e33a7985bd973fc14277feb23390ac69c (plain) (tree)







































































































































                                                                                                                                      
                   






























                                                                                          
                                            




































































                                                                                                                                
                                                                                                                                































































                                                                                                                     
                                                                                                                                  















































































































































































































































































































































                                                                                                                                                                                       
//Chris Xiong 2015,2016,2017,2018
//License: Expat

let NSInk=null;
let NSAudio=null;
let NSVisualization=null;
let NSPlayer=null;
let NSUI=null;

const sh={
	elem:function(e)
	{return document.getElementById(e);},
	newelem:function(e)
	{return document.createElement(e);},
	getcookie:function(key)
	{return document.cookie.replace(new RegExp('(?:(?:^|.*;\\s*)'+key+'\\s*\\=\\s*([^;]*).*$)|^.*$'),'$1');},
	useFLAC:function()
	{return sh.elem('audio').canPlayType('audio/ogg')=='';}
};

class Ink
{
	constructor(_vx,_vy,_c)
	{
		this.x=NSUI.canvas.width/2;this.y=NSUI.canvas.height/2;
		this.vx=_vx;this.vy=_vy;
		this.color=_c>6?6:_c;this.active=true;
	}
	update()
	{
		const canvas=NSUI.canvas;
		this.x+=this.vx;this.y+=this.vy;
		this.vx*=0.995;this.vy*=0.995;
		if(this.x<-10||this.x>canvas.width+10||this.y<-10||this.y>canvas.height+10)
			this.active=false;
	}
	draw(cctx)
	{
		cctx.drawImage(NSInk.inkimg[this.color],this.x+5*window.devicePixelRatio,this.y+5*window.devicePixelRatio);
	}
}

NSPlayer={
	plistname:null,
	tracks:null,
	current:null,
	shuffle:0,
	repeat:0,
	load_playlist:async function(pln,ord)
	{
		let r=null;
		try{
			const resp=await fetch(new Request(`/libs/music/player.d/playlists/${pln}.playlist?${new Date().getTime()}`));
			r=await resp.text();
		}catch(e){return;}
		let rarr=r.split('\n');
		let tarr=[];
		const fmt=sh.useFLAC()?'flac':'ogg';
		for(let i=1;i<rarr.length;++i)
		{
			let t=rarr[i].trim();
			if(!t.length)continue;
			let titem={};
			titem.title=t;
			titem.ord=i-1;
			titem.src=`//filestorage.chrisoft.org/music/${fmt}/${t}.${fmt}`;
			tarr.push(titem);
		}
		return ({plistname:pln,playlist:tarr,plistord:ord});
	},
	play:function(id)
	{
		if(!this.plistname)return;
		window.history.replaceState('','The Stupid Online Player',
			`#${encodeURIComponent(this.plistname)}/${encodeURIComponent(this.tracks[id].title)}`);
		NSUI.iplaypause.style.backgroundPosition=`${NSUI.bpauserect}`;
		this.current=id;
		NSUI.lbnowplaying.innerHTML=`Now Playing: ${this.tracks[id].title}`;
		if(navigator.mediaSession)
			navigator.mediaSession.metadata=new MediaMetadata({title:this.tracks[id].title,album:this.plistname});
		NSUI.set_highlighted(this.plistname,this.tracks[id].title);
		NSUI.audio.src=this.tracks[id].src;
		NSUI.audio.load();
		NSUI.audio.play();
	},
	next:function()
	{
		this.current=(this.current+1)%this.tracks.length;
		if(this.current==0&&this.shuffle)
			NSUI.switch_playlist(this.plistname);
		this.play(this.current);
	},
	prev:function()
	{
		this.current=(this.current-1+this.tracks.length)%this.tracks.length;
		this.play(this.current);
	},
	switch_playlist:function(plistname,plist)
	{
		this.plistname=plistname;
		this.tracks=plist;
	},
	sort_playlist:function(shuffle,plist)
	{
		const tarr=plist?plist:this.tracks;
		if(!tarr)return;
		if(shuffle)
		for(let i=0;i<tarr.length;++i)
			tarr[i].sord=Math.random()*tarr.length;
		tarr.sort(
			(a,b)=>{
				const ra=shuffle?a.sord:a.ord;
				const rb=shuffle?b.sord:b.ord;
				return ra<rb?-1:1;
			}
		);
		if(plist)plist=tarr;else this.tracks=tarr;
	}
};

NSUI={
	playlist:null,
	audio:null,
	canvas:null,
	iplaypause:null,
	irepeat:null,
	ishuffle:null,
	lbnowplaying:null,
	playlists:null,
	ulplaylists:null,
	swplaylist:null,
	selectedplist:null,
	vissel:null,
	pbnext:null,
	pbprev:null,
	ctrlcontainer:null,
	am3u8:null,
	bplayrect:"0 -48px",
	bpauserect:"-24px -48px",
	brallrect:"-24px -24px",
	brnonrect:"-48px -24px",
	bronerect:"0 0",
	bsoffrect:"-24px 0",
	bshonrect:"-48px 0",
	plistshown:true,
	setup_ui:function()
	{
		window.devicePixelRatio=window.devicePixelRatio?window.devicePixelRatio:1;
		window.onresize=function()
		{
			if(window.innerWidth<768)
			setupevents();
			else unsetevents();
		};
		window.onresize();
		this.playlist=sh.elem('playlist');
		this.swplaylist=sh.elem('plistsw');
		this.audio=sh.elem('audio');
		this.canvas=sh.elem('cvs');
		this.iplaypause=sh.elem('imgplaypause');
		this.irepeat=sh.elem('imgrepeat');
		this.ishuffle=sh.elem('imgshuffle');
		this.lbnowplaying=sh.elem('nowplaying');
		this.ulplaylists=sh.elem('plists');
		this.vissel=sh.elem('visualizationsel');
		this.pbnext=sh.elem('pbnext');
		this.pbprev=sh.elem('pbprev');
		this.ctrlcontainer=sh.elem('ctrlcontainer');
		this.am3u8=sh.elem('am3u8');
		NSUI.canvas.width=NSUI.canvas.clientWidth*window.devicePixelRatio;
		NSUI.canvas.height=NSUI.canvas.clientHeight*window.devicePixelRatio;
		NSUI.vissel.onchange=function(){
			if(this.value!='none'&&this.oldvalue=='none')requestAnimationFrame(NSVisualization.updateVisualization);
			else NSUI.canvas.getContext('2d').clearRect(0,0,NSUI.canvas.width,NSUI.canvas.height);
			document.cookie='playervisualization='+this.value;
			this.oldvalue=this.value;
		};
		document.getElementById('shufflesw').onclick=function(){
			NSUI.shuffle_switch(NSPlayer.shuffle=1-NSPlayer.shuffle);
			NSUI.ishuffle.style.backgroundPosition=`${NSPlayer.shuffle?NSUI.bshonrect:NSUI.bsoffrect}`;
			document.cookie=`playershuffle=${NSPlayer.shuffle}`;
		};
		document.getElementById('repeatsw').onclick=function(){
			NSPlayer.repeat=1-NSPlayer.repeat;
			NSUI.audio.loop=NSPlayer.repeat?true:false;
			NSUI.irepeat.style.backgroundPosition=`${NSPlayer.repeat?NSUI.bronerect:NSUI.brallrect}`;
			document.cookie=`playerrepeat=${NSPlayer.repeat}`;
		};
		document.getElementById('plistsw').onclick=NSUI.togglePlist;
		document.getElementById('tsliderbase').onclick=
		document.getElementById('tsliderbase').onmousemove=function(e)
		{
			if(e.type=='click'||(e.type=='mousemove'&&e.buttons==1))
			{
				const sr=this.getBoundingClientRect();
				const p=(e.clientX-sr.left)/sr.width;
				NSUI.audio.currentTime=NSUI.audio.duration*p;
			}
		};
		NSUI.iplaypause.onclick=function()
		{
			if(NSUI.audio.src=='')
			{
				if(NSUI.selectedplist)
				{
					NSUI.switch_playlist(NSUI.selectedplist);
					NSUI.switch_track(0);
				}
				return;
			}
			if(NSUI.audio.paused)
			{
				NSUI.audio.play();
				NSUI.iplaypause.style.backgroundPosition=`${NSUI.bpauserect}`;
			}
			else
			{
				NSUI.audio.pause();
				NSUI.iplaypause.style.backgroundPosition=`${NSUI.bplayrect}`;
			}
		};
		NSUI.pbprev.onclick=function()
		{
			if(NSUI.audio.currentTime>10)NSUI.audio.currentTime=0;
			else NSPlayer.prev();
		};
		NSUI.pbnext.onclick=NSPlayer.next.bind(NSPlayer);
		if(navigator.mediaSession)
		{
			navigator.mediaSession.setActionHandler('previoustrack',NSUI.pbprev.onclick);
			navigator.mediaSession.setActionHandler('nexttrack',NSUI.pbnext.onclick);
		}
	},
	load_playlists:async function()
	{
		const moi=this;
		let r=null;
		try{
			const resp=await fetch(new Request(`/libs/music/player.d/playlists/playlists?${new Date().getTime()}`));
			r=await resp.text();
		}catch(e){return;}
		let rarr=r.split('\n');
		let tarr=[];
		let cnt=0;
		for(let i=0;i<rarr.length;++i)
		if(rarr[i].trim().length)++cnt;
		const p=new Promise((a,b)=>{
				for(let i=0;i<rarr.length;++i)
				{
					let t=rarr[i].trim();
					if(!t.length)continue;
					NSPlayer.load_playlist(t,i).then((rp)=>{
							tarr[rp.plistord]=rp;
							if(--cnt<=0)a(null);
						});
				}
			});
		await p;
		for(let i=0;i<tarr.length;++i)
		{
			const e=sh.newelem('li');
			const ea=sh.newelem('a');
			ea.innerHTML=tarr[i].plistname;
			ea.classList.add('listitem');
			ea.href='javascript:void(0);';
			ea.pid=i;
			ea.onclick=function(e){
				e.preventDefault();
				for(let i=0;i<NSUI.ulplaylists.childNodes.length;++i)
				{
					const ta=NSUI.ulplaylists.childNodes[i].firstChild;
					if(ta.pid==this.pid)
					{ta.classList.add('highlighted');ta.classList.add('active');}
					else{ta.classList.remove('highlighted');ta.classList.remove('active');}
				}
				NSUI.present_playlist.bind(NSUI,this.pid)();
				if(NSPlayer.current)
				NSUI.set_highlighted(NSPlayer.plistname,NSPlayer.tracks[NSPlayer.current].title);
			};
			e.appendChild(ea);
			NSUI.ulplaylists.appendChild(e);
		}
		moi.playlists=tarr;
	},
	present_playlist:function(id)
	{
		while(this.playlist.firstChild)
		this.playlist.removeChild(this.playlist.firstChild);
		const list=this.playlists[id].playlist;
		for(let i=0;i<list.length;++i)
		{
			const l=sh.newelem('li');
			const a=sh.newelem('a');
			a.innerHTML=list[i].title;
			a.href=list[i].src;
			a.ord=i;
			a.onclick=function(e){e.preventDefault();NSUI.switch_track.bind(NSUI,this.ord)();};
			l.appendChild(a);
			this.playlist.appendChild(l);
		}
		const d=sh.newelem('div');d.style.height=`${NSUI.ctrlcontainer.getBoundingClientRect().height+16}px`;
		this.playlist.appendChild(d);
		this.selectedplist=this.playlists[id].plistname;
		this.am3u8.href=`https://chrisoft.org/libs/music/player.d/cgi-bin/m3u8.cgi?plist=${this.playlists[id].plistname}`;
	},
	switch_playlist:function(pl,setactive)
	{
		let cid=0;
		for(;cid<this.playlists.length;++cid)
		if(this.playlists[cid].plistname==pl)break;
		if(cid>=this.playlists.length)return;
		if(setactive)
		{
			NSUI.ulplaylists.childNodes[cid].firstChild.classList.add('highlighted');
			NSUI.ulplaylists.childNodes[cid].firstChild.classList.add('active');
		}
		//VERY stupid design, but it _should_ work.
		NSPlayer.switch_playlist(pl,this.playlists[cid].playlist);
		NSPlayer.sort_playlist(NSPlayer.shuffle);
		this.playlists[cid].playlist=NSPlayer.tracks;
		this.present_playlist(cid);
		this.selectedplist=pl;
	},
	switch_track:function(id)
	{
		if(NSPlayer.plistname!=this.selectedplist)
			this.switch_playlist(this.selectedplist);
		NSPlayer.play(id);
	},
	set_highlighted:function(pl,t)
	{
		if(pl!=this.selectedplist)return;
		let cid=0;
		for(;cid<this.playlists.length;++cid)
		if(this.playlists[cid].plistname==pl)break;
		const clist=this.playlists[cid].playlist;
		for(let i=0;i<clist.length;++i)
		if(clist[i].title==t)
		this.playlist.childNodes[i].firstChild.classList.add('highlighted');
		else
		this.playlist.childNodes[i].firstChild.classList.remove('highlighted');
	},
	shuffle_switch:function(shuffle)
	{
		for(let i=0;i<this.playlists.length;++i)
		{
			NSPlayer.sort_playlist(shuffle,this.playlists[i].playlist);
			if(i==this.selectedplist)this.present_playlist(i);
		}
		NSPlayer.sort_playlist(shuffle);
	},
	togglePlist:function()
	{
		NSUI.plistshown=!NSUI.plistshown;
		NSUI.swplaylist.innerHTML=`[${NSUI.plistshown?'Hide':'Show'} Playlist]`;
		NSUI.playlist.style.opacity=NSUI.plistshown?'1':'0';
		NSUI.playlist.style.pointerEvents=NSUI.plistshown?'auto':'none';
	},
	formatTime:function(t)
	{
		if(isNaN(t))return '-:--';
		let m=Math.floor(t/60),s=Math.round(t-Math.floor(t/60)*60);
		if(s<10)return `${m}:0${s}`;
		else if(s==60)return `${m+1}:00`;
		else return `${m}:${s}`;
	},
	bufferedUpdate:function()
	{
		let r=0;
		for(let i=0;i<NSUI.audio.buffered.length;++i)
		r=r<NSUI.audio.buffered.end(i)?NSUI.audio.buffered.end(i):r;
		document.getElementById('cbuff').style.width=r/NSUI.audio.duration*100+'%';
	},
	timeUpdate:function()
	{
		document.getElementById('timenow').innerHTML=NSUI.formatTime(NSUI.audio.currentTime);
		document.getElementById('timeleft').innerHTML=`-${NSUI.formatTime(NSUI.audio.duration-NSUI.audio.currentTime)}`;
		document.getElementById('ctime').style.width=NSUI.audio.currentTime/NSUI.audio.duration*100+'%';
		NSUI.bufferedUpdate();
	}
};

NSAudio={
	audioctx:null,
	srcnode:null,
	anlznode:null,
	audioInit:function()
	{
		window.AudioContext=window.AudioContext||window.webkitAudioContext||window.mozAudioContext||window.msAudioContext;
		if(!window.AudioContext)alert('This page requires Web Audio API to work...');
		this.audioctx=new AudioContext;
		this.anlznode=this.audioctx.createAnalyser();
		this.anlznode.fftSize=2*NSVisualization.nbins;
		NSUI.audio.volume=1;
	},
	connect:function()
	{
		this.srcnode=this.audioctx.createMediaElementSource(NSUI.audio);
		this.srcnode.connect(this.anlznode);
		this.anlznode.connect(this.audioctx.destination);
	},
};

NSVisualization={
	nbins:1024,
	spectgrmw:1200,
	spectrw:0.7,
	spectrm:0.08125,
	caps:[],
	cctx:null,
	spectgrmp:0,
	init:function()
	{
		window.requestAnimationFrame=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame;
		this.cctx=NSUI.canvas.getContext('2d');
		this.spectrw=NSUI.canvas.width/this.nbins;
		this.spectrm=this.spectrw/10;this.spectrw*=0.9;
		for(let i=0;i<this.nbins;++i)this.caps[i]=0;
	},
	update_spectrum:function()
	{
		const cctx=this.cctx,canvas=NSUI.canvas;
		cctx.clearRect(0,0,canvas.width,canvas.height);
		try{
			let freqdomv=new Uint8Array(NSAudio.anlznode.frequencyBinCount);
			NSAudio.anlznode.getByteFrequencyData(freqdomv);
			for(let i=0;i<this.nbins;++i)
			{
				cctx.fillStyle='hsl('+i*360.0/this.nbins+',100%,50%)';
				cctx.fillRect(i*(this.spectrw+this.spectrm),canvas.height-(canvas.height*freqdomv[i]/255.),this.spectrw,canvas.height*freqdomv[i]/255.);
				if(this.caps[i]<freqdomv[i])this.caps[i]=freqdomv[i];else if(this.caps[i]>0)--this.caps[i];
				cctx.fillStyle='hsl('+i*360.0/this.nbins+',100%,25%)';
				cctx.fillRect(i*(this.spectrw+this.spectrm),canvas.height-(canvas.height*this.caps[i]/255.)-1,this.spectrw,1);
			}
		}catch(e){
			for(let i=0;i<this.nbins;++i)
			{
				if(this.caps[i]>0)--this.caps[i];
				cctx.fillStyle='hsl('+i*360.0/this.nbins+',100%,25%)';
				cctx.fillRect(i*(this.spectrw+this.spectrm),canvas.height-this.caps[i]-1,this.spectrw,1);
			}
		}
	},
	update_scope:function()
	{
		const cctx=this.cctx,canvas=NSUI.canvas;
		cctx.clearRect(0,0,canvas.width,canvas.height);
		try{
			let timedomv=new Uint8Array(NSAudio.anlznode.frequencyBinCount);
			NSAudio.anlznode.getByteTimeDomainData(timedomv);
			cctx.lineWidth=window.devicePixelRatio;
			cctx.strokeStyle='#000';
			if(athm)cctx.strokeStyle='#FFF';
			cctx.beginPath();
			for(let i=0,x=0;i<this.nbins;++i)
			{
				if(i==0)cctx.moveTo(x,timedomv[i]/128.*canvas.height/2);
				else cctx.lineTo(x,timedomv[i]/128.*canvas.height/2);
				x+=canvas.width*1./this.nbins;
			}
			cctx.stroke();
		}catch(e){
			cctx.beginPath();
			cctx.moveTo(0,canvas.height/2);
			cctx.lineTo(canvas.width,canvas.height/2);
			cctx.stroke();
		}
	},
	update_spectrogram:function()
	{
		const cctx=this.cctx,canvas=NSUI.canvas;
		try{
			let freqdomv=new Uint8Array(NSAudio.anlznode.frequencyBinCount);
			NSAudio.anlznode.getByteFrequencyData(freqdomv);
			cctx.clearRect(this.spectgrmp/this.spectgrmw*canvas.width,0,canvas.width/this.spectgrmw,canvas.height);
			for(let i=0;i<this.nbins;++i)
			{
				let color=(athm?'rgba(255,255,255,':'rgba(0,0,0,')+freqdomv[i]/256.+')';
				cctx.fillStyle=color;
				cctx.fillRect(this.spectgrmp/this.spectgrmw*canvas.width,(this.nbins-i)/this.nbins*canvas.height,canvas.width/this.spectgrmw,canvas.height/this.nbins);
			}
			if(!NSUI.audio.paused)++this.spectgrmp;
			if(this.spectgrmp>=this.spectgrmw)this.spectgrmp=0;
		}catch(e){
			this.spectgrmp=0;
			cctx.clearRect(0,0,canvas.width,canvas.height);
		}
	},
	updateVisualization:function()
	{
		switch(NSUI.vissel.value)
		{
			case 'spectrum':
				NSVisualization.update_spectrum();
			break;
			case 'scope':
				NSVisualization.update_scope();
			break;
			case 'spectrogram':
				NSVisualization.update_spectrogram();
			break;
			case 'inkfountain':
				NSInk.update();
			break;
		}
		if(NSUI.vissel.value!='none')
		requestAnimationFrame(NSVisualization.updateVisualization);
	}
};

NSInk={
	ic1:['#FF3333','#FF8800','#FFFF00','#CCFF00','#33CCFF','#0000FF','#9966FF'],
	ic2:['rgba(204,51,51,0.6)','rgba(187,85,0,0.6)','rgba(255,204,0,0.6)','rgba(153,204,0,0.6)','rgba(51,153,255,0.6)','rgba(0,0,102,0.6)','rgba(153,51,204,0.6)'],
	inks:[],
	inkimg:[],
	ifcaps:[],
	nbinsif:128,
	inkPrepare:function()
	{
		for(let i=0;i<7;++i)
		{
			this.inkimg[i]=document.createElement('canvas');
			this.inkimg[i].width=this.inkimg[i].height=10*window.devicePixelRatio;
			const cctx=this.inkimg[i].getContext('2d');
			cctx.fillStyle=this.ic1[i];
			cctx.beginPath();
			cctx.arc(5*window.devicePixelRatio,5*window.devicePixelRatio,3*window.devicePixelRatio,0,2*Math.PI);
			cctx.fill();
			cctx.fillStyle=this.ic2[i];
			cctx.beginPath();
			cctx.arc(5*window.devicePixelRatio,5*window.devicePixelRatio,5*window.devicePixelRatio,0,2*Math.PI);
			cctx.fill();
		}
		for(let i=0;i<NSVisualization.nbins;++i)this.ifcaps[i]=0;
		this.nbinsif=NSVisualization.nbins/8;
	},
	createInk:function(_vx,_vy,_c1,_c2)
	{
		let f=false;
		for(let i=0;i<this.inks.length;++i)
		{
			if(!this.inks[i].active)
			{this.inks[i]=new Ink(_vx,_vy,_c1,_c2);f=true;break;}
		}
		if(!f&&this.inks.length<512)this.inks.push(new Ink(_vx,_vy,_c1,_c2));
	},
	frms:0,lastfpsupd:null,fps:0,
	update:function()
	{
		const canvas=NSUI.canvas;
		const cctx=canvas.getContext('2d'),ts=Date.now();
		cctx.clearRect(0,0,canvas.width,canvas.height);
		++this.frms;if(Date.now()-this.lastfpsupd>500)
		{
			if(this.lastfpsupd)
			this.fps=1000*this.frms/(Date.now()-this.lastfpsupd),this.frms=0;
			this.lastfpsupd=Date.now();
		}
		try{
			let freqarr=new Uint8Array(NSAudio.anlznode.frequencyBinCount);
			NSAudio.anlznode.getByteFrequencyData(freqarr);
			for(let i=0;i<this.nbinsif;++i)
			{
				let r=0;
				for(let j=0;j<8;++j)r+=freqarr[i*8+j];
				r/=8.;
				if(r-this.ifcaps[i]>7)
				{
					let color=Math.floor(i*8.0/this.nbinsif),rad=Math.random()*Math.PI*2;
					let ndrops=(128-i)/128.*3+1;ndrops*=(r/128.);ndrops=Math.floor(ndrops);if(ndrops<1)ndrops=1;
					for(let k=0;k<ndrops;++k)
					{
						this.createInk(((r-this.ifcaps[i]-7)/32*24+12)*Math.cos(rad),((r-this.ifcaps[i]-7)/32*24+12)*Math.sin(rad),color);
						rad=Math.random()*Math.PI*2;
					}
				}
				if(r>this.ifcaps[i])this.ifcaps[i]=r;else this.ifcaps[i]-=2;
			}
		}catch(e){
			cctx.clearRect(0,0,canvas.width,canvas.height);
		}
		const tu=Date.now();
		let aa=0;
		for(let i=0;i<this.inks.length;++i)
		if(this.inks[i].active){this.inks[i].update();this.inks[i].draw(cctx);++aa;}
		cctx.fillStyle='#000';
		cctx.font="1em 'CMU Typewriter Text w', 'CMU Typewriter Text', 'TeX Gyre Cursor', 'FreeMono', 'Courier New', Courier, monospace";
		cctx.fillText('Active droplets '+aa+', '+this.fps.toFixed(2)+' FPS',0,10);
		cctx.fillText('Update time '+(tu-ts)+'ms, render time '+(Date.now()-tu)+'ms',0,24);
	}
};

function init()
{
	if(!window.devicePixelRatio)window.devicePixelRatio=1;
	NSUI.setup_ui();
	NSUI.load_playlists()
	.then(()=>{
		sh.elem('overlaytext').innerHTML+="Done!<br>Click or tap anywhere to start."
		sh.elem('overlay').onclick=function(){
			NSAudio.audioInit();NSAudio.connect();
			if(window.location.hash.length)
			{
				let p=window.location.hash.substr(1).split('/');
				for(let i=0;i<p.length;++i)p[i]=decodeURIComponent(p[i]);
				NSUI.switch_playlist(p[0],true);
				let id=0;
				for(;id<NSPlayer.tracks.length&&NSPlayer.tracks[id].title!=p[1];++id);
				if(id<NSPlayer.tracks.length)NSUI.switch_track(id);
			}
			sh.elem('overlay').style.display='none';
		}
	});
	NSVisualization.init();NSInk.inkPrepare();
	NSUI.audio.ontimeupdate=NSUI.timeUpdate;
	NSUI.audio.onended=NSPlayer.next.bind(NSPlayer);
	switch(sh.getcookie('playershuffle'))
	{
		case '1':NSPlayer.shuffle=1;break;
		default:
		case '0':NSPlayer.shuffle=0;break;
	}
	switch(sh.getcookie('playerrepeat'))
	{
		case '1':
			NSPlayer.repeat=1;
			NSUI.audio.loop=true;
		break;
		default:
		case '0':
			NSPlayer.repeat=0;
			NSUI.audio.loop=false;
		break;
	}
	NSUI.ishuffle.style.backgroundPosition=`${NSPlayer.shuffle?NSUI.bshonrect:NSUI.bsoffrect}`;
	NSUI.irepeat.style.backgroundPosition=`${NSPlayer.repeat?NSUI.bronerect:NSUI.brallrect}`;
	if(sh.getcookie('playervisualization').length!==0)
	NSUI.vissel.value=sh.getcookie('playervisualization');
	requestAnimationFrame(NSVisualization.updateVisualization);
}