mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-11-03 19:07:00 -05:00 
			
		
		
		
	Add:Ereader device setting to set users that have access #1982
This commit is contained in:
		
							parent
							
								
									94fd3841aa
								
							
						
					
					
						commit
						27497451d9
					
				@ -8,7 +8,7 @@
 | 
				
			|||||||
    <form @submit.prevent="submitForm">
 | 
					    <form @submit.prevent="submitForm">
 | 
				
			||||||
      <div class="w-full text-sm rounded-lg bg-bg shadow-lg border border-black-300">
 | 
					      <div class="w-full text-sm rounded-lg bg-bg shadow-lg border border-black-300">
 | 
				
			||||||
        <div class="w-full px-3 py-5 md:p-12">
 | 
					        <div class="w-full px-3 py-5 md:p-12">
 | 
				
			||||||
          <div class="flex items-center -mx-1 mb-2">
 | 
					          <div class="flex items-center -mx-1 mb-4">
 | 
				
			||||||
            <div class="w-full md:w-1/2 px-1">
 | 
					            <div class="w-full md:w-1/2 px-1">
 | 
				
			||||||
              <ui-text-input-with-label ref="ereaderNameInput" v-model="newDevice.name" :disabled="processing" :label="$strings.LabelName" />
 | 
					              <ui-text-input-with-label ref="ereaderNameInput" v-model="newDevice.name" :disabled="processing" :label="$strings.LabelName" />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
@ -16,6 +16,14 @@
 | 
				
			|||||||
              <ui-text-input-with-label ref="ereaderEmailInput" v-model="newDevice.email" :disabled="processing" :label="$strings.LabelEmail" />
 | 
					              <ui-text-input-with-label ref="ereaderEmailInput" v-model="newDevice.email" :disabled="processing" :label="$strings.LabelEmail" />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="flex items-center -mx-1 mb-4">
 | 
				
			||||||
 | 
					            <div class="w-full md:w-1/2 px-1">
 | 
				
			||||||
 | 
					              <ui-dropdown v-model="newDevice.availabilityOption" :label="$strings.LabelDeviceIsAvailableTo" :items="userAvailabilityOptions" @input="availabilityOptionChanged" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="w-full md:w-1/2 px-1">
 | 
				
			||||||
 | 
					              <ui-multi-select-dropdown v-if="newDevice.availabilityOption === 'specificUsers'" v-model="newDevice.users" :label="$strings.HeaderUsers" :items="userOptions" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <div class="flex items-center pt-4">
 | 
					          <div class="flex items-center pt-4">
 | 
				
			||||||
            <div class="flex-grow" />
 | 
					            <div class="flex-grow" />
 | 
				
			||||||
