Bookshelf cover size setting and widget

This commit is contained in:
Mark Cooper 2021-08-26 09:47:51 -05:00
parent 46d7c45ca5
commit e54535f465
11 changed files with 90 additions and 30 deletions

View File

@ -67,6 +67,14 @@
font-size: 1.1rem;
}
#ab-page-wrapper {
#page-wrapper {
background-image: linear-gradient(to right bottom, #2e2e2e, #303030, #313131, #333333, #353535, #343434, #323232, #313131, #2c2c2c, #282828, #232323, #1f1f1f);
}
.box-shadow-md {
box-shadow: 2px 8px 6px #111111aa;
}
.box-shadow-xl {
box-shadow: 2px 14px 8px #111111aa;
}

View File

@ -1,5 +1,14 @@
<template>
<div id="bookshelf" ref="wrapper" class="w-full h-full overflow-y-auto">
<div id="bookshelf" ref="wrapper" class="w-full h-full overflow-y-auto relative">
<!-- Cover size widget -->
<div class="fixed bottom-2 right-4 z-20">
<div class="rounded-full py-1 bg-primary px-2 border border-black-100 text-center flex items-center box-shadow-md" @mousedown.prevent @mouseup.prevent>
<span class="material-icons" :class="selectedSizeIndex === 0 ? 'text-gray-400' : 'hover:text-yellow-300 cursor-pointer'" style="font-size: 0.9rem" @mousedown.prevent @click="decreaseSize">remove</span>
<p class="px-2 font-mono">{{ bookCoverWidth }}</p>
<span class="material-icons" :class="selectedSizeIndex === availableSizes.length - 1 ? 'text-gray-400' : 'hover:text-yellow-300 cursor-pointer'" style="font-size: 0.9rem" @mousedown.prevent @click="increaseSize">add</span>
</div>
</div>
<div v-if="!audiobooks.length" class="w-full flex flex-col items-center justify-center py-12">
<p class="text-center text-2xl font-book mb-4">Your Audiobookshelf is empty!</p>
<ui-btn color="success" @click="scan">Scan your Audiobooks</ui-btn>
@ -9,7 +18,7 @@
<div :key="index" class="w-full bookshelfRow relative">
<div class="flex justify-center items-center">
<template v-for="audiobook in shelf">
<cards-book-card :ref="`audiobookCard-${audiobook.id}`" :key="audiobook.id" :user-progress="userAudiobooks[audiobook.id]" :audiobook="audiobook" />
<cards-book-card :ref="`audiobookCard-${audiobook.id}`" :key="audiobook.id" :width="bookCoverWidth" :user-progress="userAudiobooks[audiobook.id]" :audiobook="audiobook" />
</template>
</div>
<div class="bookshelfDivider h-4 w-full absolute bottom-0 left-0 right-0 z-10" />
@ -24,10 +33,12 @@ export default {
data() {
return {
width: 0,
bookWidth: 176,
booksPerRow: 0,
groupedBooks: [],
currFilterOrderKey: null
currFilterOrderKey: null,
availableSizes: [60, 80, 100, 120, 140, 160, 180, 200, 220],
selectedSizeIndex: 3,
rowPaddingX: 40
}
},
computed: {
@ -39,9 +50,31 @@ export default {
},
filterOrderKey() {
return this.$store.getters['user/getFilterOrderKey']
},
bookCoverWidth() {
return this.availableSizes[this.selectedSizeIndex]
},
sizeMultiplier() {
return this.bookCoverWidth / 120
},
paddingX() {
return 16 * this.sizeMultiplier
},
bookWidth() {
return this.bookCoverWidth + this.paddingX * 2
}
},
methods: {
increaseSize() {
this.selectedSizeIndex = Math.min(this.availableSizes.length - 1, this.selectedSizeIndex + 1)
this.resize()
this.$store.dispatch('user/updateUserSettings', { bookshelfCoverSize: this.bookCoverWidth })
},
decreaseSize() {
this.selectedSizeIndex = Math.max(0, this.selectedSizeIndex - 1)
this.resize()
this.$store.dispatch('user/updateUserSettings', { bookshelfCoverSize: this.bookCoverWidth })
},
setGroupedBooks() {
var groups = []
var currentRow = 0
@ -66,6 +99,7 @@ export default {
},
calculateBookshelf() {
this.width = this.$refs.wrapper.clientWidth
this.width = Math.max(0, this.width - this.rowPaddingX * 2)
var booksPerRow = Math.floor(this.width / this.bookWidth)
this.booksPerRow = booksPerRow
},
@ -76,6 +110,7 @@ export default {
return null
},
init() {
this.selectedSizeIndex = this.$store.getters['user/getUserSetting']('bookshelfCoverSize')
this.calculateBookshelf()
},
resize() {
@ -88,11 +123,17 @@ export default {
console.log('[AudioBookshelf] Audiobooks Updated')
this.setGroupedBooks()
},
settingsUpdated() {
// var newSortKey = `${this.orderBy}-${this.orderDesc}`
settingsUpdated(settings) {
if (this.currFilterOrderKey !== this.filterOrderKey) {
this.setGroupedBooks()
}
if (settings.bookshelfCoverSize !== this.bookCoverWidth && settings.bookshelfCoverSize !== undefined) {
var index = this.availableSizes.indexOf(settings.bookshelfCoverSize)
if (index >= 0) {
this.selectedSizeIndex = index
this.resize()
}
}
},
scan() {
this.$root.socket.emit('scan')

View File

@ -1,24 +1,24 @@
<template>
<nuxt-link :to="`/audiobook/${audiobookId}`" :style="{ height: height + 32 + 'px', width: width + 32 + 'px' }" class="cursor-pointer p-4">
<nuxt-link :to="`/audiobook/${audiobookId}`" :style="{ padding: `16px ${paddingX}px` }" class="cursor-pointer">
<div class="rounded-sm h-full overflow-hidden relative bookCard" @mouseover="isHovering = true" @mouseleave="isHovering = false">
<div class="w-full relative" :style="{ height: width * 1.6 + 'px' }">
<cards-book-cover :audiobook="audiobook" :author-override="authorFormat" />
<div class="w-full relative" :style="{ height: height + 'px' }">
<cards-book-cover :audiobook="audiobook" :author-override="authorFormat" :width="width" />
<div v-show="isHovering" class="absolute top-0 left-0 w-full h-full bg-black bg-opacity-40">
<div class="h-full flex items-center justify-center">
<div class="hover:text-gray-200 hover:scale-110 transform duration-200" @click.stop.prevent="play">
<span class="material-icons text-5xl">play_circle_filled</span>
<span class="material-icons" :style="{ fontSize: playIconFontSize + 'rem' }">play_circle_filled</span>
</div>
</div>
<div class="absolute top-1.5 right-1.5 cursor-pointer hover:text-yellow-300 hover:scale-125 transform duration-50" @click.stop.prevent="editClick">
<span class="material-icons" style="font-size: 16px">edit</span>
<div class="absolute cursor-pointer hover:text-yellow-300 hover:scale-125 transform duration-50" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem' }" @click.stop.prevent="editClick">
<span class="material-icons" :style="{ fontSize: sizeMultiplier + 'rem' }">edit</span>
</div>
</div>
<div class="absolute bottom-0 left-0 h-1 bg-yellow-400 shadow-sm" :style="{ width: width * userProgressPercent + 'px' }"></div>
</div>
<ui-tooltip v-if="showError" :text="errorText" class="absolute top-4 left-0">
<div class="h-6 w-10 bg-error rounded-r-full shadow-md flex items-center justify-end border-r border-b border-red-300">
<span class="material-icons text-sm text-red-100 pr-1">priority_high</span>
<div :style="{ height: 1.5 * sizeMultiplier + 'rem', width: 2.5 * sizeMultiplier + 'rem' }" class="bg-error rounded-r-full shadow-md flex items-center justify-end border-r border-b border-red-300">
<span class="material-icons text-red-100 pr-1" :style="{ fontSize: 0.875 * sizeMultiplier + 'rem' }">priority_high</span>
</div>
</ui-tooltip>
</div>
@ -35,6 +35,10 @@ export default {
userProgress: {
type: Object,
default: () => null
},
width: {
type: Number,
default: 120
}
},
data() {
@ -49,15 +53,21 @@ export default {
book() {
return this.audiobook.book || {}
},
width() {
return 120
},
height() {
return this.width * 1.6
},
sizeMultiplier() {
return this.width / 120
},
paddingX() {
return 16 * this.sizeMultiplier
},
title() {
return this.book.title
},
playIconFontSize() {
return Math.max(2, 3 * this.sizeMultiplier)
},
author() {
return this.book.author
},

View File

@ -1,7 +1,9 @@
<template>
<div class="relative rounded-sm overflow-hidden" :style="{ height: width * 1.6 + 'px', width: width + 'px', maxWidth: width + 'px', minWidth: width + 'px' }">
<div class="w-full h-full bg-bg relative">
<div v-if="showCoverBg" class="absolute top-0 left-0 w-full h-full z-0" ref="coverBg" />
<div class="w-full h-full relative">
<div v-if="showCoverBg" class="bg-primary absolute top-0 left-0 w-full h-full">
<div class="w-full h-full z-0" ref="coverBg" />
</div>
<img ref="cover" :src="cover" @error="imageError" @load="imageLoaded" class="w-full h-full absolute top-0 left-0" :class="showCoverBg ? 'object-contain' : 'object-cover'" />
</div>
@ -24,8 +26,6 @@
</template>
<script>
import Path from 'path'
export default {
props: {
audiobook: {
@ -85,7 +85,6 @@ export default {
console.error(err)
return ''
}
return `${process.env.serverUrl}/${Path.normalize(this.cover)}`
},
cover() {
return this.book.cover || this.placeholderUrl

View File

@ -1,6 +1,6 @@
{
"name": "audiobookshelf-client",
"version": "0.9.80-beta",
"version": "0.9.82-beta",
"description": "Audiobook manager and player",
"main": "index.js",
"scripts": {

View File

@ -1,5 +1,5 @@
<template>
<div id="ab-page-wrapper" class="bg-bg page overflow-hidden relative" :class="streamAudiobook ? 'streaming' : ''">
<div id="page-wrapper" class="bg-bg page overflow-hidden relative" :class="streamAudiobook ? 'streaming' : ''">
<div v-show="saving" class="absolute z-20 w-full h-full flex items-center justify-center">
<ui-loading-indicator />
</div>

View File

@ -1,5 +1,5 @@
<template>
<div id="ab-page-wrapper" class="bg-bg page overflow-hidden" :class="streamAudiobook ? 'streaming' : ''">
<div id="page-wrapper" class="bg-bg page overflow-hidden" :class="streamAudiobook ? 'streaming' : ''">
<div class="w-full h-full overflow-y-auto p-8">
<div class="flex max-w-6xl mx-auto">
<div class="w-52" style="min-width: 208px">

View File

@ -1,5 +1,5 @@
<template>
<div class="page p-6" :class="streamAudiobook ? 'streaming' : ''">
<div id="page-wrapper" class="page p-6" :class="streamAudiobook ? 'streaming' : ''">
<div class="w-full max-w-4xl mx-auto">
<div class="flex items-center mb-2">
<h1 class="text-2xl">Users</h1>

View File

@ -5,7 +5,8 @@ export const state = () => ({
orderBy: 'book.title',
orderDesc: false,
filterBy: 'all',
playbackRate: 1
playbackRate: 1,
bookshelfCoverSize: 120
},
settingsListeners: []
})

View File

@ -1,6 +1,6 @@
{
"name": "audiobookshelf",
"version": "0.9.80-beta",
"version": "0.9.82-beta",
"description": "Self-hosted audiobook server for managing and playing audiobooks.",
"main": "index.js",
"scripts": {

View File

@ -20,7 +20,8 @@ class User {
orderBy: 'book.title',
orderDesc: false,
filterBy: 'all',
playbackRate: 1
playbackRate: 1,
bookshelfCoverSize: 120
}
}