Pkg scripts win/linux

This commit is contained in:
Mark Cooper 2021-09-22 20:40:35 -05:00
parent 6cb418a871
commit 94741598af
12 changed files with 235 additions and 34 deletions

View File

@ -102,3 +102,7 @@
.box-shadow-book { .box-shadow-book {
box-shadow: 4px 1px 8px #11111166, -4px 1px 8px #11111166, 1px -4px 8px #11111166; box-shadow: 4px 1px 8px #11111166, -4px 1px 8px #11111166, 1px -4px 8px #11111166;
} }
.box-shadow-side {
box-shadow: 4px 0px 4px #11111166;
}

View File

@ -7,15 +7,16 @@
<span class="material-icons text-4xl text-white">arrow_back</span> <span class="material-icons text-4xl text-white">arrow_back</span>
</a> </a>
<h1 class="text-2xl font-book mr-6">AudioBookshelf</h1> <h1 class="text-2xl font-book mr-6">AudioBookshelf</h1>
<!-- <div class="-mb-2">
<h1 class="text-lg font-book leading-3 mr-6 px-1">AudioBookshelf</h1>
<div class="bg-black bg-opacity-20 rounded-sm py-1.5 px-2 mt-1.5 flex items-center justify-between border border-bg">
<p class="text-sm text-gray-400 leading-3">My Library</p>
<span class="material-icons text-sm leading-3 text-gray-400">expand_more</span>
</div>
</div> -->
<controls-global-search /> <controls-global-search />
<div class="flex-grow" /> <div class="flex-grow" />
<!-- <a v-if="isUpdateAvailable" :href="githubTagUrl" target="_blank" class="flex items-center rounded-full bg-warning p-2 text-sm">
<span class="material-icons">notification_important</span>
<span class="pl-2">Update is available! Check release notes for v{{ latestVersion }}</span>
</a> -->
<nuxt-link v-if="userCanUpload" to="/upload" class="outline-none hover:text-gray-200 cursor-pointer w-8 h-8 flex items-center justify-center"> <nuxt-link v-if="userCanUpload" to="/upload" class="outline-none hover:text-gray-200 cursor-pointer w-8 h-8 flex items-center justify-center">
<span class="material-icons">upload</span> <span class="material-icons">upload</span>
</nuxt-link> </nuxt-link>
@ -45,10 +46,8 @@
</ui-tooltip> </ui-tooltip>
<template v-if="userCanUpdate"> <template v-if="userCanUpdate">
<ui-icon-btn v-show="!processingBatchDelete" icon="edit" bg-color="warning" class="mx-1.5" @click="batchEditClick" /> <ui-icon-btn v-show="!processingBatchDelete" icon="edit" bg-color="warning" class="mx-1.5" @click="batchEditClick" />
<!-- <ui-btn v-show="!processingBatchDelete" color="warning" small class="mx-2 w-10 h-10" :padding-y="0" :padding-x="0" @click="batchEditClick"><span class="material-icons text-gray-200 text-base">edit</span></ui-btn> -->
</template> </template>
<ui-icon-btn v-show="userCanDelete" :disabled="processingBatchDelete" icon="delete" bg-color="error" class="mx-1.5" @click="batchDeleteClick" /> <ui-icon-btn v-show="userCanDelete" :disabled="processingBatchDelete" icon="delete" bg-color="error" class="mx-1.5" @click="batchDeleteClick" />
<!-- <ui-btn v-if="userCanDelete" color="error" small class="mx-2" :loading="processingBatchDelete" @click="batchDeleteClick"><span class="material-icons text-gray-200 pt-1">delete</span></ui-btn> -->
<span class="material-icons text-4xl px-4 hover:text-gray-100 cursor-pointer" :class="processingBatchDelete ? 'text-gray-400' : ''" @click="cancelSelectionMode">close</span> <span class="material-icons text-4xl px-4 hover:text-gray-100 cursor-pointer" :class="processingBatchDelete ? 'text-gray-400' : ''" @click="cancelSelectionMode">close</span>
</div> </div>
</div> </div>