@ -45,8 +53,11 @@ export default {
 | 
				
			|||||||
      processing: false,
 | 
					      processing: false,
 | 
				
			||||||
      newDevice: {
 | 
					      newDevice: {
 | 
				
			||||||
        name: '',
 | 
					        name: '',
 | 
				
			||||||
        email: ''
 | 
					        email: '',
 | 
				
			||||||
      }
 | 
					        availabilityOption: 'adminAndUp',
 | 
				
			||||||
 | 
					        users: []
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      users: []
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  watch: {
 | 
					  watch: {
 | 
				
			||||||
@ -68,10 +79,55 @@ export default {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    title() {
 | 
					    title() {
 | 
				
			||||||
      return this.ereaderDevice ? 'Create Device' : 'Update Device'
 | 
					      return !this.ereaderDevice ? 'Create Device' : 'Update Device'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    userAvailabilityOptions() {
 | 
				
			||||||
 | 
					      return [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          text: this.$strings.LabelAdminUsersOnly,
 | 
				
			||||||
 | 
					          value: 'adminOrUp'
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          text: this.$strings.LabelAllUsersExcludingGuests,
 | 
				
			||||||
 | 
					          value: 'userOrUp'
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          text: this.$strings.LabelAllUsersIncludingGuests,
 | 
				
			||||||
 | 
					          value: 'guestOrUp'
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          text: this.$strings.LabelSelectUsers,
 | 
				
			||||||
 | 
					          value: 'specificUsers'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    userOptions() {
 | 
				
			||||||
 | 
					      return this.users.map((u) => ({ text: u.username, value: u.id }))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
 | 
					    availabilityOptionChanged(option) {
 | 
				
			||||||
 | 
					      if (option === 'specificUsers' && !this.users.length) {
 | 
				
			||||||
 | 
					        this.loadUsers()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async loadUsers() {
 | 
				
			||||||
 | 
					      this.processing = true
 | 
				
			||||||
 | 
					      this.users = await this.$axios
 | 
				
			||||||
 | 
					        .$get('/api/users')
 | 
				
			||||||
 | 
					        .then((res) => {
 | 
				
			||||||
 | 
					          return res.users.sort((a, b) => {
 | 
				
			||||||
 | 
					            return a.createdAt - b.createdAt
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch((error) => {
 | 
				
			||||||
 | 
					          console.error('Failed', error)
 | 
				
			||||||
 | 
					          return []
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .finally(() => {
 | 
				
			||||||
 | 
					          this.processing = false
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    submitForm() {
 | 
					    submitForm() {
 | 
				
			||||||
      this.$refs.ereaderNameInput.blur()
 | 
					      this.$refs.ereaderNameInput.blur()
 | 
				
			||||||
      this.$refs.ereaderEmailInput.blur()
 | 
					      this.$refs.ereaderEmailInput.blur()
 | 
				
			||||||
@ -81,19 +137,27 @@ export default {
 | 
				
			|||||||
        return
 | 
					        return
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (this.newDevice.availabilityOption === 'specificUsers' && !this.newDevice.users.length) {
 | 
				
			||||||
 | 
					        this.$toast.error('Must select at least one user')
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (this.newDevice.availabilityOption !== 'specificUsers') {
 | 
				
			||||||
 | 
					        this.newDevice.users = []
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.newDevice.name = this.newDevice.name.trim()
 | 
					      this.newDevice.name = this.newDevice.name.trim()
 | 
				
			||||||
      this.newDevice.email = this.newDevice.email.trim()
 | 
					      this.newDevice.email = this.newDevice.email.trim()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (!this.ereaderDevice) {
 | 
					      if (!this.ereaderDevice) {
 | 
				
			||||||
        if (this.existingDevices.some((d) => d.name === this.newDevice.name)) {
 | 
					        if (this.existingDevices.some((d) => d.name === this.newDevice.name)) {
 | 
				
			||||||
          this.$toast.error('EReader device with that name already exists')
 | 
					          this.$toast.error('Ereader device with that name already exists')
 | 
				
			||||||
          return
 | 
					          return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.submitCreate()
 | 
					        this.submitCreate()
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        if (this.ereaderDevice.name !== this.newDevice.name && this.existingDevices.some((d) => d.name === this.newDevice.name)) {
 | 
					        if (this.ereaderDevice.name !== this.newDevice.name && this.existingDevices.some((d) => d.name === this.newDevice.name)) {
 | 
				
			||||||
          this.$toast.error('EReader device with that name already exists')
 | 
					          this.$toast.error('Ereader device with that name already exists')
 | 
				
			||||||
          return
 | 
					          return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -160,9 +224,17 @@ export default {
 | 
				
			|||||||
      if (this.ereaderDevice) {
 | 
					      if (this.ereaderDevice) {
 | 
				
			||||||
        this.newDevice.name = this.ereaderDevice.name
 | 
					        this.newDevice.name = this.ereaderDevice.name
 | 
				
			||||||
        this.newDevice.email = this.ereaderDevice.email
 | 
					        this.newDevice.email = this.ereaderDevice.email
 | 
				
			||||||
 | 
					        this.newDevice.availabilityOption = this.ereaderDevice.availabilityOption || 'adminOrUp'
 | 
				
			||||||
 | 
					        this.newDevice.users = this.ereaderDevice.users || []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.newDevice.availabilityOption === 'specificUsers' && !this.users.length) {
 | 
				
			||||||
 | 
					          this.loadUsers()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        this.newDevice.name = ''
 | 
					        this.newDevice.name = ''
 | 
				
			||||||
        this.newDevice.email = ''
 | 
					        this.newDevice.email = ''
 | 
				
			||||||
 | 
					        this.newDevice.availabilityOption = 'adminOrUp'
 | 
				
			||||||
 | 
					        this.newDevice.users = []
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@
 | 
				
			|||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <transition name="menu">
 | 
					    <transition name="menu">
 | 
				
			||||||
      <ul v-show="showMenu" class="absolute z-10 -mt-px w-full bg-primary border border-black-200 shadow-lg max-h-56 rounded-b-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto sm:text-sm" tabindex="-1" role="listbox">
 | 
					      <ul v-show="showMenu" class="absolute z-10 -mt-px w-full bg-primary border border-black-200 shadow-lg max-h-56 rounded-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto sm:text-sm" tabindex="-1" role="listbox">
 | 
				
			||||||
        <template v-for="item in itemsToShow">
 | 
					        <template v-for="item in itemsToShow">
 | 
				
			||||||
          <li :key="item.value" class="text-gray-100 relative py-2 cursor-pointer hover:bg-black-400" :id="'listbox-option-' + item.value" role="option" tabindex="0" @keyup.enter="clickedOption(item.value)" @click="clickedOption(item.value)">
 | 
					          <li :key="item.value" class="text-gray-100 relative py-2 cursor-pointer hover:bg-black-400" :id="'listbox-option-' + item.value" role="option" tabindex="0" @keyup.enter="clickedOption(item.value)" @click="clickedOption(item.value)">
 | 
				
			||||||
            <div class="flex items-center">
 | 
					            <div class="flex items-center">
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@
 | 
				
			|||||||
    <div ref="wrapper" class="relative">
 | 
					    <div ref="wrapper" class="relative">
 | 
				
			||||||
      <form @submit.prevent="submitForm">
 | 
					      <form @submit.prevent="submitForm">
 | 
				
			||||||
        <div ref="inputWrapper" class="input-wrapper flex-wrap relative w-full shadow-sm flex items-center border border-gray-600 rounded px-2 py-2" :class="disabled ? 'pointer-events-none bg-black-300 text-gray-400' : 'bg-primary'">
 | 
					        <div ref="inputWrapper" class="input-wrapper flex-wrap relative w-full shadow-sm flex items-center border border-gray-600 rounded px-2 py-2" :class="disabled ? 'pointer-events-none bg-black-300 text-gray-400' : 'bg-primary'">
 | 
				
			||||||
          <input ref="input" v-model="textInput" :disabled="disabled" :readonly="!editable" class="h-full w-full bg-transparent focus:outline-none px-1" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" />
 | 
					          <input ref="input" v-model="textInput" :disabled="disabled" :readonly="!editable" class="h-full w-full bg-transparent focus:outline-none px-1" @focus="inputFocus" @blur="inputBlur" />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </form>
 | 
					      </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -48,8 +48,6 @@ export default {
 | 
				
			|||||||
  data() {
 | 
					  data() {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      isFocused: false,
 | 
					      isFocused: false,
 | 
				
			||||||
      // currentSearch: null,
 | 
					 | 
				
			||||||
      typingTimeout: null,
 | 
					 | 
				
			||||||
      textInput: null
 | 
					      textInput: null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@ -83,12 +81,6 @@ export default {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    keydownInput() {
 | 
					 | 
				
			||||||
      clearTimeout(this.typingTimeout)
 | 
					 | 
				
			||||||
      this.typingTimeout = setTimeout(() => {
 | 
					 | 
				
			||||||
        // this.currentSearch = this.textInput
 | 
					 | 
				
			||||||
      }, 100)
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    setFocus() {
 | 
					    setFocus() {
 | 
				
			||||||
      if (this.$refs.input && this.editable) this.$refs.input.focus()
 | 
					      if (this.$refs.input && this.editable) this.$refs.input.focus()
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -133,11 +125,9 @@ export default {
 | 
				
			|||||||
      if (val && !this.items.includes(val)) {
 | 
					      if (val && !this.items.includes(val)) {
 | 
				
			||||||
        this.$emit('newItem', val)
 | 
					        this.$emit('newItem', val)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      // this.currentSearch = null
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    clickedOption(e, item) {
 | 
					    clickedOption(e, item) {
 | 
				
			||||||
      this.textInput = null
 | 
					      this.textInput = null
 | 
				
			||||||
      // this.currentSearch = null
 | 
					 | 
				
			||||||
      this.input = item
 | 
					      this.input = item
 | 
				
			||||||
      if (this.$refs.input) this.$refs.input.blur()
 | 
					      if (this.$refs.input) this.$refs.input.blur()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="w-full" v-click-outside="closeMenu">
 | 
					  <div class="w-full" v-click-outside="clickOutsideObj">
 | 
				
			||||||
    <p class="px-1 text-sm font-semibold">{{ label }}</p>
 | 
					    <p class="px-1 text-sm font-semibold">{{ label }}</p>
 | 
				
			||||||
    <div ref="wrapper" class="relative">
 | 
					    <div ref="wrapper" class="relative">
 | 
				
			||||||
      <div ref="inputWrapper" style="min-height: 40px" class="flex-wrap relative w-full shadow-sm flex items-center bg-primary border border-gray-600 rounded-md px-2 py-1 cursor-pointer" @click.stop.prevent="clickWrapper" @mouseup.stop.prevent @mousedown.prevent>
 | 
					      <div ref="inputWrapper" style="min-height: 40px" class="flex-wrap relative w-full shadow-sm flex items-center bg-primary border border-gray-600 rounded-md px-2 py-1 cursor-pointer" @click.stop.prevent="clickWrapper" @mouseup.stop.prevent @mousedown.prevent>
 | 
				
			||||||
@ -11,23 +11,24 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <ul ref="menu" v-show="showMenu" class="absolute z-60 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label">
 | 
					      <transition name="menu">
 | 
				
			||||||
        <template v-for="item in items">
 | 
					        <ul ref="menu" v-show="showMenu" class="absolute z-60 -mt-px w-full bg-primary border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label">
 | 
				
			||||||
          <li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" role="option" @click="clickedOption($event, item)" @mouseup.stop.prevent @mousedown.prevent>
 | 
					          <template v-for="item in items">
 | 
				
			||||||
            <div class="flex items-center">
 | 
					            <li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" role="option" @click="clickedOption($event, item)" @mouseup.stop.prevent @mousedown.prevent>
 | 
				
			||||||
              <span class="font-normal ml-3 block truncate">{{ item.text }}</span>
 | 
					              <p class="font-normal ml-3 block truncate">{{ item.text }}</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              <div v-if="selected.includes(item.value)" class="text-yellow-400 absolute inset-y-0 right-0 my-auto w-5 h-5 mr-3 overflow-hidden">
 | 
				
			||||||
 | 
					                <span class="material-icons text-xl">checkmark</span>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          </template>
 | 
				
			||||||
 | 
					          <li v-if="!items.length" class="text-gray-50 select-none relative py-2 pr-9" role="option">
 | 
				
			||||||
 | 
					            <div class="flex items-center justify-center">
 | 
				
			||||||
 | 
					              <span class="font-normal">{{ $strings.MessageNoItems }}</span>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <span v-if="selected.includes(item.value)" class="text-yellow-400 absolute inset-y-0 right-0 flex items-center pr-4">
 | 
					 | 
				
			||||||
              <span class="material-icons text-xl">checkmark</span>
 | 
					 | 
				
			||||||
            </span>
 | 
					 | 
				
			||||||
          </li>
 | 
					          </li>
 | 
				
			||||||
        </template>
 | 
					        </ul>
 | 
				
			||||||
        <li v-if="!items.length" class="text-gray-50 select-none relative py-2 pr-9" role="option">
 | 
					      </transition>
 | 
				
			||||||
          <div class="flex items-center justify-center">
 | 
					 | 
				
			||||||
            <span class="font-normal">{{ $strings.MessageNoItems }}</span>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </li>
 | 
					 | 
				
			||||||
      </ul>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
@ -48,7 +49,12 @@ export default {
 | 
				
			|||||||
  data() {
 | 
					  data() {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      showMenu: false,
 | 
					      showMenu: false,
 | 
				
			||||||
      menu: null
 | 
					      menu: null,
 | 
				
			||||||
 | 
					      clickOutsideObj: {
 | 
				
			||||||
 | 
					        handler: this.closeMenu,
 | 
				
			||||||
 | 
					        events: ['mousedown'],
 | 
				
			||||||
 | 
					        isActive: true
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  computed: {
 | 
					  computed: {
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Tilføj {0} Bøger til Samling",
 | 
					  "LabelAddToCollectionBatch": "Tilføj {0} Bøger til Samling",
 | 
				
			||||||
  "LabelAddToPlaylist": "Tilføj til Afspilningsliste",
 | 
					  "LabelAddToPlaylist": "Tilføj til Afspilningsliste",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Tilføj {0} Elementer til Afspilningsliste",
 | 
					  "LabelAddToPlaylistBatch": "Tilføj {0} Elementer til Afspilningsliste",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "Alle",
 | 
					  "LabelAll": "Alle",
 | 
				
			||||||
  "LabelAllUsers": "Alle Brugere",
 | 
					  "LabelAllUsers": "Alle Brugere",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Allerede i dit bibliotek",
 | 
					  "LabelAlreadyInYourLibrary": "Allerede i dit bibliotek",
 | 
				
			||||||
  "LabelAppend": "Tilføj",
 | 
					  "LabelAppend": "Tilføj",
 | 
				
			||||||
  "LabelAuthor": "Forfatter",
 | 
					  "LabelAuthor": "Forfatter",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Fravælg Alle",
 | 
					  "LabelDeselectAll": "Fravælg Alle",
 | 
				
			||||||
  "LabelDevice": "Enheds",
 | 
					  "LabelDevice": "Enheds",
 | 
				
			||||||
  "LabelDeviceInfo": "Enhedsinformation",
 | 
					  "LabelDeviceInfo": "Enhedsinformation",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Mappe",
 | 
					  "LabelDirectory": "Mappe",
 | 
				
			||||||
  "LabelDiscFromFilename": "Disk fra Filnavn",
 | 
					  "LabelDiscFromFilename": "Disk fra Filnavn",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Disk fra Metadata",
 | 
					  "LabelDiscFromMetadata": "Disk fra Metadata",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Sæson",
 | 
					  "LabelSeason": "Sæson",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Vælg alle episoder",
 | 
					  "LabelSelectAllEpisodes": "Vælg alle episoder",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Vælg {0} episoder vist",
 | 
					  "LabelSelectEpisodesShowing": "Vælg {0} episoder vist",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Send e-bog til...",
 | 
					  "LabelSendEbookToDevice": "Send e-bog til...",
 | 
				
			||||||
  "LabelSequence": "Sekvens",
 | 
					  "LabelSequence": "Sekvens",
 | 
				
			||||||
  "LabelSeries": "Serie",
 | 
					  "LabelSeries": "Serie",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Füge {0} Hörbüch(er)/Podcast(s) der Sammlung hinzu",
 | 
					  "LabelAddToCollectionBatch": "Füge {0} Hörbüch(er)/Podcast(s) der Sammlung hinzu",
 | 
				
			||||||
  "LabelAddToPlaylist": "Zur Wiedergabeliste hinzufügen",
 | 
					  "LabelAddToPlaylist": "Zur Wiedergabeliste hinzufügen",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Füge {0} Hörbüch(er)/Podcast(s) der Wiedergabeliste hinzu",
 | 
					  "LabelAddToPlaylistBatch": "Füge {0} Hörbüch(er)/Podcast(s) der Wiedergabeliste hinzu",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "Alle",
 | 
					  "LabelAll": "Alle",
 | 
				
			||||||
  "LabelAllUsers": "Alle Benutzer",
 | 
					  "LabelAllUsers": "Alle Benutzer",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "In der Bibliothek vorhanden",
 | 
					  "LabelAlreadyInYourLibrary": "In der Bibliothek vorhanden",
 | 
				
			||||||
  "LabelAppend": "Anhängen",
 | 
					  "LabelAppend": "Anhängen",
 | 
				
			||||||
  "LabelAuthor": "Autor",
 | 
					  "LabelAuthor": "Autor",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Alles abwählen",
 | 
					  "LabelDeselectAll": "Alles abwählen",
 | 
				
			||||||
  "LabelDevice": "Gerät",
 | 
					  "LabelDevice": "Gerät",
 | 
				
			||||||
  "LabelDeviceInfo": "Geräteinformationen",
 | 
					  "LabelDeviceInfo": "Geräteinformationen",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Verzeichnis",
 | 
					  "LabelDirectory": "Verzeichnis",
 | 
				
			||||||
  "LabelDiscFromFilename": "CD aus dem Dateinamen",
 | 
					  "LabelDiscFromFilename": "CD aus dem Dateinamen",
 | 
				
			||||||
  "LabelDiscFromMetadata": "CD aus den Metadaten",
 | 
					  "LabelDiscFromMetadata": "CD aus den Metadaten",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Staffel",
 | 
					  "LabelSeason": "Staffel",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Alle Episoden auswählen",
 | 
					  "LabelSelectAllEpisodes": "Alle Episoden auswählen",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "{0} ausgewählte Episoden werden angezeigt",
 | 
					  "LabelSelectEpisodesShowing": "{0} ausgewählte Episoden werden angezeigt",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "E-Book senden an...",
 | 
					  "LabelSendEbookToDevice": "E-Book senden an...",
 | 
				
			||||||
  "LabelSequence": "Reihenfolge",
 | 
					  "LabelSequence": "Reihenfolge",
 | 
				
			||||||
  "LabelSeries": "Serien",
 | 
					  "LabelSeries": "Serien",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Add {0} Books to Collection",
 | 
					  "LabelAddToCollectionBatch": "Add {0} Books to Collection",
 | 
				
			||||||
  "LabelAddToPlaylist": "Add to Playlist",
 | 
					  "LabelAddToPlaylist": "Add to Playlist",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Add {0} Items to Playlist",
 | 
					  "LabelAddToPlaylistBatch": "Add {0} Items to Playlist",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "All",
 | 
					  "LabelAll": "All",
 | 
				
			||||||
  "LabelAllUsers": "All Users",
 | 
					  "LabelAllUsers": "All Users",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Already in your library",
 | 
					  "LabelAlreadyInYourLibrary": "Already in your library",
 | 
				
			||||||
  "LabelAppend": "Append",
 | 
					  "LabelAppend": "Append",
 | 
				
			||||||
  "LabelAuthor": "Author",
 | 
					  "LabelAuthor": "Author",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Deselect All",
 | 
					  "LabelDeselectAll": "Deselect All",
 | 
				
			||||||
  "LabelDevice": "Device",
 | 
					  "LabelDevice": "Device",
 | 
				
			||||||
  "LabelDeviceInfo": "Device Info",
 | 
					  "LabelDeviceInfo": "Device Info",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Directory",
 | 
					  "LabelDirectory": "Directory",
 | 
				
			||||||
  "LabelDiscFromFilename": "Disc from Filename",
 | 
					  "LabelDiscFromFilename": "Disc from Filename",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Disc from Metadata",
 | 
					  "LabelDiscFromMetadata": "Disc from Metadata",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Season",
 | 
					  "LabelSeason": "Season",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Select all episodes",
 | 
					  "LabelSelectAllEpisodes": "Select all episodes",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Select {0} episodes showing",
 | 
					  "LabelSelectEpisodesShowing": "Select {0} episodes showing",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Send Ebook to...",
 | 
					  "LabelSendEbookToDevice": "Send Ebook to...",
 | 
				
			||||||
  "LabelSequence": "Sequence",
 | 
					  "LabelSequence": "Sequence",
 | 
				
			||||||
  "LabelSeries": "Series",
 | 
					  "LabelSeries": "Series",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Se Añadieron {0} Libros a la Colección",
 | 
					  "LabelAddToCollectionBatch": "Se Añadieron {0} Libros a la Colección",
 | 
				
			||||||
  "LabelAddToPlaylist": "Añadido a la Lista de Reproducción",
 | 
					  "LabelAddToPlaylist": "Añadido a la Lista de Reproducción",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Se Añadieron {0} Artículos a la Lista de Reproducción",
 | 
					  "LabelAddToPlaylistBatch": "Se Añadieron {0} Artículos a la Lista de Reproducción",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "Todos",
 | 
					  "LabelAll": "Todos",
 | 
				
			||||||
  "LabelAllUsers": "Todos los Usuarios",
 | 
					  "LabelAllUsers": "Todos los Usuarios",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Ya en la Biblioteca",
 | 
					  "LabelAlreadyInYourLibrary": "Ya en la Biblioteca",
 | 
				
			||||||
  "LabelAppend": "Adjuntar",
 | 
					  "LabelAppend": "Adjuntar",
 | 
				
			||||||
  "LabelAuthor": "Autor",
 | 
					  "LabelAuthor": "Autor",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Deseleccionar Todos",
 | 
					  "LabelDeselectAll": "Deseleccionar Todos",
 | 
				
			||||||
  "LabelDevice": "Dispositivo",
 | 
					  "LabelDevice": "Dispositivo",
 | 
				
			||||||
  "LabelDeviceInfo": "Información de Dispositivo",
 | 
					  "LabelDeviceInfo": "Información de Dispositivo",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Directorio",
 | 
					  "LabelDirectory": "Directorio",
 | 
				
			||||||
  "LabelDiscFromFilename": "Disco a partir del Nombre del Archivo",
 | 
					  "LabelDiscFromFilename": "Disco a partir del Nombre del Archivo",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Disco a partir de Metadata",
 | 
					  "LabelDiscFromMetadata": "Disco a partir de Metadata",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Temporada",
 | 
					  "LabelSeason": "Temporada",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Seleccionar todos los episodios",
 | 
					  "LabelSelectAllEpisodes": "Seleccionar todos los episodios",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Seleccionar los {0} episodios visibles",
 | 
					  "LabelSelectEpisodesShowing": "Seleccionar los {0} episodios visibles",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Enviar Ebook a...",
 | 
					  "LabelSendEbookToDevice": "Enviar Ebook a...",
 | 
				
			||||||
  "LabelSequence": "Secuencia",
 | 
					  "LabelSequence": "Secuencia",
 | 
				
			||||||
  "LabelSeries": "Series",
 | 
					  "LabelSeries": "Series",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Ajout de {0} livres à la lollection",
 | 
					  "LabelAddToCollectionBatch": "Ajout de {0} livres à la lollection",
 | 
				
			||||||
  "LabelAddToPlaylist": "Ajouter à la liste de lecture",
 | 
					  "LabelAddToPlaylist": "Ajouter à la liste de lecture",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "{0} éléments ajoutés à la liste de lecture",
 | 
					  "LabelAddToPlaylistBatch": "{0} éléments ajoutés à la liste de lecture",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "Tout",
 | 
					  "LabelAll": "Tout",
 | 
				
			||||||
  "LabelAllUsers": "Tous les utilisateurs",
 | 
					  "LabelAllUsers": "Tous les utilisateurs",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Déjà dans la bibliothèque",
 | 
					  "LabelAlreadyInYourLibrary": "Déjà dans la bibliothèque",
 | 
				
			||||||
  "LabelAppend": "Ajouter",
 | 
					  "LabelAppend": "Ajouter",
 | 
				
			||||||
  "LabelAuthor": "Auteur",
 | 
					  "LabelAuthor": "Auteur",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Tout déselectionner",
 | 
					  "LabelDeselectAll": "Tout déselectionner",
 | 
				
			||||||
  "LabelDevice": "Appareil",
 | 
					  "LabelDevice": "Appareil",
 | 
				
			||||||
  "LabelDeviceInfo": "Détail de l’appareil",
 | 
					  "LabelDeviceInfo": "Détail de l’appareil",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Répertoire",
 | 
					  "LabelDirectory": "Répertoire",
 | 
				
			||||||
  "LabelDiscFromFilename": "Disque depuis le fichier",
 | 
					  "LabelDiscFromFilename": "Disque depuis le fichier",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Disque depuis les métadonnées",
 | 
					  "LabelDiscFromMetadata": "Disque depuis les métadonnées",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Saison",
 | 
					  "LabelSeason": "Saison",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Sélectionner tous les épisodes",
 | 
					  "LabelSelectAllEpisodes": "Sélectionner tous les épisodes",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Sélectionner {0} episode(s) en cours",
 | 
					  "LabelSelectEpisodesShowing": "Sélectionner {0} episode(s) en cours",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Envoyer le livre numérique à...",
 | 
					  "LabelSendEbookToDevice": "Envoyer le livre numérique à...",
 | 
				
			||||||
  "LabelSequence": "Séquence",
 | 
					  "LabelSequence": "Séquence",
 | 
				
			||||||
  "LabelSeries": "Séries",
 | 
					  "LabelSeries": "Séries",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Add {0} Books to Collection",
 | 
					  "LabelAddToCollectionBatch": "Add {0} Books to Collection",
 | 
				
			||||||
  "LabelAddToPlaylist": "Add to Playlist",
 | 
					  "LabelAddToPlaylist": "Add to Playlist",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Add {0} Items to Playlist",
 | 
					  "LabelAddToPlaylistBatch": "Add {0} Items to Playlist",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "All",
 | 
					  "LabelAll": "All",
 | 
				
			||||||
  "LabelAllUsers": "All Users",
 | 
					  "LabelAllUsers": "All Users",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Already in your library",
 | 
					  "LabelAlreadyInYourLibrary": "Already in your library",
 | 
				
			||||||
  "LabelAppend": "Append",
 | 
					  "LabelAppend": "Append",
 | 
				
			||||||
  "LabelAuthor": "Author",
 | 
					  "LabelAuthor": "Author",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Deselect All",
 | 
					  "LabelDeselectAll": "Deselect All",
 | 
				
			||||||
  "LabelDevice": "Device",
 | 
					  "LabelDevice": "Device",
 | 
				
			||||||
  "LabelDeviceInfo": "Device Info",
 | 
					  "LabelDeviceInfo": "Device Info",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Directory",
 | 
					  "LabelDirectory": "Directory",
 | 
				
			||||||
  "LabelDiscFromFilename": "Disc from Filename",
 | 
					  "LabelDiscFromFilename": "Disc from Filename",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Disc from Metadata",
 | 
					  "LabelDiscFromMetadata": "Disc from Metadata",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Season",
 | 
					  "LabelSeason": "Season",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Select all episodes",
 | 
					  "LabelSelectAllEpisodes": "Select all episodes",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Select {0} episodes showing",
 | 
					  "LabelSelectEpisodesShowing": "Select {0} episodes showing",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Send Ebook to...",
 | 
					  "LabelSendEbookToDevice": "Send Ebook to...",
 | 
				
			||||||
  "LabelSequence": "Sequence",
 | 
					  "LabelSequence": "Sequence",
 | 
				
			||||||
  "LabelSeries": "Series",
 | 
					  "LabelSeries": "Series",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Add {0} Books to Collection",
 | 
					  "LabelAddToCollectionBatch": "Add {0} Books to Collection",
 | 
				
			||||||
  "LabelAddToPlaylist": "Add to Playlist",
 | 
					  "LabelAddToPlaylist": "Add to Playlist",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Add {0} Items to Playlist",
 | 
					  "LabelAddToPlaylistBatch": "Add {0} Items to Playlist",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "All",
 | 
					  "LabelAll": "All",
 | 
				
			||||||
  "LabelAllUsers": "All Users",
 | 
					  "LabelAllUsers": "All Users",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Already in your library",
 | 
					  "LabelAlreadyInYourLibrary": "Already in your library",
 | 
				
			||||||
  "LabelAppend": "Append",
 | 
					  "LabelAppend": "Append",
 | 
				
			||||||
  "LabelAuthor": "Author",
 | 
					  "LabelAuthor": "Author",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Deselect All",
 | 
					  "LabelDeselectAll": "Deselect All",
 | 
				
			||||||
  "LabelDevice": "Device",
 | 
					  "LabelDevice": "Device",
 | 
				
			||||||
  "LabelDeviceInfo": "Device Info",
 | 
					  "LabelDeviceInfo": "Device Info",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Directory",
 | 
					  "LabelDirectory": "Directory",
 | 
				
			||||||
  "LabelDiscFromFilename": "Disc from Filename",
 | 
					  "LabelDiscFromFilename": "Disc from Filename",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Disc from Metadata",
 | 
					  "LabelDiscFromMetadata": "Disc from Metadata",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Season",
 | 
					  "LabelSeason": "Season",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Select all episodes",
 | 
					  "LabelSelectAllEpisodes": "Select all episodes",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Select {0} episodes showing",
 | 
					  "LabelSelectEpisodesShowing": "Select {0} episodes showing",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Send Ebook to...",
 | 
					  "LabelSendEbookToDevice": "Send Ebook to...",
 | 
				
			||||||
  "LabelSequence": "Sequence",
 | 
					  "LabelSequence": "Sequence",
 | 
				
			||||||
  "LabelSeries": "Series",
 | 
					  "LabelSeries": "Series",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Add {0} Books to Collection",
 | 
					  "LabelAddToCollectionBatch": "Add {0} Books to Collection",
 | 
				
			||||||
  "LabelAddToPlaylist": "Add to Playlist",
 | 
					  "LabelAddToPlaylist": "Add to Playlist",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Add {0} Items to Playlist",
 | 
					  "LabelAddToPlaylistBatch": "Add {0} Items to Playlist",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "All",
 | 
					  "LabelAll": "All",
 | 
				
			||||||
  "LabelAllUsers": "Svi korisnici",
 | 
					  "LabelAllUsers": "Svi korisnici",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Already in your library",
 | 
					  "LabelAlreadyInYourLibrary": "Already in your library",
 | 
				
			||||||
  "LabelAppend": "Append",
 | 
					  "LabelAppend": "Append",
 | 
				
			||||||
  "LabelAuthor": "Autor",
 | 
					  "LabelAuthor": "Autor",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Odznači sve",
 | 
					  "LabelDeselectAll": "Odznači sve",
 | 
				
			||||||
  "LabelDevice": "Uređaj",
 | 
					  "LabelDevice": "Uređaj",
 | 
				
			||||||
  "LabelDeviceInfo": "O uređaju",
 | 
					  "LabelDeviceInfo": "O uređaju",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Direktorij",
 | 
					  "LabelDirectory": "Direktorij",
 | 
				
			||||||
  "LabelDiscFromFilename": "CD iz imena datoteke",
 | 
					  "LabelDiscFromFilename": "CD iz imena datoteke",
 | 
				
			||||||
  "LabelDiscFromMetadata": "CD iz metapodataka",
 | 
					  "LabelDiscFromMetadata": "CD iz metapodataka",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Sezona",
 | 
					  "LabelSeason": "Sezona",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Select all episodes",
 | 
					  "LabelSelectAllEpisodes": "Select all episodes",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Select {0} episodes showing",
 | 
					  "LabelSelectEpisodesShowing": "Select {0} episodes showing",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Send Ebook to...",
 | 
					  "LabelSendEbookToDevice": "Send Ebook to...",
 | 
				
			||||||
  "LabelSequence": "Sekvenca",
 | 
					  "LabelSequence": "Sekvenca",
 | 
				
			||||||
  "LabelSeries": "Serije",
 | 
					  "LabelSeries": "Serije",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Aggiungi {0} Libri alla Raccolta",
 | 
					  "LabelAddToCollectionBatch": "Aggiungi {0} Libri alla Raccolta",
 | 
				
			||||||
  "LabelAddToPlaylist": "aggiungi alla Playlist",
 | 
					  "LabelAddToPlaylist": "aggiungi alla Playlist",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Aggiungi {0} file alla Playlist",
 | 
					  "LabelAddToPlaylistBatch": "Aggiungi {0} file alla Playlist",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "Tutti",
 | 
					  "LabelAll": "Tutti",
 | 
				
			||||||
  "LabelAllUsers": "Tutti gli Utenti",
 | 
					  "LabelAllUsers": "Tutti gli Utenti",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Già esistente nella libreria",
 | 
					  "LabelAlreadyInYourLibrary": "Già esistente nella libreria",
 | 
				
			||||||
  "LabelAppend": "Appese",
 | 
					  "LabelAppend": "Appese",
 | 
				
			||||||
  "LabelAuthor": "Autore",
 | 
					  "LabelAuthor": "Autore",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Deseleziona Tutto",
 | 
					  "LabelDeselectAll": "Deseleziona Tutto",
 | 
				
			||||||
  "LabelDevice": "Dispositivo",
 | 
					  "LabelDevice": "Dispositivo",
 | 
				
			||||||
  "LabelDeviceInfo": "Info Dispositivo",
 | 
					  "LabelDeviceInfo": "Info Dispositivo",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Elenco",
 | 
					  "LabelDirectory": "Elenco",
 | 
				
			||||||
  "LabelDiscFromFilename": "Disco dal nome file",
 | 
					  "LabelDiscFromFilename": "Disco dal nome file",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Disco dal Metadata",
 | 
					  "LabelDiscFromMetadata": "Disco dal Metadata",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Stagione",
 | 
					  "LabelSeason": "Stagione",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Seleziona tutti gli Episodi",
 | 
					  "LabelSelectAllEpisodes": "Seleziona tutti gli Episodi",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Episodi {0} selezionati ",
 | 
					  "LabelSelectEpisodesShowing": "Episodi {0} selezionati ",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Invia ebook a...",
 | 
					  "LabelSendEbookToDevice": "Invia ebook a...",
 | 
				
			||||||
  "LabelSequence": "Sequenza",
 | 
					  "LabelSequence": "Sequenza",
 | 
				
			||||||
  "LabelSeries": "Serie",
 | 
					  "LabelSeries": "Serie",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Pridėti {0} knygas į kolekciją",
 | 
					  "LabelAddToCollectionBatch": "Pridėti {0} knygas į kolekciją",
 | 
				
			||||||
  "LabelAddToPlaylist": "Pridėti į grojaraštį",
 | 
					  "LabelAddToPlaylist": "Pridėti į grojaraštį",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Pridėti {0} elementus į grojaraštį",
 | 
					  "LabelAddToPlaylistBatch": "Pridėti {0} elementus į grojaraštį",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "Visi",
 | 
					  "LabelAll": "Visi",
 | 
				
			||||||
  "LabelAllUsers": "Visi naudotojai",
 | 
					  "LabelAllUsers": "Visi naudotojai",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Jau yra jūsų bibliotekoje",
 | 
					  "LabelAlreadyInYourLibrary": "Jau yra jūsų bibliotekoje",
 | 
				
			||||||
  "LabelAppend": "Pridėti",
 | 
					  "LabelAppend": "Pridėti",
 | 
				
			||||||
  "LabelAuthor": "Autorius",
 | 
					  "LabelAuthor": "Autorius",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Išvalyti pasirinktus",
 | 
					  "LabelDeselectAll": "Išvalyti pasirinktus",
 | 
				
			||||||
  "LabelDevice": "Įrenginys",
 | 
					  "LabelDevice": "Įrenginys",
 | 
				
			||||||
  "LabelDeviceInfo": "Įrenginio informacija",
 | 
					  "LabelDeviceInfo": "Įrenginio informacija",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Katalogas",
 | 
					  "LabelDirectory": "Katalogas",
 | 
				
			||||||
  "LabelDiscFromFilename": "Diskas pagal failo pavadinimą",
 | 
					  "LabelDiscFromFilename": "Diskas pagal failo pavadinimą",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Diskas pagal metaduomenis",
 | 
					  "LabelDiscFromMetadata": "Diskas pagal metaduomenis",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Sezonas",
 | 
					  "LabelSeason": "Sezonas",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Pažymėti visus epizodus",
 | 
					  "LabelSelectAllEpisodes": "Pažymėti visus epizodus",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Pažymėti {0} rodomus epizodus",
 | 
					  "LabelSelectEpisodesShowing": "Pažymėti {0} rodomus epizodus",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Siųsti e-knygą į...",
 | 
					  "LabelSendEbookToDevice": "Siųsti e-knygą į...",
 | 
				
			||||||
  "LabelSequence": "Seka",
 | 
					  "LabelSequence": "Seka",
 | 
				
			||||||
  "LabelSeries": "Serija",
 | 
					  "LabelSeries": "Serija",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "{0} boeken toevoegen aan collectie",
 | 
					  "LabelAddToCollectionBatch": "{0} boeken toevoegen aan collectie",
 | 
				
			||||||
  "LabelAddToPlaylist": "Toevoegen aan afspeellijst",
 | 
					  "LabelAddToPlaylist": "Toevoegen aan afspeellijst",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "{0} onderdelen toevoegen aan afspeellijst",
 | 
					  "LabelAddToPlaylistBatch": "{0} onderdelen toevoegen aan afspeellijst",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "Alle",
 | 
					  "LabelAll": "Alle",
 | 
				
			||||||
  "LabelAllUsers": "Alle gebruikers",
 | 
					  "LabelAllUsers": "Alle gebruikers",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Reeds in je bibliotheek",
 | 
					  "LabelAlreadyInYourLibrary": "Reeds in je bibliotheek",
 | 
				
			||||||
  "LabelAppend": "Achteraan toevoegen",
 | 
					  "LabelAppend": "Achteraan toevoegen",
 | 
				
			||||||
  "LabelAuthor": "Auteur",
 | 
					  "LabelAuthor": "Auteur",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Deselecteer alle",
 | 
					  "LabelDeselectAll": "Deselecteer alle",
 | 
				
			||||||
  "LabelDevice": "Apparaat",
 | 
					  "LabelDevice": "Apparaat",
 | 
				
			||||||
  "LabelDeviceInfo": "Apparaat info",
 | 
					  "LabelDeviceInfo": "Apparaat info",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Map",
 | 
					  "LabelDirectory": "Map",
 | 
				
			||||||
  "LabelDiscFromFilename": "Schijf uit bestandsnaam",
 | 
					  "LabelDiscFromFilename": "Schijf uit bestandsnaam",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Schijf uit metadata",
 | 
					  "LabelDiscFromMetadata": "Schijf uit metadata",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Seizoen",
 | 
					  "LabelSeason": "Seizoen",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Selecteer alle afleveringen",
 | 
					  "LabelSelectAllEpisodes": "Selecteer alle afleveringen",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Selecteer {0} afleveringen laten zien",
 | 
					  "LabelSelectEpisodesShowing": "Selecteer {0} afleveringen laten zien",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Stuur ebook naar...",
 | 
					  "LabelSendEbookToDevice": "Stuur ebook naar...",
 | 
				
			||||||
  "LabelSequence": "Sequentie",
 | 
					  "LabelSequence": "Sequentie",
 | 
				
			||||||
  "LabelSeries": "Serie",
 | 
					  "LabelSeries": "Serie",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Legg {0} bøker til samling",
 | 
					  "LabelAddToCollectionBatch": "Legg {0} bøker til samling",
 | 
				
			||||||
  "LabelAddToPlaylist": "Legg til i spilleliste",
 | 
					  "LabelAddToPlaylist": "Legg til i spilleliste",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Legg {0} enheter til i spilleliste",
 | 
					  "LabelAddToPlaylistBatch": "Legg {0} enheter til i spilleliste",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "Alle",
 | 
					  "LabelAll": "Alle",
 | 
				
			||||||
  "LabelAllUsers": "Alle brukere",
 | 
					  "LabelAllUsers": "Alle brukere",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Allerede i biblioteket",
 | 
					  "LabelAlreadyInYourLibrary": "Allerede i biblioteket",
 | 
				
			||||||
  "LabelAppend": "Legge til",
 | 
					  "LabelAppend": "Legge til",
 | 
				
			||||||
  "LabelAuthor": "Forfatter",
 | 
					  "LabelAuthor": "Forfatter",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Fjern valg",
 | 
					  "LabelDeselectAll": "Fjern valg",
 | 
				
			||||||
  "LabelDevice": "Enhet",
 | 
					  "LabelDevice": "Enhet",
 | 
				
			||||||
  "LabelDeviceInfo": "Enhetsinformasjon",
 | 
					  "LabelDeviceInfo": "Enhetsinformasjon",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Mappe",
 | 
					  "LabelDirectory": "Mappe",
 | 
				
			||||||
  "LabelDiscFromFilename": "Disk fra filnavn",
 | 
					  "LabelDiscFromFilename": "Disk fra filnavn",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Disk fra metadata",
 | 
					  "LabelDiscFromMetadata": "Disk fra metadata",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Sesong",
 | 
					  "LabelSeason": "Sesong",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Velg alle episoder",
 | 
					  "LabelSelectAllEpisodes": "Velg alle episoder",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Velg {0} episoder vist",
 | 
					  "LabelSelectEpisodesShowing": "Velg {0} episoder vist",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Send Ebok til...",
 | 
					  "LabelSendEbookToDevice": "Send Ebok til...",
 | 
				
			||||||
  "LabelSequence": "Sekvens",
 | 
					  "LabelSequence": "Sekvens",
 | 
				
			||||||
  "LabelSeries": "Serier",
 | 
					  "LabelSeries": "Serier",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Dodaj {0} książki do kolekcji",
 | 
					  "LabelAddToCollectionBatch": "Dodaj {0} książki do kolekcji",
 | 
				
			||||||
  "LabelAddToPlaylist": "Add to Playlist",
 | 
					  "LabelAddToPlaylist": "Add to Playlist",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Add {0} Items to Playlist",
 | 
					  "LabelAddToPlaylistBatch": "Add {0} Items to Playlist",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "All",
 | 
					  "LabelAll": "All",
 | 
				
			||||||
  "LabelAllUsers": "Wszyscy użytkownicy",
 | 
					  "LabelAllUsers": "Wszyscy użytkownicy",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Already in your library",
 | 
					  "LabelAlreadyInYourLibrary": "Already in your library",
 | 
				
			||||||
  "LabelAppend": "Append",
 | 
					  "LabelAppend": "Append",
 | 
				
			||||||
  "LabelAuthor": "Autor",
 | 
					  "LabelAuthor": "Autor",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Odznacz wszystko",
 | 
					  "LabelDeselectAll": "Odznacz wszystko",
 | 
				
			||||||
  "LabelDevice": "Urządzenie",
 | 
					  "LabelDevice": "Urządzenie",
 | 
				
			||||||
  "LabelDeviceInfo": "Informacja o urządzeniu",
 | 
					  "LabelDeviceInfo": "Informacja o urządzeniu",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Katalog",
 | 
					  "LabelDirectory": "Katalog",
 | 
				
			||||||
  "LabelDiscFromFilename": "Oznaczenie dysku z nazwy pliku",
 | 
					  "LabelDiscFromFilename": "Oznaczenie dysku z nazwy pliku",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Oznaczenie dysku z metadanych",
 | 
					  "LabelDiscFromMetadata": "Oznaczenie dysku z metadanych",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Sezon",
 | 
					  "LabelSeason": "Sezon",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Select all episodes",
 | 
					  "LabelSelectAllEpisodes": "Select all episodes",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Select {0} episodes showing",
 | 
					  "LabelSelectEpisodesShowing": "Select {0} episodes showing",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Send Ebook to...",
 | 
					  "LabelSendEbookToDevice": "Send Ebook to...",
 | 
				
			||||||
  "LabelSequence": "Kolejność",
 | 
					  "LabelSequence": "Kolejność",
 | 
				
			||||||
  "LabelSeries": "Serie",
 | 
					  "LabelSeries": "Serie",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "Добавить {0} книг в коллекцию",
 | 
					  "LabelAddToCollectionBatch": "Добавить {0} книг в коллекцию",
 | 
				
			||||||
  "LabelAddToPlaylist": "Добавить в плейлист",
 | 
					  "LabelAddToPlaylist": "Добавить в плейлист",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "Добавить {0} элементов в плейлист",
 | 
					  "LabelAddToPlaylistBatch": "Добавить {0} элементов в плейлист",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "Все",
 | 
					  "LabelAll": "Все",
 | 
				
			||||||
  "LabelAllUsers": "Все пользователи",
 | 
					  "LabelAllUsers": "Все пользователи",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "Уже в Вашей библиотеке",
 | 
					  "LabelAlreadyInYourLibrary": "Уже в Вашей библиотеке",
 | 
				
			||||||
  "LabelAppend": "Добавить",
 | 
					  "LabelAppend": "Добавить",
 | 
				
			||||||
  "LabelAuthor": "Автор",
 | 
					  "LabelAuthor": "Автор",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "Снять выделение",
 | 
					  "LabelDeselectAll": "Снять выделение",
 | 
				
			||||||
  "LabelDevice": "Устройство",
 | 
					  "LabelDevice": "Устройство",
 | 
				
			||||||
  "LabelDeviceInfo": "Информация об устройстве",
 | 
					  "LabelDeviceInfo": "Информация об устройстве",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "Каталог",
 | 
					  "LabelDirectory": "Каталог",
 | 
				
			||||||
  "LabelDiscFromFilename": "Диск из Имени файла",
 | 
					  "LabelDiscFromFilename": "Диск из Имени файла",
 | 
				
			||||||
  "LabelDiscFromMetadata": "Диск из Метаданных",
 | 
					  "LabelDiscFromMetadata": "Диск из Метаданных",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "Сезон",
 | 
					  "LabelSeason": "Сезон",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "Выбрать все эпизоды",
 | 
					  "LabelSelectAllEpisodes": "Выбрать все эпизоды",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "Выберите {0} эпизодов для показа",
 | 
					  "LabelSelectEpisodesShowing": "Выберите {0} эпизодов для показа",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "Отправить e-книгу в...",
 | 
					  "LabelSendEbookToDevice": "Отправить e-книгу в...",
 | 
				
			||||||
  "LabelSequence": "Последовательность",
 | 
					  "LabelSequence": "Последовательность",
 | 
				
			||||||
  "LabelSeries": "Серия",
 | 
					  "LabelSeries": "Серия",
 | 
				
			||||||
 | 
				
			|||||||
@ -181,8 +181,11 @@
 | 
				
			|||||||
  "LabelAddToCollectionBatch": "批量添加 {0} 个媒体到收藏",
 | 
					  "LabelAddToCollectionBatch": "批量添加 {0} 个媒体到收藏",
 | 
				
			||||||
  "LabelAddToPlaylist": "添加到播放列表",
 | 
					  "LabelAddToPlaylist": "添加到播放列表",
 | 
				
			||||||
  "LabelAddToPlaylistBatch": "添加 {0} 个项目到播放列表",
 | 
					  "LabelAddToPlaylistBatch": "添加 {0} 个项目到播放列表",
 | 
				
			||||||
 | 
					  "LabelAdminUsersOnly": "Admin users only",
 | 
				
			||||||
  "LabelAll": "全部",
 | 
					  "LabelAll": "全部",
 | 
				
			||||||
  "LabelAllUsers": "所有用户",
 | 
					  "LabelAllUsers": "所有用户",
 | 
				
			||||||
 | 
					  "LabelAllUsersExcludingGuests": "All users excluding guests",
 | 
				
			||||||
 | 
					  "LabelAllUsersIncludingGuests": "All users including guests",
 | 
				
			||||||
  "LabelAlreadyInYourLibrary": "已存在你的库中",
 | 
					  "LabelAlreadyInYourLibrary": "已存在你的库中",
 | 
				
			||||||
  "LabelAppend": "附加",
 | 
					  "LabelAppend": "附加",
 | 
				
			||||||
  "LabelAuthor": "作者",
 | 
					  "LabelAuthor": "作者",
 | 
				
			||||||
@ -229,6 +232,7 @@
 | 
				
			|||||||
  "LabelDeselectAll": "全部取消选择",
 | 
					  "LabelDeselectAll": "全部取消选择",
 | 
				
			||||||
  "LabelDevice": "设备",
 | 
					  "LabelDevice": "设备",
 | 
				
			||||||
  "LabelDeviceInfo": "设备信息",
 | 
					  "LabelDeviceInfo": "设备信息",
 | 
				
			||||||
 | 
					  "LabelDeviceIsAvailableTo": "Device is available to...",
 | 
				
			||||||
  "LabelDirectory": "目录",
 | 
					  "LabelDirectory": "目录",
 | 
				
			||||||
  "LabelDiscFromFilename": "从文件名获取光盘",
 | 
					  "LabelDiscFromFilename": "从文件名获取光盘",
 | 
				
			||||||
  "LabelDiscFromMetadata": "从元数据获取光盘",
 | 
					  "LabelDiscFromMetadata": "从元数据获取光盘",
 | 
				
			||||||
@ -394,6 +398,7 @@
 | 
				
			|||||||
  "LabelSeason": "季",
 | 
					  "LabelSeason": "季",
 | 
				
			||||||
  "LabelSelectAllEpisodes": "选择所有剧集",
 | 
					  "LabelSelectAllEpisodes": "选择所有剧集",
 | 
				
			||||||
  "LabelSelectEpisodesShowing": "选择正在播放的 {0} 剧集",
 | 
					  "LabelSelectEpisodesShowing": "选择正在播放的 {0} 剧集",
 | 
				
			||||||
 | 
					  "LabelSelectUsers": "Select users",
 | 
				
			||||||
  "LabelSendEbookToDevice": "发送电子书到...",
 | 
					  "LabelSendEbookToDevice": "发送电子书到...",
 | 
				
			||||||
  "LabelSequence": "序列",
 | 
					  "LabelSequence": "序列",
 | 
				
			||||||
  "LabelSeries": "系列",
 | 
					  "LabelSeries": "系列",
 | 
				
			||||||
 | 
				
			|||||||
@ -51,32 +51,45 @@ class EmailController {
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Send ebook to device
 | 
				
			||||||
 | 
					   * User must have access to device and library item
 | 
				
			||||||
 | 
					   * 
 | 
				
			||||||
 | 
					   * @param {import('express').Request} req 
 | 
				
			||||||
 | 
					   * @param {import('express').Response} res 
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
  async sendEBookToDevice(req, res) {
 | 
					  async sendEBookToDevice(req, res) {
 | 
				
			||||||
    Logger.debug(`[EmailController] Send ebook to device request for libraryItemId=${req.body.libraryItemId}, deviceName=${req.body.deviceName}`)
 | 
					    Logger.debug(`[EmailController] Send ebook to device requested by user "${req.user.username}" for libraryItemId=${req.body.libraryItemId}, deviceName=${req.body.deviceName}`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const device = Database.emailSettings.getEReaderDevice(req.body.deviceName)
 | 
				
			||||||
 | 
					    if (!device) {
 | 
				
			||||||
 | 
					      return res.status(404).send('Ereader device not found')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check user has access to device
 | 
				
			||||||
 | 
					    if (!Database.emailSettings.checkUserCanAccessDevice(device, req.user)) {
 | 
				
			||||||
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const libraryItem = await Database.libraryItemModel.getOldById(req.body.libraryItemId)
 | 
					    const libraryItem = await Database.libraryItemModel.getOldById(req.body.libraryItemId)
 | 
				
			||||||
    if (!libraryItem) {
 | 
					    if (!libraryItem) {
 | 
				
			||||||
      return res.status(404).send('Library item not found')
 | 
					      return res.status(404).send('Library item not found')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check user has access to library item
 | 
				
			||||||
    if (!req.user.checkCanAccessLibraryItem(libraryItem)) {
 | 
					    if (!req.user.checkCanAccessLibraryItem(libraryItem)) {
 | 
				
			||||||
      return res.sendStatus(403)
 | 
					      return res.sendStatus(403)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const ebookFile = libraryItem.media.ebookFile
 | 
					    const ebookFile = libraryItem.media.ebookFile
 | 
				
			||||||
    if (!ebookFile) {
 | 
					    if (!ebookFile) {
 | 
				
			||||||
      return res.status(404).send('EBook file not found')
 | 
					      return res.status(404).send('Ebook file not found')
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const device = Database.emailSettings.getEReaderDevice(req.body.deviceName)
 | 
					 | 
				
			||||||
    if (!device) {
 | 
					 | 
				
			||||||
      return res.status(404).send('E-reader device not found')
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.emailManager.sendEBookToDevice(ebookFile, device, res)
 | 
					    this.emailManager.sendEBookToDevice(ebookFile, device, res)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  middleware(req, res, next) {
 | 
					  adminMiddleware(req, res, next) {
 | 
				
			||||||
    if (!req.user.isAdminOrUp) {
 | 
					    if (!req.user.isAdminOrUp) {
 | 
				
			||||||
      return res.sendStatus(404)
 | 
					      return res.sendStatus(404)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,14 @@
 | 
				
			|||||||
const Logger = require('../../Logger')
 | 
					const Logger = require('../../Logger')
 | 
				
			||||||
const { areEquivalent, copyValue, isNullOrNaN } = require('../../utils')
 | 
					const { areEquivalent, copyValue, isNullOrNaN } = require('../../utils')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @typedef EreaderDeviceObject
 | 
				
			||||||
 | 
					 * @property {string} name
 | 
				
			||||||
 | 
					 * @property {string} email
 | 
				
			||||||
 | 
					 * @property {string} availabilityOption
 | 
				
			||||||
 | 
					 * @property {string[]} users
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// REF: https://nodemailer.com/smtp/
 | 
					// REF: https://nodemailer.com/smtp/
 | 
				
			||||||
class EmailSettings {
 | 
					class EmailSettings {
 | 
				
			||||||
  constructor(settings = null) {
 | 
					  constructor(settings = null) {
 | 
				
			||||||
@ -13,7 +21,7 @@ class EmailSettings {
 | 
				
			|||||||
    this.testAddress = null
 | 
					    this.testAddress = null
 | 
				
			||||||
    this.fromAddress = null
 | 
					    this.fromAddress = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Array of { name:String, email:String }
 | 
					    /** @type {EreaderDeviceObject[]} */
 | 
				
			||||||
    this.ereaderDevices = []
 | 
					    this.ereaderDevices = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (settings) {
 | 
					    if (settings) {
 | 
				
			||||||
@ -57,6 +65,26 @@ class EmailSettings {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (payload.ereaderDevices !== undefined && !Array.isArray(payload.ereaderDevices)) payload.ereaderDevices = undefined
 | 
					    if (payload.ereaderDevices !== undefined && !Array.isArray(payload.ereaderDevices)) payload.ereaderDevices = undefined
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (payload.ereaderDevices?.length) {
 | 
				
			||||||
 | 
					      // Validate ereader devices
 | 
				
			||||||
 | 
					      payload.ereaderDevices = payload.ereaderDevices.map((device) => {
 | 
				
			||||||
 | 
					        if (!device.name || !device.email) {
 | 
				
			||||||
 | 
					          Logger.error(`[EmailSettings] Update ereader device is invalid`, device)
 | 
				
			||||||
 | 
					          return null
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!device.availabilityOption || !['adminOrUp', 'userOrUp', 'guestOrUp', 'specificUsers'].includes(device.availabilityOption)) {
 | 
				
			||||||
 | 
					          device.availabilityOption = 'adminOrUp'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (device.availabilityOption === 'specificUsers' && !device.users?.length) {
 | 
				
			||||||
 | 
					          device.availabilityOption = 'adminOrUp'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (device.availabilityOption !== 'specificUsers' && device.users?.length) {
 | 
				
			||||||
 | 
					          device.users = []
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return device
 | 
				
			||||||
 | 
					      }).filter(d => d)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let hasUpdates = false
 | 
					    let hasUpdates = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const json = this.toJSON()
 | 
					    const json = this.toJSON()
 | 
				
			||||||
@ -88,15 +116,40 @@ class EmailSettings {
 | 
				
			|||||||
    return payload
 | 
					    return payload
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getEReaderDevices(user) {
 | 
					  /**
 | 
				
			||||||
    // Only accessible to admin or up
 | 
					   * 
 | 
				
			||||||
    if (!user.isAdminOrUp) {
 | 
					   * @param {EreaderDeviceObject} device 
 | 
				
			||||||
      return []
 | 
					   * @param {import('../user/User')} user 
 | 
				
			||||||
 | 
					   * @returns {boolean}
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  checkUserCanAccessDevice(device, user) {
 | 
				
			||||||
 | 
					    let deviceAvailability = device.availabilityOption || 'adminOrUp'
 | 
				
			||||||
 | 
					    if (deviceAvailability === 'adminOrUp' && user.isAdminOrUp) return true
 | 
				
			||||||
 | 
					    if (deviceAvailability === 'userOrUp' && (user.isAdminOrUp || user.isUser)) return true
 | 
				
			||||||
 | 
					    if (deviceAvailability === 'guestOrUp') return true
 | 
				
			||||||
 | 
					    if (deviceAvailability === 'specificUsers') {
 | 
				
			||||||
 | 
					      let deviceUsers = device.users || []
 | 
				
			||||||
 | 
					      return deviceUsers.includes(user.id)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return false
 | 
				
			||||||
    return this.ereaderDevices.map(d => ({ ...d }))
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Get ereader devices accessible to user
 | 
				
			||||||
 | 
					   * 
 | 
				
			||||||
 | 
					   * @param {import('../user/User')} user 
 | 
				
			||||||
 | 
					   * @returns {EreaderDeviceObject[]}
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  getEReaderDevices(user) {
 | 
				
			||||||
 | 
					    return this.ereaderDevices.filter((device) => this.checkUserCanAccessDevice(device, user))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Get ereader device by name
 | 
				
			||||||
 | 
					   * 
 | 
				
			||||||
 | 
					   * @param {string} deviceName 
 | 
				
			||||||
 | 
					   * @returns {EreaderDeviceObject}
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
  getEReaderDevice(deviceName) {
 | 
					  getEReaderDevice(deviceName) {
 | 
				
			||||||
    return this.ereaderDevices.find(d => d.name === deviceName)
 | 
					    return this.ereaderDevices.find(d => d.name === deviceName)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -35,6 +35,9 @@ class User {
 | 
				
			|||||||
  get isAdmin() {
 | 
					  get isAdmin() {
 | 
				
			||||||
    return this.type === 'admin'
 | 
					    return this.type === 'admin'
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  get isUser() {
 | 
				
			||||||
 | 
					    return this.type === 'user'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  get isGuest() {
 | 
					  get isGuest() {
 | 
				
			||||||
    return this.type === 'guest'
 | 
					    return this.type === 'guest'
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -255,11 +255,11 @@ class ApiRouter {
 | 
				
			|||||||
    //
 | 
					    //
 | 
				
			||||||
    // Email Routes (Admin and up)
 | 
					    // Email Routes (Admin and up)
 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
    this.router.get('/emails/settings', EmailController.middleware.bind(this), EmailController.getSettings.bind(this))
 | 
					    this.router.get('/emails/settings', EmailController.adminMiddleware.bind(this), EmailController.getSettings.bind(this))
 | 
				
			||||||
    this.router.patch('/emails/settings', EmailController.middleware.bind(this), EmailController.updateSettings.bind(this))
 | 
					    this.router.patch('/emails/settings', EmailController.adminMiddleware.bind(this), EmailController.updateSettings.bind(this))
 | 
				
			||||||
    this.router.post('/emails/test', EmailController.middleware.bind(this), EmailController.sendTest.bind(this))
 | 
					    this.router.post('/emails/test', EmailController.adminMiddleware.bind(this), EmailController.sendTest.bind(this))
 | 
				
			||||||
    this.router.post('/emails/ereader-devices', EmailController.middleware.bind(this), EmailController.updateEReaderDevices.bind(this))
 | 
					    this.router.post('/emails/ereader-devices', EmailController.adminMiddleware.bind(this), EmailController.updateEReaderDevices.bind(this))
 | 
				
			||||||
    this.router.post('/emails/send-ebook-to-device', EmailController.middleware.bind(this), EmailController.sendEBookToDevice.bind(this))
 | 
					    this.router.post('/emails/send-ebook-to-device', EmailController.sendEBookToDevice.bind(this))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
    // Search Routes
 | 
					    // Search Routes
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user