use ffprobe for tags
This commit is contained in:
parent
c00c18d29f
commit
2b39800228
@ -11,12 +11,14 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"babel-plugin-transform-runtime": "^6.23.0",
|
||||||
"bluebird": "^3.5.2",
|
"bluebird": "^3.5.2",
|
||||||
"express": "^4.16.3",
|
"express": "^4.16.3",
|
||||||
"express-async-errors": "^3.0.0",
|
"express-async-errors": "^3.0.0",
|
||||||
"fs-extra": "^7.0.0",
|
"fs-extra": "^7.0.0",
|
||||||
|
"socket.io": "^2.1.1",
|
||||||
"sqlite": "^3.0.0",
|
"sqlite": "^3.0.0",
|
||||||
"babel-plugin-transform-runtime": "^6.23.0"
|
"sqlite3": "^4.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-core": "^6.26.3",
|
"babel-core": "^6.26.3",
|
||||||
|
@ -42,7 +42,7 @@ async function insertDB (db, track) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await db.run('INSERT INTO Track VALUES (NULL,?,?,?,?,?,?,?,?)',
|
await db.run('INSERT INTO Track VALUES (NULL,?,?,?,?,?,?,?,?)',
|
||||||
[track.title, track.artist, track.file, track.album || null, track.genre || null, track.track || null,
|
[track.title, track.artist, track.file, track.album || '', track.genre || null, track.track || null,
|
||||||
track.year || null, Math.floor(track.duration)])
|
track.year || null, Math.floor(track.duration)])
|
||||||
|
|
||||||
return track
|
return track
|
||||||
|
@ -82,29 +82,31 @@ function fetchVideo (data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getInfos (file) {
|
async function getInfos (file) {
|
||||||
let id3 = await asn.promiseExec(`id3 "${file}"`)
|
let formatData = await asn.promiseExec(`ffprobe -i "${file}" -show_entries format -v quiet -of json`)
|
||||||
let prds = id3.stdout.split('\n')
|
|
||||||
let data = {}
|
|
||||||
|
|
||||||
// Get id3 tags
|
let parsed = JSON.parse(formatData.stdout)
|
||||||
for (let i in prds) {
|
if (!parsed || !parsed.format) throw new Error('Failed to parse metadata!')
|
||||||
let line = prds[i]
|
parsed = parsed.format
|
||||||
let parts = line.split(': ')
|
|
||||||
if (parts.length) {
|
|
||||||
let tagtype = parts[0].toLowerCase()
|
|
||||||
let tagdata = line.substring(parts[0].length + 2)
|
|
||||||
|
|
||||||
if (tagtype === '') continue
|
let data = {
|
||||||
if (tagtype === 'metadata' && tagdata === 'none found') throw new Error(`No metadata for file "${file}"!`)
|
duration: parseFloat(parsed.duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.tags) {
|
||||||
|
for (let k in parsed.tags) {
|
||||||
|
let tagtype = k.toLowerCase()
|
||||||
|
let value = parsed.tags[k]
|
||||||
|
|
||||||
|
if (tagtype === 'date') tagtype = 'year'
|
||||||
if (tagtype === 'track' || tagtype === 'year') {
|
if (tagtype === 'track' || tagtype === 'year') {
|
||||||
if (tagdata.indexOf('/') !== -1) {
|
if (value.indexOf('/') !== -1) {
|
||||||
tagdata = tagdata.split('/')[0]
|
value = value.split('/')[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
tagdata = parseInt(tagdata)
|
value = parseInt(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
data[tagtype] = tagdata
|
data[tagtype] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,11 +115,6 @@ async function getInfos (file) {
|
|||||||
data.title = parsed.name
|
data.title = parsed.name
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get track length
|
|
||||||
let len = await asn.promiseExec(`ffprobe -i "${file}" -show_entries format=duration -v quiet -of csv="p=0"`)
|
|
||||||
len = parseFloat(len.stdout)
|
|
||||||
data.duration = len
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,11 +51,8 @@ async function interactive (fpath, db) {
|
|||||||
return track
|
return track
|
||||||
}
|
}
|
||||||
|
|
||||||
async function run () {
|
async function handlePassed (db, fpath) {
|
||||||
let db = await dbPromise
|
let filePath = path.resolve(fpath)
|
||||||
|
|
||||||
if (process.argv[2] != null) {
|
|
||||||
let filePath = path.resolve(process.argv[2])
|
|
||||||
let trackinf
|
let trackinf
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -65,14 +62,27 @@ async function run () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!trackinf) {
|
if (!trackinf) {
|
||||||
console.error('Nothing to do.')
|
throw new Error('Nothing to do.')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ins = await asn.insertDB(db, trackinf)
|
let ins = await asn.insertDB(db, trackinf)
|
||||||
if (!ins) {
|
if (!ins) {
|
||||||
console.error('A track of this description already exists in the database.')
|
throw new Error('A track of this description already exists in the database.')
|
||||||
return
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run () {
|
||||||
|
let db = await dbPromise
|
||||||
|
|
||||||
|
if (process.argv[2] != null) {
|
||||||
|
for (let i = 2; i < process.argv.length; i++) {
|
||||||
|
let f = process.argv[i]
|
||||||
|
console.log('=> Passing', f)
|
||||||
|
try {
|
||||||
|
await handlePassed(db, f)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('==>', e.message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('=> Done.')
|
console.log('=> Done.')
|
||||||
|
@ -2,6 +2,7 @@ import path from 'path'
|
|||||||
import sqlite from 'sqlite'
|
import sqlite from 'sqlite'
|
||||||
import Promise from 'bluebird'
|
import Promise from 'bluebird'
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
|
import sockets from './server/sockets'
|
||||||
|
|
||||||
require('express-async-errors')
|
require('express-async-errors')
|
||||||
|
|
||||||
@ -17,6 +18,9 @@ const port = process.env.PORT || 3000
|
|||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
|
const http = require('http').Server(app)
|
||||||
|
const io = require('socket.io')(http)
|
||||||
|
|
||||||
router.get('/tracks', async (req, res) => {
|
router.get('/tracks', async (req, res) => {
|
||||||
let page = parseInt(req.query.page) || 1
|
let page = parseInt(req.query.page) || 1
|
||||||
if (isNaN(page)) {
|
if (isNaN(page)) {
|
||||||
@ -152,6 +156,7 @@ app.use('/api', router)
|
|||||||
app.use('/file/track', express.static(path.resolve(values.directory)))
|
app.use('/file/track', express.static(path.resolve(values.directory)))
|
||||||
app.use('/', express.static(path.join(process.cwd(), 'public')))
|
app.use('/', express.static(path.join(process.cwd(), 'public')))
|
||||||
|
|
||||||
app.listen(port, '127.0.0.1', function () {
|
http.listen(port, '127.0.0.1', function () {
|
||||||
|
sockets.start(io)
|
||||||
console.log(`app running on port ${port}`)
|
console.log(`app running on port ${port}`)
|
||||||
})
|
})
|
||||||
|
141
src/server/sockets.js
Normal file
141
src/server/sockets.js
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
|
||||||
|
let data = {
|
||||||
|
clients: {},
|
||||||
|
|
||||||
|
// Key: Controller
|
||||||
|
// Value: Controlee
|
||||||
|
// Music is played on Value's device.
|
||||||
|
control: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSocketByDevice (devid) {
|
||||||
|
for (let id in data.clients) {
|
||||||
|
let sock = data.clients[i]
|
||||||
|
if (sock.device && sock.device === devid) {
|
||||||
|
return sock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function getControlling (sockid) {
|
||||||
|
for (let id in data.control) {
|
||||||
|
let cl = data.control[id]
|
||||||
|
if (cl === sockid) return id
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function start (io) {
|
||||||
|
io.on('connection', function (socket) {
|
||||||
|
let sockid = socket.id
|
||||||
|
|
||||||
|
socket.on('auth', function (device) {
|
||||||
|
if (getSocketByDevice(device) != null) {
|
||||||
|
return socket.emit('cerr', {error: 'devexists', code: 0})
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.device = device
|
||||||
|
data.clients[sockid] = socket
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('req:devs', function () {
|
||||||
|
let devices = []
|
||||||
|
|
||||||
|
for (let i in data.clients) {
|
||||||
|
let cl = data.clients[i]
|
||||||
|
if (cl.device === socket.device) continue
|
||||||
|
devices.push(devices)
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit('res:devs', devices)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Controller wants to take control of Controlee
|
||||||
|
socket.on('ctrl:connect', function (ctrlee) {
|
||||||
|
let dev = getSocketByDevice(ctrlee)
|
||||||
|
if (!dev) return socket.emit('cerr', {error: 'devnull', code: 1})
|
||||||
|
|
||||||
|
// Device is already controlling/being controlled
|
||||||
|
if (data.control[sockid] || getControlling(dev.id)) return socket.emit('cerr', {error: 'devctrl', code: 2})
|
||||||
|
|
||||||
|
dev.emit('ctrl:connect', { device: socket.device })
|
||||||
|
socket.emit('targ:success', { device: ctrlee })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Disconnect
|
||||||
|
socket.on('ctrl:disconnect', function (ctrlee) {
|
||||||
|
let id, ctrl
|
||||||
|
if (getControlling(sockid)) {
|
||||||
|
// If this is a controlee, disconnect from controller
|
||||||
|
id = getControlling(sockid)
|
||||||
|
ctrl = data.clients[id]
|
||||||
|
|
||||||
|
delete data.control[id]
|
||||||
|
} else if (data.control[sockid]) {
|
||||||
|
// If this is a controller, disconnect from controlee
|
||||||
|
id = data.control[sockid]
|
||||||
|
ctrl = data.clients[id]
|
||||||
|
|
||||||
|
delete data.control[sockid]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctrl) return
|
||||||
|
|
||||||
|
ctrl.emit('targ:disconnect', {})
|
||||||
|
})
|
||||||
|
|
||||||
|
let directpass = ['track', 'pause', 'seek', 'volume', 'mute', 'queue', 'req:queue', 'res:queue']
|
||||||
|
for (let i in directpass) {
|
||||||
|
let syn = 'sync:' + directpass[i]
|
||||||
|
|
||||||
|
// Set pause/play state
|
||||||
|
// Sent from controlee to controller
|
||||||
|
socket.on(syn, function (data) {
|
||||||
|
let ctrl
|
||||||
|
|
||||||
|
if (getControlling(sockid)) {
|
||||||
|
// If this is a controlee, send to controller
|
||||||
|
ctrl = data.clients[getControlling(sockid)]
|
||||||
|
} else if (data.control[sockid]) {
|
||||||
|
// If this is a controller, send to controlee
|
||||||
|
ctrl = data.clients[data.control[sockid]]
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl && ctrl.emit('set:' + directpass[i], data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronize timestamp
|
||||||
|
socket.on('sync:time', function (data) {
|
||||||
|
if (!getControlling(sockid)) return
|
||||||
|
let ctrl = data.clients[getControlling(sockid)]
|
||||||
|
ctrl && ctrl.emit('sync:time', data)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('disconnect', function () {
|
||||||
|
if (data.clients[sockid]) {
|
||||||
|
let dev = data.clients[sockid]
|
||||||
|
|
||||||
|
// Controller loses connection
|
||||||
|
// Emit to controlee
|
||||||
|
if (data.control[sockid]) {
|
||||||
|
let clt = data.clients[sockid]
|
||||||
|
clt && clt.emit('ctrl:disconnect', {})
|
||||||
|
|
||||||
|
// Controlee loses connection
|
||||||
|
// Emit to controller
|
||||||
|
} else if (getControlling(sockid)) {
|
||||||
|
let clt = data.clients[getControlling(sockid)]
|
||||||
|
clt && clt.emit('targ:disconnect', {})
|
||||||
|
}
|
||||||
|
|
||||||
|
delete data.clients[sockid]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { start }
|
Loading…
Reference in New Issue
Block a user