View File

@ -17,17 +17,18 @@
</div> </div>
</div> </div>
<div v-else class="w-full flex flex-col items-center"> <div v-else class="w-full flex flex-col items-center">
<template v-for="(shelf, index) in groupedBooks"> <template v-for="(shelf, index) in entities">
<div :key="index" class="w-full bookshelfRow relative"> <div :key="index" class="w-full bookshelfRow relative">
<div class="flex justify-center items-center"> <div class="flex justify-center items-center">
<template v-for="audiobook in shelf"> <template v-for="entity in shelf">
<cards-book-card :ref="`audiobookCard-${audiobook.id}`" :key="audiobook.id" :width="bookCoverWidth" :user-progress="userAudiobooks[audiobook.id]" :audiobook="audiobook" /> <cards-group-card v-if="page !== ''" :key="entity.id" :width="bookCoverWidth" :group="entity" />
<cards-book-card v-else :key="entity.id" :width="bookCoverWidth" :user-progress="userAudiobooks[entity.id]" :audiobook="entity" />
</template> </template>
</div> </div>
<div class="bookshelfDivider h-4 w-full absolute bottom-0 left-0 right-0 z-10" /> <div class="bookshelfDivider h-4 w-full absolute bottom-0 left-0 right-0 z-10" />
</div> </div>
</template> </template>
<div v-show="!groupedBooks.length" class="w-full py-16 text-center text-xl"> <div v-show="!entities.length" class="w-full py-16 text-center text-xl">
<div class="py-4">No Audiobooks</div> <div class="py-4">No Audiobooks</div>
<ui-btn v-if="filterBy !== 'all' || keywordFilter" @click="clearFilter">Clear Filter</ui-btn> <ui-btn v-if="filterBy !== 'all' || keywordFilter" @click="clearFilter">Clear Filter</ui-btn>
</div> </div>
@ -37,11 +38,14 @@
<script> <script>
export default { export default {
props: {
page: String
},
data() { data() {
return { return {
width: 0, width: 0,
booksPerRow: 0, booksPerRow: 0,
groupedBooks: [], entities: [],
currFilterOrderKey: null, currFilterOrderKey: null,
availableSizes: [60, 80, 100, 120, 140, 160, 180, 200, 220], availableSizes: [60, 80, 100, 120, 140, 160, 180, 200, 220],
selectedSizeIndex: 3, selectedSizeIndex: 3,
@ -95,13 +99,13 @@ export default {
filterBy: 'all' filterBy: 'all'
}) })
} else { } else {
this.setGroupedBooks() this.setBookshelfEntities()
} }
}, },
checkKeywordFilter() { checkKeywordFilter() {
clearTimeout(this.keywordFilterTimeout) clearTimeout(this.keywordFilterTimeout)
this.keywordFilterTimeout = setTimeout(() => { this.keywordFilterTimeout = setTimeout(() => {
this.setGroupedBooks() this.setBookshelfEntities()
}, 500) }, 500)
}, },
increaseSize() { increaseSize() {
@ -114,27 +118,34 @@ export default {
this.resize() this.resize()
this.$store.dispatch('user/updateUserSettings', { bookshelfCoverSize: this.bookCoverWidth }) this.$store.dispatch('user/updateUserSettings', { bookshelfCoverSize: this.bookCoverWidth })
}, },
setGroupedBooks() { setBookshelfEntities() {
if (this.page === '') {
var audiobooksSorted = this.$store.getters['audiobooks/getFilteredAndSorted']()
this.currFilterOrderKey = this.filterOrderKey
this.setGroupedBooks(audiobooksSorted)
} else {
var entities = this.$store.getters['audiobooks/getSeriesGroups']()
this.setGroupedBooks(entities)
}
},
setGroupedBooks(entities) {
var groups = [] var groups = []
var currentRow = 0 var currentRow = 0
var currentGroup = [] var currentGroup = []
var audiobooksSorted = this.$store.getters['audiobooks/getFilteredAndSorted']() for (let i = 0; i < entities.length; i++) {
this.currFilterOrderKey = this.filterOrderKey
for (let i = 0; i < audiobooksSorted.length; i++) {
var row = Math.floor(i / this.booksPerRow) var row = Math.floor(i / this.booksPerRow)
if (row > currentRow) { if (row > currentRow) {
groups.push([...currentGroup]) groups.push([...currentGroup])
currentRow = row currentRow = row
currentGroup = [] currentGroup = []
} }
currentGroup.push(audiobooksSorted[i]) currentGroup.push(entities[i])
} }
if (currentGroup.length) { if (currentGroup.length) {
groups.push([...currentGroup]) groups.push([...currentGroup])
} }
this.groupedBooks = groups this.entities = groups
}, },
calculateBookshelf() { calculateBookshelf() {
this.width = this.$refs.wrapper.clientWidth this.width = this.$refs.wrapper.clientWidth
@ -142,12 +153,6 @@ export default {
var booksPerRow = Math.floor(this.width / this.bookWidth) var booksPerRow = Math.floor(this.width / this.bookWidth)
this.booksPerRow = booksPerRow this.booksPerRow = booksPerRow
}, },
getAudiobookCard(id) {
if (this.$refs[`audiobookCard-${id}`] && this.$refs[`audiobookCard-${id}`].length) {
return this.$refs[`audiobookCard-${id}`][0]
}
return null
},
init() { init() {
var bookshelfCoverSize = this.$store.getters['user/getUserSetting']('bookshelfCoverSize') var bookshelfCoverSize = this.$store.getters['user/getUserSetting']('bookshelfCoverSize')
var sizeIndex = this.availableSizes.findIndex((s) => s === bookshelfCoverSize) var sizeIndex = this.availableSizes.findIndex((s) => s === bookshelfCoverSize)
@ -157,16 +162,16 @@ export default {
resize() { resize() {
this.$nextTick(() => { this.$nextTick(() => {
this.calculateBookshelf() this.calculateBookshelf()
this.setGroupedBooks() this.setBookshelfEntities()
}) })
}, },
audiobooksUpdated() { audiobooksUpdated() {
console.log('[AudioBookshelf] Audiobooks Updated') console.log('[AudioBookshelf] Audiobooks Updated')
this.setGroupedBooks() this.setBookshelfEntities()
}, },
settingsUpdated(settings) { settingsUpdated(settings) {
if (this.currFilterOrderKey !== this.filterOrderKey) { if (this.currFilterOrderKey !== this.filterOrderKey) {
this.setGroupedBooks() this.setBookshelfEntities()
} }
if (settings.bookshelfCoverSize !== this.bookCoverWidth && settings.bookshelfCoverSize !== undefined) { if (settings.bookshelfCoverSize !== this.bookCoverWidth && settings.bookshelfCoverSize !== undefined) {
var index = this.availableSizes.indexOf(settings.bookshelfCoverSize) var index = this.availableSizes.indexOf(settings.bookshelfCoverSize)

View File

@ -0,0 +1,71 @@
<template>
<div class="w-20 border-r border-primary bg-bg h-full relative box-shadow-side z-20">
<nuxt-link to="/library" class="w-full h-20 flex flex-col items-center justify-center text-white border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="paramId === '' ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
</svg>
<p class="font-book pt-1.5" style="font-size: 0.8rem">Library</p>
<div v-show="paramId === ''" class="h-0.5 w-full bg-yellow-400 absolute bottom-0 left-0" />
</nuxt-link>
<nuxt-link to="/library/series" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="paramId === 'series' ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2" />
</svg>
<p class="font-book pt-1.5" style="font-size: 0.8rem">Series</p>
<div v-show="paramId === 'series'" class="h-0.5 w-full bg-yellow-400 absolute bottom-0 left-0" />
</nuxt-link>
<nuxt-link to="/library/collections" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="paramId === 'collections' ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
<p class="font-book pt-1.5" style="font-size: 0.8rem">Collections</p>
<div v-show="paramId === 'collections'" class="h-0.5 w-full bg-yellow-400 absolute bottom-0 left-0" />
</nuxt-link>
<nuxt-link to="/library/tags" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="paramId === 'tags' ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
</svg>
<p class="font-book pt-1.5" style="font-size: 0.8rem">Tags</p>
<div v-show="paramId === 'tags'" class="h-0.5 w-full bg-yellow-400 absolute bottom-0 left-0" />
</nuxt-link>
<nuxt-link to="/library/authors" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="paramId === 'authors' ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
<p class="font-book pt-1.5" style="font-size: 0.8rem">Authors</p>
<div v-show="paramId === 'authors'" class="h-0.5 w-full bg-yellow-400 absolute bottom-0 left-0" />
</nuxt-link>
</div>
</template>
<script>
export default {
data() {
return {}
},
computed: {
paramId() {
return this.$route.params ? this.$route.params.id || '' : ''
},
selectedClassName() {
return ''
}
},
methods: {},
mounted() {}
}
</script>

View File

@ -0,0 +1,56 @@
<template>
<div class="relative">
<div class="rounded-sm h-full overflow-hidden relative" :style="{ padding: `16px ${paddingX}px` }" @mouseover="isHovering = true" @mouseleave="isHovering = false" @click="clickCard">
<nuxt-link :to="`/library`" class="cursor-pointer">
<div class="w-full relative box-shadow-book bg-primary" :style="{ height: height + 'px', width: height + 'px' }"></div>
</nuxt-link>
</div>
<!-- <div :style="{ width: height + 'px', height: height + 'px' }" class="box-shadow-book bg-primary">
<p class="text-white">{{ groupName }}</p>
</div> -->
</div>
</template>
<script>
export default {
props: {
group: {
type: Object,
default: () => null
},
width: {
type: Number,
default: 120
}
},
data() {
return {
isHovering: false
}
},
computed: {
_group() {
return this.group || {}
},
height() {
return this.width * 1.6
},
sizeMultiplier() {
return this.width / 120
},
paddingX() {
return 16 * this.sizeMultiplier
},
books() {
return this._group.books || []
},
groupName() {
return this._group.name || 'No Name'
}
},
methods: {
clickCard() {}
},
mounted() {}
}
</script>

View File

@ -1,7 +1,9 @@
<template> <template>
<div class="text-white max-h-screen h-screen overflow-hidden bg-bg"> <div class="text-white max-h-screen h-screen overflow-hidden bg-bg">
<app-appbar /> <app-appbar />
<Nuxt /> <Nuxt />
<app-stream-container ref="streamContainer" /> <app-stream-container ref="streamContainer" />
<modals-edit-modal /> <modals-edit-modal />
<widgets-scan-alert /> <widgets-scan-alert />

View File

@ -1,7 +1,12 @@
<template> <template>
<div class="page" :class="streamAudiobook ? 'streaming' : ''"> <div class="page" :class="streamAudiobook ? 'streaming' : ''">
<app-book-shelf-toolbar /> <app-book-shelf-toolbar />
<!-- <div class="flex h-full">
<app-side-rail />
<div class="flex-grow"> -->
<app-book-shelf /> <app-book-shelf />
<!-- </div> -->
<!-- </div> -->
</div> </div>
</template> </template>

View File

@ -0,0 +1,31 @@
<template>
<div class="page" :class="streamAudiobook ? 'streaming' : ''">
<div class="flex h-full">
<app-side-rail />
<div class="flex-grow">
<app-book-shelf-toolbar />
<app-book-shelf :page="id || ''" />
</div>
</div>
</div>
</template>
<script>
export default {
asyncData({ params }) {
return {
id: params.id
}
},
data() {
return {}
},
computed: {
streamAudiobook() {
return this.$store.state.streamAudiobook
}
},
methods: {},
mounted() {}
}
</script>

View File

@ -63,6 +63,23 @@ export const getters = {
return value return value
}) })
}, },
getSeriesGroups: (state, getters, rootState) => () => {
var series = {}
state.audiobooks.forEach((audiobook) => {
if (audiobook.book && audiobook.book.series) {
if (series[audiobook.book.series]) {
series[audiobook.book.series].books.push(audiobook)
} else {
series[audiobook.book.series] = {
type: 'series',
name: audiobook.book.series,
books: [audiobook]
}
}
}
})
return Object.values(series)
},
getUniqueAuthors: (state) => { getUniqueAuthors: (state) => {
var _authors = state.audiobooks.filter(ab => !!(ab.book && ab.book.author)).map(ab => ab.book.author) var _authors = state.audiobooks.filter(ab => !!(ab.book && ab.book.author)).map(ab => ab.book.author)
return [...new Set(_authors)].sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1) return [...new Set(_authors)].sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1)

View File

@ -7,7 +7,17 @@
"dev": "node index.js", "dev": "node index.js",
"start": "node index.js", "start": "node index.js",
"client": "cd client && npm install && npm run generate", "client": "cd client && npm install && npm run generate",
"prod": "npm run client && npm install && node prod.js" "prod": "npm run client && npm install && node prod.js",
"build-win": "cd client && npm run generate && cd .. && pkg -t node12-win-x64 -o ./dist/app .",
"build-linux": "pkg -t node12-linux-arm64 -o ./dist/app ."
},
"bin": "prod.js",
"pkg": {
"assets": "client/dist/**/*",
"scripts": [
"prod.js",
"server/**/*.js"
]
}, },
"author": "advplyr", "author": "advplyr",
"license": "ISC", "license": "ISC",

View File

@ -13,6 +13,7 @@ process.env.TOKEN_SECRET = '09f26e402586e2faa8da4c98a35f1b20d6b033c6097befa8be34
process.env.NODE_ENV = 'production' process.env.NODE_ENV = 'production'
const server = require('./server/Server') const server = require('./server/Server')
global.appRoot = __dirname global.appRoot = __dirname
var inputConfig = options.config ? Path.resolve(options.config) : null var inputConfig = options.config ? Path.resolve(options.config) : null
@ -24,7 +25,7 @@ const CONFIG_PATH = inputConfig || process.env.CONFIG_PATH || Path.resolve('conf
const AUDIOBOOK_PATH = inputAudiobook || process.env.AUDIOBOOK_PATH || Path.resolve('audiobooks') const AUDIOBOOK_PATH = inputAudiobook || process.env.AUDIOBOOK_PATH || Path.resolve('audiobooks')
const METADATA_PATH = inputMetadata || process.env.METADATA_PATH || Path.resolve('metadata') const METADATA_PATH = inputMetadata || process.env.METADATA_PATH || Path.resolve('metadata')
console.log('Config', CONFIG_PATH, METADATA_PATH, AUDIOBOOK_PATH) console.log(process.env.NODE_ENV, 'Config', CONFIG_PATH, METADATA_PATH, AUDIOBOOK_PATH)
const Server = new server(PORT, CONFIG_PATH, METADATA_PATH, AUDIOBOOK_PATH) const Server = new server(PORT, CONFIG_PATH, METADATA_PATH, AUDIOBOOK_PATH)
Server.start() Server.start()

View File

@ -113,6 +113,7 @@ class Server {
await this.streamManager.ensureStreamsDir() await this.streamManager.ensureStreamsDir()
await this.streamManager.removeOrphanStreams() await this.streamManager.removeOrphanStreams()
await this.downloadManager.removeOrphanDownloads() await this.downloadManager.removeOrphanDownloads()
await this.db.init() await this.db.init()
this.auth.init() this.auth.init()
@ -171,7 +172,6 @@ class Server {
async start() { async start() {
Logger.info('=== Starting Server ===') Logger.info('=== Starting Server ===')
await this.init() await this.init()
const app = express() const app = express()