mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-12-20 18:59:37 +00:00
Added support for custom metadata providers
WiP but already open to feedback
This commit is contained in:
parent
8c6a2ac5dd
commit
8027c4a06f
14 changed files with 642 additions and 4 deletions
|
|
@ -109,6 +109,11 @@ export default {
|
|||
id: 'config-authentication',
|
||||
title: this.$strings.HeaderAuthentication,
|
||||
path: '/config/authentication'
|
||||
},
|
||||
{
|
||||
id: 'config-custom-metadata-providers',
|
||||
title: this.$strings.HeaderCustomMetadataProviders,
|
||||
path: '/config/custom-metadata-providers'
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
|||
104
client/components/modals/AddCustomMetadataProviderModal.vue
Normal file
104
client/components/modals/AddCustomMetadataProviderModal.vue
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<template>
|
||||
<modals-modal ref="modal" v-model="show" name="custom-metadata-provider" :width="600" :height="'unset'" :processing="processing">
|
||||
<template #outer>
|
||||
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden">
|
||||
<p class="text-3xl text-white truncate">Add custom metadata provider</p>
|
||||
</div>
|
||||
</template>
|
||||
<form @submit.prevent="submitForm">
|
||||
<div class="px-4 w-full text-sm py-6 rounded-lg bg-bg shadow-lg border border-black-300 overflow-y-auto overflow-x-hidden" style="min-height: 400px; max-height: 80vh">
|
||||
<div class="w-full p-8">
|
||||
<div class="w-full mb-4">
|
||||
<ui-text-input-with-label v-model="newName" :label="$strings.LabelName" />
|
||||
</div>
|
||||
<div class="w-full mb-4">
|
||||
<ui-text-input-with-label v-model="newUrl" :label="$strings.LabelUrl" />
|
||||
</div>
|
||||
<div class="w-full mb-4">
|
||||
<ui-text-input-with-label v-model="newApiKey" :label="$strings.LabelApiKey" type="password" />
|
||||
</div>
|
||||
<div class="flex pt-4 px-2">
|
||||
<div class="flex-grow" />
|
||||
<ui-btn color="success" type="submit">{{ $strings.ButtonAdd }}</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</modals-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
processing: false,
|
||||
newName: "",
|
||||
newUrl: "",
|
||||
newApiKey: "",
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
show: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
// Force close when navigating - used in the table
|
||||
if (this.$refs.modal) this.$refs.modal.setHide()
|
||||
},
|
||||
submitForm() {
|
||||
if (!this.newName || !this.newUrl || !this.newApiKey) {
|
||||
this.$toast.error('Must add name, url and API key')
|
||||
return
|
||||
}
|
||||
|
||||
this.processing = true
|
||||
this.$axios
|
||||
.$patch('/api/custom-metadata-providers/admin', {
|
||||
name: this.newName,
|
||||
url: this.newUrl,
|
||||
apiKey: this.newApiKey,
|
||||
})
|
||||
.then((data) => {
|
||||
this.processing = false
|
||||
if (data.error) {
|
||||
this.$toast.error(`Failed to add provider: ${data.error}`)
|
||||
} else {
|
||||
this.$toast.success('New provider added')
|
||||
this.show = false
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.processing = false
|
||||
console.error('Failed to add provider', error)
|
||||
this.$toast.error('Failed to add provider')
|
||||
})
|
||||
},
|
||||
init() {
|
||||
this.processing = false
|
||||
this.newName = ""
|
||||
this.newUrl = ""
|
||||
this.newApiKey = ""
|
||||
}
|
||||
},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
||||
150
client/components/tables/CustomMetadataProviderTable.vue
Normal file
150
client/components/tables/CustomMetadataProviderTable.vue
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="text-center">
|
||||
<table id="providers">
|
||||
<tr>
|
||||
<th>{{ $strings.LabelName }}</th>
|
||||
<th>{{ $strings.LabelUrl }}</th>
|
||||
<th>{{ $strings.LabelApiKey }}</th>
|
||||
<th class="w-12"></th>
|
||||
</tr>
|
||||
<tr v-for="provider in providers" :key="provider.id">
|
||||
<td class="text-sm">{{ provider.name }}</td>
|
||||
<td class="text-sm">{{ provider.url }}</td>
|
||||
<td class="text-sm">
|
||||
<span class="custom-provider-api-key">{{ provider.apiKey }}</span>
|
||||
</td>
|
||||
<td class="py-0">
|
||||
<div class="h-8 w-8 flex items-center justify-center text-white text-opacity-50 hover:text-error cursor-pointer" @click.stop="removeProvider(provider)">
|
||||
<button type="button" :aria-label="$strings.ButtonDelete" class="material-icons text-base">delete</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
providers: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addedProvider(provider) {
|
||||
if (!Array.isArray(this.providers)) return
|
||||
|
||||
this.providers.push(provider)
|
||||
},
|
||||
removedProvider(provider) {
|
||||
this.providers = this.providers.filter((p) => p.id !== provider.id)
|
||||
},
|
||||
removeProvider(provider) {
|
||||
this.$axios
|
||||
.$delete(`/api/custom-metadata-providers/admin/${provider.id}`)
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
this.$toast.error(`Failed to remove provider: ${data.error}`)
|
||||
} else {
|
||||
this.$toast.success('Provider removed')
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to remove provider', error)
|
||||
this.$toast.error('Failed to remove provider')
|
||||
})
|
||||
},
|
||||
loadProviders() {
|
||||
this.$axios.$get('/api/custom-metadata-providers/admin')
|
||||
.then((res) => {
|
||||
this.providers = res.providers
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed', error)
|
||||
})
|
||||
},
|
||||
init(attempts = 0) {
|
||||
if (!this.$root.socket) {
|
||||
if (attempts > 10) {
|
||||
return console.error('Failed to setup socket listeners')
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.init(++attempts)
|
||||
}, 250)
|
||||
return
|
||||
}
|
||||
this.$root.socket.on('custom_metadata_provider_added', this.addedProvider)
|
||||
this.$root.socket.on('custom_metadata_provider_removed', this.removedProvider)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadProviders()
|
||||
this.init()
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.$refs.addModal) {
|
||||
this.$refs.addModal.close()
|
||||
}
|
||||
|
||||
if (this.$root.socket) {
|
||||
this.$root.socket.off('custom_metadata_provider_added', this.addedProvider)
|
||||
this.$root.socket.off('custom_metadata_provider_removed', this.removedProvider)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#providers {
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #474747;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#providers td,
|
||||
#providers th {
|
||||
/* border: 1px solid #2e2e2e; */
|
||||
padding: 8px 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#providers td.py-0 {
|
||||
padding: 0px 8px;
|
||||
}
|
||||
|
||||
#providers tr:nth-child(even) {
|
||||
background-color: #373838;
|
||||
}
|
||||
|
||||
#providers tr:nth-child(odd) {
|
||||
background-color: #2f2f2f;
|
||||
}
|
||||
|
||||
#providers tr:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
#providers th {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
background-color: #272727;
|
||||
}
|
||||
|
||||
.custom-provider-api-key {
|
||||
padding: 1px;
|
||||
background-color: #272727;
|
||||
border-radius: 4px;
|
||||
color: transparent;
|
||||
transition: color, background-color 0.5s ease;
|
||||
}
|
||||
|
||||
.custom-provider-api-key:hover {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue