mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Update:New EBook API endpoint
This commit is contained in:
		
							parent
							
								
									b3f19ef628
								
							
						
					
					
						commit
						4f75a89633
					
				@ -57,7 +57,6 @@ Archive.init({
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  props: {
 | 
			
		||||
    url: String,
 | 
			
		||||
    libraryItem: {
 | 
			
		||||
      type: Object,
 | 
			
		||||
      default: () => {}
 | 
			
		||||
@ -88,6 +87,15 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    userToken() {
 | 
			
		||||
      return this.$store.getters['user/getToken']
 | 
			
		||||
    },
 | 
			
		||||
    libraryItemId() {
 | 
			
		||||
      return this.libraryItem?.id
 | 
			
		||||
    },
 | 
			
		||||
    ebookUrl() {
 | 
			
		||||
      return `/api/items/${this.libraryItemId}/ebook`
 | 
			
		||||
    },
 | 
			
		||||
    comicMetadataKeys() {
 | 
			
		||||
      return this.comicMetadata ? Object.keys(this.comicMetadata) : []
 | 
			
		||||
    },
 | 
			
		||||
@ -146,10 +154,11 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    async extract() {
 | 
			
		||||
      this.loading = true
 | 
			
		||||
      console.log('Extracting', this.url)
 | 
			
		||||
 | 
			
		||||
      var buff = await this.$axios.$get(this.url, {
 | 
			
		||||
        responseType: 'blob'
 | 
			
		||||
      var buff = await this.$axios.$get(this.ebookUrl, {
 | 
			
		||||
        responseType: 'blob',
 | 
			
		||||
        headers: {
 | 
			
		||||
          Authorization: `Bearer ${this.userToken}`
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      const archive = await Archive.open(buff)
 | 
			
		||||
      const originalFilesObject = await archive.getFilesObject()
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,6 @@ import ePub from 'epubjs'
 | 
			
		||||
 */
 | 
			
		||||
export default {
 | 
			
		||||
  props: {
 | 
			
		||||
    url: String,
 | 
			
		||||
    libraryItem: {
 | 
			
		||||
      type: Object,
 | 
			
		||||
      default: () => {}
 | 
			
		||||
@ -47,6 +46,9 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    userToken() {
 | 
			
		||||
      return this.$store.getters['user/getToken']
 | 
			
		||||
    },
 | 
			
		||||
    /** @returns {string} */
 | 
			
		||||
    libraryItemId() {
 | 
			
		||||
      return this.libraryItem?.id
 | 
			
		||||
@ -75,6 +77,9 @@ export default {
 | 
			
		||||
    readerHeight() {
 | 
			
		||||
      if (this.windowHeight < 400 || !this.playerOpen) return this.windowHeight
 | 
			
		||||
      return this.windowHeight - 164
 | 
			
		||||
    },
 | 
			
		||||
    epubUrl() {
 | 
			
		||||
      return `/api/items/${this.libraryItemId}/ebook`
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
@ -212,9 +217,13 @@ export default {
 | 
			
		||||
      const reader = this
 | 
			
		||||
 | 
			
		||||
      /** @type {ePub.Book} */
 | 
			
		||||
      reader.book = new ePub(reader.url, {
 | 
			
		||||
      reader.book = new ePub(reader.epubUrl, {
 | 
			
		||||
        width: this.readerWidth,
 | 
			
		||||
        height: this.readerHeight - 50
 | 
			
		||||
        height: this.readerHeight - 50,
 | 
			
		||||
        openAs: 'epub',
 | 
			
		||||
        requestHeaders: {
 | 
			
		||||
          Authorization: `Bearer ${this.userToken}`
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      /** @type {ePub.Rendition} */
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,6 @@ import defaultCss from '@/assets/ebooks/basic.js'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  props: {
 | 
			
		||||
    url: String,
 | 
			
		||||
    libraryItem: {
 | 
			
		||||
      type: Object,
 | 
			
		||||
      default: () => {}
 | 
			
		||||
@ -25,7 +24,17 @@ export default {
 | 
			
		||||
  data() {
 | 
			
		||||
    return {}
 | 
			
		||||
  },
 | 
			
		||||
  computed: {},
 | 
			
		||||
  computed: {
 | 
			
		||||
    userToken() {
 | 
			
		||||
      return this.$store.getters['user/getToken']
 | 
			
		||||
    },
 | 
			
		||||
    libraryItemId() {
 | 
			
		||||
      return this.libraryItem?.id
 | 
			
		||||
    },
 | 
			
		||||
    ebookUrl() {
 | 
			
		||||
      return `/api/items/${this.libraryItemId}/ebook`
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    addHtmlCss() {
 | 
			
		||||
      let iframe = document.getElementsByTagName('iframe')[0]
 | 
			
		||||
@ -83,8 +92,11 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    async initMobi() {
 | 
			
		||||
      // Fetch mobi file as blob
 | 
			
		||||
      var buff = await this.$axios.$get(this.url, {
 | 
			
		||||
        responseType: 'blob'
 | 
			
		||||
      var buff = await this.$axios.$get(this.ebookUrl, {
 | 
			
		||||
        responseType: 'blob',
 | 
			
		||||
        headers: {
 | 
			
		||||
          Authorization: `Bearer ${this.userToken}`
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      var reader = new FileReader()
 | 
			
		||||
      reader.onload = async (event) => {
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@
 | 
			
		||||
      <div class="flex items-center justify-center">
 | 
			
		||||
        <div :style="{ width: pdfWidth + 'px', height: pdfHeight + 'px' }" class="overflow-auto">
 | 
			
		||||
          <div v-if="loadedRatio > 0 && loadedRatio < 1" style="background-color: green; color: white; text-align: center" :style="{ width: loadedRatio * 100 + '%' }">{{ Math.floor(loadedRatio * 100) }}%</div>
 | 
			
		||||
          <pdf ref="pdf" class="m-auto z-10 border border-black border-opacity-20 shadow-md" :src="url" :page="page" :rotate="rotate" @progress="progressEvt" @error="error" @num-pages="numPagesLoaded" @link-clicked="page = $event" @loaded="loadedEvt"></pdf>
 | 
			
		||||
          <pdf ref="pdf" class="m-auto z-10 border border-black border-opacity-20 shadow-md" :src="pdfDocInitParams" :page="page" :rotate="rotate" @progress="progressEvt" @error="error" @num-pages="numPagesLoaded" @link-clicked="page = $event" @loaded="loadedEvt"></pdf>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
@ -41,7 +41,6 @@ export default {
 | 
			
		||||
    pdf
 | 
			
		||||
  },
 | 
			
		||||
  props: {
 | 
			
		||||
    url: String,
 | 
			
		||||
    libraryItem: {
 | 
			
		||||
      type: Object,
 | 
			
		||||
      default: () => {}
 | 
			
		||||
@ -60,6 +59,9 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    userToken() {
 | 
			
		||||
      return this.$store.getters['user/getToken']
 | 
			
		||||
    },
 | 
			
		||||
    libraryItemId() {
 | 
			
		||||
      return this.libraryItem?.id
 | 
			
		||||
    },
 | 
			
		||||
@ -94,6 +96,14 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    savedPage() {
 | 
			
		||||
      return Number(this.userMediaProgress?.ebookLocation || 0)
 | 
			
		||||
    },
 | 
			
		||||
    pdfDocInitParams() {
 | 
			
		||||
      return {
 | 
			
		||||
        url: `/api/items/${this.libraryItemId}/ebook`,
 | 
			
		||||
        httpHeaders: {
 | 
			
		||||
          Authorization: `Bearer ${this.userToken}`
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@
 | 
			
		||||
      <span class="material-icons cursor-pointer text-2xl" @click="close">close</span>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <component v-if="componentName" ref="readerComponent" :is="componentName" :url="ebookUrl" :library-item="selectedLibraryItem" :player-open="!!streamLibraryItem" />
 | 
			
		||||
    <component v-if="componentName" ref="readerComponent" :is="componentName" :library-item="selectedLibraryItem" :player-open="!!streamLibraryItem" />
 | 
			
		||||
 | 
			
		||||
    <!-- TOC side nav -->
 | 
			
		||||
    <div v-if="tocOpen" class="w-full h-full fixed inset-0 bg-black/20 z-20" @click.stop.prevent="toggleToC"></div>
 | 
			
		||||
@ -128,21 +128,6 @@ export default {
 | 
			
		||||
    isComic() {
 | 
			
		||||
      return this.ebookFormat == 'cbz' || this.ebookFormat == 'cbr'
 | 
			
		||||
    },
 | 
			
		||||
    ebookUrl() {
 | 
			
		||||
      if (!this.ebookFile) return null
 | 
			
		||||
      let filepath = ''
 | 
			
		||||
      if (this.selectedLibraryItem.isFile) {
 | 
			
		||||
        filepath = this.$encodeUriPath(this.ebookFile.metadata.filename)
 | 
			
		||||
      } else {
 | 
			
		||||
        const itemRelPath = this.selectedLibraryItem.relPath
 | 
			
		||||
        if (itemRelPath.startsWith('/')) itemRelPath = itemRelPath.slice(1)
 | 
			
		||||
        const relPath = this.ebookFile.metadata.relPath
 | 
			
		||||
        if (relPath.startsWith('/')) relPath = relPath.slice(1)
 | 
			
		||||
 | 
			
		||||
        filepath = this.$encodeUriPath(`${itemRelPath}/${relPath}`)
 | 
			
		||||
      }
 | 
			
		||||
      return `/ebook/${this.libraryId}/${this.folderId}/${filepath}`
 | 
			
		||||
    },
 | 
			
		||||
    userToken() {
 | 
			
		||||
      return this.$store.getters['user/getToken']
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -71,9 +71,8 @@ module.exports = {
 | 
			
		||||
  ],
 | 
			
		||||
 | 
			
		||||
  proxy: {
 | 
			
		||||
    '/dev/': { target: 'http://localhost:3333', pathRewrite: { '^/dev/': '' } },
 | 
			
		||||
    '/ebook/': { target: process.env.NODE_ENV !== 'production' ? 'http://localhost:3333' : '/' },
 | 
			
		||||
    '/s/': { target: process.env.NODE_ENV !== 'production' ? 'http://localhost:3333' + process.env : '/' },
 | 
			
		||||
    '/s/': { target: process.env.NODE_ENV !== 'production' ? 'http://localhost:3333' : '/' },
 | 
			
		||||
    '/api/': { target: process.env.NODE_ENV !== 'production' ? 'http://localhost:3333' : '/' }
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  io: {
 | 
			
		||||
 | 
			
		||||
@ -7,15 +7,10 @@ export default function ({ $axios, store, $config }) {
 | 
			
		||||
    if (config.url.startsWith('http:') || config.url.startsWith('https:')) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    var bearerToken = store.state.user.user ? store.state.user.user.token : null
 | 
			
		||||
    const bearerToken = store.state.user.user?.token || null
 | 
			
		||||
    if (bearerToken) {
 | 
			
		||||
      config.headers.common['Authorization'] = `Bearer ${bearerToken}`
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (process.env.NODE_ENV === 'development') {
 | 
			
		||||
      config.url = `/dev${config.url}`
 | 
			
		||||
      console.log('Making request to ' + config.url)
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  $axios.onError(error => {
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ export const getters = {
 | 
			
		||||
  getIsRoot: (state) => state.user && state.user.type === 'root',
 | 
			
		||||
  getIsAdminOrUp: (state) => state.user && (state.user.type === 'admin' || state.user.type === 'root'),
 | 
			
		||||
  getToken: (state) => {
 | 
			
		||||
    return state.user ? state.user.token : null
 | 
			
		||||
    return state.user?.token || null
 | 
			
		||||
  },
 | 
			
		||||
  getUserMediaProgress: (state) => (libraryItemId, episodeId = null) => {
 | 
			
		||||
    if (!state.user.mediaProgress) return null
 | 
			
		||||
 | 
			
		||||
@ -165,6 +165,7 @@ class Server {
 | 
			
		||||
    router.use('/s', this.authMiddleware.bind(this), this.staticRouter.router)
 | 
			
		||||
 | 
			
		||||
    // EBook static file routes
 | 
			
		||||
    // TODO: Deprecated as of 2.2.21 edge
 | 
			
		||||
    router.get('/ebook/:library/:folder/*', (req, res) => {
 | 
			
		||||
      const library = this.db.libraries.find(lib => lib.id === req.params.library)
 | 
			
		||||
      if (!library) return res.sendStatus(404)
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,4 @@
 | 
			
		||||
const Path = require('path')
 | 
			
		||||
const fs = require('../libs/fsExtra')
 | 
			
		||||
const Logger = require('../Logger')
 | 
			
		||||
const SocketAuthority = require('../SocketAuthority')
 | 
			
		||||
@ -552,6 +553,16 @@ class LibraryItemController {
 | 
			
		||||
    res.sendStatus(200)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async getEBookFile(req, res) {
 | 
			
		||||
    const ebookFile = req.libraryItem.media.ebookFile
 | 
			
		||||
    if (!ebookFile) {
 | 
			
		||||
      Logger.error(`[LibraryItemController] No ebookFile for library item "${req.libraryItem.media.metadata.title}"`)
 | 
			
		||||
      return res.sendStatus(404)
 | 
			
		||||
    }
 | 
			
		||||
    const ebookFilePath = ebookFile.metadata.path
 | 
			
		||||
    res.sendFile(ebookFilePath)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  middleware(req, res, next) {
 | 
			
		||||
    const item = this.db.libraryItems.find(li => li.id === req.params.id)
 | 
			
		||||
    if (!item || !item.media) return res.sendStatus(404)
 | 
			
		||||
 | 
			
		||||
@ -121,6 +121,7 @@ class ApiRouter {
 | 
			
		||||
    this.router.post('/items/:id/chapters', LibraryItemController.middleware.bind(this), LibraryItemController.updateMediaChapters.bind(this))
 | 
			
		||||
    this.router.post('/items/:id/tone-scan/:index?', LibraryItemController.middleware.bind(this), LibraryItemController.toneScan.bind(this))
 | 
			
		||||
    this.router.delete('/items/:id/file/:ino', LibraryItemController.middleware.bind(this), LibraryItemController.deleteLibraryFile.bind(this))
 | 
			
		||||
    this.router.get('/items/:id/ebook', LibraryItemController.middleware.bind(this), LibraryItemController.getEBookFile.bind(this))
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // User Routes
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user