listen by youtube url, fix context menu positioning
This commit is contained in:
parent
7a3ad95d60
commit
710f1a5e48
@ -174,7 +174,7 @@
|
|||||||
var posx = 0
|
var posx = 0
|
||||||
var posy = 0
|
var posy = 0
|
||||||
|
|
||||||
if (!e) var e = window.event
|
if (!e) e = window.event
|
||||||
|
|
||||||
if (e.pageX || e.pageY) {
|
if (e.pageX || e.pageY) {
|
||||||
posx = e.pageX
|
posx = e.pageX
|
||||||
@ -193,24 +193,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function positionMenu (e) {
|
function positionMenu (e) {
|
||||||
clickCoords = getPosition(e)
|
const clickCoords = getPosition(e)
|
||||||
clickCoordsX = clickCoords.x
|
const clickCoordsX = clickCoords.x
|
||||||
clickCoordsY = clickCoords.y
|
const clickCoordsY = clickCoords.y
|
||||||
|
|
||||||
menuWidth = menu.offsetWidth + 4
|
const menuWidth = menu.offsetWidth + 4
|
||||||
menuHeight = menu.offsetHeight + 4
|
const menuHeight = menu.offsetHeight + 4
|
||||||
|
|
||||||
windowWidth = window.innerWidth
|
const windowWidth = window.innerWidth
|
||||||
windowHeight = window.innerHeight
|
const windowHeight = window.innerHeight - 80
|
||||||
|
|
||||||
if ((windowWidth - clickCoordsX) < menuWidth) {
|
if ((windowWidth - clickCoordsX) < menuWidth) {
|
||||||
menu.style.left = windowWidth - menuWidth + 'px'
|
menu.style.left = (windowWidth - menuWidth) + 'px'
|
||||||
} else {
|
} else {
|
||||||
menu.style.left = clickCoordsX + 'px'
|
menu.style.left = clickCoordsX + 'px'
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((windowHeight - clickCoordsY) < menuHeight) {
|
if ((windowHeight - clickCoordsY) < menuHeight) {
|
||||||
menu.style.top = windowHeight - menuHeight + 'px'
|
menu.style.top = (windowHeight - menuHeight) + 'px'
|
||||||
} else {
|
} else {
|
||||||
menu.style.top = clickCoordsY + 'px'
|
menu.style.top = clickCoordsY + 'px'
|
||||||
}
|
}
|
||||||
@ -330,7 +330,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ctxTrack (xid, ev, qe) {
|
function ctxTrack (xid, ev, qe) {
|
||||||
positionMenu(ev)
|
|
||||||
menu.style.display = 'block'
|
menu.style.display = 'block'
|
||||||
|
|
||||||
let qbtn = menu.querySelector('.ctx-item[data-action="queue"]')
|
let qbtn = menu.querySelector('.ctx-item[data-action="queue"]')
|
||||||
@ -345,6 +344,8 @@
|
|||||||
let plDel = menu.querySelector('.ctx-item[data-action="playlist-remove"]')
|
let plDel = menu.querySelector('.ctx-item[data-action="playlist-remove"]')
|
||||||
plDel.style.display = (playlist != null && playlist >= 0) ? 'block' : 'none'
|
plDel.style.display = (playlist != null && playlist >= 0) ? 'block' : 'none'
|
||||||
|
|
||||||
|
positionMenu(ev)
|
||||||
|
|
||||||
ctxState = xid
|
ctxState = xid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import {spawn} from 'child_process'
|
|||||||
import ffmpeg from 'fluent-ffmpeg'
|
import ffmpeg from 'fluent-ffmpeg'
|
||||||
|
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import asn from './async'
|
|
||||||
|
|
||||||
function parseTitle (data) {
|
function parseTitle (data) {
|
||||||
let tt = data.title
|
let tt = data.title
|
||||||
@ -11,16 +10,16 @@ function parseTitle (data) {
|
|||||||
tt = tt.replace(/^\[\w+\]\s?/i, '')
|
tt = tt.replace(/^\[\w+\]\s?/i, '')
|
||||||
|
|
||||||
// Remove "Official Video/Audio" tag
|
// Remove "Official Video/Audio" tag
|
||||||
tt = tt.replace(/\s?(?:[\(\)\[\]])?Official\s?[\w]+(?:[\(\)\[\]])?/i, '')
|
tt = tt.replace(/\s?(?:[()[]])?Official\s?[\w]+(?:[()[]])?/i, '')
|
||||||
|
|
||||||
// Remove "Audio" tag
|
// Remove "Audio" tag
|
||||||
tt = tt.replace(/\s?(?:[\(\)\[\]])Audio?(?:[\(\)\[\]])/i, '')
|
tt = tt.replace(/\s?(?:[()[]])Audio?(?:[()[]])/i, '')
|
||||||
|
|
||||||
// Remove "lyrics" tag
|
// Remove "lyrics" tag
|
||||||
tt = tt.replace(/\s?(?:[\(\)\[\]])?lyrics?\s?(?:[\w]+)?(?:[\(\)\[\]])?\s?/i, '')
|
tt = tt.replace(/\s?(?:[()[]])?lyrics?\s?(?:[\w]+)?(?:[()[]])?\s?/i, '')
|
||||||
|
|
||||||
// Artist / Title split
|
// Artist / Title split
|
||||||
let at = tt.split(' - ', 2)
|
const at = tt.split(' - ', 2)
|
||||||
let artist
|
let artist
|
||||||
let title
|
let title
|
||||||
|
|
||||||
@ -39,7 +38,7 @@ function parseTitle (data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getVideoInfo (arg) {
|
function getVideoInfo (arg) {
|
||||||
let yt = spawn('youtube-dl', ['--no-playlist', '-j', '-f', 'bestaudio/best', arg])
|
const yt = spawn('youtube-dl', ['--no-playlist', '-j', '-f', 'bestaudio/best', arg])
|
||||||
|
|
||||||
let output = ''
|
let output = ''
|
||||||
yt.stdout.on('data', function (chunk) {
|
yt.stdout.on('data', function (chunk) {
|
||||||
@ -48,10 +47,10 @@ function getVideoInfo (arg) {
|
|||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
yt.on('close', function () {
|
yt.on('close', function () {
|
||||||
let ftdata = output.trim().split('\n')
|
const ftdata = output.trim().split('\n')
|
||||||
if (ftdata.length > 1) {
|
if (ftdata.length > 1) {
|
||||||
let composite = []
|
const composite = []
|
||||||
for (let i in ftdata) {
|
for (const i in ftdata) {
|
||||||
let dat
|
let dat
|
||||||
try {
|
try {
|
||||||
dat = JSON.parse(ftdata[i])
|
dat = JSON.parse(ftdata[i])
|
||||||
@ -65,7 +64,7 @@ function getVideoInfo (arg) {
|
|||||||
return resolve(composite)
|
return resolve(composite)
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = JSON.parse(output)
|
const data = JSON.parse(output)
|
||||||
delete data.formats
|
delete data.formats
|
||||||
resolve(data)
|
resolve(data)
|
||||||
})
|
})
|
||||||
@ -74,7 +73,7 @@ function getVideoInfo (arg) {
|
|||||||
|
|
||||||
function fetchVideo (data) {
|
function fetchVideo (data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let tempName = path.join(process.cwd(), `/tmp.yt.${data.id}.mp3`)
|
const tempName = path.join(process.cwd(), `/tmp.yt.${data.id}.mp3`)
|
||||||
ffmpeg(data.url || data.file)
|
ffmpeg(data.url || data.file)
|
||||||
.audioCodec('libmp3lame')
|
.audioCodec('libmp3lame')
|
||||||
.format('mp3')
|
.format('mp3')
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import fs from 'fs-extra'
|
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import readline from 'readline'
|
import readline from 'readline'
|
||||||
|
|
||||||
@ -12,33 +11,35 @@ const musicdir = path.resolve(values.directory)
|
|||||||
// 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) {
|
async function interactive (fpath) {
|
||||||
let rl = readline.createInterface({
|
const rl = readline.createInterface({
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
output: process.stdout
|
output: process.stdout
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('=> No metadata found for specified file! Interactive mode enabled.\n')
|
console.log('=> No metadata found for specified file! Interactive mode enabled.\n')
|
||||||
|
|
||||||
let pt = path.parse(fpath)
|
const pt = path.parse(fpath)
|
||||||
let track = {
|
const track = {
|
||||||
file: fpath,
|
file: fpath,
|
||||||
title: pt.name
|
title: pt.name
|
||||||
}
|
}
|
||||||
let clean = dl.parseTitle(track)
|
const clean = dl.parseTitle(track)
|
||||||
|
|
||||||
console.log('== Determined Title: ' + clean.title)
|
console.log('== Determined Title: ' + clean.title)
|
||||||
console.log('== Determined Artist: ' + clean.artist)
|
console.log('== Determined Artist: ' + clean.artist)
|
||||||
|
|
||||||
let newTitle = await asn.askAsync(rl, `Title [${clean.title}] ? `)
|
const newTitle = await asn.askAsync(rl, `Title [${clean.title}] ? `)
|
||||||
let newArtist = await asn.askAsync(rl, `Artist [${clean.artist}] ? `)
|
const newArtist = await asn.askAsync(rl, `Artist [${clean.artist}] ? `)
|
||||||
|
|
||||||
if (newTitle.trim() !== '')
|
if (newTitle.trim() !== '') {
|
||||||
track.title = newTitle
|
track.title = newTitle
|
||||||
|
}
|
||||||
|
|
||||||
if (newArtist.trim() !== '')
|
if (newArtist.trim() !== '') {
|
||||||
track.artist = newArtist
|
track.artist = newArtist
|
||||||
|
}
|
||||||
|
|
||||||
let len = await asn.promiseExec(`ffprobe -i "${track.file}" -show_entries format=duration -v quiet -of csv="p=0"`)
|
const len = await asn.promiseExec(`ffprobe -i "${track.file}" -show_entries format=duration -v quiet -of csv="p=0"`)
|
||||||
track.duration = parseFloat(len)
|
track.duration = parseFloat(len)
|
||||||
|
|
||||||
rl.close()
|
rl.close()
|
||||||
@ -47,7 +48,7 @@ async function interactive (fpath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handlePassed (db, fpath) {
|
async function handlePassed (db, fpath) {
|
||||||
let filePath = path.resolve(fpath)
|
const filePath = path.resolve(fpath)
|
||||||
let trackinf
|
let trackinf
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -60,18 +61,18 @@ async function handlePassed (db, fpath) {
|
|||||||
throw new Error('Nothing to do.')
|
throw new Error('Nothing to do.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let ins = await asn.insertDB(db, trackinf)
|
const ins = await asn.insertDB(db, trackinf)
|
||||||
if (!ins) {
|
if (!ins) {
|
||||||
throw new Error('A track of this description already exists in the database.')
|
throw new Error('A track of this description already exists in the database.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function run () {
|
async function run () {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
|
|
||||||
if (process.argv[2] != null) {
|
if (process.argv[2] != null) {
|
||||||
for (let i = 2; i < process.argv.length; i++) {
|
for (let i = 2; i < process.argv.length; i++) {
|
||||||
let f = process.argv[i]
|
const f = process.argv[i]
|
||||||
console.log('=> Passing', f)
|
console.log('=> Passing', f)
|
||||||
try {
|
try {
|
||||||
await handlePassed(db, f)
|
await handlePassed(db, f)
|
||||||
@ -84,16 +85,16 @@ async function run () {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let files = await asn.getFiles(musicdir)
|
const files = await asn.getFiles(musicdir)
|
||||||
let cleanTrackData = []
|
const cleanTrackData = []
|
||||||
let skips = 0
|
let skips = 0
|
||||||
|
|
||||||
for (let i in files) {
|
for (const i in files) {
|
||||||
let file = files[i]
|
const file = files[i]
|
||||||
// if (cleanTrackData.length > 10) break // debug purposes
|
// if (cleanTrackData.length > 10) break // debug purposes
|
||||||
process.stdout.write(`\rProcessing file ${parseInt(i) + 1} of ${files.length}.. (${skips} skipped)`)
|
process.stdout.write(`\rProcessing file ${parseInt(i) + 1} of ${files.length}.. (${skips} skipped)`)
|
||||||
try {
|
try {
|
||||||
let fd = await asn.getInfos(file)
|
const fd = await asn.getInfos(file)
|
||||||
cleanTrackData.push(fd)
|
cleanTrackData.push(fd)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
skips++
|
skips++
|
||||||
@ -103,11 +104,11 @@ async function run () {
|
|||||||
process.stdout.write(`\r${cleanTrackData.length} files indexed, ${skips} files were skipped. \n`)
|
process.stdout.write(`\r${cleanTrackData.length} files indexed, ${skips} files were skipped. \n`)
|
||||||
let entries = 0
|
let entries = 0
|
||||||
|
|
||||||
for (let i in cleanTrackData) {
|
for (const i in cleanTrackData) {
|
||||||
let track = cleanTrackData[i]
|
const track = cleanTrackData[i]
|
||||||
process.stdout.write(`\rPopulating database.. (Track ${parseInt(i) + 1} of ${cleanTrackData.length})`)
|
process.stdout.write(`\rPopulating database.. (Track ${parseInt(i) + 1} of ${cleanTrackData.length})`)
|
||||||
try {
|
try {
|
||||||
let ins = await asn.insertDB(db, track)
|
const ins = await asn.insertDB(db, track)
|
||||||
if (!ins) continue
|
if (!ins) continue
|
||||||
entries++
|
entries++
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
import fs from 'fs-extra'
|
|
||||||
import readline from 'readline'
|
import readline from 'readline'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
@ -18,13 +17,13 @@ const rl = readline.createInterface({
|
|||||||
|
|
||||||
async function download (furl) {
|
async function download (furl) {
|
||||||
console.log('=> Getting information..')
|
console.log('=> Getting information..')
|
||||||
let data = await dl.getVideoInfo(furl)
|
const data = await dl.getVideoInfo(furl)
|
||||||
|
|
||||||
console.log('=> Downloading file..')
|
console.log('=> Downloading file..')
|
||||||
let file = await dl.fetchVideo(data)
|
const file = await dl.fetchVideo(data)
|
||||||
|
|
||||||
console.log('=> Cleaning up..')
|
console.log('=> Cleaning up..')
|
||||||
let clean = dl.parseTitle(file)
|
const clean = dl.parseTitle(file)
|
||||||
let filename = clean.artist + ' - ' + clean.title + '.mp3'
|
let filename = clean.artist + ' - ' + clean.title + '.mp3'
|
||||||
|
|
||||||
console.log('=> Original Title: ' + file.title + '\n')
|
console.log('=> Original Title: ' + file.title + '\n')
|
||||||
@ -32,9 +31,9 @@ async function download (furl) {
|
|||||||
console.log('== Determined Artist: ' + clean.artist)
|
console.log('== Determined Artist: ' + clean.artist)
|
||||||
console.log('== Determined File Name: ' + filename)
|
console.log('== Determined File Name: ' + filename)
|
||||||
|
|
||||||
let titleAnsw = await asn.askAsync(rl, `Title [${clean.title}] ? `)
|
const titleAnsw = await asn.askAsync(rl, `Title [${clean.title}] ? `)
|
||||||
let artistAnsw = await asn.askAsync(rl, `Artist [${clean.artist}] ? `)
|
const artistAnsw = await asn.askAsync(rl, `Artist [${clean.artist}] ? `)
|
||||||
let fileAnsw = await asn.askAsync(rl, `File [${filename}] ? `)
|
const fileAnsw = await asn.askAsync(rl, `File [${filename}] ? `)
|
||||||
|
|
||||||
if (titleAnsw && titleAnsw.trim() !== '') {
|
if (titleAnsw && titleAnsw.trim() !== '') {
|
||||||
clean.title = titleAnsw
|
clean.title = titleAnsw
|
||||||
@ -48,14 +47,14 @@ async function download (furl) {
|
|||||||
filename = fileAnsw
|
filename = fileAnsw
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn = path.join(musicdir, filename)
|
const fn = path.join(musicdir, filename)
|
||||||
await asn.setMetadata(fn, clean, file.source, true)
|
await asn.setMetadata(fn, clean, file.source, true)
|
||||||
|
|
||||||
let addAnsw = await asn.askAsync(rl, `Would you like to add it to the database now? [N/y] ? `)
|
const 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) {
|
if (addAnsw && addAnsw.trim().toLowerCase().indexOf('y') === 0) {
|
||||||
// Add to database
|
// Add to database
|
||||||
try {
|
try {
|
||||||
let verify = await asn.getInfos(fn)
|
const verify = await asn.getInfos(fn)
|
||||||
await asn.insertDB(await dbPromise, verify)
|
await asn.insertDB(await dbPromise, verify)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('=!= Add to database failed!')
|
console.warn('=!= Add to database failed!')
|
||||||
|
141
src/external.js
Normal file
141
src/external.js
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import path from 'path'
|
||||||
|
import asn from './common/async'
|
||||||
|
import dl from './common/download'
|
||||||
|
import { dbPromise } from './database'
|
||||||
|
import { URL } from 'url'
|
||||||
|
|
||||||
|
const fs = require('fs').promises
|
||||||
|
const values = require(path.join(process.cwd(), 'values.json'))
|
||||||
|
const memexpire = 1800
|
||||||
|
|
||||||
|
const externalTracks = {}
|
||||||
|
const downloadQueue = []
|
||||||
|
let downloading = false
|
||||||
|
|
||||||
|
async function downloadLocally (id) {
|
||||||
|
const info = await getTrackMetaReal(id)
|
||||||
|
if (!info) throw new Error('No track with this ID in external list.')
|
||||||
|
const file = await dl.fetchVideo(info)
|
||||||
|
const filename = info.artist + ' - ' + info.title + '.mp3'
|
||||||
|
info.file = path.join(values.directory, filename)
|
||||||
|
await asn.promiseExec(`ffmpeg -i "${file.source}" -metadata artist="${info.artist}" -metadata title="${info.title}" -codec copy "${info.file}"`)
|
||||||
|
await fs.unlink(file.source)
|
||||||
|
const db = await dbPromise
|
||||||
|
const ins = await asn.insertDB(db, info)
|
||||||
|
if (!ins) {
|
||||||
|
throw new Error('A track of this description already exists in the database.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTrackMetaReal (id) {
|
||||||
|
if (!id || !externalTracks[id]) return null
|
||||||
|
const trdata = externalTracks[id]
|
||||||
|
|
||||||
|
// Check for expiry
|
||||||
|
if (trdata.file && trdata.expires > Date.now()) {
|
||||||
|
return Object.assign({}, trdata)
|
||||||
|
}
|
||||||
|
|
||||||
|
const trsrch = 'ytsearch3:' + trdata.artist + ' - ' + trdata.title
|
||||||
|
const dldata = await dl.getVideoInfo(trsrch)
|
||||||
|
|
||||||
|
let bestMatch
|
||||||
|
if (dldata.length === 1) bestMatch = dldata[0]
|
||||||
|
|
||||||
|
let candidates = []
|
||||||
|
for (const i in dldata) {
|
||||||
|
const obj = dldata[i]
|
||||||
|
const title = obj.title.toLowerCase()
|
||||||
|
|
||||||
|
// Skip any video with 'video' in it, but keep lyric videos
|
||||||
|
if (title.indexOf('video') !== -1 && title.indexOf('lyric') === -1) continue
|
||||||
|
|
||||||
|
// If the title has 'audio' in it, it might be the best match
|
||||||
|
if (title.indexOf('audio') !== -1) {
|
||||||
|
bestMatch = obj
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates.push(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidates.length && !bestMatch) {
|
||||||
|
// Sort candidates by view count
|
||||||
|
candidates = candidates.sort(function (a, b) {
|
||||||
|
return b.view_count - a.view_count
|
||||||
|
})
|
||||||
|
|
||||||
|
// Select the one with the most views
|
||||||
|
bestMatch = candidates[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there were no suitable candidates, just take the first response
|
||||||
|
if (!candidates.length && !bestMatch) bestMatch = dldata[0]
|
||||||
|
|
||||||
|
externalTracks[id] = {
|
||||||
|
id: trdata.id,
|
||||||
|
title: trdata.title,
|
||||||
|
artist: trdata.artist,
|
||||||
|
file: bestMatch.url,
|
||||||
|
duration: bestMatch.duration,
|
||||||
|
expires: Date.now() + memexpire * 1000,
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.assign({}, externalTracks[id])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download thread
|
||||||
|
let dltd = null
|
||||||
|
function invokeDownload (add) {
|
||||||
|
if (add) downloadQueue.push(add)
|
||||||
|
if (dltd) return
|
||||||
|
dltd = setTimeout(function (argument) {
|
||||||
|
dltd = null
|
||||||
|
if (downloading) return invokeDownload()
|
||||||
|
if (!downloadQueue.length) return
|
||||||
|
downloading = true
|
||||||
|
downloadLocally(downloadQueue.shift()).then(() => {
|
||||||
|
downloading = false
|
||||||
|
if (downloadQueue.length) invokeDownload()
|
||||||
|
}).catch((e) => {
|
||||||
|
console.error(e)
|
||||||
|
downloading = false
|
||||||
|
if (downloadQueue.length) invokeDownload()
|
||||||
|
})
|
||||||
|
}, 2 * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
function addListExternal (id, obj) {
|
||||||
|
if (externalTracks[id]) return Object.assign({}, externalTracks[id])
|
||||||
|
externalTracks[id] = obj
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchURL (url) {
|
||||||
|
const urlp = new URL(url)
|
||||||
|
if (urlp.hostname.indexOf('youtube.com') !== -1 ||
|
||||||
|
urlp.hostname.indexOf('youtu.be') !== -1) {
|
||||||
|
const id = urlp.searchParams.get('v') || urlp.pathname.substr(1)
|
||||||
|
const ytb = await dl.getVideoInfo(id)
|
||||||
|
externalTracks[id] = {
|
||||||
|
id: id,
|
||||||
|
title: ytb.title,
|
||||||
|
artist: ytb.artist || ytb.uploader,
|
||||||
|
file: ytb.url,
|
||||||
|
duration: ytb.duration,
|
||||||
|
expires: Date.now() + memexpire * 1000,
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
return [externalTracks[id]]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
downloadLocally,
|
||||||
|
invokeDownload,
|
||||||
|
addListExternal,
|
||||||
|
getTrackMetaReal,
|
||||||
|
searchURL
|
||||||
|
}
|
161
src/lastfm.js
161
src/lastfm.js
@ -1,18 +1,11 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import rp from 'request-promise-native'
|
import rp from 'request-promise-native'
|
||||||
import asn from './common/async'
|
|
||||||
import dl from './common/download'
|
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
import { dbPromise } from './database'
|
import { dbPromise } from './database'
|
||||||
|
import { addListExternal } from './external'
|
||||||
import { parseStringPromise } from 'xml2js'
|
import { parseStringPromise } from 'xml2js'
|
||||||
|
|
||||||
const fs = require('fs').promises
|
|
||||||
const values = require(path.join(process.cwd(), 'values.json'))
|
const values = require(path.join(process.cwd(), 'values.json'))
|
||||||
const memexpire = 1800
|
|
||||||
|
|
||||||
let externalTracks = {}
|
|
||||||
let downloadQueue = []
|
|
||||||
let downloading = false
|
|
||||||
|
|
||||||
function createHash (data) {
|
function createHash (data) {
|
||||||
return crypto
|
return crypto
|
||||||
@ -22,79 +15,6 @@ function createHash (data) {
|
|||||||
.substr(0, 8)
|
.substr(0, 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadLocally (id) {
|
|
||||||
let info = await getTrackMetaReal(id)
|
|
||||||
if (!info) throw new Error('No track with this ID in external list.')
|
|
||||||
let file = await dl.fetchVideo(info)
|
|
||||||
let filename = info.artist + ' - ' + info.title + '.mp3'
|
|
||||||
info.file = path.join(values.directory, filename)
|
|
||||||
await asn.promiseExec(`ffmpeg -i "${file.source}" -metadata artist="${info.artist}" -metadata title="${info.title}" -codec copy "${info.file}"`)
|
|
||||||
await fs.unlink(file.source)
|
|
||||||
let db = await dbPromise
|
|
||||||
let ins = await asn.insertDB(db, info)
|
|
||||||
if (!ins) {
|
|
||||||
throw new Error('A track of this description already exists in the database.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getTrackMetaReal (id) {
|
|
||||||
if (!id || !externalTracks[id]) return null
|
|
||||||
let trdata = externalTracks[id]
|
|
||||||
|
|
||||||
// Check for expiry
|
|
||||||
if (trdata.file && trdata.expires > Date.now()) {
|
|
||||||
return Object.assign({}, trdata)
|
|
||||||
}
|
|
||||||
|
|
||||||
let trsrch = 'ytsearch3:' + trdata.artist + ' - ' + trdata.title
|
|
||||||
let dldata = await dl.getVideoInfo(trsrch)
|
|
||||||
|
|
||||||
let bestMatch
|
|
||||||
if (dldata.length === 1) bestMatch = dldata[0]
|
|
||||||
|
|
||||||
let candidates = []
|
|
||||||
for (let i in dldata) {
|
|
||||||
let obj = dldata[i]
|
|
||||||
let title = obj.title.toLowerCase()
|
|
||||||
|
|
||||||
// Skip any video with 'video' in it, but keep lyric videos
|
|
||||||
if (title.indexOf('video') !== -1 && title.indexOf('lyric') === -1) continue
|
|
||||||
|
|
||||||
// If the title has 'audio' in it, it might be the best match
|
|
||||||
if (title.indexOf('audio') !== -1) {
|
|
||||||
bestMatch = obj
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
candidates.push(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (candidates.length && !bestMatch) {
|
|
||||||
// Sort candidates by view count
|
|
||||||
candidates = candidates.sort(function (a, b) {
|
|
||||||
return b.view_count - a.view_count
|
|
||||||
})
|
|
||||||
|
|
||||||
// Select the one with the most views
|
|
||||||
bestMatch = candidates[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there were no suitable candidates, just take the first response
|
|
||||||
if (!candidates.length && !bestMatch) bestMatch = dldata[0]
|
|
||||||
|
|
||||||
externalTracks[id] = {
|
|
||||||
id: trdata.id,
|
|
||||||
title: trdata.title,
|
|
||||||
artist: trdata.artist,
|
|
||||||
file: bestMatch.url,
|
|
||||||
duration: bestMatch.duration,
|
|
||||||
expires: Date.now() + memexpire * 1000,
|
|
||||||
external: true
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.assign({}, externalTracks[id])
|
|
||||||
}
|
|
||||||
|
|
||||||
async function search (track, limit = 30) {
|
async function search (track, limit = 30) {
|
||||||
if (!values.lastfm) return []
|
if (!values.lastfm) return []
|
||||||
|
|
||||||
@ -110,9 +30,9 @@ async function search (track, limit = 30) {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let final = []
|
const final = []
|
||||||
for (let i in data.results.trackmatches.track) {
|
for (const i in data.results.trackmatches.track) {
|
||||||
let res = data.results.trackmatches.track[i]
|
const res = data.results.trackmatches.track[i]
|
||||||
let clean = {
|
let clean = {
|
||||||
id: createHash(res),
|
id: createHash(res),
|
||||||
artist: res.artist,
|
artist: res.artist,
|
||||||
@ -121,13 +41,7 @@ async function search (track, limit = 30) {
|
|||||||
mbid: res.mbid
|
mbid: res.mbid
|
||||||
}
|
}
|
||||||
|
|
||||||
if (externalTracks[clean.id]) {
|
clean = addListExternal(clean.id, clean)
|
||||||
// Copy object
|
|
||||||
clean = Object.assign({}, externalTracks[clean.id])
|
|
||||||
} else {
|
|
||||||
// Save in cache
|
|
||||||
externalTracks[clean.id] = clean
|
|
||||||
}
|
|
||||||
|
|
||||||
final.push(clean)
|
final.push(clean)
|
||||||
}
|
}
|
||||||
@ -135,42 +49,21 @@ async function search (track, limit = 30) {
|
|||||||
return final
|
return final
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download thread
|
|
||||||
let dltd = null
|
|
||||||
function invokeDownload (add) {
|
|
||||||
if (add) downloadQueue.push(add)
|
|
||||||
if (dltd) return
|
|
||||||
dltd = setTimeout(function (argument) {
|
|
||||||
dltd = null
|
|
||||||
if (downloading) return invokeDownload()
|
|
||||||
if (!downloadQueue.length) return
|
|
||||||
downloading = true
|
|
||||||
downloadLocally(downloadQueue.shift()).then(() => {
|
|
||||||
downloading = false
|
|
||||||
if (downloadQueue.length) invokeDownload()
|
|
||||||
}).catch((e) => {
|
|
||||||
console.error(e)
|
|
||||||
downloading = false
|
|
||||||
if (downloadQueue.length) invokeDownload()
|
|
||||||
})
|
|
||||||
}, 2 * 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
|
|
||||||
function getAPISig (params) {
|
function getAPISig (params) {
|
||||||
let allStrings = []
|
let allStrings = []
|
||||||
let qs = {}
|
const qs = {}
|
||||||
params['api_key'] = values.lastfm.key
|
params.api_key = values.lastfm.key
|
||||||
for (let key in params) {
|
for (const key in params) {
|
||||||
let val = params[key]
|
const val = params[key]
|
||||||
if (val == null || val === '') continue
|
if (val == null || val === '') continue
|
||||||
allStrings.push(key + val)
|
allStrings.push(key + val)
|
||||||
qs[key] = val
|
qs[key] = val
|
||||||
}
|
}
|
||||||
allStrings = allStrings.sort()
|
allStrings = allStrings.sort()
|
||||||
allStrings.push(values.lastfm.secret)
|
allStrings.push(values.lastfm.secret)
|
||||||
qs['api_sig'] = crypto.createHash('md5').update(allStrings.join('')).digest('hex')
|
qs.api_sig = crypto.createHash('md5').update(allStrings.join('')).digest('hex')
|
||||||
return qs
|
return qs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,18 +72,18 @@ function getAuthURL () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getSession (token) {
|
async function getSession (token) {
|
||||||
let sessSig = getAPISig({ token, method: 'auth.getSession' })
|
const sessSig = getAPISig({ token, method: 'auth.getSession' })
|
||||||
let res = await rp('http://ws.audioscrobbler.com/2.0/', { qs: sessSig })
|
const res = await rp('http://ws.audioscrobbler.com/2.0/', { qs: sessSig })
|
||||||
let rep = await parseStringPromise(res)
|
const rep = await parseStringPromise(res)
|
||||||
let name = rep.lfm.session[0].name[0]
|
const name = rep.lfm.session[0].name[0]
|
||||||
let key = rep.lfm.session[0].key[0]
|
const key = rep.lfm.session[0].key[0]
|
||||||
return { name, key }
|
return { name, key }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function storeSession (userId, session) {
|
async function storeSession (userId, session) {
|
||||||
if (!session.name || !session.key) throw new Error('Invalid session parameter.')
|
if (!session.name || !session.key) throw new Error('Invalid session parameter.')
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let existing = await db.get('SELECT * FROM LastFM WHERE userId = ?', userId)
|
const existing = await db.get('SELECT * FROM LastFM WHERE userId = ?', userId)
|
||||||
if (existing) {
|
if (existing) {
|
||||||
await db.run('UPDATE LastFM SET name = ?, key = ? WHERE userId = ?', session.name, session.key, userId)
|
await db.run('UPDATE LastFM SET name = ?, key = ? WHERE userId = ?', session.name, session.key, userId)
|
||||||
} else {
|
} else {
|
||||||
@ -200,19 +93,19 @@ async function storeSession (userId, session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function disregardSession (userId) {
|
async function disregardSession (userId) {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
return db.run('DELETE FROM LastFM WHERE userId = ?', userId)
|
return db.run('DELETE FROM LastFM WHERE userId = ?', userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSessionForUser (userId) {
|
async function getSessionForUser (userId) {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
return db.get('SELECT * FROM LastFM WHERE userId = ?', userId)
|
return db.get('SELECT * FROM LastFM WHERE userId = ?', userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function scrobbleTrack (userId, trackData) {
|
async function scrobbleTrack (userId, trackData) {
|
||||||
let sess = await getSessionForUser(userId)
|
const sess = await getSessionForUser(userId)
|
||||||
if (!sess) throw new Error('User does not have a LastFM session.')
|
if (!sess) throw new Error('User does not have a LastFM session.')
|
||||||
let scrobbleSig = getAPISig({
|
const scrobbleSig = getAPISig({
|
||||||
sk: sess.key,
|
sk: sess.key,
|
||||||
method: 'track.scrobble',
|
method: 'track.scrobble',
|
||||||
artist: trackData.artist || 'Unknown',
|
artist: trackData.artist || 'Unknown',
|
||||||
@ -228,5 +121,13 @@ async function scrobbleTrack (userId, trackData) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { search, getTrackMetaReal, invokeDownload, getAPISig, getAuthURL, getSession,
|
export default {
|
||||||
getSessionForUser, storeSession, disregardSession, scrobbleTrack }
|
search,
|
||||||
|
getAPISig,
|
||||||
|
getAuthURL,
|
||||||
|
getSession,
|
||||||
|
getSessionForUser,
|
||||||
|
storeSession,
|
||||||
|
disregardSession,
|
||||||
|
scrobbleTrack
|
||||||
|
}
|
||||||
|
@ -1,34 +1,33 @@
|
|||||||
import path from 'path'
|
|
||||||
import { dbPromise } from './database'
|
import { dbPromise } from './database'
|
||||||
|
|
||||||
async function getPlaylist (id, order = 'ntry.indx', direction = 'ASC') {
|
async function getPlaylist (id, order = 'ntry.indx', direction = 'ASC') {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let p = await db.get('SELECT * FROM Playlist WHERE id = ?', id)
|
const p = await db.get('SELECT * FROM Playlist WHERE id = ?', id)
|
||||||
if (!p) throw new Error('No such playlist!')
|
if (!p) throw new Error('No such playlist!')
|
||||||
let q = await db.all('SELECT ntry.id,ntry.trackId,ntry.playlistId,ntry.indx,ntry.userId, \
|
const 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 \
|
trck.title,trck.artist,trck.genre,trck.year,trck.duration,trck.track,trck.album
|
||||||
FROM PlaylistEntry ntry LEFT JOIN Track \
|
FROM PlaylistEntry ntry INNER JOIN Track
|
||||||
trck ON ntry.trackId = trck.id WHERE ntry.playlistId = ? ORDER BY ' + order + ' ' + direction.toUpperCase(), id)
|
trck ON ntry.trackId = trck.id WHERE ntry.playlistId = ? ORDER BY ${order} ${direction}`, id)
|
||||||
p.tracks = q || []
|
p.tracks = q || []
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPlaylists (userId) {
|
async function getPlaylists (userId) {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
return db.all('SELECT * FROM Playlist WHERE userId = ? OR userId = NULL', userId)
|
return db.all('SELECT * FROM Playlist WHERE userId = ? OR userId = NULL', userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPlaylist (userId, title) {
|
async function createPlaylist (userId, title) {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let alreadyExists = await db.get('SELECT * FROM Playlist WHERE userId = ? AND title = ?', userId, title)
|
const 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!')
|
if (alreadyExists) throw new Error('Playlist with the same name already exists!')
|
||||||
await db.run('INSERT INTO Playlist (title,userId) VALUES (?,?)', title, userId)
|
await db.run('INSERT INTO Playlist (title,userId) VALUES (?,?)', title, userId)
|
||||||
return db.get('SELECT id FROM Playlist WHERE title = ? AND userId = ?', title, userId)
|
return db.get('SELECT id FROM Playlist WHERE title = ? AND userId = ?', title, userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deletePlaylist (userId, playlistId) {
|
async function deletePlaylist (userId, playlistId) {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let ply = await db.get('SELECT * FROM Playlist WHERE id = ?', playlistId)
|
const ply = await db.get('SELECT * FROM Playlist WHERE id = ?', playlistId)
|
||||||
if (!ply) throw new Error('Could not find playlist specified.')
|
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.')
|
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 Playlist WHERE id = ?', playlistId)
|
||||||
@ -37,14 +36,14 @@ async function deletePlaylist (userId, playlistId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function addTrack (userId, playlistId, trackId) {
|
async function addTrack (userId, playlistId, trackId) {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let p = await getPlaylist(playlistId)
|
const p = await getPlaylist(playlistId)
|
||||||
if (!p) throw new Error('Invalid playlist.')
|
if (!p) throw new Error('Invalid playlist.')
|
||||||
if (p.userId != null && p.userId !== userId) throw new Error('Permission denied.')
|
if (p.userId != null && p.userId !== userId) throw new Error('Permission denied.')
|
||||||
|
|
||||||
let alreadyExists = false
|
let alreadyExists = false
|
||||||
for (let i in p.tracks) {
|
for (const i in p.tracks) {
|
||||||
let tr = p.tracks[i]
|
const tr = p.tracks[i]
|
||||||
if (tr.trackId === parseInt(trackId)) {
|
if (tr.trackId === parseInt(trackId)) {
|
||||||
alreadyExists = true
|
alreadyExists = true
|
||||||
break
|
break
|
||||||
@ -57,29 +56,29 @@ async function addTrack (userId, playlistId, trackId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function removeTrack (userId, playlistId, trackId) {
|
async function removeTrack (userId, playlistId, trackId) {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let p = await getPlaylist(playlistId)
|
const p = await getPlaylist(playlistId)
|
||||||
if (!p) throw new Error('Invalid playlist.')
|
if (!p) throw new Error('Invalid playlist.')
|
||||||
if (p.userId != null && p.userId !== userId) throw new Error('Permission denied.')
|
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)
|
const 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.')
|
if (!trackEntry) throw new Error('This track is not in the playlist.')
|
||||||
|
|
||||||
return db.run('DELETE FROM PlaylistEntry WHERE id = ?', trackEntry.id)
|
return db.run('DELETE FROM PlaylistEntry WHERE id = ?', trackEntry.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function moveTrack (userId, playlistId, trackId, position) {
|
async function moveTrack (userId, playlistId, trackId, position) {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let p = await getPlaylist(playlistId)
|
const p = await getPlaylist(playlistId)
|
||||||
if (!p) throw new Error('Invalid playlist.')
|
if (!p) throw new Error('Invalid playlist.')
|
||||||
if (p.userId != null && p.userId !== userId) throw new Error('Permission denied.')
|
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)
|
const 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.')
|
if (!trackEntry) throw new Error('This track is not in the playlist.')
|
||||||
|
|
||||||
let trcksNew = []
|
const trcksNew = []
|
||||||
for (let i in p.tracks) {
|
for (const i in p.tracks) {
|
||||||
let trck = p.tracks[i]
|
const trck = p.tracks[i]
|
||||||
if (trck.trackId === trackId) {
|
if (trck.trackId === trackId) {
|
||||||
trck.indx = position
|
trck.indx = position
|
||||||
continue
|
continue
|
||||||
@ -91,8 +90,8 @@ async function moveTrack (userId, playlistId, trackId, position) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update indexes
|
// Update indexes
|
||||||
for (let i in trcksNew) {
|
for (const i in trcksNew) {
|
||||||
let trck = trcksNew[i]
|
const trck = trcksNew[i]
|
||||||
await db.run('UPDATE PlaylistEntry SET indx = ? WHERE trackId = ? AND playlistId = ?', trck.indx, trck.trackId, playlistId)
|
await db.run('UPDATE PlaylistEntry SET indx = ? WHERE trackId = ? AND playlistId = ?', trck.indx, trck.trackId, playlistId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
114
src/server.js
114
src/server.js
@ -5,7 +5,6 @@ import bodyParser from 'body-parser'
|
|||||||
import connectSession from 'connect-redis'
|
import connectSession from 'connect-redis'
|
||||||
import redis from 'redis'
|
import redis from 'redis'
|
||||||
import http from 'http'
|
import http from 'http'
|
||||||
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 { dbPromise } from './database'
|
||||||
@ -14,6 +13,7 @@ import asn from './common/async'
|
|||||||
|
|
||||||
import playlist from './playlist'
|
import playlist from './playlist'
|
||||||
import lastfm from './lastfm'
|
import lastfm from './lastfm'
|
||||||
|
import external from './external.js'
|
||||||
|
|
||||||
require('express-async-errors')
|
require('express-async-errors')
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ const router = express.Router()
|
|||||||
const sortfields = ['id', 'track', 'artist', 'title', 'album', 'year']
|
const sortfields = ['id', 'track', 'artist', 'title', 'album', 'year']
|
||||||
const srchcategories = ['title', 'artist', 'album']
|
const srchcategories = ['title', 'artist', 'album']
|
||||||
|
|
||||||
let SessionStore = connectSession(session)
|
const 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!',
|
||||||
@ -73,17 +73,17 @@ router.get('/tracks', userMiddleware, async (req, res) => {
|
|||||||
sortdir = 'asc'
|
sortdir = 'asc'
|
||||||
}
|
}
|
||||||
|
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let count = (await db.get('SELECT COUNT(*) FROM Track'))['COUNT(*)']
|
const count = (await db.get('SELECT COUNT(*) as \'count\' FROM Track')).count
|
||||||
|
|
||||||
let pageCount = Math.ceil(count / tracksPerPage)
|
const pageCount = Math.ceil(count / tracksPerPage)
|
||||||
|
|
||||||
if (page > pageCount) page = pageCount
|
if (page > pageCount) page = pageCount
|
||||||
|
|
||||||
let offset = (page - 1) * tracksPerPage
|
const offset = (page - 1) * tracksPerPage
|
||||||
let tracks = await db.all(`SELECT * FROM Track ORDER BY ${sort} ${sortdir.toUpperCase()} LIMIT ? OFFSET ?`, tracksPerPage, offset)
|
const tracks = await db.all(`SELECT * FROM Track ORDER BY ${sort} ${sortdir} LIMIT ? OFFSET ?`, tracksPerPage, offset)
|
||||||
|
|
||||||
for (let i in tracks) {
|
for (const i in tracks) {
|
||||||
delete tracks[i].file
|
delete tracks[i].file
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,13 +93,21 @@ router.get('/tracks', userMiddleware, async (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.get('/tracks/search', userMiddleware, async (req, res) => {
|
router.get('/tracks/search', userMiddleware, async (req, res) => {
|
||||||
|
const streamable = (req.query.streamable === '1')
|
||||||
let query = req.query.q
|
let query = req.query.q
|
||||||
let streamable = (req.query.streamable === '1')
|
|
||||||
let qr = ''
|
let qr = ''
|
||||||
let exact = false
|
let exact = false
|
||||||
|
|
||||||
|
if (query.indexOf('http') !== -1) {
|
||||||
|
const result = await external.searchURL(query)
|
||||||
|
res.jsonp({
|
||||||
|
page: 1, count: 1, pageCount: 1, tracks: result
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (query.indexOf(':') !== -1) {
|
if (query.indexOf(':') !== -1) {
|
||||||
let ctr = query.split(':')
|
const ctr = query.split(':')
|
||||||
|
|
||||||
if (srchcategories.indexOf(ctr[0]) !== -1) {
|
if (srchcategories.indexOf(ctr[0]) !== -1) {
|
||||||
qr = `ifnull(${ctr[0]}, '')`
|
qr = `ifnull(${ctr[0]}, '')`
|
||||||
@ -108,8 +116,8 @@ router.get('/tracks/search', userMiddleware, async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (qr === '') {
|
if (qr === '') {
|
||||||
for (let c in srchcategories) {
|
for (const c in srchcategories) {
|
||||||
let cat = srchcategories[c]
|
const cat = srchcategories[c]
|
||||||
if (parseInt(c) !== 0) qr += ' || '
|
if (parseInt(c) !== 0) qr += ' || '
|
||||||
qr += `ifnull(${cat}, '')`
|
qr += `ifnull(${cat}, '')`
|
||||||
}
|
}
|
||||||
@ -120,7 +128,7 @@ router.get('/tracks/search', userMiddleware, async (req, res) => {
|
|||||||
exact = true
|
exact = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let original = String(query)
|
const original = String(query)
|
||||||
if (!exact) query = `%${query}%`
|
if (!exact) query = `%${query}%`
|
||||||
|
|
||||||
let sort = req.query.sort
|
let sort = req.query.sort
|
||||||
@ -139,31 +147,31 @@ router.get('/tracks/search', userMiddleware, async (req, res) => {
|
|||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let count = (await db.get(`SELECT COUNT(*) FROM Track WHERE ${qr} LIKE ?`, query))['COUNT(*)']
|
let count = (await db.get(`SELECT COUNT(*) as 'count' FROM Track WHERE ${qr} LIKE ?`, query)).count
|
||||||
let pageCount = Math.ceil(count / tracksPerPage)
|
let pageCount = Math.ceil(count / tracksPerPage)
|
||||||
|
|
||||||
if (page > pageCount) page = pageCount
|
if (page > pageCount) page = pageCount
|
||||||
|
|
||||||
let offset = (page - 1) * tracksPerPage
|
const offset = (page - 1) * tracksPerPage
|
||||||
let tracks = await db.all(`SELECT * FROM Track WHERE ${qr} LIKE ? ORDER BY ${sort} ${sortdir.toUpperCase()} LIMIT ? OFFSET ?`,
|
let tracks = await db.all(`SELECT * FROM Track WHERE ${qr} LIKE ? ORDER BY ${sort} ${sortdir} LIMIT ? OFFSET ?`,
|
||||||
query, tracksPerPage, offset)
|
query, tracksPerPage, offset)
|
||||||
|
|
||||||
let llimit = tracksPerPage - count
|
let llimit = tracksPerPage - count
|
||||||
if (streamable && page === pageCount && llimit > 1) {
|
if (streamable && page === pageCount && llimit > 1) {
|
||||||
if (llimit < 10) llimit = 10
|
if (llimit < 10) llimit = 10
|
||||||
try {
|
try {
|
||||||
let lfm = await lastfm.search(original, llimit)
|
const lfm = await lastfm.search(original, llimit)
|
||||||
if (lfm && lfm.length) {
|
if (lfm && lfm.length) {
|
||||||
tracks = tracks.concat(lfm)
|
tracks = tracks.concat(lfm)
|
||||||
count = count + lfm.length
|
count = count + lfm.length
|
||||||
if (page == 0) page = 1
|
if (page === 0) page = 1
|
||||||
if (pageCount == 0) pageCount = 1
|
if (pageCount === 0) pageCount = 1
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i in tracks) {
|
for (const i in tracks) {
|
||||||
delete tracks[i].file
|
delete tracks[i].file
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,12 +181,12 @@ router.get('/tracks/search', userMiddleware, async (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.get('/track/:id', userMiddleware, async (req, res, next) => {
|
router.get('/track/:id', userMiddleware, async (req, res, next) => {
|
||||||
let id = req.params.id
|
const id = req.params.id
|
||||||
|
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
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 external.getTrackMetaReal(id)
|
||||||
if (!track) throw new Error('404 track not found')
|
if (!track) throw new Error('404 track not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,14 +196,14 @@ router.get('/track/:id', userMiddleware, async (req, res, next) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.post('/track/:id', userMiddleware, async (req, res, next) => {
|
router.post('/track/:id', userMiddleware, async (req, res, next) => {
|
||||||
let id = req.params.id
|
const id = req.params.id
|
||||||
let meta = req.body
|
const meta = req.body
|
||||||
|
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let track = await db.get('SELECT file FROM Track WHERE id = ?', id)
|
const track = await db.get('SELECT file FROM Track WHERE id = ?', id)
|
||||||
if (!track) throw new Error('404 track not found')
|
if (!track) throw new Error('404 track not found')
|
||||||
|
|
||||||
let m = await asn.setMetadata(track.file, meta)
|
const m = await asn.setMetadata(track.file, meta)
|
||||||
await asn.updateDB(db, id, m.dbq)
|
await asn.updateDB(db, id, m.dbq)
|
||||||
|
|
||||||
res.jsonp(m)
|
res.jsonp(m)
|
||||||
@ -209,12 +217,12 @@ router.post('/track/:id', userMiddleware, async (req, res, next) => {
|
|||||||
|
|
||||||
router.post('/playlist/new', userMiddleware, async (req, res, next) => {
|
router.post('/playlist/new', userMiddleware, async (req, res, next) => {
|
||||||
if (!req.body.title) throw new Error('Title missing from body.')
|
if (!req.body.title) throw new Error('Title missing from body.')
|
||||||
let id = await playlist.createPlaylist(req.session.user, req.body.title)
|
const id = await playlist.createPlaylist(req.session.user, req.body.title)
|
||||||
res.jsonp({ success: true, playlist: id.id })
|
res.jsonp({ success: true, playlist: id.id })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/playlist/delete/:playlistId', userMiddleware, async (req, res, next) => {
|
router.post('/playlist/delete/:playlistId', userMiddleware, async (req, res, next) => {
|
||||||
let pId = req.params.playlistId
|
const pId = req.params.playlistId
|
||||||
await playlist.deletePlaylist(req.session.user, pId)
|
await playlist.deletePlaylist(req.session.user, pId)
|
||||||
res.jsonp({ success: true })
|
res.jsonp({ success: true })
|
||||||
})
|
})
|
||||||
@ -222,23 +230,23 @@ router.post('/playlist/delete/:playlistId', userMiddleware, async (req, res, nex
|
|||||||
// Playlist track endpoints
|
// Playlist track endpoints
|
||||||
|
|
||||||
router.post('/playlist/track/put/:playlistId/:trackId', userMiddleware, async (req, res, next) => {
|
router.post('/playlist/track/put/:playlistId/:trackId', userMiddleware, async (req, res, next) => {
|
||||||
let pId = req.params.playlistId
|
const pId = req.params.playlistId
|
||||||
let tId = req.params.trackId
|
const tId = req.params.trackId
|
||||||
await playlist.addTrack(req.session.user, pId, tId)
|
await playlist.addTrack(req.session.user, pId, tId)
|
||||||
res.jsonp({ success: true })
|
res.jsonp({ success: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/playlist/track/remove/:playlistId/:trackId', userMiddleware, async (req, res, next) => {
|
router.post('/playlist/track/remove/:playlistId/:trackId', userMiddleware, async (req, res, next) => {
|
||||||
let pId = req.params.playlistId
|
const pId = req.params.playlistId
|
||||||
let tId = req.params.trackId
|
const tId = req.params.trackId
|
||||||
await playlist.removeTrack(req.session.user, pId, tId)
|
await playlist.removeTrack(req.session.user, pId, tId)
|
||||||
res.jsonp({ success: true })
|
res.jsonp({ success: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/playlist/track/move/:playlistId/:trackId', userMiddleware, async (req, res, next) => {
|
router.post('/playlist/track/move/:playlistId/:trackId', userMiddleware, async (req, res, next) => {
|
||||||
let pId = req.params.playlistId
|
const pId = req.params.playlistId
|
||||||
let tId = req.params.trackId
|
const tId = req.params.trackId
|
||||||
let pos = parseInt(req.body.position)
|
const pos = parseInt(req.body.position)
|
||||||
if (!pos || isNaN(pos)) throw new Error('Invalid position.')
|
if (!pos || isNaN(pos)) throw new Error('Invalid position.')
|
||||||
await playlist.moveTrack(req.session.user, pId, tId, pos)
|
await playlist.moveTrack(req.session.user, pId, tId, pos)
|
||||||
res.jsonp({ success: true })
|
res.jsonp({ success: true })
|
||||||
@ -284,7 +292,7 @@ async function scrobble (user, track) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
router.get('/lastfm', userMiddleware, async (req, res, next) => {
|
router.get('/lastfm', userMiddleware, async (req, res, next) => {
|
||||||
let sess = await lastfm.getSessionForUser(req.session.user)
|
const sess = await lastfm.getSessionForUser(req.session.user)
|
||||||
if (sess) return res.jsonp({ connected: true, name: sess.name })
|
if (sess) return res.jsonp({ connected: true, name: sess.name })
|
||||||
res.jsonp({ connected: false })
|
res.jsonp({ connected: false })
|
||||||
})
|
})
|
||||||
@ -300,20 +308,20 @@ router.get('/lastfm/disconnect', userMiddleware, async (req, res, next) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.get('/lastfm/_redirect', userMiddleware, async (req, res, next) => {
|
router.get('/lastfm/_redirect', userMiddleware, async (req, res, next) => {
|
||||||
let token = req.query.token
|
const token = req.query.token
|
||||||
if (!token) throw new Error('Failed to get token from LastFM!')
|
if (!token) throw new Error('Failed to get token from LastFM!')
|
||||||
let session = await lastfm.getSession(token)
|
const session = await lastfm.getSession(token)
|
||||||
await lastfm.storeSession(req.session.user, session)
|
await lastfm.storeSession(req.session.user, session)
|
||||||
res.redirect('/?success=lastfm')
|
res.redirect('/?success=lastfm')
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/lastfm/scrobble/:track', userMiddleware, async (req, res, next) => {
|
router.post('/lastfm/scrobble/:track', userMiddleware, async (req, res, next) => {
|
||||||
let id = req.params.track
|
const id = req.params.track
|
||||||
let user = req.session.user
|
const user = req.session.user
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let track = await db.get('SELECT title,artist,album,duration FROM Track WHERE id = ?', id)
|
let track = await db.get('SELECT title,artist,album,duration FROM Track WHERE id = ?', id)
|
||||||
if (!track) {
|
if (!track) {
|
||||||
track = await lastfm.getTrackMetaReal(id)
|
track = await external.getTrackMetaReal(id)
|
||||||
if (!track) throw new Error('404 file not found')
|
if (!track) throw new Error('404 file not found')
|
||||||
}
|
}
|
||||||
await scrobble(user, track)
|
await scrobble(user, track)
|
||||||
@ -325,18 +333,18 @@ router.post('/lastfm/scrobble/:track', userMiddleware, async (req, res, next) =>
|
|||||||
// ------------ //
|
// ------------ //
|
||||||
|
|
||||||
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
|
const id = req.params.id
|
||||||
let dl = (req.query.dl === '1')
|
const dl = (req.query.dl === '1')
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
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 external.getTrackMetaReal(id)
|
||||||
if (!track) throw new Error('404 file not found')
|
if (!track) throw new Error('404 file not found')
|
||||||
if (dl) {
|
if (dl) {
|
||||||
lastfm.invokeDownload(id)
|
external.invokeDownload(id)
|
||||||
return res.end('<p>OK</p><script>window.close();</script>')
|
return res.end('<p>OK</p><script>window.close();</script>')
|
||||||
}
|
}
|
||||||
dev && console.log("Remote", track.file)
|
dev && console.log('Remote', track.file)
|
||||||
return ffmpeg(track.file)
|
return ffmpeg(track.file)
|
||||||
.audioCodec('libmp3lame')
|
.audioCodec('libmp3lame')
|
||||||
.format('mp3')
|
.format('mp3')
|
||||||
@ -344,7 +352,7 @@ router.get('/serve/by-id/:id', userMiddleware, async (req, res, next) => {
|
|||||||
.pipe(res, { end: true })
|
.pipe(res, { end: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
let fpath = path.resolve(track.file)
|
const fpath = path.resolve(track.file)
|
||||||
|
|
||||||
res.set('Cache-Control', 'public, max-age=31557600')
|
res.set('Cache-Control', 'public, max-age=31557600')
|
||||||
if (dl) return res.download(fpath)
|
if (dl) return res.download(fpath)
|
||||||
@ -357,7 +365,7 @@ router.get('/serve/by-id/:id', userMiddleware, async (req, res, next) => {
|
|||||||
// ---------- //
|
// ---------- //
|
||||||
|
|
||||||
router.use((err, req, res, next) => {
|
router.use((err, req, res, next) => {
|
||||||
let msg = err.message
|
const msg = err.message
|
||||||
dev && console.error(err.stack)
|
dev && console.error(err.stack)
|
||||||
res.status(msg.indexOf('404') !== -1 ? 404 : 400).jsonp({ error: err.message, stack: dev ? err.stack.toString() : undefined })
|
res.status(msg.indexOf('404') !== -1 ? 404 : 400).jsonp({ error: err.message, stack: dev ? err.stack.toString() : undefined })
|
||||||
})
|
})
|
||||||
|
50
src/user.js
50
src/user.js
@ -7,8 +7,8 @@ import { dbPromise } from './database'
|
|||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
async function userInfoPublic (id) {
|
async function userInfoPublic (id) {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let u = await db.get('SELECT id, username, image FROM User WHERE id = ?', id)
|
const u = await db.get('SELECT id, username, image FROM User WHERE id = ?', id)
|
||||||
if (!u) return {}
|
if (!u) return {}
|
||||||
return {
|
return {
|
||||||
id: u.id,
|
id: u.id,
|
||||||
@ -18,7 +18,7 @@ async function userInfoPublic (id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function userInfo (id) {
|
export async function userInfo (id) {
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
return db.get('SELECT * FROM User WHERE id = ?', id)
|
return db.get('SELECT * FROM User WHERE id = ?', id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,11 +39,11 @@ export function user (oauth, registrations) {
|
|||||||
|
|
||||||
if (!oauth) return router
|
if (!oauth) return router
|
||||||
|
|
||||||
let oauth2 = new PromiseOAuth2(oauth.clientId, oauth.clientSecret, oauth.baseUrl, oauth.authorizePath, oauth.tokenPath)
|
const oauth2 = new PromiseOAuth2(oauth.clientId, oauth.clientSecret, oauth.baseUrl, oauth.authorizePath, oauth.tokenPath)
|
||||||
|
|
||||||
router.get('/login/oauth/_redirect', async (req, res) => {
|
router.get('/login/oauth/_redirect', async (req, res) => {
|
||||||
let code = req.query.code
|
const code = req.query.code
|
||||||
let state = req.query.state
|
const state = req.query.state
|
||||||
if (!code || !state) throw new Error('Something went wrong!')
|
if (!code || !state) throw new Error('Something went wrong!')
|
||||||
if (!req.session || !req.session.oauthState || req.session.oauthState !== state) throw new Error('Possible request forgery detected! Try again.')
|
if (!req.session || !req.session.oauthState || req.session.oauthState !== state) throw new Error('Possible request forgery detected! Try again.')
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ export function user (oauth, registrations) {
|
|||||||
throw new Error('No authorization!')
|
throw new Error('No authorization!')
|
||||||
}
|
}
|
||||||
|
|
||||||
let accessToken = tokens[2].access_token
|
const accessToken = tokens[2].access_token
|
||||||
|
|
||||||
// Get user information on remote
|
// Get user information on remote
|
||||||
let userInfo
|
let userInfo
|
||||||
@ -70,8 +70,8 @@ export function user (oauth, registrations) {
|
|||||||
if (!userInfo) throw new Error('Couldn\'t get user information!')
|
if (!userInfo) throw new Error('Couldn\'t get user information!')
|
||||||
|
|
||||||
// Let's see if there's a link for this user already..
|
// Let's see if there's a link for this user already..
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let userLocal = await db.get('SELECT * FROM OAuth WHERE remoteId = ?', userInfo.id)
|
const userLocal = await db.get('SELECT * FROM OAuth WHERE remoteId = ?', userInfo.id)
|
||||||
|
|
||||||
// User and link both exist
|
// User and link both exist
|
||||||
if (userLocal) {
|
if (userLocal) {
|
||||||
@ -92,7 +92,7 @@ export function user (oauth, registrations) {
|
|||||||
// Create a new user and log in
|
// Create a new user and log in
|
||||||
await db.run('INSERT INTO User (username,email,image,created) VALUES (?,?,?,?)', userInfo.username, userInfo.email, userInfo.image, new Date())
|
await db.run('INSERT INTO User (username,email,image,created) VALUES (?,?,?,?)', userInfo.username, userInfo.email, userInfo.image, new Date())
|
||||||
|
|
||||||
let newU = await db.get('SELECT * FROM User WHERE username = ?', userInfo.username)
|
const newU = await db.get('SELECT * FROM User WHERE username = ?', userInfo.username)
|
||||||
|
|
||||||
if (!newU) throw new Error('Something went wrong!')
|
if (!newU) throw new Error('Something went wrong!')
|
||||||
|
|
||||||
@ -103,27 +103,27 @@ export function user (oauth, registrations) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.get('/login/oauth', async (req, res) => {
|
router.get('/login/oauth', async (req, res) => {
|
||||||
let state = crypto.randomBytes(16).toString('hex')
|
const state = crypto.randomBytes(16).toString('hex')
|
||||||
req.session.oauthState = state
|
req.session.oauthState = state
|
||||||
|
|
||||||
return res.redirect(oauth2.getAuthorizeUrl({
|
return res.redirect(oauth2.getAuthorizeUrl({
|
||||||
'redirect_uri': oauth.redirectUri,
|
redirect_uri: oauth.redirectUri,
|
||||||
'scope': oauth.scope,
|
scope: oauth.scope,
|
||||||
'response_type': 'code',
|
response_type: 'code',
|
||||||
'state': state
|
state: state
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
router.use('/login', async (req, res, next) => {
|
router.use('/login', async (req, res, next) => {
|
||||||
if (req.session && req.session.user) return res.redirect('/')
|
if (req.session && req.session.user) return res.redirect('/')
|
||||||
let header = req.get('authorization') || ''
|
const header = req.get('authorization') || ''
|
||||||
let token = header.split(/\s+/).pop() || ''
|
const token = header.split(/\s+/).pop() || ''
|
||||||
let auth = Buffer.from(token, 'base64').toString()
|
const auth = Buffer.from(token, 'base64').toString()
|
||||||
let parts = auth.split(/:/)
|
const parts = auth.split(/:/)
|
||||||
let username = parts[0]
|
const username = parts[0]
|
||||||
let password = parts[1]
|
const password = parts[1]
|
||||||
|
|
||||||
let message = oauth != null ? 'Enter \'oauth\' to log in remotely.' : 'Log in'
|
const message = oauth != null ? 'Enter \'oauth\' to log in remotely.' : 'Log in'
|
||||||
req.message = message
|
req.message = message
|
||||||
|
|
||||||
if ((!username || !password) && (username !== 'oauth' && oauth)) {
|
if ((!username || !password) && (username !== 'oauth' && oauth)) {
|
||||||
@ -134,8 +134,8 @@ export function user (oauth, registrations) {
|
|||||||
return res.redirect('/user/login/oauth')
|
return res.redirect('/user/login/oauth')
|
||||||
}
|
}
|
||||||
|
|
||||||
let db = await dbPromise
|
const db = await dbPromise
|
||||||
let user = await db.get('SELECT * FROM User WHERE username = ?', username)
|
const user = await db.get('SELECT * FROM User WHERE username = ?', username)
|
||||||
if (!user) return next()
|
if (!user) return next()
|
||||||
|
|
||||||
if (!user.password && oauth) {
|
if (!user.password && oauth) {
|
||||||
@ -143,7 +143,7 @@ export function user (oauth, registrations) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compare passwords
|
// Compare passwords
|
||||||
let ures = await bcrypt.compare(password, user.password)
|
const ures = await bcrypt.compare(password, user.password)
|
||||||
if (!ures) return next()
|
if (!ures) return next()
|
||||||
|
|
||||||
// Set login success
|
// Set login success
|
||||||
|
Loading…
Reference in New Issue
Block a user