Simple playlists, download automatic db insert
This commit is contained in:
parent
5b5414a189
commit
a04bd3e69b
@ -21,6 +21,7 @@ CREATE TABLE PlaylistEntry (
|
|||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
playlistId INTEGER,
|
playlistId INTEGER,
|
||||||
trackId INTEGER,
|
trackId INTEGER,
|
||||||
|
indx INTEGER,
|
||||||
CONSTRAINT PE_fk_playlistId FOREIGN KEY (playlistId)
|
CONSTRAINT PE_fk_playlistId FOREIGN KEY (playlistId)
|
||||||
REFERENCES Playlist (id) ON UPDATE CASCADE ON DELETE CASCADE,
|
REFERENCES Playlist (id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
CONSTRAINT PE_fk_trackId FOREIGN KEY (trackId)
|
CONSTRAINT PE_fk_trackId FOREIGN KEY (trackId)
|
||||||
|
18
package.json
18
package.json
@ -13,16 +13,18 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.7.6",
|
"@babel/runtime": "^7.7.6",
|
||||||
"bcrypt": "^3.0.7",
|
"bcrypt": "^3.0.7",
|
||||||
"bluebird": "^3.5.2",
|
"bluebird": "^3.7.2",
|
||||||
"connect-redis": "^3.4.1",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.16.3",
|
"connect-redis": "^3.4.2",
|
||||||
"express-async-errors": "^3.0.0",
|
"express": "^4.17.1",
|
||||||
"express-session": "^1.16.2",
|
"express-async-errors": "^3.1.1",
|
||||||
|
"express-session": "^1.17.0",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"fs-extra": "^7.0.0",
|
"fs-extra": "^7.0.1",
|
||||||
"oauth-libre": "^0.9.17",
|
"oauth-libre": "^0.9.17",
|
||||||
"socket.io": "^2.1.1",
|
"redis": "^2.8.0",
|
||||||
"sqlite": "^3.0.2",
|
"socket.io": "^2.3.0",
|
||||||
|
"sqlite": "^3.0.3",
|
||||||
"sqlite3": "^4.1.1"
|
"sqlite3": "^4.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -177,7 +177,7 @@ tr.external td {
|
|||||||
width: 0%;
|
width: 0%;
|
||||||
background-color: #00b7ff;
|
background-color: #00b7ff;
|
||||||
}
|
}
|
||||||
.ctx-menu {
|
.ctx-menu, .ctx-sub-items {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: #0c2233;
|
background-color: #0c2233;
|
||||||
border: 2px solid #031421;
|
border: 2px solid #031421;
|
||||||
@ -200,6 +200,17 @@ tr.external td {
|
|||||||
.ctx-item:hover, .dropdown-content div:hover {
|
.ctx-item:hover, .dropdown-content div:hover {
|
||||||
background-color: #0c273c;
|
background-color: #0c273c;
|
||||||
}
|
}
|
||||||
|
.ctx-multi {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.ctx-multi:hover > .ctx-sub-items {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
.ctx-sub-items {
|
||||||
|
left: 100%;
|
||||||
|
top: 0;
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
.inline-flex {
|
.inline-flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -210,6 +221,9 @@ tr.external td {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
.player .inline-flex {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
#search-clear {
|
#search-clear {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,10 @@
|
|||||||
<li><a class="ctx-item" data-action="play">Play Track</a></li>
|
<li><a class="ctx-item" data-action="play">Play Track</a></li>
|
||||||
<li><a class="ctx-item" data-action="queue">Queue Track</a></li>
|
<li><a class="ctx-item" data-action="queue">Queue Track</a></li>
|
||||||
<li><a class="ctx-item" data-action="download">Download</a></li>
|
<li><a class="ctx-item" data-action="download">Download</a></li>
|
||||||
|
<li class="ctx-multi"><a class="ctx-item playlist-add" style="display: none;">Add to Playlist</a>
|
||||||
|
<ul class="ctx-sub-items playlist-list" id="ctx-playlists"></ul>
|
||||||
|
</li>
|
||||||
|
<li><a class="ctx-item" data-action="playlist-remove" style="display: none;">Remove from Playlist</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar background" id="options-drop">
|
<div class="sidebar background" id="options-drop">
|
||||||
|
226
public/index.js
226
public/index.js
@ -38,7 +38,11 @@
|
|||||||
|
|
||||||
// Playlists
|
// Playlists
|
||||||
var playlist = null
|
var playlist = null
|
||||||
|
var playlistCache = {}
|
||||||
|
var playingList = null
|
||||||
var pSel = document.getElementById('playlist-select')
|
var pSel = document.getElementById('playlist-select')
|
||||||
|
var dropdownBtn = pSel.querySelector('.dropdown-button')
|
||||||
|
var ctxPlaylist = document.getElementById('ctx-playlists')
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
var options = {
|
var options = {
|
||||||
@ -108,22 +112,38 @@
|
|||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
function httpGet (url, callback) {
|
function _httpRequest (method, url, data) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
var xmlHttp = new XMLHttpRequest()
|
var xmlHttp = new XMLHttpRequest()
|
||||||
xmlHttp.onreadystatechange = function () {
|
xmlHttp.onreadystatechange = function () {
|
||||||
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
|
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
|
||||||
resolve(JSON.parse(xmlHttp.responseText))
|
resolve(JSON.parse(xmlHttp.responseText))
|
||||||
} else if (xmlHttp.readyState === 4) {
|
} else if (xmlHttp.readyState === 4) {
|
||||||
reject(new Error(xmlHttp.status))
|
let msg = 'Server returned code ' + xmlHttp.status
|
||||||
|
try {
|
||||||
|
let ct = JSON.parse(xmlHttp.responseText)
|
||||||
|
if (ct.error) msg = ct.error
|
||||||
|
} catch (e) {}
|
||||||
|
reject(new Error(msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xmlHttp.withCredentials = true
|
xmlHttp.withCredentials = true
|
||||||
xmlHttp.open('GET', url, true)
|
xmlHttp.open(method, url, true)
|
||||||
xmlHttp.send(null)
|
if (method !== 'GET' && method !== 'HEAD') {
|
||||||
|
xmlHttp.setRequestHeader('Content-Type', 'application/json;charset=UTF-8')
|
||||||
|
}
|
||||||
|
xmlHttp.send(data ? JSON.stringify(data) : null)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function httpGet (url) {
|
||||||
|
return _httpRequest('GET', url)
|
||||||
|
}
|
||||||
|
|
||||||
|
function httpPost (url, data) {
|
||||||
|
return _httpRequest('POST', url, data)
|
||||||
|
}
|
||||||
|
|
||||||
function shortTitle (title, artist) {
|
function shortTitle (title, artist) {
|
||||||
if (!artist) return title
|
if (!artist) return title
|
||||||
return artist + ' - ' + title
|
return artist + ' - ' + title
|
||||||
@ -217,7 +237,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateQT (q) {
|
function updateQT (q) {
|
||||||
if (playlist === 0) showTracks(pageNum)
|
if (playlist === -1) showTracks(pageNum)
|
||||||
if (q) {
|
if (q) {
|
||||||
qTag.style.display = 'block'
|
qTag.style.display = 'block'
|
||||||
var cnt = '+' + queue.length
|
var cnt = '+' + queue.length
|
||||||
@ -228,6 +248,25 @@
|
|||||||
qTag.style.display = 'none'
|
qTag.style.display = 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeFromPlaylist(track) {
|
||||||
|
if (playlist == null || playlist < 0) return
|
||||||
|
httpPost('/api/playlist/track/remove/' + playlist + '/' + track).then(function () {
|
||||||
|
alert('Track removed from playlist successfully!')
|
||||||
|
showPlaylist(playlist, true)
|
||||||
|
}).catch(function (e) {
|
||||||
|
alert(e.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function addToPlaylist (playlistId, trackId) {
|
||||||
|
httpPost('/api/playlist/track/put/' + playlistId + '/' + trackId).then(function () {
|
||||||
|
alert('Track added to playlist successfully!')
|
||||||
|
showPlaylist(playlistId, true)
|
||||||
|
}).catch(function (e) {
|
||||||
|
alert(e.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function ctxHandle (el) {
|
function ctxHandle (el) {
|
||||||
if (ctxState === 0) return
|
if (ctxState === 0) return
|
||||||
let dt = el.getAttribute("data-action")
|
let dt = el.getAttribute("data-action")
|
||||||
@ -236,7 +275,7 @@
|
|||||||
play(ctxState)
|
play(ctxState)
|
||||||
break
|
break
|
||||||
case 'queue':
|
case 'queue':
|
||||||
if (playlist === 0)
|
if (playlist === -1)
|
||||||
removeFromQueue(ctxState)
|
removeFromQueue(ctxState)
|
||||||
else
|
else
|
||||||
queue.push(ctxState)
|
queue.push(ctxState)
|
||||||
@ -244,6 +283,17 @@
|
|||||||
case 'download':
|
case 'download':
|
||||||
window.open('/api/serve/by-id/' + ctxState + '?dl=1', '_blank')
|
window.open('/api/serve/by-id/' + ctxState + '?dl=1', '_blank')
|
||||||
break
|
break
|
||||||
|
case 'playlist-remove':
|
||||||
|
removeFromPlaylist(ctxState)
|
||||||
|
break
|
||||||
|
case 'playlist-new':
|
||||||
|
createPlaylist(ctxState)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Adding to playlist
|
||||||
|
if (dt && dt.indexOf('push-') === 0) {
|
||||||
|
let id = dt.substr(5)
|
||||||
|
addToPlaylist(id, ctxState)
|
||||||
}
|
}
|
||||||
ctxHide()
|
ctxHide()
|
||||||
updateQT(qTag.style.display === 'block')
|
updateQT(qTag.style.display === 'block')
|
||||||
@ -256,6 +306,12 @@
|
|||||||
let qbtn = menu.querySelector('.ctx-item[data-action="queue"]')
|
let qbtn = menu.querySelector('.ctx-item[data-action="queue"]')
|
||||||
qbtn.innerHTML = qe ? 'Remove from Queue' : 'Queue Track'
|
qbtn.innerHTML = qe ? 'Remove from Queue' : 'Queue Track'
|
||||||
|
|
||||||
|
let plAdd = menu.querySelector('.playlist-add')
|
||||||
|
plAdd.style.display = isNaN(parseInt(xid)) ? 'none' : 'block'
|
||||||
|
|
||||||
|
let plDel = menu.querySelector('.ctx-item[data-action="playlist-remove"]')
|
||||||
|
plDel.style.display = (playlist != null && playlist >= 0) ? 'block' : 'none'
|
||||||
|
|
||||||
ctxState = xid
|
ctxState = xid
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +331,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleHeads () {
|
function handleHeads () {
|
||||||
if (playlist === 0) return
|
if (playlist === -1) return
|
||||||
let els = elementsToArray(document.querySelectorAll('th'))
|
let els = elementsToArray(document.querySelectorAll('th'))
|
||||||
let sortarr = document.createElement('span')
|
let sortarr = document.createElement('span')
|
||||||
sortarr.className = 'order' + (options.sortdir === 'desc' ? ' dn' : '')
|
sortarr.className = 'order' + (options.sortdir === 'desc' ? ' dn' : '')
|
||||||
@ -291,11 +347,13 @@
|
|||||||
if (options.sortby !== sortattr) {
|
if (options.sortby !== sortattr) {
|
||||||
options.sortdir = 'asc'
|
options.sortdir = 'asc'
|
||||||
options.sortby = sortattr
|
options.sortby = sortattr
|
||||||
return showTracks(pageNum)
|
if (playlist == null || playlist < 0) return showTracks(pageNum)
|
||||||
|
showPlaylist(playlist)
|
||||||
}
|
}
|
||||||
|
|
||||||
options.sortdir = (options.sortdir === 'asc' ? 'desc' : 'asc')
|
options.sortdir = (options.sortdir === 'asc' ? 'desc' : 'asc')
|
||||||
showTracks(pageNum)
|
if (playlist == null || playlist < 0) return showTracks(pageNum)
|
||||||
|
showPlaylist(playlist)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -308,12 +366,12 @@
|
|||||||
let tag = trackDataRow(track)
|
let tag = trackDataRow(track)
|
||||||
|
|
||||||
tag.addEventListener('click', function (e) {
|
tag.addEventListener('click', function (e) {
|
||||||
play(track.id)
|
play(track.trackId || track.id)
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
tag.addEventListener('contextmenu', function (e) {
|
tag.addEventListener('contextmenu', function (e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
ctxTrack(track.id, e)
|
ctxTrack(track.trackId || track.id, e)
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
table.appendChild(tag)
|
table.appendChild(tag)
|
||||||
@ -324,7 +382,7 @@
|
|||||||
|
|
||||||
function recursionQueueList (index) {
|
function recursionQueueList (index) {
|
||||||
let qid = queue[index]
|
let qid = queue[index]
|
||||||
if (!qid || playlist !== 0) return
|
if (!qid || playlist !== -1) return
|
||||||
|
|
||||||
httpGet('/api/track/' + qid).then(function (data) {
|
httpGet('/api/track/' + qid).then(function (data) {
|
||||||
let tag = trackDataRow(data)
|
let tag = trackDataRow(data)
|
||||||
@ -358,6 +416,76 @@
|
|||||||
pageNumText.innerHTML = pageNum + ' / ' + pages
|
pageNumText.innerHTML = pageNum + ' / ' + pages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function populatePlaylists () {
|
||||||
|
// Remove existing playlist names from the dropdown
|
||||||
|
let elems = elementsToArray(pSel.querySelectorAll('.playlist'))
|
||||||
|
for (let i in elems) {
|
||||||
|
let element = elems[i]
|
||||||
|
element.parentNode.removeChild(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user playlists and add them to the dropdown
|
||||||
|
httpGet('/api/playlist').then(function (content) {
|
||||||
|
ctxPlaylist.innerHTML = ''
|
||||||
|
for (let i in content) {
|
||||||
|
let plylit = content[i]
|
||||||
|
// Add selection
|
||||||
|
let elem = document.createElement('div')
|
||||||
|
elem.className = 'playlist'
|
||||||
|
elem.innerHTML = plylit.title
|
||||||
|
elem.setAttribute('data-value', plylit.id)
|
||||||
|
pSel.querySelector('.dropdown-content').appendChild(elem)
|
||||||
|
// Add CTX clues
|
||||||
|
let li = document.createElement('li')
|
||||||
|
let ctxItem = document.createElement('div')
|
||||||
|
ctxItem.className = 'ctx-item'
|
||||||
|
ctxItem.innerHTML = plylit.title
|
||||||
|
ctxItem.setAttribute('data-action', 'push-' + plylit.id)
|
||||||
|
li.appendChild(ctxItem)
|
||||||
|
ctxPlaylist.appendChild(li)
|
||||||
|
}
|
||||||
|
|
||||||
|
let lit = document.createElement('li')
|
||||||
|
let ctxItemGeneral = document.createElement('div')
|
||||||
|
ctxItemGeneral.className = 'ctx-item'
|
||||||
|
ctxItemGeneral.innerHTML = 'New..'
|
||||||
|
ctxItemGeneral.setAttribute('data-action', 'playlist-new')
|
||||||
|
lit.appendChild(ctxItemGeneral)
|
||||||
|
ctxPlaylist.appendChild(lit)
|
||||||
|
|
||||||
|
handleSelect()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPlaylist (pid, updateOnly) {
|
||||||
|
if (!(updateOnly && playlist !== pid)) {
|
||||||
|
playlist = pid
|
||||||
|
pages = 1
|
||||||
|
pageNum = 1
|
||||||
|
}
|
||||||
|
httpGet('/api/playlist/' + pid + '?page=' + pageNum + '&sort=' + options.sortby + '&sortdir=' + options.sortdir).then(function (content) {
|
||||||
|
playlistCache[content.id] = content
|
||||||
|
if (!(updateOnly && playlist !== pid)) {
|
||||||
|
constructList(content.tracks)
|
||||||
|
updatePaging()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPlaylist (toAdd) {
|
||||||
|
let title = window.prompt('Enter name for playlist')
|
||||||
|
if (!title || title === '') return
|
||||||
|
httpPost('/api/playlist/new', { title: title }).then(function (d) {
|
||||||
|
if (toAdd == null) alert('Playlist created successfully!')
|
||||||
|
populatePlaylists()
|
||||||
|
if (toAdd != null) {
|
||||||
|
addToPlaylist(d.playlist, toAdd)
|
||||||
|
}
|
||||||
|
}).catch(function (e) {
|
||||||
|
alert(e.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function showTracks (page) {
|
function showTracks (page) {
|
||||||
let query = input.value
|
let query = input.value
|
||||||
if (searching && query.trim() === '') {
|
if (searching && query.trim() === '') {
|
||||||
@ -370,10 +498,13 @@
|
|||||||
if (page > pages) page = pages
|
if (page > pages) page = pages
|
||||||
updatePaging()
|
updatePaging()
|
||||||
|
|
||||||
if (playlist === 0) {
|
if (playlist === -1) {
|
||||||
return constructQueue()
|
return constructQueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playlist = null
|
||||||
|
dropdownBtn.innerHTML = 'All Tracks'
|
||||||
|
|
||||||
if (query.trim() === '') {
|
if (query.trim() === '') {
|
||||||
return httpGet('/api/tracks?page=' + page + '&sort=' + options.sortby + '&sortdir=' + options.sortdir).then(function (data) {
|
return httpGet('/api/tracks?page=' + page + '&sort=' + options.sortby + '&sortdir=' + options.sortdir).then(function (data) {
|
||||||
pageNum = page
|
pageNum = page
|
||||||
@ -399,7 +530,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function play (id, q) {
|
function play (id, q, fromNext) {
|
||||||
if (id < 1) return playNext()
|
if (id < 1) return playNext()
|
||||||
httpGet('/api/track/' + id).then(function (data) {
|
httpGet('/api/track/' + id).then(function (data) {
|
||||||
let title = shortTitle(data.title, data.artist)
|
let title = shortTitle(data.title, data.artist)
|
||||||
@ -415,19 +546,57 @@
|
|||||||
updateQT(q)
|
updateQT(q)
|
||||||
|
|
||||||
nowPlaying = id
|
nowPlaying = id
|
||||||
|
if (!fromNext) {
|
||||||
|
if (playlist != null && playlist > -1 && !q) {
|
||||||
|
playingList = playlist
|
||||||
|
} else {
|
||||||
|
playingList = null
|
||||||
|
}
|
||||||
|
}
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function playNext () {
|
function playPrevious () {
|
||||||
if (queue.length !== 0) return play(queue.shift(), true)
|
if (playingList) {
|
||||||
|
let pl = playlistCache[playingList]
|
||||||
|
if (pl) {
|
||||||
|
let prevTrack = null
|
||||||
|
for (let i in pl.tracks) {
|
||||||
|
let tr = pl.tracks[i]
|
||||||
|
if (tr.trackId === nowPlaying) {
|
||||||
|
prevTrack = parseInt(i) - 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prevTrack == null || !pl.tracks[prevTrack]) return
|
||||||
|
return play(pl.tracks[prevTrack].trackId, false, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
if (externalStream) return
|
if (externalStream) return
|
||||||
play(nowPlaying + 1)
|
play(nowPlaying - 1, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
function showPlaylist (pid) {
|
function playNext () {
|
||||||
|
if (queue.length !== 0) return play(queue.shift(), true, true)
|
||||||
|
if (playingList) {
|
||||||
|
let pl = playlistCache[playingList]
|
||||||
|
if (pl) {
|
||||||
|
let nextTrack = null
|
||||||
|
for (let i in pl.tracks) {
|
||||||
|
let tr = pl.tracks[i]
|
||||||
|
if (tr.trackId === nowPlaying) {
|
||||||
|
nextTrack = parseInt(i) + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextTrack == null || !pl.tracks[nextTrack]) return
|
||||||
|
return play(pl.tracks[nextTrack].trackId, false, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (externalStream) return
|
||||||
|
play(nowPlaying + 1, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ignoreFirst = false
|
var ignoreFirst = false
|
||||||
@ -441,33 +610,37 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleSelect () {
|
function handleSelect () {
|
||||||
let btn = pSel.querySelector('.dropdown-button')
|
|
||||||
let btns = elementsToArray(pSel.querySelectorAll('.dropdown-content div'))
|
let btns = elementsToArray(pSel.querySelectorAll('.dropdown-content div'))
|
||||||
|
|
||||||
for (let i in btns) {
|
for (let i in btns) {
|
||||||
let btni = btns[i]
|
let btni = btns[i]
|
||||||
let cnt = btni.getAttribute('data-value')
|
let cnt = btni.getAttribute('data-value')
|
||||||
|
if (btni.getAttribute('data-bound')) continue
|
||||||
|
|
||||||
btni.addEventListener('click', function (e) {
|
btni.addEventListener('click', function (e) {
|
||||||
let last = btn.innerHTML
|
let isPlaylist = false
|
||||||
|
let last = dropdownBtn.innerHTML
|
||||||
let set = btni.innerHTML
|
let set = btni.innerHTML
|
||||||
switch (cnt) {
|
switch (cnt) {
|
||||||
case 'all':
|
case 'all':
|
||||||
playlist = null
|
playlist = null
|
||||||
break
|
break
|
||||||
case 'queue':
|
case 'queue':
|
||||||
playlist = 0
|
playlist = -1
|
||||||
break
|
break
|
||||||
case 'options':
|
case 'options':
|
||||||
toggleOptions()
|
toggleOptions()
|
||||||
set = last
|
set = last
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
isPlaylist = true
|
||||||
showPlaylist(cnt)
|
showPlaylist(cnt)
|
||||||
}
|
}
|
||||||
btn.innerHTML = set
|
dropdownBtn.innerHTML = set
|
||||||
showTracks(pageNum)
|
if (!isPlaylist) showTracks(pageNum)
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
|
btni.setAttribute('data-bound', true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,9 +705,7 @@
|
|||||||
|
|
||||||
document.getElementById('player-next').addEventListener('click', playNext, false)
|
document.getElementById('player-next').addEventListener('click', playNext, false)
|
||||||
|
|
||||||
document.getElementById('player-prev').addEventListener('click', function (e) {
|
document.getElementById('player-prev').addEventListener('click', playPrevious, false)
|
||||||
play(nowPlaying - 1)
|
|
||||||
}, false)
|
|
||||||
|
|
||||||
document.getElementById('jump-first').addEventListener('click', function (e) {
|
document.getElementById('jump-first').addEventListener('click', function (e) {
|
||||||
showTracks(1)
|
showTracks(1)
|
||||||
@ -600,6 +771,7 @@
|
|||||||
loadOptions()
|
loadOptions()
|
||||||
showTracks(1)
|
showTracks(1)
|
||||||
handleHash(window.location.hash)
|
handleHash(window.location.hash)
|
||||||
|
populatePlaylists()
|
||||||
handleSelect()
|
handleSelect()
|
||||||
handleOptions()
|
handleOptions()
|
||||||
})()
|
})()
|
||||||
|
11
src/database.js
Normal file
11
src/database.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import path from 'path'
|
||||||
|
import sqlite from 'sqlite'
|
||||||
|
import Promise from 'bluebird'
|
||||||
|
|
||||||
|
const values = require(path.join(process.cwd(), 'values.json'))
|
||||||
|
|
||||||
|
const dbPromise = Promise.resolve()
|
||||||
|
.then(() => sqlite.open(path.join(process.cwd(), values.database), { Promise, cache: true }))
|
||||||
|
.then(db => db.migrate())
|
||||||
|
|
||||||
|
export { dbPromise }
|
@ -1,22 +1,17 @@
|
|||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import sqlite from 'sqlite'
|
|
||||||
import Promise from 'bluebird'
|
|
||||||
import readline from 'readline'
|
import readline from 'readline'
|
||||||
|
|
||||||
import asn from './common/async'
|
import asn from './common/async'
|
||||||
import dl from './common/download'
|
import dl from './common/download'
|
||||||
|
import { dbPromise } from './database'
|
||||||
|
|
||||||
const values = require(path.join(process.cwd(), 'values.json'))
|
const values = require(path.join(process.cwd(), 'values.json'))
|
||||||
const musicdir = path.resolve(values.directory)
|
const musicdir = path.resolve(values.directory)
|
||||||
|
|
||||||
const dbPromise = Promise.resolve()
|
|
||||||
.then(() => sqlite.open(path.join(process.cwd(), values.database), { Promise, cache: true }))
|
|
||||||
.then(db => db.migrate())
|
|
||||||
|
|
||||||
// ffprobe -i <file> -show_entries format=duration -v quiet -of csv="p=0"
|
// ffprobe -i <file> -show_entries format=duration -v quiet -of csv="p=0"
|
||||||
|
|
||||||
async function interactive (fpath, db) {
|
async function interactive (fpath) {
|
||||||
let rl = readline.createInterface({
|
let rl = readline.createInterface({
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
output: process.stdout
|
output: process.stdout
|
||||||
|
@ -6,6 +6,7 @@ import path from 'path'
|
|||||||
|
|
||||||
import asn from './common/async'
|
import asn from './common/async'
|
||||||
import dl from './common/download'
|
import dl from './common/download'
|
||||||
|
import { dbPromise } from './database'
|
||||||
|
|
||||||
const values = require(path.join(process.cwd(), 'values.json'))
|
const values = require(path.join(process.cwd(), 'values.json'))
|
||||||
const musicdir = path.resolve(values.directory)
|
const musicdir = path.resolve(values.directory)
|
||||||
@ -52,6 +53,18 @@ async function download (furl) {
|
|||||||
await asn.promiseExec(`ffmpeg -i "${file.source}" -metadata artist="${clean.artist}" -metadata title="${clean.title}" -codec copy "${fn}"`)
|
await asn.promiseExec(`ffmpeg -i "${file.source}" -metadata artist="${clean.artist}" -metadata title="${clean.title}" -codec copy "${fn}"`)
|
||||||
await fs.unlink(file.source)
|
await fs.unlink(file.source)
|
||||||
|
|
||||||
|
let addAnsw = await asn.askAsync(rl, `Would you like to add it to the database now? [N/y] ? `)
|
||||||
|
if (addAnsw && addAnsw.trim().toLowerCase().indexOf('y') === 0) {
|
||||||
|
// Add to database
|
||||||
|
try {
|
||||||
|
let verify = await dl.getInfos(fn)
|
||||||
|
await asn.insertDB(await dbPromise, verify)
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('=!= Add to database failed!')
|
||||||
|
console.error(e.stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log('=> Done.')
|
console.log('=> Done.')
|
||||||
rl.close()
|
rl.close()
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import path from 'path'
|
|||||||
import asn from './common/async'
|
import asn from './common/async'
|
||||||
import dl from './common/download'
|
import dl from './common/download'
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
|
import { dbPromise } from './database'
|
||||||
|
|
||||||
const fs = require('fs').promises
|
const fs = require('fs').promises
|
||||||
const values = require(path.join(process.cwd(), 'values.json'))
|
const values = require(path.join(process.cwd(), 'values.json'))
|
||||||
@ -10,7 +11,6 @@ const memexpire = 1800
|
|||||||
let externalTracks = {}
|
let externalTracks = {}
|
||||||
let downloadQueue = []
|
let downloadQueue = []
|
||||||
let downloading = false
|
let downloading = false
|
||||||
let dbPromise
|
|
||||||
|
|
||||||
function createHash (data) {
|
function createHash (data) {
|
||||||
return crypto
|
return crypto
|
||||||
@ -154,7 +154,4 @@ function invokeDownload (add) {
|
|||||||
}, 2 * 1000)
|
}, 2 * 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function (db) {
|
export default { search, getTrackMetaReal, invokeDownload }
|
||||||
dbPromise = db
|
|
||||||
return { search, getTrackMetaReal, invokeDownload }
|
|
||||||
}
|
|
||||||
|
102
src/playlist.js
Normal file
102
src/playlist.js
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import path from 'path'
|
||||||
|
import { dbPromise } from './database'
|
||||||
|
|
||||||
|
async function getPlaylist (id, order = 'ntry.indx', direction = 'ASC') {
|
||||||
|
let db = await dbPromise
|
||||||
|
let p = await db.get('SELECT * FROM Playlist WHERE id = ?', id)
|
||||||
|
if (!p) throw new Error('No such playlist!')
|
||||||
|
let q = await db.all('SELECT ntry.id,ntry.trackId,ntry.playlistId,ntry.indx,ntry.userId, \
|
||||||
|
trck.title,trck.artist,trck.genre,trck.year,trck.duration,trck.track,trck.album \
|
||||||
|
FROM PlaylistEntry ntry LEFT JOIN Track \
|
||||||
|
trck ON ntry.trackId = trck.id WHERE ntry.playlistId = ? ORDER BY ' + order + ' ' + direction.toUpperCase(), id)
|
||||||
|
p.tracks = q || []
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPlaylists (userId) {
|
||||||
|
let db = await dbPromise
|
||||||
|
return db.all('SELECT * FROM Playlist WHERE userId = ? OR userId = NULL', userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createPlaylist (userId, title) {
|
||||||
|
let db = await dbPromise
|
||||||
|
let alreadyExists = await db.get('SELECT * FROM Playlist WHERE userId = ? AND title = ?', userId, title)
|
||||||
|
if (alreadyExists) throw new Error('Playlist with the same name already exists!')
|
||||||
|
await db.run('INSERT INTO Playlist (title,userId) VALUES (?,?)', title, userId)
|
||||||
|
return db.get('SELECT id FROM Playlist WHERE title = ? AND userId = ?', title, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deletePlaylist (userId, playlistId) {
|
||||||
|
let db = await dbPromise
|
||||||
|
let ply = await db.get('SELECT * FROM Playlist WHERE id = ?', playlistId)
|
||||||
|
if (!ply) throw new Error('Could not find playlist specified.')
|
||||||
|
if (ply.userId !== userId) throw new Error(ply.userId == null ? 'This public playlist cannot be deleted through the interface.' : 'Permission denied.')
|
||||||
|
db.run('DELETE Playlist WHERE id = ?', playlistId)
|
||||||
|
db.run('DELETE PlaylistEntry WHERE playlistId = ?', playlistId)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addTrack (userId, playlistId, trackId) {
|
||||||
|
let db = await dbPromise
|
||||||
|
let p = await getPlaylist(playlistId)
|
||||||
|
if (!p) throw new Error('Invalid playlist.')
|
||||||
|
if (p.userId != null && p.userId !== userId) throw new Error('Permission denied.')
|
||||||
|
|
||||||
|
let alreadyExists = false
|
||||||
|
for (let i in p.tracks) {
|
||||||
|
let tr = p.tracks[i]
|
||||||
|
if (tr.trackId === parseInt(trackId)) {
|
||||||
|
alreadyExists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alreadyExists) throw new Error('This track is already in the playlist.')
|
||||||
|
|
||||||
|
return db.run('INSERT INTO PlaylistEntry (playlistId,trackId,userId,indx) VALUES (?,?,?,?)', playlistId, trackId, userId, p.tracks.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeTrack (userId, playlistId, trackId) {
|
||||||
|
let db = await dbPromise
|
||||||
|
let p = await getPlaylist(playlistId)
|
||||||
|
if (!p) throw new Error('Invalid playlist.')
|
||||||
|
if (p.userId != null && p.userId !== userId) throw new Error('Permission denied.')
|
||||||
|
|
||||||
|
let trackEntry = await db.get('SELECT * FROM PlaylistEntry WHERE playlistId = ? AND trackId = ?', playlistId, trackId)
|
||||||
|
if (!trackEntry) throw new Error('This track is not in the playlist.')
|
||||||
|
|
||||||
|
return db.run('DELETE FROM PlaylistEntry WHERE id = ?', trackEntry.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function moveTrack (userId, playlistId, trackId, position) {
|
||||||
|
let db = await dbPromise
|
||||||
|
let p = await getPlaylist(playlistId)
|
||||||
|
if (!p) throw new Error('Invalid playlist.')
|
||||||
|
if (p.userId != null && p.userId !== userId) throw new Error('Permission denied.')
|
||||||
|
|
||||||
|
let trackEntry = await db.get('SELECT * FROM PlaylistEntry WHERE playlistId = ? AND trackId = ?', playlistId, trackId)
|
||||||
|
if (!trackEntry) throw new Error('This track is not in the playlist.')
|
||||||
|
|
||||||
|
let trcksNew = []
|
||||||
|
for (let i in p.tracks) {
|
||||||
|
let trck = p.tracks[i]
|
||||||
|
if (trck.trackId === trackId) {
|
||||||
|
trck.indx = position
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (trck.indx >= position) {
|
||||||
|
trck.indx++
|
||||||
|
}
|
||||||
|
trcksNew.push(trck)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update indexes
|
||||||
|
for (let i in trcksNew) {
|
||||||
|
let trck = trcksNew[i]
|
||||||
|
await db.run('UPDATE PlaylistEntry SET indx = ? WHERE trackId = ? AND playlistId = ?', trck.indx, trck.trackId, playlistId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { getPlaylist, getPlaylists, createPlaylist, deletePlaylist, addTrack, removeTrack, moveTrack }
|
116
src/server.js
116
src/server.js
@ -1,50 +1,48 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import sqlite from 'sqlite'
|
|
||||||
import Promise from 'bluebird'
|
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import session from 'express-session'
|
import session from 'express-session'
|
||||||
import redis from 'connect-redis'
|
import bodyParser from 'body-parser'
|
||||||
|
import connectSession from 'connect-redis'
|
||||||
|
import redis from 'redis'
|
||||||
import http from 'http'
|
import http from 'http'
|
||||||
import https from 'https'
|
import https from 'https'
|
||||||
import ffmpeg from 'fluent-ffmpeg'
|
import ffmpeg from 'fluent-ffmpeg'
|
||||||
import { user, userMiddleware } from './user'
|
import { user, userMiddleware } from './user'
|
||||||
|
import { dbPromise } from './database'
|
||||||
|
|
||||||
import lfmda from './lastfm'
|
import playlist from './playlist'
|
||||||
|
import lastfm from './lastfm'
|
||||||
|
|
||||||
require('express-async-errors')
|
require('express-async-errors')
|
||||||
|
|
||||||
const values = require(path.join(process.cwd(), 'values.json'))
|
const values = require(path.join(process.cwd(), 'values.json'))
|
||||||
const tracksPerPage = 100
|
const tracksPerPage = 100
|
||||||
|
|
||||||
const dbPromise = Promise.resolve()
|
|
||||||
.then(() => sqlite.open(path.join(process.cwd(), values.database), { Promise, cache: true }))
|
|
||||||
.then(db => db.migrate())
|
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
const port = process.env.PORT || 3000
|
const port = process.env.PORT || 3000
|
||||||
|
|
||||||
const dev = process.env.NODE_ENV === 'development'
|
const dev = process.env.NODE_ENV === 'development'
|
||||||
const server = http.createServer(app)
|
const server = http.createServer(app)
|
||||||
|
|
||||||
const lastfm = lfmda(dbPromise)
|
|
||||||
|
|
||||||
if (dev) {
|
if (dev) {
|
||||||
const morgan = require('morgan')
|
const morgan = require('morgan')
|
||||||
app.use(morgan('dev'))
|
app.use(morgan('dev'))
|
||||||
}
|
}
|
||||||
|
|
||||||
app.set('trust proxy', 1)
|
app.set('trust proxy', 1)
|
||||||
|
app.use(bodyParser.urlencoded({ extended: false }))
|
||||||
|
app.use(bodyParser.json())
|
||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
const sortfields = ['id', 'track', 'artist', 'title', 'album', 'year', 'file']
|
const sortfields = ['id', 'track', 'artist', 'title', 'album', 'year']
|
||||||
const srchcategories = ['title', 'artist', 'album']
|
const srchcategories = ['title', 'artist', 'album']
|
||||||
|
|
||||||
let SessionStore = redis(session)
|
let SessionStore = connectSession(session)
|
||||||
app.use(session({
|
app.use(session({
|
||||||
key: values.session_key || 'Session',
|
key: values.session_key || 'Session',
|
||||||
secret: values.session_secret || 'ch4ng3 m3!',
|
secret: values.session_secret || 'ch4ng3 m3!',
|
||||||
store: new SessionStore(values.redis || { port: 6379 }),
|
store: new SessionStore({ client: redis.createClient(values.redis || { port: 6379 }) }),
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: true,
|
saveUninitialized: true,
|
||||||
cookie: {
|
cookie: {
|
||||||
@ -53,6 +51,10 @@ app.use(session({
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// ------ //
|
||||||
|
// TRACKS //
|
||||||
|
// ------ //
|
||||||
|
|
||||||
router.get('/tracks', userMiddleware, async (req, res) => {
|
router.get('/tracks', userMiddleware, async (req, res) => {
|
||||||
let page = parseInt(req.query.page) || 1
|
let page = parseInt(req.query.page) || 1
|
||||||
if (isNaN(page)) {
|
if (isNaN(page)) {
|
||||||
@ -175,7 +177,7 @@ router.get('/track/:id', userMiddleware, async (req, res, next) => {
|
|||||||
let track = await db.get('SELECT * FROM Track WHERE id = ?', id)
|
let track = await db.get('SELECT * FROM Track WHERE id = ?', id)
|
||||||
if (!track) {
|
if (!track) {
|
||||||
track = await lastfm.getTrackMetaReal(id)
|
track = await lastfm.getTrackMetaReal(id)
|
||||||
if (!track) return next(new Error('404 file not found'))
|
if (!track) throw new Error('404 track not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
delete track.file
|
delete track.file
|
||||||
@ -183,26 +185,77 @@ router.get('/track/:id', userMiddleware, async (req, res, next) => {
|
|||||||
res.jsonp(track)
|
res.jsonp(track)
|
||||||
})
|
})
|
||||||
|
|
||||||
router.get('/playlists', userMiddleware, async (req, res, next) => {
|
// --------- //
|
||||||
let db = await dbPromise
|
// PLAYLISTS //
|
||||||
let playlists = await db.all('SELECT * FROM Playlist WHERE userId = ? OR userId = NULL', req.session.user)
|
// --------- //
|
||||||
|
|
||||||
res.jsonp(playlists)
|
// General playlist endpoints
|
||||||
|
|
||||||
|
router.post('/playlist/new', userMiddleware, async (req, res, next) => {
|
||||||
|
if (!req.body.title) throw new Error('Title missing from body.')
|
||||||
|
let id = await playlist.createPlaylist(req.session.user, req.body.title)
|
||||||
|
res.jsonp({success: true, playlist: id.id})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/playlist/delete/:playlistId', userMiddleware, async (req, res, next) => {
|
||||||
|
let pId = req.params.playlistId
|
||||||
|
await playlist.deletePlaylist(req.session.user, pId)
|
||||||
|
res.jsonp({success: true})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Playlist track endpoints
|
||||||
|
|
||||||
|
router.post('/playlist/track/put/:playlistId/:trackId', userMiddleware, async (req, res, next) => {
|
||||||
|
let pId = req.params.playlistId
|
||||||
|
let tId = req.params.trackId
|
||||||
|
await playlist.addTrack(req.session.user, pId, tId)
|
||||||
|
res.jsonp({success: true})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/playlist/track/remove/:playlistId/:trackId', userMiddleware, async (req, res, next) => {
|
||||||
|
let pId = req.params.playlistId
|
||||||
|
let tId = req.params.trackId
|
||||||
|
await playlist.removeTrack(req.session.user, pId, tId)
|
||||||
|
res.jsonp({success: true})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/playlist/track/move/:playlistId/:trackId', userMiddleware, async (req, res, next) => {
|
||||||
|
let pId = req.params.playlistId
|
||||||
|
let tId = req.params.trackId
|
||||||
|
let pos = parseInt(req.body.position)
|
||||||
|
if (!pos || isNaN(pos)) throw new Error('Invalid position.')
|
||||||
|
await playlist.moveTrack(req.session.user, pId, tId, pos)
|
||||||
|
res.jsonp({success: true})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/playlist', userMiddleware, async (req, res, next) => {
|
||||||
|
res.jsonp(await playlist.getPlaylists(req.session.user))
|
||||||
})
|
})
|
||||||
|
|
||||||
router.get('/playlist/:id', userMiddleware, async (req, res, next) => {
|
router.get('/playlist/:id', userMiddleware, async (req, res, next) => {
|
||||||
let id = req.params.id
|
let sort = req.query.sort
|
||||||
let db = await dbPromise
|
if (!sort || sortfields.indexOf(sort.toLowerCase()) === -1) {
|
||||||
let playlist = await db.get('SELECT title FROM Playlist WHERE id = ?', id)
|
sort = 'artist'
|
||||||
|
}
|
||||||
|
|
||||||
if (!playlist) return next(new Error('404 file not found'))
|
let sortdir = req.query.sortdir
|
||||||
|
if (!sortdir || (sortdir !== 'desc' && sortdir !== 'asc')) {
|
||||||
|
sortdir = 'asc'
|
||||||
|
}
|
||||||
|
|
||||||
let tracks = await db.all('SELECT trackId FROM PlaylistEntry WHERE playlistId = ?', id)
|
if (sort === 'id') {
|
||||||
playlist.tracks = tracks
|
sort = 'ntry.indx'
|
||||||
|
} else {
|
||||||
|
sort = 'trck.' + sort
|
||||||
|
}
|
||||||
|
|
||||||
res.jsonp(playlist)
|
res.jsonp(await playlist.getPlaylist(req.params.id, sort, sortdir))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ----------- //
|
||||||
|
// FILE SERVER //
|
||||||
|
// ----------- //
|
||||||
|
|
||||||
router.get('/serve/by-id/:id', userMiddleware, async (req, res, next) => {
|
router.get('/serve/by-id/:id', userMiddleware, async (req, res, next) => {
|
||||||
let id = req.params.id
|
let id = req.params.id
|
||||||
let dl = (req.query.dl === '1')
|
let dl = (req.query.dl === '1')
|
||||||
@ -210,7 +263,7 @@ router.get('/serve/by-id/:id', userMiddleware, async (req, res, next) => {
|
|||||||
let track = await db.get('SELECT file FROM Track WHERE id = ?', id)
|
let track = await db.get('SELECT file FROM Track WHERE id = ?', id)
|
||||||
if (!track) {
|
if (!track) {
|
||||||
track = await lastfm.getTrackMetaReal(id)
|
track = await lastfm.getTrackMetaReal(id)
|
||||||
if (!track) return next(new Error('404 file not found'))
|
if (!track) throw new Error('404 file not found')
|
||||||
if (dl) {
|
if (dl) {
|
||||||
lastfm.invokeDownload(id)
|
lastfm.invokeDownload(id)
|
||||||
return res.end('<p>OK</p><script>window.close();</script>')
|
return res.end('<p>OK</p><script>window.close();</script>')
|
||||||
@ -231,12 +284,17 @@ router.get('/serve/by-id/:id', userMiddleware, async (req, res, next) => {
|
|||||||
res.redirect('/file/track' + fpath.substring(values.directory.length))
|
res.redirect('/file/track' + fpath.substring(values.directory.length))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ---------- //
|
||||||
|
// ERROR SINK //
|
||||||
|
// ---------- //
|
||||||
|
|
||||||
router.use((err, req, res, next) => {
|
router.use((err, req, res, next) => {
|
||||||
console.error(err)
|
let msg = err.message
|
||||||
res.status(404).jsonp({error: 404})
|
dev && console.error(err.stack)
|
||||||
|
res.status(msg.indexOf('404') !== -1 ? 404 : 400).jsonp({ error: err.message, stack: dev ? err.stack.toString() : undefined })
|
||||||
})
|
})
|
||||||
|
|
||||||
app.use('/user', user(dbPromise, values.oauth, values.registrations === true))
|
app.use('/user', user(values.oauth, values.registrations === true))
|
||||||
|
|
||||||
app.use('/api', router)
|
app.use('/api', router)
|
||||||
app.use('/file/track', express.static(path.resolve(values.directory)))
|
app.use('/file/track', express.static(path.resolve(values.directory)))
|
||||||
|
13
src/user.js
13
src/user.js
@ -2,10 +2,12 @@ import express from 'express'
|
|||||||
import bcrypt from 'bcrypt'
|
import bcrypt from 'bcrypt'
|
||||||
import { PromiseOAuth2 } from 'oauth-libre'
|
import { PromiseOAuth2 } from 'oauth-libre'
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
|
import { dbPromise } from './database'
|
||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
async function userInfoPublic (db, id) {
|
async function userInfoPublic (id) {
|
||||||
|
let db = await dbPromise
|
||||||
let u = await db.get('SELECT id, username, image FROM User WHERE id = ?', id)
|
let u = await db.get('SELECT id, username, image FROM User WHERE id = ?', id)
|
||||||
if (!u) return {}
|
if (!u) return {}
|
||||||
return {
|
return {
|
||||||
@ -15,7 +17,8 @@ async function userInfoPublic (db, id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function userInfo (db, id) {
|
export async function userInfo (id) {
|
||||||
|
let db = await dbPromise
|
||||||
return db.get('SELECT * FROM User WHERE id = ?', id)
|
return db.get('SELECT * FROM User WHERE id = ?', id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,14 +27,14 @@ export function userMiddleware (req, res, next) {
|
|||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function user (dbPromise, oauth, registrations) {
|
export function user (oauth, registrations) {
|
||||||
router.get('/info', userMiddleware, async (req, res) => {
|
router.get('/info', userMiddleware, async (req, res) => {
|
||||||
res.jsonp(await userInfoPublic(await dbPromise, req.session.user))
|
res.jsonp(await userInfoPublic(req.session.user))
|
||||||
})
|
})
|
||||||
|
|
||||||
router.get('/info/:id', userMiddleware, async (req, res) => {
|
router.get('/info/:id', userMiddleware, async (req, res) => {
|
||||||
if (isNaN(parseInt(req.params.id))) throw new Error('Invalid user ID!')
|
if (isNaN(parseInt(req.params.id))) throw new Error('Invalid user ID!')
|
||||||
res.jsonp(await userInfoPublic(await dbPromise, parseInt(req.params.id)))
|
res.jsonp(await userInfoPublic(parseInt(req.params.id)))
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!oauth) return router
|
if (!oauth) return router
|
||||||
|
Loading…
Reference in New Issue
Block a user