mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-11-03 19:07:00 -05:00 
			
		
		
		
	Add LibrarySettings and update edit library modal to include settings tab
This commit is contained in:
		
							parent
							
								
									cbde451120
								
							
						
					
					
						commit
						5a26b01ffb
					
				@ -5,7 +5,7 @@
 | 
				
			|||||||
        <p class="font-book text-3xl text-white truncate pointer-events-none">{{ title }}</p>
 | 
					        <p class="font-book text-3xl text-white truncate pointer-events-none">{{ title }}</p>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </template>
 | 
					    </template>
 | 
				
			||||||
    <div class="absolute -top-10 left-0 w-full flex">
 | 
					    <div class="absolute -top-10 left-0 z-10 w-full flex">
 | 
				
			||||||
      <template v-for="tab in availableTabs">
 | 
					      <template v-for="tab in availableTabs">
 | 
				
			||||||
        <div :key="tab.id" class="w-28 rounded-t-lg flex items-center justify-center mr-1 cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300 tab text-xs sm:text-base" :class="selectedTab === tab.id ? 'tab-selected bg-bg pb-px' : 'bg-primary text-gray-400'" @click="selectTab(tab.id)">{{ tab.title }}</div>
 | 
					        <div :key="tab.id" class="w-28 rounded-t-lg flex items-center justify-center mr-1 cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300 tab text-xs sm:text-base" :class="selectedTab === tab.id ? 'tab-selected bg-bg pb-px' : 'bg-primary text-gray-400'" @click="selectTab(tab.id)">{{ tab.title }}</div>
 | 
				
			||||||
      </template>
 | 
					      </template>
 | 
				
			||||||
