Update Series and Author model to be library specific

This commit is contained in:
advplyr 2023-07-08 09:57:32 -05:00
parent 1d13d0a553
commit 0ac63b2678
10 changed files with 119 additions and 61 deletions

View File

@ -271,12 +271,16 @@ export default {
let filterValue = null let filterValue = null
if (parts.length > 1) { if (parts.length > 1) {
const decoded = this.$decode(parts[1]) const decoded = this.$decode(parts[1])
if (decoded.startsWith('aut_')) { if (parts[0] === 'authors') {
const author = this.authors.find((au) => au.id == decoded) const author = this.authors.find((au) => au.id == decoded)
if (author) filterValue = author.name if (author) filterValue = author.name
} else if (decoded.startsWith('ser_')) { } else if (parts[0] === 'series') {
const series = this.series.find((se) => se.id == decoded) if (decoded === 'no-series') {
if (series) filterValue = series.name filterValue = this.$strings.MessageNoSeries
} else {
const series = this.series.find((se) => se.id == decoded)
if (series) filterValue = series.name
}
} else { } else {
filterValue = decoded filterValue = decoded
} }

View File

@ -105,7 +105,7 @@ class LibraryItemController {
// Book specific // Book specific
if (libraryItem.isBook) { if (libraryItem.isBook) {
await this.createAuthorsAndSeriesForItemUpdate(mediaPayload) await this.createAuthorsAndSeriesForItemUpdate(mediaPayload, libraryItem.libraryId)
} }
// Podcast specific // Podcast specific
@ -342,7 +342,7 @@ class LibraryItemController {
var libraryItem = Database.libraryItems.find(_li => _li.id === updatePayloads[i].id) var libraryItem = Database.libraryItems.find(_li => _li.id === updatePayloads[i].id)
if (!libraryItem) return null if (!libraryItem) return null
await this.createAuthorsAndSeriesForItemUpdate(mediaPayload) await this.createAuthorsAndSeriesForItemUpdate(mediaPayload, libraryItem.libraryId)
var hasUpdates = libraryItem.media.update(mediaPayload) var hasUpdates = libraryItem.media.update(mediaPayload)
if (hasUpdates) { if (hasUpdates) {

View File

@ -10,7 +10,7 @@ class SeriesController {
* /api/series/:id * /api/series/:id
* *
* TODO: Update mobile app to use /api/libraries/:id/series/:seriesId API route instead * TODO: Update mobile app to use /api/libraries/:id/series/:seriesId API route instead
* Series are not library specific so we need to know what the library id is * Series are not library specific so we need to know what the library id is
* *
* @param {*} req * @param {*} req
* @param {*} res * @param {*} res

View File

@ -74,5 +74,9 @@ module.exports = (sequelize) => {
modelName: 'author' modelName: 'author'
}) })
const { library } = sequelize.models
library.hasMany(Author)
Author.belongsTo(library)
return Author return Author
} }

View File

@ -68,5 +68,9 @@ module.exports = (sequelize) => {
modelName: 'series' modelName: 'series'
}) })
const { library } = sequelize.models
library.hasMany(Series)
Series.belongsTo(library)
return Series return Series
} }

View File

