Change: audio player default volume to 100% #118, Change: username case insensitive #117, Fix: allowing multiple users of the same name, Added: experimental scan audio tracks show raw tags #114

This commit is contained in:
advplyr 2021-10-20 18:54:05 -05:00
parent 09aed354b3
commit 7d9ed75a28
9 changed files with 52 additions and 24 deletions

View File

@ -91,7 +91,7 @@ export default {
return { return {
hlsInstance: null, hlsInstance: null,
staleHlsInstance: null, staleHlsInstance: null,
volume: 0.5, volume: 1,
playbackRate: 1, playbackRate: 1,
trackWidth: 0, trackWidth: 0,
isPaused: true, isPaused: true,

View File

@ -151,9 +151,10 @@ export default {
} }
}) })
.catch((error) => { .catch((error) => {
console.error('Failed to update account', error)
this.processing = false this.processing = false
this.$toast.error('Failed to update account') console.error('Failed to update account', error)
var errMsg = error.response ? error.response.data || '' : ''
this.$toast.error(errMsg || 'Failed to update account')
}) })
}, },
submitCreateAccount() { submitCreateAccount() {
@ -176,9 +177,10 @@ export default {
} }
}) })
.catch((error) => { .catch((error) => {
console.error('Failed to create account', error)
this.processing = false this.processing = false
this.$toast.error('Failed to create account') console.error('Failed to create account', error)
var errMsg = error.response ? error.response.data || '' : ''
this.$toast.error(errMsg || 'Failed to create account')
}) })
}, },
toggleActive() { toggleActive() {

View File

@ -1,6 +1,6 @@
{ {
"name": "audiobookshelf-client", "name": "audiobookshelf-client",
"version": "1.4.12", "version": "1.4.13",
"description": "Audiobook manager and player", "description": "Audiobook manager and player",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -67,6 +67,7 @@
<th class="w-32"># From Metadata</th> <th class="w-32"># From Metadata</th>
<th class="w-32"># From Filename</th> <th class="w-32"># From Filename</th>
<th class="w-32"># From Probe</th> <th class="w-32"># From Probe</th>
<th class="w-32">Raw Tags</th>
</tr> </tr>
<tr v-for="trackData in trackNumData" :key="trackData.filename"> <tr v-for="trackData in trackNumData" :key="trackData.filename">
<td class="text-xs">{{ trackData.filename }}</td> <td class="text-xs">{{ trackData.filename }}</td>
@ -74,6 +75,7 @@
<td class="text-center">{{ trackData.trackNumFromMeta }}</td> <td class="text-center">{{ trackData.trackNumFromMeta }}</td>
<td class="text-center">{{ trackData.trackNumFromFilename }}</td> <td class="text-center">{{ trackData.trackNumFromFilename }}</td>
<td class="text-center">{{ trackData.scanDataTrackNum }}</td> <td class="text-center">{{ trackData.scanDataTrackNum }}</td>
<td class="text-left text-xs">{{ JSON.stringify(trackData.rawTags || '') }}</td>
</tr> </tr>
</table> </table>
</div> </div>

View File

@ -1,6 +1,6 @@
{ {
"name": "audiobookshelf", "name": "audiobookshelf",
"version": "1.4.12", "version": "1.4.13",
"description": "Self-hosted audiobook server for managing and playing audiobooks", "description": "Self-hosted audiobook server for managing and playing audiobooks",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -597,6 +597,13 @@ class ApiController {
return res.sendStatus(403) return res.sendStatus(403)
} }
var account = req.body var account = req.body
var username = account.username
var usernameExists = this.db.users.find(u => u.username.toLowerCase() === username.toLowerCase())
if (usernameExists) {
return res.status(500).send('Username already taken')
}
account.id = (Math.trunc(Math.random() * 1000) + Date.now()).toString(36) account.id = (Math.trunc(Math.random() * 1000) + Date.now()).toString(36)
account.pash = await this.auth.hashPass(account.password) account.pash = await this.auth.hashPass(account.password)
delete account.password delete account.password
@ -610,9 +617,7 @@ class ApiController {
user: newUser.toJSONForBrowser() user: newUser.toJSONForBrowser()
}) })
} else { } else {
res.json({ return res.status(500).send('Failed to save new user')
error: 'Failed to save new user'
})
} }
} }
@ -628,6 +633,14 @@ class ApiController {
} }
var account = req.body var account = req.body
if (account.username !== undefined && account.username !== user.username) {
var usernameExists = this.db.users.find(u => u.username.toLowerCase() === account.username.toLowerCase())
if (usernameExists) {
return res.status(500).send('Username already taken')
}
}
// Updating password // Updating password
if (account.password) { if (account.password) {
account.pash = await this.auth.hashPass(account.password) account.pash = await this.auth.hashPass(account.password)

View File

@ -97,11 +97,11 @@ class Auth {
} }
async login(req, res) { async login(req, res) {
var username = req.body.username var username = (req.body.username || '').toLowerCase()
var password = req.body.password || '' var password = req.body.password || ''
Logger.debug('Check Auth', username, !!password) Logger.debug('Check Auth', username, !!password)
var user = this.users.find(u => u.username === username) var user = this.users.find(u => u.username.toLowerCase() === username)
if (!user || !user.isActive) { if (!user || !user.isActive) {
Logger.debug(`[Auth] Failed login attempt ${req.rateLimit.current} of ${req.rateLimit.limit}`) Logger.debug(`[Auth] Failed login attempt ${req.rateLimit.current} of ${req.rateLimit.limit}`)

View File

@ -11,9 +11,9 @@ function getDefaultAudioStream(audioStreams) {
return defaultStream return defaultStream
} }
async function scan(path) { async function scan(path, verbose = false) {
Logger.debug(`Scanning path "${path}"`) Logger.debug(`Scanning path "${path}"`)
var probeData = await prober(path) var probeData = await prober(path, verbose)
if (!probeData || !probeData.audio_streams || !probeData.audio_streams.length) { if (!probeData || !probeData.audio_streams || !probeData.audio_streams.length) {
return { return {
error: 'Invalid audio file' error: 'Invalid audio file'
@ -62,6 +62,10 @@ async function scan(path) {
} }
} }
if (verbose && probeData.rawTags) {
finalData.rawTags = probeData.rawTags
}
return finalData return finalData
} }
module.exports.scan = scan module.exports.scan = scan
@ -239,7 +243,7 @@ async function scanTrackNumbers(audiobook) {
var scannedTrackNumData = [] var scannedTrackNumData = []
for (let i = 0; i < tracks.length; i++) { for (let i = 0; i < tracks.length; i++) {
var track = tracks[i] var track = tracks[i]
var scanData = await scan(track.fullPath) var scanData = await scan(track.fullPath, true)
var trackNumFromMeta = getTrackNumberFromMeta(scanData) var trackNumFromMeta = getTrackNumberFromMeta(scanData)
var book = audiobook.book || {} var book = audiobook.book || {}
@ -250,7 +254,8 @@ async function scanTrackNumbers(audiobook) {
currentTrackNum: track.index, currentTrackNum: track.index,
trackNumFromFilename, trackNumFromFilename,
trackNumFromMeta, trackNumFromMeta,
scanDataTrackNum: scanData.file_tag_track scanDataTrackNum: scanData.file_tag_track,
rawTags: scanData.rawTags || null
}) })
} }
return scannedTrackNumData return scannedTrackNumData

View File

@ -135,11 +135,13 @@ function parseChapters(chapters) {
}) })
} }
function parseTags(format) { function parseTags(format, verbose) {
if (!format.tags) { if (!format.tags) {
return {} return {}
} }
// Logger.debug('Tags', format.tags) if (verbose) {
Logger.debug('Tags', format.tags)
}
const tags = { const tags = {
file_tag_encoder: tryGrabTags(format, 'encoder', 'tsse', 'tss'), file_tag_encoder: tryGrabTags(format, 'encoder', 'tsse', 'tss'),
file_tag_encodedby: tryGrabTags(format, 'encoded_by', 'tenc', 'ten'), file_tag_encodedby: tryGrabTags(format, 'encoded_by', 'tenc', 'ten'),
@ -166,7 +168,8 @@ function parseTags(format) {
file_tag_series: tryGrabTag(format, 'series'), file_tag_series: tryGrabTag(format, 'series'),
file_tag_seriespart: tryGrabTag(format, 'series-part'), file_tag_seriespart: tryGrabTag(format, 'series-part'),
file_tag_genre1: tryGrabTags(format, 'tmp_genre1', 'genre1'), file_tag_genre1: tryGrabTags(format, 'tmp_genre1', 'genre1'),
file_tag_genre2: tryGrabTags(format, 'tmp_genre2', 'genre2') file_tag_genre2: tryGrabTags(format, 'tmp_genre2', 'genre2'),
file_tag_genre: tryGrabTags(format, 'genre', 'genre')
} }
for (const key in tags) { for (const key in tags) {
if (!tags[key]) { if (!tags[key]) {
@ -174,7 +177,7 @@ function parseTags(format) {
} }
} }
var keysToLookOutFor = ['file_tag_genre1', 'file_tag_genre2', 'file_tag_series', 'file_tag_seriespart', 'file_tag_movement', 'file_tag_movementname', 'file_tag_wwwaudiofile', 'file_tag_contentgroup', 'file_tag_releasetime'] var keysToLookOutFor = ['file_tag_genre1', 'file_tag_genre2', 'file_tag_genre', 'file_tag_series', 'file_tag_seriespart', 'file_tag_movement', 'file_tag_movementname', 'file_tag_wwwaudiofile', 'file_tag_contentgroup', 'file_tag_releasetime']
var success = keysToLookOutFor.find(key => !!tags[key]) var success = keysToLookOutFor.find(key => !!tags[key])
if (success) { if (success) {
Logger.debug('Notable!', success) Logger.debug('Notable!', success)
@ -182,7 +185,7 @@ function parseTags(format) {
return tags return tags
} }
function parseProbeData(data) { function parseProbeData(data, verbose = false) {
try { try {
var { format, streams, chapters } = data var { format, streams, chapters } = data
var { format_long_name, duration, size, bit_rate } = format var { format_long_name, duration, size, bit_rate } = format
@ -191,7 +194,7 @@ function parseProbeData(data) {
var sizeMb = sizeBytes !== null ? Number((sizeBytes / (1024 * 1024)).toFixed(2)) : null var sizeMb = sizeBytes !== null ? Number((sizeBytes / (1024 * 1024)).toFixed(2)) : null
// Logger.debug('Parsing Data for', Path.basename(format.filename)) // Logger.debug('Parsing Data for', Path.basename(format.filename))
var tags = parseTags(format) var tags = parseTags(format, verbose)
var cleanedData = { var cleanedData = {
format: format_long_name, format: format_long_name,
duration: !isNaN(duration) ? Number(duration) : null, duration: !isNaN(duration) ? Number(duration) : null,
@ -200,6 +203,9 @@ function parseProbeData(data) {
bit_rate: !isNaN(bit_rate) ? Number(bit_rate) : null, bit_rate: !isNaN(bit_rate) ? Number(bit_rate) : null,
...tags ...tags
} }
if (verbose && format.tags) {
cleanedData.rawTags = format.tags
}
const cleaned_streams = streams.map(s => parseMediaStreamInfo(s, streams, cleanedData.bit_rate)) const cleaned_streams = streams.map(s => parseMediaStreamInfo(s, streams, cleanedData.bit_rate))
cleanedData.video_stream = cleaned_streams.find(s => s.type === 'video') cleanedData.video_stream = cleaned_streams.find(s => s.type === 'video')
@ -223,14 +229,14 @@ function parseProbeData(data) {
} }
} }
function probe(filepath) { function probe(filepath, verbose = false) {
return new Promise((resolve) => { return new Promise((resolve) => {
Ffmpeg.ffprobe(filepath, ['-show_chapters'], (err, raw) => { Ffmpeg.ffprobe(filepath, ['-show_chapters'], (err, raw) => {
if (err) { if (err) {
console.error(err) console.error(err)
resolve(null) resolve(null)
} else { } else {
resolve(parseProbeData(raw)) resolve(parseProbeData(raw, verbose))
} }
}) })
}) })