mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-02-19 00:18:56 +01:00
Add:Library media type selection and library icons
This commit is contained in:
parent
d3a8ecc8d1
commit
408775a25a
23
client/components/icons/audiobooks-svg.vue
Normal file
23
client/components/icons/audiobooks-svg.vue
Normal file
@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<svg fill="currentColor" class="h-full w-full p-px" viewBox="0 0 1978.03 2349.44">
|
||||
<path
|
||||
d="M2519.5,1438.39c-12.13-10.1-31-25-56.57-42.62V1197.31c0-505.94-410.15-916.09-916.1-916.09h0c-505.94,0-916.09,410.15-916.09,916.09v198.46c-25.57,17.66-44.44,32.52-56.57,42.62a45.45,45.45,0,0,0-16.35,34.95v237.74a45.45,45.45,0,0,0,16.35,35c28.28,23.54,93.18,72.92,194.22,123.55v23.11c0,62.32,40.21,112.85,89.8,112.85h0c49.59,0,89.8-50.53,89.8-112.85V1322.51c0-62.33-40.21-112.86-89.8-112.86h0c-47.51,0-86.4,46.38-89.58,105.07l-.22.11V1197.31c0-429.92,348.52-778.43,778.44-778.43h0c429.92,0,778.44,348.51,778.44,778.43v117.52l-.22-.11c-3.18-58.69-42.06-105.07-89.58-105.07h0c-49.59,0-89.79,50.53-89.79,112.86v570.18c0,62.32,40.2,112.85,89.79,112.85h0c49.6,0,89.8-50.53,89.8-112.85v-23.11c101.05-50.63,165.95-100,194.23-123.55a45.48,45.48,0,0,0,16.35-35V1473.34A45.48,45.48,0,0,0,2519.5,1438.39Z"
|
||||
transform="translate(-557.82 -281.22)"
|
||||
/>
|
||||
<path d="M1227.4,2429.63a108.47,108.47,0,0,0,108.47-108.47V1106.56A108.47,108.47,0,0,0,1227.4,998.08H1115.33a108.48,108.48,0,0,0-108.48,108.48v1214.6a108.47,108.47,0,0,0,108.48,108.47ZM1047.75,1289.38H1295v25.83H1047.75Z" transform="translate(-557.82 -281.22)" />
|
||||
<path d="M1602.87,2429.63a108.47,108.47,0,0,0,108.47-108.47V1106.56a108.47,108.47,0,0,0-108.47-108.48H1490.8a108.48,108.48,0,0,0-108.48,108.48v1214.6a108.47,108.47,0,0,0,108.48,108.47ZM1423.22,1289.38h247.22v25.83H1423.22Z" transform="translate(-557.82 -281.22)" />
|
||||
<path d="M1978.34,2429.63a108.47,108.47,0,0,0,108.47-108.47V1106.56a108.47,108.47,0,0,0-108.47-108.48H1866.27a108.48,108.48,0,0,0-108.48,108.48v1214.6a108.47,108.47,0,0,0,108.48,108.47ZM1798.69,1289.38h247.22v25.83H1798.69Z" transform="translate(-557.82 -281.22)" />
|
||||
<rect x="180.05" y="2185.95" width="1617.93" height="163.49" rx="81.74" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {},
|
||||
methods: {},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
16
client/components/icons/books-svg.vue
Normal file
16
client/components/icons/books-svg.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg fill="currentColor" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M9 3V18H12V3H9M12 5L16 18L19 17L15 4L12 5M5 5V18H8V5H5M3 19V21H21V19H3Z" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {},
|
||||
methods: {},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
16
client/components/icons/comics-svg.vue
Normal file
16
client/components/icons/comics-svg.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M6,19L9,15.14L11.14,17.72L14.14,13.86L18,19H6M6,4H11V12L8.5,10.5L6,12M18,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V4A2,2 0 0,0 18,2Z" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {},
|
||||
methods: {},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
16
client/components/icons/database-svg.vue
Normal file
16
client/components/icons/database-svg.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg class="p-px" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {},
|
||||
methods: {},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
19
client/components/icons/podcasts-svg.vue
Normal file
19
client/components/icons/podcasts-svg.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<svg class="p-px" viewBox="0 0 122.877 120.596">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M68.925,69.906v50.689H53.953V69.906c-4.918-2.662-8.259-7.867-8.259-13.854 c0-8.694,7.05-15.744,15.745-15.744c8.694,0,15.745,7.05,15.745,15.744C77.184,62.039,73.843,67.244,68.925,69.906L68.925,69.906z M39.32,11.165c2.916-1.438,4.111-4.969,2.673-7.882c-1.438-2.914-4.966-4.111-7.88-2.674C22.213,6.479,12.958,16.19,7.11,27.625 c-4.32,8.445-6.783,17.842-7.08,27.325c-0.299,9.563,1.587,19.223,5.973,28.114c5.401,10.953,14.558,20.695,28.039,27.592 c2.889,1.477,6.429,0.33,7.905-2.559c1.477-2.889,0.331-6.428-2.558-7.904c-11.037-5.645-18.486-13.525-22.833-22.334 c-3.506-7.111-5.014-14.857-4.774-22.539c0.243-7.757,2.256-15.442,5.79-22.348C22.304,23.721,29.76,15.879,39.32,11.165 L39.32,11.165z M88.765,0.608c-2.914-1.438-6.443-0.24-7.881,2.674c-1.438,2.914-0.242,6.445,2.674,7.882 c9.561,4.715,17.017,12.556,21.747,21.808c3.533,6.905,5.547,14.59,5.789,22.348c0.24,7.682-1.268,15.428-4.773,22.539 c-4.347,8.809-11.796,16.689-22.833,22.334c-2.889,1.477-4.034,5.016-2.558,7.904c1.476,2.889,5.016,4.035,7.905,2.559 c13.48-6.896,22.638-16.639,28.039-27.592c4.386-8.891,6.272-18.551,5.973-28.114c-0.297-9.483-2.76-18.88-7.079-27.325 C109.919,16.19,100.665,6.479,88.765,0.608L88.765,0.608z M82.791,26.505c-2.195-1.581-5.256-1.082-6.837,1.113 c-1.58,2.195-1.082,5.256,1.113,6.837c0.885,0.637,1.753,1.352,2.604,2.134c4.971,4.583,7.919,10.694,8.538,17.16 c0.626,6.524-1.111,13.437-5.518,19.552c-0.748,1.039-1.61,2.092-2.585,3.15c-1.835,1.992-1.708,5.098,0.287,6.932 c1.994,1.834,5.099,1.705,6.933-0.287c1.18-1.279,2.286-2.641,3.315-4.072c5.862-8.139,8.166-17.4,7.322-26.197 c-0.848-8.853-4.871-17.208-11.648-23.457C85.249,28.387,84.074,27.431,82.791,26.505L82.791,26.505z M45.81,34.458 c2.195-1.581,2.694-4.642,1.113-6.837c-1.581-2.195-4.642-2.694-6.837-1.114c-1.284,0.926-2.458,1.882-3.524,2.864 c-6.778,6.25-10.801,14.604-11.649,23.457c-0.844,8.796,1.46,18.06,7.323,26.199c1.031,1.43,2.136,2.791,3.315,4.07 c1.834,1.992,4.939,2.121,6.932,0.287c1.996-1.834,2.123-4.939,0.288-6.932c-0.975-1.059-1.837-2.111-2.585-3.15 c-4.406-6.115-6.144-13.027-5.518-19.551c0.619-6.465,3.567-12.577,8.538-17.16C44.058,35.81,44.926,35.095,45.81,34.458 L45.81,34.458z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {},
|
||||
methods: {},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
@ -6,11 +6,14 @@
|
||||
</div>
|
||||
|
||||
<div v-if="!showDirectoryPicker" class="w-full h-full py-4">
|
||||
<div class="flex flex-wrap -mx-1">
|
||||
<div class="w-full md:w-2/3 px-1">
|
||||
<div class="flex flex-wrap md:flex-nowrap -mx-1">
|
||||
<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" />
|
||||
</div>
|
||||
<div class="w-full md:w-1/3 px-1">
|
||||
<div class="w-1/2 md:w-72 px-1 py-1 md:py-0">
|
||||
<ui-media-type-picker v-model="mediaType" />
|
||||
</div>
|
||||
<div class="w-1/2 md:w-72 px-1 py-1 md:py-0">
|
||||
<ui-dropdown v-model="provider" :items="providers" label="Metadata Provider" small />
|
||||
</div>
|
||||
</div>
|
||||
@ -18,7 +21,6 @@
|
||||
<div class="w-full py-4">
|
||||
<p class="px-1 text-sm font-semibold">Folders</p>
|
||||
<div v-for="(folder, index) in folders" :key="index" class="w-full flex items-center py-1 px-2">
|
||||
<!-- <ui-text-input :value="folder.fullPath" type="text" class="w-full" /> -->
|
||||
<span class="material-icons bg-opacity-50 mr-2 text-yellow-200" style="font-size: 1.2rem">folder</span>
|
||||
<ui-editable-text v-model="folder.fullPath" 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>
|
||||
@ -60,6 +62,7 @@ export default {
|
||||
return {
|
||||
name: '',
|
||||
provider: '',
|
||||
mediaType: '',
|
||||
folders: [],
|
||||
showDirectoryPicker: false,
|
||||
disableWatcher: false
|
||||
@ -80,7 +83,7 @@ export default {
|
||||
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
|
||||
return newfolderpaths === origfolderpaths && this.name === this.library.name && this.provider === this.library.provider && this.disableWatcher === this.library.disableWatcher && this.mediaType === this.library.mediaType
|
||||
},
|
||||
providers() {
|
||||
return this.$store.state.scanners.providers
|
||||
@ -103,6 +106,7 @@ export default {
|
||||
this.provider = this.library ? this.library.provider : ''
|
||||
this.folders = this.library ? this.library.folders.map((p) => ({ ...p })) : []
|
||||
this.disableWatcher = this.library ? !!this.library.disableWatcher : false
|
||||
this.mediaType = this.library ? this.library.mediaType : 'default'
|
||||
this.showDirectoryPicker = false
|
||||
},
|
||||
selectFolder(fullPath) {
|
||||
@ -129,6 +133,8 @@ export default {
|
||||
name: this.name,
|
||||
provider: this.provider,
|
||||
folders: this.folders,
|
||||
mediaType: this.mediaType,
|
||||
icon: this.mediaType,
|
||||
disableWatcher: this.disableWatcher
|
||||
}
|
||||
|
||||
@ -163,6 +169,8 @@ export default {
|
||||
name: this.name,
|
||||
provider: this.provider,
|
||||
folders: this.folders,
|
||||
mediaType: this.mediaType,
|
||||
icon: this.mediaType,
|
||||
disableWatcher: this.disableWatcher
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
<template>
|
||||
<div class="w-full px-4 h-12 border border-white border-opacity-10 flex items-center relative -mt-px" :class="selected ? 'bg-primary bg-opacity-50' : 'hover:bg-primary hover:bg-opacity-25'" @mouseover="mouseover = true" @mouseleave="mouseover = false">
|
||||
<div v-show="selected" class="absolute top-0 left-0 h-full w-0.5 bg-warning z-10" />
|
||||
<svg v-if="!libraryScan" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" :class="isHovering ? 'text-opacity-90' : 'text-opacity-50'" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
|
||||
</svg>
|
||||
<widgets-library-icon v-if="!libraryScan" :icon="library.icon" :size="6" class="text-white" :class="isHovering ? 'text-opacity-90' : 'text-opacity-50'" />
|
||||
<svg v-else viewBox="0 0 24 24" class="h-6 w-6 text-white text-opacity-50 animate-spin">
|
||||
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
|
||||
</svg>
|
||||
|
@ -12,7 +12,7 @@
|
||||
<template v-for="library in librariesFiltered">
|
||||
<li :key="library.id" class="text-gray-100 select-none relative py-2 cursor-pointer hover:bg-black-400" id="listbox-option-0" role="option" @click="selectLibrary(library)">
|
||||
<div class="flex items-center px-3">
|
||||
<widgets-library-icon :icon="currentLibraryIcon" class="mr-2" />
|
||||
<widgets-library-icon :icon="library.icon" class="mr-2" />
|
||||
<span class="font-normal block truncate font-sans text-sm">{{ library.name }}</span>
|
||||
</div>
|
||||
</li>
|
||||
|
104
client/components/ui/MediaTypePicker.vue
Normal file
104
client/components/ui/MediaTypePicker.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="relative w-full h-9" v-click-outside="clickOutsideObj">
|
||||
<p class="text-sm font-semibold">{{ label }}</p>
|
||||
|
||||
<button type="button" :disabled="disabled" class="relative h-full w-full border border-gray-600 rounded shadow-sm pl-3 pr-3 text-left focus:outline-none cursor-pointer bg-primary text-gray-100 hover:text-gray-200" aria-haspopup="listbox" aria-expanded="true" @click.stop.prevent="clickShowMenu">
|
||||
<span class="flex items-center">
|
||||
<widgets-library-icon :icon="selected" class="mr-2" />
|
||||
<span class="block truncate text-sm">{{ selectedName }}</span>
|
||||
</span>
|
||||
<span class="ml-3 absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<span class="material-icons text-gray-100">expand_more</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<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 focus:outline-none sm:text-sm" tabindex="-1" role="listbox">
|
||||
<template v-for="type in types">
|
||||
<li :key="type.id" class="text-gray-100 select-none relative py-2 cursor-pointer hover:bg-black-400" id="listbox-option-0" role="option" @click="select(type)">
|
||||
<div class="flex items-center px-3">
|
||||
<widgets-library-icon :icon="type.id" class="mr-2" />
|
||||
<span class="font-normal block truncate font-sans text-sm">{{ type.name }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: String,
|
||||
disabled: Boolean,
|
||||
label: {
|
||||
type: String,
|
||||
default: 'Media Type'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
clickOutsideObj: {
|
||||
handler: this.clickedOutside,
|
||||
events: ['mousedown'],
|
||||
isActive: true
|
||||
},
|
||||
showMenu: false,
|
||||
types: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default'
|
||||
},
|
||||
{
|
||||
id: 'audiobooks',
|
||||
name: 'Audiobooks'
|
||||
},
|
||||
{
|
||||
id: 'books',
|
||||
name: 'Books'
|
||||
},
|
||||
{
|
||||
id: 'podcasts',
|
||||
name: 'Podcasts'
|
||||
},
|
||||
{
|
||||
id: 'comics',
|
||||
name: 'Comics'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selected: {
|
||||
get() {
|
||||
return this.value || 'default'
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
},
|
||||
selectedItem() {
|
||||
return this.types.find((t) => t.id === this.selected)
|
||||
},
|
||||
selectedName() {
|
||||
return this.selectedItem ? this.selectedItem.name : 'Default'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickShowMenu() {
|
||||
if (this.disabled) return
|
||||
this.showMenu = !this.showMenu
|
||||
},
|
||||
clickedOutside() {
|
||||
this.showMenu = false
|
||||
},
|
||||
select(type) {
|
||||
if (this.disabled) return
|
||||
this.selected = type.id
|
||||
this.showMenu = false
|
||||
}
|
||||
},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
@ -1,20 +1,27 @@
|
||||
<template>
|
||||
<div class="h-4 w-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
|
||||
</svg>
|
||||
<div :class="`h-${size} w-${size}`">
|
||||
<component :is="iconComponentName" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
icon: String
|
||||
icon: String,
|
||||
size: {
|
||||
type: Number,
|
||||
default: 5
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {},
|
||||
computed: {
|
||||
iconComponentName() {
|
||||
if (this.icon === 'default') return `icons-database-svg`
|
||||
return `icons-${this.icon}-svg`
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
mounted() {}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ class Library {
|
||||
this.folders = []
|
||||
this.displayOrder = 1
|
||||
this.icon = 'database'
|
||||
this.mediaType = 'default'
|
||||
this.provider = 'google'
|
||||
this.disableWatcher = false
|
||||
|
||||
@ -31,6 +32,7 @@ class Library {
|
||||
this.folders = (library.folders || []).map(f => new Folder(f))
|
||||
this.displayOrder = library.displayOrder || 1
|
||||
this.icon = library.icon || 'database'
|
||||
this.mediaType = library.mediaType || 'default'
|
||||
this.provider = library.provider || 'google'
|
||||
this.disableWatcher = !!library.disableWatcher
|
||||
|
||||
@ -45,6 +47,7 @@ class Library {
|
||||
folders: (this.folders || []).map(f => f.toJSON()),
|
||||
displayOrder: this.displayOrder,
|
||||
icon: this.icon,
|
||||
mediaType: this.mediaType,
|
||||
provider: this.provider,
|
||||
disableWatcher: this.disableWatcher,
|
||||
createdAt: this.createdAt,
|
||||
@ -71,6 +74,7 @@ class Library {
|
||||
}
|
||||
this.displayOrder = data.displayOrder || 1
|
||||
this.icon = data.icon || 'database'
|
||||
this.mediaType = data.mediaType || 'default'
|
||||
this.disableWatcher = !!data.disableWatcher
|
||||
this.createdAt = Date.now()
|
||||
this.lastUpdate = Date.now()
|
||||
@ -86,6 +90,15 @@ class Library {
|
||||
this.provider = payload.provider
|
||||
hasUpdates = true
|
||||
}
|
||||
if (payload.mediaType && payload.mediaType !== this.mediaType) {
|
||||
this.mediaType = payload.mediaType
|
||||
hasUpdates = true
|
||||
}
|
||||
if (payload.icon && payload.icon !== this.icon) {
|
||||
this.icon = payload.icon
|
||||
hasUpdates = true
|
||||
}
|
||||
|
||||
if (payload.disableWatcher !== this.disableWatcher) {
|
||||
this.disableWatcher = !!payload.disableWatcher
|
||||
hasUpdates = true
|
||||
|
Loading…
Reference in New Issue
Block a user