audiobookshelf/client/mixins/uploadHelpers.js

287 lines
9.1 KiB
JavaScript

import Path from 'path'
export default {
data() {
return {
uploadHelpers: {
getItemsFromDrop: this.getItemsFromDataTransferItems,
getItemsFromPicker: this.getItemsFromFilelist
}
}
},
methods: {
checkFileType(filename) {
var ext = Path.extname(filename)
if (!ext) return false
if (ext.startsWith('.')) ext = ext.slice(1)
ext = ext.toLowerCase()
for (const filetype in this.$constants.SupportedFileTypes) {
if (this.$constants.SupportedFileTypes[filetype].includes(ext)) {
return filetype
}
}
return false
},
filterItemFiles(files, mediaType) {
var validItemFiles = []
var validOtherFiles = []
var ignoredFiles = []
files.forEach((file) => {
// var filetype = this.checkFileType(file.name)
if (!file.filetype) ignoredFiles.push(file)
else {
// file.filetype = filetype
if (file.filetype === 'audio' || (file.filetype === 'ebook' && mediaType === 'book')) validItemFiles.push(file)
else validOtherFiles.push(file)
}
})
return {
itemFiles: validItemFiles,
otherFiles: validOtherFiles,
ignoredFiles
}
},
itemFromTreeItems(items, mediaType) {
var { itemFiles, otherFiles, ignoredFiles } = this.filterItemFiles(items, mediaType)
if (!itemFiles.length) {
ignoredFiles = ignoredFiles.concat(otherFiles)
otherFiles = []
}
return [
{
itemFiles,
otherFiles,
ignoredFiles
}
]
},
traverseForItem(folder, mediaType, depth = 1) {
if (folder.items.some((f) => f.isDirectory)) {
var items = []
folder.items.forEach((file) => {
if (file.isDirectory) {
var itemResults = this.traverseForItem(file, mediaType, ++depth)
items = items.concat(itemResults)
}
})
return items
} else {
return this.itemFromTreeItems(folder.items, mediaType)
}
},
fileTreeToItems(filetree, mediaType) {
// Has directores - Is Multi Book Drop
if (filetree.some((f) => f.isDirectory)) {
var ignoredFilesInRoot = filetree.filter((f) => !f.isDirectory)
if (ignoredFilesInRoot.length) filetree = filetree.filter((f) => f.isDirectory)
var itemResults = this.traverseForItem({ items: filetree }, mediaType)
return {
items: itemResults,
ignoredFiles: ignoredFilesInRoot
}
} else if (filetree.some((f) => f.filetype !== 'audio') || mediaType !== 'book') {
// Single Book drop
return {
items: this.itemFromTreeItems(filetree, mediaType),
ignoredFiles: []
}
} else {
// Only audio files dropped so treat each one as an audiobook
return {
items: filetree.map((audioFile) => ({ itemFiles: [audioFile], otherFiles: [], ignoredFiles: [] })),
ignoredFiles: []
}
}
},
getFilesDropped(dataTransferItems) {
var treemap = {
path: '/',
items: []
}
function traverseFileTreePromise(item, currtreemap, checkFileType) {
return new Promise((resolve) => {
if (item.isFile) {
item.file((file) => {
file.filepath = currtreemap.path + file.name //save full path
file.filetype = checkFileType(file.name)
currtreemap.items.push(file)
resolve(file)
})
} else if (item.isDirectory) {
let dirReader = item.createReader()
currtreemap.items.push({
isDirectory: true,
dirname: item.name,
path: currtreemap.path + item.name + '/',
items: []
})
var newtreemap = currtreemap.items[currtreemap.items.length - 1]
let entriesPromises = []
// readEntries returns 100 items max, continue calling readEntries until empty
function readEntries() {
dirReader.readEntries((entries) => {
if (entries.length > 0) {
for (let entr of entries) {
entriesPromises.push(traverseFileTreePromise(entr, newtreemap, checkFileType))
}
readEntries()
} else {
resolve(Promise.all(entriesPromises))
}
})
}
readEntries()
}
})
}
return new Promise((resolve, reject) => {
let entriesPromises = []
for (let it of dataTransferItems) {
var filetree = traverseFileTreePromise(it.webkitGetAsEntry(), treemap, this.checkFileType)
entriesPromises.push(filetree)
}
Promise.all(entriesPromises).then(() => {
resolve(treemap.items)
})
})
},
cleanBook(book, index) {
var audiobook = {
index,
title: '',
author: '',
series: '',
...book
}
var firstBookFile = book.itemFiles[0]
if (!firstBookFile.filepath) {
return audiobook // No path
}
var firstBookPath = Path.dirname(firstBookFile.filepath)
var dirs = firstBookPath.split('/').filter(d => !!d && d !== '.')
if (dirs.length) {
audiobook.title = dirs.pop()
if (dirs.length > 1) {
audiobook.series = dirs.pop()
}
if (dirs.length) {
audiobook.author = dirs.pop()
}
} else {
// Use file basename as title
audiobook.title = Path.basename(firstBookFile.name, Path.extname(firstBookFile.name))
}
return audiobook
},
cleanPodcast(item, index) {
var podcast = {
index,
title: '',
...item
}
var firstAudioFile = podcast.itemFiles[0]
if (!firstAudioFile.filepath) return podcast // No path
var firstPath = Path.dirname(firstAudioFile.filepath)
var dirs = firstPath.split('/').filter(d => !!d && d !== '.')
if (dirs.length) {
podcast.title = dirs.length > 1 ? dirs[1] : dirs[0]
} else {
podcast.title = Path.basename(firstAudioFile.name, Path.extname(firstAudioFile.name))
}
return podcast
},
cleanItem(item, mediaType, index) {
if (mediaType === 'podcast') return this.cleanPodcast(item, index)
return this.cleanBook(item, index)
},
async getItemsFromDataTransferItems(dataTransferItems, mediaType) {
var files = await this.getFilesDropped(dataTransferItems)
if (!files || !files.length) return { error: 'No files found ' }
var itemData = this.fileTreeToItems(files, mediaType)
if (!itemData.items.length && !itemData.ignoredFiles.length) {
return { error: 'Invalid file drop' }
}
var ignoredFiles = itemData.ignoredFiles
var index = 1
var items = itemData.items.filter((ab) => {
if (!ab.itemFiles.length) {
if (ab.otherFiles.length) ignoredFiles = ignoredFiles.concat(ab.otherFiles)
if (ab.ignoredFiles.length) ignoredFiles = ignoredFiles.concat(ab.ignoredFiles)
}
return ab.itemFiles.length
}).map(ab => this.cleanItem(ab, mediaType, index++))
return {
items,
ignoredFiles
}
},
getItemsFromFilelist(filelist, mediaType) {
var ignoredFiles = []
var otherFiles = []
var itemMap = {}
filelist.forEach((file) => {
var filetype = this.checkFileType(file.name)
if (!filetype) ignoredFiles.push(file)
else {
file.filetype = filetype
if (file.webkitRelativePath) file.filepath = file.webkitRelativePath
else file.filepath = file.name
if (filetype === 'audio' || (filetype === 'ebook' && mediaType === 'book')) {
var dir = file.filepath ? Path.dirname(file.filepath) : ''
if (dir === '.') dir = ''
if (!itemMap[dir]) {
itemMap[dir] = {
path: dir,
ignoredFiles: [],
itemFiles: [],
otherFiles: []
}
}
itemMap[dir].itemFiles.push(file)
} else {
otherFiles.push(file)
}
}
})
otherFiles.forEach((file) => {
var dir = Path.dirname(file.filepath)
var findItem = Object.values(itemMap).find(b => dir.startsWith(b.path))
if (findItem) {
findItem.otherFiles.push(file)
} else {
ignoredFiles.push(file)
}
})
var items = []
var index = 1
// If book media type and all files are audio files then treat each one as an audiobook
if (itemMap[''] && !otherFiles.length && mediaType === 'book' && !itemMap[''].itemFiles.some(f => f.filetype !== 'audio')) {
items = itemMap[''].itemFiles.map((audioFile) => {
return this.cleanItem({ itemFiles: [audioFile], otherFiles: [], ignoredFiles: [] }, mediaType, index++)
})
} else {
items = Object.values(itemMap).map(i => this.cleanItem(i, mediaType, index++))
}
return {
items,
ignoredFiles: ignoredFiles
}
},
}
}