2022-04-15 14:24:30 +00:00
|
|
|
import { without } from 'lodash'
|
2022-04-21 18:39:18 +00:00
|
|
|
import { reactive } from 'vue'
|
2022-09-15 09:07:25 +00:00
|
|
|
import { http } from '@/services'
|
2023-12-28 22:32:58 +00:00
|
|
|
import { albumStore, commonStore, overviewStore, songStore } from '@/stores'
|
2022-09-12 11:11:56 +00:00
|
|
|
import { logger } from '@/utils'
|
2024-04-23 11:24:29 +00:00
|
|
|
import axios from 'axios'
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-06-10 10:47:46 +00:00
|
|
|
interface UploadResult {
|
|
|
|
song: Song
|
|
|
|
album: Album
|
|
|
|
}
|
|
|
|
|
2022-10-08 10:54:25 +00:00
|
|
|
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()
|
2022-08-04 10:39:03 +00:00
|
|
|
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
|
|
|
},
|
|
|
|
|
2022-08-04 10:39:03 +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 {
|
2022-09-15 09:07:25 +00:00
|
|
|
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)
|
2023-12-28 22:32:58 +00:00
|
|
|
commonStore.state.song_length += 1
|
2022-06-10 10:47:46 +00:00
|
|
|
overviewStore.refresh()
|
|
|
|
|
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)
|
2024-04-23 11:24:29 +00:00
|
|
|
} 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'
|
2024-04-23 11:24:29 +00:00
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|