Files
Cultivation/src/utils/download.ts
2022-05-16 18:56:48 -07:00

156 lines
4.7 KiB
TypeScript

import { invoke } from '@tauri-apps/api/tauri'
import { listen } from '@tauri-apps/api/event'
import { byteToString } from './string'
export default class DownloadHandler {
downloads: {
path: string,
progress: number,
total: number,
status: string,
startTime: number,
error?: string,
speed?: string,
onFinish?: () => void,
}[]
// Pass tauri invoke function
constructor() {
this.downloads = []
listen('download_progress', ({ payload }) => {
// @ts-expect-error Payload may be unknown but backend always returns this object
const obj: {
downloaded: string,
total: string,
path: string,
} = payload
const index = this.downloads.findIndex(download => download.path === obj.path)
this.downloads[index].progress = parseInt(obj.downloaded, 10)
this.downloads[index].total = parseInt(obj.total, 10)
// Set download speed based on startTime
const now = Date.now()
const timeDiff = now - this.downloads[index].startTime
const speed = (this.downloads[index].progress / timeDiff) * 1000
this.downloads[index].speed = byteToString(speed) + '/s'
})
listen('download_finished', ({ payload }) => {
// Remove from array
const filename = payload
// set status to finished
const index = this.downloads.findIndex(download => download.path === filename)
this.downloads[index].status = 'finished'
// Call onFinish callback
if (this.downloads[index]?.onFinish) {
// @ts-expect-error onFinish is checked for existence before being called
this.downloads[index]?.onFinish()
}
})
listen('download_error', ({ payload }) => {
// @ts-expect-error shut up typescript
const errorData: {
path: string,
error: string,
} = payload
// Set download to error
const index = this.downloads.findIndex(download => download.path === errorData.path)
this.downloads[index].status = 'error'
this.downloads[index].error = errorData.error
})
// Extraction events
listen('extract_start', ({ payload }) => {
// Find the download that is no extracting and set it's status as such
const index = this.downloads.findIndex(download => download.path === payload)
this.downloads[index].status = 'extracting'
})
listen('extract_end', ({ payload }) => {
// Find the download that is no extracting and set it's status as such
const index = this.downloads.findIndex(download => download.path === payload)
this.downloads[index].status = 'finished'
})
}
getDownloads() {
return this.downloads
}
downloadingJar() {
// Kinda hacky but it works
return this.downloads.some(d => d.path.includes('grasscutter.zip'))
}
downloadingResources() {
// Kinda hacky but it works
return this.downloads.some(d => d.path.includes('resources'))
}
downloadingRepo() {
return this.downloads.some(d => d.path.includes('grasscutter_repo.zip'))
}
addDownload(url: string, path: string, onFinish?: () => void) {
// Begin download from rust backend, don't add if the download addition fails
invoke('download_file', { url, path })
const obj = {
path,
progress: 0,
total: 0,
status: 'downloading',
startTime: Date.now(),
onFinish,
}
this.downloads.push(obj)
}
stopDownload(path: string) {
// Stop download and remove from list.
invoke('stop_download', { path })
// Remove from list
const index = this.downloads.findIndex(download => download.path === path)
this.downloads.splice(index, 1)
}
getDownloadProgress(path: string) {
const index = this.downloads.findIndex(download => download.path === path)
return this.downloads[index] || null
}
getDownloadSize(path: string) {
const index = this.downloads.findIndex(download => download.path === path)
return byteToString(this.downloads[index].total) || null
}
getTotalAverage() {
const files = this.downloads.filter(d => d.status === 'downloading')
const total = files.reduce((acc, d) => acc + d.total, 0)
const progress = files.reduce((acc, d) => acc + d.progress, 0)
let speedStr = '0 B/s'
// Get download speed based on startTimes
if (files.length > 0) {
const now = Date.now()
const timeDiff = now - files[0].startTime
const speed = (progress / timeDiff) * 1000
speedStr = byteToString(speed) + '/s'
}
return {
average: (progress / total) * 100 || 0,
files: this.downloads.filter(d => d.status === 'downloading').length,
extracting: this.downloads.filter(d => d.status === 'extracting').length,
totalSize: total,
speed: speedStr
}
}
}