@ -252,7 +252,7 @@ export default {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style scoped>
 | 
				
			||||||
.tab {
 | 
					.tab {
 | 
				
			||||||
  height: 40px;
 | 
					  height: 40px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,22 +1,18 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="w-full h-full px-4 py-2 mb-4">
 | 
					  <div class="w-full h-full px-4 py-2 mb-4">
 | 
				
			||||||
    <div v-show="showDirectoryPicker" class="flex items-center py-1 mb-2">
 | 
					 | 
				
			||||||
      <span class="material-icons text-3xl cursor-pointer hover:text-gray-300" @click="backArrowPress">arrow_back</span>
 | 
					 | 
				
			||||||
      <p class="px-4 text-xl">{{ title }}</p>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
    <div v-if="!showDirectoryPicker" class="w-full h-full py-4">
 | 
					    <div v-if="!showDirectoryPicker" class="w-full h-full py-4">
 | 
				
			||||||
      <div class="flex flex-wrap md:flex-nowrap -mx-1">
 | 
					      <div class="flex flex-wrap md:flex-nowrap -mx-1">
 | 
				
			||||||
        <div class="w-2/5 md:w-72 px-1 py-1 md:py-0">
 | 
					        <div class="w-2/5 md:w-72 px-1 py-1 md:py-0">
 | 
				
			||||||
          <ui-dropdown v-model="mediaType" :items="mediaTypes" label="Media Type" :disabled="!!library" small @input="changedMediaType" />
 | 
					          <ui-dropdown v-model="mediaType" :items="mediaTypes" label="Media Type" :disabled="!isNew" small @input="changedMediaType" />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="w-full md:flex-grow px-1 py-1 md:py-0">
 | 
					        <div class="w-full md:flex-grow px-1 py-1 md:py-0">
 | 
				
			||||||
          <ui-text-input-with-label v-model="name" label="Library Name" />
 | 
					          <ui-text-input-with-label v-model="name" label="Library Name" @blur="nameBlurred" />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="w-1/5 md:w-18 px-1 py-1 md:py-0">
 | 
					        <div class="w-1/5 md:w-18 px-1 py-1 md:py-0">
 | 
				
			||||||
          <ui-media-icon-picker v-model="icon" />
 | 
					          <ui-media-icon-picker v-model="icon" @input="iconChanged" />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="w-2/5 md:w-72 px-1 py-1 md:py-0">
 | 
					        <div class="w-2/5 md:w-72 px-1 py-1 md:py-0">
 | 
				
			||||||
          <ui-dropdown v-model="provider" :items="providers" label="Metadata Provider" small />
 | 
					          <ui-dropdown v-model="provider" :items="providers" label="Metadata Provider" small @input="formUpdated" />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -27,39 +23,23 @@
 | 
				
			|||||||
          <ui-editable-text v-model="folder.fullPath" readonly type="text" class="w-full" />
 | 
					          <ui-editable-text v-model="folder.fullPath" readonly type="text" class="w-full" />
 | 
				
			||||||
          <span v-show="folders.length > 1" class="material-icons ml-2 cursor-pointer hover:text-error" @click="removeFolder(folder)">close</span>
 | 
					          <span v-show="folders.length > 1" class="material-icons ml-2 cursor-pointer hover:text-error" @click="removeFolder(folder)">close</span>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <!-- <p v-if="!folders.length" class="text-sm text-gray-300 px-1 py-2">No folders</p> -->
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div class="flex py-1 px-2 items-center w-full">
 | 
					        <div class="flex py-1 px-2 items-center w-full">
 | 
				
			||||||
          <span class="material-icons bg-opacity-50 mr-2 text-yellow-200" style="font-size: 1.2rem">folder</span>
 | 
					          <span class="material-icons bg-opacity-50 mr-2 text-yellow-200" style="font-size: 1.2rem">folder</span>
 | 
				
			||||||
          <ui-editable-text v-model="newFolderPath" placeholder="New folder path" type="text" class="w-full" />
 | 
					          <ui-editable-text v-model="newFolderPath" placeholder="New folder path" type="text" class="w-full" @blur="newFolderInputBlurred" />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <ui-btn class="w-full mt-2" color="primary" @click="showDirectoryPicker = true">Browse for Folder</ui-btn>
 | 
					        <ui-btn class="w-full mt-2" color="primary" @click="showDirectoryPicker = true">Browse for Folder</ui-btn>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="absolute bottom-0 left-0 w-full py-4 px-4">
 | 
					 | 
				
			||||||
        <div class="flex items-center">
 | 
					 | 
				
			||||||
          <div class="flex-grow" />
 | 
					 | 
				
			||||||
          <ui-btn v-show="!disableSubmit" color="success" :disabled="disableSubmit" @click="submit">{{ library ? 'Update Library' : 'Create Library' }}</ui-btn>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <modals-libraries-folder-chooser v-else :paths="folderPaths" @select="selectFolder" />
 | 
					    <modals-libraries-folder-chooser v-else :paths="folderPaths" @back="showDirectoryPicker = false" @select="selectFolder" />
 | 
				
			||||||
 | 
					 | 
				
			||||||
    <div v-if="!showDirectoryPicker">
 | 
					 | 
				
			||||||
      <div class="flex items-center pt-2">
 | 
					 | 
				
			||||||
        <ui-toggle-switch v-if="!globalWatcherDisabled" v-model="disableWatcher" />
 | 
					 | 
				
			||||||
        <ui-toggle-switch v-else disabled :value="false" />
 | 
					 | 
				
			||||||
        <p class="pl-4 text-lg">Disable folder watcher for library</p>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <p v-if="globalWatcherDisabled" class="text-xs text-warning">*Watcher is disabled globally in server settings</p>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  props: {
 | 
					  props: {
 | 
				
			||||||
 | 
					    isNew: Boolean,
 | 
				
			||||||
    library: {
 | 
					    library: {
 | 
				
			||||||
      type: Object,
 | 
					      type: Object,
 | 
				
			||||||
      default: () => null
 | 
					      default: () => null
 | 
				
			||||||
@ -73,7 +53,6 @@ export default {
 | 
				
			|||||||
      icon: '',
 | 
					      icon: '',
 | 
				
			||||||
      folders: [],
 | 
					      folders: [],
 | 
				
			||||||
      showDirectoryPicker: false,
 | 
					      showDirectoryPicker: false,
 | 
				
			||||||
      disableWatcher: false,
 | 
					 | 
				
			||||||
      newFolderPath: '',
 | 
					      newFolderPath: '',
 | 
				
			||||||
      mediaType: null,
 | 
					      mediaType: null,
 | 
				
			||||||
      mediaTypes: [
 | 
					      mediaTypes: [
 | 
				
			||||||
@ -89,36 +68,54 @@ export default {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  computed: {
 | 
					  computed: {
 | 
				
			||||||
    title() {
 | 
					 | 
				
			||||||
      if (this.showDirectoryPicker) return 'Choose a Folder'
 | 
					 | 
				
			||||||
      return ''
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    folderPaths() {
 | 
					    folderPaths() {
 | 
				
			||||||
      return this.folders.map((f) => f.fullPath)
 | 
					      return this.folders.map((f) => f.fullPath)
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    disableSubmit() {
 | 
					 | 
				
			||||||
      if (!this.library) {
 | 
					 | 
				
			||||||
        return false
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      var newfolderpaths = this.folderPaths.join(',')
 | 
					 | 
				
			||||||
      var origfolderpaths = this.library.folders.map((f) => f.fullPath).join(',')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return newfolderpaths === origfolderpaths && this.name === this.library.name && this.provider === this.library.provider && this.disableWatcher === this.library.disableWatcher && this.icon === this.library.icon && !this.newFolderPath
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    providers() {
 | 
					    providers() {
 | 
				
			||||||
      if (this.mediaType === 'podcast') return this.$store.state.scanners.podcastProviders
 | 
					      if (this.mediaType === 'podcast') return this.$store.state.scanners.podcastProviders
 | 
				
			||||||
      return this.$store.state.scanners.providers
 | 
					      return this.$store.state.scanners.providers
 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    globalWatcherDisabled() {
 | 
					 | 
				
			||||||
      return this.$store.getters['getServerSetting']('scannerDisableWatcher')
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
 | 
					    getLibraryData() {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        name: this.name,
 | 
				
			||||||
 | 
					        provider: this.provider,
 | 
				
			||||||
 | 
					        folders: this.folders,
 | 
				
			||||||
 | 
					        icon: this.icon,
 | 
				
			||||||
 | 
					        mediaType: this.mediaType
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    formUpdated() {
 | 
				
			||||||
 | 
					      this.$emit('update', this.getLibraryData())
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    newFolderInputBlurred() {
 | 
				
			||||||
 | 
					      if (this.newFolderPath) {
 | 
				
			||||||
 | 
					        this.folders.push({ fullPath: this.newFolderPath })
 | 
				
			||||||
 | 
					        this.newFolderPath = ''
 | 
				
			||||||
 | 
					        this.formUpdated()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    iconChanged() {
 | 
				
			||||||
 | 
					      this.formUpdated()
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    nameBlurred() {
 | 
				
			||||||
 | 
					      if (this.name !== this.library.name) {
 | 
				
			||||||
 | 
					        this.formUpdated()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    changedMediaType() {
 | 
					    changedMediaType() {
 | 
				
			||||||
      this.provider = this.providers[0].value
 | 
					      this.provider = this.providers[0].value
 | 
				
			||||||
 | 
					      this.formUpdated()
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    selectFolder(fullPath) {
 | 
				
			||||||
 | 
					      this.folders.push({ fullPath })
 | 
				
			||||||
 | 
					      this.showDirectoryPicker = false
 | 
				
			||||||
 | 
					      this.formUpdated()
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    removeFolder(folder) {
 | 
					    removeFolder(folder) {
 | 
				
			||||||
      this.folders = this.folders.filter((f) => f.fullPath !== folder.fullPath)
 | 
					      this.folders = this.folders.filter((f) => f.fullPath !== folder.fullPath)
 | 
				
			||||||
 | 
					      this.formUpdated()
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    backArrowPress() {
 | 
					    backArrowPress() {
 | 
				
			||||||
      if (this.showDirectoryPicker) {
 | 
					      if (this.showDirectoryPicker) {
 | 
				
			||||||
@ -129,95 +126,9 @@ export default {
 | 
				
			|||||||
      this.name = this.library ? this.library.name : ''
 | 
					      this.name = this.library ? this.library.name : ''
 | 
				
			||||||
      this.provider = this.library ? this.library.provider : 'google'
 | 
					      this.provider = this.library ? this.library.provider : 'google'
 | 
				
			||||||
      this.folders = this.library ? this.library.folders.map((p) => ({ ...p })) : []
 | 
					      this.folders = this.library ? this.library.folders.map((p) => ({ ...p })) : []
 | 
				
			||||||
      this.disableWatcher = this.library ? !!this.library.disableWatcher : false
 | 
					 | 
				
			||||||
      this.icon = this.library ? this.library.icon : 'default'
 | 
					      this.icon = this.library ? this.library.icon : 'default'
 | 
				
			||||||
      this.mediaType = this.library ? this.library.mediaType : 'book'
 | 
					      this.mediaType = this.library ? this.library.mediaType : 'book'
 | 
				
			||||||
      this.showDirectoryPicker = false
 | 
					      this.showDirectoryPicker = false
 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    selectFolder(fullPath) {
 | 
					 | 
				
			||||||
      this.folders.push({ fullPath })
 | 
					 | 
				
			||||||
      this.showDirectoryPicker = false
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    submit() {
 | 
					 | 
				
			||||||
      if (this.newFolderPath) {
 | 
					 | 
				
			||||||
        this.folders.push({ fullPath: this.newFolderPath })
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (this.library) {
 | 
					 | 
				
			||||||
        this.updateLibrary()
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        this.createLibrary()
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    updateLibrary() {
 | 
					 | 
				
			||||||
      if (!this.name) {
 | 
					 | 
				
			||||||
        this.$toast.error('Library must have a name')
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (!this.folders.length) {
 | 
					 | 
				
			||||||
        this.$toast.error('Library must have at least 1 path')
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      var newLibraryPayload = {
 | 
					 | 
				
			||||||
        name: this.name,
 | 
					 | 
				
			||||||
        provider: this.provider,
 | 
					 | 
				
			||||||
        folders: this.folders,
 | 
					 | 
				
			||||||
        icon: this.icon,
 | 
					 | 
				
			||||||
        disableWatcher: this.disableWatcher
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      this.$emit('update:processing', true)
 | 
					 | 
				
			||||||
      this.$axios
 | 
					 | 
				
			||||||
        .$patch(`/api/libraries/${this.library.id}`, newLibraryPayload)
 | 
					 | 
				
			||||||
        .then((res) => {
 | 
					 | 
				
			||||||
          this.$emit('update:processing', false)
 | 
					 | 
				
			||||||
          this.$emit('close')
 | 
					 | 
				
			||||||
          this.$toast.success(`Library "${res.name}" updated successfully`)
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .catch((error) => {
 | 
					 | 
				
			||||||
          console.error(error)
 | 
					 | 
				
			||||||
          if (error.response && error.response.data) {
 | 
					 | 
				
			||||||
            this.$toast.error(error.response.data)
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            this.$toast.error('Failed to update library')
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          this.$emit('update:processing', false)
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    createLibrary() {
 | 
					 | 
				
			||||||
      if (!this.name) {
 | 
					 | 
				
			||||||
        this.$toast.error('Library must have a name')
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (!this.folders.length) {
 | 
					 | 
				
			||||||
        this.$toast.error('Library must have at least 1 path')
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      var newLibraryPayload = {
 | 
					 | 
				
			||||||
        name: this.name,
 | 
					 | 
				
			||||||
        provider: this.provider,
 | 
					 | 
				
			||||||
        folders: this.folders,
 | 
					 | 
				
			||||||
        icon: this.icon,
 | 
					 | 
				
			||||||
        mediaType: this.mediaType,
 | 
					 | 
				
			||||||
        disableWatcher: this.disableWatcher
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      this.$emit('update:processing', true)
 | 
					 | 
				
			||||||
      this.$axios
 | 
					 | 
				
			||||||
        .$post('/api/libraries', newLibraryPayload)
 | 
					 | 
				
			||||||
        .then((res) => {
 | 
					 | 
				
			||||||
          this.$emit('update:processing', false)
 | 
					 | 
				
			||||||
          this.$emit('close')
 | 
					 | 
				
			||||||
          this.$toast.success(`Library "${res.name}" created successfully`)
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .catch((error) => {
 | 
					 | 
				
			||||||
          console.error(error)
 | 
					 | 
				
			||||||
          if (error.response && error.response.data) {
 | 
					 | 
				
			||||||
            this.$toast.error(error.response.data)
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            this.$toast.error('Failed to create library')
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          this.$emit('update:processing', false)
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  mounted() {
 | 
					  mounted() {
 | 
				
			||||||
 | 
				
			|||||||
@ -5,8 +5,20 @@
 | 
				
			|||||||
        <p class="font-book text-3xl text-white truncate">{{ title }}</p>
 | 
					        <p class="font-book text-3xl text-white truncate">{{ title }}</p>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </template>
 | 
					    </template>
 | 
				
			||||||
    <div class="p-4 w-full text-sm py-6 rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden" style="min-height: 400px; max-height: 80vh">
 | 
					    <div class="absolute -top-10 left-0 z-10 w-full flex">
 | 
				
			||||||
      <modals-libraries-edit-library v-if="show" :library="library" :processing.sync="processing" @close="show = false" />
 | 
					      <template v-for="tab in tabs">
 | 
				
			||||||
 | 
					        <div :key="tab.id" class="w-28 rounded-t-lg flex items-center justify-center mr-1 cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300 tab text-xs sm:text-base" :class="selectedTab === tab.id ? 'tab-selected bg-bg pb-px' : 'bg-primary text-gray-400'" @click="selectTab(tab.id)">{{ tab.title }}</div>
 | 
				
			||||||
 | 
					      </template>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="px-4 w-full text-sm pt-6 pb-20 rounded-b-lg rounded-tr-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden" style="min-height: 400px; max-height: 80vh">
 | 
				
			||||||
 | 
					      <component v-if="libraryCopy && show" :is="tabName" :is-new="!library" :library="libraryCopy" :processing.sync="processing" @update="updateLibrary" @close="show = false" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <div class="absolute bottom-0 left-0 w-full px-4 py-4 border-t border-opacity-10">
 | 
				
			||||||
 | 
					        <div class="flex justify-end">
 | 
				
			||||||
 | 
					          <ui-btn @click="submit">{{ buttonText }}</ui-btn>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </modals-modal>
 | 
					  </modals-modal>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
@ -22,7 +34,21 @@ export default {
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  data() {
 | 
					  data() {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      processing: false
 | 
					      processing: false,
 | 
				
			||||||
 | 
					      selectedTab: 'details',
 | 
				
			||||||
 | 
					      tabs: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 'details',
 | 
				
			||||||
 | 
					          title: 'Details',
 | 
				
			||||||
 | 
					          component: 'modals-libraries-edit-library'
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: 'settings',
 | 
				
			||||||
 | 
					          title: 'Settings',
 | 
				
			||||||
 | 
					          component: 'modals-libraries-library-settings'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      libraryCopy: null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  computed: {
 | 
					  computed: {
 | 
				
			||||||
@ -36,10 +62,157 @@ export default {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    title() {
 | 
					    title() {
 | 
				
			||||||
      return this.library ? 'Update Library' : 'New Library'
 | 
					      return this.library ? 'Update Library' : 'New Library'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    buttonText() {
 | 
				
			||||||
 | 
					      return this.library ? 'Update Library' : 'Create New Library'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    tabName() {
 | 
				
			||||||
 | 
					      var _tab = this.tabs.find((t) => t.id === this.selectedTab)
 | 
				
			||||||
 | 
					      return _tab ? _tab.component : ''
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  watch: {
 | 
				
			||||||
 | 
					    show: {
 | 
				
			||||||
 | 
					      handler(newVal) {
 | 
				
			||||||
 | 
					        if (newVal) this.init()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    selectTab(tab) {
 | 
				
			||||||
 | 
					      this.selectedTab = tab
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    updateLibrary(library) {
 | 
				
			||||||
 | 
					      this.mapLibraryToCopy(library)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    getNewLibraryData() {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        name: '',
 | 
				
			||||||
 | 
					        provider: 'google',
 | 
				
			||||||
 | 
					        folders: [],
 | 
				
			||||||
 | 
					        icon: 'database',
 | 
				
			||||||
 | 
					        mediaType: 'book',
 | 
				
			||||||
 | 
					        settings: {
 | 
				
			||||||
 | 
					          disableWatcher: false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    init() {
 | 
				
			||||||
 | 
					      this.selectedTab = 'details'
 | 
				
			||||||
 | 
					      this.libraryCopy = this.getNewLibraryData()
 | 
				
			||||||
 | 
					      if (this.library) {
 | 
				
			||||||
 | 
					        this.mapLibraryToCopy(this.library)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    mapLibraryToCopy(library) {
 | 
				
			||||||
 | 
					      for (const key in this.libraryCopy) {
 | 
				
			||||||
 | 
					        if (library[key] !== undefined) {
 | 
				
			||||||
 | 
					          if (key === 'folders') {
 | 
				
			||||||
 | 
					            this.libraryCopy.folders = library.folders.map((f) => ({ ...f }))
 | 
				
			||||||
 | 
					          } else if (key === 'settings') {
 | 
				
			||||||
 | 
					            this.libraryCopy.settings = { ...library.settings }
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            this.libraryCopy[key] = library[key]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    validate() {
 | 
				
			||||||
 | 
					      if (!this.libraryCopy.name) {
 | 
				
			||||||
 | 
					        this.$toast.error('Library must have a name')
 | 
				
			||||||
 | 
					        return false
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (!this.libraryCopy.folders.length) {
 | 
				
			||||||
 | 
					        this.$toast.error('Library must have at least 1 path')
 | 
				
			||||||
 | 
					        return false
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    submit() {
 | 
				
			||||||
 | 
					      if (!this.validate()) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (this.library) {
 | 
				
			||||||
 | 
					        this.submitUpdateLibrary()
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.submitCreateLibrary()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    getLibraryUpdatePayload() {
 | 
				
			||||||
 | 
					      var updatePayload = {}
 | 
				
			||||||
 | 
					      for (const key in this.libraryCopy) {
 | 
				
			||||||
 | 
					        if (key === 'folders') {
 | 
				
			||||||
 | 
					          if (this.libraryCopy.folders.join(',') !== this.library.folders.join(',')) {
 | 
				
			||||||
 | 
					            updatePayload.folders = [...this.libraryCopy.folders]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else if (key === 'settings') {
 | 
				
			||||||
 | 
					          for (const settingsKey in this.libraryCopy.settings) {
 | 
				
			||||||
 | 
					            if (this.libraryCopy.settings[settingsKey] !== this.library.settings[settingsKey]) {
 | 
				
			||||||
 | 
					              if (!updatePayload.settings) updatePayload.settings = {}
 | 
				
			||||||
 | 
					              updatePayload.settings[settingsKey] = this.libraryCopy.settings[settingsKey]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else if (key !== 'mediaType' && this.libraryCopy[key] !== this.library[key]) {
 | 
				
			||||||
 | 
					          updatePayload[key] = this.libraryCopy[key]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return updatePayload
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    submitUpdateLibrary() {
 | 
				
			||||||
 | 
					      var newLibraryPayload = this.getLibraryUpdatePayload()
 | 
				
			||||||
 | 
					      if (!Object.keys(newLibraryPayload).length) {
 | 
				
			||||||
 | 
					        this.$toast.info('No updates are necessary')
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.processing = true
 | 
				
			||||||
 | 
					      this.$axios
 | 
				
			||||||
 | 
					        .$patch(`/api/libraries/${this.library.id}`, newLibraryPayload)
 | 
				
			||||||
 | 
					        .then((res) => {
 | 
				
			||||||
 | 
					          this.processing = false
 | 
				
			||||||
 | 
					          this.show = false
 | 
				
			||||||
 | 
					          this.$toast.success(`Library "${res.name}" updated successfully`)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch((error) => {
 | 
				
			||||||
 | 
					          console.error(error)
 | 
				
			||||||
 | 
					          if (error.response && error.response.data) {
 | 
				
			||||||
 | 
					            this.$toast.error(error.response.data)
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            this.$toast.error('Failed to update library')
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          this.processing = false
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    submitCreateLibrary() {
 | 
				
			||||||
 | 
					      this.processing = true
 | 
				
			||||||
 | 
					      this.$axios
 | 
				
			||||||
 | 
					        .$post('/api/libraries', this.libraryCopy)
 | 
				
			||||||
 | 
					        .then((res) => {
 | 
				
			||||||
 | 
					          this.processing = false
 | 
				
			||||||
 | 
					          this.show = false
 | 
				
			||||||
 | 
					          this.$toast.success(`Library "${res.name}" created successfully`)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch((error) => {
 | 
				
			||||||
 | 
					          console.error(error)
 | 
				
			||||||
 | 
					          if (error.response && error.response.data) {
 | 
				
			||||||
 | 
					            this.$toast.error(error.response.data)
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            this.$toast.error('Failed to create library')
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          this.processing = false
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {},
 | 
					 | 
				
			||||||
  mounted() {},
 | 
					  mounted() {},
 | 
				
			||||||
  beforeDestroy() {}
 | 
					  beforeDestroy() {}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
					.tab {
 | 
				
			||||||
 | 
					  height: 40px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.tab.tab-selected {
 | 
				
			||||||
 | 
					  height: 41px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
@ -1,10 +1,14 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="w-full h-full">
 | 
					  <div class="w-full h-full bg-bg absolute top-0 left-0 px-4 py-4 z-10">
 | 
				
			||||||
 | 
					    <div class="flex items-center py-1 mb-2">
 | 
				
			||||||
 | 
					      <span class="material-icons text-3xl cursor-pointer hover:text-gray-300" @click="$emit('back')">arrow_back</span>
 | 
				
			||||||
 | 
					      <p class="px-4 text-xl">Choose a Folder</p>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
    <div v-if="allFolders.length" class="w-full bg-primary bg-opacity-70 py-1 px-4 mb-2">
 | 
					    <div v-if="allFolders.length" class="w-full bg-primary bg-opacity-70 py-1 px-4 mb-2">
 | 
				
			||||||
      <p class="font-mono truncate">{{ selectedPath || '\\' }}</p>
 | 
					      <p class="font-mono truncate">{{ selectedPath || '\\' }}</p>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div v-if="allFolders.length" class="flex bg-primary bg-opacity-50 p-4">
 | 
					    <div v-if="allFolders.length" class="flex bg-primary bg-opacity-50 p-4 folder-container">
 | 
				
			||||||
      <div class="w-1/2 border-r border-bg">
 | 
					      <div class="w-1/2 border-r border-bg h-full overflow-y-auto">
 | 
				
			||||||
        <div v-if="level > 0" class="w-full p-1 cursor-pointer flex items-center" @click="goBack">
 | 
					        <div v-if="level > 0" class="w-full p-1 cursor-pointer flex items-center" @click="goBack">
 | 
				
			||||||
          <span class="material-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
 | 
					          <span class="material-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
 | 
				
			||||||
          <p class="text-base font-mono px-2">..</p>
 | 
					          <p class="text-base font-mono px-2">..</p>
 | 
				
			||||||
@ -15,7 +19,7 @@
 | 
				
			|||||||
          <span v-if="dir.dirs && dir.dirs.length && dir.path === selectedPath" class="material-icons" style="font-size: 1.1rem">arrow_right</span>
 | 
					          <span v-if="dir.dirs && dir.dirs.length && dir.path === selectedPath" class="material-icons" style="font-size: 1.1rem">arrow_right</span>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="w-1/2">
 | 
					      <div class="w-1/2 h-full overflow-y-auto">
 | 
				
			||||||
        <div v-for="dir in _subdirs" :key="dir.path" :class="dir.className" class="dir-item w-full p-1 cursor-pointer flex items-center hover:text-white text-gray-200" @click="selectSubDir(dir)">
 | 
					        <div v-for="dir in _subdirs" :key="dir.path" :class="dir.className" class="dir-item w-full p-1 cursor-pointer flex items-center hover:text-white text-gray-200" @click="selectSubDir(dir)">
 | 
				
			||||||
          <span class="material-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
 | 
					          <span class="material-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
 | 
				
			||||||
          <p class="text-base font-mono px-2 truncate">{{ dir.dirname }}</p>
 | 
					          <p class="text-base font-mono px-2 truncate">{{ dir.dirname }}</p>
 | 
				
			||||||
@ -30,12 +34,8 @@
 | 
				
			|||||||
      <p class="text-gray-300">Note: folders already mapped will not be shown</p>
 | 
					      <p class="text-gray-300">Note: folders already mapped will not be shown</p>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div class="absolute bottom-0 left-0 w-full py-4 px-8">
 | 
					    <div class="w-full py-2">
 | 
				
			||||||
      <ui-btn :disabled="!selectedPath" color="primary" class="w-full mt-2" @click="selectFolder">Select Folder Path</ui-btn>
 | 
					      <ui-btn :disabled="!selectedPath" color="primary" class="w-full mt-2" @click="selectFolder">Select Folder Path</ui-btn>
 | 
				
			||||||
      <!-- <div class="flex items-center">
 | 
					 | 
				
			||||||
        <div class="flex-grow" />
 | 
					 | 
				
			||||||
        <ui-btn color="success" @click="selectFolder">Select</ui-btn>
 | 
					 | 
				
			||||||
      </div> -->
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
@ -161,4 +161,9 @@ export default {
 | 
				
			|||||||
.dir-item.dir-used {
 | 
					.dir-item.dir-used {
 | 
				
			||||||
  background-color: rgba(255, 25, 0, 0.1);
 | 
					  background-color: rgba(255, 25, 0, 0.1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.folder-container {
 | 
				
			||||||
 | 
					  max-height: calc(100% - 130px);
 | 
				
			||||||
 | 
					  height: calc(100% - 130px);
 | 
				
			||||||
 | 
					  min-height: calc(100% - 130px);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										63
									
								
								client/components/modals/libraries/LibrarySettings.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								client/components/modals/libraries/LibrarySettings.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="w-full h-full px-4 py-1 mb-4">
 | 
				
			||||||
 | 
					    <div class="py-3">
 | 
				
			||||||
 | 
					      <div class="flex items-center">
 | 
				
			||||||
 | 
					        <ui-toggle-switch v-if="!globalWatcherDisabled" v-model="disableWatcher" @input="formUpdated" />
 | 
				
			||||||
 | 
					        <ui-toggle-switch v-else disabled :value="false" />
 | 
				
			||||||
 | 
					        <p class="pl-4 text-lg">Disable folder watcher for library</p>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <p v-if="globalWatcherDisabled" class="text-xs text-warning">*Watcher is disabled globally in server settings</p>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    library: {
 | 
				
			||||||
 | 
					      type: Object,
 | 
				
			||||||
 | 
					      default: () => null
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    processing: Boolean
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      provider: null,
 | 
				
			||||||
 | 
					      disableWatcher: false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    librarySettings() {
 | 
				
			||||||
 | 
					      return this.library.settings || {}
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    globalWatcherDisabled() {
 | 
				
			||||||
 | 
					      return this.$store.getters['getServerSetting']('scannerDisableWatcher')
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    mediaType() {
 | 
				
			||||||
 | 
					      return this.library.mediaType
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    providers() {
 | 
				
			||||||
 | 
					      if (this.mediaType === 'podcast') return this.$store.state.scanners.podcastProviders
 | 
				
			||||||
 | 
					      return this.$store.state.scanners.providers
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    getLibraryData() {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        settings: {
 | 
				
			||||||
 | 
					          disableWatcher: !!this.disableWatcher
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    formUpdated() {
 | 
				
			||||||
 | 
					      this.$emit('update', this.getLibraryData())
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    init() {
 | 
				
			||||||
 | 
					      this.disableWatcher = !!this.librarySettings.disableWatcher
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  mounted() {
 | 
				
			||||||
 | 
					    this.init()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
@ -40,8 +40,8 @@ export default {
 | 
				
			|||||||
      showMenu: false,
 | 
					      showMenu: false,
 | 
				
			||||||
      types: [
 | 
					      types: [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          id: 'default',
 | 
					          id: 'database',
 | 
				
			||||||
          name: 'Default'
 | 
					          name: 'Database'
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          id: 'audiobook',
 | 
					          id: 'audiobook',
 | 
				
			||||||
@ -65,7 +65,7 @@ export default {
 | 
				
			|||||||
  computed: {
 | 
					  computed: {
 | 
				
			||||||
    selected: {
 | 
					    selected: {
 | 
				
			||||||
      get() {
 | 
					      get() {
 | 
				
			||||||
        return this.value || 'default'
 | 
					        return this.value || 'database'
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set(val) {
 | 
					      set(val) {
 | 
				
			||||||
        this.$emit('input', val)
 | 
					        this.$emit('input', val)
 | 
				
			||||||
@ -75,7 +75,7 @@ export default {
 | 
				
			|||||||
      return this.types.find((t) => t.id === this.selected)
 | 
					      return this.types.find((t) => t.id === this.selected)
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    selectedName() {
 | 
					    selectedName() {
 | 
				
			||||||
      return this.selectedItem ? this.selectedItem.name : 'Default'
 | 
					      return this.selectedItem ? this.selectedItem.name : 'Database'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
    <p class="px-1 text-sm font-semibold" :class="disabled ? 'text-gray-400' : ''">
 | 
					    <p class="px-1 text-sm font-semibold" :class="disabled ? 'text-gray-400' : ''">
 | 
				
			||||||
      {{ label }}<em v-if="note" class="font-normal text-xs pl-2">{{ note }}</em>
 | 
					      {{ label }}<em v-if="note" class="font-normal text-xs pl-2">{{ note }}</em>
 | 
				
			||||||
    </p>
 | 
					    </p>
 | 
				
			||||||
    <ui-text-input ref="input" v-model="inputValue" :disabled="disabled" :readonly="readonly" :type="type" class="w-full" />
 | 
					    <ui-text-input ref="input" v-model="inputValue" :disabled="disabled" :readonly="readonly" :type="type" class="w-full" @blur="inputBlurred" />
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -38,6 +38,9 @@ export default {
 | 
				
			|||||||
      if (this.$refs.input && this.$refs.input.blur) {
 | 
					      if (this.$refs.input && this.$refs.input.blur) {
 | 
				
			||||||
        this.$refs.input.blur()
 | 
					        this.$refs.input.blur()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    inputBlurred() {
 | 
				
			||||||
 | 
					      this.$emit('blur')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  mounted() {}
 | 
					  mounted() {}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ const UserCollection = require('./objects/UserCollection')
 | 
				
			|||||||
const Library = require('./objects/Library')
 | 
					const Library = require('./objects/Library')
 | 
				
			||||||
const Author = require('./objects/entities/Author')
 | 
					const Author = require('./objects/entities/Author')
 | 
				
			||||||
const Series = require('./objects/entities/Series')
 | 
					const Series = require('./objects/entities/Series')
 | 
				
			||||||
const ServerSettings = require('./objects/ServerSettings')
 | 
					const ServerSettings = require('./objects/settings/ServerSettings')
 | 
				
			||||||
const PlaybackSession = require('./objects/PlaybackSession')
 | 
					const PlaybackSession = require('./objects/PlaybackSession')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Db {
 | 
					class Db {
 | 
				
			||||||
 | 
				
			|||||||
@ -69,19 +69,19 @@ class FolderWatcher extends EventEmitter {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  initWatcher(libraries) {
 | 
					  initWatcher(libraries) {
 | 
				
			||||||
    libraries.forEach((lib) => {
 | 
					    libraries.forEach((lib) => {
 | 
				
			||||||
      if (!lib.disableWatcher) {
 | 
					      if (!lib.settings.disableWatcher) {
 | 
				
			||||||
        this.buildLibraryWatcher(lib)
 | 
					        this.buildLibraryWatcher(lib)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  addLibrary(library) {
 | 
					  addLibrary(library) {
 | 
				
			||||||
    if (this.disabled || library.disableWatcher) return
 | 
					    if (this.disabled || library.settings.disableWatcher) return
 | 
				
			||||||
    this.buildLibraryWatcher(library)
 | 
					    this.buildLibraryWatcher(library)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  updateLibrary(library) {
 | 
					  updateLibrary(library) {
 | 
				
			||||||
    if (this.disabled || library.disableWatcher) return
 | 
					    if (this.disabled || library.settings.disableWatcher) return
 | 
				
			||||||
    var libwatcher = this.libraryWatchers.find(lib => lib.id === library.id)
 | 
					    var libwatcher = this.libraryWatchers.find(lib => lib.id === library.id)
 | 
				
			||||||
    if (libwatcher) {
 | 
					    if (libwatcher) {
 | 
				
			||||||
      libwatcher.name = library.name
 | 
					      libwatcher.name = library.name
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
const Folder = require('./Folder')
 | 
					const Folder = require('./Folder')
 | 
				
			||||||
 | 
					const LibrarySettings = require('./settings/LibrarySettings')
 | 
				
			||||||
const { getId } = require('../utils/index')
 | 
					const { getId } = require('../utils/index')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Library {
 | 
					class Library {
 | 
				
			||||||
@ -10,9 +11,9 @@ class Library {
 | 
				
			|||||||
    this.icon = 'database' // database, podcast, book, audiobook, comic
 | 
					    this.icon = 'database' // database, podcast, book, audiobook, comic
 | 
				
			||||||
    this.mediaType = 'book' // book, podcast
 | 
					    this.mediaType = 'book' // book, podcast
 | 
				
			||||||
    this.provider = 'google'
 | 
					    this.provider = 'google'
 | 
				
			||||||
    this.disableWatcher = false
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.lastScan = 0
 | 
					    this.lastScan = 0
 | 
				
			||||||
 | 
					    this.settings = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.createdAt = null
 | 
					    this.createdAt = null
 | 
				
			||||||
    this.lastUpdate = null
 | 
					    this.lastUpdate = null
 | 
				
			||||||
@ -34,7 +35,11 @@ class Library {
 | 
				
			|||||||
    this.icon = library.icon || 'database'
 | 
					    this.icon = library.icon || 'database'
 | 
				
			||||||
    this.mediaType = library.mediaType
 | 
					    this.mediaType = library.mediaType
 | 
				
			||||||
    this.provider = library.provider || 'google'
 | 
					    this.provider = library.provider || 'google'
 | 
				
			||||||
    this.disableWatcher = !!library.disableWatcher
 | 
					
 | 
				
			||||||
 | 
					    this.settings = new LibrarySettings(library.settings)
 | 
				
			||||||
 | 
					    if (library.settings === undefined) { // LibrarySettings added in v2, migrate settings
 | 
				
			||||||
 | 
					      this.settings.disableWatcher = !!library.disableWatcher
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.createdAt = library.createdAt
 | 
					    this.createdAt = library.createdAt
 | 
				
			||||||
    this.lastUpdate = library.lastUpdate
 | 
					    this.lastUpdate = library.lastUpdate
 | 
				
			||||||
@ -62,7 +67,7 @@ class Library {
 | 
				
			|||||||
      icon: this.icon,
 | 
					      icon: this.icon,
 | 
				
			||||||
      mediaType: this.mediaType,
 | 
					      mediaType: this.mediaType,
 | 
				
			||||||
      provider: this.provider,
 | 
					      provider: this.provider,
 | 
				
			||||||
      disableWatcher: this.disableWatcher,
 | 
					      settings: this.settings.toJSON(),
 | 
				
			||||||
      createdAt: this.createdAt,
 | 
					      createdAt: this.createdAt,
 | 
				
			||||||
      lastUpdate: this.lastUpdate
 | 
					      lastUpdate: this.lastUpdate
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -89,7 +94,7 @@ class Library {
 | 
				
			|||||||
    this.icon = data.icon || 'database'
 | 
					    this.icon = data.icon || 'database'
 | 
				
			||||||
    this.mediaType = data.mediaType || 'book'
 | 
					    this.mediaType = data.mediaType || 'book'
 | 
				
			||||||
    this.provider = data.provider || 'google'
 | 
					    this.provider = data.provider || 'google'
 | 
				
			||||||
    this.disableWatcher = !!data.disableWatcher
 | 
					    this.settings = new LibrarySettings(data.settings)
 | 
				
			||||||
    this.createdAt = Date.now()
 | 
					    this.createdAt = Date.now()
 | 
				
			||||||
    this.lastUpdate = Date.now()
 | 
					    this.lastUpdate = Date.now()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -105,10 +110,10 @@ class Library {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (payload.disableWatcher !== this.disableWatcher) {
 | 
					    if (payload.settings && this.settings.update(payload.settings)) {
 | 
				
			||||||
      this.disableWatcher = !!payload.disableWatcher
 | 
					 | 
				
			||||||
      hasUpdates = true
 | 
					      hasUpdates = true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!isNaN(payload.displayOrder) && payload.displayOrder !== this.displayOrder) {
 | 
					    if (!isNaN(payload.displayOrder) && payload.displayOrder !== this.displayOrder) {
 | 
				
			||||||
      this.displayOrder = Number(payload.displayOrder)
 | 
					      this.displayOrder = Number(payload.displayOrder)
 | 
				
			||||||
      hasUpdates = true
 | 
					      hasUpdates = true
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										34
									
								
								server/objects/settings/LibrarySettings.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								server/objects/settings/LibrarySettings.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					const { BookCoverAspectRatio } = require('../../utils/constants')
 | 
				
			||||||
 | 
					const Logger = require('../../Logger')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LibrarySettings {
 | 
				
			||||||
 | 
					  constructor(settings) {
 | 
				
			||||||
 | 
					    this.disableWatcher = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (settings) {
 | 
				
			||||||
 | 
					      this.construct(settings)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  construct(settings) {
 | 
				
			||||||
 | 
					    this.disableWatcher = !!settings.disableWatcher
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  toJSON() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      disableWatcher: this.disableWatcher
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  update(payload) {
 | 
				
			||||||
 | 
					    var hasUpdates = false
 | 
				
			||||||
 | 
					    for (const key in payload) {
 | 
				
			||||||
 | 
					      if (this[key] !== payload[key]) {
 | 
				
			||||||
 | 
					        this[key] = payload[key]
 | 
				
			||||||
 | 
					        hasUpdates = true
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return hasUpdates
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					module.exports = LibrarySettings
 | 
				
			||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
const { BookCoverAspectRatio, BookshelfView } = require('../utils/constants')
 | 
					const { BookCoverAspectRatio, BookshelfView } = require('../../utils/constants')
 | 
				
			||||||
const Logger = require('../Logger')
 | 
					const Logger = require('../../Logger')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ServerSettings {
 | 
					class ServerSettings {
 | 
				
			||||||
  constructor(settings) {
 | 
					  constructor(settings) {
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user