Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace tone with ffmpeg for metadata and cover embedding #3111

Merged
merged 7 commits into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,3 @@ RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \
curl tzdata ffmpeg && \
rm -rf /var/lib/apt/lists/*

# Move tone executable to appropriate directory
COPY --from=sandreas/tone:v0.1.5 /usr/local/bin/tone /usr/local/bin/
2 changes: 0 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ RUN npm ci && npm cache clean --force
RUN npm run generate

### STAGE 1: Build server ###
FROM sandreas/tone:v0.1.5 AS tone
FROM node:20-alpine

ENV NODE_ENV=production
Expand All @@ -21,7 +20,6 @@ RUN apk update && \
g++ \
tini

COPY --from=tone /usr/local/bin/tone /usr/local/bin/
COPY --from=build /client/dist /client/dist
COPY index.js package* /
COPY server server
Expand Down
16 changes: 1 addition & 15 deletions build/debian/DEBIAN/preinst
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ install_ffmpeg() {
echo "Starting FFMPEG Install"

WGET="wget https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz --output-document=ffmpeg-git-amd64-static.tar.xz"
WGET_TONE="wget https://github.com/sandreas/tone/releases/download/v0.1.5/tone-0.1.5-linux-x64.tar.gz --output-document=tone-0.1.5-linux-x64.tar.gz"

if ! cd "$FFMPEG_INSTALL_DIR"; then
echo "Creating ffmpeg install dir at $FFMPEG_INSTALL_DIR"
Expand All @@ -63,26 +62,14 @@ install_ffmpeg() {
tar xvf ffmpeg-git-amd64-static.tar.xz --strip-components=1 --no-same-owner
rm ffmpeg-git-amd64-static.tar.xz

# Temp downloading tone library to the ffmpeg dir
echo "Getting tone.."
$WGET_TONE
tar xvf tone-0.1.5-linux-x64.tar.gz --strip-components=1 --no-same-owner
rm tone-0.1.5-linux-x64.tar.gz

echo "Good to go on Ffmpeg (& tone)... hopefully"
echo "Good to go on Ffmpeg... hopefully"
}

setup_config() {
if [ -f "$CONFIG_PATH" ]; then
echo "Existing config found."
cat $CONFIG_PATH

# TONE_PATH variable added in 2.1.6, if it doesnt exist then add it
if ! grep -q "TONE_PATH" "$CONFIG_PATH"; then
echo "Adding TONE_PATH to existing config"
echo "TONE_PATH=$FFMPEG_INSTALL_DIR/tone" >> "$CONFIG_PATH"
fi

else

if [ ! -d "$DEFAULT_DATA_DIR" ]; then
Expand All @@ -98,7 +85,6 @@ setup_config() {
CONFIG_PATH=$DEFAULT_DATA_DIR/config
FFMPEG_PATH=$FFMPEG_INSTALL_DIR/ffmpeg
FFPROBE_PATH=$FFMPEG_INSTALL_DIR/ffprobe
TONE_PATH=$FFMPEG_INSTALL_DIR/tone
PORT=$DEFAULT_PORT
HOST=$DEFAULT_HOST"

Expand Down
24 changes: 11 additions & 13 deletions client/pages/audiobook/_id/manage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
</div>
</div>

<div class="flex justify-center">
<div class="flex justify-center mb-2">
<div class="w-full max-w-2xl">
<p class="text-xl mb-1">{{ $strings.HeaderMetadataToEmbed }}</p>
<p class="mb-2 text-base text-gray-300">audiobookshelf uses <a href="https://github.com/sandreas/tone" target="_blank" class="hover:underline text-blue-400 hover:text-blue-300">tone</a> to write metadata.</p>
<p class="text-xl">{{ $strings.HeaderMetadataToEmbed }}</p>
</div>
<div class="w-full max-w-2xl"></div>
</div>
Expand All @@ -26,7 +25,7 @@
<div class="w-2/3 text-xs font-semibold uppercase text-gray-200">{{ $strings.LabelValue }}</div>
</div>
<div class="w-full max-h-72 overflow-auto">
<template v-for="(value, key, index) in toneObject">
<template v-for="(value, key, index) in metadataObject">
<div :key="key" class="flex py-1 px-4 text-sm" :class="index % 2 === 0 ? 'bg-primary bg-opacity-25' : ''">
<div class="w-1/3 font-semibold">{{ key }}</div>
<div class="w-2/3">
Expand Down Expand Up @@ -208,7 +207,7 @@ export default {
processing: false,
audiofilesEncoding: {},
audiofilesFinished: {},
toneObject: null,
metadataObject: null,
selectedTool: 'embed',
isCancelingEncode: false,
showEncodeOptions: false,
Expand Down Expand Up @@ -387,7 +386,7 @@ export default {
window.history.replaceState({ path: newurl }, '', newurl)
},
init() {
this.fetchToneObject()
this.fetchMetadataEmbedObject()
if (this.$route.query.tool === 'm4b') {
if (this.availableTools.some((t) => t.value === 'm4b')) {
this.selectedTool = 'm4b'
Expand All @@ -401,15 +400,14 @@ export default {
const shouldBackupAudioFiles = localStorage.getItem('embedMetadataShouldBackup')
this.shouldBackupAudioFiles = shouldBackupAudioFiles != 0
},
fetchToneObject() {
fetchMetadataEmbedObject() {
this.$axios
.$get(`/api/items/${this.libraryItemId}/tone-object`)
.then((toneObject) => {
delete toneObject.CoverFile
this.toneObject = toneObject
.$get(`/api/items/${this.libraryItemId}/metadata-object`)
.then((metadataObject) => {
this.metadataObject = metadataObject
})
.catch((error) => {
console.error('Failed to fetch tone object', error)
console.error('Failed to fetch metadata object', error)
})
},
taskUpdated(task) {
Expand All @@ -426,4 +424,4 @@ export default {
this.$root.socket.off('audiofile_metadata_finished', this.audiofileMetadataFinished)
}
}
</script>
</script>
6 changes: 0 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
"graceful-fs": "^4.2.10",
"htmlparser2": "^8.0.1",
"lru-cache": "^10.0.3",
"node-tone": "^1.0.1",
"nodemailer": "^6.9.13",
"openid-client": "^5.6.1",
"p-throttle": "^4.1.1",
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Audiobookshelf is a self-hosted audiobook and podcast server.
- Fetch metadata and cover art from several sources
- Chapter editor and chapter lookup (using [Audnexus API](https://audnex.us/))
- Merge your audio files into a single m4b
- Embed metadata and cover image into your audio files (using [Tone](https://github.com/sandreas/tone))
- Embed metadata and cover image into your audio files
- Basic ebook support and ereader
- Epub, pdf, cbr, cbz
- Send ebook to device (i.e. Kindle)
Expand Down
6 changes: 3 additions & 3 deletions server/controllers/LibraryItemController.js
Original file line number Diff line number Diff line change
Expand Up @@ -559,9 +559,9 @@ class LibraryItemController {
})
}

getToneMetadataObject(req, res) {
getMetadataObject(req, res) {
if (!req.user.isAdminOrUp) {
Logger.error(`[LibraryItemController] Non-admin user attempted to get tone metadata object`, req.user)
Logger.error(`[LibraryItemController] Non-admin user attempted to get metadata object`, req.user)
return res.sendStatus(403)
}

Expand All @@ -570,7 +570,7 @@ class LibraryItemController {
return res.sendStatus(500)
}

res.json(this.audioMetadataManager.getToneMetadataObjectForApi(req.libraryItem))
res.json(this.audioMetadataManager.getMetadataObjectForApi(req.libraryItem))
}

// POST: api/items/:id/chapters
Expand Down
2 changes: 1 addition & 1 deletion server/managers/AudioMetadataManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class AudioMetadataMangaer {
return this.tasksQueued.some((t) => t.data.libraryItemId === libraryItemId) || this.tasksRunning.some((t) => t.data.libraryItemId === libraryItemId)
}

getToneMetadataObjectForApi(libraryItem) {
getMetadataObjectForApi(libraryItem) {
return ffmpegHelpers.getFFMetadataObject(libraryItem, libraryItem.media.includedAudioFiles.length)
}

Expand Down
26 changes: 15 additions & 11 deletions server/objects/metadata/AudioMetaTags.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class AudioMetaTags {

// Track ID3 tag might be "3/10" or just "3"
if (this.tagTrack) {
const trackParts = this.tagTrack.split('/').map(part => Number(part))
const trackParts = this.tagTrack.split('/').map((part) => Number(part))
if (trackParts.length > 0) {
// Fractional track numbers not supported
data.number = !isNaN(trackParts[0]) ? Math.trunc(trackParts[0]) : null
Expand All @@ -81,7 +81,7 @@ class AudioMetaTags {
}

if (this.tagDisc) {
const discParts = this.tagDisc.split('/').map(p => Number(p))
const discParts = this.tagDisc.split('/').map((p) => Number(p))
if (discParts.length > 0) {
data.number = !isNaN(discParts[0]) ? Math.trunc(discParts[0]) : null
}
Expand All @@ -93,10 +93,18 @@ class AudioMetaTags {
return data
}

get discNumber() { return this.discNumAndTotal.number }
get discTotal() { return this.discNumAndTotal.total }
get trackNumber() { return this.trackNumAndTotal.number }
get trackTotal() { return this.trackNumAndTotal.total }
get discNumber() {
return this.discNumAndTotal.number
}
get discTotal() {
return this.discNumAndTotal.total
}
get trackNumber() {
return this.trackNumAndTotal.number
}
get trackTotal() {
return this.trackNumAndTotal.total
}

construct(metadata) {
this.tagAlbum = metadata.tagAlbum || null
Expand Down Expand Up @@ -177,10 +185,6 @@ class AudioMetaTags {
this.tagMusicBrainzArtistId = payload.file_tag_musicbrainz_artistid || null
}

setDataFromTone(tags) {
// TODO: Implement
}

updateData(payload) {
const dataMap = {
tagAlbum: payload.file_tag_album || null,
Expand Down Expand Up @@ -243,4 +247,4 @@ class AudioMetaTags {
return true
}
}
module.exports = AudioMetaTags
module.exports = AudioMetaTags
2 changes: 1 addition & 1 deletion server/routers/ApiRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class ApiRouter {
this.router.post('/items/:id/play/:episodeId', LibraryItemController.middleware.bind(this), LibraryItemController.startEpisodePlaybackSession.bind(this))
this.router.patch('/items/:id/tracks', LibraryItemController.middleware.bind(this), LibraryItemController.updateTracks.bind(this))
this.router.post('/items/:id/scan', LibraryItemController.middleware.bind(this), LibraryItemController.scan.bind(this))
this.router.get('/items/:id/tone-object', LibraryItemController.middleware.bind(this), LibraryItemController.getToneMetadataObject.bind(this))
this.router.get('/items/:id/metadata-object', LibraryItemController.middleware.bind(this), LibraryItemController.getMetadataObject.bind(this))
this.router.post('/items/:id/chapters', LibraryItemController.middleware.bind(this), LibraryItemController.updateMediaChapters.bind(this))
this.router.get('/items/:id/ffprobe/:fileid', LibraryItemController.middleware.bind(this), LibraryItemController.getFFprobeData.bind(this))
this.router.get('/items/:id/file/:fileid', LibraryItemController.middleware.bind(this), LibraryItemController.getLibraryFile.bind(this))
Expand Down
12 changes: 1 addition & 11 deletions server/scanner/MediaProbeData.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,5 @@ class MediaProbeData {
this.audioMetaTags = new AudioMetaTags()
this.audioMetaTags.setData(data.tags)
}

setDataFromTone(data) {
// TODO: Implement

this.format = data.format
this.duration = data.duration
this.size = data.size
this.audioMetaTags = new AudioMetaTags()
this.audioMetaTags.setDataFromTone(data.tags)
}
}
module.exports = MediaProbeData
module.exports = MediaProbeData
Loading
Loading