btrtracks/src/server.js

229 lines
5.8 KiB
JavaScript

import path from 'path'
import sqlite from 'sqlite'
import Promise from 'bluebird'
import express from 'express'
import http from 'http'
import https from 'https'
import ffmpeg from 'fluent-ffmpeg'
import lfmda from './lastfm'
require('express-async-errors')
const values = require(path.join(process.cwd(), 'values.json'))
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 port = process.env.PORT || 3000
const dev = process.env.NODE_ENV === 'development'
const server = http.createServer(app)
const lastfm = lfmda(dbPromise)
if (dev) {
const morgan = require('morgan')
app.use(morgan('dev'))
}
const router = express.Router()
const sortfields = ['id', 'track', 'artist', 'title', 'album', 'year', 'file']
const srchcategories = ['title', 'artist', 'album']
router.get('/tracks', async (req, res) => {
let page = parseInt(req.query.page) || 1
if (isNaN(page)) {
page = 1
}
let sort = req.query.sort
if (!sort || sortfields.indexOf(sort.toLowerCase()) === -1) {
sort = 'artist'
}
let sortdir = req.query.sortdir
if (!sortdir || (sortdir !== 'desc' && sortdir !== 'asc')) {
sortdir = 'asc'
}
let db = await dbPromise
let count = (await db.get('SELECT COUNT(*) FROM Track'))['COUNT(*)']
let pageCount = Math.ceil(count / tracksPerPage)
if (page > pageCount) page = pageCount
let offset = (page - 1) * tracksPerPage
let tracks = await db.all(`SELECT * FROM Track ORDER BY ${sort} ${sortdir.toUpperCase()} LIMIT ? OFFSET ?`, tracksPerPage, offset)
for (let i in tracks) {
delete tracks[i].file
}
res.jsonp({
page, count, pageCount, tracks
})
})
router.get('/tracks/search', async (req, res) => {
let query = req.query.q
let streamable = (req.query.streamable === '1')
let qr = ''
let exact = false
if (query.indexOf(':') !== -1) {
let ctr = query.split(':')
if (srchcategories.indexOf(ctr[0]) !== -1) {
qr = `ifnull(${ctr[0]}, '')`
query = query.substring(ctr[0].length + 1)
}
}
if (qr === '') {
for (let c in srchcategories) {
let cat = srchcategories[c]
if (parseInt(c) !== 0) qr += ' || '
qr += `ifnull(${cat}, '')`
}
}
if (query.indexOf('=') !== -1 && query.indexOf('\\=') === -1) {
query = query.replace('=', '')
exact = true
}
let original = String(query)
if (!exact) query = `%${query}%`
let sort = req.query.sort
if (!sort || sortfields.indexOf(sort.toLowerCase()) === -1) {
sort = 'artist'
}
let sortdir = req.query.sortdir
if (!sortdir || (sortdir !== 'desc' && sortdir !== 'asc')) {
sortdir = 'asc'
}
// Paging
let page = parseInt(req.query.page) || 1
if (isNaN(page)) {
page = 1
}
let db = await dbPromise
let count = (await db.get(`SELECT COUNT(*) FROM Track WHERE ${qr} LIKE ?`, query))['COUNT(*)']
let pageCount = Math.ceil(count / tracksPerPage)
if (page > pageCount) page = pageCount
let offset = (page - 1) * tracksPerPage
let tracks = await db.all(`SELECT * FROM Track WHERE ${qr} LIKE ? ORDER BY ${sort} ${sortdir.toUpperCase()} LIMIT ? OFFSET ?`,
query, tracksPerPage, offset)
let llimit = tracksPerPage - count
if (streamable && page === pageCount && llimit > 1) {
if (llimit < 10) llimit = 10
try {
let lfm = await lastfm.search(original, llimit)
if (lfm && lfm.length) {
tracks = tracks.concat(lfm)
count = count + lfm.length
if (page == 0) page = 1
if (pageCount == 0) pageCount = 1
}
} catch (e) {}
}
for (let i in tracks) {
delete tracks[i].file
}
res.jsonp({
page, count, pageCount, tracks
})
})
router.get('/track/:id', async (req, res, next) => {
let id = req.params.id
let db = await dbPromise
let track = await db.get('SELECT * FROM Track WHERE id = ?', id)
if (!track) {
track = await lastfm.getTrackMetaReal(id)
if (!track) return next(new Error('404 file not found'))
}
delete track.file
res.jsonp(track)
})
router.get('/playlists', async (req, res, next) => {
let db = await dbPromise
let playlists = await db.all('SELECT * FROM Playlist')
res.jsonp(playlists)
})
router.get('/playlist/:id', async (req, res, next) => {
let id = req.params.id
let db = await dbPromise
let playlist = await db.get('SELECT title FROM Playlist WHERE id = ?', id)
if (!playlist) return next(new Error('404 file not found'))
let tracks = await db.all('SELECT trackId FROM PlaylistEntry WHERE playlistId = ?', id)
playlist.tracks = tracks
res.jsonp(playlist)
})
router.get('/serve/by-id/:id', async (req, res, next) => {
let id = req.params.id
let dl = (req.query.dl === '1')
let db = await dbPromise
let track = await db.get('SELECT file FROM Track WHERE id = ?', id)
if (!track) {
track = await lastfm.getTrackMetaReal(id)
if (!track) return next(new Error('404 file not found'))
if (dl) {
lastfm.invokeDownload(id)
return res.end('<p>OK</p><script>window.close();</script>')
}
return ffmpeg(track.file)
.audioCodec('libmp3lame')
.format('mp3')
.on('error', (e) => console.error(e))
.pipe(res, {end: true})
}
let fpath = path.resolve(track.file)
res.set('Cache-Control', 'public, max-age=31557600')
if (dl) return res.download(fpath)
res.redirect('/file/track' + fpath.substring(values.directory.length))
})
router.use((err, req, res, next) => {
console.error(err)
res.status(404).jsonp({error: 404})
})
app.use('/api', router)
app.use('/file/track', express.static(path.resolve(values.directory)))
app.use('/', express.static(path.join(process.cwd(), 'public')))
const host = process.env.NODE_ENV === 'development' ? '0.0.0.0' : '127.0.0.1'
server.listen(port, host, function () {
console.log(`app running on port ${port}`)
})