mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Fix:Relative file path for single book scans, Change:Route names & refactor api
This commit is contained in:
		
							parent
							
								
									2194d55cc0
								
							
						
					
					
						commit
						66a490365a
					
				@ -158,7 +158,7 @@ export default {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .patch(`/api/user/audiobooks`, updateProgressPayloads)
 | 
					        .patch(`/api/me/audiobook/batch/update`, updateProgressPayloads)
 | 
				
			||||||
        .then(() => {
 | 
					        .then(() => {
 | 
				
			||||||
          this.$toast.success('Batch update success!')
 | 
					          this.$toast.success('Batch update success!')
 | 
				
			||||||
          this.$store.commit('setProcessingBatch', false)
 | 
					          this.$store.commit('setProcessingBatch', false)
 | 
				
			||||||
@ -177,7 +177,7 @@ export default {
 | 
				
			|||||||
        this.processingBatchDelete = true
 | 
					        this.processingBatchDelete = true
 | 
				
			||||||
        this.$store.commit('setProcessingBatch', true)
 | 
					        this.$store.commit('setProcessingBatch', true)
 | 
				
			||||||
        this.$axios
 | 
					        this.$axios
 | 
				
			||||||
          .$post(`/api/audiobooks/delete`, {
 | 
					          .$post(`/api/books/batch/delete`, {
 | 
				
			||||||
            audiobookIds: this.selectedAudiobooks
 | 
					            audiobookIds: this.selectedAudiobooks
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
          .then(() => {
 | 
					          .then(() => {
 | 
				
			||||||
 | 
				
			|||||||
@ -162,7 +162,7 @@ export default {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      this.isProcessingReadUpdate = true
 | 
					      this.isProcessingReadUpdate = true
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$patch(`/api/user/audiobook/${this.audiobookId}`, updatePayload)
 | 
					        .$patch(`/api/me/audiobook/${this.audiobookId}`, updatePayload)
 | 
				
			||||||
        .then(() => {
 | 
					        .then(() => {
 | 
				
			||||||
          this.isProcessingReadUpdate = false
 | 
					          this.isProcessingReadUpdate = false
 | 
				
			||||||
          this.$toast.success(`"${this.title}" Marked as ${updatePayload.isRead ? 'Read' : 'Not Read'}`)
 | 
					          this.$toast.success(`"${this.title}" Marked as ${updatePayload.isRead ? 'Read' : 'Not Read'}`)
 | 
				
			||||||
 | 
				
			|||||||
@ -326,7 +326,7 @@ export default {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      this.isProcessingReadUpdate = true
 | 
					      this.isProcessingReadUpdate = true
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$patch(`/api/user/audiobook/${this.audiobookId}`, updatePayload)
 | 
					        .$patch(`/api/me/audiobook/${this.audiobookId}`, updatePayload)
 | 
				
			||||||
        .then(() => {
 | 
					        .then(() => {
 | 
				
			||||||
          this.isProcessingReadUpdate = false
 | 
					          this.isProcessingReadUpdate = false
 | 
				
			||||||
          this.$toast.success(`"${this.title}" Marked as ${updatePayload.isRead ? 'Read' : 'Not Read'}`)
 | 
					          this.$toast.success(`"${this.title}" Marked as ${updatePayload.isRead ? 'Read' : 'Not Read'}`)
 | 
				
			||||||
 | 
				
			|||||||
@ -131,7 +131,7 @@ export default {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      this.isFetching = true
 | 
					      this.isFetching = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var searchResults = await this.$axios.$get(`/api/library/${this.currentLibraryId}/search?q=${value}`).catch((error) => {
 | 
					      var searchResults = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/search?q=${value}`).catch((error) => {
 | 
				
			||||||
        console.error('Search error', error)
 | 
					        console.error('Search error', error)
 | 
				
			||||||
        return []
 | 
					        return []
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
				
			|||||||
@ -171,7 +171,7 @@ export default {
 | 
				
			|||||||
      this.processing = true
 | 
					      this.processing = true
 | 
				
			||||||
      console.log('Calling update', account)
 | 
					      console.log('Calling update', account)
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$patch(`/api/user/${this.account.id}`, account)
 | 
					        .$patch(`/api/users/${this.account.id}`, account)
 | 
				
			||||||
        .then((data) => {
 | 
					        .then((data) => {
 | 
				
			||||||
          this.processing = false
 | 
					          this.processing = false
 | 
				
			||||||
          if (data.error) {
 | 
					          if (data.error) {
 | 
				
			||||||
@ -198,7 +198,7 @@ export default {
 | 
				
			|||||||
      var account = { ...this.newUser }
 | 
					      var account = { ...this.newUser }
 | 
				
			||||||
      this.processing = true
 | 
					      this.processing = true
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$post('/api/user', account)
 | 
					        .$post('/api/users', account)
 | 
				
			||||||
        .then((data) => {
 | 
					        .then((data) => {
 | 
				
			||||||
          this.processing = false
 | 
					          this.processing = false
 | 
				
			||||||
          if (data.error) {
 | 
					          if (data.error) {
 | 
				
			||||||
 | 
				
			|||||||
@ -94,7 +94,7 @@ export default {
 | 
				
			|||||||
        this.processing = true
 | 
					        this.processing = true
 | 
				
			||||||
        var collectionName = this.collectionName
 | 
					        var collectionName = this.collectionName
 | 
				
			||||||
        this.$axios
 | 
					        this.$axios
 | 
				
			||||||
          .$delete(`/api/collection/${this.collection.id}`)
 | 
					          .$delete(`/api/collections/${this.collection.id}`)
 | 
				
			||||||
          .then(() => {
 | 
					          .then(() => {
 | 
				
			||||||
            this.processing = false
 | 
					            this.processing = false
 | 
				
			||||||
            this.show = false
 | 
					            this.show = false
 | 
				
			||||||
@ -122,7 +122,7 @@ export default {
 | 
				
			|||||||
        description: this.newCollectionDescription || null
 | 
					        description: this.newCollectionDescription || null
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$patch(`/api/collection/${this.collection.id}`, collectionUpdate)
 | 
					        .$patch(`/api/collections/${this.collection.id}`, collectionUpdate)
 | 
				
			||||||
        .then((collection) => {
 | 
					        .then((collection) => {
 | 
				
			||||||
          console.log('Collection Updated', collection)
 | 
					          console.log('Collection Updated', collection)
 | 
				
			||||||
          this.processing = false
 | 
					          this.processing = false
 | 
				
			||||||
 | 
				
			|||||||
@ -220,7 +220,7 @@ export default {
 | 
				
			|||||||
    async fetchFull() {
 | 
					    async fetchFull() {
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        this.processing = true
 | 
					        this.processing = true
 | 
				
			||||||
        this.audiobook = await this.$axios.$get(`/api/audiobook/${this.selectedAudiobookId}`)
 | 
					        this.audiobook = await this.$axios.$get(`/api/books/${this.selectedAudiobookId}`)
 | 
				
			||||||
        this.processing = false
 | 
					        this.processing = false
 | 
				
			||||||
      } catch (error) {
 | 
					      } catch (error) {
 | 
				
			||||||
        console.error('Failed to fetch audiobook', this.selectedAudiobookId, error)
 | 
					        console.error('Failed to fetch audiobook', this.selectedAudiobookId, error)
 | 
				
			||||||
 | 
				
			|||||||
@ -96,7 +96,7 @@ export default {
 | 
				
			|||||||
      this.processing = true
 | 
					      this.processing = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$delete(`/api/collection/${collection.id}/book/${this.selectedAudiobookId}`)
 | 
					        .$delete(`/api/collections/${collection.id}/book/${this.selectedAudiobookId}`)
 | 
				
			||||||
        .then((updatedCollection) => {
 | 
					        .then((updatedCollection) => {
 | 
				
			||||||
          console.log(`Book removed from collection`, updatedCollection)
 | 
					          console.log(`Book removed from collection`, updatedCollection)
 | 
				
			||||||
          this.$toast.success('Book removed from collection')
 | 
					          this.$toast.success('Book removed from collection')
 | 
				
			||||||
@ -114,7 +114,7 @@ export default {
 | 
				
			|||||||
      this.processing = true
 | 
					      this.processing = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$post(`/api/collection/${collection.id}/book`, { id: this.selectedAudiobookId })
 | 
					        .$post(`/api/collections/${collection.id}/book`, { id: this.selectedAudiobookId })
 | 
				
			||||||
        .then((updatedCollection) => {
 | 
					        .then((updatedCollection) => {
 | 
				
			||||||
          console.log(`Book added to collection`, updatedCollection)
 | 
					          console.log(`Book added to collection`, updatedCollection)
 | 
				
			||||||
          this.$toast.success('Book added to collection')
 | 
					          this.$toast.success('Book added to collection')
 | 
				
			||||||
 | 
				
			|||||||
@ -154,7 +154,7 @@ export default {
 | 
				
			|||||||
        var coverPayload = {
 | 
					        var coverPayload = {
 | 
				
			||||||
          url: updatePayload.cover
 | 
					          url: updatePayload.cover
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        var success = await this.$axios.$post(`/api/audiobook/${this.audiobook.id}/cover`, coverPayload).catch((error) => {
 | 
					        var success = await this.$axios.$post(`/api/books/${this.audiobook.id}/cover`, coverPayload).catch((error) => {
 | 
				
			||||||
          console.error('Failed to update', error)
 | 
					          console.error('Failed to update', error)
 | 
				
			||||||
          return false
 | 
					          return false
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
@ -171,7 +171,7 @@ export default {
 | 
				
			|||||||
        var bookUpdatePayload = {
 | 
					        var bookUpdatePayload = {
 | 
				
			||||||
          book: updatePayload
 | 
					          book: updatePayload
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        var success = await this.$axios.$patch(`/api/audiobook/${this.audiobook.id}`, bookUpdatePayload).catch((error) => {
 | 
					        var success = await this.$axios.$patch(`/api/books/${this.audiobook.id}`, bookUpdatePayload).catch((error) => {
 | 
				
			||||||
          console.error('Failed to update', error)
 | 
					          console.error('Failed to update', error)
 | 
				
			||||||
          return false
 | 
					          return false
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
				
			|||||||
@ -155,7 +155,7 @@ export default {
 | 
				
			|||||||
      form.set('cover', this.selectedFile)
 | 
					      form.set('cover', this.selectedFile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$post(`/api/audiobook/${this.audiobook.id}/cover`, form)
 | 
					        .$post(`/api/books/${this.audiobook.id}/cover`, form)
 | 
				
			||||||
        .then((data) => {
 | 
					        .then((data) => {
 | 
				
			||||||
          if (data.error) {
 | 
					          if (data.error) {
 | 
				
			||||||
            this.$toast.error(data.error)
 | 
					            this.$toast.error(data.error)
 | 
				
			||||||
@ -217,7 +217,7 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // Download cover from url and use
 | 
					      // Download cover from url and use
 | 
				
			||||||
      if (cover.startsWith('http:') || cover.startsWith('https:')) {
 | 
					      if (cover.startsWith('http:') || cover.startsWith('https:')) {
 | 
				
			||||||
        success = await this.$axios.$post(`/api/audiobook/${this.audiobook.id}/cover`, { url: cover }).catch((error) => {
 | 
					        success = await this.$axios.$post(`/api/books/${this.audiobook.id}/cover`, { url: cover }).catch((error) => {
 | 
				
			||||||
          console.error('Failed to download cover from url', error)
 | 
					          console.error('Failed to download cover from url', error)
 | 
				
			||||||
          if (error.response && error.response.data) {
 | 
					          if (error.response && error.response.data) {
 | 
				
			||||||
            this.$toast.error(error.response.data)
 | 
					            this.$toast.error(error.response.data)
 | 
				
			||||||
@ -231,7 +231,7 @@ export default {
 | 
				
			|||||||
            cover: cover
 | 
					            cover: cover
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        success = await this.$axios.$patch(`/api/audiobook/${this.audiobook.id}`, updatePayload).catch((error) => {
 | 
					        success = await this.$axios.$patch(`/api/books/${this.audiobook.id}`, updatePayload).catch((error) => {
 | 
				
			||||||
          console.error('Failed to update', error)
 | 
					          console.error('Failed to update', error)
 | 
				
			||||||
          if (error.response && error.response.data) {
 | 
					          if (error.response && error.response.data) {
 | 
				
			||||||
            this.$toast.error(error.response.data)
 | 
					            this.$toast.error(error.response.data)
 | 
				
			||||||
@ -266,7 +266,7 @@ export default {
 | 
				
			|||||||
    setCover(coverFile) {
 | 
					    setCover(coverFile) {
 | 
				
			||||||
      this.isProcessing = true
 | 
					      this.isProcessing = true
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$patch(`/api/audiobook/${this.audiobook.id}/coverfile`, coverFile)
 | 
					        .$patch(`/api/books/${this.audiobook.id}/coverfile`, coverFile)
 | 
				
			||||||
        .then((data) => {
 | 
					        .then((data) => {
 | 
				
			||||||
          console.log('response data', data)
 | 
					          console.log('response data', data)
 | 
				
			||||||
          if (data && typeof data === 'string') {
 | 
					          if (data && typeof data === 'string') {
 | 
				
			||||||
 | 
				
			|||||||
@ -195,7 +195,7 @@ export default {
 | 
				
			|||||||
        tags: this.newTags
 | 
					        tags: this.newTags
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var updatedAudiobook = await this.$axios.$patch(`/api/audiobook/${this.audiobook.id}`, updatePayload).catch((error) => {
 | 
					      var updatedAudiobook = await this.$axios.$patch(`/api/books/${this.audiobook.id}`, updatePayload).catch((error) => {
 | 
				
			||||||
        console.error('Failed to update', error)
 | 
					        console.error('Failed to update', error)
 | 
				
			||||||
        return false
 | 
					        return false
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
@ -220,27 +220,11 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      this.newTags = this.audiobook.tags || []
 | 
					      this.newTags = this.audiobook.tags || []
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    resetProgress() {
 | 
					 | 
				
			||||||
      if (confirm(`Are you sure you want to reset your progress?`)) {
 | 
					 | 
				
			||||||
        this.resettingProgress = true
 | 
					 | 
				
			||||||
        this.$axios
 | 
					 | 
				
			||||||
          .$delete(`/api/user/audiobook/${this.audiobookId}`)
 | 
					 | 
				
			||||||
          .then(() => {
 | 
					 | 
				
			||||||
            console.log('Progress reset complete')
 | 
					 | 
				
			||||||
            this.$toast.success(`Your progress was reset`)
 | 
					 | 
				
			||||||
            this.resettingProgress = false
 | 
					 | 
				
			||||||
          })
 | 
					 | 
				
			||||||
          .catch((error) => {
 | 
					 | 
				
			||||||
            console.error('Progress reset failed', error)
 | 
					 | 
				
			||||||
            this.resettingProgress = false
 | 
					 | 
				
			||||||
          })
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    deleteAudiobook() {
 | 
					    deleteAudiobook() {
 | 
				
			||||||
      if (confirm(`Are you sure you want to remove this audiobook?\n\n*Does not delete your files, only removes the audiobook from AudioBookshelf`)) {
 | 
					      if (confirm(`Are you sure you want to remove this audiobook?\n\n*Does not delete your files, only removes the audiobook from AudioBookshelf`)) {
 | 
				
			||||||
        this.isProcessing = true
 | 
					        this.isProcessing = true
 | 
				
			||||||
        this.$axios
 | 
					        this.$axios
 | 
				
			||||||
          .$delete(`/api/audiobook/${this.audiobookId}`)
 | 
					          .$delete(`/api/books/${this.audiobookId}`)
 | 
				
			||||||
          .then(() => {
 | 
					          .then(() => {
 | 
				
			||||||
            console.log('Audiobook removed')
 | 
					            console.log('Audiobook removed')
 | 
				
			||||||
            this.$toast.success('Audiobook Removed')
 | 
					            this.$toast.success('Audiobook Removed')
 | 
				
			||||||
 | 
				
			|||||||
@ -133,7 +133,7 @@ export default {
 | 
				
			|||||||
        publisher: true,
 | 
					        publisher: true,
 | 
				
			||||||
        publishYear: true,
 | 
					        publishYear: true,
 | 
				
			||||||
        series: true,
 | 
					        series: true,
 | 
				
			||||||
        volumeNumber: true,
 | 
					        volumeNumber: true
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@ -198,7 +198,7 @@ export default {
 | 
				
			|||||||
        publisher: true,
 | 
					        publisher: true,
 | 
				
			||||||
        publishYear: true,
 | 
					        publishYear: true,
 | 
				
			||||||
        series: true,
 | 
					        series: true,
 | 
				
			||||||
        volumeNumber: true,
 | 
					        volumeNumber: true
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (this.audiobook.id !== this.audiobookId) {
 | 
					      if (this.audiobook.id !== this.audiobookId) {
 | 
				
			||||||
@ -238,7 +238,7 @@ export default {
 | 
				
			|||||||
        var coverPayload = {
 | 
					        var coverPayload = {
 | 
				
			||||||
          url: updatePayload.cover
 | 
					          url: updatePayload.cover
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        var success = await this.$axios.$post(`/api/audiobook/${this.audiobook.id}/cover`, coverPayload).catch((error) => {
 | 
					        var success = await this.$axios.$post(`/api/books/${this.audiobook.id}/cover`, coverPayload).catch((error) => {
 | 
				
			||||||
          console.error('Failed to update', error)
 | 
					          console.error('Failed to update', error)
 | 
				
			||||||
          return false
 | 
					          return false
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
@ -255,7 +255,7 @@ export default {
 | 
				
			|||||||
        var bookUpdatePayload = {
 | 
					        var bookUpdatePayload = {
 | 
				
			||||||
          book: updatePayload
 | 
					          book: updatePayload
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        var success = await this.$axios.$patch(`/api/audiobook/${this.audiobook.id}`, bookUpdatePayload).catch((error) => {
 | 
					        var success = await this.$axios.$patch(`/api/books/${this.audiobook.id}`, bookUpdatePayload).catch((error) => {
 | 
				
			||||||
          console.error('Failed to update', error)
 | 
					          console.error('Failed to update', error)
 | 
				
			||||||
          return false
 | 
					          return false
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
				
			|||||||
@ -105,7 +105,7 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      this.$emit('update:processing', true)
 | 
					      this.$emit('update:processing', true)
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$patch(`/api/library/${this.library.id}`, newLibraryPayload)
 | 
					        .$patch(`/api/libraries/${this.library.id}`, newLibraryPayload)
 | 
				
			||||||
        .then((res) => {
 | 
					        .then((res) => {
 | 
				
			||||||
          this.$emit('update:processing', false)
 | 
					          this.$emit('update:processing', false)
 | 
				
			||||||
          this.$emit('close')
 | 
					          this.$emit('close')
 | 
				
			||||||
@ -137,7 +137,7 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      this.$emit('update:processing', true)
 | 
					      this.$emit('update:processing', true)
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$post('/api/library', newLibraryPayload)
 | 
					        .$post('/api/libraries', newLibraryPayload)
 | 
				
			||||||
        .then((res) => {
 | 
					        .then((res) => {
 | 
				
			||||||
          this.$emit('update:processing', false)
 | 
					          this.$emit('update:processing', false)
 | 
				
			||||||
          this.$emit('close')
 | 
					          this.$emit('close')
 | 
				
			||||||
 | 
				
			|||||||
@ -72,7 +72,7 @@ export default {
 | 
				
			|||||||
      if (confirm(`Are you sure you want to permanently delete library "${this.library.name}"?`)) {
 | 
					      if (confirm(`Are you sure you want to permanently delete library "${this.library.name}"?`)) {
 | 
				
			||||||
        this.isDeleting = true
 | 
					        this.isDeleting = true
 | 
				
			||||||
        this.$axios
 | 
					        this.$axios
 | 
				
			||||||
          .$delete(`/api/library/${this.library.id}`)
 | 
					          .$delete(`/api/libraries/${this.library.id}`)
 | 
				
			||||||
          .then((data) => {
 | 
					          .then((data) => {
 | 
				
			||||||
            this.isDeleting = false
 | 
					            this.isDeleting = false
 | 
				
			||||||
            if (data.error) {
 | 
					            if (data.error) {
 | 
				
			||||||
 | 
				
			|||||||
@ -68,7 +68,7 @@ export default {
 | 
				
			|||||||
        books: this.booksCopy.map((b) => b.id)
 | 
					        books: this.booksCopy.map((b) => b.id)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$patch(`/api/collection/${this.collectionId}`, collectionUpdate)
 | 
					        .$patch(`/api/collections/${this.collectionId}`, collectionUpdate)
 | 
				
			||||||
        .then((collection) => {
 | 
					        .then((collection) => {
 | 
				
			||||||
          console.log('Collection updated', collection)
 | 
					          console.log('Collection updated', collection)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
				
			|||||||
@ -101,7 +101,7 @@ export default {
 | 
				
			|||||||
      if (confirm(`Are you sure you want to permanently delete user "${user.username}"?`)) {
 | 
					      if (confirm(`Are you sure you want to permanently delete user "${user.username}"?`)) {
 | 
				
			||||||
        this.isDeletingUser = true
 | 
					        this.isDeletingUser = true
 | 
				
			||||||
        this.$axios
 | 
					        this.$axios
 | 
				
			||||||
          .$delete(`/api/user/${user.id}`)
 | 
					          .$delete(`/api/users/${user.id}`)
 | 
				
			||||||
          .then((data) => {
 | 
					          .then((data) => {
 | 
				
			||||||
            this.isDeletingUser = false
 | 
					            this.isDeletingUser = false
 | 
				
			||||||
            if (data.error) {
 | 
					            if (data.error) {
 | 
				
			||||||
 | 
				
			|||||||
@ -140,7 +140,7 @@ export default {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      this.isProcessingReadUpdate = true
 | 
					      this.isProcessingReadUpdate = true
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$patch(`/api/user/audiobook/${this.book.id}`, updatePayload)
 | 
					        .$patch(`/api/me/audiobook/${this.book.id}`, updatePayload)
 | 
				
			||||||
        .then(() => {
 | 
					        .then(() => {
 | 
				
			||||||
          this.isProcessingReadUpdate = false
 | 
					          this.isProcessingReadUpdate = false
 | 
				
			||||||
          this.$toast.success(`"${this.bookTitle}" Marked as ${updatePayload.isRead ? 'Read' : 'Not Read'}`)
 | 
					          this.$toast.success(`"${this.bookTitle}" Marked as ${updatePayload.isRead ? 'Read' : 'Not Read'}`)
 | 
				
			||||||
@ -155,7 +155,7 @@ export default {
 | 
				
			|||||||
      this.processingRemove = true
 | 
					      this.processingRemove = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$delete(`/api/collection/${this.collectionId}/book/${this.book.id}`)
 | 
					        .$delete(`/api/collections/${this.collectionId}/book/${this.book.id}`)
 | 
				
			||||||
        .then((updatedCollection) => {
 | 
					        .then((updatedCollection) => {
 | 
				
			||||||
          console.log(`Book removed from collection`, updatedCollection)
 | 
					          console.log(`Book removed from collection`, updatedCollection)
 | 
				
			||||||
          this.$toast.success('Book removed from collection')
 | 
					          this.$toast.success('Book removed from collection')
 | 
				
			||||||
 | 
				
			|||||||
@ -90,7 +90,7 @@ export default {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      this.changingPassword = true
 | 
					      this.changingPassword = true
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$patch('/api/user/password', {
 | 
					        .$patch('/api/me/password', {
 | 
				
			||||||
          password: this.password,
 | 
					          password: this.password,
 | 
				
			||||||
          newPassword: this.newPassword
 | 
					          newPassword: this.newPassword
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
				
			|||||||
@ -115,7 +115,7 @@ export default {
 | 
				
			|||||||
    if (!store.getters['user/getUserCanUpdate']) {
 | 
					    if (!store.getters['user/getUserCanUpdate']) {
 | 
				
			||||||
      return redirect('/?error=unauthorized')
 | 
					      return redirect('/?error=unauthorized')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    var audiobook = await app.$axios.$get(`/api/audiobook/${params.id}`).catch((error) => {
 | 
					    var audiobook = await app.$axios.$get(`/api/books/${params.id}`).catch((error) => {
 | 
				
			||||||
      console.error('Failed', error)
 | 
					      console.error('Failed', error)
 | 
				
			||||||
      return false
 | 
					      return false
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
@ -291,7 +291,7 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      this.saving = true
 | 
					      this.saving = true
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$patch(`/api/audiobook/${this.audiobook.id}/tracks`, { orderedFileData })
 | 
					        .$patch(`/api/books/${this.audiobook.id}/tracks`, { orderedFileData })
 | 
				
			||||||
        .then((data) => {
 | 
					        .then((data) => {
 | 
				
			||||||
          console.log('Finished patching files', data)
 | 
					          console.log('Finished patching files', data)
 | 
				
			||||||
          this.saving = false
 | 
					          this.saving = false
 | 
				
			||||||
 | 
				
			|||||||
@ -161,7 +161,7 @@ export default {
 | 
				
			|||||||
    if (!store.state.user.user) {
 | 
					    if (!store.state.user.user) {
 | 
				
			||||||
      return redirect(`/login?redirect=${route.path}`)
 | 
					      return redirect(`/login?redirect=${route.path}`)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    var audiobook = await app.$axios.$get(`/api/audiobook/${params.id}`).catch((error) => {
 | 
					    var audiobook = await app.$axios.$get(`/api/books/${params.id}`).catch((error) => {
 | 
				
			||||||
      console.error('Failed', error)
 | 
					      console.error('Failed', error)
 | 
				
			||||||
      return false
 | 
					      return false
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
@ -383,7 +383,7 @@ export default {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      this.isProcessingReadUpdate = true
 | 
					      this.isProcessingReadUpdate = true
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$patch(`/api/user/audiobook/${this.audiobookId}`, updatePayload)
 | 
					        .$patch(`/api/me/audiobook/${this.audiobookId}`, updatePayload)
 | 
				
			||||||
        .then(() => {
 | 
					        .then(() => {
 | 
				
			||||||
          this.isProcessingReadUpdate = false
 | 
					          this.isProcessingReadUpdate = false
 | 
				
			||||||
          this.$toast.success(`"${this.title}" Marked as ${updatePayload.isRead ? 'Read' : 'Not Read'}`)
 | 
					          this.$toast.success(`"${this.title}" Marked as ${updatePayload.isRead ? 'Read' : 'Not Read'}`)
 | 
				
			||||||
@ -417,7 +417,7 @@ export default {
 | 
				
			|||||||
    audiobookUpdated() {
 | 
					    audiobookUpdated() {
 | 
				
			||||||
      console.log('Audiobook Updated - Fetch full audiobook')
 | 
					      console.log('Audiobook Updated - Fetch full audiobook')
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$get(`/api/audiobook/${this.audiobookId}`)
 | 
					        .$get(`/api/books/${this.audiobookId}`)
 | 
				
			||||||
        .then((audiobook) => {
 | 
					        .then((audiobook) => {
 | 
				
			||||||
          console.log('Updated audiobook', audiobook)
 | 
					          console.log('Updated audiobook', audiobook)
 | 
				
			||||||
          this.audiobook = audiobook
 | 
					          this.audiobook = audiobook
 | 
				
			||||||
@ -430,7 +430,7 @@ export default {
 | 
				
			|||||||
      if (confirm(`Are you sure you want to reset your progress?`)) {
 | 
					      if (confirm(`Are you sure you want to reset your progress?`)) {
 | 
				
			||||||
        this.resettingProgress = true
 | 
					        this.resettingProgress = true
 | 
				
			||||||
        this.$axios
 | 
					        this.$axios
 | 
				
			||||||
          .$patch(`/api/user/audiobook/${this.audiobookId}/reset-progress`)
 | 
					          .$patch(`/api/me/audiobook/${this.audiobookId}/reset-progress`)
 | 
				
			||||||
          .then(() => {
 | 
					          .then(() => {
 | 
				
			||||||
            console.log('Progress reset complete')
 | 
					            console.log('Progress reset complete')
 | 
				
			||||||
            this.$toast.success(`Your progress was reset`)
 | 
					            this.$toast.success(`Your progress was reset`)
 | 
				
			||||||
 | 
				
			|||||||
@ -169,7 +169,7 @@ export default {
 | 
				
			|||||||
      this.isProcessing = true
 | 
					      this.isProcessing = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.$axios
 | 
					      this.$axios
 | 
				
			||||||
        .$post('/api/audiobooks/update', this.audiobookCopies)
 | 
					        .$post('/api/books/batch/update', this.audiobookCopies)
 | 
				
			||||||
        .then((data) => {
 | 
					        .then((data) => {
 | 
				
			||||||
          this.isProcessing = false
 | 
					          this.isProcessing = false
 | 
				
			||||||
          if (data.updates) {
 | 
					          if (data.updates) {
 | 
				
			||||||
 | 
				
			|||||||
@ -44,7 +44,7 @@ export default {
 | 
				
			|||||||
    if (!store.state.user.user) {
 | 
					    if (!store.state.user.user) {
 | 
				
			||||||
      return redirect(`/login?redirect=${route.path}`)
 | 
					      return redirect(`/login?redirect=${route.path}`)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    var collection = await app.$axios.$get(`/api/collection/${params.id}`).catch((error) => {
 | 
					    var collection = await app.$axios.$get(`/api/collections/${params.id}`).catch((error) => {
 | 
				
			||||||
      console.error('Failed', error)
 | 
					      console.error('Failed', error)
 | 
				
			||||||
      return false
 | 
					      return false
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
@ -105,7 +105,7 @@ export default {
 | 
				
			|||||||
        this.processingRemove = true
 | 
					        this.processingRemove = true
 | 
				
			||||||
        var collectionName = this.collectionName
 | 
					        var collectionName = this.collectionName
 | 
				
			||||||
        this.$axios
 | 
					        this.$axios
 | 
				
			||||||
          .$delete(`/api/collection/${this.collection.id}`)
 | 
					          .$delete(`/api/collections/${this.collection.id}`)
 | 
				
			||||||
          .then(() => {
 | 
					          .then(() => {
 | 
				
			||||||
            this.processingRemove = false
 | 
					            this.processingRemove = false
 | 
				
			||||||
            this.$toast.success(`Collection "${collectionName}" Removed`)
 | 
					            this.$toast.success(`Collection "${collectionName}" Removed`)
 | 
				
			||||||
 | 
				
			|||||||
@ -150,7 +150,7 @@ export default {
 | 
				
			|||||||
      if (confirm('WARNING! This action will remove all audiobooks from the database including any updates or matches you have made. This does not do anything to your actual files. Shall we continue?')) {
 | 
					      if (confirm('WARNING! This action will remove all audiobooks from the database including any updates or matches you have made. This does not do anything to your actual files. Shall we continue?')) {
 | 
				
			||||||
        this.isResettingAudiobooks = true
 | 
					        this.isResettingAudiobooks = true
 | 
				
			||||||
        this.$axios
 | 
					        this.$axios
 | 
				
			||||||
          .$delete('/api/audiobooks')
 | 
					          .$delete('/api/books/all')
 | 
				
			||||||
          .then(() => {
 | 
					          .then(() => {
 | 
				
			||||||
            this.isResettingAudiobooks = false
 | 
					            this.isResettingAudiobooks = false
 | 
				
			||||||
            this.$toast.success('Successfully reset audiobooks')
 | 
					            this.$toast.success('Successfully reset audiobooks')
 | 
				
			||||||
 | 
				
			|||||||
@ -97,7 +97,7 @@ export default {
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    async init() {
 | 
					    async init() {
 | 
				
			||||||
      this.listeningStats = await this.$axios.$get(`/api/user/${this.user.id}/listeningStats`).catch((err) => {
 | 
					      this.listeningStats = await this.$axios.$get(`/api/me/listening-stats`).catch((err) => {
 | 
				
			||||||
        console.error('Failed to load listening sesions', err)
 | 
					        console.error('Failed to load listening sesions', err)
 | 
				
			||||||
        return []
 | 
					        return []
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
				
			|||||||
@ -71,7 +71,7 @@
 | 
				
			|||||||
<script>
 | 
					<script>
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  async asyncData({ params, redirect, app }) {
 | 
					  async asyncData({ params, redirect, app }) {
 | 
				
			||||||
    var user = await app.$axios.$get(`/api/user/${params.id}`).catch((error) => {
 | 
					    var user = await app.$axios.$get(`/api/users/${params.id}`).catch((error) => {
 | 
				
			||||||
      console.error('Failed to get user', error)
 | 
					      console.error('Failed to get user', error)
 | 
				
			||||||
      return null
 | 
					      return null
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
@ -115,11 +115,11 @@ export default {
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    async init() {
 | 
					    async init() {
 | 
				
			||||||
      this.listeningSessions = await this.$axios.$get(`/api/user/${this.user.id}/listeningSessions`).catch((err) => {
 | 
					      this.listeningSessions = await this.$axios.$get(`/api/users/${this.user.id}/listening-sessions`).catch((err) => {
 | 
				
			||||||
        console.error('Failed to load listening sesions', err)
 | 
					        console.error('Failed to load listening sesions', err)
 | 
				
			||||||
        return []
 | 
					        return []
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
      this.listeningStats = await this.$axios.$get(`/api/user/${this.user.id}/listeningStats`).catch((err) => {
 | 
					      this.listeningStats = await this.$axios.$get(`/api/users/${this.user.id}/listening-stats`).catch((err) => {
 | 
				
			||||||
        console.error('Failed to load listening sesions', err)
 | 
					        console.error('Failed to load listening sesions', err)
 | 
				
			||||||
        return []
 | 
					        return []
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
				
			|||||||
@ -31,7 +31,7 @@ export default {
 | 
				
			|||||||
    if (params.id === 'search' && query.query) {
 | 
					    if (params.id === 'search' && query.query) {
 | 
				
			||||||
      searchQuery = query.query
 | 
					      searchQuery = query.query
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      searchResults = await app.$axios.$get(`/api/library/${libraryId}/search?q=${searchQuery}`).catch((error) => {
 | 
					      searchResults = await app.$axios.$get(`/api/libraries/${libraryId}/search?q=${searchQuery}`).catch((error) => {
 | 
				
			||||||
        console.error('Search error', error)
 | 
					        console.error('Search error', error)
 | 
				
			||||||
        return {}
 | 
					        return {}
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
@ -92,7 +92,7 @@ export default {
 | 
				
			|||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    async newQuery() {
 | 
					    async newQuery() {
 | 
				
			||||||
      var query = this.$route.query.query
 | 
					      var query = this.$route.query.query
 | 
				
			||||||
      this.searchResults = await this.$axios.$get(`/api/library/${this.libraryId}/search?q=${query}`).catch((error) => {
 | 
					      this.searchResults = await this.$axios.$get(`/api/libraries/${this.libraryId}/search?q=${query}`).catch((error) => {
 | 
				
			||||||
        console.error('Search error', error)
 | 
					        console.error('Search error', error)
 | 
				
			||||||
        return {}
 | 
					        return {}
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
				
			|||||||
@ -211,7 +211,7 @@ export const actions = {
 | 
				
			|||||||
    commit('setLoadedLibrary', currentLibraryId)
 | 
					    commit('setLoadedLibrary', currentLibraryId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.$axios
 | 
					    this.$axios
 | 
				
			||||||
      .$get(`/api/library/${currentLibraryId}/audiobooks`)
 | 
					      .$get(`/api/libraries/${currentLibraryId}/books`)
 | 
				
			||||||
      .then((data) => {
 | 
					      .then((data) => {
 | 
				
			||||||
        commit('set', data)
 | 
					        commit('set', data)
 | 
				
			||||||
        commit('setLastLoad')
 | 
					        commit('setLastLoad')
 | 
				
			||||||
 | 
				
			|||||||
@ -60,7 +60,7 @@ export const actions = {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return this.$axios
 | 
					    return this.$axios
 | 
				
			||||||
      .$get(`/api/library/${libraryId}`)
 | 
					      .$get(`/api/libraries/${libraryId}`)
 | 
				
			||||||
      .then((data) => {
 | 
					      .then((data) => {
 | 
				
			||||||
        commit('addUpdate', data)
 | 
					        commit('addUpdate', data)
 | 
				
			||||||
        commit('setCurrentLibrary', libraryId)
 | 
					        commit('setCurrentLibrary', libraryId)
 | 
				
			||||||
 | 
				
			|||||||
@ -64,7 +64,7 @@ export const actions = {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    // Immediately update
 | 
					    // Immediately update
 | 
				
			||||||
    commit('setSettings', updatePayload)
 | 
					    commit('setSettings', updatePayload)
 | 
				
			||||||
    return this.$axios.$patch('/api/user/settings', updatePayload).then((result) => {
 | 
					    return this.$axios.$patch('/api/me/settings', updatePayload).then((result) => {
 | 
				
			||||||
      if (result.success) {
 | 
					      if (result.success) {
 | 
				
			||||||
        commit('setSettings', result.settings)
 | 
					        commit('setSettings', result.settings)
 | 
				
			||||||
        return true
 | 
					        return true
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -355,9 +355,6 @@ class Scanner {
 | 
				
			|||||||
      Logger.info(`[Scanner] Updated Audiobook "${existingAudiobook.title}" library and folder to "${libraryId}" "${folderId}"`)
 | 
					      Logger.info(`[Scanner] Updated Audiobook "${existingAudiobook.title}" library and folder to "${libraryId}" "${folderId}"`)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // var audiobooksInLibrary = this.audiobooks.filter(ab => ab.libraryId === libraryId)
 | 
					 | 
				
			||||||
    // var existingAudiobook = audiobooksInLibrary.find(a => a.ino === audiobookData.ino)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // inode value may change when using shared drives, update inode if matching path is found
 | 
					    // inode value may change when using shared drives, update inode if matching path is found
 | 
				
			||||||
    // Note: inode will not change on rename
 | 
					    // Note: inode will not change on rename
 | 
				
			||||||
    var hasUpdatedIno = false
 | 
					    var hasUpdatedIno = false
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										31
									
								
								server/controllers/BackupController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								server/controllers/BackupController.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					const Logger = require('../Logger')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BackupController {
 | 
				
			||||||
 | 
					  constructor() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async delete(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.isRoot) {
 | 
				
			||||||
 | 
					      Logger.error(`[ApiController] Non-Root user attempting to delete backup`, req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var backup = this.backupManager.backups.find(b => b.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!backup) {
 | 
				
			||||||
 | 
					      return res.sendStatus(404)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    await this.backupManager.removeBackup(backup)
 | 
				
			||||||
 | 
					    res.json(this.backupManager.backups.map(b => b.toJSON()))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async upload(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.isRoot) {
 | 
				
			||||||
 | 
					      Logger.error(`[ApiController] Non-Root user attempting to upload backup`, req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!req.files.file) {
 | 
				
			||||||
 | 
					      Logger.error('[ApiController] Upload backup invalid')
 | 
				
			||||||
 | 
					      return res.sendStatus(500)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.backupManager.uploadBackup(req, res)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					module.exports = new BackupController()
 | 
				
			||||||
							
								
								
									
										220
									
								
								server/controllers/BookController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								server/controllers/BookController.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,220 @@
 | 
				
			|||||||
 | 
					const Logger = require('../Logger')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BookController {
 | 
				
			||||||
 | 
					  constructor(db, emitter, clientEmitter, streamManager, coverController) {
 | 
				
			||||||
 | 
					    this.db = db
 | 
				
			||||||
 | 
					    this.emitter = emitter
 | 
				
			||||||
 | 
					    this.clientEmitter = clientEmitter
 | 
				
			||||||
 | 
					    this.streamManager = streamManager
 | 
				
			||||||
 | 
					    this.coverController = coverController
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  findAll(req, res) {
 | 
				
			||||||
 | 
					    var audiobooks = []
 | 
				
			||||||
 | 
					    if (req.query.q) {
 | 
				
			||||||
 | 
					      audiobooks = this.db.audiobooks.filter(ab => {
 | 
				
			||||||
 | 
					        return ab.isSearchMatch(req.query.q)
 | 
				
			||||||
 | 
					      }).map(ab => ab.toJSONMinified())
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      audiobooks = this.db.audiobooks.map(ab => ab.toJSONMinified())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    res.json(audiobooks)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  findOne(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user) {
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var audiobook = this.db.audiobooks.find(a => a.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!audiobook) return res.sendStatus(404)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check user can access this audiobooks library
 | 
				
			||||||
 | 
					    if (!req.user.checkCanAccessLibrary(audiobook.libraryId)) {
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.json(audiobook.toJSONExpanded())
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async update(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.canUpdate) {
 | 
				
			||||||
 | 
					      Logger.warn('User attempted to update without permission', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var audiobook = this.db.audiobooks.find(a => a.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!audiobook) return res.sendStatus(404)
 | 
				
			||||||
 | 
					    var hasUpdates = audiobook.update(req.body)
 | 
				
			||||||
 | 
					    if (hasUpdates) {
 | 
				
			||||||
 | 
					      await this.db.updateAudiobook(audiobook)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.emitter('audiobook_updated', audiobook.toJSONMinified())
 | 
				
			||||||
 | 
					    res.json(audiobook.toJSON())
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async delete(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.canDelete) {
 | 
				
			||||||
 | 
					      Logger.warn('User attempted to delete without permission', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var audiobook = this.db.audiobooks.find(a => a.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!audiobook) return res.sendStatus(404)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await this.handleDeleteAudiobook(audiobook)
 | 
				
			||||||
 | 
					    res.sendStatus(200)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // DELETE: api/books/all
 | 
				
			||||||
 | 
					  async deleteAll(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.isRoot) {
 | 
				
			||||||
 | 
					      Logger.warn('User other than root attempted to delete all audiobooks', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Logger.info('Removing all Audiobooks')
 | 
				
			||||||
 | 
					    var success = await this.db.recreateAudiobookDb()
 | 
				
			||||||
 | 
					    if (success) res.sendStatus(200)
 | 
				
			||||||
 | 
					    else res.sendStatus(500)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // POST: api/books/batch/delete
 | 
				
			||||||
 | 
					  async batchDelete(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.canDelete) {
 | 
				
			||||||
 | 
					      Logger.warn('User attempted to delete without permission', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var { audiobookIds } = req.body
 | 
				
			||||||
 | 
					    if (!audiobookIds || !audiobookIds.length) {
 | 
				
			||||||
 | 
					      return res.sendStatus(500)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var audiobooksToDelete = this.db.audiobooks.filter(ab => audiobookIds.includes(ab.id))
 | 
				
			||||||
 | 
					    if (!audiobooksToDelete.length) {
 | 
				
			||||||
 | 
					      return res.sendStatus(404)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (let i = 0; i < audiobooksToDelete.length; i++) {
 | 
				
			||||||
 | 
					      Logger.info(`[ApiController] Deleting Audiobook "${audiobooksToDelete[i].title}"`)
 | 
				
			||||||
 | 
					      await this.handleDeleteAudiobook(audiobooksToDelete[i])
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    res.sendStatus(200)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // POST: api/books/batch/update
 | 
				
			||||||
 | 
					  async batchUpdate(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.canUpdate) {
 | 
				
			||||||
 | 
					      Logger.warn('User attempted to batch update without permission', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var audiobooks = req.body
 | 
				
			||||||
 | 
					    if (!audiobooks || !audiobooks.length) {
 | 
				
			||||||
 | 
					      return res.sendStatus(500)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var audiobooksUpdated = 0
 | 
				
			||||||
 | 
					    audiobooks = audiobooks.map((ab) => {
 | 
				
			||||||
 | 
					      var _ab = this.db.audiobooks.find(__ab => __ab.id === ab.id)
 | 
				
			||||||
 | 
					      if (!_ab) return null
 | 
				
			||||||
 | 
					      var hasUpdated = _ab.update(ab)
 | 
				
			||||||
 | 
					      if (!hasUpdated) return null
 | 
				
			||||||
 | 
					      audiobooksUpdated++
 | 
				
			||||||
 | 
					      return _ab
 | 
				
			||||||
 | 
					    }).filter(ab => ab)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (audiobooksUpdated) {
 | 
				
			||||||
 | 
					      Logger.info(`[ApiController] ${audiobooksUpdated} Audiobooks have updates`)
 | 
				
			||||||
 | 
					      for (let i = 0; i < audiobooks.length; i++) {
 | 
				
			||||||
 | 
					        await this.db.updateAudiobook(audiobooks[i])
 | 
				
			||||||
 | 
					        this.emitter('audiobook_updated', audiobooks[i].toJSONMinified())
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.json({
 | 
				
			||||||
 | 
					      success: true,
 | 
				
			||||||
 | 
					      updates: audiobooksUpdated
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // PATCH: api/books/:id/tracks
 | 
				
			||||||
 | 
					  async updateTracks(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.canUpdate) {
 | 
				
			||||||
 | 
					      Logger.warn('User attempted to update audiotracks without permission', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var audiobook = this.db.audiobooks.find(a => a.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!audiobook) return res.sendStatus(404)
 | 
				
			||||||
 | 
					    var orderedFileData = req.body.orderedFileData
 | 
				
			||||||
 | 
					    Logger.info(`Updating audiobook tracks called ${audiobook.id}`)
 | 
				
			||||||
 | 
					    audiobook.updateAudioTracks(orderedFileData)
 | 
				
			||||||
 | 
					    await this.db.updateAudiobook(audiobook)
 | 
				
			||||||
 | 
					    this.emitter('audiobook_updated', audiobook.toJSONMinified())
 | 
				
			||||||
 | 
					    res.json(audiobook.toJSON())
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // GET: api/books/:id/stream
 | 
				
			||||||
 | 
					  openStream(req, res) {
 | 
				
			||||||
 | 
					    var audiobook = this.db.audiobooks.find(a => a.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!audiobook) return res.sendStatus(404)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.streamManager.openStreamApiRequest(res, req.user, audiobook)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // POST: api/books/:id/cover
 | 
				
			||||||
 | 
					  async uploadCover(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.canUpload || !req.user.canUpdate) {
 | 
				
			||||||
 | 
					      Logger.warn('User attempted to upload a cover without permission', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var audiobookId = req.params.id
 | 
				
			||||||
 | 
					    var audiobook = this.db.audiobooks.find(ab => ab.id === audiobookId)
 | 
				
			||||||
 | 
					    if (!audiobook) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Audiobook not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var result = null
 | 
				
			||||||
 | 
					    if (req.body && req.body.url) {
 | 
				
			||||||
 | 
					      Logger.debug(`[ApiController] Requesting download cover from url "${req.body.url}"`)
 | 
				
			||||||
 | 
					      result = await this.coverController.downloadCoverFromUrl(audiobook, req.body.url)
 | 
				
			||||||
 | 
					    } else if (req.files && req.files.cover) {
 | 
				
			||||||
 | 
					      Logger.debug(`[ApiController] Handling uploaded cover`)
 | 
				
			||||||
 | 
					      var coverFile = req.files.cover
 | 
				
			||||||
 | 
					      result = await this.coverController.uploadCover(audiobook, coverFile)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return res.status(400).send('Invalid request no file or url')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (result && result.error) {
 | 
				
			||||||
 | 
					      return res.status(400).send(result.error)
 | 
				
			||||||
 | 
					    } else if (!result || !result.cover) {
 | 
				
			||||||
 | 
					      return res.status(500).send('Unknown error occurred')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await this.db.updateAudiobook(audiobook)
 | 
				
			||||||
 | 
					    this.emitter('audiobook_updated', audiobook.toJSONMinified())
 | 
				
			||||||
 | 
					    res.json({
 | 
				
			||||||
 | 
					      success: true,
 | 
				
			||||||
 | 
					      cover: result.cover
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // PATCH api/books/:id/coverfile
 | 
				
			||||||
 | 
					  async updateCoverFromFile(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.canUpdate) {
 | 
				
			||||||
 | 
					      Logger.warn('User attempted to update without permission', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var audiobook = this.db.audiobooks.find(a => a.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!audiobook) return res.sendStatus(404)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var coverFile = req.body
 | 
				
			||||||
 | 
					    var updated = await audiobook.setCoverFromFile(coverFile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (updated) {
 | 
				
			||||||
 | 
					      await this.db.updateAudiobook(audiobook)
 | 
				
			||||||
 | 
					      this.emitter('audiobook_updated', audiobook.toJSONMinified())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (updated) res.status(200).send('Cover updated successfully')
 | 
				
			||||||
 | 
					    else res.status(200).send('No update was made to cover')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					module.exports = new BookController()
 | 
				
			||||||
							
								
								
									
										97
									
								
								server/controllers/CollectionController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								server/controllers/CollectionController.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					const Logger = require('../Logger')
 | 
				
			||||||
 | 
					const UserCollection = require('../objects/UserCollection')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CollectionController {
 | 
				
			||||||
 | 
					  constructor() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async create(req, res) {
 | 
				
			||||||
 | 
					    var newCollection = new UserCollection()
 | 
				
			||||||
 | 
					    req.body.userId = req.user.id
 | 
				
			||||||
 | 
					    var success = newCollection.setData(req.body)
 | 
				
			||||||
 | 
					    if (!success) {
 | 
				
			||||||
 | 
					      return res.status(500).send('Invalid collection data')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var jsonExpanded = newCollection.toJSONExpanded(this.db.audiobooks)
 | 
				
			||||||
 | 
					    await this.db.insertEntity('collection', newCollection)
 | 
				
			||||||
 | 
					    this.clientEmitter(req.user.id, 'collection_added', jsonExpanded)
 | 
				
			||||||
 | 
					    res.json(jsonExpanded)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  findAll(req, res) {
 | 
				
			||||||
 | 
					    var collections = this.db.collections.filter(c => c.userId === req.user.id)
 | 
				
			||||||
 | 
					    var expandedCollections = collections.map(c => c.toJSONExpanded(this.db.audiobooks))
 | 
				
			||||||
 | 
					    res.json(expandedCollections)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  findOne(req, res) {
 | 
				
			||||||
 | 
					    var collection = this.db.collections.find(c => c.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!collection) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Collection not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    res.json(collection.toJSONExpanded(this.db.audiobooks))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async update(req, res) {
 | 
				
			||||||
 | 
					    var collection = this.db.collections.find(c => c.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!collection) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Collection not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var wasUpdated = collection.update(req.body)
 | 
				
			||||||
 | 
					    var jsonExpanded = collection.toJSONExpanded(this.db.audiobooks)
 | 
				
			||||||
 | 
					    if (wasUpdated) {
 | 
				
			||||||
 | 
					      await this.db.updateEntity('collection', collection)
 | 
				
			||||||
 | 
					      this.clientEmitter(req.user.id, 'collection_updated', jsonExpanded)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    res.json(jsonExpanded)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async delete(req, res) {
 | 
				
			||||||
 | 
					    var collection = this.db.collections.find(c => c.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!collection) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Collection not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var jsonExpanded = collection.toJSONExpanded(this.db.audiobooks)
 | 
				
			||||||
 | 
					    await this.db.removeEntity('collection', collection.id)
 | 
				
			||||||
 | 
					    this.clientEmitter(req.user.id, 'collection_removed', jsonExpanded)
 | 
				
			||||||
 | 
					    res.sendStatus(200)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async addBook(req, res) {
 | 
				
			||||||
 | 
					    var collection = this.db.collections.find(c => c.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!collection) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Collection not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var audiobook = this.db.audiobooks.find(ab => ab.id === req.body.id)
 | 
				
			||||||
 | 
					    if (!audiobook) {
 | 
				
			||||||
 | 
					      return res.status(500).send('Book not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (audiobook.libraryId !== collection.libraryId) {
 | 
				
			||||||
 | 
					      return res.status(500).send('Book in different library')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (collection.books.includes(req.body.id)) {
 | 
				
			||||||
 | 
					      return res.status(500).send('Book already in collection')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    collection.addBook(req.body.id)
 | 
				
			||||||
 | 
					    var jsonExpanded = collection.toJSONExpanded(this.db.audiobooks)
 | 
				
			||||||
 | 
					    await this.db.updateEntity('collection', collection)
 | 
				
			||||||
 | 
					    this.clientEmitter(req.user.id, 'collection_updated', jsonExpanded)
 | 
				
			||||||
 | 
					    res.json(jsonExpanded)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // DELETE: api/collections/:id/book/:bookId
 | 
				
			||||||
 | 
					  async removeBook(req, res) {
 | 
				
			||||||
 | 
					    var collection = this.db.collections.find(c => c.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!collection) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Collection not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (collection.books.includes(req.params.bookId)) {
 | 
				
			||||||
 | 
					      collection.removeBook(req.params.bookId)
 | 
				
			||||||
 | 
					      var jsonExpanded = collection.toJSONExpanded(this.db.audiobooks)
 | 
				
			||||||
 | 
					      await this.db.updateEntity('collection', collection)
 | 
				
			||||||
 | 
					      this.clientEmitter(req.user.id, 'collection_updated', jsonExpanded)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    res.json(collection.toJSONExpanded(this.db.audiobooks))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					module.exports = new CollectionController()
 | 
				
			||||||
							
								
								
									
										205
									
								
								server/controllers/LibraryController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								server/controllers/LibraryController.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,205 @@
 | 
				
			|||||||
 | 
					const Logger = require('../Logger')
 | 
				
			||||||
 | 
					const Library = require('../objects/Library')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LibraryController {
 | 
				
			||||||
 | 
					  constructor() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async create(req, res) {
 | 
				
			||||||
 | 
					    var newLibraryPayload = {
 | 
				
			||||||
 | 
					      ...req.body
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!newLibraryPayload.name || !newLibraryPayload.folders || !newLibraryPayload.folders.length) {
 | 
				
			||||||
 | 
					      return res.status(500).send('Invalid request')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var library = new Library()
 | 
				
			||||||
 | 
					    newLibraryPayload.displayOrder = this.db.libraries.length + 1
 | 
				
			||||||
 | 
					    library.setData(newLibraryPayload)
 | 
				
			||||||
 | 
					    await this.db.insertEntity('library', library)
 | 
				
			||||||
 | 
					    this.emitter('library_added', library.toJSON())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Add library watcher
 | 
				
			||||||
 | 
					    this.watcher.addLibrary(library)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.json(library)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  findAll(req, res) {
 | 
				
			||||||
 | 
					    res.json(this.db.libraries.map(lib => lib.toJSON()))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  findOne(req, res) {
 | 
				
			||||||
 | 
					    if (!req.params.id) return res.status(500).send('Invalid id parameter')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var library = this.db.libraries.find(lib => lib.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!library) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Library not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res.json(library.toJSON())
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async update(req, res) {
 | 
				
			||||||
 | 
					    var library = this.db.libraries.find(lib => lib.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!library) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Library not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var hasUpdates = library.update(req.body)
 | 
				
			||||||
 | 
					    if (hasUpdates) {
 | 
				
			||||||
 | 
					      // Update watcher
 | 
				
			||||||
 | 
					      this.watcher.updateLibrary(library)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Remove audiobooks no longer in library
 | 
				
			||||||
 | 
					      var audiobooksToRemove = this.db.audiobooks.filter(ab => !library.checkFullPathInLibrary(ab.fullPath))
 | 
				
			||||||
 | 
					      if (audiobooksToRemove.length) {
 | 
				
			||||||
 | 
					        Logger.info(`[Scanner] Updating library, removing ${audiobooksToRemove.length} audiobooks`)
 | 
				
			||||||
 | 
					        for (let i = 0; i < audiobooksToRemove.length; i++) {
 | 
				
			||||||
 | 
					          await this.handleDeleteAudiobook(audiobooksToRemove[i])
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      await this.db.updateEntity('library', library)
 | 
				
			||||||
 | 
					      this.emitter('library_updated', library.toJSON())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res.json(library.toJSON())
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async delete(req, res) {
 | 
				
			||||||
 | 
					    var library = this.db.libraries.find(lib => lib.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!library) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Library not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Remove library watcher
 | 
				
			||||||
 | 
					    this.watcher.removeLibrary(library)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Remove audiobooks in this library
 | 
				
			||||||
 | 
					    var audiobooks = this.db.audiobooks.filter(ab => ab.libraryId === library.id)
 | 
				
			||||||
 | 
					    Logger.info(`[Server] deleting library "${library.name}" with ${audiobooks.length} audiobooks"`)
 | 
				
			||||||
 | 
					    for (let i = 0; i < audiobooks.length; i++) {
 | 
				
			||||||
 | 
					      await this.handleDeleteAudiobook(audiobooks[i])
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var libraryJson = library.toJSON()
 | 
				
			||||||
 | 
					    await this.db.removeEntity('library', library.id)
 | 
				
			||||||
 | 
					    this.emitter('library_removed', libraryJson)
 | 
				
			||||||
 | 
					    return res.json(libraryJson)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // api/libraries/:id/books
 | 
				
			||||||
 | 
					  getBooksForLibrary(req, res) {
 | 
				
			||||||
 | 
					    var libraryId = req.params.id
 | 
				
			||||||
 | 
					    var library = this.db.libraries.find(lib => lib.id === libraryId)
 | 
				
			||||||
 | 
					    if (!library) {
 | 
				
			||||||
 | 
					      return res.status(400).send('Library does not exist')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var audiobooks = []
 | 
				
			||||||
 | 
					    if (req.query.q) {
 | 
				
			||||||
 | 
					      audiobooks = this.db.audiobooks.filter(ab => {
 | 
				
			||||||
 | 
					        return ab.libraryId === libraryId && ab.isSearchMatch(req.query.q)
 | 
				
			||||||
 | 
					      }).map(ab => ab.toJSONMinified())
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      audiobooks = this.db.audiobooks.filter(ab => ab.libraryId === libraryId).map(ab => ab.toJSONMinified())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    res.json(audiobooks)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // PATCH: Change the order of libraries
 | 
				
			||||||
 | 
					  async reorder(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.isRoot) {
 | 
				
			||||||
 | 
					      Logger.error('[ApiController] ReorderLibraries invalid user', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(401)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var orderdata = req.body
 | 
				
			||||||
 | 
					    var hasUpdates = false
 | 
				
			||||||
 | 
					    for (let i = 0; i < orderdata.length; i++) {
 | 
				
			||||||
 | 
					      var library = this.db.libraries.find(lib => lib.id === orderdata[i].id)
 | 
				
			||||||
 | 
					      if (!library) {
 | 
				
			||||||
 | 
					        Logger.error(`[ApiController] Invalid library not found in reorder ${orderdata[i].id}`)
 | 
				
			||||||
 | 
					        return res.sendStatus(500)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (library.update({ displayOrder: orderdata[i].newOrder })) {
 | 
				
			||||||
 | 
					        hasUpdates = true
 | 
				
			||||||
 | 
					        await this.db.updateEntity('library', library)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (hasUpdates) {
 | 
				
			||||||
 | 
					      Logger.info(`[ApiController] Updated library display orders`)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      Logger.info(`[ApiController] Library orders were up to date`)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var libraries = this.db.libraries.map(lib => lib.toJSON())
 | 
				
			||||||
 | 
					    res.json(libraries)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // GET: Global library search
 | 
				
			||||||
 | 
					  search(req, res) {
 | 
				
			||||||
 | 
					    var library = this.db.libraries.find(lib => lib.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!library) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Library not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!req.query.q) {
 | 
				
			||||||
 | 
					      return res.status(400).send('No query string')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var maxResults = req.query.max || 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var bookMatches = []
 | 
				
			||||||
 | 
					    var authorMatches = {}
 | 
				
			||||||
 | 
					    var seriesMatches = {}
 | 
				
			||||||
 | 
					    var tagMatches = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var audiobooksInLibrary = this.db.audiobooks.filter(ab => ab.libraryId === library.id)
 | 
				
			||||||
 | 
					    audiobooksInLibrary.forEach((ab) => {
 | 
				
			||||||
 | 
					      var queryResult = ab.searchQuery(req.query.q)
 | 
				
			||||||
 | 
					      if (queryResult.book) {
 | 
				
			||||||
 | 
					        var bookMatchObj = {
 | 
				
			||||||
 | 
					          audiobook: ab,
 | 
				
			||||||
 | 
					          matchKey: queryResult.book,
 | 
				
			||||||
 | 
					          matchText: queryResult.bookMatchText
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        bookMatches.push(bookMatchObj)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (queryResult.authors) {
 | 
				
			||||||
 | 
					        queryResult.authors.forEach((author) => {
 | 
				
			||||||
 | 
					          if (!authorMatches[author]) {
 | 
				
			||||||
 | 
					            authorMatches[author] = {
 | 
				
			||||||
 | 
					              author: author
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (queryResult.series) {
 | 
				
			||||||
 | 
					        if (!seriesMatches[queryResult.series]) {
 | 
				
			||||||
 | 
					          seriesMatches[queryResult.series] = {
 | 
				
			||||||
 | 
					            series: queryResult.series,
 | 
				
			||||||
 | 
					            audiobooks: [ab]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          seriesMatches[queryResult.series].audiobooks.push(ab)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (queryResult.tags && queryResult.tags.length) {
 | 
				
			||||||
 | 
					        queryResult.tags.forEach((tag) => {
 | 
				
			||||||
 | 
					          if (!tagMatches[tag]) {
 | 
				
			||||||
 | 
					            tagMatches[tag] = {
 | 
				
			||||||
 | 
					              tag,
 | 
				
			||||||
 | 
					              audiobooks: [ab]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            tagMatches[tag].audiobooks.push(ab)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.json({
 | 
				
			||||||
 | 
					      audiobooks: bookMatches.slice(0, maxResults),
 | 
				
			||||||
 | 
					      tags: Object.values(tagMatches).slice(0, maxResults),
 | 
				
			||||||
 | 
					      authors: Object.values(authorMatches).slice(0, maxResults),
 | 
				
			||||||
 | 
					      series: Object.values(seriesMatches).slice(0, maxResults)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					module.exports = new LibraryController()
 | 
				
			||||||
							
								
								
									
										96
									
								
								server/controllers/MeController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								server/controllers/MeController.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
				
			|||||||
 | 
					const Logger = require('../Logger')
 | 
				
			||||||
 | 
					const { isObject } = require('../utils/index')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MeController {
 | 
				
			||||||
 | 
					  constructor() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // GET: api/me/listening-sessions
 | 
				
			||||||
 | 
					  async getListeningSessions(req, res) {
 | 
				
			||||||
 | 
					    var listeningSessions = await this.getUserListeningSessionsHelper(req.user.id)
 | 
				
			||||||
 | 
					    res.json(listeningSessions.slice(0, 10))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // GET: api/me/listening-stats
 | 
				
			||||||
 | 
					  async getListeningStats(req, res) {
 | 
				
			||||||
 | 
					    var listeningStats = await this.getUserListeningStatsHelpers(req.user.id)
 | 
				
			||||||
 | 
					    res.json(listeningStats)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // PATCH: api/me/audiobook/:id/reset-progress
 | 
				
			||||||
 | 
					  async resetAudiobookProgress(req, res) {
 | 
				
			||||||
 | 
					    var audiobook = this.db.audiobooks.find(ab => ab.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!audiobook) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Audiobook not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    req.user.resetAudiobookProgress(audiobook)
 | 
				
			||||||
 | 
					    await this.db.updateEntity('user', req.user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var userAudiobookData = req.user.audiobooks[audiobook.id]
 | 
				
			||||||
 | 
					    if (userAudiobookData) {
 | 
				
			||||||
 | 
					      this.clientEmitter(req.user.id, 'current_user_audiobook_update', { id: audiobook.id, data: userAudiobookData })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
 | 
				
			||||||
 | 
					    res.sendStatus(200)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // PATCH: api/me/audiobook/:id
 | 
				
			||||||
 | 
					  async updateAudiobookData(req, res) {
 | 
				
			||||||
 | 
					    var audiobook = this.db.audiobooks.find(ab => ab.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!audiobook) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Audiobook not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var wasUpdated = req.user.updateAudiobookData(audiobook, req.body)
 | 
				
			||||||
 | 
					    if (wasUpdated) {
 | 
				
			||||||
 | 
					      await this.db.updateEntity('user', req.user)
 | 
				
			||||||
 | 
					      this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    res.sendStatus(200)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // PATCH: api/me/audiobook/batch/update
 | 
				
			||||||
 | 
					  async batchUpdateAudiobookData(req, res) {
 | 
				
			||||||
 | 
					    var userAbDataPayloads = req.body
 | 
				
			||||||
 | 
					    if (!userAbDataPayloads || !userAbDataPayloads.length) {
 | 
				
			||||||
 | 
					      return res.sendStatus(500)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var shouldUpdate = false
 | 
				
			||||||
 | 
					    userAbDataPayloads.forEach((userAbData) => {
 | 
				
			||||||
 | 
					      var audiobook = this.db.audiobooks.find(ab => ab.id === userAbData.audiobookId)
 | 
				
			||||||
 | 
					      if (audiobook) {
 | 
				
			||||||
 | 
					        var wasUpdated = req.user.updateAudiobookData(audiobook, userAbData)
 | 
				
			||||||
 | 
					        if (wasUpdated) shouldUpdate = true
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (shouldUpdate) {
 | 
				
			||||||
 | 
					      await this.db.updateEntity('user', req.user)
 | 
				
			||||||
 | 
					      this.clientEmitter(req.user.id, 'user_updated', req.user.toJSONForBrowser())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.sendStatus(200)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // PATCH: api/me/password
 | 
				
			||||||
 | 
					  updatePassword(req, res) {
 | 
				
			||||||
 | 
					    this.auth.userChangePassword(req, res)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // PATCH: api/me/settings
 | 
				
			||||||
 | 
					  async updateSettings(req, res) {
 | 
				
			||||||
 | 
					    var settingsUpdate = req.body
 | 
				
			||||||
 | 
					    if (!settingsUpdate || !isObject(settingsUpdate)) {
 | 
				
			||||||
 | 
					      return res.sendStatus(500)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var madeUpdates = req.user.updateSettings(settingsUpdate)
 | 
				
			||||||
 | 
					    if (madeUpdates) {
 | 
				
			||||||
 | 
					      await this.db.updateEntity('user', req.user)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res.json({
 | 
				
			||||||
 | 
					      success: true,
 | 
				
			||||||
 | 
					      settings: req.user.settings
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					module.exports = new MeController()
 | 
				
			||||||
							
								
								
									
										152
									
								
								server/controllers/UserController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								server/controllers/UserController.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,152 @@
 | 
				
			|||||||
 | 
					const Logger = require('../Logger')
 | 
				
			||||||
 | 
					const User = require('../objects/User')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const { getId } = require('../utils/index')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UserController {
 | 
				
			||||||
 | 
					  constructor() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async create(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.isRoot) {
 | 
				
			||||||
 | 
					      Logger.warn('Non-root user attempted to create user', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var account = req.body
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var username = account.username
 | 
				
			||||||
 | 
					    var usernameExists = this.db.users.find(u => u.username.toLowerCase() === username.toLowerCase())
 | 
				
			||||||
 | 
					    if (usernameExists) {
 | 
				
			||||||
 | 
					      return res.status(500).send('Username already taken')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    account.id = getId('usr')
 | 
				
			||||||
 | 
					    account.pash = await this.auth.hashPass(account.password)
 | 
				
			||||||
 | 
					    delete account.password
 | 
				
			||||||
 | 
					    account.token = await this.auth.generateAccessToken({ userId: account.id })
 | 
				
			||||||
 | 
					    account.createdAt = Date.now()
 | 
				
			||||||
 | 
					    var newUser = new User(account)
 | 
				
			||||||
 | 
					    var success = await this.db.insertEntity('user', newUser)
 | 
				
			||||||
 | 
					    if (success) {
 | 
				
			||||||
 | 
					      this.clientEmitter(req.user.id, 'user_added', newUser)
 | 
				
			||||||
 | 
					      res.json({
 | 
				
			||||||
 | 
					        user: newUser.toJSONForBrowser()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return res.status(500).send('Failed to save new user')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  findAll(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.isRoot) return res.sendStatus(403)
 | 
				
			||||||
 | 
					    var users = this.db.users.map(u => this.userJsonWithBookProgressDetails(u))
 | 
				
			||||||
 | 
					    res.json(users)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  findOne(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.isRoot) {
 | 
				
			||||||
 | 
					      Logger.error('User other than root attempting to get user', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var user = this.db.users.find(u => u.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!user) {
 | 
				
			||||||
 | 
					      return res.sendStatus(404)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.json(this.userJsonWithBookProgressDetails(user))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async update(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.isRoot) {
 | 
				
			||||||
 | 
					      Logger.error('User other than root attempting to update user', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var user = this.db.users.find(u => u.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!user) {
 | 
				
			||||||
 | 
					      return res.sendStatus(404)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var account = req.body
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (account.username !== undefined && account.username !== user.username) {
 | 
				
			||||||
 | 
					      var usernameExists = this.db.users.find(u => u.username.toLowerCase() === account.username.toLowerCase())
 | 
				
			||||||
 | 
					      if (usernameExists) {
 | 
				
			||||||
 | 
					        return res.status(500).send('Username already taken')
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Updating password
 | 
				
			||||||
 | 
					    if (account.password) {
 | 
				
			||||||
 | 
					      account.pash = await this.auth.hashPass(account.password)
 | 
				
			||||||
 | 
					      delete account.password
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var hasUpdated = user.update(account)
 | 
				
			||||||
 | 
					    if (hasUpdated) {
 | 
				
			||||||
 | 
					      await this.db.updateEntity('user', user)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.clientEmitter(req.user.id, 'user_updated', user.toJSONForBrowser())
 | 
				
			||||||
 | 
					    res.json({
 | 
				
			||||||
 | 
					      success: true,
 | 
				
			||||||
 | 
					      user: user.toJSONForBrowser()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async delete(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.isRoot) {
 | 
				
			||||||
 | 
					      Logger.error('User other than root attempting to delete user', req.user)
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (req.params.id === 'root') {
 | 
				
			||||||
 | 
					      return res.sendStatus(500)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (req.user.id === req.params.id) {
 | 
				
			||||||
 | 
					      Logger.error('Attempting to delete themselves...')
 | 
				
			||||||
 | 
					      return res.sendStatus(500)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var user = this.db.users.find(u => u.id === req.params.id)
 | 
				
			||||||
 | 
					    if (!user) {
 | 
				
			||||||
 | 
					      Logger.error('User not found')
 | 
				
			||||||
 | 
					      return res.json({
 | 
				
			||||||
 | 
					        error: 'User not found'
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // delete user collections
 | 
				
			||||||
 | 
					    var userCollections = this.db.collections.filter(c => c.userId === user.id)
 | 
				
			||||||
 | 
					    var collectionsToRemove = userCollections.map(uc => uc.id)
 | 
				
			||||||
 | 
					    for (let i = 0; i < collectionsToRemove.length; i++) {
 | 
				
			||||||
 | 
					      await this.db.removeEntity('collection', collectionsToRemove[i])
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Todo: check if user is logged in and cancel streams
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var userJson = user.toJSONForBrowser()
 | 
				
			||||||
 | 
					    await this.db.removeEntity('user', user.id)
 | 
				
			||||||
 | 
					    this.clientEmitter(req.user.id, 'user_removed', userJson)
 | 
				
			||||||
 | 
					    res.json({
 | 
				
			||||||
 | 
					      success: true
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // GET: api/users/:id/listening-sessions
 | 
				
			||||||
 | 
					  async getListeningSessions(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.isRoot && req.user.id !== req.params.id) {
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var listeningSessions = await this.getUserListeningSessionsHelper(req.params.id)
 | 
				
			||||||
 | 
					    res.json(listeningSessions.slice(0, 10))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // GET: api/users/:id/listening-stats
 | 
				
			||||||
 | 
					  async getListeningStats(req, res) {
 | 
				
			||||||
 | 
					    if (!req.user.isRoot && req.user.id !== req.params.id) {
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var listeningStats = await this.getUserListeningStatsHelpers(req.params.id)
 | 
				
			||||||
 | 
					    res.json(listeningStats)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					module.exports = new UserController()
 | 
				
			||||||
@ -353,6 +353,7 @@ class Audiobook {
 | 
				
			|||||||
      if (imageFile) {
 | 
					      if (imageFile) {
 | 
				
			||||||
        data.coverFullPath = imageFile.fullPath
 | 
					        data.coverFullPath = imageFile.fullPath
 | 
				
			||||||
        var relImagePath = imageFile.path.replace(this.path, '')
 | 
					        var relImagePath = imageFile.path.replace(this.path, '')
 | 
				
			||||||
 | 
					        console.log('SET BOOK PATH', imageFile.path, 'REPLACE', this.path, 'RESULT', relImagePath)
 | 
				
			||||||
        data.cover = Path.posix.join(`/s/book/${this.id}`, relImagePath)
 | 
					        data.cover = Path.posix.join(`/s/book/${this.id}`, relImagePath)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -89,10 +89,17 @@ function setFileOwner(path, uid, gid) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
module.exports.setFileOwner = setFileOwner
 | 
					module.exports.setFileOwner = setFileOwner
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function recurseFiles(path) {
 | 
					async function recurseFiles(path, relPathToReplace = null) {
 | 
				
			||||||
  path = path.replace(/\\/g, '/')
 | 
					  path = path.replace(/\\/g, '/')
 | 
				
			||||||
  if (!path.endsWith('/')) path = path + '/'
 | 
					  if (!path.endsWith('/')) path = path + '/'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (relPathToReplace) {
 | 
				
			||||||
 | 
					    relPathToReplace = relPathToReplace.replace(/\\/g, '/')
 | 
				
			||||||
 | 
					    if (!relPathToReplace.endsWith('/')) relPathToReplace += '/'
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    relPathToReplace = path
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const options = {
 | 
					  const options = {
 | 
				
			||||||
    mode: rra.LIST,
 | 
					    mode: rra.LIST,
 | 
				
			||||||
    recursive: true,
 | 
					    recursive: true,
 | 
				
			||||||
@ -116,7 +123,7 @@ async function recurseFiles(path) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Ignore any file if a directory or the filename starts with "."
 | 
					    // Ignore any file if a directory or the filename starts with "."
 | 
				
			||||||
    var relpath = item.fullname.replace(path, '')
 | 
					    var relpath = item.fullname.replace(relPathToReplace, '')
 | 
				
			||||||
    var pathStartsWithPeriod = relpath.split('/').find(p => p.startsWith('.'))
 | 
					    var pathStartsWithPeriod = relpath.split('/').find(p => p.startsWith('.'))
 | 
				
			||||||
    if (pathStartsWithPeriod) {
 | 
					    if (pathStartsWithPeriod) {
 | 
				
			||||||
      Logger.debug(`[fileUtils] Ignoring path has . "${relpath}"`)
 | 
					      Logger.debug(`[fileUtils] Ignoring path has . "${relpath}"`)
 | 
				
			||||||
@ -126,9 +133,9 @@ async function recurseFiles(path) {
 | 
				
			|||||||
    return true
 | 
					    return true
 | 
				
			||||||
  }).map((item) => ({
 | 
					  }).map((item) => ({
 | 
				
			||||||
    name: item.name,
 | 
					    name: item.name,
 | 
				
			||||||
    path: item.fullname.replace(path, ''),
 | 
					    path: item.fullname.replace(relPathToReplace, ''),
 | 
				
			||||||
    dirpath: item.path,
 | 
					    dirpath: item.path,
 | 
				
			||||||
    reldirpath: item.path.replace(path, ''),
 | 
					    reldirpath: item.path.replace(relPathToReplace, ''),
 | 
				
			||||||
    fullpath: item.fullname,
 | 
					    fullpath: item.fullname,
 | 
				
			||||||
    extension: item.extension,
 | 
					    extension: item.extension,
 | 
				
			||||||
    deep: item.deep
 | 
					    deep: item.deep
 | 
				
			||||||
 | 
				
			|||||||
@ -267,7 +267,7 @@ function getAudiobookDataFromDir(folderPath, dir, parseSubtitle = false) {
 | 
				
			|||||||
async function getAudiobookFileData(folder, audiobookPath, serverSettings = {}) {
 | 
					async function getAudiobookFileData(folder, audiobookPath, serverSettings = {}) {
 | 
				
			||||||
  var parseSubtitle = !!serverSettings.scannerParseSubtitle
 | 
					  var parseSubtitle = !!serverSettings.scannerParseSubtitle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var fileItems = await recurseFiles(audiobookPath)
 | 
					  var fileItems = await recurseFiles(audiobookPath, folder.fullPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  audiobookPath = audiobookPath.replace(/\\/g, '/')
 | 
					  audiobookPath = audiobookPath.replace(/\\/g, '/')
 | 
				
			||||||
  var folderFullPath = folder.fullPath.replace(/\\/g, '/')
 | 
					  var folderFullPath = folder.fullPath.replace(/\\/g, '/')
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user