mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-12-20 19:06:06 +01:00
Update model casing & associations
This commit is contained in:
parent
2131a65299
commit
54ca58e610
@ -12,12 +12,12 @@ class Database {
|
||||
return this.sequelize?.models || {}
|
||||
}
|
||||
|
||||
async init() {
|
||||
async init(force = false) {
|
||||
if (!await this.connect()) {
|
||||
throw new Error('Database connection failed')
|
||||
}
|
||||
|
||||
await this.buildModels()
|
||||
await this.buildModels(force)
|
||||
Logger.info(`[Database] Db initialized`, Object.keys(this.sequelize.models))
|
||||
}
|
||||
|
||||
@ -30,6 +30,9 @@ class Database {
|
||||
logging: false
|
||||
})
|
||||
|
||||
// Helper function
|
||||
this.sequelize.uppercaseFirst = str => str ? `${str[0].toUpperCase()}${str.substr(1)}` : ''
|
||||
|
||||
try {
|
||||
await this.sequelize.authenticate()
|
||||
Logger.info(`[Database] Db connection was successful`)
|
||||
@ -40,15 +43,15 @@ class Database {
|
||||
}
|
||||
}
|
||||
|
||||
buildModels() {
|
||||
buildModels(force = false) {
|
||||
require('./models/User')(this.sequelize)
|
||||
require('./models/FileMetadata')(this.sequelize)
|
||||
require('./models/Library')(this.sequelize)
|
||||
require('./models/LibraryFolder')(this.sequelize)
|
||||
require('./models/LibraryItem')(this.sequelize)
|
||||
require('./models/EBookFile')(this.sequelize)
|
||||
require('./models/Book')(this.sequelize)
|
||||
require('./models/Podcast')(this.sequelize)
|
||||
require('./models/Library')(this.sequelize)
|
||||
require('./models/LibraryFolder')(this.sequelize)
|
||||
require('./models/LibraryItem')(this.sequelize)
|
||||
require('./models/PodcastEpisode')(this.sequelize)
|
||||
require('./models/MediaProgress')(this.sequelize)
|
||||
require('./models/LibraryFile')(this.sequelize)
|
||||
@ -82,24 +85,7 @@ class Database {
|
||||
require('./models/Notification')(this.sequelize)
|
||||
require('./models/UserPermission')(this.sequelize)
|
||||
|
||||
return this.sequelize.sync({ force: true })
|
||||
}
|
||||
|
||||
async createTestUser() {
|
||||
const User = this.sequelize.models.User
|
||||
|
||||
let user = await User.findOne({
|
||||
where: {
|
||||
username: 'Tester'
|
||||
}
|
||||
})
|
||||
|
||||
if (user) {
|
||||
Logger.info(`[Database] Tester user was found`, user.toJSON())
|
||||
} else {
|
||||
user = await User.create({ username: 'Tester' })
|
||||
Logger.info(`[Database] Created Tester user`, user.toJSON())
|
||||
}
|
||||
return this.sequelize.sync({ force })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ const Database = require('./Database')
|
||||
const SocketAuthority = require('./SocketAuthority')
|
||||
|
||||
const ApiRouter = require('./routers/ApiRouter')
|
||||
const ApiRouter2 = require('./routers/ApiRouter2')
|
||||
const HlsRouter = require('./routers/HlsRouter')
|
||||
const StaticRouter = require('./routers/StaticRouter')
|
||||
|
||||
@ -84,6 +85,7 @@ class Server {
|
||||
|
||||
// Routers
|
||||
this.apiRouter = new ApiRouter(this)
|
||||
|
||||
this.hlsRouter = new HlsRouter(this.db, this.auth, this.playbackSessionManager)
|
||||
this.staticRouter = new StaticRouter(this.db)
|
||||
|
||||
@ -102,9 +104,9 @@ class Server {
|
||||
await this.playbackSessionManager.removeOrphanStreams()
|
||||
|
||||
// TODO: Test new db connection
|
||||
await Database.init()
|
||||
// await Database.createTestUser()
|
||||
await dbMigration3.migrate()
|
||||
const force = true
|
||||
await Database.init(force)
|
||||
if (force) await dbMigration3.migrate()
|
||||
|
||||
const previousVersion = await this.db.checkPreviousVersion() // Returns null if same server version
|
||||
if (previousVersion) {
|
||||
@ -169,6 +171,7 @@ class Server {
|
||||
// Static folder
|
||||
router.use(express.static(Path.join(global.appRoot, 'static')))
|
||||
|
||||
router.use('/api/v1', new ApiRouter2(this).router)
|
||||
router.use('/api', this.authMiddleware.bind(this), this.apiRouter.router)
|
||||
router.use('/hls', this.authMiddleware.bind(this), this.hlsRouter.router)
|
||||
router.use('/s', this.authMiddleware.bind(this), this.staticRouter.router)
|
||||
|
192
server/controllers2/LibraryItemController.js
Normal file
192
server/controllers2/LibraryItemController.js
Normal file
@ -0,0 +1,192 @@
|
||||
const Database = require('../Database')
|
||||
|
||||
class LibraryItemController {
|
||||
constructor() { }
|
||||
|
||||
// Example get library item fully expanded or minified
|
||||
async get(req, res) {
|
||||
const key = req.query.minified == 1 ? 'minified' : 'full'
|
||||
const include = {
|
||||
minified: [
|
||||
{
|
||||
model: Database.models.book,
|
||||
include: [
|
||||
{
|
||||
model: Database.models.audioTrack
|
||||
},
|
||||
{
|
||||
model: Database.models.genre,
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.tag,
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.person,
|
||||
as: 'authors',
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.person,
|
||||
as: 'narrators',
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.series,
|
||||
through: {
|
||||
attributes: ['sequence']
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.bookChapter
|
||||
},
|
||||
{
|
||||
model: Database.models.eBookFile,
|
||||
include: 'fileMetadata'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: Database.models.podcast,
|
||||
include: [
|
||||
{
|
||||
model: Database.models.podcastEpisode,
|
||||
include: {
|
||||
model: Database.models.audioTrack
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.genre,
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.tag,
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
full: [
|
||||
{
|
||||
model: Database.models.book,
|
||||
include: [
|
||||
{
|
||||
model: Database.models.fileMetadata,
|
||||
as: 'imageFile'
|
||||
},
|
||||
{
|
||||
model: Database.models.audioTrack,
|
||||
include: {
|
||||
model: Database.models.mediaFile,
|
||||
include: [
|
||||
'fileMetadata',
|
||||
'mediaStreams'
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.genre,
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.tag,
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.person,
|
||||
as: 'authors',
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.person,
|
||||
as: 'narrators',
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.series,
|
||||
through: {
|
||||
attributes: ['sequence']
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.bookChapter
|
||||
},
|
||||
{
|
||||
model: Database.models.eBookFile,
|
||||
include: 'fileMetadata'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: Database.models.podcast,
|
||||
include: [
|
||||
{
|
||||
model: Database.models.fileMetadata,
|
||||
as: 'imageFile'
|
||||
},
|
||||
{
|
||||
model: Database.models.podcastEpisode,
|
||||
include: {
|
||||
model: Database.models.audioTrack,
|
||||
include: {
|
||||
model: Database.models.mediaFile,
|
||||
include: [
|
||||
'fileMetadata',
|
||||
'mediaStreams'
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.genre,
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.models.tag,
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
model: Database.models.libraryFile,
|
||||
include: 'fileMetadata'
|
||||
},
|
||||
{
|
||||
model: Database.models.libraryFolder,
|
||||
include: 'library'
|
||||
}
|
||||
]
|
||||
}
|
||||
const LibraryItem = await Database.models.libraryItem.findByPk(req.params.id, {
|
||||
include: include[key]
|
||||
})
|
||||
|
||||
res.json(LibraryItem)
|
||||
}
|
||||
}
|
||||
module.exports = new LibraryItemController()
|
@ -8,7 +8,7 @@ module.exports = (sequelize) => {
|
||||
class AudioBookmark extends Model {
|
||||
getMediaItem(options) {
|
||||
if (!this.mediaItemType) return Promise.resolve(null)
|
||||
const mixinMethodName = `get${this.mediaItemType}`
|
||||
const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}`
|
||||
return this[mixinMethodName](options)
|
||||
}
|
||||
}
|
||||
@ -19,52 +19,57 @@ module.exports = (sequelize) => {
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true
|
||||
},
|
||||
MediaItemId: DataTypes.UUIDV4,
|
||||
mediaItemId: DataTypes.UUIDV4,
|
||||
mediaItemType: DataTypes.STRING,
|
||||
title: DataTypes.STRING,
|
||||
time: DataTypes.INTEGER
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'AudioBookmark'
|
||||
modelName: 'audioBookmark'
|
||||
})
|
||||
|
||||
const { User, Book, PodcastEpisode } = sequelize.models
|
||||
Book.hasMany(AudioBookmark, {
|
||||
foreignKey: 'MediaItemId',
|
||||
const { user, book, podcastEpisode } = sequelize.models
|
||||
book.hasMany(AudioBookmark, {
|
||||
foreignKey: 'mediaItemId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaItemType: 'Book'
|
||||
mediaItemType: 'book'
|
||||
}
|
||||
})
|
||||
AudioBookmark.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false })
|
||||
AudioBookmark.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false })
|
||||
|
||||
PodcastEpisode.hasMany(AudioBookmark, {
|
||||
foreignKey: 'MediaItemId',
|
||||
podcastEpisode.hasMany(AudioBookmark, {
|
||||
foreignKey: 'mediaItemId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaItemType: 'PodcastEpisode'
|
||||
mediaItemType: 'podcastEpisode'
|
||||
}
|
||||
})
|
||||
AudioBookmark.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false })
|
||||
AudioBookmark.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
|
||||
|
||||
AudioBookmark.addHook('afterFind', findResult => {
|
||||
if (!findResult) return
|
||||
|
||||
if (!Array.isArray(findResult)) findResult = [findResult]
|
||||
|
||||
for (const instance of findResult) {
|
||||
if (instance.mediaItemType === 'Book' && instance.Book !== undefined) {
|
||||
instance.MediaItem = instance.Book
|
||||
} else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) {
|
||||
instance.MediaItem = instance.PodcastEpisode
|
||||
if (instance.mediaItemType === 'book' && instance.book !== undefined) {
|
||||
instance.mediaItem = instance.book
|
||||
instance.dataValues.mediaItem = instance.dataValues.book
|
||||
} else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) {
|
||||
instance.mediaItem = instance.podcastEpisode
|
||||
instance.dataValues.mediaItem = instance.dataValues.podcastEpisode
|
||||
}
|
||||
// To prevent mistakes:
|
||||
delete instance.Book
|
||||
delete instance.dataValues.Book
|
||||
delete instance.PodcastEpisode
|
||||
delete instance.dataValues.PodcastEpisode
|
||||
delete instance.book
|
||||
delete instance.dataValues.book
|
||||
delete instance.podcastEpisode
|
||||
delete instance.dataValues.podcastEpisode
|
||||
}
|
||||
})
|
||||
|
||||
User.hasMany(AudioBookmark)
|
||||
AudioBookmark.belongsTo(User)
|
||||
user.hasMany(AudioBookmark)
|
||||
AudioBookmark.belongsTo(user)
|
||||
|
||||
return AudioBookmark
|
||||
}
|
@ -8,7 +8,7 @@ module.exports = (sequelize) => {
|
||||
class AudioTrack extends Model {
|
||||
getMediaItem(options) {
|
||||
if (!this.mediaItemType) return Promise.resolve(null)
|
||||
const mixinMethodName = `get${this.mediaItemType}`
|
||||
const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}`
|
||||
return this[mixinMethodName](options)
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@ module.exports = (sequelize) => {
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true
|
||||
},
|
||||
MediaItemId: DataTypes.UUIDV4,
|
||||
mediaItemId: DataTypes.UUIDV4,
|
||||
mediaItemType: DataTypes.STRING,
|
||||
index: DataTypes.INTEGER,
|
||||
startOffset: DataTypes.FLOAT,
|
||||
@ -31,45 +31,50 @@ module.exports = (sequelize) => {
|
||||
discNumber: DataTypes.INTEGER
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'AudioTrack'
|
||||
modelName: 'audioTrack'
|
||||
})
|
||||
|
||||
const { Book, PodcastEpisode, MediaFile } = sequelize.models
|
||||
const { book, podcastEpisode, mediaFile } = sequelize.models
|
||||
|
||||
MediaFile.hasOne(AudioTrack)
|
||||
AudioTrack.belongsTo(MediaFile)
|
||||
mediaFile.hasOne(AudioTrack)
|
||||
AudioTrack.belongsTo(mediaFile)
|
||||
|
||||
Book.hasMany(AudioTrack, {
|
||||
foreignKey: 'MediaItemId',
|
||||
book.hasMany(AudioTrack, {
|
||||
foreignKey: 'mediaItemId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaItemType: 'Book'
|
||||
mediaItemType: 'book'
|
||||
}
|
||||
})
|
||||
AudioTrack.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false })
|
||||
AudioTrack.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false })
|
||||
|
||||
PodcastEpisode.hasOne(AudioTrack, {
|
||||
foreignKey: 'MediaItemId',
|
||||
podcastEpisode.hasOne(AudioTrack, {
|
||||
foreignKey: 'mediaItemId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaItemType: 'PodcastEpisode'
|
||||
mediaItemType: 'podcastEpisode'
|
||||
}
|
||||
})
|
||||
AudioTrack.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false })
|
||||
AudioTrack.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
|
||||
|
||||
AudioTrack.addHook('afterFind', findResult => {
|
||||
if (!findResult) return
|
||||
|
||||
if (!Array.isArray(findResult)) findResult = [findResult]
|
||||
|
||||
for (const instance of findResult) {
|
||||
if (instance.mediaItemType === 'Book' && instance.Book !== undefined) {
|
||||
instance.MediaItem = instance.Book
|
||||
} else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) {
|
||||
instance.MediaItem = instance.PodcastEpisode
|
||||
if (instance.mediaItemType === 'book' && instance.book !== undefined) {
|
||||
instance.mediaItem = instance.book
|
||||
instance.dataValues.mediaItem = instance.dataValues.book
|
||||
} else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) {
|
||||
instance.mediaItem = instance.podcastEpisode
|
||||
instance.dataValues.mediaItem = instance.dataValues.podcastEpisode
|
||||
}
|
||||
// To prevent mistakes:
|
||||
delete instance.Book
|
||||
delete instance.dataValues.Book
|
||||
delete instance.PodcastEpisode
|
||||
delete instance.dataValues.PodcastEpisode
|
||||
delete instance.book
|
||||
delete instance.dataValues.book
|
||||
delete instance.podcastEpisode
|
||||
delete instance.dataValues.podcastEpisode
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -23,18 +23,16 @@ module.exports = (sequelize) => {
|
||||
lastCoverSearch: DataTypes.DATE
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Book'
|
||||
modelName: 'book'
|
||||
})
|
||||
|
||||
const { LibraryItem, FileMetadata, EBookFile } = sequelize.models
|
||||
LibraryItem.hasOne(Book)
|
||||
Book.belongsTo(LibraryItem)
|
||||
const { fileMetadata, eBookFile } = sequelize.models
|
||||
|
||||
FileMetadata.hasOne(Book, { foreignKey: 'ImageFileId ' })
|
||||
Book.belongsTo(FileMetadata, { as: 'ImageFile', foreignKey: 'ImageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
|
||||
fileMetadata.hasOne(Book, { foreignKey: 'imageFileId' })
|
||||
Book.belongsTo(fileMetadata, { as: 'imageFile', foreignKey: 'imageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
|
||||
|
||||
EBookFile.hasOne(Book)
|
||||
Book.belongsTo(EBookFile)
|
||||
eBookFile.hasOne(Book)
|
||||
Book.belongsTo(eBookFile)
|
||||
|
||||
return Book
|
||||
}
|
@ -11,21 +11,21 @@ module.exports = (sequelize) => {
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'BookAuthor',
|
||||
modelName: 'bookAuthor',
|
||||
timestamps: false
|
||||
})
|
||||
|
||||
// Super Many-to-Many
|
||||
// ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship
|
||||
const { Book, Person } = sequelize.models
|
||||
Book.belongsToMany(Person, { through: BookAuthor })
|
||||
Person.belongsToMany(Book, { through: BookAuthor })
|
||||
const { book, person } = sequelize.models
|
||||
book.belongsToMany(person, { through: BookAuthor, as: 'authors', otherKey: 'authorId' })
|
||||
person.belongsToMany(book, { through: BookAuthor, foreignKey: 'authorId' })
|
||||
|
||||
Book.hasMany(BookAuthor)
|
||||
BookAuthor.belongsTo(Book)
|
||||
book.hasMany(BookAuthor)
|
||||
BookAuthor.belongsTo(book)
|
||||
|
||||
Person.hasMany(BookAuthor)
|
||||
BookAuthor.belongsTo(Person)
|
||||
person.hasMany(BookAuthor, { foreignKey: 'authorId' })
|
||||
BookAuthor.belongsTo(person, { as: 'author', foreignKey: 'authorId' })
|
||||
|
||||
return BookAuthor
|
||||
}
|
@ -15,13 +15,13 @@ module.exports = (sequelize) => {
|
||||
end: DataTypes.FLOAT
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'BookChapter'
|
||||
modelName: 'bookChapter'
|
||||
})
|
||||
|
||||
const { Book } = sequelize.models
|
||||
const { book } = sequelize.models
|
||||
|
||||
Book.hasMany(BookChapter)
|
||||
BookChapter.belongsTo(Book)
|
||||
book.hasMany(BookChapter)
|
||||
BookChapter.belongsTo(book)
|
||||
|
||||
return BookChapter
|
||||
}
|
@ -11,21 +11,21 @@ module.exports = (sequelize) => {
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'BookGenre',
|
||||
modelName: 'bookGenre',
|
||||
timestamps: false
|
||||
})
|
||||
|
||||
// Super Many-to-Many
|
||||
// ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship
|
||||
const { Book, Genre } = sequelize.models
|
||||
Book.belongsToMany(Genre, { through: BookGenre })
|
||||
Genre.belongsToMany(Book, { through: BookGenre })
|
||||
const { book, genre } = sequelize.models
|
||||
book.belongsToMany(genre, { through: BookGenre })
|
||||
genre.belongsToMany(book, { through: BookGenre })
|
||||
|
||||
Book.hasMany(BookGenre)
|
||||
BookGenre.belongsTo(Book)
|
||||
book.hasMany(BookGenre)
|
||||
BookGenre.belongsTo(book)
|
||||
|
||||
Genre.hasMany(BookGenre)
|
||||
BookGenre.belongsTo(Genre)
|
||||
genre.hasMany(BookGenre)
|
||||
BookGenre.belongsTo(genre)
|
||||
|
||||
return BookGenre
|
||||
}
|
@ -11,21 +11,21 @@ module.exports = (sequelize) => {
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'BookNarrator',
|
||||
modelName: 'bookNarrator',
|
||||
timestamps: false
|
||||
})
|
||||
|
||||
// Super Many-to-Many
|
||||
// ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship
|
||||
const { Book, Person } = sequelize.models
|
||||
Book.belongsToMany(Person, { through: BookNarrator })
|
||||
Person.belongsToMany(Book, { through: BookNarrator })
|
||||
const { book, person } = sequelize.models
|
||||
book.belongsToMany(person, { through: BookNarrator, as: 'narrators', otherKey: 'narratorId' })
|
||||
person.belongsToMany(book, { through: BookNarrator, foreignKey: 'narratorId' })
|
||||
|
||||
Book.hasMany(BookNarrator)
|
||||
BookNarrator.belongsTo(Book)
|
||||
book.hasMany(BookNarrator)
|
||||
BookNarrator.belongsTo(book)
|
||||
|
||||
Person.hasMany(BookNarrator)
|
||||
BookNarrator.belongsTo(Person)
|
||||
person.hasMany(BookNarrator, { foreignKey: 'narratorId' })
|
||||
BookNarrator.belongsTo(person, { as: 'narrator', foreignKey: 'narratorId' })
|
||||
|
||||
return BookNarrator
|
||||
}
|
@ -12,21 +12,21 @@ module.exports = (sequelize) => {
|
||||
sequence: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'BookSeries',
|
||||
modelName: 'bookSeries',
|
||||
timestamps: false
|
||||
})
|
||||
|
||||
// Super Many-to-Many
|
||||
// ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship
|
||||
const { Book, Series } = sequelize.models
|
||||
Book.belongsToMany(Series, { through: BookSeries })
|
||||
Series.belongsToMany(Book, { through: BookSeries })
|
||||
const { book, series } = sequelize.models
|
||||
book.belongsToMany(series, { through: BookSeries })
|
||||
series.belongsToMany(book, { through: BookSeries })
|
||||
|
||||
Book.hasMany(BookSeries)
|
||||
BookSeries.belongsTo(Book)
|
||||
book.hasMany(BookSeries)
|
||||
BookSeries.belongsTo(book)
|
||||
|
||||
Series.hasMany(BookSeries)
|
||||
BookSeries.belongsTo(Series)
|
||||
series.hasMany(BookSeries)
|
||||
BookSeries.belongsTo(series)
|
||||
|
||||
return BookSeries
|
||||
}
|
@ -11,21 +11,21 @@ module.exports = (sequelize) => {
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'BookTag',
|
||||
modelName: 'bookTag',
|
||||
timestamps: false
|
||||
})
|
||||
|
||||
// Super Many-to-Many
|
||||
// ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship
|
||||
const { Book, Tag } = sequelize.models
|
||||
Book.belongsToMany(Tag, { through: BookTag })
|
||||
Tag.belongsToMany(Book, { through: BookTag })
|
||||
const { book, tag } = sequelize.models
|
||||
book.belongsToMany(tag, { through: BookTag })
|
||||
tag.belongsToMany(book, { through: BookTag })
|
||||
|
||||
Book.hasMany(BookTag)
|
||||
BookTag.belongsTo(Book)
|
||||
book.hasMany(BookTag)
|
||||
BookTag.belongsTo(book)
|
||||
|
||||
Tag.hasMany(BookTag)
|
||||
BookTag.belongsTo(Tag)
|
||||
tag.hasMany(BookTag)
|
||||
BookTag.belongsTo(tag)
|
||||
|
||||
return BookTag
|
||||
}
|
@ -13,13 +13,13 @@ module.exports = (sequelize) => {
|
||||
description: DataTypes.TEXT
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Collection'
|
||||
modelName: 'collection'
|
||||
})
|
||||
|
||||
const { Library } = sequelize.models
|
||||
const { library } = sequelize.models
|
||||
|
||||
Library.hasMany(Collection)
|
||||
Collection.belongsTo(Library)
|
||||
library.hasMany(Collection)
|
||||
Collection.belongsTo(library)
|
||||
|
||||
return Collection
|
||||
}
|
@ -13,20 +13,20 @@ module.exports = (sequelize) => {
|
||||
sequelize,
|
||||
timestamps: true,
|
||||
updatedAt: false,
|
||||
modelName: 'CollectionBook'
|
||||
modelName: 'collectionBook'
|
||||
})
|
||||
|
||||
// Super Many-to-Many
|
||||
// ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship
|
||||
const { Book, Collection } = sequelize.models
|
||||
Book.belongsToMany(Collection, { through: CollectionBook })
|
||||
Collection.belongsToMany(Book, { through: CollectionBook })
|
||||
const { book, collection } = sequelize.models
|
||||
book.belongsToMany(collection, { through: CollectionBook })
|
||||
collection.belongsToMany(book, { through: CollectionBook })
|
||||
|
||||
Book.hasMany(CollectionBook)
|
||||
CollectionBook.belongsTo(Book)
|
||||
book.hasMany(CollectionBook)
|
||||
CollectionBook.belongsTo(book)
|
||||
|
||||
Collection.hasMany(CollectionBook)
|
||||
CollectionBook.belongsTo(Collection)
|
||||
collection.hasMany(CollectionBook)
|
||||
CollectionBook.belongsTo(collection)
|
||||
|
||||
return CollectionBook
|
||||
}
|
@ -17,13 +17,13 @@ module.exports = (sequelize) => {
|
||||
deviceVersion: DataTypes.STRING // e.g. Browser version or Android SDK
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Device'
|
||||
modelName: 'device'
|
||||
})
|
||||
|
||||
const { User } = sequelize.models
|
||||
const { user } = sequelize.models
|
||||
|
||||
User.hasMany(Device)
|
||||
Device.belongsTo(User)
|
||||
user.hasMany(Device)
|
||||
Device.belongsTo(user)
|
||||
|
||||
return Device
|
||||
}
|
@ -8,16 +8,17 @@ module.exports = (sequelize) => {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true
|
||||
}
|
||||
},
|
||||
format: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'EBookFile'
|
||||
modelName: 'eBookFile'
|
||||
})
|
||||
|
||||
const { FileMetadata } = sequelize.models
|
||||
const { fileMetadata } = sequelize.models
|
||||
|
||||
FileMetadata.hasOne(EBookFile, { foreignKey: 'FileMetadataId' })
|
||||
EBookFile.belongsTo(FileMetadata, { as: 'FileMetadata', foreignKey: 'FileMetadataId' })
|
||||
fileMetadata.hasOne(EBookFile, { foreignKey: 'fileMetadataId' })
|
||||
EBookFile.belongsTo(fileMetadata, { as: 'fileMetadata', foreignKey: 'fileMetadataId' })
|
||||
|
||||
return EBookFile
|
||||
}
|
@ -8,7 +8,7 @@ module.exports = (sequelize) => {
|
||||
class Feed extends Model {
|
||||
getEntity(options) {
|
||||
if (!this.entityType) return Promise.resolve(null)
|
||||
const mixinMethodName = `get${this.entityType}`
|
||||
const mixinMethodName = `get${sequelize.uppercaseFirst(this.entityType)}`
|
||||
return this[mixinMethodName](options)
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,7 @@ module.exports = (sequelize) => {
|
||||
},
|
||||
slug: DataTypes.STRING,
|
||||
entityType: DataTypes.STRING,
|
||||
EntityId: DataTypes.UUIDV4,
|
||||
entityId: DataTypes.UUIDV4,
|
||||
entityUpdatedAt: DataTypes.DATE,
|
||||
serverAddress: DataTypes.STRING,
|
||||
feedURL: DataTypes.STRING,
|
||||
@ -38,72 +38,78 @@ module.exports = (sequelize) => {
|
||||
preventIndexing: DataTypes.BOOLEAN
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Feed'
|
||||
modelName: 'feed'
|
||||
})
|
||||
|
||||
const { User, LibraryItem, Collection, Series, Playlist } = sequelize.models
|
||||
const { user, libraryItem, collection, series, playlist } = sequelize.models
|
||||
|
||||
User.hasMany(Feed)
|
||||
Feed.belongsTo(User)
|
||||
user.hasMany(Feed)
|
||||
Feed.belongsTo(user)
|
||||
|
||||
LibraryItem.hasMany(Feed, {
|
||||
foreignKey: 'EntityId',
|
||||
libraryItem.hasMany(Feed, {
|
||||
foreignKey: 'entityId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
entityType: 'LibraryItem'
|
||||
entityType: 'libraryItem'
|
||||
}
|
||||
})
|
||||
Feed.belongsTo(LibraryItem, { foreignKey: 'EntityId', constraints: false })
|
||||
Feed.belongsTo(libraryItem, { foreignKey: 'entityId', constraints: false })
|
||||
|
||||
Collection.hasMany(Feed, {
|
||||
foreignKey: 'EntityId',
|
||||
collection.hasMany(Feed, {
|
||||
foreignKey: 'entityId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
entityType: 'Collection'
|
||||
entityType: 'collection'
|
||||
}
|
||||
})
|
||||
Feed.belongsTo(Collection, { foreignKey: 'EntityId', constraints: false })
|
||||
Feed.belongsTo(collection, { foreignKey: 'entityId', constraints: false })
|
||||
|
||||
Series.hasMany(Feed, {
|
||||
foreignKey: 'EntityId',
|
||||
series.hasMany(Feed, {
|
||||
foreignKey: 'entityId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
entityType: 'Series'
|
||||
entityType: 'series'
|
||||
}
|
||||
})
|
||||
Feed.belongsTo(Series, { foreignKey: 'EntityId', constraints: false })
|
||||
Feed.belongsTo(series, { foreignKey: 'entityId', constraints: false })
|
||||
|
||||
Playlist.hasMany(Feed, {
|
||||
foreignKey: 'EntityId',
|
||||
playlist.hasMany(Feed, {
|
||||
foreignKey: 'entityId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
entityType: 'Playlist'
|
||||
entityType: 'playlist'
|
||||
}
|
||||
})
|
||||
Feed.belongsTo(Playlist, { foreignKey: 'EntityId', constraints: false })
|
||||
Feed.belongsTo(playlist, { foreignKey: 'entityId', constraints: false })
|
||||
|
||||
Feed.addHook('afterFind', findResult => {
|
||||
if (!findResult) return
|
||||
|
||||
if (!Array.isArray(findResult)) findResult = [findResult]
|
||||
for (const instance of findResult) {
|
||||
if (instance.entityType === 'LibraryItem' && instance.LibraryItem !== undefined) {
|
||||
instance.Entity = instance.LibraryItem
|
||||
} else if (instance.mediaItemType === 'Collection' && instance.Collection !== undefined) {
|
||||
instance.Entity = instance.Collection
|
||||
} else if (instance.mediaItemType === 'Series' && instance.Series !== undefined) {
|
||||
instance.Entity = instance.Series
|
||||
} else if (instance.mediaItemType === 'Playlist' && instance.Playlist !== undefined) {
|
||||
instance.Entity = instance.Playlist
|
||||
if (instance.entityType === 'libraryItem' && instance.libraryItem !== undefined) {
|
||||
instance.entity = instance.libraryItem
|
||||
instance.dataValues.entity = instance.dataValues.libraryItem
|
||||
} else if (instance.entityType === 'collection' && instance.collection !== undefined) {
|
||||
instance.entity = instance.collection
|
||||
instance.dataValues.entity = instance.dataValues.collection
|
||||
} else if (instance.entityType === 'series' && instance.series !== undefined) {
|
||||
instance.entity = instance.series
|
||||
instance.dataValues.entity = instance.dataValues.series
|
||||
} else if (instance.entityType === 'playlist' && instance.playlist !== undefined) {
|
||||
instance.entity = instance.playlist
|
||||
instance.dataValues.entity = instance.dataValues.playlist
|
||||
}
|
||||
|
||||
// To prevent mistakes:
|
||||
delete instance.LibraryItem
|
||||
delete instance.dataValues.LibraryItem
|
||||
delete instance.Collection
|
||||
delete instance.dataValues.Collection
|
||||
delete instance.Series
|
||||
delete instance.dataValues.Series
|
||||
delete instance.Playlist
|
||||
delete instance.dataValues.Playlist
|
||||
delete instance.libraryItem
|
||||
delete instance.dataValues.libraryItem
|
||||
delete instance.collection
|
||||
delete instance.dataValues.collection
|
||||
delete instance.series
|
||||
delete instance.dataValues.series
|
||||
delete instance.playlist
|
||||
delete instance.dataValues.playlist
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -25,13 +25,13 @@ module.exports = (sequelize) => {
|
||||
explicit: DataTypes.BOOLEAN
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'FeedEpisode'
|
||||
modelName: 'feedEpisode'
|
||||
})
|
||||
|
||||
const { Feed } = sequelize.models
|
||||
const { feed } = sequelize.models
|
||||
|
||||
Feed.hasMany(FeedEpisode)
|
||||
FeedEpisode.belongsTo(Feed)
|
||||
feed.hasMany(FeedEpisode)
|
||||
FeedEpisode.belongsTo(feed)
|
||||
|
||||
return FeedEpisode
|
||||
}
|
@ -21,10 +21,10 @@ module.exports = (sequelize) => {
|
||||
sequelize,
|
||||
freezeTableName: true, // sequelize uses datum as singular of data
|
||||
name: {
|
||||
singular: 'FileMetadata',
|
||||
plural: 'FileMetadata'
|
||||
singular: 'fileMetadata',
|
||||
plural: 'fileMetadata'
|
||||
},
|
||||
modelName: 'FileMetadata'
|
||||
modelName: 'fileMetadata'
|
||||
})
|
||||
|
||||
return FileMetadata
|
||||
|
@ -13,7 +13,7 @@ module.exports = (sequelize) => {
|
||||
cleanName: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Genre'
|
||||
modelName: 'genre'
|
||||
})
|
||||
|
||||
return Genre
|
||||
|
@ -18,7 +18,7 @@ module.exports = (sequelize) => {
|
||||
lastScanVersion: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Library'
|
||||
modelName: 'library'
|
||||
})
|
||||
|
||||
return Library
|
||||
|
@ -11,15 +11,15 @@ module.exports = (sequelize) => {
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'LibraryFile'
|
||||
modelName: 'libraryFile'
|
||||
})
|
||||
|
||||
const { LibraryItem, FileMetadata } = sequelize.models
|
||||
LibraryItem.hasMany(LibraryFile)
|
||||
LibraryFile.belongsTo(LibraryItem)
|
||||
const { libraryItem, fileMetadata } = sequelize.models
|
||||
libraryItem.hasMany(LibraryFile)
|
||||
LibraryFile.belongsTo(libraryItem)
|
||||
|
||||
FileMetadata.hasOne(LibraryFile, { foreignKey: 'FileMetadataId' })
|
||||
LibraryFile.belongsTo(FileMetadata, { as: 'FileMetadata', foreignKey: 'FileMetadataId' })
|
||||
fileMetadata.hasOne(LibraryFile, { foreignKey: 'fileMetadataId' })
|
||||
LibraryFile.belongsTo(fileMetadata, { as: 'fileMetadata', foreignKey: 'fileMetadataId' })
|
||||
|
||||
return LibraryFile
|
||||
}
|
@ -12,12 +12,12 @@ module.exports = (sequelize) => {
|
||||
path: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'LibraryFolder'
|
||||
modelName: 'libraryFolder'
|
||||
})
|
||||
|
||||
const { Library } = sequelize.models
|
||||
Library.hasMany(LibraryFolder)
|
||||
LibraryFolder.belongsTo(Library)
|
||||
const { library } = sequelize.models
|
||||
library.hasMany(LibraryFolder)
|
||||
LibraryFolder.belongsTo(library)
|
||||
|
||||
return LibraryFolder
|
||||
}
|
@ -1,7 +1,13 @@
|
||||
const { DataTypes, Model } = require('sequelize')
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
class LibraryItem extends Model { }
|
||||
class LibraryItem extends Model {
|
||||
getMedia(options) {
|
||||
if (!this.mediaType) return Promise.resolve(null)
|
||||
const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaType)}`
|
||||
return this[mixinMethodName](options)
|
||||
}
|
||||
}
|
||||
|
||||
LibraryItem.init({
|
||||
id: {
|
||||
@ -12,6 +18,7 @@ module.exports = (sequelize) => {
|
||||
ino: DataTypes.STRING,
|
||||
path: DataTypes.STRING,
|
||||
relPath: DataTypes.STRING,
|
||||
mediaId: DataTypes.UUIDV4,
|
||||
mediaType: DataTypes.STRING,
|
||||
isFile: DataTypes.BOOLEAN,
|
||||
isMissing: DataTypes.BOOLEAN,
|
||||
@ -23,12 +30,50 @@ module.exports = (sequelize) => {
|
||||
lastScanVersion: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'LibraryItem'
|
||||
modelName: 'libraryItem'
|
||||
})
|
||||
|
||||
const { LibraryFolder } = sequelize.models
|
||||
LibraryFolder.hasMany(LibraryItem)
|
||||
LibraryItem.belongsTo(LibraryFolder)
|
||||
const { libraryFolder, book, podcast } = sequelize.models
|
||||
libraryFolder.hasMany(LibraryItem)
|
||||
LibraryItem.belongsTo(libraryFolder)
|
||||
|
||||
book.hasOne(LibraryItem, {
|
||||
foreignKey: 'mediaId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaType: 'book'
|
||||
}
|
||||
})
|
||||
LibraryItem.belongsTo(book, { foreignKey: 'mediaId', constraints: false })
|
||||
|
||||
podcast.hasOne(LibraryItem, {
|
||||
foreignKey: 'mediaId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaType: 'podcast'
|
||||
}
|
||||
})
|
||||
LibraryItem.belongsTo(podcast, { foreignKey: 'mediaId', constraints: false })
|
||||
|
||||
LibraryItem.addHook('afterFind', findResult => {
|
||||
if (!findResult) return
|
||||
|
||||
if (!Array.isArray(findResult)) findResult = [findResult]
|
||||
for (const instance of findResult) {
|
||||
if (instance.mediaType === 'book' && instance.book !== undefined) {
|
||||
instance.media = instance.book
|
||||
instance.dataValues.media = instance.dataValues.book
|
||||
} else if (instance.mediaType === 'podcast' && instance.podcast !== undefined) {
|
||||
instance.media = instance.podcast
|
||||
instance.dataValues.media = instance.dataValues.podcast
|
||||
}
|
||||
// To prevent mistakes:
|
||||
delete instance.book
|
||||
delete instance.dataValues.book
|
||||
delete instance.podcast
|
||||
delete instance.dataValues.podcast
|
||||
}
|
||||
})
|
||||
|
||||
return LibraryItem
|
||||
}
|
@ -13,13 +13,13 @@ module.exports = (sequelize) => {
|
||||
value: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'LibrarySetting'
|
||||
modelName: 'librarySetting'
|
||||
})
|
||||
|
||||
const { Library } = sequelize.models
|
||||
const { library } = sequelize.models
|
||||
|
||||
Library.hasMany(LibrarySetting)
|
||||
LibrarySetting.belongsTo(Library)
|
||||
library.hasMany(LibrarySetting)
|
||||
LibrarySetting.belongsTo(library)
|
||||
|
||||
return LibrarySetting
|
||||
}
|
@ -17,13 +17,13 @@ module.exports = (sequelize) => {
|
||||
tags: DataTypes.JSON
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'MediaFile'
|
||||
modelName: 'mediaFile'
|
||||
})
|
||||
|
||||
const { FileMetadata } = sequelize.models
|
||||
const { fileMetadata } = sequelize.models
|
||||
|
||||
FileMetadata.hasOne(MediaFile, { foreignKey: 'FileMetadataId' })
|
||||
MediaFile.belongsTo(FileMetadata, { as: 'FileMetadata', foreignKey: 'FileMetadataId' })
|
||||
fileMetadata.hasOne(MediaFile, { foreignKey: 'fileMetadataId' })
|
||||
MediaFile.belongsTo(fileMetadata, { as: 'fileMetadata', foreignKey: 'fileMetadataId' })
|
||||
|
||||
return MediaFile
|
||||
}
|
@ -8,7 +8,7 @@ module.exports = (sequelize) => {
|
||||
class MediaProgress extends Model {
|
||||
getMediaItem(options) {
|
||||
if (!this.mediaItemType) return Promise.resolve(null)
|
||||
const mixinMethodName = `get${this.mediaItemType}`
|
||||
const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}`
|
||||
return this[mixinMethodName](options)
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@ module.exports = (sequelize) => {
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true
|
||||
},
|
||||
MediaItemId: DataTypes.UUIDV4,
|
||||
mediaItemId: DataTypes.UUIDV4,
|
||||
mediaItemType: DataTypes.STRING,
|
||||
duration: DataTypes.FLOAT,
|
||||
currentTime: DataTypes.FLOAT,
|
||||
@ -28,46 +28,52 @@ module.exports = (sequelize) => {
|
||||
finishedAt: DataTypes.DATE
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'MediaProgress'
|
||||
modelName: 'mediaProgress'
|
||||
})
|
||||
|
||||
const { Book, PodcastEpisode, User } = sequelize.models
|
||||
Book.hasMany(MediaProgress, {
|
||||
foreignKey: 'MediaItemId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaItemType: 'Book'
|
||||
}
|
||||
})
|
||||
MediaProgress.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false })
|
||||
const { book, podcastEpisode, user } = sequelize.models
|
||||
|
||||
PodcastEpisode.hasMany(MediaProgress, {
|
||||
foreignKey: 'MediaItemId',
|
||||
book.hasMany(MediaProgress, {
|
||||
foreignKey: 'mediaItemId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaItemType: 'PodcastEpisode'
|
||||
mediaItemType: 'book'
|
||||
}
|
||||
})
|
||||
MediaProgress.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false })
|
||||
MediaProgress.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false })
|
||||
|
||||
podcastEpisode.hasMany(MediaProgress, {
|
||||
foreignKey: 'mediaItemId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaItemType: 'podcastEpisode'
|
||||
}
|
||||
})
|
||||
MediaProgress.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
|
||||
|
||||
MediaProgress.addHook('afterFind', findResult => {
|
||||
if (!findResult) return
|
||||
|
||||
if (!Array.isArray(findResult)) findResult = [findResult]
|
||||
|
||||
for (const instance of findResult) {
|
||||
if (instance.mediaItemType === 'Book' && instance.Book !== undefined) {
|
||||
instance.MediaItem = instance.Book
|
||||
} else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) {
|
||||
instance.MediaItem = instance.PodcastEpisode
|
||||
if (instance.mediaItemType === 'book' && instance.book !== undefined) {
|
||||
instance.mediaItem = instance.book
|
||||
instance.dataValues.mediaItem = instance.dataValues.book
|
||||
} else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) {
|
||||
instance.mediaItem = instance.podcastEpisode
|
||||
instance.dataValues.mediaItem = instance.dataValues.podcastEpisode
|
||||
}
|
||||
// To prevent mistakes:
|
||||
delete instance.Book
|
||||
delete instance.dataValues.Book
|
||||
delete instance.PodcastEpisode
|
||||
delete instance.dataValues.PodcastEpisode
|
||||
delete instance.book
|
||||
delete instance.dataValues.book
|
||||
delete instance.podcastEpisode
|
||||
delete instance.dataValues.podcastEpisode
|
||||
}
|
||||
})
|
||||
|
||||
User.hasMany(MediaProgress)
|
||||
MediaProgress.belongsTo(User)
|
||||
user.hasMany(MediaProgress)
|
||||
MediaProgress.belongsTo(user)
|
||||
|
||||
return MediaProgress
|
||||
}
|
@ -37,13 +37,13 @@ module.exports = (sequelize) => {
|
||||
chapters: DataTypes.JSON
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'MediaStream'
|
||||
modelName: 'mediaStream'
|
||||
})
|
||||
|
||||
const { MediaFile } = sequelize.models
|
||||
const { mediaFile } = sequelize.models
|
||||
|
||||
MediaFile.hasMany(MediaStream)
|
||||
MediaStream.belongsTo(MediaFile)
|
||||
mediaFile.hasMany(MediaStream)
|
||||
MediaStream.belongsTo(mediaFile)
|
||||
|
||||
return MediaStream
|
||||
}
|
@ -22,7 +22,7 @@ module.exports = (sequelize) => {
|
||||
extraData: DataTypes.JSON
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Notification'
|
||||
modelName: 'notification'
|
||||
})
|
||||
|
||||
return Notification
|
||||
|
@ -15,12 +15,12 @@ module.exports = (sequelize) => {
|
||||
description: DataTypes.TEXT
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Person'
|
||||
modelName: 'person'
|
||||
})
|
||||
|
||||
const { FileMetadata } = sequelize.models
|
||||
FileMetadata.hasMany(Person, { foreignKey: 'ImageFileId' })
|
||||
Person.belongsTo(FileMetadata, { as: 'ImageFile', foreignKey: 'ImageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
|
||||
const { fileMetadata } = sequelize.models
|
||||
fileMetadata.hasMany(Person, { foreignKey: 'imageFileId' })
|
||||
Person.belongsTo(fileMetadata, { as: 'imageFile', foreignKey: 'imageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
|
||||
|
||||
return Person
|
||||
}
|
@ -4,7 +4,7 @@ module.exports = (sequelize) => {
|
||||
class PlaybackSession extends Model {
|
||||
getMediaItem(options) {
|
||||
if (!this.mediaItemType) return Promise.resolve(null)
|
||||
const mixinMethodName = `get${this.mediaItemType}`
|
||||
const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}`
|
||||
return this[mixinMethodName](options)
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,7 @@ module.exports = (sequelize) => {
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true
|
||||
},
|
||||
MediaItemId: DataTypes.UUIDV4,
|
||||
mediaItemId: DataTypes.UUIDV4,
|
||||
mediaItemType: DataTypes.STRING,
|
||||
displayTitle: DataTypes.STRING,
|
||||
displayAuthor: DataTypes.STRING,
|
||||
@ -27,48 +27,53 @@ module.exports = (sequelize) => {
|
||||
serverVersion: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'PlaybackSession'
|
||||
modelName: 'playbackSession'
|
||||
})
|
||||
|
||||
const { Book, PodcastEpisode, User, Device } = sequelize.models
|
||||
const { book, podcastEpisode, user, device } = sequelize.models
|
||||
|
||||
User.hasMany(PlaybackSession)
|
||||
PlaybackSession.belongsTo(User)
|
||||
user.hasMany(PlaybackSession)
|
||||
PlaybackSession.belongsTo(user)
|
||||
|
||||
Device.hasMany(PlaybackSession)
|
||||
PlaybackSession.belongsTo(Device)
|
||||
device.hasMany(PlaybackSession)
|
||||
PlaybackSession.belongsTo(device)
|
||||
|
||||
Book.hasMany(PlaybackSession, {
|
||||
foreignKey: 'MediaItemId',
|
||||
book.hasMany(PlaybackSession, {
|
||||
foreignKey: 'mediaItemId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaItemType: 'Book'
|
||||
mediaItemType: 'book'
|
||||
}
|
||||
})
|
||||
PlaybackSession.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false })
|
||||
PlaybackSession.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false })
|
||||
|
||||
PodcastEpisode.hasOne(PlaybackSession, {
|
||||
foreignKey: 'MediaItemId',
|
||||
podcastEpisode.hasOne(PlaybackSession, {
|
||||
foreignKey: 'mediaItemId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaItemType: 'PodcastEpisode'
|
||||
mediaItemType: 'podcastEpisode'
|
||||
}
|
||||
})
|
||||
PlaybackSession.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false })
|
||||
PlaybackSession.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
|
||||
|
||||
PlaybackSession.addHook('afterFind', findResult => {
|
||||
if (!findResult) return
|
||||
|
||||
if (!Array.isArray(findResult)) findResult = [findResult]
|
||||
|
||||
for (const instance of findResult) {
|
||||
if (instance.mediaItemType === 'Book' && instance.Book !== undefined) {
|
||||
instance.MediaItem = instance.Book
|
||||
} else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) {
|
||||
instance.MediaItem = instance.PodcastEpisode
|
||||
if (instance.mediaItemType === 'book' && instance.book !== undefined) {
|
||||
instance.mediaItem = instance.book
|
||||
instance.dataValues.mediaItem = instance.dataValues.book
|
||||
} else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) {
|
||||
instance.mediaItem = instance.podcastEpisode
|
||||
instance.dataValues.mediaItem = instance.dataValues.podcastEpisode
|
||||
}
|
||||
// To prevent mistakes:
|
||||
delete instance.Book
|
||||
delete instance.dataValues.Book
|
||||
delete instance.PodcastEpisode
|
||||
delete instance.dataValues.PodcastEpisode
|
||||
delete instance.book
|
||||
delete instance.dataValues.book
|
||||
delete instance.podcastEpisode
|
||||
delete instance.dataValues.podcastEpisode
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -13,13 +13,13 @@ module.exports = (sequelize) => {
|
||||
date: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'PlaybackSessionListenTime'
|
||||
modelName: 'playbackSessionListenTime'
|
||||
})
|
||||
|
||||
const { PlaybackSession } = sequelize.models
|
||||
const { playbackSession } = sequelize.models
|
||||
|
||||
PlaybackSession.hasMany(PlaybackSessionListenTime)
|
||||
PlaybackSessionListenTime.belongsTo(PlaybackSession)
|
||||
playbackSession.hasMany(PlaybackSessionListenTime)
|
||||
PlaybackSessionListenTime.belongsTo(playbackSession)
|
||||
|
||||
return PlaybackSessionListenTime
|
||||
}
|
@ -13,15 +13,15 @@ module.exports = (sequelize) => {
|
||||
description: DataTypes.TEXT
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Playlist'
|
||||
modelName: 'playlist'
|
||||
})
|
||||
|
||||
const { Library, User } = sequelize.models
|
||||
Library.hasMany(Playlist)
|
||||
Playlist.belongsTo(Library)
|
||||
const { library, user } = sequelize.models
|
||||
library.hasMany(Playlist)
|
||||
Playlist.belongsTo(library)
|
||||
|
||||
User.hasMany(Playlist)
|
||||
Playlist.belongsTo(User)
|
||||
user.hasMany(Playlist)
|
||||
Playlist.belongsTo(user)
|
||||
|
||||
return Playlist
|
||||
}
|
@ -4,7 +4,7 @@ module.exports = (sequelize) => {
|
||||
class PlaylistMediaItem extends Model {
|
||||
getMediaItem(options) {
|
||||
if (!this.mediaItemType) return Promise.resolve(null)
|
||||
const mixinMethodName = `get${this.mediaItemType}`
|
||||
const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}`
|
||||
return this[mixinMethodName](options)
|
||||
}
|
||||
}
|
||||
@ -15,53 +15,58 @@ module.exports = (sequelize) => {
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true
|
||||
},
|
||||
MediaItemId: DataTypes.UUIDV4,
|
||||
mediaItemId: DataTypes.UUIDV4,
|
||||
mediaItemType: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
timestamps: true,
|
||||
updatedAt: false,
|
||||
modelName: 'PlaylistMediaItem'
|
||||
modelName: 'playlistMediaItem'
|
||||
})
|
||||
|
||||
const { Book, PodcastEpisode, Playlist } = sequelize.models
|
||||
const { book, podcastEpisode, playlist } = sequelize.models
|
||||
|
||||
Book.hasMany(PlaylistMediaItem, {
|
||||
foreignKey: 'MediaItemId',
|
||||
book.hasMany(PlaylistMediaItem, {
|
||||
foreignKey: 'mediaItemId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaItemType: 'Book'
|
||||
mediaItemType: 'book'
|
||||
}
|
||||
})
|
||||
PlaylistMediaItem.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false })
|
||||
PlaylistMediaItem.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false })
|
||||
|
||||
PodcastEpisode.hasOne(PlaylistMediaItem, {
|
||||
foreignKey: 'MediaItemId',
|
||||
podcastEpisode.hasOne(PlaylistMediaItem, {
|
||||
foreignKey: 'mediaItemId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
mediaItemType: 'PodcastEpisode'
|
||||
mediaItemType: 'podcastEpisode'
|
||||
}
|
||||
})
|
||||
PlaylistMediaItem.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false })
|
||||
PlaylistMediaItem.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
|
||||
|
||||
PlaylistMediaItem.addHook('afterFind', findResult => {
|
||||
if (!findResult) return
|
||||
|
||||
if (!Array.isArray(findResult)) findResult = [findResult]
|
||||
|
||||
for (const instance of findResult) {
|
||||
if (instance.mediaItemType === 'Book' && instance.Book !== undefined) {
|
||||
instance.MediaItem = instance.Book
|
||||
} else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) {
|
||||
instance.MediaItem = instance.PodcastEpisode
|
||||
if (instance.mediaItemType === 'book' && instance.book !== undefined) {
|
||||
instance.mediaItem = instance.book
|
||||
instance.dataValues.mediaItem = instance.dataValues.book
|
||||
} else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) {
|
||||
instance.mediaItem = instance.podcastEpisode
|
||||
instance.dataValues.mediaItem = instance.dataValues.podcastEpisode
|
||||
}
|
||||
// To prevent mistakes:
|
||||
delete instance.Book
|
||||
delete instance.dataValues.Book
|
||||
delete instance.PodcastEpisode
|
||||
delete instance.dataValues.PodcastEpisode
|
||||
delete instance.book
|
||||
delete instance.dataValues.book
|
||||
delete instance.podcastEpisode
|
||||
delete instance.dataValues.podcastEpisode
|
||||
}
|
||||
})
|
||||
|
||||
Playlist.hasMany(PlaylistMediaItem)
|
||||
PlaylistMediaItem.belongsTo(Playlist)
|
||||
playlist.hasMany(PlaylistMediaItem)
|
||||
PlaylistMediaItem.belongsTo(playlist)
|
||||
|
||||
return PlaylistMediaItem
|
||||
}
|
@ -9,7 +9,6 @@ module.exports = (sequelize) => {
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true
|
||||
},
|
||||
// Metadata
|
||||
title: DataTypes.STRING,
|
||||
author: DataTypes.STRING,
|
||||
releaseDate: DataTypes.STRING,
|
||||
@ -32,15 +31,13 @@ module.exports = (sequelize) => {
|
||||
lastCoverSearch: DataTypes.DATE
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Podcast'
|
||||
modelName: 'podcast'
|
||||
})
|
||||
|
||||
const { LibraryItem, FileMetadata } = sequelize.models
|
||||
LibraryItem.hasOne(Podcast)
|
||||
Podcast.belongsTo(LibraryItem)
|
||||
const { fileMetadata } = sequelize.models
|
||||
|
||||
FileMetadata.hasOne(Podcast, { foreignKey: 'ImageFileId' })
|
||||
Podcast.belongsTo(FileMetadata, { as: 'ImageFile', foreignKey: 'ImageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
|
||||
fileMetadata.hasOne(Podcast, { foreignKey: 'imageFileId' })
|
||||
Podcast.belongsTo(fileMetadata, { as: 'imageFile', foreignKey: 'imageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
|
||||
|
||||
return Podcast
|
||||
}
|
@ -23,12 +23,12 @@ module.exports = (sequelize) => {
|
||||
publishedAt: DataTypes.DATE
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'PodcastEpisode'
|
||||
modelName: 'podcastEpisode'
|
||||
})
|
||||
|
||||
const { Podcast } = sequelize.models
|
||||
Podcast.hasMany(PodcastEpisode)
|
||||
PodcastEpisode.belongsTo(Podcast)
|
||||
const { podcast } = sequelize.models
|
||||
podcast.hasMany(PodcastEpisode)
|
||||
PodcastEpisode.belongsTo(podcast)
|
||||
|
||||
return PodcastEpisode
|
||||
}
|
@ -11,21 +11,21 @@ module.exports = (sequelize) => {
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'PodcastGenre',
|
||||
modelName: 'podcastGenre',
|
||||
timestamps: false
|
||||
})
|
||||
|
||||
// Super Many-to-Many
|
||||
// ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship
|
||||
const { Podcast, Genre } = sequelize.models
|
||||
Podcast.belongsToMany(Genre, { through: PodcastGenre })
|
||||
Genre.belongsToMany(Podcast, { through: PodcastGenre })
|
||||
const { podcast, genre } = sequelize.models
|
||||
podcast.belongsToMany(genre, { through: PodcastGenre })
|
||||
genre.belongsToMany(podcast, { through: PodcastGenre })
|
||||
|
||||
Podcast.hasMany(PodcastGenre)
|
||||
PodcastGenre.belongsTo(Podcast)
|
||||
podcast.hasMany(PodcastGenre)
|
||||
PodcastGenre.belongsTo(podcast)
|
||||
|
||||
Genre.hasMany(PodcastGenre)
|
||||
PodcastGenre.belongsTo(Genre)
|
||||
genre.hasMany(PodcastGenre)
|
||||
PodcastGenre.belongsTo(genre)
|
||||
|
||||
return PodcastGenre
|
||||
}
|
@ -11,21 +11,21 @@ module.exports = (sequelize) => {
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'PodcastTag',
|
||||
modelName: 'podcastTag',
|
||||
timestamps: false
|
||||
})
|
||||
|
||||
// Super Many-to-Many
|
||||
// ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship
|
||||
const { Podcast, Tag } = sequelize.models
|
||||
Podcast.belongsToMany(Tag, { through: PodcastTag })
|
||||
Tag.belongsToMany(Podcast, { through: PodcastTag })
|
||||
const { podcast, tag } = sequelize.models
|
||||
podcast.belongsToMany(tag, { through: PodcastTag })
|
||||
tag.belongsToMany(podcast, { through: PodcastTag })
|
||||
|
||||
Podcast.hasMany(PodcastTag)
|
||||
PodcastTag.belongsTo(Podcast)
|
||||
podcast.hasMany(PodcastTag)
|
||||
PodcastTag.belongsTo(podcast)
|
||||
|
||||
Tag.hasMany(PodcastTag)
|
||||
PodcastTag.belongsTo(Tag)
|
||||
tag.hasMany(PodcastTag)
|
||||
PodcastTag.belongsTo(tag)
|
||||
|
||||
return PodcastTag
|
||||
}
|
@ -13,7 +13,7 @@ module.exports = (sequelize) => {
|
||||
description: DataTypes.TEXT
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Series'
|
||||
modelName: 'series'
|
||||
})
|
||||
|
||||
return Series
|
||||
|
@ -12,7 +12,7 @@ module.exports = (sequelize) => {
|
||||
type: DataTypes.INTEGER
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Setting'
|
||||
modelName: 'setting'
|
||||
})
|
||||
|
||||
return Setting
|
||||
|
@ -13,7 +13,7 @@ module.exports = (sequelize) => {
|
||||
cleanName: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Tag'
|
||||
modelName: 'tag'
|
||||
})
|
||||
|
||||
return Tag
|
||||
|
@ -26,7 +26,7 @@ module.exports = (sequelize) => {
|
||||
extraData: DataTypes.JSON
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'User'
|
||||
modelName: 'user'
|
||||
})
|
||||
|
||||
return User
|
||||
|
@ -13,13 +13,13 @@ module.exports = (sequelize) => {
|
||||
value: DataTypes.STRING
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'UserPermission'
|
||||
modelName: 'userPermission'
|
||||
})
|
||||
|
||||
const { User } = sequelize.models
|
||||
const { user } = sequelize.models
|
||||
|
||||
User.hasMany(UserPermission)
|
||||
UserPermission.belongsTo(User)
|
||||
user.hasMany(UserPermission)
|
||||
UserPermission.belongsTo(user)
|
||||
|
||||
return UserPermission
|
||||
}
|
15
server/routers/ApiRouter2.js
Normal file
15
server/routers/ApiRouter2.js
Normal file
@ -0,0 +1,15 @@
|
||||
const express = require('express')
|
||||
const LibraryItemController = require('../controllers2/LibraryItemController')
|
||||
|
||||
class ApiRouter2 {
|
||||
constructor(Server) {
|
||||
this.router = express()
|
||||
this.router.disable('x-powered-by')
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.router.get('/items/:id', LibraryItemController.get.bind(this))
|
||||
}
|
||||
}
|
||||
module.exports = ApiRouter2
|
@ -1,6 +1,5 @@
|
||||
const Path = require('path')
|
||||
const uuidv4 = require("uuid").v4
|
||||
const package = require('../../../package.json')
|
||||
const { AudioMimeType } = require('../constants')
|
||||
const Logger = require('../../Logger')
|
||||
const Database = require('../../Database')
|
||||
@ -20,49 +19,50 @@ const oldDbIdMap = {
|
||||
files: {}, // key is fullpath
|
||||
podcastEpisodes: {},
|
||||
books: {}, // key is library item id
|
||||
podcasts: {}, // key is library item id
|
||||
devices: {} // key is a json stringify of the old DeviceInfo data
|
||||
}
|
||||
const newRecords = {
|
||||
User: [],
|
||||
UserPermission: [],
|
||||
Library: [],
|
||||
LibraryFolder: [],
|
||||
LibrarySetting: [],
|
||||
FileMetadata: [],
|
||||
Person: [],
|
||||
LibraryItem: [],
|
||||
LibraryFile: [],
|
||||
EBookFile: [],
|
||||
Book: [],
|
||||
BookAuthor: [],
|
||||
BookNarrator: [],
|
||||
BookChapter: [],
|
||||
Tag: [],
|
||||
BookTag: [],
|
||||
Genre: [],
|
||||
BookGenre: [],
|
||||
Series: [],
|
||||
BookSeries: [],
|
||||
Podcast: [],
|
||||
PodcastTag: [],
|
||||
PodcastGenre: [],
|
||||
PodcastEpisode: [],
|
||||
MediaProgress: [],
|
||||
AudioBookmark: [],
|
||||
MediaFile: [],
|
||||
MediaStream: [],
|
||||
AudioTrack: [],
|
||||
Device: [],
|
||||
PlaybackSession: [],
|
||||
PlaybackSessionListenTime: [],
|
||||
Collection: [],
|
||||
CollectionBook: [],
|
||||
Playlist: [],
|
||||
PlaylistMediaItem: [],
|
||||
Feed: [],
|
||||
FeedEpisode: [],
|
||||
Setting: [],
|
||||
Notification: []
|
||||
user: [],
|
||||
userPermission: [],
|
||||
library: [],
|
||||
libraryFolder: [],
|
||||
librarySetting: [],
|
||||
fileMetadata: [],
|
||||
person: [],
|
||||
eBookFile: [],
|
||||
book: [],
|
||||
podcast: [],
|
||||
libraryItem: [],
|
||||
libraryFile: [],
|
||||
bookAuthor: [],
|
||||
bookNarrator: [],
|
||||
bookChapter: [],
|
||||
tag: [],
|
||||
bookTag: [],
|
||||
genre: [],
|
||||
bookGenre: [],
|
||||
series: [],
|
||||
bookSeries: [],
|
||||
podcastTag: [],
|
||||
podcastGenre: [],
|
||||
podcastEpisode: [],
|
||||
mediaProgress: [],
|
||||
audioBookmark: [],
|
||||
mediaFile: [],
|
||||
mediaStream: [],
|
||||
audioTrack: [],
|
||||
device: [],
|
||||
playbackSession: [],
|
||||
playbackSessionListenTime: [],
|
||||
collection: [],
|
||||
collectionBook: [],
|
||||
playlist: [],
|
||||
playlistMediaItem: [],
|
||||
feed: [],
|
||||
feedEpisode: [],
|
||||
setting: [],
|
||||
notification: []
|
||||
}
|
||||
|
||||
function getDeviceInfoString(deviceInfo, UserId) {
|
||||
@ -102,10 +102,10 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
//
|
||||
// Migrate ImageFile
|
||||
//
|
||||
let ImageFileId = null
|
||||
let imageFileId = null
|
||||
if (oldBook.coverPath) {
|
||||
ImageFileId = oldDbIdMap.files[oldBook.coverPath] || null
|
||||
if (!ImageFileId) {
|
||||
imageFileId = oldDbIdMap.files[oldBook.coverPath] || null
|
||||
if (!imageFileId) {
|
||||
const FileMetadata = {
|
||||
id: uuidv4(),
|
||||
filename: Path.basename(oldBook.coverPath),
|
||||
@ -114,26 +114,28 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt
|
||||
}
|
||||
newRecords.FileMetadata.push(FileMetadata)
|
||||
newRecords.fileMetadata.push(FileMetadata)
|
||||
oldDbIdMap.files[oldBook.coverPath] = FileMetadata.id
|
||||
ImageFileId = FileMetadata.id
|
||||
imageFileId = FileMetadata.id
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Migrate EBookFile
|
||||
//
|
||||
let EBookFileId = null
|
||||
let eBookFileId = null
|
||||
if (oldBook.ebookFile) {
|
||||
if (oldDbIdMap.files[oldBook.ebookFile.metadata?.path]) {
|
||||
const ext = oldBook.ebookFile.metadata.ext || ''
|
||||
const EBookFile = {
|
||||
id: uuidv4(),
|
||||
FileMetadataId: oldDbIdMap.files[oldBook.ebookFile.metadata?.path]
|
||||
format: ext.toLowerCase().slice(1),
|
||||
fileMetadataId: oldDbIdMap.files[oldBook.ebookFile.metadata.path]
|
||||
}
|
||||
newRecords.EBookFile.push(EBookFile)
|
||||
EBookFileId = EBookFile.id
|
||||
newRecords.eBookFile.push(EBookFile)
|
||||
eBookFileId = EBookFile.id
|
||||
} else {
|
||||
Logger.warn(`[dbMigration] migrateBook: `)
|
||||
Logger.warn(`[dbMigration] migrateBook: Unable to find ebook file`)
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,11 +158,10 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
lastCoverSearch: oldBook.lastCoverSearch,
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt,
|
||||
ImageFileId,
|
||||
EBookFileId,
|
||||
LibraryItemId: LibraryItem.id,
|
||||
imageFileId,
|
||||
eBookFileId
|
||||
}
|
||||
newRecords.Book.push(Book)
|
||||
newRecords.book.push(Book)
|
||||
oldDbIdMap.books[oldLibraryItem.id] = Book.id
|
||||
|
||||
//
|
||||
@ -169,8 +170,8 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
const oldAudioFiles = oldBook.audioFiles
|
||||
let startOffset = 0
|
||||
for (const oldAudioFile of oldAudioFiles) {
|
||||
const FileMetadataId = oldDbIdMap.files[oldAudioFile.metadata.path]
|
||||
if (!FileMetadataId) {
|
||||
const fileMetadataId = oldDbIdMap.files[oldAudioFile.metadata.path]
|
||||
if (!fileMetadataId) {
|
||||
Logger.warn(`[dbMigration] migrateBook: File metadata not found for audio file "${oldAudioFile.metadata.path}"`)
|
||||
continue
|
||||
}
|
||||
@ -187,9 +188,9 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
tags: cleanAudioFileMetaTags(oldAudioFile.metaTags),
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt,
|
||||
FileMetadataId
|
||||
fileMetadataId
|
||||
}
|
||||
newRecords.MediaFile.push(MediaFile)
|
||||
newRecords.mediaFile.push(MediaFile)
|
||||
|
||||
const MediaStream = {
|
||||
id: uuidv4(),
|
||||
@ -207,9 +208,9 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
chapters: oldAudioFile.chapters,
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt,
|
||||
MediaFileId: MediaFile.id
|
||||
mediaFileId: MediaFile.id
|
||||
}
|
||||
newRecords.MediaStream.push(MediaStream)
|
||||
newRecords.mediaStream.push(MediaStream)
|
||||
|
||||
if (oldAudioFile.embeddedCoverArt) {
|
||||
const CoverMediaStream = {
|
||||
@ -220,17 +221,17 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
default: true,
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt,
|
||||
MediaFileId: MediaFile.id
|
||||
mediaFileId: MediaFile.id
|
||||
}
|
||||
newRecords.MediaStream.push(CoverMediaStream)
|
||||
newRecords.mediaStream.push(CoverMediaStream)
|
||||
}
|
||||
|
||||
const include = !oldAudioFile.exclude && !oldAudioFile.invalid
|
||||
|
||||
const AudioTrack = {
|
||||
id: uuidv4(),
|
||||
MediaItemId: Book.id,
|
||||
mediaItemType: 'Book',
|
||||
mediaItemId: Book.id,
|
||||
mediaItemType: 'book',
|
||||
index: oldAudioFile.index,
|
||||
startOffset: include ? startOffset : null,
|
||||
duration: oldAudioFile.duration,
|
||||
@ -241,9 +242,9 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
discNumber: oldAudioFile.discNumFromMeta || oldAudioFile.discNumFromFilename,
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt,
|
||||
MediaFileId: MediaFile.id
|
||||
mediaFileId: MediaFile.id
|
||||
}
|
||||
newRecords.AudioTrack.push(AudioTrack)
|
||||
newRecords.audioTrack.push(AudioTrack)
|
||||
|
||||
if (include) {
|
||||
startOffset += AudioTrack.duration
|
||||
@ -267,13 +268,13 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
updatedAt: LibraryItem.updatedAt
|
||||
}
|
||||
tagId = Tag.id
|
||||
newRecords.Tag.push(Tag)
|
||||
newRecords.tag.push(Tag)
|
||||
}
|
||||
|
||||
newRecords.BookTag.push({
|
||||
newRecords.bookTag.push({
|
||||
id: uuidv4(),
|
||||
BookId: Book.id,
|
||||
TagId: tagId
|
||||
bookId: Book.id,
|
||||
tagId
|
||||
})
|
||||
}
|
||||
|
||||
@ -281,7 +282,7 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
// Migrate BookChapters
|
||||
//
|
||||
for (const oldChapter of oldBook.chapters) {
|
||||
newRecords.BookChapter.push({
|
||||
newRecords.bookChapter.push({
|
||||
id: uuidv4(),
|
||||
index: oldChapter.id,
|
||||
start: oldChapter.start,
|
||||
@ -289,7 +290,7 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
title: oldChapter.title,
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt,
|
||||
BookId: Book.id
|
||||
bookId: Book.id
|
||||
})
|
||||
}
|
||||
|
||||
@ -311,13 +312,13 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
updatedAt: LibraryItem.updatedAt
|
||||
}
|
||||
genreId = Genre.id
|
||||
newRecords.Genre.push(Genre)
|
||||
newRecords.genre.push(Genre)
|
||||
}
|
||||
|
||||
newRecords.BookGenre.push({
|
||||
newRecords.bookGenre.push({
|
||||
id: uuidv4(),
|
||||
BookId: Book.id,
|
||||
GenreId: genreId
|
||||
bookId: Book.id,
|
||||
genreId
|
||||
})
|
||||
}
|
||||
|
||||
@ -326,10 +327,10 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
//
|
||||
for (const oldBookAuthor of oldBook.metadata.authors) {
|
||||
if (oldDbIdMap.people[oldBookAuthor.id]) {
|
||||
newRecords.BookAuthor.push({
|
||||
newRecords.bookAuthor.push({
|
||||
id: uuidv4(),
|
||||
PersonId: oldDbIdMap.people[oldBookAuthor.id],
|
||||
BookId: Book.id
|
||||
authorId: oldDbIdMap.people[oldBookAuthor.id],
|
||||
bookId: Book.id
|
||||
})
|
||||
} else {
|
||||
Logger.warn(`[dbMigration] migrateBook: Book author not found "${oldBookAuthor.name}"`)
|
||||
@ -340,23 +341,23 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
// Migrate BookNarrators
|
||||
//
|
||||
for (const oldBookNarrator of oldBook.metadata.narrators) {
|
||||
let PersonId = oldDbIdMap.people[oldBookNarrator]
|
||||
if (!PersonId) {
|
||||
let personId = oldDbIdMap.people[oldBookNarrator]
|
||||
if (!personId) {
|
||||
const Person = {
|
||||
id: uuidv4(),
|
||||
type: 'Narrator',
|
||||
type: 'narrator',
|
||||
name: oldBookNarrator,
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt
|
||||
}
|
||||
newRecords.Person.push(Person)
|
||||
PersonId = Person.id
|
||||
newRecords.person.push(Person)
|
||||
personId = Person.id
|
||||
}
|
||||
|
||||
newRecords.BookNarrator.push({
|
||||
newRecords.bookNarrator.push({
|
||||
id: uuidv4(),
|
||||
PersonId,
|
||||
BookId: Book.id
|
||||
narratorId: personId,
|
||||
bookId: Book.id
|
||||
})
|
||||
}
|
||||
|
||||
@ -368,10 +369,10 @@ function migrateBook(oldLibraryItem, LibraryItem) {
|
||||
const BookSeries = {
|
||||
id: uuidv4(),
|
||||
sequence: oldBookSeries.sequence,
|
||||
SeriesId: oldDbIdMap.series[oldBookSeries.id],
|
||||
BookId: Book.id
|
||||
seriesId: oldDbIdMap.series[oldBookSeries.id],
|
||||
bookId: Book.id
|
||||
}
|
||||
newRecords.BookSeries.push(BookSeries)
|
||||
newRecords.bookSeries.push(BookSeries)
|
||||
} else {
|
||||
Logger.warn(`[dbMigration] migrateBook: Series not found "${oldBookSeries.name}"`)
|
||||
}
|
||||
@ -385,10 +386,10 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
||||
//
|
||||
// Migrate ImageFile
|
||||
//
|
||||
let ImageFileId = null
|
||||
let imageFileId = null
|
||||
if (oldPodcast.coverPath) {
|
||||
ImageFileId = oldDbIdMap.files[oldPodcast.coverPath] || null
|
||||
if (!ImageFileId) {
|
||||
imageFileId = oldDbIdMap.files[oldPodcast.coverPath] || null
|
||||
if (!imageFileId) {
|
||||
const FileMetadata = {
|
||||
id: uuidv4(),
|
||||
filename: Path.basename(oldPodcast.coverPath),
|
||||
@ -397,9 +398,9 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt
|
||||
}
|
||||
newRecords.FileMetadata.push(FileMetadata)
|
||||
newRecords.fileMetadata.push(FileMetadata)
|
||||
oldDbIdMap.files[oldPodcast.coverPath] = FileMetadata.id
|
||||
ImageFileId = FileMetadata.id
|
||||
imageFileId = FileMetadata.id
|
||||
}
|
||||
}
|
||||
|
||||
@ -429,10 +430,10 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
||||
lastCoverSearch: oldPodcast.lastCoverSearch,
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt,
|
||||
ImageFileId,
|
||||
LibraryItemId: LibraryItem.id
|
||||
imageFileId,
|
||||
}
|
||||
newRecords.Podcast.push(Podcast)
|
||||
newRecords.podcast.push(Podcast)
|
||||
oldDbIdMap.podcasts[oldLibraryItem.id] = Podcast.id
|
||||
|
||||
//
|
||||
// Migrate Tags
|
||||
@ -451,13 +452,13 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
||||
updatedAt: LibraryItem.updatedAt
|
||||
}
|
||||
tagId = Tag.id
|
||||
newRecords.Tag.push(Tag)
|
||||
newRecords.tag.push(Tag)
|
||||
}
|
||||
|
||||
newRecords.PodcastTag.push({
|
||||
newRecords.podcastTag.push({
|
||||
id: uuidv4(),
|
||||
PodcastId: Podcast.id,
|
||||
TagId: tagId
|
||||
podcastId: Podcast.id,
|
||||
tagId
|
||||
})
|
||||
}
|
||||
|
||||
@ -478,13 +479,13 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
||||
updatedAt: LibraryItem.updatedAt
|
||||
}
|
||||
genreId = Genre.id
|
||||
newRecords.Genre.push(Genre)
|
||||
newRecords.genre.push(Genre)
|
||||
}
|
||||
|
||||
newRecords.PodcastGenre.push({
|
||||
newRecords.podcastGenre.push({
|
||||
id: uuidv4(),
|
||||
PodcastId: Podcast.id,
|
||||
GenreId: genreId
|
||||
podcastId: Podcast.id,
|
||||
genreId
|
||||
})
|
||||
}
|
||||
|
||||
@ -494,8 +495,8 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
||||
const oldEpisodes = oldPodcast.episodes || []
|
||||
for (const oldEpisode of oldEpisodes) {
|
||||
const oldAudioFile = oldEpisode.audioFile
|
||||
const FileMetadataId = oldDbIdMap.files[oldAudioFile.metadata.path]
|
||||
if (!FileMetadataId) {
|
||||
const fileMetadataId = oldDbIdMap.files[oldAudioFile.metadata.path]
|
||||
if (!fileMetadataId) {
|
||||
Logger.warn(`[dbMigration] migratePodcast: File metadata not found for audio file "${oldAudioFile.metadata.path}"`)
|
||||
continue
|
||||
}
|
||||
@ -516,9 +517,9 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
||||
publishedAt: oldEpisode.publishedAt,
|
||||
createdAt: oldEpisode.addedAt,
|
||||
updatedAt: oldEpisode.updatedAt,
|
||||
PodcastId: Podcast.id
|
||||
podcastId: Podcast.id
|
||||
}
|
||||
newRecords.PodcastEpisode.push(PodcastEpisode)
|
||||
newRecords.podcastEpisode.push(PodcastEpisode)
|
||||
oldDbIdMap.podcastEpisodes[oldEpisode.id] = PodcastEpisode.id
|
||||
|
||||
//
|
||||
@ -535,9 +536,9 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
||||
tags: cleanAudioFileMetaTags(oldAudioFile.metaTags),
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt,
|
||||
FileMetadataId
|
||||
fileMetadataId
|
||||
}
|
||||
newRecords.MediaFile.push(MediaFile)
|
||||
newRecords.mediaFile.push(MediaFile)
|
||||
|
||||
const MediaStream = {
|
||||
id: uuidv4(),
|
||||
@ -555,9 +556,9 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
||||
chapters: oldAudioFile.chapters,
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt,
|
||||
MediaFileId: MediaFile.id
|
||||
mediaFileId: MediaFile.id
|
||||
}
|
||||
newRecords.MediaStream.push(MediaStream)
|
||||
newRecords.mediaStream.push(MediaStream)
|
||||
|
||||
if (oldAudioFile.embeddedCoverArt) {
|
||||
const CoverMediaStream = {
|
||||
@ -568,15 +569,15 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
||||
default: true,
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt,
|
||||
MediaFileId: MediaFile.id
|
||||
mediaFileId: MediaFile.id
|
||||
}
|
||||
newRecords.MediaStream.push(CoverMediaStream)
|
||||
newRecords.mediaStream.push(CoverMediaStream)
|
||||
}
|
||||
|
||||
const AudioTrack = {
|
||||
id: uuidv4(),
|
||||
MediaItemId: Podcast.id,
|
||||
mediaItemType: 'Podcast',
|
||||
mediaItemId: PodcastEpisode.id,
|
||||
mediaItemType: 'podcastEpisode',
|
||||
index: oldAudioFile.index,
|
||||
startOffset: 0,
|
||||
duration: oldAudioFile.duration,
|
||||
@ -587,16 +588,16 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
||||
discNumber: oldAudioFile.discNumFromMeta || oldAudioFile.discNumFromFilename,
|
||||
createdAt: LibraryItem.createdAt,
|
||||
updatedAt: LibraryItem.updatedAt,
|
||||
MediaFileId: MediaFile.id
|
||||
mediaFileId: MediaFile.id
|
||||
}
|
||||
newRecords.AudioTrack.push(AudioTrack)
|
||||
newRecords.audioTrack.push(AudioTrack)
|
||||
}
|
||||
}
|
||||
|
||||
function migrateLibraryItems(oldLibraryItems) {
|
||||
for (const oldLibraryItem of oldLibraryItems) {
|
||||
const LibraryId = oldDbIdMap.libraryFolders[oldLibraryItem.folderId]
|
||||
if (!LibraryId) {
|
||||
const libraryFolderId = oldDbIdMap.libraryFolders[oldLibraryItem.folderId]
|
||||
if (!libraryFolderId) {
|
||||
Logger.error(`[dbMigration] migrateLibraryItems: Old library folder id not found "${oldLibraryItem.folderId}"`)
|
||||
continue
|
||||
}
|
||||
@ -609,6 +610,7 @@ function migrateLibraryItems(oldLibraryItems) {
|
||||
ino: oldLibraryItem.ino,
|
||||
path: oldLibraryItem.path,
|
||||
relPath: oldLibraryItem.relPath,
|
||||
mediaId: null, // set below
|
||||
mediaType: oldLibraryItem.mediaType,
|
||||
isFile: !!oldLibraryItem.isFile,
|
||||
isMissing: !!oldLibraryItem.isMissing,
|
||||
@ -620,10 +622,10 @@ function migrateLibraryItems(oldLibraryItems) {
|
||||
lastScanVersion: oldLibraryItem.scanVersion,
|
||||
createdAt: oldLibraryItem.addedAt,
|
||||
updatedAt: oldLibraryItem.updatedAt,
|
||||
LibraryId
|
||||
libraryFolderId
|
||||
}
|
||||
oldDbIdMap.libraryItems[oldLibraryItem.id] = LibraryItem.id
|
||||
newRecords.LibraryItem.push(LibraryItem)
|
||||
newRecords.libraryItem.push(LibraryItem)
|
||||
|
||||
//
|
||||
// Migrate LibraryFiles
|
||||
@ -642,17 +644,17 @@ function migrateLibraryItems(oldLibraryItems) {
|
||||
createdAt: oldLibraryFile.addedAt || Date.now(),
|
||||
updatedAt: oldLibraryFile.updatedAt || Date.now()
|
||||
}
|
||||
newRecords.FileMetadata.push(FileMetadata)
|
||||
newRecords.fileMetadata.push(FileMetadata)
|
||||
oldDbIdMap.files[FileMetadata.path] = FileMetadata.id
|
||||
|
||||
const LibraryFile = {
|
||||
id: uuidv4(),
|
||||
createdAt: FileMetadata.createdAt,
|
||||
updatedAt: FileMetadata.updatedAt,
|
||||
FileMetadataId: FileMetadata.id,
|
||||
LibraryItemId: LibraryItem.id
|
||||
fileMetadataId: FileMetadata.id,
|
||||
libraryItemId: LibraryItem.id
|
||||
}
|
||||
newRecords.LibraryFile.push(LibraryFile)
|
||||
newRecords.libraryFile.push(LibraryFile)
|
||||
}
|
||||
|
||||
//
|
||||
@ -660,8 +662,10 @@ function migrateLibraryItems(oldLibraryItems) {
|
||||
//
|
||||
if (oldLibraryItem.mediaType === 'book') {
|
||||
migrateBook(oldLibraryItem, LibraryItem)
|
||||
LibraryItem.mediaId = oldDbIdMap.books[oldLibraryItem.id]
|
||||
} else if (oldLibraryItem.mediaType === 'podcast') {
|
||||
migratePodcast(oldLibraryItem, LibraryItem)
|
||||
LibraryItem.mediaId = oldDbIdMap.podcasts[oldLibraryItem.id]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -682,20 +686,20 @@ function migrateLibraries(oldLibraries) {
|
||||
updatedAt: oldLibrary.lastUpdate
|
||||
}
|
||||
oldDbIdMap.libraries[oldLibrary.id] = Library.id
|
||||
newRecords.Library.push(Library)
|
||||
newRecords.library.push(Library)
|
||||
|
||||
//
|
||||
// Migrate LibrarySettings
|
||||
//
|
||||
const oldLibrarySettings = oldLibrary.settings || {}
|
||||
for (const oldSettingsKey in oldLibrarySettings) {
|
||||
newRecords.LibrarySetting.push({
|
||||
newRecords.librarySetting.push({
|
||||
id: uuidv4(),
|
||||
key: oldSettingsKey,
|
||||
value: oldLibrarySettings[oldSettingsKey],
|
||||
createdAt: oldLibrary.createdAt,
|
||||
updatedAt: oldLibrary.lastUpdate,
|
||||
LibraryId: Library.id
|
||||
libraryId: Library.id
|
||||
})
|
||||
}
|
||||
|
||||
@ -708,10 +712,10 @@ function migrateLibraries(oldLibraries) {
|
||||
path: oldFolder.fullPath,
|
||||
createdAt: oldFolder.addedAt,
|
||||
updatedAt: oldLibrary.lastUpdate,
|
||||
LibraryId: Library.id
|
||||
libraryId: Library.id
|
||||
}
|
||||
oldDbIdMap.libraryFolders[oldFolder.id] = LibraryFolder.id
|
||||
newRecords.LibraryFolder.push(LibraryFolder)
|
||||
newRecords.libraryFolder.push(LibraryFolder)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -728,22 +732,22 @@ function migrateAuthors(oldAuthors) {
|
||||
createdAt: oldAuthor.addedAt || Date.now(),
|
||||
updatedAt: oldAuthor.updatedAt || Date.now()
|
||||
}
|
||||
newRecords.FileMetadata.push(FileMetadata)
|
||||
newRecords.fileMetadata.push(FileMetadata)
|
||||
imageFileId = FileMetadata.id
|
||||
}
|
||||
|
||||
const Person = {
|
||||
id: uuidv4(),
|
||||
type: 'Author',
|
||||
type: 'author',
|
||||
name: oldAuthor.name,
|
||||
asin: oldAuthor.asin || null,
|
||||
description: oldAuthor.description,
|
||||
createdAt: oldAuthor.addedAt || Date.now(),
|
||||
updatedAt: oldAuthor.updatedAt || Date.now(),
|
||||
ImageFileId: imageFileId
|
||||
imageFileId
|
||||
}
|
||||
oldDbIdMap.people[oldAuthor.id] = Person.id
|
||||
newRecords.Person.push(Person)
|
||||
newRecords.person.push(Person)
|
||||
}
|
||||
}
|
||||
|
||||
@ -757,7 +761,7 @@ function migrateSeries(oldSerieses) {
|
||||
updatedAt: oldSeries.updatedAt || Date.now()
|
||||
}
|
||||
oldDbIdMap.series[oldSeries.id] = Series.id
|
||||
newRecords.Series.push(Series)
|
||||
newRecords.series.push(Series)
|
||||
}
|
||||
}
|
||||
|
||||
@ -780,7 +784,7 @@ function migrateUsers(oldUsers) {
|
||||
createdAt: oldUser.createdAt || Date.now()
|
||||
}
|
||||
oldDbIdMap.users[oldUser.id] = User.id
|
||||
newRecords.User.push(User)
|
||||
newRecords.user.push(User)
|
||||
|
||||
//
|
||||
// Migrate UserPermissions
|
||||
@ -792,9 +796,9 @@ function migrateUsers(oldUsers) {
|
||||
key: oldUserPermission,
|
||||
value: !!oldUser.permissions[oldUserPermission],
|
||||
createdAt: User.createdAt,
|
||||
UserId: User.id
|
||||
userId: User.id
|
||||
}
|
||||
newRecords.UserPermission.push(UserPermission)
|
||||
newRecords.userPermission.push(UserPermission)
|
||||
}
|
||||
}
|
||||
if (oldUser.librariesAccessible?.length) {
|
||||
@ -803,9 +807,9 @@ function migrateUsers(oldUsers) {
|
||||
key: 'librariesAccessible',
|
||||
value: JSON.stringify(oldUser.librariesAccessible),
|
||||
createdAt: User.createdAt,
|
||||
UserId: User.id
|
||||
userId: User.id
|
||||
}
|
||||
newRecords.UserPermission.push(UserPermission)
|
||||
newRecords.userPermission.push(UserPermission)
|
||||
}
|
||||
if (oldUser.itemTagsAccessible?.length) {
|
||||
const UserPermission = {
|
||||
@ -813,32 +817,32 @@ function migrateUsers(oldUsers) {
|
||||
key: 'itemTagsAccessible',
|
||||
value: JSON.stringify(oldUser.itemTagsAccessible),
|
||||
createdAt: User.createdAt,
|
||||
UserId: User.id
|
||||
userId: User.id
|
||||
}
|
||||
newRecords.UserPermission.push(UserPermission)
|
||||
newRecords.userPermission.push(UserPermission)
|
||||
}
|
||||
|
||||
//
|
||||
// Migrate MediaProgress
|
||||
//
|
||||
for (const oldMediaProgress of oldUser.mediaProgress) {
|
||||
let mediaItemType = 'Book'
|
||||
let MediaItemId = null
|
||||
let mediaItemType = 'book'
|
||||
let mediaItemId = null
|
||||
if (oldMediaProgress.episodeId) {
|
||||
mediaItemType = 'PodcastEpisode'
|
||||
MediaItemId = oldDbIdMap.podcastEpisodes[oldMediaProgress.episodeId]
|
||||
mediaItemType = 'podcastEpisode'
|
||||
mediaItemId = oldDbIdMap.podcastEpisodes[oldMediaProgress.episodeId]
|
||||
} else {
|
||||
MediaItemId = oldDbIdMap.books[oldMediaProgress.libraryItemId]
|
||||
mediaItemId = oldDbIdMap.books[oldMediaProgress.libraryItemId]
|
||||
}
|
||||
|
||||
if (!MediaItemId) {
|
||||
if (!mediaItemId) {
|
||||
Logger.warn(`[dbMigration] migrateUsers: Unable to find media item for media progress "${oldMediaProgress.id}"`)
|
||||
continue
|
||||
}
|
||||
|
||||
const MediaProgress = {
|
||||
id: uuidv4(),
|
||||
MediaItemId,
|
||||
mediaItemId,
|
||||
mediaItemType,
|
||||
duration: oldMediaProgress.duration,
|
||||
currentTime: oldMediaProgress.currentTime,
|
||||
@ -847,49 +851,49 @@ function migrateUsers(oldUsers) {
|
||||
finishedAt: oldMediaProgress.finishedAt,
|
||||
createdAt: oldMediaProgress.startedAt || oldMediaProgress.lastUpdate,
|
||||
updatedAt: oldMediaProgress.lastUpdate,
|
||||
UserId: User.id
|
||||
userId: User.id
|
||||
}
|
||||
newRecords.MediaProgress.push(MediaProgress)
|
||||
newRecords.mediaProgress.push(MediaProgress)
|
||||
}
|
||||
|
||||
//
|
||||
// Migrate AudioBookmarks
|
||||
//
|
||||
for (const oldBookmark of oldUser.bookmarks) {
|
||||
const MediaItemId = oldDbIdMap.books[oldBookmark.libraryItemId]
|
||||
if (!MediaItemId) {
|
||||
const mediaItemId = oldDbIdMap.books[oldBookmark.libraryItemId]
|
||||
if (!mediaItemId) {
|
||||
Logger.warn(`[dbMigration] migrateUsers: Unable to find media item for audio bookmark "${oldBookmark.id}"`)
|
||||
continue
|
||||
}
|
||||
|
||||
const AudioBookmark = {
|
||||
id: uuidv4(),
|
||||
MediaItemId,
|
||||
mediaItemType: 'Book',
|
||||
mediaItemId,
|
||||
mediaItemType: 'book',
|
||||
title: oldBookmark.title,
|
||||
time: oldBookmark.time,
|
||||
createdAt: oldBookmark.createdAt,
|
||||
updatedAt: oldBookmark.createdAt,
|
||||
UserId: User.id
|
||||
userId: User.id
|
||||
}
|
||||
newRecords.AudioBookmark.push(AudioBookmark)
|
||||
newRecords.audioBookmark.push(AudioBookmark)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function migrateSessions(oldSessions) {
|
||||
for (const oldSession of oldSessions) {
|
||||
const UserId = oldDbIdMap.users[oldSession.userId] || null // Can be null
|
||||
const userId = oldDbIdMap.users[oldSession.userId] || null // Can be null
|
||||
|
||||
//
|
||||
// Migrate Device
|
||||
//
|
||||
let DeviceId = null
|
||||
let deviceId = null
|
||||
if (oldSession.deviceInfo) {
|
||||
const oldDeviceInfo = oldSession.deviceInfo
|
||||
const deviceInfoStr = getDeviceInfoString(oldDeviceInfo, UserId)
|
||||
DeviceId = oldDbIdMap.devices[deviceInfoStr]
|
||||
if (!DeviceId) {
|
||||
const deviceInfoStr = getDeviceInfoString(oldDeviceInfo, userId)
|
||||
deviceId = oldDbIdMap.devices[deviceInfoStr]
|
||||
if (!deviceId) {
|
||||
let clientName = 'Unknown'
|
||||
let clientVersion = null
|
||||
let deviceName = null
|
||||
@ -918,9 +922,9 @@ function migrateSessions(oldSessions) {
|
||||
ipAddress: oldDeviceInfo.ipAddress,
|
||||
deviceName, // e.g. Windows 10 Chrome, Google Pixel 6, Apple iPhone 10,3
|
||||
deviceVersion,
|
||||
UserId
|
||||
userId
|
||||
}
|
||||
newRecords.Device.push(Device)
|
||||
newRecords.device.push(Device)
|
||||
oldDbIdMap.devices[deviceInfoStr] = Device.id
|
||||
}
|
||||
}
|
||||
@ -929,18 +933,18 @@ function migrateSessions(oldSessions) {
|
||||
//
|
||||
// Migrate PlaybackSession
|
||||
//
|
||||
let MediaItemId = null
|
||||
let mediaItemType = 'Book'
|
||||
let mediaItemId = null
|
||||
let mediaItemType = 'book'
|
||||
if (oldSession.mediaType === 'podcast') {
|
||||
MediaItemId = oldDbIdMap.podcastEpisodes[oldSession.episodeId] || null
|
||||
mediaItemType = 'PodcastEpisode'
|
||||
mediaItemId = oldDbIdMap.podcastEpisodes[oldSession.episodeId] || null
|
||||
mediaItemType = 'podcastEpisode'
|
||||
} else {
|
||||
MediaItemId = oldDbIdMap.books[oldSession.libraryItemId] || null
|
||||
mediaItemId = oldDbIdMap.books[oldSession.libraryItemId] || null
|
||||
}
|
||||
|
||||
const PlaybackSession = {
|
||||
id: uuidv4(),
|
||||
MediaItemId, // Can be null
|
||||
mediaItemId, // Can be null
|
||||
mediaItemType,
|
||||
displayTitle: oldSession.displayTitle,
|
||||
displayAuthor: oldSession.displayAuthor,
|
||||
@ -952,10 +956,10 @@ function migrateSessions(oldSessions) {
|
||||
serverVersion: oldSession.deviceInfo?.serverVersion || null,
|
||||
createdAt: oldSession.startedAt,
|
||||
updatedAt: oldSession.updatedAt,
|
||||
UserId, // Can be null
|
||||
DeviceId
|
||||
userId, // Can be null
|
||||
deviceId
|
||||
}
|
||||
newRecords.PlaybackSession.push(PlaybackSession)
|
||||
newRecords.playbackSession.push(PlaybackSession)
|
||||
|
||||
if (oldSession.timeListening) {
|
||||
const PlaybackSessionListenTime = {
|
||||
@ -964,17 +968,17 @@ function migrateSessions(oldSessions) {
|
||||
date: oldSession.date || dateAndTime.format(new Date(PlaybackSession.createdAt), 'YYYY-MM-DD'),
|
||||
createdAt: PlaybackSession.createdAt,
|
||||
updatedAt: PlaybackSession.updatedAt,
|
||||
PlaybackSessionId: PlaybackSession.id
|
||||
playbackSessionId: PlaybackSession.id
|
||||
}
|
||||
newRecords.PlaybackSessionListenTime.push(PlaybackSessionListenTime)
|
||||
newRecords.playbackSessionListenTime.push(PlaybackSessionListenTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function migrateCollections(oldCollections) {
|
||||
for (const oldCollection of oldCollections) {
|
||||
const LibraryId = oldDbIdMap.libraries[oldCollection.libraryId]
|
||||
if (!LibraryId) {
|
||||
const libraryId = oldDbIdMap.libraries[oldCollection.libraryId]
|
||||
if (!libraryId) {
|
||||
Logger.warn(`[dbMigration] migrateCollections: Library not found for collection "${oldCollection.name}" (id:${oldCollection.libraryId})`)
|
||||
continue
|
||||
}
|
||||
@ -991,42 +995,42 @@ function migrateCollections(oldCollections) {
|
||||
description: oldCollection.description,
|
||||
createdAt: oldCollection.createdAt,
|
||||
updatedAt: oldCollection.lastUpdate,
|
||||
LibraryId
|
||||
libraryId
|
||||
}
|
||||
oldDbIdMap.collections[oldCollection.id] = Collection.id
|
||||
newRecords.Collection.push(Collection)
|
||||
newRecords.collection.push(Collection)
|
||||
|
||||
BookIds.forEach((BookId) => {
|
||||
BookIds.forEach((bookId) => {
|
||||
const CollectionBook = {
|
||||
id: uuidv4(),
|
||||
createdAt: Collection.createdAt,
|
||||
BookId,
|
||||
CollectionId: Collection.id
|
||||
bookId,
|
||||
collectionId: Collection.id
|
||||
}
|
||||
newRecords.CollectionBook.push(CollectionBook)
|
||||
newRecords.collectionBook.push(CollectionBook)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function migratePlaylists(oldPlaylists) {
|
||||
for (const oldPlaylist of oldPlaylists) {
|
||||
const LibraryId = oldDbIdMap.libraries[oldPlaylist.libraryId]
|
||||
if (!LibraryId) {
|
||||
const libraryId = oldDbIdMap.libraries[oldPlaylist.libraryId]
|
||||
if (!libraryId) {
|
||||
Logger.warn(`[dbMigration] migratePlaylists: Library not found for playlist "${oldPlaylist.name}" (id:${oldPlaylist.libraryId})`)
|
||||
continue
|
||||
}
|
||||
|
||||
const UserId = oldDbIdMap.users[oldPlaylist.userId]
|
||||
if (!UserId) {
|
||||
const userId = oldDbIdMap.users[oldPlaylist.userId]
|
||||
if (!userId) {
|
||||
Logger.warn(`[dbMigration] migratePlaylists: User not found for playlist "${oldPlaylist.name}" (id:${oldPlaylist.userId})`)
|
||||
continue
|
||||
}
|
||||
|
||||
let mediaItemType = 'Book'
|
||||
let mediaItemType = 'book'
|
||||
let MediaItemIds = []
|
||||
oldPlaylist.items.forEach((itemObj) => {
|
||||
if (itemObj.episodeId) {
|
||||
mediaItemType = 'PodcastEpisode'
|
||||
mediaItemType = 'podcastEpisode'
|
||||
if (oldDbIdMap.podcastEpisodes[itemObj.episodeId]) {
|
||||
MediaItemIds.push(oldDbIdMap.podcastEpisodes[itemObj.episodeId])
|
||||
}
|
||||
@ -1045,20 +1049,20 @@ function migratePlaylists(oldPlaylists) {
|
||||
description: oldPlaylist.description,
|
||||
createdAt: oldPlaylist.createdAt,
|
||||
updatedAt: oldPlaylist.lastUpdate,
|
||||
UserId,
|
||||
LibraryId
|
||||
userId,
|
||||
libraryId
|
||||
}
|
||||
newRecords.Playlist.push(Playlist)
|
||||
newRecords.playlist.push(Playlist)
|
||||
|
||||
MediaItemIds.forEach((MediaItemId) => {
|
||||
MediaItemIds.forEach((mediaItemId) => {
|
||||
const PlaylistMediaItem = {
|
||||
id: uuidv4(),
|
||||
MediaItemId,
|
||||
mediaItemId,
|
||||
mediaItemType,
|
||||
createdAt: Playlist.createdAt,
|
||||
PlaylistId: Playlist.id
|
||||
playlistId: Playlist.id
|
||||
}
|
||||
newRecords.PlaylistMediaItem.push(PlaylistMediaItem)
|
||||
newRecords.playlistMediaItem.push(PlaylistMediaItem)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1069,27 +1073,23 @@ function migrateFeeds(oldFeeds) {
|
||||
continue
|
||||
}
|
||||
|
||||
let entityType = null
|
||||
let EntityId = null
|
||||
let entityId = null
|
||||
|
||||
if (oldFeed.entityType === 'collection') {
|
||||
entityType = 'Collection'
|
||||
EntityId = oldDbIdMap.collections[oldFeed.entityId]
|
||||
entityId = oldDbIdMap.collections[oldFeed.entityId]
|
||||
} else if (oldFeed.entityType === 'libraryItem') {
|
||||
entityType = 'LibraryItem'
|
||||
EntityId = oldDbIdMap.libraryItems[oldFeed.entityId]
|
||||
entityId = oldDbIdMap.libraryItems[oldFeed.entityId]
|
||||
} else if (oldFeed.entityType === 'series') {
|
||||
entityType = 'Series'
|
||||
EntityId = oldDbIdMap.series[oldFeed.entityId]
|
||||
entityId = oldDbIdMap.series[oldFeed.entityId]
|
||||
}
|
||||
|
||||
if (!EntityId) {
|
||||
Logger.warn(`[dbMigration] migrateFeeds: Entity not found for feed "${entityType}" (id:${oldFeed.entityId})`)
|
||||
if (!entityId) {
|
||||
Logger.warn(`[dbMigration] migrateFeeds: Entity not found for feed "${oldFeed.entityType}" (id:${oldFeed.entityId})`)
|
||||
continue
|
||||
}
|
||||
|
||||
const UserId = oldDbIdMap.users[oldFeed.userId]
|
||||
if (!UserId) {
|
||||
const userId = oldDbIdMap.users[oldFeed.userId]
|
||||
if (!userId) {
|
||||
Logger.warn(`[dbMigration] migrateFeeds: User not found for feed (id:${oldFeed.userId})`)
|
||||
continue
|
||||
}
|
||||
@ -1099,8 +1099,8 @@ function migrateFeeds(oldFeeds) {
|
||||
const Feed = {
|
||||
id: uuidv4(),
|
||||
slug: oldFeed.slug,
|
||||
entityType,
|
||||
EntityId,
|
||||
entityType: oldFeed.entityType,
|
||||
entityId,
|
||||
entityUpdatedAt: oldFeed.entityUpdatedAt,
|
||||
serverAddress: oldFeed.serverAddress,
|
||||
feedURL: oldFeed.feedUrl,
|
||||
@ -1117,9 +1117,9 @@ function migrateFeeds(oldFeeds) {
|
||||
preventIndexing: !!oldFeedMeta.preventIndexing,
|
||||
createdAt: oldFeed.createdAt,
|
||||
updatedAt: oldFeed.updatedAt,
|
||||
UserId
|
||||
userId
|
||||
}
|
||||
newRecords.Feed.push(Feed)
|
||||
newRecords.feed.push(Feed)
|
||||
|
||||
//
|
||||
// Migrate FeedEpisodes
|
||||
@ -1143,9 +1143,9 @@ function migrateFeeds(oldFeeds) {
|
||||
explicit: !!oldFeedEpisode.explicit,
|
||||
createdAt: oldFeed.createdAt,
|
||||
updatedAt: oldFeed.updatedAt,
|
||||
FeedId: Feed.id
|
||||
feedId: Feed.id
|
||||
}
|
||||
newRecords.FeedEpisode.push(FeedEpisode)
|
||||
newRecords.feedEpisode.push(FeedEpisode)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1162,7 +1162,7 @@ function migrateSettings(oldSettings) {
|
||||
if (value === undefined) value = null
|
||||
else if (serverSettingsKey === 'sortingPrefixes') value = JSON.stringify(value)
|
||||
|
||||
newRecords.Setting.push({
|
||||
newRecords.setting.push({
|
||||
key: serverSettingsKey,
|
||||
value,
|
||||
type: 0
|
||||
@ -1178,7 +1178,7 @@ function migrateSettings(oldSettings) {
|
||||
notificationDelay: notificationSettings.notificationDelay ?? 1000 // ms delay between firing notifications
|
||||
}
|
||||
for (const notificationSettingKey in cleanedCopy) {
|
||||
newRecords.Setting.push({
|
||||
newRecords.setting.push({
|
||||
key: notificationSettingKey,
|
||||
value: cleanedCopy[notificationSettingKey],
|
||||
type: 1
|
||||
@ -1206,7 +1206,7 @@ function migrateSettings(oldSettings) {
|
||||
createdAt: oldNotification.createdAt,
|
||||
updatedAt: oldNotification.createdAt
|
||||
}
|
||||
newRecords.Notification.push(Notification)
|
||||
newRecords.notification.push(Notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user