summaryrefslogtreecommitdiff
path: root/libs/music/player.d/pcm.js
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2019-02-14 22:45:27 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2019-02-14 22:45:27 +0800
commit98c5434a32cc546937550726aebf3cbc1b1b6b9e (patch)
tree61cceb107483b8246c2dacd88c10045b1683a3d0 /libs/music/player.d/pcm.js
parent2702467eda7328115d827656e3804e063fe2341c (diff)
downloadweb-98c5434a32cc546937550726aebf3cbc1b1b6b9e.tar.xz
Pineapple Cloud Music.
Diffstat (limited to 'libs/music/player.d/pcm.js')
-rw-r--r--libs/music/player.d/pcm.js365
1 files changed, 365 insertions, 0 deletions
diff --git a/libs/music/player.d/pcm.js b/libs/music/player.d/pcm.js
new file mode 100644
index 0000000..575b7f7
--- /dev/null
+++ b/libs/music/player.d/pcm.js
@@ -0,0 +1,365 @@
+; // Private Cloud Music - player.js
+; // Licence: WTFPL
+; // BLumia - 2016/11/11
+; // szO Chris && 2jjy && jxpxxzj Orz
+; // ↑ Moe ↑ Moe ↑ Moe
+; // Modified to use on chrisoft.org by Chris Xiong
+; // szO BLumia Orz
+; // ↑ Moe
+// formatTime,getCookie by Chrissssss
+function formatTime(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}`;
+}
+function getCookie(key) {
+ if (!navigator.cookieEnabled) return "";
+ return document.cookie.replace(new RegExp('(?:(?:^|.*;\\s*)'+key+'\\s*\\=\\s*([^;]*).*$)|^.*$'),'$1');
+}
+function setCookie(cookieName, cookieValue, maxAge = 0) {
+ if (!navigator.cookieEnabled) return;
+ var cookieStr = cookieName + "=" + cookieValue;
+ if (maxAge > 0) cookieStr += ";max-age=" + maxAge;
+ document.cookie = cookieStr;
+}
+
+const PCMAPI_URL='/libs/music/player.d/cgi-bin/pcm.cgi';
+const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/';
+
+(function() {
+ var Helper = function() {
+ this.el = null;
+ this.entry = function(selector) {
+ if (typeof selector == 'string') {
+ if (selector[0] == '<') {
+ var singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
+ if (singleTagRE.test(selector)) this.el = document.createElement(RegExp.$1);
+ } else {
+ this.el = document.getElementById(selector);
+ }
+ }
+ else this.el = selector;
+ return this;
+ }
+ }
+ Helper.prototype = {
+ css: function(property, value) {
+ if(this.el) this.el.style.cssText += ';' + property + ":" + value;
+ return this;
+ },
+ attr: function(property, value) {
+ if(this.el) this.el.setAttribute(property, value);
+ return this;
+ },
+ append: function(node) {
+ if(this.el) this.el.appendChild(node);
+ return this;
+ },
+ text: function(content) {
+ if(this.el) this.el.textContent = content;
+ return this;
+ },
+ click: function(handler) {
+ if(!this.el) return this;
+ if (typeof(handler) == "function") this.el.onclick = handler;
+ else this.el.click();
+ return this;
+ },
+ innerHTML: function(text) {
+ if(this.el) this.el.innerHTML = text;
+ return this;
+ }
+ }
+ var H = function(selector) {
+ var f = new Helper();
+ return f.entry(selector);
+ }
+ var Player = {
+ path: null, // sample: 'Test/'
+ data: null,
+ audio: document.getElementsByTagName('audio')[0],
+ currentIndex: -1,
+ loop: 0,
+ order: 0,
+ playlist: H("playlist").el,
+ folderlist: H("folderlist").el,
+ nowPlaying: H("nowPlaying").el,
+
+ updateMetadata: function() {
+ if ('mediaSession' in navigator) {
+ window.navigator.mediaSession.metadata = new MediaMetadata({
+ title: nowPlaying.innerHTML,
+ album: decodeURIComponent(this.path)
+ });
+ }
+ },
+
+ playAtIndex: function(i) {
+ // FIXME: trigger this when audio doesn't finished load will cause play promise error.
+ this.audio.pause();
+ this.currentIndex = i;
+ this.audio.src = AUDIO_URL + this.data[i].fileName + '.ogg';
+ this.audio.load();
+ this.audio.play();
+ window.history.replaceState("","Useless Title","#/"+this.path+this.data[i].fileName+"/"); // title seems be fucked.
+ H(this.nowPlaying).innerHTML(decodeURIComponent(this.data[i].fileName));
+ },
+
+ freshFolderlist: function(callback) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("POST", PCMAPI_URL, true);
+ xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ var that = this;
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState != 4 || xhr.status != 200) return;
+ var data = JSON.parse(xhr.responseText);
+ if (data.status != 200) {
+ console.error("Fetch error. Reason: " + data.message + " Url: ./api.php");
+ return;
+ }
+ data.result.data.subFolderList.forEach(function(item, i) {
+ var decodedFolderName = decodeURIComponent(item);
+ if (that.path == null) that.path = item + '/';
+ // attr aim data as uriencoded path.
+ H(that.folderlist).append(
+ H("<a>").attr('aim', item).append(
+ H("<li>").text(decodedFolderName + '/').el
+ ).el
+ );
+ });
+ };
+ xhr.onerror = function() {
+ console.error("Ajax load folders failed. Status: " + xhr.status + " Url: ./api.php");
+ };
+ xhr.onloadend = function() {
+ var nodeList = document.querySelectorAll('#folderlist a');
+ for(var i = 0; i < nodeList.length; i++) {
+ var el = nodeList[i];
+ el.onclick = function() {
+ that.path = this.getAttribute('aim') + '/';
+ that.fetchData();
+ };
+ }
+ typeof callback === 'function' && callback();
+ }
+ xhr.send("do=getfilelist");
+ },
+
+ fetchData: function() {
+ var that = this;
+ var xhr = new XMLHttpRequest();
+ xhr.open("POST", PCMAPI_URL, true);
+ xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState != 4 || xhr.status != 200) return;
+ var data = JSON.parse(xhr.responseText);
+ if (data.status != 200) {
+ console.error("Fetch error. Reason: " + data.message + " Url: ./api.php");
+ return;
+ }
+ that.data = data.result.data.musicList;
+ that.freshPlaylist();
+ that.freshSubFolderList(data.result.data.subFolderList);
+ };
+ xhr.onerror = function() {
+ console.error("Ajax load playlist failed. Status: " + xhr.status + " Url: ./api.php");
+ that.data = [];
+ };
+ xhr.send("do=getfilelist&folder="+that.path);
+ },
+
+ freshPlaylist : function() {
+ var that = this;
+ var data = this.data;
+ var songTitle = '';
+ this.playlist.innerHTML = '';
+ data.forEach(function(item, i) {
+ songTitle = decodeURIComponent(item.fileName);
+ H(that.playlist).append(
+ H("<a>").attr('index', i).append(
+ H("<li>").text(songTitle).el
+ ).el
+ );
+ });
+ // everytime after update playlist dom, do this.
+ var nodeList = document.querySelectorAll('#playlist a');
+ for(var i = 0; i < nodeList.length; i++) {
+ var el = nodeList[i];
+ el.onclick = function() {
+ that.playAtIndex(this.getAttribute('index'));
+ };
+ }
+ },
+
+ freshSubFolderList : function(list) {
+ var that = this;
+ H("subfolderlist").innerHTML("");
+ list.forEach(function(item, i) {
+ var decodedFolderName = decodeURIComponent(item);
+ // attr aim data as uriencoded path.
+ H("subfolderlist").append(
+ H("<a>").attr('aim', item).append(
+ H("<li>").text(decodedFolderName + '/').el
+ ).el
+ );
+ });
+ var nodeList = document.querySelectorAll('#subfolderlist a');
+ for(var i = 0; i < nodeList.length; i++) {
+ var el = nodeList[i];
+ el.onclick = function() {
+ that.path = this.getAttribute('aim') + '/';
+ that.fetchData();
+ };
+ }
+ },
+
+ urlMatch : function() {
+ var isUrlMatched = false;
+ // Match folder name and song title.
+ var re = new RegExp("[#][/](.*[/])(.*)[/]$");
+ var urlMatch = re.exec(location.href);
+ if (urlMatch != null) {
+ isUrlMatched = true;
+ this.path = urlMatch[1];
+ this.audio.src = AUDIO_URL + urlMatch[2] + '.ogg';
+ this.audio.play();
+ H(this.nowPlaying).innerHTML(decodeURIComponent(urlMatch[2]));
+ }
+ // Only match folder name.
+ if (!isUrlMatched) {
+ re = new RegExp("[#][/](.*[/])");
+ urlMatch = re.exec(location.href);
+ if (urlMatch != null) {
+ isUrlMatched = true;
+ this.path = urlMatch[1];
+ }
+ }
+ },
+
+ applyLoop : function() {
+ if (this.loop == 1) {
+ this.audio.loop = true;
+ H("btn-loop").innerHTML("Loop: √");
+ } else {
+ this.audio.loop = false;
+ H("btn-loop").innerHTML("Loop: ×");
+ }
+ },
+
+ applyOrder : function() {
+ if (this.order == 1) {
+ this.audio.onended = function() {
+ if (this.loop == 0) {
+ H("btn-next").click();
+ }
+ };
+ H("btn-order").innerHTML("Order: √");
+ } else {
+ this.audio.onended = undefined;
+ H("btn-order").innerHTML("Order: ×");
+ }
+ },
+
+ init : function() {
+ var that = this;
+ this.freshFolderlist(function() {
+ that.urlMatch();
+ that.fetchData();
+ });
+ this.loop = getCookie("pcm-loop") == "1" ? 1 : 0;
+ this.order = getCookie("pcm-order") == "1" ? 1 : 0;
+ this.applyLoop();
+ this.applyOrder();
+ },
+
+ ready : function() {
+ var that = this;
+
+ this.audio.ontimeupdate = function() {
+ H("curTime").innerHTML(formatTime(Player.audio.currentTime));
+ H("totalTime").innerHTML(formatTime(Player.audio.duration));
+ H("timebar").css("width", Player.audio.currentTime / Player.audio.duration*100+"%");
+ var r = 0;
+ for(var i=0; i<Player.audio.buffered.length; ++i)
+ r = r<Player.audio.buffered.end(i) ? Player.audio.buffered.end(i) : r;
+ H("bufferbar").css("width", r / Player.audio.duration*100+"%");
+ };
+
+ this.audio.onpause = function() {
+ H("btn-play").innerHTML("Play");
+ }
+
+ this.audio.onplay = function() {
+ H("btn-play").innerHTML("Pause");
+ that.updateMetadata();
+ }
+
+ H("progressbar").click(function(e) {
+ var sr=this.getBoundingClientRect();
+ var p=(e.clientX-sr.left)/sr.width;
+ that.audio.currentTime=that.audio.duration*p;
+ });
+
+ var nodeList = document.getElementsByTagName('button');
+ for(var i = 0; i < nodeList.length; i++) {
+ var el = nodeList[i];
+ el.onclick = function() {
+ if(that.data[that.currentIndex]) H(that.nowPlaying).innerHTML(decodeURIComponent(that.data[that.currentIndex].fileName));
+ };
+ }
+
+ H("btn-play").click(function() {
+ if(that.audio.paused) {
+ that.audio.play();
+ } else {
+ that.audio.pause();
+ }
+ if (that.currentIndex == -1 && that.audio.readyState == 0) {
+ H("btn-next").click();
+ }
+ });
+
+ H("btn-next").click(function() {
+ if (that.currentIndex == -1) {
+ that.playAtIndex(0);
+ } else if (that.currentIndex == (that.data.length - 1)) {
+ that.playAtIndex(0);
+ } else {
+ that.playAtIndex(Number(that.currentIndex) + 1);
+ }
+ });
+
+ H("btn-prev").click(function() {
+ if (that.currentIndex == -1) {
+ that.playAtIndex(0);
+ } else if (that.currentIndex == 0) {
+ that.playAtIndex(that.data.length - 1);
+ } else {
+ that.playAtIndex(Number(that.currentIndex) - 1);
+ }
+ });
+
+ H("btn-loop").click(function() {
+ that.loop = 1 - that.loop;
+ that.applyLoop();
+ setCookie("pcm-loop", that.loop, 157680000);
+ });
+
+ H("btn-order").click(function() {
+ that.order = 1 - that.order;
+ that.applyOrder();
+ setCookie("pcm-order", that.order, 157680000);
+ });
+
+ if ('mediaSession' in navigator) {
+ navigator.mediaSession.setActionHandler('previoustrack', function() { H("btn-prev").click(); });
+ navigator.mediaSession.setActionHandler('nexttrack', function() { H("btn-next").click(); });
+ }
+ }
+ };
+
+ Player.init();
+ Player.ready();
+}());