koel/resources/assets/js/services/uploadService.ts

130 lines
2.9 KiB
TypeScript
Raw Normal View History

import axios from 'axios'
2022-04-15 14:24:30 +00:00
import { without } from 'lodash'
2022-04-21 18:39:18 +00:00
import { reactive } from 'vue'
import { http } from '@/services'
import { albumStore, commonStore, songStore } from '@/stores'
import { eventBus, logger } from '@/utils'
2022-04-15 14:24:30 +00:00
2022-06-10 10:47:46 +00:00
interface UploadResult {
song: Song
album: Album
}
export type UploadStatus =
| 'Ready'
| 'Uploading'
| 'Uploaded'
| 'Canceled'
| 'Errored'
export interface UploadFile {
id: string
file: File
status: UploadStatus
name: string
progress: number
message?: string
}
2022-04-24 08:50:45 +00:00
export const uploadService = {
2022-04-21 18:39:18 +00:00
state: reactive({
2022-04-15 14:24:30 +00:00
files: [] as UploadFile[]
2022-04-21 18:39:18 +00:00
}),
2022-04-15 14:24:30 +00:00
simultaneousUploads: 5,
2022-04-21 18:39:18 +00:00
queue (file: UploadFile | UploadFile[]) {
2022-04-15 14:24:30 +00:00
this.state.files = this.state.files.concat(file)
this.proceed()
},
2022-04-21 18:39:18 +00:00
remove (file: UploadFile) {
2022-04-15 14:24:30 +00:00
this.state.files = without(this.state.files, file)
this.proceed()
},
2022-04-21 18:39:18 +00:00
proceed () {
2022-04-15 14:24:30 +00:00
const remainingSlots = this.simultaneousUploads - this.getUploadingFiles().length
if (remainingSlots <= 0) {
return
}
for (let i = 0; i < remainingSlots; ++i) {
const file = this.getUploadCandidate()
file && this.upload(file)
2022-04-15 14:24:30 +00:00
}
},
2022-04-21 18:39:18 +00:00
getUploadingFiles () {
2024-01-24 22:39:47 +00:00
return this.state.files.filter(({ status }) => status === 'Uploading')
2022-04-15 14:24:30 +00:00
},
2022-04-21 18:39:18 +00:00
getUploadCandidate () {
2024-01-24 22:39:47 +00:00
return this.state.files.find(({ status }) => status === 'Ready')
2022-04-15 14:24:30 +00:00
},
shouldWarnUponWindowUnload () {
return this.state.files.length > 0
},
2022-04-21 18:39:18 +00:00
async upload (file: UploadFile) {
2022-04-15 14:24:30 +00:00
if (file.status === 'Uploading') {
return
}
const formData = new FormData()
formData.append('file', file.file)
file.progress = 0
file.status = 'Uploading'
try {
const result = await http.post<UploadResult>('upload', formData, (progressEvent: ProgressEvent) => {
2022-04-15 14:24:30 +00:00
file.progress = progressEvent.loaded * 100 / progressEvent.total
})
file.status = 'Uploaded'
2022-06-10 10:47:46 +00:00
songStore.syncWithVault(result.song)
albumStore.syncWithVault(result.album)
commonStore.state.song_length += 1
eventBus.emit('SONG_UPLOADED', result.song)
2022-06-10 10:47:46 +00:00
2022-04-15 14:24:30 +00:00
this.proceed() // upload the next file
2022-06-10 10:47:46 +00:00
2022-04-21 18:39:18 +00:00
window.setTimeout(() => this.remove(file), 1000)
} catch (error: unknown) {
2022-07-20 08:00:02 +00:00
logger.error(error)
2022-04-15 14:24:30 +00:00
file.status = 'Errored'
if (axios.isAxiosError(error) && error.response?.data?.message) {
file.message = `Upload failed: ${error.response.data.message}`
} else {
file.message = 'Upload failed: Unknown error.'
}
2022-04-15 14:24:30 +00:00
this.proceed() // upload the next file
}
},
2022-04-21 18:39:18 +00:00
retry (file: UploadFile) {
2022-04-15 14:24:30 +00:00
// simply reset the status and wait for the next process
this.resetFile(file)
this.proceed()
},
2022-04-21 18:39:18 +00:00
retryAll () {
2022-04-15 14:24:30 +00:00
this.state.files.forEach(this.resetFile)
this.proceed()
},
2022-04-21 18:39:18 +00:00
resetFile: (file: UploadFile) => {
2022-04-15 14:24:30 +00:00
file.status = 'Ready'
file.progress = 0
},
2022-04-21 18:39:18 +00:00
removeFailed () {
2024-01-24 22:39:47 +00:00
this.state.files = this.state.files.filter(({ status }) => status !== 'Errored')
2022-04-15 14:24:30 +00:00
}
}