diff options
Diffstat (limited to 'libs/music/player.d/pcm.js')
-rw-r--r-- | libs/music/player.d/pcm.js | 200 |
1 files changed, 139 insertions, 61 deletions
diff --git a/libs/music/player.d/pcm.js b/libs/music/player.d/pcm.js index 0efe849..6e2022c 100644 --- a/libs/music/player.d/pcm.js +++ b/libs/music/player.d/pcm.js @@ -1,11 +1,8 @@ -; // Private Cloud Music - player.js -; // Licence: WTFPL -; // BLumia - 2016/11/11 +; // SPDX-FileCopyrightText: 2021 Gary Wang <toblumia@outlook.com> +; // SPDX-License-Identifier: MIT ; // 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 '--:--'; @@ -24,9 +21,9 @@ function setCookie(cookieName, cookieValue, maxAge = 0) { 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 displayName(item) { + return item.displayName ? item.displayName : decodeURIComponent(item.fileName); +} (function() { var Helper = function() { @@ -49,8 +46,16 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; if(this.el) this.el.style.cssText += ';' + property + ":" + value; return this; }, - attr: function(property, value) { - if(this.el) this.el.setAttribute(property, value); + attr: function(attr, value) { + if(this.el) this.el.setAttribute(attr, value); + return this; + }, + removeData: function(attr) { + if(this.el) this.el.removeAttribute("data-" + attr); + return this; + }, + data: function(attr, value) { + if(this.el) this.el.setAttribute("data-" + attr, JSON.stringify(value)); return this; }, append: function(node) { @@ -76,9 +81,17 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; var f = new Helper(); return f.entry(selector); } + function TrickOrTreat(promiseRsp) { + if (!promiseRsp.ok) { + throw Error(promiseRsp.statusText); // to cancel the Promise chain... + } + return promiseRsp.json(); + } var Player = { + mediaRootUrl: '', path: null, // sample: 'Test/' data: null, + preferredFormats: undefined, // sample: 'mp3,ogg' audio: document.getElementsByTagName('audio')[0], currentIndex: -1, loop: 0, @@ -86,6 +99,14 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; playlist: H("playlist").el, folderlist: H("folderlist").el, nowPlaying: H("nowPlaying").el, + apiUrl: "/libs/music/player.d/cgi-bin/pcm.cgi", + _currentSongInfoJson: undefined, + _chapterNeedUpdate: true, + + setInfoJson: (jsonData) => { + this._currentSongInfoJson = jsonData; + this._chapterNeedUpdate = true; + }, updateMetadata: function() { if ('mediaSession' in navigator) { @@ -96,25 +117,90 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; } }, + applyChapterData: () => { + if (!this._chapterNeedUpdate) return; + if (Player.audio.duration) { + if (this._currentSongInfoJson) { + let duration = Player.audio.duration; + let progressChapterData = []; + this._currentSongInfoJson.chapters.forEach((chapter) => { + let chapterObj = {}; + chapterObj.start = chapter.start_time / duration * 100; + chapterObj.title = chapter.title; + progressChapterData.push(chapterObj); + }); + H("progress-bar").data("chapters", progressChapterData); + } else { + H("progress-bar").removeData("chapters"); + } + this._chapterNeedUpdate = false; + } + }, + + fetchAdditionalInfo: (infoJsonfileUrl) => { + fetch(infoJsonfileUrl).then(TrickOrTreat).then((data) => { + Player.setInfoJson(data); + }); + }, + playAtIndex: function(i) { + let fullPath = this.path + this.data[i].fileName; + let srcUrl = this.data[i].url ? this.data[i].url : (this.mediaRootUrl + fullPath); // 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; + this.audio.src = srcUrl; 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)); + window.history.replaceState("","Useless Title","#/" + fullPath + "/"); // title seems be fucked. + H(this.nowPlaying).innerHTML(displayName(this.data[i])); + + if (this.data[i].additionalInfo) { + let infoJsonFile = (fullPath.substring(0, fullPath.lastIndexOf('.')) || fullPath) + ".info.json"; + this.fetchAdditionalInfo(infoJsonFile); + } else { + this.setInfoJson(undefined); + } + }, + + fetchServerInfo: function(callback) { + var that = this; + fetch(this.apiUrl, { + method: 'POST', + body: new URLSearchParams({ + 'do': 'getserverinfo' + }) + }).then(TrickOrTreat).then((data) => { + if (data.result.mediaRootUrl && data.result.mediaRootUrl.length > 1) { + that.mediaRootUrl = data.result.mediaRootUrl; + if (!that.mediaRootUrl.endsWith('/')) { + that.mediaRootUrl = that.mediaRootUrl + '/'; + } + } + if (data.result.serverName) { + let el = H("server-name"); + if (el) { + el.text(data.result.serverName); + } + document.title = data.result.serverName; + } + + typeof callback === 'function' && callback(); + }) }, 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); + requestBody = { + 'do': 'getfilelist', + }; + if (that.preferredFormats) { + requestBody['preferredFormats'] = that.preferredFormats; + } + fetch(this.apiUrl, { + method: 'POST', + body: new URLSearchParams(requestBody) + }).then(TrickOrTreat).then((data) => { if (data.status != 200) { console.error("Fetch error. Reason: " + data.message + " Url: ./api.php"); return; @@ -129,11 +215,7 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; ).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]; @@ -142,41 +224,34 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; 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; - } + + fetch(this.apiUrl, { + method: 'POST', + body: new URLSearchParams({ + 'do': 'getfilelist', + 'folder': that.path + }) + }).then(TrickOrTreat).then((data) => { 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); + songTitle = displayName(item); H(that.playlist).append( H("<a>").attr('index', i).append( H("<li>").text(songTitle).el @@ -218,13 +293,13 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; urlMatch : function() { var isUrlMatched = false; // Match folder name and song title. - var re = new RegExp("[#][/](.*[/])(.*)[/]$"); + var re = new RegExp("[#][/](.*[/])(.*.[a-zA-z0-9]{1,3})[/]"); 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(); + this.audio.src = (this.path + urlMatch[2]); + this.audio.play().catch((reason) => { console.log(reason); }); H(this.nowPlaying).innerHTML(decodeURIComponent(urlMatch[2])); } // Only match folder name. @@ -261,30 +336,33 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; H("btn-order").innerHTML("Order: ×"); } }, - + init : function() { var that = this; - this.freshFolderlist(function() { - that.urlMatch(); - that.fetchData(); + this.fetchServerInfo(function() { + that.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() { + this.audio.ontimeupdate = () => { + this.applyChapterData(); 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+"%"); + H("progress-bar").attr("value", Player.audio.currentTime / Player.audio.duration*100); var r = 0; - for(var i=0; i<Player.audio.buffered.length; ++i) + 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+"%"); + H("progress-bar").attr("buffer", r / Player.audio.duration*100); }; this.audio.onpause = function() { @@ -296,7 +374,7 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; that.updateMetadata(); } - H("progressbar").click(function(e) { + H("progress-bar").click(function(e) { var sr=this.getBoundingClientRect(); var p=(e.clientX-sr.left)/sr.width; that.audio.currentTime=that.audio.duration*p; @@ -306,7 +384,7 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; 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)); + if(that.data[that.currentIndex]) H(that.nowPlaying).innerHTML(displayName(that.data[that.currentIndex])); }; } @@ -346,7 +424,7 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; that.applyLoop(); setCookie("pcm-loop", that.loop, 157680000); }); - + H("btn-order").click(function() { that.order = 1 - that.order; that.applyOrder(); @@ -359,7 +437,7 @@ const AUDIO_URL='//filestorage.chrisoft.org/music/ogg/'; } } }; - + Player.init(); Player.ready(); }()); |