This commit is contained in:
Tian Lan 2026-08-01 10:02:28 +00:00 committed by GitHub
commit 6174c87569
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 103 additions and 1 deletions

View file

@ -16,7 +16,7 @@
<ui-multi-select-query-input ref="authorsSelect" v-model="details.authors" :label="$strings.LabelAuthors" filter-key="authors" @input="handleInputChange" />
</div>
<div class="grow px-1 mt-2 md:mt-0">
<ui-text-input-with-label ref="publishYearInput" v-model="details.publishedYear" type="number" :label="$strings.LabelPublishYear" @input="handleInputChange" />
<ui-text-input-with-label ref="publishYearInput" v-model="details.publishedYear" :label="$strings.LabelPublishYear" @input="handleInputChange" />
</div>
</div>

View file

@ -1,6 +1,7 @@
const { DataTypes, Model } = require('sequelize')
const Logger = require('../Logger')
const { getTitlePrefixAtEnd, getTitleIgnorePrefix } = require('../utils')
const { normalizePublishedYear } = require('../utils/metadataUtils')
const parseNameString = require('../utils/parsers/parseNameString')
const htmlSanitizer = require('../utils/htmlSanitizer')
const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilters')
@ -379,6 +380,10 @@ class Book extends Model {
payload.metadata[key] = String(payload.metadata[key])
}
if (key === 'publishedYear') {
payload.metadata[key] = normalizePublishedYear(payload.metadata[key])
}
if ((typeof payload.metadata[key] === 'string' || payload.metadata[key] === null) && this[key] !== payload.metadata[key]) {
// Sanitize description HTML
if (key === 'description' && payload.metadata[key]) {

View file

@ -3,6 +3,7 @@ const Path = require('path')
const sequelize = require('sequelize')
const { LogLevel } = require('../utils/constants')
const { getTitleIgnorePrefix, areEquivalent } = require('../utils/index')
const { normalizePublishedYear } = require('../utils/metadataUtils')
const parseNameString = require('../utils/parsers/parseNameString')
const parseEbookMetadata = require('../utils/parsers/parseEbookMetadata')
const globals = require('../utils/globals')
@ -691,6 +692,9 @@ class BookScanner {
}
}
// Metadata precedence may leave a full ISO date in the year-only field.
bookMetadata.publishedYear = normalizePublishedYear(bookMetadata.publishedYear)
// Set cover from library file if one is found otherwise check audiofile
if (libraryItemData.imageLibraryFiles.length) {
const coverMatch = libraryItemData.imageLibraryFiles.find((iFile) => /\/cover\.[^.\/]*$/.test(iFile.metadata.path))

View file

@ -0,0 +1,14 @@
/**
* Normalize ISO-style published dates to the book metadata year format.
*
* @param {*} value
* @returns {*}
*/
function normalizePublishedYear(value) {
if (typeof value !== 'string') return value
const match = value.match(/^(\d{4})(?:-|$)/)
return match ? match[1] : value
}
module.exports = { normalizePublishedYear }

View file

@ -0,0 +1,24 @@
const { expect } = require('chai')
const sinon = require('sinon')
const Book = require('../../../server/models/Book')
describe('Book', () => {
describe('updateFromRequest', () => {
it('normalizes an ISO date submitted as the published year', async () => {
const book = {
title: 'Test Book',
publishedYear: null,
save: sinon.stub().resolves(),
changed: sinon.stub().returns(['publishedYear'])
}
const updated = await Book.prototype.updateFromRequest.call(book, {
metadata: { publishedYear: '2013-01-01' }
})
expect(updated).to.equal(true)
expect(book.publishedYear).to.equal('2013')
expect(book.save.calledOnce).to.equal(true)
})
})
})

View file

@ -0,0 +1,33 @@
const chai = require('chai')
const expect = chai.expect
const BookScanner = require('../../../server/scanner/BookScanner')
describe('BookScanner', () => {
let serverSettings
beforeEach(() => {
serverSettings = global.ServerSettings
global.ServerSettings = { sortingPrefixes: [] }
})
afterEach(() => {
if (serverSettings === undefined) delete global.ServerSettings
else global.ServerSettings = serverSettings
})
describe('getBookMetadataFromScanData', () => {
it('normalizes an ISO date from audio metadata after sources are applied', async () => {
const libraryItemData = {
mediaMetadata: { title: 'Test Book' },
imageLibraryFiles: []
}
const libraryScan = { addLog() {} }
const metadata = await BookScanner.getBookMetadataFromScanData([{ metaTags: { tagDate: '2013-01-01' }, chapters: [] }], null, libraryItemData, libraryScan, {
metadataPrecedence: ['audioMetatags']
})
expect(metadata.publishedYear).to.equal('2013')
})
})
})

View file

@ -0,0 +1,22 @@
const chai = require('chai')
const expect = chai.expect
const { normalizePublishedYear } = require('../../../server/utils/metadataUtils')
describe('normalizePublishedYear', () => {
it('keeps a four-digit year unchanged', () => {
expect(normalizePublishedYear('2013')).to.equal('2013')
})
it('extracts the year from ISO-style dates', () => {
expect(normalizePublishedYear('2013-01')).to.equal('2013')
expect(normalizePublishedYear('2013-01-01')).to.equal('2013')
expect(normalizePublishedYear('2013-01-01T12:34:56')).to.equal('2013')
})
it('leaves unsupported values unchanged', () => {
expect(normalizePublishedYear(null)).to.equal(null)
expect(normalizePublishedYear(2013)).to.equal(2013)
expect(normalizePublishedYear('01-01-2013')).to.equal('01-01-2013')
expect(normalizePublishedYear('2013/01/01')).to.equal('2013/01/01')
})
})