mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-25 21:04:02 +02:00
Merge branch 'master' into feat_android_auto_browse
This commit is contained in:
@@ -3,11 +3,11 @@
|
||||
<template v-if="!showSelectedFeed">
|
||||
<div class="w-full mx-auto h-20 flex items-center px-2">
|
||||
<form class="w-full" @submit.prevent="submit">
|
||||
<ui-text-input v-model="searchInput" :disabled="processing || !networkConnected" placeholder="Enter search term or RSS feed URL" text-size="sm" />
|
||||
<ui-text-input v-model="searchInput" :disabled="processing || !socketConnected" :placeholder="$strings.MessagePodcastSearchField" text-size="sm" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div v-if="!networkConnected" class="w-full text-center py-6">
|
||||
<div v-if="!socketConnected" class="w-full text-center py-6">
|
||||
<p class="text-lg text-error">{{ $strings.MessageNoNetworkConnection }}</p>
|
||||
</div>
|
||||
<div v-else class="w-full mx-auto pb-2 overflow-y-auto overflow-x-hidden h-[calc(100%-85px)]">
|
||||
@@ -65,8 +65,8 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
networkConnected() {
|
||||
return this.$store.state.networkConnected
|
||||
socketConnected() {
|
||||
return this.$store.state.socketConnected
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
<template>
|
||||
<bookshelf-lazy-bookshelf page="series-books" :series-id="seriesId" />
|
||||
<bookshelf-lazy-bookshelf page="series-books" :series-id="seriesId" v-on:downloadSeriesClick="downloadSeriesClick" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Dialog } from '@capacitor/dialog'
|
||||
import { AbsDownloader } from '@/plugins/capacitor'
|
||||
import cellularPermissionHelpers from '@/mixins/cellularPermissionHelpers'
|
||||
|
||||
export default {
|
||||
async asyncData({ params, app, store, redirect }) {
|
||||
var series = await app.$nativeHttp.get(`/api/series/${params.id}`).catch((error) => {
|
||||
@@ -19,10 +23,162 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
return {
|
||||
startingDownload: false,
|
||||
mediaType: 'book',
|
||||
booksPerFetch: 20,
|
||||
books: 0,
|
||||
missingFiles: 0,
|
||||
missingFilesSize: 0,
|
||||
libraryIds: []
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {},
|
||||
mounted() {}
|
||||
mixins: [cellularPermissionHelpers],
|
||||
computed: {
|
||||
isIos() {
|
||||
return this.$platform === 'ios'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async downloadSeriesClick() {
|
||||
console.log('Download Series clicked')
|
||||
if (this.startingDownload) return
|
||||
|
||||
const hasPermission = await this.checkCellularPermission('download')
|
||||
if (!hasPermission) return
|
||||
|
||||
this.startingDownload = true
|
||||
setTimeout(() => {
|
||||
this.startingDownload = false
|
||||
}, 1000)
|
||||
|
||||
await this.$hapticsImpact()
|
||||
this.download()
|
||||
},
|
||||
buildSearchParams() {
|
||||
let searchParams = new URLSearchParams()
|
||||
searchParams.set('filter', `series.${this.$encode(this.seriesId)}`)
|
||||
return searchParams.toString()
|
||||
},
|
||||
async fetchSeriesEntities(page) {
|
||||
const startIndex = page * this.booksPerFetch
|
||||
|
||||
this.currentSFQueryString = this.buildSearchParams()
|
||||
|
||||
const entityPath = `items`
|
||||
const sfQueryString = this.currentSFQueryString ? this.currentSFQueryString + '&' : ''
|
||||
const fullQueryString = `?${sfQueryString}limit=${this.booksPerFetch}&page=${page}&minified=1&include=rssfeed,numEpisodesIncomplete`
|
||||
|
||||
const payload = await this.$nativeHttp.get(`/api/libraries/${this.series.libraryId}/${entityPath}${fullQueryString}`).catch((error) => {
|
||||
console.error('failed to fetch books', error)
|
||||
return null
|
||||
})
|
||||
|
||||
if (payload && payload.results) {
|
||||
console.log('Received payload', payload)
|
||||
this.books = payload.total
|
||||
|
||||
for (let i = 0; i < payload.results.length; i++) {
|
||||
if (!(await this.$db.getLocalLibraryItem(`local_${payload.results[i].id}`))) {
|
||||
this.missingFiles += payload.results[i].numFiles
|
||||
this.missingFilesSize += payload.results[i].size
|
||||
this.libraryIds.push(payload.results[i].id)
|
||||
}
|
||||
}
|
||||
}
|
||||
let totalPages = Math.ceil(this.books / this.booksPerFetch)
|
||||
if (totalPages > page + 1) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
async download(selectedLocalFolder = null) {
|
||||
// Get the local folder to download to
|
||||
let localFolder = selectedLocalFolder
|
||||
if (!this.isIos && !localFolder) {
|
||||
const localFolders = (await this.$db.getLocalFolders()) || []
|
||||
console.log('Local folders loaded', localFolders.length)
|
||||
const foldersWithMediaType = localFolders.filter((lf) => {
|
||||
console.log('Checking local folder', lf.mediaType)
|
||||
return lf.mediaType == this.mediaType
|
||||
})
|
||||
console.log('Folders with media type', this.mediaType, foldersWithMediaType.length)
|
||||
const internalStorageFolder = foldersWithMediaType.find((f) => f.id === `internal-${this.mediaType}`)
|
||||
if (!foldersWithMediaType.length) {
|
||||
localFolder = {
|
||||
id: `internal-${this.mediaType}`,
|
||||
name: this.$strings.LabelInternalAppStorage,
|
||||
mediaType: this.mediaType
|
||||
}
|
||||
} else if (foldersWithMediaType.length === 1 && internalStorageFolder) {
|
||||
localFolder = internalStorageFolder
|
||||
} else {
|
||||
this.$store.commit('globals/showSelectLocalFolderModal', {
|
||||
mediaType: this.mediaType,
|
||||
callback: (folder) => {
|
||||
this.download(folder)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch series data from server
|
||||
let page = 0
|
||||
let fetchFinished = false
|
||||
this.missingFiles = 0
|
||||
this.missingFilesSize = 0
|
||||
while (fetchFinished === false) {
|
||||
fetchFinished = await this.fetchSeriesEntities(page)
|
||||
page += 1
|
||||
}
|
||||
if (fetchFinished !== true) {
|
||||
console.error('failed to fetch series books data')
|
||||
return null
|
||||
}
|
||||
if (this.missingFiles == 0) {
|
||||
alert(this.$getString('MessageSeriesAlreadyDownloaded'))
|
||||
}
|
||||
|
||||
// Format message for dialog
|
||||
let startDownloadMessage = this.$getString('MessageSeriesDownloadConfirmIos', [this.libraryIds.length, this.missingFiles, this.$bytesPretty(this.missingFilesSize)])
|
||||
if (!this.isIos) {
|
||||
startDownloadMessage = this.$getString('MessageSeriesDownloadConfirm', [this.libraryIds.length, this.missingFiles, this.$bytesPretty(this.missingFilesSize), localFolder.name])
|
||||
}
|
||||
|
||||
// Show confirmation dialog and start downloading if user chooses so
|
||||
const { value } = await Dialog.confirm({
|
||||
title: 'Confirm',
|
||||
message: startDownloadMessage
|
||||
})
|
||||
if (value) {
|
||||
for (let i = 0; i < this.libraryIds.length; i++) {
|
||||
this.startDownload(localFolder, this.libraryIds[i])
|
||||
}
|
||||
}
|
||||
this.libraryIds = []
|
||||
},
|
||||
async startDownload(localFolder = null, libraryItemId) {
|
||||
const payload = {
|
||||
libraryItemId: libraryItemId
|
||||
}
|
||||
if (localFolder) {
|
||||
console.log('Starting download to local folder', localFolder.name)
|
||||
payload.localFolderId = localFolder.id
|
||||
}
|
||||
var downloadRes = await AbsDownloader.downloadLibraryItem(payload)
|
||||
if (downloadRes && downloadRes.error) {
|
||||
var errorMsg = downloadRes.error || 'Unknown error'
|
||||
console.error('Download error', errorMsg)
|
||||
this.$toast.error(errorMsg)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on('download-series-click', this.downloadSeriesClick)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventBus.$off('download-series-click', this.downloadSeriesClick)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
+2
-6
@@ -34,11 +34,7 @@ export default {
|
||||
deviceData: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
networkConnected() {
|
||||
return this.$store.state.networkConnected
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
async init() {
|
||||
this.deviceData = await this.$db.getDeviceData()
|
||||
@@ -53,4 +49,4 @@ export default {
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
<p ref="description" class="text-sm text-justify whitespace-pre-line font-light" :class="{ 'line-clamp-4': !showFullDescription }" style="hyphens: auto">{{ description }}</p>
|
||||
|
||||
<div v-if="descriptionClamped" class="text-fg text-sm py-2" @click="showFullDescription = !showFullDescription">
|
||||
{{ showFullDescription ? 'Read less' : 'Read more' }}
|
||||
{{ showFullDescription ? $strings.ButtonReadLess : $strings.ButtonReadMore }}
|
||||
<span class="material-icons align-middle text-base -mt-px">{{ showFullDescription ? 'expand_less' : 'expand_more' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -188,7 +188,7 @@ export default {
|
||||
if (libraryItem?.libraryItemId?.startsWith('li_')) {
|
||||
// Detect old library item id
|
||||
console.error('Local library item has old server library item id', libraryItem.libraryItemId)
|
||||
} else if (query.noredirect !== '1' && libraryItem?.libraryItemId && libraryItem?.serverAddress === store.getters['user/getServerAddress'] && store.state.networkConnected) {
|
||||
} else if (query.noredirect !== '1' && libraryItem?.libraryItemId && libraryItem?.serverAddress === store.getters['user/getServerAddress'] && store.state.socketConnected) {
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.set('localLibraryItemId', libraryItemId)
|
||||
if (libraryItem.mediaType === 'podcast') {
|
||||
@@ -609,7 +609,7 @@ export default {
|
||||
this.download(localFolder)
|
||||
},
|
||||
async downloadClick() {
|
||||
if (this.downloadItem || this.startingDownload) return
|
||||
if (this.downloadItem || this.startingDownload) return
|
||||
|
||||
const hasPermission = await this.checkCellularPermission('download')
|
||||
if (!hasPermission) return
|
||||
|
||||
@@ -71,6 +71,7 @@ export default {
|
||||
let lastKey = null
|
||||
let numSaves = 0
|
||||
let numSyncs = 0
|
||||
let lastSaveName = null
|
||||
|
||||
this.mediaEvents.forEach((evt) => {
|
||||
const date = this.$formatDate(evt.timestamp, 'MMM dd, yyyy')
|
||||
@@ -90,7 +91,8 @@ export default {
|
||||
|
||||
// Collapse saves
|
||||
if (evt.name === 'Save') {
|
||||
if (numSaves > 0 && !keyUpdated) {
|
||||
let saveName = evt.name + "-" + evt.serverSyncAttempted + "-" + evt.serverSyncSuccess
|
||||
if (lastSaveName === saveName && numSaves > 0 && !keyUpdated) {
|
||||
include = false
|
||||
const totalInGroup = groups[key].length
|
||||
groups[key][totalInGroup - 1].num = numSaves
|
||||
@@ -98,6 +100,7 @@ export default {
|
||||
} else {
|
||||
numSaves = 1
|
||||
}
|
||||
lastSaveName = saveName
|
||||
} else {
|
||||
numSaves = 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user