mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Change:Series volume numbers to use language sensitive sorting #261
This commit is contained in:
		
							parent
							
								
									49fcaefd01
								
							
						
					
					
						commit
						ef94a6bb29
					
				@ -15,6 +15,7 @@ const BackupController = require('./controllers/BackupController')
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const BookFinder = require('./BookFinder')
 | 
					const BookFinder = require('./BookFinder')
 | 
				
			||||||
const AuthorFinder = require('./AuthorFinder')
 | 
					const AuthorFinder = require('./AuthorFinder')
 | 
				
			||||||
 | 
					const FileSystemController = require('./controllers/FileSystemController')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ApiController {
 | 
					class ApiController {
 | 
				
			||||||
  constructor(MetadataPath, db, auth, streamManager, rssFeeds, downloadManager, coverController, backupManager, watcher, cacheManager, emitter, clientEmitter) {
 | 
					  constructor(MetadataPath, db, auth, streamManager, rssFeeds, downloadManager, coverController, backupManager, watcher, cacheManager, emitter, clientEmitter) {
 | 
				
			||||||
@ -145,6 +146,11 @@ class ApiController {
 | 
				
			|||||||
    this.router.get('/search/covers', this.findCovers.bind(this))
 | 
					    this.router.get('/search/covers', this.findCovers.bind(this))
 | 
				
			||||||
    this.router.get('/search/books', this.findBooks.bind(this))
 | 
					    this.router.get('/search/books', this.findBooks.bind(this))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    // File System Routes
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    this.router.get('/filesystem', FileSystemController.getPaths.bind(this))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
    // Others
 | 
					    // Others
 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
@ -163,8 +169,6 @@ class ApiController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    this.router.get('/download/:id', this.download.bind(this))
 | 
					    this.router.get('/download/:id', this.download.bind(this))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.router.get('/filesystem', this.getFileSystemPaths.bind(this))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.router.post('/syncUserAudiobookData', this.syncUserAudiobookData.bind(this))
 | 
					    this.router.post('/syncUserAudiobookData', this.syncUserAudiobookData.bind(this))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.router.post('/purgecache', this.purgeCache.bind(this))
 | 
					    this.router.post('/purgecache', this.purgeCache.bind(this))
 | 
				
			||||||
@ -339,25 +343,6 @@ class ApiController {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async getFileSystemPaths(req, res) {
 | 
					 | 
				
			||||||
    var excludedDirs = ['node_modules', 'client', 'server', '.git', 'static', 'build', 'dist', 'metadata', 'config', 'sys', 'proc'].map(dirname => {
 | 
					 | 
				
			||||||
      return Path.sep + dirname
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Do not include existing mapped library paths in response
 | 
					 | 
				
			||||||
    this.db.libraries.forEach(lib => {
 | 
					 | 
				
			||||||
      lib.folders.forEach((folder) => {
 | 
					 | 
				
			||||||
        var dir = folder.fullPath
 | 
					 | 
				
			||||||
        if (dir.includes(global.appRoot)) dir = dir.replace(global.appRoot, '')
 | 
					 | 
				
			||||||
        excludedDirs.push(dir)
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Logger.debug(`[Server] get file system paths, excluded: ${excludedDirs.join(', ')}`)
 | 
					 | 
				
			||||||
    var dirs = await this.getDirectories(global.appRoot, '/', excludedDirs)
 | 
					 | 
				
			||||||
    res.json(dirs)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  async syncUserAudiobookData(req, res) {
 | 
					  async syncUserAudiobookData(req, res) {
 | 
				
			||||||
    if (!req.body.data) {
 | 
					    if (!req.body.data) {
 | 
				
			||||||
      return res.status(403).send('Invalid local user audiobook data')
 | 
					      return res.status(403).send('Invalid local user audiobook data')
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										25
									
								
								server/controllers/FileSystemController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								server/controllers/FileSystemController.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					const Logger = require('../Logger')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FileSystemController {
 | 
				
			||||||
 | 
					  constructor() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async getPaths(req, res) {
 | 
				
			||||||
 | 
					    var excludedDirs = ['node_modules', 'client', 'server', '.git', 'static', 'build', 'dist', 'metadata', 'config', 'sys', 'proc'].map(dirname => {
 | 
				
			||||||
 | 
					      return Path.sep + dirname
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Do not include existing mapped library paths in response
 | 
				
			||||||
 | 
					    this.db.libraries.forEach(lib => {
 | 
				
			||||||
 | 
					      lib.folders.forEach((folder) => {
 | 
				
			||||||
 | 
					        var dir = folder.fullPath
 | 
				
			||||||
 | 
					        if (dir.includes(global.appRoot)) dir = dir.replace(global.appRoot, '')
 | 
				
			||||||
 | 
					        excludedDirs.push(dir)
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Logger.debug(`[Server] get file system paths, excluded: ${excludedDirs.join(', ')}`)
 | 
				
			||||||
 | 
					    var dirs = await this.getDirectories(global.appRoot, '/', excludedDirs)
 | 
				
			||||||
 | 
					    res.json(dirs)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					module.exports = new FileSystemController()
 | 
				
			||||||
@ -189,12 +189,9 @@ class LibraryController {
 | 
				
			|||||||
    if (!audiobooks.length) {
 | 
					    if (!audiobooks.length) {
 | 
				
			||||||
      return res.status(404).send('Series not found')
 | 
					      return res.status(404).send('Series not found')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    audiobooks = sort(audiobooks).asc(ab => {
 | 
					    var sortedBooks = libraryHelpers.sortSeriesBooks(audiobooks, false)
 | 
				
			||||||
      if (!isNaN(ab.book.volumeNumber) && ab.book.volumeNumber !== null) return Number(ab.book.volumeNumber)
 | 
					 | 
				
			||||||
      return ab.book.volumeNumber
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    res.json({
 | 
					    res.json({
 | 
				
			||||||
      results: audiobooks.map(ab => ab.toJSONExpanded()),
 | 
					      results: sortedBooks,
 | 
				
			||||||
      total: audiobooks.length
 | 
					      total: audiobooks.length
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,7 @@
 | 
				
			|||||||
const { sort } = require('fast-sort')
 | 
					const { sort, createNewSortInstance } = require('fast-sort')
 | 
				
			||||||
 | 
					const naturalSort = createNewSortInstance({
 | 
				
			||||||
 | 
					  comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  decode(text) {
 | 
					  decode(text) {
 | 
				
			||||||
@ -92,14 +95,18 @@ module.exports = {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    return Object.values(_series).map((series) => {
 | 
					    return Object.values(_series).map((series) => {
 | 
				
			||||||
      series.books = sort(series.books).asc(ab => {
 | 
					      series.books = naturalSort(series.books).asc(ab => ab.book.volumeNumber)
 | 
				
			||||||
        if (!isNaN(ab.book.volumeNumber) && ab.book.volumeNumber !== null) return Number(ab.book.volumeNumber)
 | 
					 | 
				
			||||||
        return ab.book.volumeNumber
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
      return series
 | 
					      return series
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sortSeriesBooks(books, minified = false) {
 | 
				
			||||||
 | 
					    return naturalSort(books).asc(ab => ab.book.volumeNumber).map(ab => {
 | 
				
			||||||
 | 
					      if (minified) return ab.toJSONMinified()
 | 
				
			||||||
 | 
					      return ab.toJSONExpanded()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getBooksWithUserAudiobook(user, books) {
 | 
					  getBooksWithUserAudiobook(user, books) {
 | 
				
			||||||
    return books.map(book => {
 | 
					    return books.map(book => {
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user