优化任务下载状态显示

pull/1761/head
lyswhut 2023-12-30 12:31:18 +08:00
parent 2337a3e8a3
commit 1de087eac7
11 changed files with 52 additions and 8 deletions

View File

@ -21,6 +21,7 @@
- 优化搜索框交互逻辑,防止鼠标操作时意外搜索候选列表的内容
- 添加对wy源某些歌曲有问题的歌词进行修复
- 改进本地音乐在线信息的匹配机制
- 优化任务下载状态显示,现在下载时若数据传输完成但数据写入未完成时会显示相应的状态
### 修复

View File

@ -21,6 +21,7 @@ declare global {
speed: string
downloaded: number
total: number
writeQueue: number
}
interface DownloadTaskActionBase <A> {
@ -50,6 +51,7 @@ declare global {
total: number
progress: number
speed: string
writeQueue: number
metadata: {
musicInfo: LX.Music.MusicInfoOnline
url: string | null

View File

@ -8,6 +8,7 @@ import { request, type Options as RequestOptions } from './request'
export interface Options {
forceResume: boolean
timeout: number
requestOptions: RequestOptions
}
@ -23,6 +24,7 @@ const defaultRequestOptions: Options['requestOptions'] = {
}
const defaultOptions: Options = {
forceResume: true,
timeout: 20_000,
requestOptions: { ...defaultRequestOptions },
}
@ -41,6 +43,7 @@ class Task extends EventEmitter {
private redirectNum = 0
private dataWriteQueueLength = 0
private closeWaiting = false
private timeout: null | NodeJS.Timeout = null
constructor(url: string, savePath: string, filename: string, options: Partial<Options> = {}) {
@ -69,6 +72,8 @@ class Task extends EventEmitter {
this.progress.speed = 0
this.dataWriteQueueLength = 0
this.closeWaiting = false
this.__clearTimeout()
this.__startTimeout()
if (startByte) this.requestOptions.headers!.range = `bytes=${startByte}-${endByte}`
if (!path) return
@ -144,6 +149,7 @@ class Task extends EventEmitter {
}
this.status = STATUS.failed
this.emit('fail', response)
this.__clearTimeout()
this.__closeRequest()
void this.__closeWriteStream()
return
@ -156,6 +162,7 @@ class Task extends EventEmitter {
return
}
this.status = STATUS.running
this.__startTimeout()
response
.on('data', this.__handleWriteData.bind(this))
.on('error', err => { this.__handleError(err) })
@ -222,6 +229,7 @@ class Task extends EventEmitter {
__handleComplete() {
if (this.status == STATUS.error) return
this.__clearTimeout()
void this.__closeWriteStream().then(() => {
if (this.progress.downloaded == this.progress.total) {
this.status = STATUS.completed
@ -237,6 +245,7 @@ class Task extends EventEmitter {
__handleError(error: Error) {
if (this.status == STATUS.error) return
this.status = STATUS.error
this.__clearTimeout()
this.__closeRequest()
void this.__closeWriteStream()
if (error.message == 'aborted') return
@ -304,9 +313,11 @@ class Task extends EventEmitter {
return
}
this.dataWriteQueueLength++
this.__startTimeout()
this.__calculateProgress(chunk.length)
this.ws.write(chunk, err => {
this.dataWriteQueueLength--
if (this.status == STATUS.running) this.__calculateProgress(0)
if (err) {
console.log(err)
this.__handleError(err)
@ -322,24 +333,38 @@ class Task extends EventEmitter {
let chunkLen = chunk.length
let isOk
if (chunkLen >= resumeLastChunkLen) {
isOk = chunk.slice(0, resumeLastChunkLen).toString('hex') === this.resumeLastChunk!.toString('hex')
isOk = chunk.subarray(0, resumeLastChunkLen).toString('hex') === this.resumeLastChunk!.toString('hex')
if (!isOk) return null
this.resumeLastChunk = null
return chunk.slice(resumeLastChunkLen)
return chunk.subarray(resumeLastChunkLen)
} else {
isOk = chunk.slice(0, chunkLen).toString('hex') === this.resumeLastChunk!.slice(0, chunkLen).toString('hex')
isOk = chunk.subarray(0, chunkLen).toString('hex') === this.resumeLastChunk!.subarray(0, chunkLen).toString('hex')
if (!isOk) return null
this.resumeLastChunk = this.resumeLastChunk!.slice(chunkLen)
return chunk.slice(chunkLen)
this.resumeLastChunk = this.resumeLastChunk!.subarray(chunkLen)
return chunk.subarray(chunkLen)
}
}
async __handleStop() {
this.__clearTimeout()
this.__closeRequest()
return this.__closeWriteStream()
}
private __clearTimeout() {
if (!this.timeout) return
clearTimeout(this.timeout)
this.timeout = null
}
private __startTimeout() {
this.__clearTimeout()
this.timeout = setTimeout(() => {
this.__handleError(new Error('download timeout'))
}, this.options.timeout)
}
__calculateProgress(receivedBytes: number) {
const currentTime = performance.now()
const elaspsedTime = currentTime - this.statsEstimate.time
@ -350,7 +375,7 @@ class Task extends EventEmitter {
// emit the progress every second or if finished
if (progress.downloaded === progress.total || elaspsedTime > 1000) {
if ((progress.downloaded === progress.total && this.dataWriteQueueLength == 0) || elaspsedTime > 1000) {
this.statsEstimate.time = currentTime
this.statsEstimate.bytes = progress.downloaded - this.statsEstimate.prevBytes
this.statsEstimate.prevBytes = progress.downloaded
@ -359,6 +384,7 @@ class Task extends EventEmitter {
downloaded: progress.downloaded,
progress: progress.progress,
speed: this.statsEstimate.bytes,
writeQueue: this.dataWriteQueueLength,
})
}
}

View File

@ -74,6 +74,7 @@ export const createDownload = ({
speed,
downloaded: stats.downloaded,
total: stats.total,
writeQueue: stats.writeQueue,
})
// if (debugDownload) {
// const downloaded = sizeFormate(stats.downloaded)

View File

@ -71,6 +71,7 @@
"download_status_error_write": "The song save location is occupied or does not have write permission, please try to change the song save directory or restart the software or restart the computer, the error details:",
"download_status_start": "start download",
"download_status_url_geting": "Getting music link...",
"download_status_write_queue": "Data is being written ({num})",
"duplicate_list_tip": "You have favorited this list [{name}] before, do you need to update the songs in it?",
"export": "Export",
"fullscreen_exit": "Exit Full Screen",

View File

@ -71,6 +71,7 @@
"download_status_error_write": "歌曲保存位置被占用或没有写入权限,请尝试更改歌曲保存目录或重启软件或重启电脑,错误详情:",
"download_status_start": "开始下载",
"download_status_url_geting": "音乐链接获取中...",
"download_status_write_queue": "数据写入中({num}",
"duplicate_list_tip": "你之前已收藏过该列表 [{name}],是否需要更新里面的歌曲?",
"export": "导出",
"fullscreen_exit": "退出全屏",

View File

@ -71,6 +71,7 @@
"download_status_error_write": "歌曲儲存位置被佔用或沒有寫入權限,請嘗試變更歌曲儲存目錄或重新啟動軟體或重新啟動電腦,錯誤詳情:",
"download_status_start": "開始下載",
"download_status_url_geting": "音樂連結獲取中...",
"download_status_write_queue": "資料寫入中({num}",
"duplicate_list_tip": "你之前已收藏過該清單 [{name}],是否需要更新裡面的歌曲?",
"export": "匯出",
"fullscreen_exit": "退出全螢幕",

View File

@ -41,6 +41,7 @@ const initDownloadList = () => {
total: item.progress_total,
progress: item.progress_total ? parseInt((item.progress_downloaded / item.progress_total).toFixed(2)) * 100 : 0,
speed: '',
writeQueue: 0,
metadata: {
musicInfo,
url: item.url,

View File

@ -82,10 +82,17 @@ const updateFilePath = (downloadInfo: LX.Download.ListItem, filePath: string) =>
}
const setProgress = (downloadInfo: LX.Download.ListItem, progress: LX.Download.ProgressInfo) => {
downloadInfo.progress = progress.progress
downloadInfo.total = progress.total
downloadInfo.downloaded = progress.downloaded
downloadInfo.speed = progress.speed
downloadInfo.writeQueue = progress.writeQueue
if (progress.progress == 100) {
downloadInfo.speed = ''
downloadInfo.progress = 99.99
setStatusText(downloadInfo, window.i18n.t('download_status_write_queue', { num: progress.writeQueue }))
} else {
downloadInfo.speed = progress.speed
downloadInfo.progress = progress.progress
}
throttleUpdateTask([downloadInfo])
}
@ -248,6 +255,7 @@ const handleStartTask = async(downloadInfo: LX.Download.ListItem) => {
setStatus(downloadInfo, DOWNLOAD_STATUS.RUN)
break
case 'complete':
downloadInfo.progress = 100
saveMeta(downloadInfo)
downloadLyric(downloadInfo)
void window.lx.worker.download.removeTask(downloadInfo.id)

View File

@ -205,6 +205,7 @@ const createTask = async(downloadInfo: LX.Download.ListItem, savePath: string, s
downloadInfo.downloaded = status.downloaded
downloadInfo.progress = status.progress
downloadInfo.speed = status.speed
downloadInfo.writeQueue = status.writeQueue
sendAction(downloadInfo.id, { action: 'progress', data: status })
// console.log(status)
},

View File

@ -80,6 +80,7 @@ export const createDownloadInfo = (musicInfo: LX.Music.MusicInfoOnline, type: LX
total: 0,
progress: 0,
speed: '',
writeQueue: 0,
metadata: {
musicInfo,
url: null,