mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-26 21:34:05 +02:00
init
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="w-full h-full p-4">
|
||||
<div class="w-full max-w-xs mx-auto">
|
||||
<ui-text-input-with-label :value="serverUrl" label="Server Url" disabled class="my-4" />
|
||||
|
||||
<ui-text-input-with-label :value="username" label="Username" disabled class="my-4" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center pt-4">
|
||||
<div class="flex-grow" />
|
||||
<p class="pr-2 text-sm font-book text-yellow-400">Report bugs, request features, provide feedback, and contribute on <a class="underline" href="https://github.com/advplyr/audiobookshelf" target="_blank">github</a>.</p>
|
||||
<a href="https://github.com/advplyr/audiobookshelf" target="_blank" class="text-white hover:text-gray-200 hover:scale-150 hover:rotate-6 transform duration-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<p class="font-mono pt-1 pb-4">v{{ $config.version }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
username() {
|
||||
return this.user.username
|
||||
},
|
||||
user() {
|
||||
return this.$store.state.user.user
|
||||
},
|
||||
serverUrl() {
|
||||
return this.$server.url
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="w-full h-full px-3 py-4 overflow-y-auto">
|
||||
<div class="flex">
|
||||
<div class="w-32">
|
||||
<cards-book-cover :audiobook="audiobook" :width="128" />
|
||||
</div>
|
||||
<div class="flex-grow px-3">
|
||||
<h1 class="text-lg">{{ title }}</h1>
|
||||
<h3 v-if="series" class="font-book text-gray-300 text-lg leading-7">{{ seriesText }}</h3>
|
||||
<p class="text-sm text-gray-400">by {{ author }}</p>
|
||||
<p class="text-gray-300 text-sm my-1">
|
||||
{{ durationPretty }}<span class="px-4">{{ sizePretty }}</span>
|
||||
</p>
|
||||
<ui-btn color="success" class="flex items-center justify-center w-full mt-2" :padding-x="4" @click="playClick">
|
||||
<span class="material-icons">play_arrow</span>
|
||||
<span class="px-1">Play</span>
|
||||
</ui-btn>
|
||||
<div class="flex my-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full py-4">
|
||||
<p>{{ description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
async asyncData({ params, redirect, app }) {
|
||||
var audiobookId = params.id
|
||||
var audiobook = await app.$axios.$get(`/api/audiobook/${audiobookId}`).catch((error) => {
|
||||
console.error('Failed', error)
|
||||
return false
|
||||
})
|
||||
if (!audiobook) {
|
||||
console.error('No audiobook...', params.id)
|
||||
return redirect('/')
|
||||
}
|
||||
return {
|
||||
audiobook
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
book() {
|
||||
return this.audiobook.book || {}
|
||||
},
|
||||
title() {
|
||||
return this.book.title
|
||||
},
|
||||
author() {
|
||||
return this.book.author || 'Unknown'
|
||||
},
|
||||
description() {
|
||||
return this.book.description || 'No Description'
|
||||
},
|
||||
series() {
|
||||
return this.book.series || null
|
||||
},
|
||||
volumeNumber() {
|
||||
return this.book.volumeNumber || null
|
||||
},
|
||||
seriesText() {
|
||||
if (!this.series) return ''
|
||||
if (!this.volumeNumber) return this.series
|
||||
return `${this.series} #${this.volumeNumber}`
|
||||
},
|
||||
durationPretty() {
|
||||
return this.audiobook.durationPretty
|
||||
},
|
||||
duration() {
|
||||
return this.audiobook.duration
|
||||
},
|
||||
sizePretty() {
|
||||
return this.audiobook.sizePretty
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
playClick() {
|
||||
this.$store.commit('setPlayOnLoad', true)
|
||||
this.$store.commit('setStreamAudiobook', this.audiobook)
|
||||
this.$server.socket.emit('open_stream', this.audiobook.id)
|
||||
}
|
||||
},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div class="w-full h-full">
|
||||
<div class="relative flex items-center justify-center min-h-screen sm:pt-0">
|
||||
<div class="absolute top-0 left-0 w-full p-6 flex items-center flex-col justify-center z-0 short:hidden">
|
||||
<img src="/Logo.png" class="h-20 w-20 mb-2" />
|
||||
<h1 class="text-2xl font-book">AudioBookshelf</h1>
|
||||
</div>
|
||||
<p class="hidden absolute short:block top-0 left-0 p-2 font-book text-xl">AudioBookshelf</p>
|
||||
|
||||
<div class="max-w-sm mx-auto sm:px-6 lg:px-8 z-10">
|
||||
<div v-show="loggedIn" class="mt-8 bg-primary overflow-hidden shadow rounded-lg p-6 text-center">
|
||||
<p class="text-success text-xl mb-2">Login Success!</p>
|
||||
<p>Connecting socket..</p>
|
||||
</div>
|
||||
<div v-show="!loggedIn" class="mt-8 bg-primary overflow-hidden shadow rounded-lg p-6">
|
||||
<h2 class="text-xl leading-7 mb-4">Enter an <span class="font-book font-normal">AudioBookshelf</span><br />server address:</h2>
|
||||
<form v-show="!showAuth" @submit.prevent="submit">
|
||||
<ui-text-input v-model="serverUrl" :disabled="processing" placeholder="http://55.55.55.55:13378" type="url" class="w-60 sm:w-72 h-10" />
|
||||
<ui-btn :disabled="processing" type="submit" :padding-x="3" class="h-10">Submit</ui-btn>
|
||||
</form>
|
||||
<template v-if="showAuth">
|
||||
<div class="flex items-center">
|
||||
<p class="">{{ serverUrl }}</p>
|
||||
<div class="flex-grow" />
|
||||
<span class="material-icons" style="font-size: 1.1rem" @click="editServerUrl">edit</span>
|
||||
</div>
|
||||
<div class="w-full h-px bg-gray-200 my-2" />
|
||||
<form @submit.prevent="submitAuth" class="pt-3">
|
||||
<ui-text-input v-model="username" :disabled="processing" placeholder="username" class="w-full my-1 text-lg" />
|
||||
<ui-text-input v-model="password" type="password" :disabled="processing" placeholder="password" class="w-full my-1 text-lg" />
|
||||
|
||||
<ui-btn :disabled="processing" type="submit" class="mt-1 h-10">Submit</ui-btn>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<div v-show="error" class="w-full rounded-lg bg-red-600 bg-opacity-10 border border-error border-opacity-50 py-3 px-2 flex items-center mt-4">
|
||||
<span class="material-icons mr-2 text-error" style="font-size: 1.1rem">warning</span>
|
||||
<p class="text-error">{{ error }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="processing ? 'opacity-100' : 'opacity-0 pointer-events-none'" class="fixed w-full h-full top-0 left-0 bg-black bg-opacity-75 flex items-center justify-center z-30 transition-opacity duration-500">
|
||||
<div>
|
||||
<div class="absolute top-0 left-0 w-full p-6 flex items-center flex-col justify-center z-0 short:hidden">
|
||||
<img src="/Logo.png" class="h-20 w-20 mb-2" />
|
||||
</div>
|
||||
<svg class="animate-spin w-16 h-16" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center pt-4 fixed bottom-4 left-0 right-0">
|
||||
<a href="https://github.com/advplyr/audiobookshelf" target="_blank" class="text-sm pr-2">Follow the project on Github</a>
|
||||
<a href="https://github.com/advplyr/audiobookshelf" target="_blank"
|
||||
><svg class="w-8 h-8 text-gray-100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12 2.247a10 10 0 0 0-3.162 19.487c.5.088.687-.212.687-.475c0-.237-.012-1.025-.012-1.862c-2.513.462-3.163-.613-3.363-1.175a3.636 3.636 0 0 0-1.025-1.413c-.35-.187-.85-.65-.013-.662a2.001 2.001 0 0 1 1.538 1.025a2.137 2.137 0 0 0 2.912.825a2.104 2.104 0 0 1 .638-1.338c-2.225-.25-4.55-1.112-4.55-4.937a3.892 3.892 0 0 1 1.025-2.688a3.594 3.594 0 0 1 .1-2.65s.837-.262 2.75 1.025a9.427 9.427 0 0 1 5 0c1.912-1.3 2.75-1.025 2.75-1.025a3.593 3.593 0 0 1 .1 2.65a3.869 3.869 0 0 1 1.025 2.688c0 3.837-2.338 4.687-4.563 4.937a2.368 2.368 0 0 1 .675 1.85c0 1.338-.012 2.413-.012 2.75c0 .263.187.575.687.475A10.005 10.005 0 0 0 12 2.247z"
|
||||
fill="currentColor"
|
||||
/></svg
|
||||
></a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
layout: 'blank',
|
||||
data() {
|
||||
return {
|
||||
serverUrl: null,
|
||||
processing: false,
|
||||
showAuth: false,
|
||||
username: null,
|
||||
password: null,
|
||||
error: null,
|
||||
loggedIn: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async submit() {
|
||||
this.processing = true
|
||||
this.error = null
|
||||
var success = await this.$server.check(this.serverUrl)
|
||||
this.processing = false
|
||||
if (!success) {
|
||||
console.error('Server invalid')
|
||||
this.error = 'Invalid Server'
|
||||
} else {
|
||||
this.showAuth = true
|
||||
}
|
||||
},
|
||||
async submitAuth() {
|
||||
if (!this.username) {
|
||||
this.error = 'Invalid username'
|
||||
return
|
||||
}
|
||||
this.error = null
|
||||
|
||||
this.processing = true
|
||||
var response = await this.$server.login(this.serverUrl, this.username, this.password)
|
||||
this.processing = false
|
||||
if (response.error) {
|
||||
console.error('Login failed')
|
||||
this.error = response.error
|
||||
} else {
|
||||
console.log('Login Success!')
|
||||
this.loggedIn = true
|
||||
}
|
||||
},
|
||||
editServerUrl() {
|
||||
this.error = null
|
||||
this.showAuth = false
|
||||
},
|
||||
redirect() {
|
||||
if (this.$route.query && this.$route.query.redirect) {
|
||||
this.$router.replace(this.$route.query.redirect)
|
||||
} else {
|
||||
this.$router.replace('/')
|
||||
}
|
||||
},
|
||||
socketConnected() {
|
||||
console.log('Socket connected')
|
||||
this.redirect()
|
||||
},
|
||||
async init() {
|
||||
if (!this.$server) {
|
||||
console.error('Invalid server not initialized')
|
||||
return
|
||||
}
|
||||
if (this.$server.connected) {
|
||||
console.warn('Server already connected')
|
||||
return this.redirect()
|
||||
}
|
||||
this.$server.on('connected', this.socketConnected)
|
||||
|
||||
var localServerUrl = localStorage.getItem('serverUrl')
|
||||
var localUserToken = localStorage.getItem('userToken')
|
||||
if (localServerUrl) {
|
||||
this.serverUrl = localServerUrl
|
||||
if (localUserToken) {
|
||||
this.processing = true
|
||||
var success = await this.$server.connect(localServerUrl, localUserToken)
|
||||
|
||||
if (!success && !this.$server.url) {
|
||||
this.processing = false
|
||||
this.serverUrl = null
|
||||
this.showAuth = false
|
||||
} else if (!success) {
|
||||
console.log('Server connect success')
|
||||
this.processing = false
|
||||
}
|
||||
this.showAuth = true
|
||||
} else {
|
||||
this.submit()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="w-full h-full">
|
||||
<div class="w-full h-12 relative z-20">
|
||||
<div id="toolbar" class="asolute top-0 left-0 w-full h-full bg-bg flex items-center px-2">
|
||||
<p class="font-book">{{ numAudiobooks }} Audiobooks</p>
|
||||
<div class="flex-grow" />
|
||||
<span class="material-icons px-2" @click="showFilterModal = true">filter_alt</span>
|
||||
<span class="material-icons px-2" @click="showSortModal = true">sort</span>
|
||||
</div>
|
||||
</div>
|
||||
<app-bookshelf />
|
||||
|
||||
<modals-order-modal v-model="showSortModal" :order-by.sync="settings.orderBy" :descending.sync="settings.orderDesc" @change="updateOrder" />
|
||||
<modals-filter-modal v-model="showFilterModal" :filter-by.sync="settings.filterBy" @change="updateFilter" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
showSortModal: false,
|
||||
showFilterModal: false,
|
||||
settings: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
numAudiobooks() {
|
||||
return this.$store.getters['audiobooks/getFiltered']().length
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateOrder() {
|
||||
this.saveSettings()
|
||||
},
|
||||
updateFilter() {
|
||||
this.saveSettings()
|
||||
},
|
||||
saveSettings() {
|
||||
this.$store.commit('user/setSettings', this.settings) // Immediate update
|
||||
this.$store.dispatch('user/updateUserSettings', this.settings)
|
||||
},
|
||||
init() {
|
||||
this.settings = { ...this.$store.state.user.settings }
|
||||
},
|
||||
settingsUpdated(settings) {
|
||||
for (const key in settings) {
|
||||
this.settings[key] = settings[key]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
this.$store.commit('user/addSettingsListener', { id: 'bookshelftoolbar', meth: this.settingsUpdated })
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$store.commit('user/removeSettingsListener', 'bookshelftoolbar')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#toolbar {
|
||||
box-shadow: 0px 5px 5px #11111155;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user