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