mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-08 04:48:36 +02:00
Add:iOS audio player
This commit is contained in:
@@ -15,7 +15,8 @@
|
||||
<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 v-if="numTracks" class="text-gray-300 text-sm my-1">
|
||||
{{ $elapsedPretty(duration) }}<span class="px-4">{{ $bytesPretty(size) }}</span>
|
||||
{{ $elapsedPretty(duration) }}
|
||||
<span class="px-4">{{ $bytesPretty(size) }}</span>
|
||||
</p>
|
||||
|
||||
<div v-if="progressPercent > 0" class="px-4 py-2 bg-primary text-sm font-semibold rounded-md text-gray-200 mt-4 relative" :class="resettingProgress ? 'opacity-25' : ''">
|
||||
@@ -35,7 +36,7 @@
|
||||
<span class="material-icons">auto_stories</span>
|
||||
<span v-if="!showPlay" class="px-2 text-base">Read {{ ebookFormat }}</span>
|
||||
</ui-btn>
|
||||
<ui-btn v-if="isConnected && showPlay" color="primary" class="flex items-center justify-center" :padding-x="2" @click="downloadClick">
|
||||
<ui-btn v-if="isConnected && showPlay && !isIos" color="primary" class="flex items-center justify-center" :padding-x="2" @click="downloadClick">
|
||||
<span class="material-icons" :class="downloadObj ? 'animate-pulse' : ''">{{ downloadObj ? (isDownloading || isDownloadPreparing ? 'downloading' : 'download_done') : 'download' }}</span>
|
||||
</ui-btn>
|
||||
</div>
|
||||
@@ -84,6 +85,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isIos() {
|
||||
return this.$platform === 'ios'
|
||||
},
|
||||
isConnected() {
|
||||
return this.$store.state.socketConnected
|
||||
},
|
||||
|
||||
+3
-1
@@ -7,7 +7,7 @@
|
||||
|
||||
<!-- <div v-if="isLoading" class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
|
||||
<ui-loading-indicator />
|
||||
</div> -->
|
||||
</div>-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -27,11 +27,13 @@ export default {
|
||||
|
||||
<style>
|
||||
.main-content {
|
||||
height: calc(100% - 72px);
|
||||
max-height: calc(100% - 72px);
|
||||
min-height: calc(100% - 72px);
|
||||
max-width: 100vw;
|
||||
}
|
||||
.main-content.home-page {
|
||||
height: calc(100% - 36px);
|
||||
max-height: calc(100% - 36px);
|
||||
min-height: calc(100% - 36px);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full">
|
||||
<div class="w-full h-full min-h-full relative">
|
||||
<template v-for="(shelf, index) in shelves">
|
||||
<bookshelf-shelf :key="shelf.id" :label="shelf.label" :entities="shelf.entities" :type="shelf.type" :style="{ zIndex: shelves.length - index }" />
|
||||
</template>
|
||||
@@ -7,19 +7,24 @@
|
||||
<div v-if="!shelves.length" class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
|
||||
<div>
|
||||
<p class="mb-4 text-center text-xl">
|
||||
Bookshelf empty<span v-show="isSocketConnected">
|
||||
for library <strong>{{ currentLibraryName }}</strong></span
|
||||
>
|
||||
Bookshelf empty
|
||||
<span v-show="isSocketConnected">
|
||||
for library
|
||||
<strong>{{ currentLibraryName }}</strong>
|
||||
</span>
|
||||
</p>
|
||||
<div class="w-full" v-if="!isSocketConnected">
|
||||
<div class="flex justify-center items-center mb-3">
|
||||
<span class="material-icons text-error text-lg">cloud_off</span>
|
||||
<p class="pl-2 text-error text-sm">Audiobookshelf server not connected.</p>
|
||||
</div>
|
||||
<p class="px-4 text-center text-error absolute bottom-12 left-0 right-0 mx-auto"><strong>Important!</strong> This app requires that you are running <u>your own server</u> and does not provide any content.</p>
|
||||
<p class="px-4 text-center text-error absolute bottom-12 left-0 right-0 mx-auto">
|
||||
<strong>Important!</strong> This app requires that you are running
|
||||
<u>your own server</u> and does not provide any content.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<ui-btn v-if="!isSocketConnected" small @click="$router.push('/connect')" class="w-32"> Connect </ui-btn>
|
||||
<ui-btn v-if="!isSocketConnected" small @click="$router.push('/connect')" class="w-32">Connect</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+84
-76
@@ -2,91 +2,96 @@
|
||||
<div class="w-full h-full py-6">
|
||||
<h1 class="text-2xl px-4">Downloads</h1>
|
||||
|
||||
<div class="w-full px-2 py-2" :class="hasStoragePermission ? '' : 'text-error'">
|
||||
<div class="flex items-center">
|
||||
<span class="material-icons" @click="changeDownloadFolderClick">{{ hasStoragePermission ? 'folder' : 'error' }}</span>
|
||||
<p v-if="hasStoragePermission" class="text-sm px-4" @click="changeDownloadFolderClick">{{ downloadFolderSimplePath || 'No Download Folder Selected' }}</p>
|
||||
<p v-else class="text-sm px-4" @click="changeDownloadFolderClick">No Storage Permissions. Click here</p>
|
||||
</div>
|
||||
<!-- <p v-if="hasStoragePermission" class="text-xs text-gray-400 break-all max-w-full">{{ downloadFolderUri }}</p> -->
|
||||
<template v-if="isIos"></template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="w-full px-2 py-2" :class="hasStoragePermission ? '' : 'text-error'">
|
||||
<div class="flex items-center">
|
||||
<span class="material-icons" @click="changeDownloadFolderClick">{{ hasStoragePermission ? 'folder' : 'error' }}</span>
|
||||
<p v-if="hasStoragePermission" class="text-sm px-4" @click="changeDownloadFolderClick">{{ downloadFolderSimplePath || 'No Download Folder Selected' }}</p>
|
||||
<p v-else class="text-sm px-4" @click="changeDownloadFolderClick">No Storage Permissions. Click here</p>
|
||||
</div>
|
||||
<!-- <p v-if="hasStoragePermission" class="text-xs text-gray-400 break-all max-w-full">{{ downloadFolderUri }}</p> -->
|
||||
</div>
|
||||
|
||||
<div class="w-full h-10 relative">
|
||||
<div class="absolute top-px left-0 z-10 w-full h-full flex">
|
||||
<div class="flex-grow h-full bg-primary rounded-t-md mr-px" @click="showingDownloads = true">
|
||||
<div class="flex items-center justify-center rounded-t-md border-t border-l border-r border-white border-opacity-20 h-full" :class="showingDownloads ? 'text-gray-100' : 'border-b bg-black bg-opacity-20 text-gray-400'">
|
||||
<p>Downloads</p>
|
||||
</div>
|
||||
<div class="w-full h-10 relative">
|
||||
<div class="absolute top-px left-0 z-10 w-full h-full flex">
|
||||
<div class="flex-grow h-full bg-primary rounded-t-md mr-px" @click="showingDownloads = true">
|
||||
<div class="flex items-center justify-center rounded-t-md border-t border-l border-r border-white border-opacity-20 h-full" :class="showingDownloads ? 'text-gray-100' : 'border-b bg-black bg-opacity-20 text-gray-400'">
|
||||
<p>Downloads</p>
|
||||
</div>
|
||||
<div class="flex-grow h-full bg-primary rounded-t-md ml-px" @click="showingDownloads = false">
|
||||
<div class="flex items-center justify-center h-full rounded-t-md border-t border-l border-r border-white border-opacity-20" :class="!showingDownloads ? 'text-gray-100' : 'border-b bg-black bg-opacity-20 text-gray-400'">
|
||||
<p>Files</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow h-full bg-primary rounded-t-md ml-px" @click="showingDownloads = false">
|
||||
<div class="flex items-center justify-center h-full rounded-t-md border-t border-l border-r border-white border-opacity-20" :class="!showingDownloads ? 'text-gray-100' : 'border-b bg-black bg-opacity-20 text-gray-400'">
|
||||
<p>Files</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-content-body relative w-full overflow-x-hidden bg-primary">
|
||||
<template v-if="showingDownloads">
|
||||
<div v-if="!totalDownloads" class="flex items-center justify-center h-40">
|
||||
<p>No Downloads</p>
|
||||
</div>
|
||||
<ul v-else class="h-full w-full" role="listbox" aria-labelledby="listbox-label">
|
||||
<template v-for="download in downloadsDownloading">
|
||||
<li :key="download.id" class="text-gray-400 select-none relative px-4 py-5 border-b border-white border-opacity-10 bg-black bg-opacity-10">
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="w-3/4">
|
||||
<span class="text-xs">({{ downloadingProgress[download.id] || 0 }}%) {{ download.isPreparing ? 'Preparing' : 'Downloading' }}...</span>
|
||||
<p class="font-normal truncate text-sm">{{ download.audiobook.book.title }}</p>
|
||||
</div>
|
||||
<div class="flex-grow" />
|
||||
</div>
|
||||
<div class="list-content-body relative w-full overflow-x-hidden bg-primary">
|
||||
<template v-if="showingDownloads">
|
||||
<div v-if="!totalDownloads" class="flex items-center justify-center h-40">
|
||||
<p>No Downloads</p>
|
||||
</div>
|
||||
<ul v-else class="h-full w-full" role="listbox" aria-labelledby="listbox-label">
|
||||
<template v-for="download in downloadsDownloading">
|
||||
<li :key="download.id" class="text-gray-400 select-none relative px-4 py-5 border-b border-white border-opacity-10 bg-black bg-opacity-10">
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="w-3/4">
|
||||
<span class="text-xs">({{ downloadingProgress[download.id] || 0 }}%) {{ download.isPreparing ? 'Preparing' : 'Downloading' }}...</span>
|
||||
<p class="font-normal truncate text-sm">{{ download.audiobook.book.title }}</p>
|
||||
</div>
|
||||
<div class="flex-grow" />
|
||||
|
||||
<div class="shadow-sm text-white flex items-center justify-center rounded-full animate-spin">
|
||||
<span class="material-icons">refresh</span>
|
||||
</div>
|
||||
<div class="shadow-sm text-white flex items-center justify-center rounded-full animate-spin">
|
||||
<span class="material-icons">refresh</span>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<template v-for="download in downloadsReady">
|
||||
<li :key="download.id" class="text-gray-50 select-none relative pr-4 pl-2 py-5 border-b border-white border-opacity-10" @click="jumpToAudiobook(download)">
|
||||
<modals-downloads-download-item :download="download" @play="playDownload" @delete="clickDeleteDownload" />
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="w-full h-full">
|
||||
<div class="w-full flex justify-around py-4 px-2">
|
||||
<ui-btn small @click="searchFolder">Re-Scan</ui-btn>
|
||||
<ui-btn small @click="changeDownloadFolderClick">Change Folder</ui-btn>
|
||||
<ui-btn small color="error" @click="resetFolder">Reset</ui-btn>
|
||||
</div>
|
||||
<p v-if="isScanning" class="text-center my-8">Scanning Folder..</p>
|
||||
<p v-else-if="!mediaScanResults" class="text-center my-8">No Files Found</p>
|
||||
<template v-else>
|
||||
<template v-for="mediaFolder in mediaScanResults.folders">
|
||||
<div :key="mediaFolder.uri" class="w-full px-2 py-2">
|
||||
<div class="flex items-center">
|
||||
<span class="material-icons text-base text-white text-opacity-50">folder</span>
|
||||
<p class="ml-1 py-0.5">{{ mediaFolder.name }}</p>
|
||||
</div>
|
||||
<div v-for="mediaFile in mediaFolder.files" :key="mediaFile.uri" class="ml-3 flex items-center">
|
||||
<span class="material-icons text-base text-white text-opacity-50">{{ mediaFile.isAudio ? 'music_note' : 'image' }}</span>
|
||||
<p class="ml-1 py-0.5">{{ mediaFile.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-for="mediaFile in mediaScanResults.files">
|
||||
<div :key="mediaFile.uri" class="w-full px-2 py-2">
|
||||
<div class="flex items-center">
|
||||
<span class="material-icons text-base text-white text-opacity-50">{{ mediaFile.isAudio ? 'music_note' : 'image' }}</span>
|
||||
<p class="ml-1 py-0.5">{{ mediaFile.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<template v-for="download in downloadsReady">
|
||||
<li :key="download.id" class="text-gray-50 select-none relative pr-4 pl-2 py-5 border-b border-white border-opacity-10" @click="jumpToAudiobook(download)">
|
||||
<modals-downloads-download-item :download="download" @play="playDownload" @delete="clickDeleteDownload" />
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="w-full h-full">
|
||||
<div class="w-full flex justify-around py-4 px-2">
|
||||
<ui-btn small @click="searchFolder">Re-Scan</ui-btn>
|
||||
<ui-btn small @click="changeDownloadFolderClick">Change Folder</ui-btn>
|
||||
<ui-btn small color="error" @click="resetFolder">Reset</ui-btn>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<p v-if="isScanning" class="text-center my-8">Scanning Folder..</p>
|
||||
<p v-else-if="!mediaScanResults" class="text-center my-8">No Files Found</p>
|
||||
<template v-else>
|
||||
<template v-for="mediaFolder in mediaScanResults.folders">
|
||||
<div :key="mediaFolder.uri" class="w-full px-2 py-2">
|
||||
<div class="flex items-center">
|
||||
<span class="material-icons text-base text-white text-opacity-50">folder</span>
|
||||
<p class="ml-1 py-0.5">{{ mediaFolder.name }}</p>
|
||||
</div>
|
||||
<div v-for="mediaFile in mediaFolder.files" :key="mediaFile.uri" class="ml-3 flex items-center">
|
||||
<span class="material-icons text-base text-white text-opacity-50">{{ mediaFile.isAudio ? 'music_note' : 'image' }}</span>
|
||||
<p class="ml-1 py-0.5">{{ mediaFile.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-for="mediaFile in mediaScanResults.files">
|
||||
<div :key="mediaFile.uri" class="w-full px-2 py-2">
|
||||
<div class="flex items-center">
|
||||
<span class="material-icons text-base text-white text-opacity-50">{{ mediaFile.isAudio ? 'music_note' : 'image' }}</span>
|
||||
<p class="ml-1 py-0.5">{{ mediaFile.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -105,6 +110,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isIos() {
|
||||
return this.$platform === 'ios'
|
||||
},
|
||||
isSocketConnected() {
|
||||
return this.$store.state.socketConnected
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user