mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-12-20 19:06:06 +01:00
Add:Tools tab on library modal, api endpoint to remove all metadata files from library item folders
This commit is contained in:
parent
0d5792405f
commit
b4ce5342c0
@ -12,9 +12,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-2 md:px-4 w-full text-sm pt-2 md:pt-6 pb-20 rounded-b-lg rounded-tr-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden" style="min-height: 400px; max-height: 80vh">
|
<div class="px-2 md:px-4 w-full text-sm pt-2 md:pt-6 pb-20 rounded-b-lg rounded-tr-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden" style="min-height: 400px; max-height: 80vh">
|
||||||
<component v-if="libraryCopy && show" ref="tabComponent" :is="tabName" :is-new="!library" :library="libraryCopy" :processing.sync="processing" @update="updateLibrary" @close="show = false" />
|
<component v-if="libraryCopy && show" ref="tabComponent" :is="tabName" :is-new="!library" :library="libraryCopy" :library-id="libraryId" :processing.sync="processing" @update="updateLibrary" @close="show = false" />
|
||||||
|
|
||||||
<div class="absolute bottom-0 left-0 w-full px-4 py-4 border-t border-white border-opacity-10">
|
<div v-show="selectedTab !== 'tools'" class="absolute bottom-0 left-0 w-full px-4 py-4 border-t border-white border-opacity-10">
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<ui-btn @click="submit">{{ buttonText }}</ui-btn>
|
<ui-btn @click="submit">{{ buttonText }}</ui-btn>
|
||||||
</div>
|
</div>
|
||||||
@ -57,6 +57,9 @@ export default {
|
|||||||
mediaType() {
|
mediaType() {
|
||||||
return this.libraryCopy?.mediaType
|
return this.libraryCopy?.mediaType
|
||||||
},
|
},
|
||||||
|
libraryId() {
|
||||||
|
return this.library?.id
|
||||||
|
},
|
||||||
tabs() {
|
tabs() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -78,6 +81,11 @@ export default {
|
|||||||
id: 'schedule',
|
id: 'schedule',
|
||||||
title: this.$strings.HeaderSchedule,
|
title: this.$strings.HeaderSchedule,
|
||||||
component: 'modals-libraries-schedule-scan'
|
component: 'modals-libraries-schedule-scan'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tools',
|
||||||
|
title: this.$strings.HeaderTools,
|
||||||
|
component: 'modals-libraries-library-tools'
|
||||||
}
|
}
|
||||||
].filter((tab) => {
|
].filter((tab) => {
|
||||||
return tab.id !== 'scanner' || this.mediaType === 'book'
|
return tab.id !== 'scanner' || this.mediaType === 'book'
|
||||||
|
70
client/components/modals/libraries/LibraryTools.vue
Normal file
70
client/components/modals/libraries/LibraryTools.vue
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full h-full px-1 md:px-4 py-1 mb-4">
|
||||||
|
<ui-btn class="mb-4" @click.stop="removeAllMetadataClick('json')">Remove all metadata.json files in library item folders</ui-btn>
|
||||||
|
<ui-btn @click.stop="removeAllMetadataClick('abs')">Remove all metadata.abs files in library item folders</ui-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
library: {
|
||||||
|
type: Object,
|
||||||
|
default: () => null
|
||||||
|
},
|
||||||
|
libraryId: String,
|
||||||
|
processing: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
librarySettings() {
|
||||||
|
return this.library.settings || {}
|
||||||
|
},
|
||||||
|
mediaType() {
|
||||||
|
return this.library.mediaType
|
||||||
|
},
|
||||||
|
isBookLibrary() {
|
||||||
|
return this.mediaType === 'book'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
removeAllMetadataClick(ext) {
|
||||||
|
const payload = {
|
||||||
|
message: `Are you sure you want to remove all metadata.${ext} files in your library item folders?`,
|
||||||
|
persistent: true,
|
||||||
|
callback: (confirmed) => {
|
||||||
|
if (confirmed) {
|
||||||
|
this.removeAllMetadataInLibrary(ext)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
type: 'yesNo'
|
||||||
|
}
|
||||||
|
this.$store.commit('globals/setConfirmPrompt', payload)
|
||||||
|
},
|
||||||
|
removeAllMetadataInLibrary(ext) {
|
||||||
|
this.$emit('update:processing', true)
|
||||||
|
this.$axios
|
||||||
|
.$post(`/api/libraries/${this.libraryId}/remove-metadata?ext=${ext}`)
|
||||||
|
.then((data) => {
|
||||||
|
if (!data.found) {
|
||||||
|
this.$toast.info(`No metadata.${ext} files were found in library`)
|
||||||
|
} else if (!data.removed) {
|
||||||
|
this.$toast.success(`No metadata.${ext} files removed`)
|
||||||
|
} else {
|
||||||
|
this.$toast.success(`Successfully removed ${data.removed} metadata.${ext} files`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to remove metadata files', error)
|
||||||
|
this.$toast.error('Failed to remove metadata files')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.$emit('update:processing', false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {}
|
||||||
|
}
|
||||||
|
</script>
|
@ -854,6 +854,56 @@ class LibraryController {
|
|||||||
res.send(opmlText)
|
res.send(opmlText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all metadata.json or metadata.abs files in library item folders
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
async removeAllMetadataFiles(req, res) {
|
||||||
|
if (!req.user.isAdminOrUp) {
|
||||||
|
Logger.error(`[LibraryController] Non-admin user attempted to remove all metadata files`, req.user)
|
||||||
|
return res.sendStatus(403)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileExt = req.query.ext === 'abs' ? 'abs' : 'json'
|
||||||
|
const metadataFilename = `metadata.${fileExt}`
|
||||||
|
const libraryItemsWithMetadata = await Database.libraryItemModel.findAll({
|
||||||
|
attributes: ['id', 'libraryFiles'],
|
||||||
|
where: [
|
||||||
|
{
|
||||||
|
libraryId: req.library.id
|
||||||
|
},
|
||||||
|
Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(libraryFiles) WHERE json_valid(libraryFiles) AND json_extract(json_each.value, "$.metadata.filename") = "${metadataFilename}")`), {
|
||||||
|
[Sequelize.Op.gte]: 1
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
if (!libraryItemsWithMetadata.length) {
|
||||||
|
Logger.info(`[LibraryController] No ${metadataFilename} files found to remove`)
|
||||||
|
return res.json({
|
||||||
|
found: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.info(`[LibraryController] Found ${libraryItemsWithMetadata.length} ${metadataFilename} files to remove`)
|
||||||
|
|
||||||
|
let numRemoved = 0
|
||||||
|
for (const libraryItem of libraryItemsWithMetadata) {
|
||||||
|
const metadataFilepath = libraryItem.libraryFiles.find(lf => lf.metadata.filename === metadataFilename)?.metadata.path
|
||||||
|
if (!metadataFilepath) continue
|
||||||
|
Logger.debug(`[LibraryController] Removing file "${metadataFilepath}"`)
|
||||||
|
if ((await fileUtils.removeFile(metadataFilepath))) {
|
||||||
|
numRemoved++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
found: libraryItemsWithMetadata.length,
|
||||||
|
removed: numRemoved
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Middleware that is not using libraryItems from memory
|
* Middleware that is not using libraryItems from memory
|
||||||
* @param {import('express').Request} req
|
* @param {import('express').Request} req
|
||||||
|
@ -84,6 +84,7 @@ class ApiRouter {
|
|||||||
this.router.get('/libraries/:id/recent-episodes', LibraryController.middleware.bind(this), LibraryController.getRecentEpisodes.bind(this))
|
this.router.get('/libraries/:id/recent-episodes', LibraryController.middleware.bind(this), LibraryController.getRecentEpisodes.bind(this))
|
||||||
this.router.get('/libraries/:id/opml', LibraryController.middleware.bind(this), LibraryController.getOPMLFile.bind(this))
|
this.router.get('/libraries/:id/opml', LibraryController.middleware.bind(this), LibraryController.getOPMLFile.bind(this))
|
||||||
this.router.post('/libraries/order', LibraryController.reorder.bind(this))
|
this.router.post('/libraries/order', LibraryController.reorder.bind(this))
|
||||||
|
this.router.post('/libraries/:id/remove-metadata', LibraryController.middleware.bind(this), LibraryController.removeAllMetadataFiles.bind(this))
|
||||||
|
|
||||||
//
|
//
|
||||||
// Item Routes
|
// Item Routes
|
||||||
|
Loading…
Reference in New Issue
Block a user