@ -11,6 +11,7 @@ class Author {
this.imagePath = null this.imagePath = null
this.addedAt = null this.addedAt = null
this.updatedAt = null this.updatedAt = null
this.libraryId = null
if (author) { if (author) {
this.construct(author) this.construct(author)
@ -25,6 +26,7 @@ class Author {
this.imagePath = author.imagePath this.imagePath = author.imagePath
this.addedAt = author.addedAt this.addedAt = author.addedAt
this.updatedAt = author.updatedAt this.updatedAt = author.updatedAt
this.libraryId = author.libraryId
} }
toJSON() { toJSON() {
@ -35,7 +37,8 @@ class Author {
description: this.description, description: this.description,
imagePath: this.imagePath, imagePath: this.imagePath,
addedAt: this.addedAt, addedAt: this.addedAt,
updatedAt: this.updatedAt updatedAt: this.updatedAt,
libraryId: this.libraryId
} }
} }
@ -52,7 +55,7 @@ class Author {
} }
} }
setData(data) { setData(data, libraryId) {
this.id = uuidv4() this.id = uuidv4()
this.name = data.name this.name = data.name
this.description = data.description || null this.description = data.description || null
@ -60,6 +63,7 @@ class Author {
this.imagePath = data.imagePath || null this.imagePath = data.imagePath || null
this.addedAt = Date.now() this.addedAt = Date.now()
this.updatedAt = Date.now() this.updatedAt = Date.now()
this.libraryId = libraryId
} }
update(payload) { update(payload) {

View File

@ -7,6 +7,7 @@ class Series {
this.description = null this.description = null
this.addedAt = null this.addedAt = null
this.updatedAt = null this.updatedAt = null
this.libraryId = null
if (series) { if (series) {
this.construct(series) this.construct(series)
@ -19,6 +20,7 @@ class Series {
this.description = series.description || null this.description = series.description || null
this.addedAt = series.addedAt this.addedAt = series.addedAt
this.updatedAt = series.updatedAt this.updatedAt = series.updatedAt
this.libraryId = series.libraryId
} }
toJSON() { toJSON() {
@ -27,7 +29,8 @@ class Series {
name: this.name, name: this.name,
description: this.description, description: this.description,
addedAt: this.addedAt, addedAt: this.addedAt,
updatedAt: this.updatedAt updatedAt: this.updatedAt,
libraryId: this.libraryId
} }
} }
@ -39,12 +42,13 @@ class Series {
} }
} }
setData(data) { setData(data, libraryId) {
this.id = uuidv4() this.id = uuidv4()
this.name = data.name this.name = data.name
this.description = data.description || null this.description = data.description || null
this.addedAt = Date.now() this.addedAt = Date.now()
this.updatedAt = Date.now() this.updatedAt = Date.now()
this.libraryId = libraryId
} }
update(series) { update(series) {

View File

@ -519,7 +519,7 @@ class ApiRouter {
return listeningStats return listeningStats
} }
async createAuthorsAndSeriesForItemUpdate(mediaPayload) { async createAuthorsAndSeriesForItemUpdate(mediaPayload, libraryId) {
if (mediaPayload.metadata) { if (mediaPayload.metadata) {
const mediaMetadata = mediaPayload.metadata const mediaMetadata = mediaPayload.metadata
@ -534,10 +534,10 @@ class ApiRouter {
} }
if (!mediaMetadata.authors[i].id || mediaMetadata.authors[i].id.startsWith('new')) { if (!mediaMetadata.authors[i].id || mediaMetadata.authors[i].id.startsWith('new')) {
let author = Database.authors.find(au => au.checkNameEquals(authorName)) let author = Database.authors.find(au => au.libraryId === libraryId && au.checkNameEquals(authorName))
if (!author) { if (!author) {
author = new Author() author = new Author()
author.setData(mediaMetadata.authors[i]) author.setData(mediaMetadata.authors[i], libraryId)
Logger.debug(`[ApiRouter] Created new author "${author.name}"`) Logger.debug(`[ApiRouter] Created new author "${author.name}"`)
newAuthors.push(author) newAuthors.push(author)
} }
@ -563,10 +563,10 @@ class ApiRouter {
} }
if (!mediaMetadata.series[i].id || mediaMetadata.series[i].id.startsWith('new')) { if (!mediaMetadata.series[i].id || mediaMetadata.series[i].id.startsWith('new')) {
let seriesItem = Database.series.find(se => se.checkNameEquals(seriesName)) let seriesItem = Database.series.find(se => se.libraryId === libraryId && se.checkNameEquals(seriesName))
if (!seriesItem) { if (!seriesItem) {
seriesItem = new Series() seriesItem = new Series()
seriesItem.setData(mediaMetadata.series[i]) seriesItem.setData(mediaMetadata.series[i], libraryId)
Logger.debug(`[ApiRouter] Created new series "${seriesItem.name}"`) Logger.debug(`[ApiRouter] Created new series "${seriesItem.name}"`)
newSeries.push(seriesItem) newSeries.push(seriesItem)
} }

View File

@ -477,13 +477,13 @@ class Scanner {
// Create or match all new authors and series // Create or match all new authors and series
if (libraryItem.media.metadata.authors.some(au => au.id.startsWith('new'))) { if (libraryItem.media.metadata.authors.some(au => au.id.startsWith('new'))) {
var newAuthors = [] const newAuthors = []
libraryItem.media.metadata.authors = libraryItem.media.metadata.authors.map((tempMinAuthor) => { libraryItem.media.metadata.authors = libraryItem.media.metadata.authors.map((tempMinAuthor) => {
var _author = Database.authors.find(au => au.checkNameEquals(tempMinAuthor.name)) let _author = Database.authors.find(au => au.libraryId === libraryItem.libraryId && au.checkNameEquals(tempMinAuthor.name))
if (!_author) _author = newAuthors.find(au => au.checkNameEquals(tempMinAuthor.name)) // Check new unsaved authors if (!_author) _author = newAuthors.find(au => au.libraryId === libraryItem.libraryId && au.checkNameEquals(tempMinAuthor.name)) // Check new unsaved authors
if (!_author) { // Must create new author if (!_author) { // Must create new author
_author = new Author() _author = new Author()
_author.setData(tempMinAuthor) _author.setData(tempMinAuthor, libraryItem.libraryId)
newAuthors.push(_author) newAuthors.push(_author)
} }
@ -498,13 +498,13 @@ class Scanner {
} }
} }
if (libraryItem.media.metadata.series.some(se => se.id.startsWith('new'))) { if (libraryItem.media.metadata.series.some(se => se.id.startsWith('new'))) {
var newSeries = [] const newSeries = []
libraryItem.media.metadata.series = libraryItem.media.metadata.series.map((tempMinSeries) => { libraryItem.media.metadata.series = libraryItem.media.metadata.series.map((tempMinSeries) => {
var _series = Database.series.find(se => se.checkNameEquals(tempMinSeries.name)) let _series = Database.series.find(se => se.libraryId === libraryItem.libraryId && se.checkNameEquals(tempMinSeries.name))
if (!_series) _series = newSeries.find(se => se.checkNameEquals(tempMinSeries.name)) // Check new unsaved series if (!_series) _series = newSeries.find(se => se.libraryId === libraryItem.libraryId && se.checkNameEquals(tempMinSeries.name)) // Check new unsaved series
if (!_series) { // Must create new series if (!_series) { // Must create new series
_series = new Series() _series = new Series()
_series.setData(tempMinSeries) _series.setData(tempMinSeries, libraryItem.libraryId)
newSeries.push(_series) newSeries.push(_series)
} }
return { return {
@ -877,12 +877,11 @@ class Scanner {
matchData.author = matchData.author.split(',').map(au => au.trim()).filter(au => !!au) matchData.author = matchData.author.split(',').map(au => au.trim()).filter(au => !!au)
} }
const authorPayload = [] const authorPayload = []
for (let index = 0; index < matchData.author.length; index++) { for (const authorName of matchData.author) {
const authorName = matchData.author[index] let author = Database.authors.find(au => au.libraryId === libraryItem.libraryId && au.checkNameEquals(authorName))
var author = Database.authors.find(au => au.checkNameEquals(authorName))
if (!author) { if (!author) {
author = new Author() author = new Author()
author.setData({ name: authorName }) author.setData({ name: authorName }, libraryItem.libraryId)
await Database.createAuthor(author) await Database.createAuthor(author)
SocketAuthority.emitter('author_added', author.toJSON()) SocketAuthority.emitter('author_added', author.toJSON())
} }
@ -895,12 +894,11 @@ class Scanner {
if (matchData.series && (!libraryItem.media.metadata.seriesName || options.overrideDetails)) { if (matchData.series && (!libraryItem.media.metadata.seriesName || options.overrideDetails)) {
if (!Array.isArray(matchData.series)) matchData.series = [{ series: matchData.series, sequence: matchData.sequence }] if (!Array.isArray(matchData.series)) matchData.series = [{ series: matchData.series, sequence: matchData.sequence }]
const seriesPayload = [] const seriesPayload = []
for (let index = 0; index < matchData.series.length; index++) { for (const seriesMatchItem of matchData.series) {
const seriesMatchItem = matchData.series[index] let seriesItem = Database.series.find(se => se.libraryId === libraryItem.libraryId && se.checkNameEquals(seriesMatchItem.series))
var seriesItem = Database.series.find(au => au.checkNameEquals(seriesMatchItem.series))
if (!seriesItem) { if (!seriesItem) {
seriesItem = new Series() seriesItem = new Series()
seriesItem.setData({ name: seriesMatchItem.series }) seriesItem.setData({ name: seriesMatchItem.series }, libraryItem.libraryId)
await Database.createSeries(seriesItem) await Database.createSeries(seriesItem)
SocketAuthority.emitter('series_added', seriesItem.toJSON()) SocketAuthority.emitter('series_added', seriesItem.toJSON())
} }

View File

@ -9,8 +9,8 @@ const oldDbIdMap = {
libraries: {}, libraries: {},
libraryFolders: {}, libraryFolders: {},
libraryItems: {}, libraryItems: {},
authors: {}, authors: {}, // key is (new) library id with another map of author ids
series: {}, series: {}, // key is (new) library id with another map of series ids
collections: {}, collections: {},
podcastEpisodes: {}, podcastEpisodes: {},
books: {}, // key is library item id books: {}, // key is library item id
@ -98,10 +98,10 @@ function migrateBook(oldLibraryItem, LibraryItem) {
// Migrate BookAuthors // Migrate BookAuthors
// //
for (const oldBookAuthor of oldBook.metadata.authors) { for (const oldBookAuthor of oldBook.metadata.authors) {
if (oldDbIdMap.authors[oldBookAuthor.id]) { if (oldDbIdMap.authors[LibraryItem.libraryId][oldBookAuthor.id]) {
newRecords.bookAuthor.push({ newRecords.bookAuthor.push({
id: uuidv4(), id: uuidv4(),
authorId: oldDbIdMap.authors[oldBookAuthor.id], authorId: oldDbIdMap.authors[LibraryItem.libraryId][oldBookAuthor.id],
bookId: Book.id bookId: Book.id
}) })
} else { } else {
@ -113,11 +113,11 @@ function migrateBook(oldLibraryItem, LibraryItem) {
// Migrate BookSeries // Migrate BookSeries
// //
for (const oldBookSeries of oldBook.metadata.series) { for (const oldBookSeries of oldBook.metadata.series) {
if (oldDbIdMap.series[oldBookSeries.id]) { if (oldDbIdMap.series[LibraryItem.libraryId][oldBookSeries.id]) {
const BookSeries = { const BookSeries = {
id: uuidv4(), id: uuidv4(),
sequence: oldBookSeries.sequence, sequence: oldBookSeries.sequence,
seriesId: oldDbIdMap.series[oldBookSeries.id], seriesId: oldDbIdMap.series[LibraryItem.libraryId][oldBookSeries.id],
bookId: Book.id bookId: Book.id
} }
newRecords.bookSeries.push(BookSeries) newRecords.bookSeries.push(BookSeries)
@ -297,33 +297,66 @@ function migrateLibraries(oldLibraries) {
} }
} }
function migrateAuthors(oldAuthors) { function migrateAuthors(oldAuthors, oldLibraryItems) {
for (const oldAuthor of oldAuthors) { for (const oldAuthor of oldAuthors) {
const Author = { // Get an array of NEW library ids that have this author
id: uuidv4(), const librariesWithThisAuthor = [...new Set(oldLibraryItems.map(li => {
name: oldAuthor.name, if (!li.media.metadata.authors?.some(au => au.id === oldAuthor.id)) return null
asin: oldAuthor.asin || null, if (!oldDbIdMap.libraries[li.libraryId]) {
description: oldAuthor.description, Logger.warn(`[dbMigration] Authors library id ${li.libraryId} was not migrated`)
imagePath: oldAuthor.imagePath, }
createdAt: oldAuthor.addedAt || Date.now(), return oldDbIdMap.libraries[li.libraryId]
updatedAt: oldAuthor.updatedAt || Date.now() }).filter(lid => lid))]
if (!librariesWithThisAuthor.length) {
Logger.error(`[dbMigration] Author ${oldAuthor.name} was not found in any libraries`)
}
for (const libraryId of librariesWithThisAuthor) {
const Author = {
id: uuidv4(),
name: oldAuthor.name,
asin: oldAuthor.asin || null,
description: oldAuthor.description,
imagePath: oldAuthor.imagePath,
createdAt: oldAuthor.addedAt || Date.now(),
updatedAt: oldAuthor.updatedAt || Date.now(),
libraryId
}
if (!oldDbIdMap.authors[libraryId]) oldDbIdMap.authors[libraryId] = {}
oldDbIdMap.authors[libraryId][oldAuthor.id] = Author.id
newRecords.author.push(Author)
} }
oldDbIdMap.authors[oldAuthor.id] = Author.id
newRecords.author.push(Author)
} }
} }
function migrateSeries(oldSerieses) { function migrateSeries(oldSerieses, oldLibraryItems) {
// Originaly series were shared between libraries if they had the same name
// Series will be separate between libraries
for (const oldSeries of oldSerieses) { for (const oldSeries of oldSerieses) {
const Series = { // Get an array of NEW library ids that have this series
id: uuidv4(), const librariesWithThisSeries = [...new Set(oldLibraryItems.map(li => {
name: oldSeries.name, if (!li.media.metadata.series?.some(se => se.id === oldSeries.id)) return null
description: oldSeries.description || null, return oldDbIdMap.libraries[li.libraryId]
createdAt: oldSeries.addedAt || Date.now(), }).filter(lid => lid))]
updatedAt: oldSeries.updatedAt || Date.now()
if (!librariesWithThisSeries.length) {
Logger.error(`[dbMigration] Series ${oldSeries.name} was not found in any libraries`)
}
for (const libraryId of librariesWithThisSeries) {
const Series = {
id: uuidv4(),
name: oldSeries.name,
description: oldSeries.description || null,
createdAt: oldSeries.addedAt || Date.now(),
updatedAt: oldSeries.updatedAt || Date.now(),
libraryId
}
if (!oldDbIdMap.series[libraryId]) oldDbIdMap.series[libraryId] = {}
oldDbIdMap.series[libraryId][oldSeries.id] = Series.id
newRecords.series.push(Series)
} }
oldDbIdMap.series[oldSeries.id] = Series.id
newRecords.series.push(Series)
} }
} }
@ -615,7 +648,14 @@ function migrateFeeds(oldFeeds) {
} else if (oldFeed.entityType === 'libraryItem') { } else if (oldFeed.entityType === 'libraryItem') {
entityId = oldDbIdMap.libraryItems[oldFeed.entityId] entityId = oldDbIdMap.libraryItems[oldFeed.entityId]
} else if (oldFeed.entityType === 'series') { } else if (oldFeed.entityType === 'series') {
entityId = oldDbIdMap.series[oldFeed.entityId] // Series were split to be per library
// This will use the first series it finds
for (const libraryId in oldDbIdMap.series) {
if (oldDbIdMap.series[libraryId][oldFeed.entityId]) {
entityId = oldDbIdMap.series[libraryId][oldFeed.entityId]
break
}
}
} }
if (!entityId) { if (!entityId) {
@ -719,9 +759,9 @@ module.exports.migrate = async (DatabaseModels) => {
const start = Date.now() const start = Date.now()
migrateSettings(data.settings) migrateSettings(data.settings)
migrateAuthors(data.authors)
migrateSeries(data.series)
migrateLibraries(data.libraries) migrateLibraries(data.libraries)
migrateAuthors(data.authors, data.libraryItems)
migrateSeries(data.series, data.libraryItems)
migrateLibraryItems(data.libraryItems) migrateLibraryItems(data.libraryItems)
migrateUsers(data.users) migrateUsers(data.users)
migrateSessions(data.sessions) migrateSessions(data.sessions)