mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-12-20 18:59:37 +00:00
Update uploader to support podcast folder structure
This commit is contained in:
parent
5a26b01ffb
commit
a62f7a4861
6 changed files with 186 additions and 138 deletions
|
|
@ -3,11 +3,14 @@
|
|||
<div class="w-full max-w-6xl mx-auto">
|
||||
<!-- Library & folder picker -->
|
||||
<div class="flex my-6 -mx-2">
|
||||
<div class="w-1/3 px-2">
|
||||
<ui-dropdown v-model="selectedLibraryId" :items="libraryItems" label="Library" :disabled="processing" @input="libraryChanged" />
|
||||
<div class="w-1/5 px-2">
|
||||
<ui-dropdown v-model="selectedLibraryId" :items="libraryItems" label="Library" :disabled="!!items.length" @input="libraryChanged" />
|
||||
</div>
|
||||
<div class="w-2/3 px-2">
|
||||
<ui-dropdown v-model="selectedFolderId" :items="folderItems" :disabled="!selectedLibraryId || processing" label="Folder" />
|
||||
<div class="w-3/5 px-2">
|
||||
<ui-dropdown v-model="selectedFolderId" :items="folderItems" :disabled="!selectedLibraryId || !!items.length" label="Folder" />
|
||||
</div>
|
||||
<div class="w-1/5 px-2">
|
||||
<ui-text-input-with-label :value="selectedLibraryMediaType" readonly label="Media Type" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -16,7 +19,7 @@
|
|||
</widgets-alert>
|
||||
|
||||
<!-- Picker display -->
|
||||
<div v-if="!books.length && !ignoredFiles.length" class="w-full mx-auto border border-white border-opacity-20 px-12 pt-12 pb-4 my-12 relative" :class="isDragging ? 'bg-primary bg-opacity-40' : 'border-dashed'">
|
||||
<div v-if="!items.length && !ignoredFiles.length" class="w-full mx-auto border border-white border-opacity-20 px-12 pt-12 pb-4 my-12 relative" :class="isDragging ? 'bg-primary bg-opacity-40' : 'border-dashed'">
|
||||
<p class="text-2xl text-center">{{ isDragging ? 'Drop files' : "Drag n' drop files or folders" }}</p>
|
||||
<p class="text-center text-sm my-5">or</p>
|
||||
<div class="w-full max-w-xl mx-auto">
|
||||
|
|
@ -29,33 +32,33 @@
|
|||
<p class="text-xs text-white text-opacity-50 font-mono"><strong>Supported File Types: </strong>{{ inputAccept.join(', ') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Book list header -->
|
||||
<!-- Item list header -->
|
||||
<div v-else class="w-full flex items-center pb-4 border-b border-white border-opacity-10">
|
||||
<p class="text-lg">{{ books.length }} book{{ books.length === 1 ? '' : 's' }}</p>
|
||||
<p class="text-lg">{{ items.length }} item{{ items.length === 1 ? '' : 's' }}</p>
|
||||
<p v-if="ignoredFiles.length" class="text-lg"> | {{ ignoredFiles.length }} file{{ ignoredFiles.length === 1 ? '' : 's' }} ignored</p>
|
||||
<div class="flex-grow" />
|
||||
<ui-btn :disabled="processing" small @click="reset">Reset</ui-btn>
|
||||
</div>
|
||||
|
||||
<!-- Alerts -->
|
||||
<widgets-alert v-if="!books.length && !uploadReady" type="error" class="my-4">
|
||||
<p class="text-lg">No books found</p>
|
||||
<widgets-alert v-if="!items.length && !uploadReady" type="error" class="my-4">
|
||||
<p class="text-lg">No items found</p>
|
||||
</widgets-alert>
|
||||
<widgets-alert v-if="ignoredFiles.length" type="warning" class="my-4">
|
||||
<div class="w-full pr-12">
|
||||
<p class="text-base mb-1">Unsupported files are ignored. When choosing or dropping a folder, other files that are not in a book folder are ignored.</p>
|
||||
<p class="text-base mb-1">Unsupported files are ignored. When choosing or dropping a folder, other files that are not in an item folder are ignored.</p>
|
||||
<tables-uploaded-files-table :files="ignoredFiles" title="Ignored Files" class="text-white" />
|
||||
<p class="text-xs text-white text-opacity-50 font-mono pt-1"><strong>Supported File Types: </strong>{{ inputAccept.join(', ') }}</p>
|
||||
</div>
|
||||
</widgets-alert>
|
||||
|
||||
<!-- Book Upload cards -->
|
||||
<template v-for="(book, index) in books">
|
||||
<cards-book-upload-card :ref="`bookCard-${book.index}`" :key="index" :book="book" :processing="processing" @remove="removeBook(book)" />
|
||||
<!-- Item Upload cards -->
|
||||
<template v-for="(item, index) in items">
|
||||
<cards-item-upload-card :ref="`itemCard-${item.index}`" :key="index" :media-type="selectedLibraryMediaType" :item="item" :processing="processing" @remove="removeItem(item)" />
|
||||
</template>
|
||||
|
||||
<!-- Upload/Reset btns -->
|
||||
<div v-show="books.length" class="flex justify-end pb-8 pt-4">
|
||||
<div v-show="items.length" class="flex justify-end pb-8 pt-4">
|
||||
<ui-btn v-if="!uploadFinished" color="success" :loading="processing" @click="submit">Upload</ui-btn>
|
||||
<ui-btn v-else @click="reset">Reset</ui-btn>
|
||||
</div>
|
||||
|
|
@ -75,7 +78,7 @@ export default {
|
|||
return {
|
||||
isDragging: false,
|
||||
error: '',
|
||||
books: [],
|
||||
items: [],
|
||||
ignoredFiles: [],
|
||||
selectedLibraryId: null,
|
||||
selectedFolderId: null,
|
||||
|
|
@ -108,6 +111,12 @@ export default {
|
|||
selectedLibrary() {
|
||||
return this.libraries.find((lib) => lib.id === this.selectedLibraryId)
|
||||
},
|
||||
selectedLibraryMediaType() {
|
||||
return this.selectedLibrary ? this.selectedLibrary.mediaType : null
|
||||
},
|
||||
selectedLibraryIsPodcast() {
|
||||
return this.selectedLibraryMediaType === 'podcast'
|
||||
},
|
||||
selectedFolder() {
|
||||
if (!this.selectedLibrary) return null
|
||||
return this.selectedLibrary.folders.find((fold) => fold.id === this.selectedFolderId)
|
||||
|
|
@ -122,7 +131,7 @@ export default {
|
|||
})
|
||||
},
|
||||
uploadReady() {
|
||||
return !this.books.length && !this.ignoredFiles.length && !this.uploadFinished
|
||||
return !this.items.length && !this.ignoredFiles.length && !this.uploadFinished
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -141,15 +150,15 @@ export default {
|
|||
this.selectedFolderId = this.selectedLibrary.folders[0].id
|
||||
}
|
||||
},
|
||||
removeBook(book) {
|
||||
this.books = this.books.filter((b) => b.index !== book.index)
|
||||
if (!this.books.length) {
|
||||
removeItem(item) {
|
||||
this.items = this.items.filter((b) => b.index !== item.index)
|
||||
if (!this.items.length) {
|
||||
this.reset()
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
this.error = ''
|
||||
this.books = []
|
||||
this.items = []
|
||||
this.ignoredFiles = []
|
||||
this.uploadFinished = false
|
||||
if (this.$refs.fileInput) this.$refs.fileInput.value = ''
|
||||
|
|
@ -186,31 +195,31 @@ export default {
|
|||
e.preventDefault()
|
||||
this.isDragging = false
|
||||
var items = e.dataTransfer.items || []
|
||||
var bookResults = await this.uploadHelpers.getBooksFromDrop(items)
|
||||
this.setResults(bookResults)
|
||||
var itemResults = await this.uploadHelpers.getItemsFromDrop(items)
|
||||
this.setResults(itemResults)
|
||||
},
|
||||
inputChanged(e) {
|
||||
if (!e.target || !e.target.files) return
|
||||
var _files = Array.from(e.target.files)
|
||||
if (_files && _files.length) {
|
||||
var bookResults = this.uploadHelpers.getBooksFromPicker(_files)
|
||||
this.setResults(bookResults)
|
||||
var itemResults = this.uploadHelpers.getItemsFromPicker(_files, this.selectedLibraryMediaType)
|
||||
this.setResults(itemResults)
|
||||
}
|
||||
},
|
||||
setResults(bookResults) {
|
||||
if (bookResults.error) {
|
||||
this.error = bookResults.error
|
||||
this.books = []
|
||||
setResults(itemResults) {
|
||||
if (itemResults.error) {
|
||||
this.error = itemResults.error
|
||||
this.items = []
|
||||
this.ignoredFiles = []
|
||||
} else {
|
||||
this.error = ''
|
||||
this.books = bookResults.books
|
||||
this.ignoredFiles = bookResults.ignoredFiles
|
||||
this.items = itemResults.items
|
||||
this.ignoredFiles = itemResults.ignoredFiles
|
||||
}
|
||||
console.log('Upload results', bookResults)
|
||||
console.log('Upload results', itemResults)
|
||||
},
|
||||
updateBookCardStatus(index, status) {
|
||||
var ref = this.$refs[`bookCard-${index}`]
|
||||
updateItemCardStatus(index, status) {
|
||||
var ref = this.$refs[`itemCard-${index}`]
|
||||
if (ref && ref.length) ref = ref[0]
|
||||
if (!ref) {
|
||||
console.error('Book card ref not found', index, this.$refs)
|
||||
|
|
@ -218,16 +227,18 @@ export default {
|
|||
ref.setUploadStatus(status)
|
||||
}
|
||||
},
|
||||
uploadBook(book) {
|
||||
uploadItem(item) {
|
||||
var form = new FormData()
|
||||
form.set('title', book.title)
|
||||
form.set('author', book.author)
|
||||
form.set('series', book.series)
|
||||
form.set('title', item.title)
|
||||
if (!this.selectedLibraryIsPodcast) {
|
||||
form.set('author', item.author)
|
||||
form.set('series', item.series)
|
||||
}
|
||||
form.set('library', this.selectedLibraryId)
|
||||
form.set('folder', this.selectedFolderId)
|
||||
|
||||
var index = 0
|
||||
book.files.forEach((file) => {
|
||||
item.files.forEach((file) => {
|
||||
form.set(`${index++}`, file)
|
||||
})
|
||||
|
||||
|
|
@ -241,24 +252,24 @@ export default {
|
|||
return false
|
||||
})
|
||||
},
|
||||
validateBooks() {
|
||||
var bookData = []
|
||||
for (var book of this.books) {
|
||||
var bookref = this.$refs[`bookCard-${book.index}`]
|
||||
if (bookref && bookref.length) bookref = bookref[0]
|
||||
validateItems() {
|
||||
var itemData = []
|
||||
for (var item of this.items) {
|
||||
var itemref = this.$refs[`itemCard-${item.index}`]
|
||||
if (itemref && itemref.length) itemref = itemref[0]
|
||||
|
||||
if (!bookref) {
|
||||
console.error('Invalid book index no ref', book.index, this.$refs.bookCard)
|
||||
if (!itemref) {
|
||||
console.error('Invalid item index no ref', item.index, this.$refs.itemCard)
|
||||
return false
|
||||
} else {
|
||||
var data = bookref.getData()
|
||||
var data = itemref.getData()
|
||||
if (!data) {
|
||||
return false
|
||||
}
|
||||
bookData.push(data)
|
||||
itemData.push(data)
|
||||
}
|
||||
}
|
||||
return bookData
|
||||
return itemData
|
||||
},
|
||||
async submit() {
|
||||
if (!this.selectedFolderId || !this.selectedLibraryId) {
|
||||
|
|
@ -267,27 +278,27 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
var books = this.validateBooks()
|
||||
if (!books) {
|
||||
this.$toast.error('Some invalid books')
|
||||
var items = this.validateItems()
|
||||
if (!items) {
|
||||
this.$toast.error('Some invalid items')
|
||||
return
|
||||
}
|
||||
this.processing = true
|
||||
var booksUploaded = 0
|
||||
var booksFailed = 0
|
||||
for (let i = 0; i < books.length; i++) {
|
||||
var book = books[i]
|
||||
this.updateBookCardStatus(book.index, 'uploading')
|
||||
var result = await this.uploadBook(book)
|
||||
if (result) booksUploaded++
|
||||
else booksFailed++
|
||||
this.updateBookCardStatus(book.index, result ? 'success' : 'failed')
|
||||
var itemsUploaded = 0
|
||||
var itemsFailed = 0
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
var item = items[i]
|
||||
this.updateItemCardStatus(item.index, 'uploading')
|
||||
var result = await this.uploadItem(item)
|
||||
if (result) itemsUploaded++
|
||||
else itemsFailed++
|
||||
this.updateItemCardStatus(item.index, result ? 'success' : 'failed')
|
||||
}
|
||||
if (booksUploaded) {
|
||||
this.$toast.success(`Successfully uploaded ${booksUploaded} book${booksUploaded > 1 ? 's' : ''}`)
|
||||
if (itemsUploaded) {
|
||||
this.$toast.success(`Successfully uploaded ${itemsUploaded} item${itemsUploaded > 1 ? 's' : ''}`)
|
||||
}
|
||||
if (booksFailed) {
|
||||
this.$toast.success(`Failed to upload ${booksFailed} book${booksFailed > 1 ? 's' : ''}`)
|
||||
if (itemsFailed) {
|
||||
this.$toast.success(`Failed to upload ${itemsFailed} item${itemsFailed > 1 ? 's' : ''}`)
|
||||
}
|
||||
this.processing = false
|
||||
this.uploadFinished = true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue