mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Migration change metadata folder from /books to /items, podcast data model updates, add podcast routes
This commit is contained in:
		
							parent
							
								
									43bbfbfee3
								
							
						
					
					
						commit
						f8d0384155
					
				| @ -82,7 +82,7 @@ export default { | ||||
|         return | ||||
|       } | ||||
|       this.processing = true | ||||
|       var podcastfeed = await this.$axios.$post(`/api/getPodcastFeed`, { rssFeed: podcast.feedUrl }).catch((error) => { | ||||
|       var podcastfeed = await this.$axios.$post(`/api/podcasts/feed`, { rssFeed: podcast.feedUrl }).catch((error) => { | ||||
|         console.error('Failed to get feed', error) | ||||
|         this.$toast.error('Failed to get podcast feed') | ||||
|         return null | ||||
|  | ||||
| @ -15,7 +15,7 @@ const Backup = require('./objects/Backup') | ||||
| class BackupManager { | ||||
|   constructor(db, emitter) { | ||||
|     this.BackupPath = Path.join(global.MetadataPath, 'backups') | ||||
|     this.MetadataBooksPath = Path.join(global.MetadataPath, 'books') | ||||
|     this.ItemsMetadataPath = Path.join(global.MetadataPath, 'items') | ||||
| 
 | ||||
|     this.db = db | ||||
|     this.emitter = emitter | ||||
| @ -115,7 +115,7 @@ class BackupManager { | ||||
|     const zip = new StreamZip.async({ file: backup.fullPath }) | ||||
|     await zip.extract('config/', global.ConfigPath) | ||||
|     if (backup.backupMetadataCovers) { | ||||
|       await zip.extract('metadata-books/', this.MetadataBooksPath) | ||||
|       await zip.extract('metadata-items/', this.ItemsMetadataPath) | ||||
|     } | ||||
|     await this.db.reinit() | ||||
|     this.emitter('backup_applied') | ||||
| @ -154,7 +154,7 @@ class BackupManager { | ||||
|   async runBackup() { | ||||
|     // Check if Metadata Path is inside Config Path (otherwise there will be an infinite loop as the archiver tries to zip itself)
 | ||||
|     Logger.info(`[BackupManager] Running Backup`) | ||||
|     var metadataBooksPath = this.serverSettings.backupMetadataCovers ? this.MetadataBooksPath : null | ||||
|     var metadataItemsPath = this.serverSettings.backupMetadataCovers ? this.ItemsMetadataPath : null | ||||
| 
 | ||||
|     var newBackup = new Backup() | ||||
| 
 | ||||
| @ -164,7 +164,7 @@ class BackupManager { | ||||
|     } | ||||
|     newBackup.setData(newBackData) | ||||
| 
 | ||||
|     var zipResult = await this.zipBackup(metadataBooksPath, newBackup).then(() => true).catch((error) => { | ||||
|     var zipResult = await this.zipBackup(metadataItemsPath, newBackup).then(() => true).catch((error) => { | ||||
|       Logger.error(`[BackupManager] Backup Failed ${error}`) | ||||
|       return false | ||||
|     }) | ||||
| @ -204,7 +204,7 @@ class BackupManager { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   zipBackup(metadataBooksPath, backup) { | ||||
|   zipBackup(metadataItemsPath, backup) { | ||||
|     return new Promise((resolve, reject) => { | ||||
|       // create a file to stream archive data to
 | ||||
|       const output = fs.createWriteStream(backup.fullPath) | ||||
| @ -274,9 +274,9 @@ class BackupManager { | ||||
|       archive.directory(this.db.AuthorsPath, 'config/authors') | ||||
|       archive.directory(this.db.SeriesPath, 'config/series') | ||||
| 
 | ||||
|       if (metadataBooksPath) { | ||||
|         Logger.debug(`[BackupManager] Backing up Metadata Books "${metadataBooksPath}"`) | ||||
|         archive.directory(metadataBooksPath, 'metadata-books') | ||||
|       if (metadataItemsPath) { | ||||
|         Logger.debug(`[BackupManager] Backing up Metadata Items "${metadataItemsPath}"`) | ||||
|         archive.directory(metadataItemsPath, 'metadata-items') | ||||
|       } | ||||
|       archive.append(backup.detailsString, { name: 'details' }) | ||||
| 
 | ||||
|  | ||||
| @ -15,14 +15,14 @@ class CoverController { | ||||
|     this.db = db | ||||
|     this.cacheManager = cacheManager | ||||
| 
 | ||||
|     this.BookMetadataPath = Path.posix.join(global.MetadataPath, 'books') | ||||
|     this.ItemMetadataPath = Path.posix.join(global.MetadataPath, 'items') | ||||
|   } | ||||
| 
 | ||||
|   getCoverDirectory(libraryItem) { | ||||
|     if (this.db.serverSettings.storeCoverWithBook) { | ||||
|       return libraryItem.path | ||||
|     } else { | ||||
|       return Path.posix.join(this.BookMetadataPath, libraryItem.id) | ||||
|       return Path.posix.join(this.ItemMetadataPath, libraryItem.id) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -237,7 +237,7 @@ class CoverController { | ||||
| 
 | ||||
|     var coverAlreadyExists = await fs.pathExists(coverFilePath) | ||||
|     if (coverAlreadyExists) { | ||||
|       Logger.warn(`[Audiobook] Extract embedded cover art but cover already exists for "${libraryItem.media.metadata.title}" - bail`) | ||||
|       Logger.warn(`[CoverController] Extract embedded cover art but cover already exists for "${libraryItem.media.metadata.title}" - bail`) | ||||
|       return false | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -111,28 +111,19 @@ class Server { | ||||
|     await this.downloadManager.removeOrphanDownloads() | ||||
| 
 | ||||
|     if (version.localeCompare('1.7.3') < 0) { // Old version data model migration
 | ||||
|       await dbMigration.migrateUserData(this.db) // Db not yet loaded
 | ||||
|       await this.db.init() | ||||
|       await dbMigration.migrateLibraryItems(this.db) | ||||
|       // TODO: Eventually remove audiobooks db when stable
 | ||||
|       await dbMigration.migrate(this.db) | ||||
|     } else { | ||||
|       await this.db.init() | ||||
|     } | ||||
| 
 | ||||
|     this.auth.init() | ||||
| 
 | ||||
|     // TODO: Implement method to remove old user auidobook data and book metadata folders
 | ||||
|     // await this.checkUserAudiobookData()
 | ||||
|     // await this.purgeMetadata()
 | ||||
|     await this.checkUserLibraryItemProgress() // Remove invalid user item progress
 | ||||
|     await this.purgeMetadata() // Remove metadata folders without library item
 | ||||
| 
 | ||||
|     await this.backupManager.init() | ||||
|     await this.logManager.init() | ||||
| 
 | ||||
|     // If server upgrade and last version was 1.7.0 or earlier - add abmetadata files
 | ||||
|     // if (this.db.checkPreviousVersionIsBefore('1.7.1')) {
 | ||||
|     // TODO: wait until stable
 | ||||
|     // }
 | ||||
| 
 | ||||
|     if (this.db.serverSettings.scannerDisableWatcher) { | ||||
|       Logger.info(`[Server] Watcher is disabled`) | ||||
|       this.watcher.disabled = true | ||||
| @ -275,18 +266,17 @@ class Server { | ||||
|     socket.emit('save_metadata_complete', response) | ||||
|   } | ||||
| 
 | ||||
|   // Remove unused /metadata/books/{id} folders
 | ||||
|   // Remove unused /metadata/items/{id} folders
 | ||||
|   async purgeMetadata() { | ||||
|     var booksMetadata = Path.join(global.MetadataPath, 'books') | ||||
|     var booksMetadataExists = await fs.pathExists(booksMetadata) | ||||
|     if (!booksMetadataExists) return | ||||
|     var foldersInBooksMetadata = await fs.readdir(booksMetadata) | ||||
|     var itemsMetadata = Path.join(global.MetadataPath, 'items') | ||||
|     if (!(await fs.pathExists(itemsMetadata))) return | ||||
|     var foldersInItemsMetadata = await fs.readdir(itemsMetadata) | ||||
| 
 | ||||
|     var purged = 0 | ||||
|     await Promise.all(foldersInBooksMetadata.map(async foldername => { | ||||
|       var hasMatchingAudiobook = this.db.audiobooks.find(ab => ab.id === foldername) | ||||
|       if (!hasMatchingAudiobook) { | ||||
|         var folderPath = Path.join(booksMetadata, foldername) | ||||
|     await Promise.all(foldersInItemsMetadata.map(async foldername => { | ||||
|       var hasMatchingItem = this.db.libraryItems.find(ab => ab.id === foldername) | ||||
|       if (!hasMatchingItem) { | ||||
|         var folderPath = Path.join(itemsMetadata, foldername) | ||||
|         Logger.debug(`[Server] Purging unused metadata ${folderPath}`) | ||||
| 
 | ||||
|         await fs.remove(folderPath).then(() => { | ||||
| @ -297,24 +287,21 @@ class Server { | ||||
|       } | ||||
|     })) | ||||
|     if (purged > 0) { | ||||
|       Logger.info(`[Server] Purged ${purged} unused audiobook metadata`) | ||||
|       Logger.info(`[Server] Purged ${purged} unused library item metadata`) | ||||
|     } | ||||
|     return purged | ||||
|   } | ||||
| 
 | ||||
|   // Check user audiobook data has matching audiobook
 | ||||
|   async checkUserAudiobookData() { | ||||
|   // Remove user library item progress entries that dont have a library item
 | ||||
|   async checkUserLibraryItemProgress() { | ||||
|     for (let i = 0; i < this.db.users.length; i++) { | ||||
|       var _user = this.db.users[i] | ||||
|       if (_user.audiobooks) { | ||||
|         // Find user audiobook data that has no matching audiobook
 | ||||
|         var audiobookIdsToRemove = Object.keys(_user.audiobooks).filter(aid => { | ||||
|           return !this.db.audiobooks.find(ab => ab.id === aid) | ||||
|         }) | ||||
|         if (audiobookIdsToRemove.length) { | ||||
|           Logger.debug(`[Server] Found ${audiobookIdsToRemove.length} audiobook data to remove from user ${_user.username}`) | ||||
|           for (let y = 0; y < audiobookIdsToRemove.length; y++) { | ||||
|             _user.removeLibraryItemProgress(audiobookIdsToRemove[y]) | ||||
|       if (_user.libraryItemProgress) { | ||||
|         var itemProgressIdsToRemove = _user.libraryItemProgress.map(lip => lip.id).filter(lipId => !this.db.libraryItems.find(_li => _li.id == lipId)) | ||||
|         if (itemProgressIdsToRemove.length) { | ||||
|           Logger.debug(`[Server] Found ${itemProgressIdsToRemove.length} library item progress data to remove from user ${_user.username}`) | ||||
|           for (const lipId of itemProgressIdsToRemove) { | ||||
|             _user.removeLibraryItemProgress(lipId) | ||||
|           } | ||||
|           await this.db.updateEntity('user', _user) | ||||
|         } | ||||
|  | ||||
| @ -1,9 +1,7 @@ | ||||
| const Path = require('path') | ||||
| const fs = require('fs-extra') | ||||
| const axios = require('axios') | ||||
| 
 | ||||
| const Logger = require('../Logger') | ||||
| const { parsePodcastRssFeedXml } = require('../utils/podcastUtils') | ||||
| 
 | ||||
| const { isObject } = require('../utils/index') | ||||
| 
 | ||||
| //
 | ||||
| @ -139,28 +137,6 @@ class MiscController { | ||||
|     res.sendStatus(200) | ||||
|   } | ||||
| 
 | ||||
|   getPodcastFeed(req, res) { | ||||
|     var url = req.body.rssFeed | ||||
|     if (!url) { | ||||
|       return res.status(400).send('Bad request') | ||||
|     } | ||||
| 
 | ||||
|     axios.get(url).then(async (data) => { | ||||
|       if (!data || !data.data) { | ||||
|         Logger.error('Invalid podcast feed request response') | ||||
|         return res.status(500).send('Bad response from feed request') | ||||
|       } | ||||
|       var podcast = await parsePodcastRssFeedXml(data.data) | ||||
|       if (!podcast) { | ||||
|         return res.status(500).send('Invalid podcast RSS feed') | ||||
|       } | ||||
|       res.json(podcast) | ||||
|     }).catch((error) => { | ||||
|       console.error('Failed', error) | ||||
|       res.status(500).send(error) | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   async findBooks(req, res) { | ||||
|     var provider = req.query.provider || 'google' | ||||
|     var title = req.query.title || '' | ||||
|  | ||||
							
								
								
									
										62
									
								
								server/controllers/PodcastController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								server/controllers/PodcastController.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | ||||
| const axios = require('axios') | ||||
| const fs = require('fs-extra') | ||||
| const Logger = require('../Logger') | ||||
| const { parsePodcastRssFeedXml } = require('../utils/podcastUtils') | ||||
| const LibraryItem = require('../objects/LibraryItem') | ||||
| 
 | ||||
| class PodcastController { | ||||
| 
 | ||||
|   async create(req, res) { | ||||
|     if (!req.user.isRoot) { | ||||
|       Logger.error(`[PodcastController] Non-root user attempted to create podcast`, req.user) | ||||
|       return res.sendStatus(500) | ||||
|     } | ||||
|     const payload = req.body | ||||
| 
 | ||||
|     if (await fs.pathExists(payload.path)) { | ||||
|       Logger.error(`[PodcastController] Attempt to create podcast when folder path already exists "${payload.path}"`) | ||||
|       return res.status(400).send('Path already exists') | ||||
|     } | ||||
| 
 | ||||
|     var success = await fs.ensureDir(payload.path).then(() => true).catch((error) => { | ||||
|       Logger.error(`[PodcastController] Failed to ensure podcast dir "${payload.path}"`, error) | ||||
|       return false | ||||
|     }) | ||||
|     if (!success) return res.status(400).send('Invalid podcast path') | ||||
| 
 | ||||
|     if (payload.mediaMetadata.imageUrl) { | ||||
|       // TODO: Download image
 | ||||
|     } | ||||
| 
 | ||||
|     var libraryItem = new LibraryItem() | ||||
|     libraryItem.setData('podcast', payload) | ||||
| 
 | ||||
|     await this.db.insertLibraryItem(libraryItem) | ||||
|     this.emitter('item_added', libraryItem.toJSONExpanded()) | ||||
| 
 | ||||
|     res.json(libraryItem.toJSONExpanded()) | ||||
|   } | ||||
| 
 | ||||
|   getPodcastFeed(req, res) { | ||||
|     var url = req.body.rssFeed | ||||
|     if (!url) { | ||||
|       return res.status(400).send('Bad request') | ||||
|     } | ||||
| 
 | ||||
|     axios.get(url).then(async (data) => { | ||||
|       if (!data || !data.data) { | ||||
|         Logger.error('Invalid podcast feed request response') | ||||
|         return res.status(500).send('Bad response from feed request') | ||||
|       } | ||||
|       var podcast = await parsePodcastRssFeedXml(data.data) | ||||
|       if (!podcast) { | ||||
|         return res.status(500).send('Invalid podcast RSS feed') | ||||
|       } | ||||
|       res.json(podcast) | ||||
|     }).catch((error) => { | ||||
|       console.error('Failed', error) | ||||
|       res.status(500).send(error) | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| module.exports = new PodcastController() | ||||
| @ -1,3 +1,4 @@ | ||||
| const { getId } = require('../../utils/index') | ||||
| const AudioFile = require('../files/AudioFile') | ||||
| const AudioTrack = require('../files/AudioTrack') | ||||
| 
 | ||||
| @ -5,9 +6,8 @@ class PodcastEpisode { | ||||
|   constructor(episode) { | ||||
|     this.id = null | ||||
|     this.index = null | ||||
|     this.podcastId = null | ||||
|     this.episodeNumber = null | ||||
| 
 | ||||
|     this.episodeNumber = null | ||||
|     this.title = null | ||||
|     this.description = null | ||||
|     this.enclosure = null | ||||
| @ -25,7 +25,6 @@ class PodcastEpisode { | ||||
|   construct(episode) { | ||||
|     this.id = episode.id | ||||
|     this.index = episode.index | ||||
|     this.podcastId = episode.podcastId | ||||
|     this.episodeNumber = episode.episodeNumber | ||||
|     this.title = episode.title | ||||
|     this.description = episode.description | ||||
| @ -40,7 +39,6 @@ class PodcastEpisode { | ||||
|     return { | ||||
|       id: this.id, | ||||
|       index: this.index, | ||||
|       podcastId: this.podcastId, | ||||
|       episodeNumber: this.episodeNumber, | ||||
|       title: this.title, | ||||
|       description: this.description, | ||||
| @ -61,6 +59,18 @@ class PodcastEpisode { | ||||
|   } | ||||
|   get size() { return this.audioFile.metadata.size } | ||||
| 
 | ||||
|   setData(data, index = 1) { | ||||
|     this.id = getId('ep') | ||||
|     this.index = index | ||||
|     this.title = data.title | ||||
|     this.pubDate = data.pubDate || '' | ||||
|     this.description = data.description || '' | ||||
|     this.enclosure = data.enclosure ? { ...data.enclosure } : null | ||||
|     this.episodeNumber = data.episodeNumber || '' | ||||
|     this.addedAt = Date.now() | ||||
|     this.updatedAt = Date.now() | ||||
|   } | ||||
| 
 | ||||
|   // Only checks container format
 | ||||
|   checkCanDirectPlay(payload) { | ||||
|     var supportedMimeTypes = payload.supportedMimeTypes || [] | ||||
|  | ||||
| @ -4,8 +4,6 @@ const { areEquivalent, copyValue } = require('../../utils/index') | ||||
| 
 | ||||
| class Podcast { | ||||
|   constructor(podcast) { | ||||
|     this.id = null | ||||
| 
 | ||||
|     this.metadata = null | ||||
|     this.coverPath = null | ||||
|     this.tags = [] | ||||
| @ -22,7 +20,6 @@ class Podcast { | ||||
|   } | ||||
| 
 | ||||
|   construct(podcast) { | ||||
|     this.id = podcast.id | ||||
|     this.metadata = new PodcastMetadata(podcast.metadata) | ||||
|     this.coverPath = podcast.coverPath | ||||
|     this.tags = [...podcast.tags] | ||||
| @ -32,7 +29,6 @@ class Podcast { | ||||
| 
 | ||||
|   toJSON() { | ||||
|     return { | ||||
|       id: this.id, | ||||
|       metadata: this.metadata.toJSON(), | ||||
|       coverPath: this.coverPath, | ||||
|       tags: [...this.tags], | ||||
| @ -43,7 +39,6 @@ class Podcast { | ||||
| 
 | ||||
|   toJSONMinified() { | ||||
|     return { | ||||
|       id: this.id, | ||||
|       metadata: this.metadata.toJSON(), | ||||
|       coverPath: this.coverPath, | ||||
|       tags: [...this.tags], | ||||
| @ -54,7 +49,6 @@ class Podcast { | ||||
| 
 | ||||
|   toJSONExpanded() { | ||||
|     return { | ||||
|       id: this.id, | ||||
|       metadata: this.metadata.toJSONExpanded(), | ||||
|       coverPath: this.coverPath, | ||||
|       tags: [...this.tags], | ||||
| @ -124,9 +118,10 @@ class Podcast { | ||||
|     return this.episodes[0] | ||||
|   } | ||||
| 
 | ||||
|   setData(scanMediaMetadata) { | ||||
|     this.metadata = new PodcastMetadata() | ||||
|     this.metadata.setData(scanMediaMetadata) | ||||
|   setData(metadata, coverPath = null, autoDownload = false) { | ||||
|     this.metadata = new PodcastMetadata(metadata) | ||||
|     this.coverPath = coverPath | ||||
|     this.autoDownloadEpisodes = autoDownload | ||||
|   } | ||||
| 
 | ||||
|   async syncMetadataFiles(textMetadataFiles, opfMetadataOverrideDetails) { | ||||
|  | ||||
| @ -14,6 +14,7 @@ const SeriesController = require('../controllers/SeriesController') | ||||
| const AuthorController = require('../controllers/AuthorController') | ||||
| const MediaEntityController = require('../controllers/MediaEntityController') | ||||
| const SessionController = require('../controllers/SessionController') | ||||
| const PodcastController = require('../controllers/PodcastController') | ||||
| const MiscController = require('../controllers/MiscController') | ||||
| 
 | ||||
| const BookFinder = require('../finders/BookFinder') | ||||
| @ -173,6 +174,12 @@ class ApiRouter { | ||||
|     this.router.post('/session/:id/sync', SessionController.middleware.bind(this), SessionController.sync.bind(this)) | ||||
|     this.router.post('/session/:id/close', SessionController.middleware.bind(this), SessionController.close.bind(this)) | ||||
| 
 | ||||
|     //
 | ||||
|     // Podcast Routes
 | ||||
|     //
 | ||||
|     this.router.post('/podcasts', PodcastController.create.bind(this)) | ||||
|     this.router.post('/podcasts/feed', PodcastController.getPodcastFeed.bind(this)) | ||||
| 
 | ||||
|     //
 | ||||
|     // Misc Routes
 | ||||
|     //
 | ||||
| @ -180,7 +187,6 @@ class ApiRouter { | ||||
|     this.router.get('/download/:id', MiscController.download.bind(this)) | ||||
|     this.router.patch('/settings', MiscController.updateServerSettings.bind(this)) // Root only
 | ||||
|     this.router.post('/purgecache', MiscController.purgeCache.bind(this)) // Root only
 | ||||
|     this.router.post('/getPodcastFeed', MiscController.getPodcastFeed.bind(this)) | ||||
|     this.router.post('/authorize', MiscController.authorize.bind(this)) | ||||
|     this.router.get('/search/covers', MiscController.findCovers.bind(this)) | ||||
|     this.router.get('/search/books', MiscController.findBooks.bind(this)) | ||||
|  | ||||
| @ -18,7 +18,6 @@ const Series = require('../objects/entities/Series') | ||||
| 
 | ||||
| class Scanner { | ||||
|   constructor(db, coverController, emitter) { | ||||
|     this.BookMetadataPath = Path.posix.join(global.MetadataPath, 'books') | ||||
|     this.ScanLogPath = Path.posix.join(global.MetadataPath, 'logs', 'scans') | ||||
| 
 | ||||
|     this.db = db | ||||
|  | ||||
| @ -151,6 +151,17 @@ function makeFilesFromOldAb(audiobook) { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // Metadata path was changed to /metadata/items make sure cover is using new path
 | ||||
| function cleanOldCoverPath(coverPath) { | ||||
|   if (!coverPath) return null | ||||
|   var oldMetadataPath = Path.posix.join(global.MetadataPath, 'books') | ||||
|   if (coverPath.startsWith(oldMetadataPath)) { | ||||
|     const newMetadataPath = Path.posix.join(global.MetadataPath, 'items') | ||||
|     return coverPath.replace(oldMetadataPath, newMetadataPath) | ||||
|   } | ||||
|   return coverPath | ||||
| } | ||||
| 
 | ||||
| function makeLibraryItemFromOldAb(audiobook) { | ||||
|   var libraryItem = new LibraryItem() | ||||
|   libraryItem.id = getId('li') | ||||
| @ -184,7 +195,7 @@ function makeLibraryItemFromOldAb(audiobook) { | ||||
|   } | ||||
| 
 | ||||
|   bookEntity.metadata = bookMetadata | ||||
|   bookEntity.coverPath = audiobook.book.coverFullPath | ||||
|   bookEntity.coverPath = cleanOldCoverPath(audiobook.book.coverFullPath) | ||||
|   bookEntity.tags = [...audiobook.tags] | ||||
| 
 | ||||
|   var payload = makeFilesFromOldAb(audiobook) | ||||
| @ -312,8 +323,6 @@ async function migrateLibraryItems(db) { | ||||
|   seriesToAdd = [] | ||||
|   Logger.info(`==== Library Item migration complete ====`) | ||||
| } | ||||
| module.exports.migrateLibraryItems = migrateLibraryItems | ||||
| 
 | ||||
| 
 | ||||
| function cleanUserObject(db, userObj) { | ||||
|   var cleanedUserPayload = { | ||||
| @ -445,4 +454,24 @@ async function migrateUserData(db) { | ||||
| 
 | ||||
|   Logger.info(`==== User migration complete (${userCount} Users, ${sessionCount} Sessions) ====`) | ||||
| } | ||||
| module.exports.migrateUserData = migrateUserData | ||||
| 
 | ||||
| async function checkUpdateMetadataPath() { | ||||
|   var bookMetadataPath = Path.posix.join(global.MetadataPath, 'books') // OLD
 | ||||
|   if (!(await fs.pathExists(bookMetadataPath))) { | ||||
|     Logger.debug(`[dbMigration] No need to update books metadata path`) | ||||
|     return | ||||
|   } | ||||
|   var itemsMetadataPath = Path.posix.join(global.MetadataPath, 'items') | ||||
|   await fs.rename(bookMetadataPath, itemsMetadataPath) | ||||
|   Logger.info(`>>> Renamed metadata dir from /metadata/books to /metadata/items`) | ||||
| } | ||||
| 
 | ||||
| module.exports.migrate = async (db) => { | ||||
|   await checkUpdateMetadataPath() | ||||
|   // Before DB Load clean data
 | ||||
|   await migrateUserData(db) | ||||
|   await db.init() | ||||
|   // After DB Load
 | ||||
|   await migrateLibraryItems(db) | ||||
|   // TODO: Eventually remove audiobooks db when stable
 | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user