mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-08-05 13:46:21 +02:00
Added server wide audiobook rating (admin only)
This commit is contained in:
parent
d21fe49ce2
commit
7c3504fe2b
2
client/assets/logos/audible.svg
Normal file
2
client/assets/logos/audible.svg
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 192 192" xmlns="http://www.w3.org/2000/svg" style="enable-background:new 0 0 192 192" xml:space="preserve"><path fill="#FF9900" d="m129.8 96.1.2-.1c3.5-2.2 4.1-7.1 1.2-10.1-5.8-6.1-16.3-14.5-31.7-16.2-25.1-2.8-42 14.8-45.8 19.2-.4.5-.5 1.3 0 1.8.5.7 1.5.8 2.1.2 4.2-3.6 14.4-11.1 29-11.7 18.1-.7 30.9 9.8 36.7 15.8 2.2 2.3 5.6 2.8 8.3 1.1z"/><path fill="#FF9900" d="M148.8 82.8c3.7-2.2 4.4-7.2 1.5-10.4-8.5-9.5-27.3-26-55.6-26.4-34.4-.3-55.2 23.4-59.7 29-.4.6-.5 1.3-.1 1.9.6.8 1.8.9 2.5.2C42.9 71.5 62.2 54.3 91 56c25.7 1.6 42.1 17.2 49 25.3 2.2 2.7 5.9 3.3 8.8 1.5z"/><path d="m22 88.8 65.2 54.3c4.4 3.7 10.8 3.8 15.3.2L170 90.7" style="fill:none;stroke:#FF9900;stroke-width:12;stroke-linecap:round;stroke-miterlimit:10"/><path fill="#FF9900" d="M109.6 106.9c3.6-2.2 4-7.3.7-10-2.9-2.3-6.8-4.7-11.9-5.7-13.4-2.6-23.6 6.4-26.4 9.2-.5.5-.6 1.2-.3 1.7.4.8 1.4 1 2.1.6 2.4-1.5 6.4-3.4 11.5-3.5 7.8-.2 13.7 3.9 17 7 2 1.8 5 2.1 7.3.7z"/></svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -21,9 +21,9 @@
|
||||
<p>{{ $strings.MessageNoResults }}</p>
|
||||
</div>
|
||||
<div v-show="!processing" class="w-full max-h-full overflow-y-auto overflow-x-hidden matchListWrapper mt-4">
|
||||
<template v-for="(res, index) in searchResults">
|
||||
<cards-book-match-card :key="index" :book="res" :current-book-duration="currentBookDuration" :is-podcast="isPodcast" :book-cover-aspect-ratio="bookCoverAspectRatio" @select="selectMatch" />
|
||||
</template>
|
||||
<div v-for="(res, index) in searchResults" :key="index">
|
||||
<cards-book-match-card :book="res" :current-book-duration="currentBookDuration" :is-podcast="isPodcast" :book-cover-aspect-ratio="bookCoverAspectRatio" @select="selectMatch" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="selectedMatchOrig" class="absolute top-0 left-0 w-full bg-bg h-full px-2 py-6 md:p-8 max-h-full overflow-y-auto overflow-x-hidden">
|
||||
<div class="flex mb-4">
|
||||
@ -225,6 +225,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedMatchOrig.rating != null" class="flex items-center py-2">
|
||||
<ui-checkbox v-model="selectedMatchUsage.rating" checkbox-bg="bg" @input="checkboxToggled" />
|
||||
<div class="grow ml-4">
|
||||
<ui-rating-input v-model="selectedMatch.rating" :disabled="!selectedMatchUsage.rating" :label="$strings.LabelRating" />
|
||||
<p v-if="mediaMetadata.rating" class="text-xs ml-1 text-white/60">
|
||||
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('rating', mediaMetadata.rating)">{{ mediaMetadata.rating }}/5</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end py-2">
|
||||
<ui-btn color="bg-success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
|
||||
</div>
|
||||
@ -234,7 +244,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UiRatingInput from '~/components/ui/RatingInput.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UiRatingInput
|
||||
},
|
||||
props: {
|
||||
processing: Boolean,
|
||||
libraryItem: {
|
||||
@ -270,6 +285,7 @@ export default {
|
||||
asin: true,
|
||||
isbn: true,
|
||||
abridged: true,
|
||||
rating: true,
|
||||
// Podcast specific
|
||||
itunesPageUrl: true,
|
||||
itunesId: true,
|
||||
@ -452,6 +468,7 @@ export default {
|
||||
asin: true,
|
||||
isbn: true,
|
||||
abridged: true,
|
||||
rating: true,
|
||||
// Podcast specific
|
||||
itunesPageUrl: true,
|
||||
itunesId: true,
|
||||
@ -534,6 +551,9 @@ export default {
|
||||
if (match.narrator && !Array.isArray(match.narrator)) {
|
||||
match.narrator = match.narrator.split(',').map((g) => g.trim())
|
||||
}
|
||||
if (match.rating) {
|
||||
match.rating = parseFloat(match.rating)
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Select Match', match)
|
||||
@ -590,6 +610,14 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.selectedMatchUsage.rating && this.selectedMatchOrig.rating) {
|
||||
updatePayload.provider_data = {
|
||||
provider: this.provider,
|
||||
providerId: this.selectedMatchOrig.asin || this.selectedMatchOrig.id,
|
||||
rating: this.selectedMatchOrig.rating
|
||||
}
|
||||
}
|
||||
|
||||
return updatePayload
|
||||
},
|
||||
async submitMatchUpdate() {
|
||||
|
107
client/components/ui/RatingInput.vue
Normal file
107
client/components/ui/RatingInput.vue
Normal file
@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="rating-input-container">
|
||||
<label v-if="label" class="px-1 text-sm font-semibold">{{ label }}</label>
|
||||
<div
|
||||
class="flex items-center"
|
||||
@mouseleave="handleMouseleave"
|
||||
>
|
||||
<div
|
||||
v-for="star in 5"
|
||||
:key="star"
|
||||
class="star-container relative"
|
||||
:data-star="star"
|
||||
@mousemove="handleMousemove"
|
||||
@click="handleClick()"
|
||||
>
|
||||
<svg class="star star-empty" viewBox="0 0 24 24">
|
||||
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z" />
|
||||
</svg>
|
||||
<svg class="star star-filled absolute top-0 left-0" :style="{ clipPath: getClipPath(star) }" viewBox="0 0 24 24">
|
||||
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="ml-2 text-white/70">{{ displayValue }}/5</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hoverValue: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
internalValue() {
|
||||
return this.hoverValue > 0 ? this.hoverValue : this.value
|
||||
},
|
||||
displayValue() {
|
||||
return this.value.toFixed(1)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
if (this.readOnly) return
|
||||
this.$emit('input', this.hoverValue)
|
||||
},
|
||||
handleMousemove(event) {
|
||||
if (this.readOnly) return
|
||||
const { left, width } = event.currentTarget.getBoundingClientRect()
|
||||
const x = event.clientX - left
|
||||
const star = parseInt(event.currentTarget.dataset.star)
|
||||
const halfWidth = width / 2
|
||||
this.hoverValue = x < halfWidth ? star - 0.5 : star
|
||||
},
|
||||
handleMouseleave() {
|
||||
if (this.readOnly) return
|
||||
this.hoverValue = 0
|
||||
},
|
||||
getClipPath(star) {
|
||||
if (this.internalValue >= star) {
|
||||
return 'inset(0 0 0 0)'
|
||||
} else if (this.internalValue > star - 1 && this.internalValue < star) {
|
||||
if (this.internalValue >= star - 0.5) {
|
||||
return 'inset(0 50% 0 0)'
|
||||
}
|
||||
}
|
||||
return 'inset(0 100% 0 0)'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.star {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.star.read-only {
|
||||
cursor: default;
|
||||
}
|
||||
.star-empty {
|
||||
fill: transparent;
|
||||
stroke: #d1d5db;
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
.star-filled {
|
||||
fill: #f59e0b;
|
||||
stroke: #f59e0b;
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
</style>
|
@ -50,29 +50,47 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap mt-2 -mx-1">
|
||||
<div class="w-full md:w-1/4 px-1">
|
||||
<div class="w-full md:w-1/2 px-1">
|
||||
<ui-text-input-with-label ref="publisherInput" v-model="details.publisher" :label="$strings.LabelPublisher" trim-whitespace @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="w-1/2 md:w-1/4 px-1 mt-2 md:mt-0">
|
||||
<div class="w-full md:w-1/2 px-1 mt-2 md:mt-0">
|
||||
<ui-text-input-with-label ref="languageInput" v-model="details.language" :label="$strings.LabelLanguage" trim-whitespace @input="handleInputChange" />
|
||||
</div>
|
||||
<div class="grow px-1 pt-6 mt-2 md:mt-0">
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap mt-2 -mx-1 items-end">
|
||||
<div class="w-full md:w-1/2 px-1">
|
||||
<div class="flex -mx-1">
|
||||
<div class="w-1/2 px-1">
|
||||
<div class="flex justify-center">
|
||||
<ui-checkbox v-model="details.explicit" :label="$strings.LabelExplicit" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grow px-1 pt-6 mt-2 md:mt-0">
|
||||
<div class="w-1/2 px-1">
|
||||
<div class="flex justify-center">
|
||||
<ui-checkbox v-model="details.abridged" :label="$strings.LabelAbridged" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 px-1 mt-2 md:mt-0">
|
||||
<div class="flex justify-center items-center">
|
||||
<label class="px-1 text-sm font-semibold mr-2">{{ $strings.LabelRating }}</label>
|
||||
<ui-rating-input v-model="details.rating" @input="handleInputChange" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UiRatingInput from '~/components/ui/RatingInput.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UiRatingInput
|
||||
},
|
||||
props: {
|
||||
libraryItem: {
|
||||
type: Object,
|
||||
@ -95,7 +113,8 @@ export default {
|
||||
asin: null,
|
||||
genres: [],
|
||||
explicit: false,
|
||||
abridged: false
|
||||
abridged: false,
|
||||
rating: 0
|
||||
},
|
||||
newTags: []
|
||||
}
|
||||
@ -285,6 +304,7 @@ export default {
|
||||
this.details.asin = this.mediaMetadata.asin || null
|
||||
this.details.explicit = !!this.mediaMetadata.explicit
|
||||
this.details.abridged = !!this.mediaMetadata.abridged
|
||||
this.details.rating = this.mediaMetadata.rating || 0
|
||||
this.newTags = [...(this.media.tags || [])]
|
||||
},
|
||||
submitForm() {
|
||||
|
@ -32,6 +32,8 @@
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<p v-if="bookTitle" class="text-gray-200 text-3xl md:text-4xl leading-snug">{{ bookTitle }}</p>
|
||||
|
||||
<p v-if="bookSubtitle" class="text-gray-200 text-xl md:text-2xl">{{ bookSubtitle }}</p>
|
||||
|
||||
<template v-for="(_series, index) in seriesList">
|
||||
@ -45,6 +47,19 @@
|
||||
</p>
|
||||
<p v-else class="mb-2 mt-0.5 text-gray-200 text-xl">by Unknown</p>
|
||||
|
||||
<div class="flex items-center space-x-4 mt-2">
|
||||
<div v-if="userRating > 0" class="flex items-center">
|
||||
<ui-rating-input :value="userRating" :label="$strings.LabelYourRating" :read-only="true" />
|
||||
</div>
|
||||
<div v-if="provider === 'audible' && providerRating != null" class="flex items-center bg-zinc-800 rounded-lg p-1.5 space-x-1.5 opacity-80">
|
||||
<img src="~/assets/logos/audible.svg" alt="Audible Logo" class="w-12 h-auto" />
|
||||
<span class="font-semibold text-white">{{ providerRating.toFixed(1) }}/5</span>
|
||||
</div>
|
||||
<div v-else-if="providerRating > 0" class="flex items-center">
|
||||
<ui-rating-input :value="providerRating" :read-only="true" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<content-library-item-details :library-item="libraryItem" />
|
||||
</div>
|
||||
<div class="hidden md:block grow" />
|
||||
@ -147,7 +162,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UiRatingInput from '~/components/ui/RatingInput.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UiRatingInput
|
||||
},
|
||||
async asyncData({ store, params, app, redirect, route }) {
|
||||
if (!store.state.user.user) {
|
||||
return redirect(`/login?redirect=${route.path}`)
|
||||
@ -309,6 +329,17 @@ export default {
|
||||
description() {
|
||||
return this.mediaMetadata.description || ''
|
||||
},
|
||||
userRating() {
|
||||
return this.mediaMetadata.rating || 0
|
||||
},
|
||||
providerRating() {
|
||||
console.log('Provider Rating: ', this.media.providerRating)
|
||||
return this.media.providerRating || 0
|
||||
},
|
||||
provider() {
|
||||
console.log('Provider: ', this.media.provider)
|
||||
return this.media.provider || null
|
||||
},
|
||||
userMediaProgress() {
|
||||
return this.$store.getters['user/getUserMediaProgress'](this.libraryItemId)
|
||||
},
|
||||
|
@ -518,6 +518,7 @@
|
||||
"LabelRSSFeedPreventIndexing": "Prevent Indexing",
|
||||
"LabelRSSFeedSlug": "RSS Feed Slug",
|
||||
"LabelRSSFeedURL": "RSS Feed URL",
|
||||
"LabelRating": "Rating",
|
||||
"LabelRandomly": "Randomly",
|
||||
"LabelReAddSeriesToContinueListening": "Re-add series to Continue Listening",
|
||||
"LabelRead": "Read",
|
||||
|
@ -1,7 +1,8 @@
|
||||
### EXAMPLE DOCKER COMPOSE ###
|
||||
services:
|
||||
audiobookshelf:
|
||||
image: ghcr.io/advplyr/audiobookshelf:latest
|
||||
#image: ghcr.io/advplyr/audiobookshelf:latest
|
||||
build: .
|
||||
# ABS runs on port 13378 by default. If you want to change
|
||||
# the port, only change the external port, not the internal port
|
||||
ports:
|
||||
|
58
server/migrations/v2.25.2-add-book-ratings.js
Normal file
58
server/migrations/v2.25.2-add-book-ratings.js
Normal file
@ -0,0 +1,58 @@
|
||||
const { DataTypes } = require('sequelize')
|
||||
|
||||
module.exports = {
|
||||
up: async ({ context: queryInterface }) => {
|
||||
const transaction = await queryInterface.sequelize.transaction()
|
||||
try {
|
||||
await queryInterface.addColumn(
|
||||
'books',
|
||||
'rating',
|
||||
{
|
||||
type: DataTypes.FLOAT
|
||||
},
|
||||
{ transaction }
|
||||
)
|
||||
await queryInterface.addColumn(
|
||||
'books',
|
||||
'providerRating',
|
||||
{
|
||||
type: DataTypes.FLOAT
|
||||
},
|
||||
{ transaction }
|
||||
)
|
||||
await queryInterface.addColumn(
|
||||
'books',
|
||||
'provider',
|
||||
{
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
{ transaction }
|
||||
)
|
||||
await queryInterface.addColumn(
|
||||
'books',
|
||||
'providerId',
|
||||
{
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
{ transaction }
|
||||
)
|
||||
await transaction.commit()
|
||||
} catch (err) {
|
||||
await transaction.rollback()
|
||||
throw err
|
||||
}
|
||||
},
|
||||
down: async ({ context: queryInterface }) => {
|
||||
const transaction = await queryInterface.sequelize.transaction()
|
||||
try {
|
||||
await queryInterface.removeColumn('books', 'rating', { transaction })
|
||||
await queryInterface.removeColumn('books', 'providerRating', { transaction })
|
||||
await queryInterface.removeColumn('books', 'provider', { transaction })
|
||||
await queryInterface.removeColumn('books', 'providerId', { transaction })
|
||||
await transaction.commit()
|
||||
} catch (err) {
|
||||
await transaction.rollback()
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
@ -130,6 +130,15 @@ class Book extends Model {
|
||||
this.authors
|
||||
/** @type {import('./Series')[]} - optional if expanded */
|
||||
this.series
|
||||
|
||||
/** @type {number} */
|
||||
this.rating
|
||||
/** @type {number} */
|
||||
this.providerRating
|
||||
/** @type {string} */
|
||||
this.provider
|
||||
/** @type {string} */
|
||||
this.providerId
|
||||
}
|
||||
|
||||
/**
|
||||
@ -159,6 +168,11 @@ class Book extends Model {
|
||||
coverPath: DataTypes.STRING,
|
||||
duration: DataTypes.FLOAT,
|
||||
|
||||
rating: DataTypes.FLOAT,
|
||||
providerRating: DataTypes.FLOAT,
|
||||
provider: DataTypes.STRING,
|
||||
providerId: DataTypes.STRING,
|
||||
|
||||
narrators: DataTypes.JSON,
|
||||
audioFiles: DataTypes.JSON,
|
||||
ebookFile: DataTypes.JSON,
|
||||
@ -357,7 +371,8 @@ class Book extends Model {
|
||||
asin: this.asin,
|
||||
language: this.language,
|
||||
explicit: !!this.explicit,
|
||||
abridged: !!this.abridged
|
||||
abridged: !!this.abridged,
|
||||
rating: this.rating
|
||||
}
|
||||
}
|
||||
|
||||
@ -405,6 +420,10 @@ class Book extends Model {
|
||||
this.abridged = !!payload.metadata.abridged
|
||||
hasUpdates = true
|
||||
}
|
||||
if (payload.metadata.rating !== undefined && this.rating !== payload.metadata.rating) {
|
||||
this.rating = payload.metadata.rating
|
||||
hasUpdates = true
|
||||
}
|
||||
const arrayOfStringsKeys = ['narrators', 'genres']
|
||||
arrayOfStringsKeys.forEach((key) => {
|
||||
if (Array.isArray(payload.metadata[key]) && !payload.metadata[key].some((item) => typeof item !== 'string') && JSON.stringify(this[key]) !== JSON.stringify(payload.metadata[key])) {
|
||||
@ -415,6 +434,21 @@ class Book extends Model {
|
||||
})
|
||||
}
|
||||
|
||||
if (payload.provider_data) {
|
||||
if (this.providerRating !== payload.provider_data.rating) {
|
||||
this.providerRating = payload.provider_data.rating
|
||||
hasUpdates = true
|
||||
}
|
||||
if (this.provider !== payload.provider_data.provider) {
|
||||
this.provider = payload.provider_data.provider
|
||||
hasUpdates = true
|
||||
}
|
||||
if (this.providerId !== payload.provider_data.providerId) {
|
||||
this.providerId = payload.provider_data.providerId
|
||||
hasUpdates = true
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(payload.tags) && !payload.tags.some((tag) => typeof tag !== 'string') && JSON.stringify(this.tags) !== JSON.stringify(payload.tags)) {
|
||||
this.tags = payload.tags
|
||||
this.changed('tags', true)
|
||||
@ -569,7 +603,8 @@ class Book extends Model {
|
||||
asin: this.asin,
|
||||
language: this.language,
|
||||
explicit: this.explicit,
|
||||
abridged: this.abridged
|
||||
abridged: this.abridged,
|
||||
rating: this.rating
|
||||
}
|
||||
}
|
||||
|
||||
@ -591,7 +626,8 @@ class Book extends Model {
|
||||
asin: this.asin,
|
||||
language: this.language,
|
||||
explicit: this.explicit,
|
||||
abridged: this.abridged
|
||||
abridged: this.abridged,
|
||||
rating: this.rating
|
||||
}
|
||||
}
|
||||
|
||||
@ -603,6 +639,7 @@ class Book extends Model {
|
||||
oldMetadataJSON.narratorName = (this.narrators || []).join(', ')
|
||||
oldMetadataJSON.seriesName = this.seriesName
|
||||
oldMetadataJSON.descriptionPlain = this.description ? htmlSanitizer.stripAllTags(this.description) : null
|
||||
oldMetadataJSON.rating = this.rating
|
||||
return oldMetadataJSON
|
||||
}
|
||||
|
||||
@ -680,7 +717,10 @@ class Book extends Model {
|
||||
ebookFile: structuredClone(this.ebookFile),
|
||||
duration: this.duration,
|
||||
size: this.size,
|
||||
tracks: this.getTracklist(libraryItemId)
|
||||
tracks: this.getTracklist(libraryItemId),
|
||||
provider: this.provider,
|
||||
providerId: this.providerId,
|
||||
providerRating: this.providerRating
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ class Scanner {
|
||||
*/
|
||||
async quickMatchBookBuildUpdatePayload(apiRouterCtx, libraryItem, matchData, options) {
|
||||
// Update media metadata if not set OR overrideDetails flag
|
||||
const detailKeysToUpdate = ['title', 'subtitle', 'description', 'narrator', 'publisher', 'publishedYear', 'genres', 'tags', 'language', 'explicit', 'abridged', 'asin', 'isbn']
|
||||
const detailKeysToUpdate = ['title', 'subtitle', 'description', 'narrator', 'publisher', 'publishedYear', 'genres', 'tags', 'language', 'explicit', 'abridged', 'asin', 'isbn', 'rating']
|
||||
const updatePayload = {}
|
||||
|
||||
for (const key in matchData) {
|
||||
@ -236,6 +236,12 @@ class Scanner {
|
||||
}
|
||||
}
|
||||
|
||||
if (matchData.rating && (!libraryItem.media.providerRating || options.overrideDetails)) {
|
||||
updatePayload.providerRating = matchData.rating
|
||||
updatePayload.provider = 'audible'
|
||||
updatePayload.providerId = matchData.asin
|
||||
}
|
||||
|
||||
// Add or set author if not set
|
||||
let hasAuthorUpdates = false
|
||||
if (matchData.author && (!libraryItem.media.authorName || options.overrideDetails)) {
|
||||
|
Loading…
Reference in New Issue
Block a user