mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-05-31 12:15:49 -04:00
API route to generate waveform images
This commit is contained in:
parent
4db26f9f79
commit
74652e2e54
@ -94,9 +94,16 @@
|
|||||||
<span class="material-icons-outlined text-lg">error_outline</span>
|
<span class="material-icons-outlined text-lg">error_outline</span>
|
||||||
</button>
|
</button>
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
|
<button class="w-7 h-7 rounded-full flex items-center justify-center text-white" @click="setShowWaveform(chapter.id)">
|
||||||
|
<span class="material-icons-outlined text-lg">graphic_eq</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="showWaveform[chapter.id]" :key="`${chapter.id}-waveform`">
|
||||||
|
<img :src="`${baseUrl}/api/tools/item/${libraryItem.id}/waveform?start=${Math.max(0, chapter.start - 10)}&end=${Math.min(mediaDuration, chapter.start + 10)}&token=${userToken}`" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -246,7 +253,8 @@ export default {
|
|||||||
chapterData: null,
|
chapterData: null,
|
||||||
showSecondInputs: false,
|
showSecondInputs: false,
|
||||||
audibleRegions: ['US', 'CA', 'UK', 'AU', 'FR', 'DE', 'JP', 'IT', 'IN', 'ES'],
|
audibleRegions: ['US', 'CA', 'UK', 'AU', 'FR', 'DE', 'JP', 'IT', 'IN', 'ES'],
|
||||||
hasChanges: false
|
hasChanges: false,
|
||||||
|
showWaveform: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -256,6 +264,9 @@ export default {
|
|||||||
userToken() {
|
userToken() {
|
||||||
return this.$store.getters['user/getToken']
|
return this.$store.getters['user/getToken']
|
||||||
},
|
},
|
||||||
|
baseUrl() {
|
||||||
|
return process.env.serverUrl
|
||||||
|
},
|
||||||
media() {
|
media() {
|
||||||
return this.libraryItem.media || {}
|
return this.libraryItem.media || {}
|
||||||
},
|
},
|
||||||
@ -288,6 +299,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setShowWaveform(chapterId) {
|
||||||
|
this.$set(this.showWaveform, chapterId, true)
|
||||||
|
},
|
||||||
setChaptersFromTracks() {
|
setChaptersFromTracks() {
|
||||||
let currentStartTime = 0
|
let currentStartTime = 0
|
||||||
let index = 0
|
let index = 0
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
|
const ffmpegHelpers = require('../utils/ffmpegHelpers')
|
||||||
|
|
||||||
class ToolsController {
|
class ToolsController {
|
||||||
constructor() { }
|
constructor() { }
|
||||||
@ -98,6 +99,32 @@ class ToolsController {
|
|||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAudioFileWaveform(req, res) {
|
||||||
|
let start = Number(req.query.start || 0)
|
||||||
|
let end = Number(req.query.end || 0)
|
||||||
|
if (isNaN(start) || isNaN(end) || start < 0 || end > req.libraryItem.media.duration || end <= start || end - start < 5) {
|
||||||
|
return res.status(400).send('Invalid start/end query params')
|
||||||
|
}
|
||||||
|
|
||||||
|
const paths = []
|
||||||
|
let currentTime = 0
|
||||||
|
let startOffset = 0
|
||||||
|
for (const track of req.libraryItem.media.tracks) {
|
||||||
|
currentTime += track.duration
|
||||||
|
if (currentTime > start) {
|
||||||
|
if (!paths.length) startOffset = track.startOffset
|
||||||
|
paths.push(track.metadata.path)
|
||||||
|
}
|
||||||
|
if (currentTime > end) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start -= startOffset
|
||||||
|
end -= startOffset
|
||||||
|
|
||||||
|
ffmpegHelpers.generateWaveform(paths, start, end, res)
|
||||||
|
}
|
||||||
|
|
||||||
middleware(req, res, next) {
|
middleware(req, res, next) {
|
||||||
if (!req.user.isAdminOrUp) {
|
if (!req.user.isAdminOrUp) {
|
||||||
Logger.error(`[LibraryItemController] Non-root user attempted to access tools route`, req.user)
|
Logger.error(`[LibraryItemController] Non-root user attempted to access tools route`, req.user)
|
||||||
|
@ -275,6 +275,7 @@ class ApiRouter {
|
|||||||
this.router.delete('/tools/item/:id/encode-m4b', ToolsController.middleware.bind(this), ToolsController.cancelM4bEncode.bind(this))
|
this.router.delete('/tools/item/:id/encode-m4b', ToolsController.middleware.bind(this), ToolsController.cancelM4bEncode.bind(this))
|
||||||
this.router.post('/tools/item/:id/embed-metadata', ToolsController.middleware.bind(this), ToolsController.embedAudioFileMetadata.bind(this))
|
this.router.post('/tools/item/:id/embed-metadata', ToolsController.middleware.bind(this), ToolsController.embedAudioFileMetadata.bind(this))
|
||||||
this.router.post('/tools/batch/embed-metadata', ToolsController.middleware.bind(this), ToolsController.batchEmbedMetadata.bind(this))
|
this.router.post('/tools/batch/embed-metadata', ToolsController.middleware.bind(this), ToolsController.batchEmbedMetadata.bind(this))
|
||||||
|
this.router.get('/tools/item/:id/waveform', ToolsController.middleware.bind(this), ToolsController.getAudioFileWaveform.bind(this))
|
||||||
|
|
||||||
//
|
//
|
||||||
// RSS Feed Routes (Admin and up)
|
// RSS Feed Routes (Admin and up)
|
||||||
|
@ -152,3 +152,26 @@ module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => {
|
|||||||
ffmpeg.run()
|
ffmpeg.run()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.generateWaveform = (filepaths, start, end, res) => {
|
||||||
|
let ffmpeg = null
|
||||||
|
if (filepaths.length === 1) ffmpeg = Ffmpeg(filepaths[0])
|
||||||
|
else {
|
||||||
|
ffmpeg = Ffmpeg(`concat:${filepaths.join('|')}`)
|
||||||
|
}
|
||||||
|
ffmpeg.inputOptions('-ss', start)
|
||||||
|
ffmpeg.inputOptions('-to', end)
|
||||||
|
ffmpeg.complexFilter('aformat=channel_layouts=mono,showwavespic=s=1280x240')
|
||||||
|
ffmpeg.frames(1)
|
||||||
|
ffmpeg.format('image2pipe')
|
||||||
|
ffmpeg.on('start', (cmd) => {
|
||||||
|
Logger.debug(`[FfmpegHelpers] generateWaveform: Cmd: ${cmd}`)
|
||||||
|
})
|
||||||
|
ffmpeg.on('error', (error) => {
|
||||||
|
Logger.error(`[FfmpegHelpers] generateWaveform: Error`, error)
|
||||||
|
}).on('end', () => {
|
||||||
|
Logger.debug(`[FfmpegHelpers] generateWaveform finished`)
|
||||||
|
}).pipe(res, {
|
||||||
|
end: true
|
||||||
|
})
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user