From b8d4cf6fa1541700cc763bec99f4e742322445b0 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Fri, 5 Oct 2018 16:28:44 +0300 Subject: [PATCH] Custom Audio Player --- dbpopulate.js | 3 +- public/icon/pause.svg | 10 ++ public/icon/play-next.svg | 9 ++ public/icon/play-prev.svg | 10 ++ public/icon/play.svg | 3 + public/icon/volume-low.svg | 7 ++ public/icon/volume-off.svg | 40 ++++++++ public/icon/volume.svg | 9 ++ public/index.css | 78 ++++++++++++++++ public/index.html | 21 ++++- public/index.js | 13 ++- public/player.js | 183 +++++++++++++++++++++++++++++++++++++ 12 files changed, 379 insertions(+), 7 deletions(-) create mode 100644 public/icon/pause.svg create mode 100644 public/icon/play-next.svg create mode 100644 public/icon/play-prev.svg create mode 100644 public/icon/play.svg create mode 100644 public/icon/volume-low.svg create mode 100644 public/icon/volume-off.svg create mode 100644 public/icon/volume.svg create mode 100644 public/player.js diff --git a/dbpopulate.js b/dbpopulate.js index 519b156..df47dfb 100755 --- a/dbpopulate.js +++ b/dbpopulate.js @@ -34,8 +34,7 @@ function filewalker (dir, done) { // If directory, execute a recursive call if (stat && stat.isDirectory()) { - // Add directory to array [comment if you need to remove the directories from the array] - results.push(file) + //results.push(file) filewalker(file, function (err, res) { if (err) return done(err) diff --git a/public/icon/pause.svg b/public/icon/pause.svg new file mode 100644 index 0000000..7b8f99c --- /dev/null +++ b/public/icon/pause.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/icon/play-next.svg b/public/icon/play-next.svg new file mode 100644 index 0000000..e2d3601 --- /dev/null +++ b/public/icon/play-next.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icon/play-prev.svg b/public/icon/play-prev.svg new file mode 100644 index 0000000..5d4b441 --- /dev/null +++ b/public/icon/play-prev.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/icon/play.svg b/public/icon/play.svg new file mode 100644 index 0000000..5cecb82 --- /dev/null +++ b/public/icon/play.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icon/volume-low.svg b/public/icon/volume-low.svg new file mode 100644 index 0000000..4923380 --- /dev/null +++ b/public/icon/volume-low.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/icon/volume-off.svg b/public/icon/volume-off.svg new file mode 100644 index 0000000..ae0022c --- /dev/null +++ b/public/icon/volume-off.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icon/volume.svg b/public/icon/volume.svg new file mode 100644 index 0000000..0f31a8d --- /dev/null +++ b/public/icon/volume.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/index.css b/public/index.css index c3e65c4..ff8da8b 100644 --- a/public/index.css +++ b/public/index.css @@ -73,9 +73,84 @@ tr:nth-child(even) { background-color: #112635; cursor: pointer; } +.paging.btn.inner { + background-color: #102331; +} .paging.bg { background-color: #0e202c; } +.player-controls { + display: flex; + flex-direction: row; + height: 40px; + background-color: #0c2233; +} +.player-controls .grow { + flex-grow: 1; +} +.player-controls span { + display: block; +} +.player-controls .icon { + width: 30px; + height: 30px; + cursor: pointer; + background-size: 60%; + background-repeat: no-repeat; + background-position: center; + margin: 5px; + padding: 0 5px; +} +.player-controls .timestamp { + margin-top: 0.9em; + font-size: 0.8em; + padding: 0 5px; + min-width: 8em; + text-align: center; +} +.play-btn { + background-image: url('icon/play.svg'); +} +.pause-btn { + background-image: url('icon/pause.svg'); +} +.mute-btn { + background-image: url('icon/volume.svg'); +} +.mute1-btn { + background-image: url('icon/volume-low.svg'); +} +.muted-btn { + background-image: url('icon/volume-off.svg'); +} +.next-btn { + background-image: url('icon/play-next.svg'); +} +.prev-btn { + background-image: url('icon/play-prev.svg'); +} +.volume { + display: flex; + flex-direction: row; +} +.volume .volume-bar { + width: 5em; +} +.volume .volume-bar .seek-inner { + width: 100%; +} +.seek-container { + height: 8px; + width: 100%; + margin: 16px 5px 0 5px; + background-color: #00131b; + cursor: pointer; +} +.seek-inner { + height: 100%; + width: 0%; + background-color: #00b7ff; +} @media only screen and (max-width: 600px) { tr td:nth-child(1), th:nth-child(1) { display: none; @@ -100,4 +175,7 @@ tr:nth-child(even) { .allowance { margin-bottom: 100px !important; } + .volume .volume-bar { + display: none; + } } diff --git a/public/index.html b/public/index.html index 9cad845..be6cdbd 100644 --- a/public/index.html +++ b/public/index.html @@ -25,19 +25,32 @@
<<
-
<
-
0 / 0
-
>
+
<
+
0 / 0
+
>
>>
Nothing playing
- + + + + 0:00 / 0:00 +
+
+
+
+ +
+
+
+
+ diff --git a/public/index.js b/public/index.js index accd7f9..8c2a624 100644 --- a/public/index.js +++ b/public/index.js @@ -3,6 +3,9 @@ var input = document.querySelector('#search') var audio = document.querySelector('#player') var playing = document.querySelector('#playing') +var next = document.querySelector('#player-next') +var prev = document.querySelector('#player-prev') + var tableHead = ' \ # \ Track \ @@ -60,7 +63,6 @@ function httpGet (url, callback) { } function shortTitle (title, artist) { - title = title.replace(/&/g, '&') if (!artist) return title return artist + ' - ' + title } @@ -118,6 +120,7 @@ function search (query) { } function play (id) { + if (id < 1) return httpGet('/api/track/' + id).then(function (data) { let title = shortTitle(data.title, data.artist) playing.innerHTML = title @@ -140,6 +143,14 @@ audio.addEventListener('ended', function (e) { play(nowPlaying + 1) }) +next.addEventListener('click', function (e) { + play(nowPlaying + 1) +}) + +prev.addEventListener('click', function (e) { + play(nowPlaying - 1) +}) + showTracks(1) window.play = play diff --git a/public/player.js b/public/player.js new file mode 100644 index 0000000..ff30ce6 --- /dev/null +++ b/public/player.js @@ -0,0 +1,183 @@ +(function () { + var audio = document.querySelector('#player') + + var play = document.querySelector('#player-play') + + var ts = document.querySelector('#player-ts') + var seek = document.querySelector('#player-seekbar') + var seekBar = document.querySelector('#player-seekbar .seek-inner') + + var mute = document.querySelector('#player-mute') + var vol = document.querySelector('#player-volbar') + var volBar = document.querySelector('#player-volbar .seek-inner') + + // Seconds into HH:MM:SS + function toHHMMSS (numbr) { + let secNum = parseInt(numbr, 10) // don't forget the second param + let hours = Math.floor(secNum / 3600) + let minutes = Math.floor((secNum - (hours * 3600)) / 60) + let seconds = secNum - (hours * 3600) - (minutes * 60) + + if (hours < 10) hours = '0' + hours + if (minutes < 10) minutes = '0' + minutes + if (seconds < 10) seconds = '0' + seconds + + let time = '' + if (parseInt(hours) > 0) { + time = hours + ':' + minutes + ':' + seconds + } else { + time = minutes + ':' + seconds + } + return time + } + + function relMouseCoords (e) { + let x + let y + + if (e.changedTouches) { + let touch = e.changedTouches[0] + if (touch) { + e.pageX = touch.pageX + e.pageY = touch.pageY + } + } + + if (e.pageX || e.pageY) { + x = e.pageX + y = e.pageY + } else { + x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft + y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop + } + + x -= this.offsetLeft + y -= this.offsetTop + + return {x: x, y: y} + } + + function updateSeeker() { + if (isNaN(audio.duration) || audio.duration === 0) return + + ts.innerHTML = toHHMMSS(audio.currentTime) + ' / ' + toHHMMSS(audio.duration) + + let progress = 100 * audio.currentTime / audio.duration + + seekBar.style.width = progress + '%' + } + + function updateVolume() { + volBar.style.width = (100 * audio.volume) + '%' + + if (audio.muted) { + mute.className = 'muted-btn flex-col icon' + } else if (audio.volume > 0.5) { + mute.className = 'mute-btn flex-col icon' + } else { + mute.className = 'mute1-btn flex-col icon' + } + } + + function seekProperty (prop, max, e) { + if (isNaN(max) || max === 0) return + + let pos = relMouseCoords.call(this, e) + let clickPercent = pos.x / this.offsetWidth + + if (max == 1) { + audio[prop] = clickPercent + return + } + + audio[prop] = Math.floor(max * clickPercent) + } + + function setClass (elem, int, rep) { + let classString = elem.className + + if (classString.indexOf(int) !== -1) { + classString = classString.replace(int, rep) + } else if (classString.indexOf(rep) !== -1) { + return + } + + elem.className = classString + } + + function togglePlayButton () { + if (audio.paused) { + setClass(play, 'pause-btn', 'play-btn') + } else { + setClass(play, 'play-btn', 'pause-btn') + } + } + + function registerSeekBar (element, v, mv) { + let seeking = false + let moved = false + + element.addEventListener('mousedown', function (e) { + seeking = true + moved = false + }, false) + + element.addEventListener('mousemove', function (e) { + if (!seeking) return + moved = true + + if (v === 'currentTime') { + audio.seeking = true + } + + seekProperty.call(element, v, (mv ? audio[mv] : 1), e) + }) + + element.addEventListener('mouseup', function (e) { + if (!seeking) return + seeking = false + + if (moved) { + if (v === 'currentTime') { + audio.seeking = false + } + return + } + + seekProperty.call(element, v, (mv ? audio[mv] : 1), e) + }) + + element.addEventListener('mouseleave', function (e) { + if (!seeking) return + seeking = false + + updateSeeker() + }) + } + + audio.addEventListener('durationchange', updateSeeker, false) + audio.addEventListener('volumechange', updateVolume, false) + audio.addEventListener('timeupdate', updateSeeker, false) + + audio.addEventListener('play', togglePlayButton, false) + audio.addEventListener('pause', togglePlayButton, false) + audio.addEventListener('stop', togglePlayButton, false) + + play.addEventListener('click', function (e) { + if (audio.paused) { + audio.play() + } else { + audio.pause() + } + }, false) + + mute.addEventListener('click', function (e) { + audio.muted = !audio.muted + updateVolume() + }, false) + + registerSeekBar(seek, 'currentTime', 'duration') + registerSeekBar(vol, 'volume') + + updateVolume() +})()