This commit is contained in:
lyswhut
2022-10-29 11:36:35 +08:00
parent 66b965d9f7
commit e45cee6596
715 changed files with 57096 additions and 31771 deletions
+7
View File
@@ -0,0 +1,7 @@
import { setMeta } from '@common/utils/musicMeta'
export const writeMeta = (filePath: string, meta: LX.Music.MusicFileMeta) => {
setMeta(filePath, meta)
}
export { saveLrc } from './utils'
+288
View File
@@ -0,0 +1,288 @@
import { createDownload, DownloaderType, Options as DownloadOptions } from '@common/utils/download'
// import music from '@renderer/utils/musicSdk'
import { createDownloadInfo } from './utils'
// import {
// filterFileName,
// } from '@common/utils/common'
// import {
// assertApiSupport,
// getExt,
// } from '..'
import { checkPath, removeFile } from '@common/utils/nodejs'
import { DOWNLOAD_STATUS } from '@common/constants'
// import { download as eventDownloadNames } from '@renderer/event/names'
// window.downloadList = []
// window.downloadListFull = []
// window.downloadListFullMap = new Map()
const dls = new Map<string, DownloaderType>()
const tryNum = new Map<string, number>()
const taskActions = new Map<string, (action: LX.Download.DownloadTaskActions) => void>()
const tasks = new Map<string, LX.Download.ListItem>()
export const checkList = (list: LX.Download.ListItem[], musicInfo: LX.Music.MusicInfo, quality: LX.Quality, ext: string): boolean => {
return list.some(s => s.id === musicInfo.id && (s.metadata.quality === quality || s.metadata.ext === ext))
}
// const removeTask = (id: string) => {
// dls.delete(id)
// tryNum.delete(id)
// taskActions.delete(id)
// tasks.delete(id)
// }
const sendAction = (id: string, action: LX.Download.DownloadTaskActions) => {
const callback = taskActions.get(id)
if (!callback) return
callback(action)
}
export const createDownloadTasks = (
list: LX.Music.MusicInfoOnline[],
quality: LX.Quality,
savePath: string,
fileNameFormat: string,
qualityList: LX.QualityList,
): LX.Download.ListItem[] => {
return list.map(musicInfo => {
return createDownloadInfo(musicInfo, quality, fileNameFormat, savePath, qualityList)
}).filter(task => task)
// commit('addTasks', { list: taskList, addMusicLocationType: rootState.setting.list.addMusicLocationType })
// let result = getStartTask(downloadList, DOWNLOAD_STATUS, rootState.setting.download.maxDownloadNum)
// while (result) {
// dispatch('startTask', result)
// result = getStartTask(downloadList, DOWNLOAD_STATUS, rootState.setting.download.maxDownloadNum)
// }
}
const createTask = async(downloadInfo: LX.Download.ListItem, savePath: string) => {
// console.log('createTask', downloadInfo, savePath)
// 开始任务
/* commit('onStart', downloadInfo)
commit('setStatusText', { downloadInfo, text: '任务初始化中' }) */
if (!await checkPath(savePath)) {
sendAction(downloadInfo.id, {
action: 'error',
data: {
error: 'download_status_error_check_path',
},
})
return
}
if (!tasks.has(downloadInfo.id)) return
// TODO check file exists
const downloadOptions: DownloadOptions = {
url: downloadInfo.metadata.url ?? '',
path: savePath,
fileName: downloadInfo.metadata.fileName,
method: 'get',
onCompleted() {
// if (downloadInfo.progress.progress != '100.00') {
// delete.get(downloadInfo.id)?
// return dispatch('startTask', downloadInfo)
// }
downloadInfo.isComplate = true
downloadInfo.status = DOWNLOAD_STATUS.COMPLETED
sendAction(downloadInfo.id, { action: 'complete' })
console.log('on complate')
},
onError(err: any) {
console.log(err)
if (err.code == 'EPERM') {
sendAction(downloadInfo.id, {
action: 'error',
data: {
error: 'download_status_error_write',
message: err.message,
},
// data: `歌曲保存位置被占用或没有写入权限,请尝试更改歌曲保存目录或重启软件或重启电脑,错误详情:${err.message as string}`,
})
return
}
// console.log(tryNum[downloadInfo.id])
let retryNum = tryNum.get(downloadInfo.id) ?? 0
tryNum.set(downloadInfo.id, ++retryNum)
if (retryNum > 2) {
sendAction(downloadInfo.id, {
action: 'error',
data: {
message: err.message,
},
})
// dispatch('startTask')
return
}
if (err.message?.startsWith('Resume failed')) {
removeFile(downloadInfo.metadata.filePath).catch(err => {
console.log('删除不匹配的文件失败:', err.message)
// commit('onError', { downloadInfo, errorMsg: '删除不匹配的文件失败:' + err.message })
}).finally(() => {
console.log('正在重试')
void dls.get(downloadInfo.id)?.start()
// sendAction(downloadInfo.id, {
// action: 'statusText',
// data: 'download_status_error_retrying',
// })
})
return
}
if (err.code == 'ENOTFOUND') {
sendAction(downloadInfo.id, { action: 'refreshUrl' })
} else {
console.log('Download failed, Attempting Retry')
void dls.get(downloadInfo.id)?.start()
console.log('正在重试')
}
},
onFail(response) {
let retryNum = tryNum.get(downloadInfo.id) ?? 0
tryNum.set(downloadInfo.id, ++retryNum)
if (retryNum > 2) {
sendAction(downloadInfo.id, {
action: 'error',
data: {},
})
return
}
switch (response.statusCode) {
case 401:
case 403:
case 410:
sendAction(downloadInfo.id, { action: 'refreshUrl' })
// commit('onError', { downloadInfo, errorMsg: '链接失效' })
// refreshUrl.call(_this, commit, downloadInfo, rootState.setting.download.isUseOtherSource)
break
default:
void dls.get(downloadInfo.id)?.start()
console.log('正在重试')
// commit('setStatusText', { downloadInfo, text: '正在重试' })
break
}
},
onStart() {
// sendAction(downloadInfo.id, { action: 'start' })
console.log('on start')
},
onProgress(status) {
downloadInfo.total = status.total
downloadInfo.downloaded = status.downloaded
downloadInfo.progress = status.progress
downloadInfo.speed = status.speed
sendAction(downloadInfo.id, { action: 'progress', data: status })
// console.log(status)
},
onStop() {
console.log('on stop')
// sendAction(downloadInfo.id, { action: 'pause' })
// commit('pauseTask', downloadInfo)
// dispatch('startTask')
},
}
// commit('setStatusText', { downloadInfo, text: '获取URL中...' })
tryNum.set(downloadInfo.id, 0)
dls.set(downloadInfo.id, createDownload(downloadOptions))
}
export const updateUrl = (id: string, url: string) => {
const task = tasks.get(id)
if (!task) return
task.metadata.url = url
// commit('setStatusText', { downloadInfo, text: '链接刷新成功' })
const dl = dls.get(id)
if (!dl) return
dl.refreshUrl(url)
dl.start().catch(err => {
sendAction(id, {
action: 'error',
data: {
message: err.message,
},
})
})
}
export const startTask = async(downloadInfo: LX.Download.ListItem, savePath: string, callback: (action: LX.Download.DownloadTaskActions) => void) => {
await pauseTask(downloadInfo.id)
tasks.set(downloadInfo.id, downloadInfo)
taskActions.set(downloadInfo.id, callback)
// 检查是否可以开始任务
// if (!downloadInfo.isComplate && downloadInfo.status != DOWNLOAD_STATUS.RUN) {
// const result = getStartTask(downloadList, DOWNLOAD_STATUS, rootState.setting.download.maxDownloadNum)
// if (result === false) {
// commit('setStatus', { downloadInfo, status: DOWNLOAD_STATUS.WAITING })
// return
// }
// } else {
// const result = getStartTask(downloadList, DOWNLOAD_STATUS, rootState.setting.download.maxDownloadNum)
// if (!result) return
// downloadInfo = result
// }
// commit('setStatus', { downloadInfo, status: DOWNLOAD_STATUS.RUN })
let dl = dls.get(downloadInfo.id)
if (dl) {
// commit('updateFilePath', {
// downloadInfo,
// filePath: path.join(rootState.setting.download.savePath, downloadInfo.metadata.fileName),
// })
dl.updateSaveInfo(savePath, downloadInfo.metadata.fileName)
if (tryNum.has(downloadInfo.id)) tryNum.set(downloadInfo.id, 0)
try {
await dl.start()
} catch (error) {
// commit('onError', { downloadInfo, errorMsg: error.message })
// commit('setStatusText', error.message)
// await dispatch('startTask')
}
} else {
await createTask(downloadInfo, savePath)
// await dispatch('handleStartTask', downloadInfo)
}
}
export const pauseTask = async(id: string) => {
const dl = dls.get(id)
if (dl) {
dls.delete(id)
tasks.delete(id)
taskActions.delete(id)
tryNum.delete(id)
try {
await dl.stop()
} catch (e) {
console.log(e)
}
}
// commit('setStatus', { downloadInfo: downloadInfo, status: DOWNLOAD_STATUS.PAUSE })
}
export const removeTask = async(id: string) => {
const dl = dls.get(id)
const downloadInfo = tasks.get(id)
if (dl) {
dls.delete(id)
tasks.delete(id)
taskActions.delete(id)
tryNum.delete(id)
try {
await dl.stop()
} catch (e) {
console.log(e)
}
}
if (downloadInfo) {
// 没有未完成、已下载大于1k
if (!downloadInfo.isComplate && downloadInfo.total && downloadInfo.downloaded > 1024) {
try {
await removeFile(downloadInfo.metadata.filePath)
} catch (_) {}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
import { exposeWorker } from '../utils/worker'
import * as common from './common'
import * as download from './download'
console.log('hello download worker')
exposeWorker(Object.assign({}, common, download))
export declare type workerDownloadTypes = typeof common &
typeof download
+103
View File
@@ -0,0 +1,103 @@
import { DOWNLOAD_STATUS, QUALITYS } from '@common/constants'
import { filterFileName } from '@common/utils/common'
import { joinPath } from '@common/utils/nodejs'
import fs from 'fs'
/**
* 保存歌词文件
* @param {*} filePath
* @param {*} lrc
* @param {*} format
*/
export const saveLrc = async(filePath: string, lrc: string, format: LX.LyricFormat) => {
const iconv = await import('iconv-lite')
switch (format) {
case 'gbk':
fs.writeFile(filePath, iconv.encode(lrc, 'gbk', { addBOM: true }), err => {
if (err) console.log(err)
})
break
case 'utf8':
default:
fs.writeFile(filePath, lrc, 'utf8', err => {
if (err) console.log(err)
})
break
}
}
export const getExt = (type: string): LX.Download.FileExt => {
switch (type) {
case 'ape':
return 'ape'
case 'flac':
case 'flac24bit':
return 'flac'
case 'wav':
return 'wav'
case '128k':
case '192k':
case '320k':
default:
return 'mp3'
}
}
/**
* 获取音乐音质
* @param musicInfo
* @param type
* @param qualityList
*/
export const getMusicType = (musicInfo: LX.Music.MusicInfoOnline, type: LX.Quality, qualityList: LX.QualityList): LX.Quality => {
let list = qualityList[musicInfo.source]
if (!list) return '128k'
if (!list.includes(type)) type = list[list.length - 1]
const rangeType = QUALITYS.slice(QUALITYS.indexOf(type))
for (const type of rangeType) {
if (musicInfo.meta._qualitys[type]) return type
}
return '128k'
}
// const checkExistList = (list: LX.Download.ListItem[], musicInfo: LX.Music.MusicInfo, type: LX.Quality, ext: string): boolean => {
// return list.some(s => s.id === musicInfo.id && (s.metadata.type === type || s.metadata.ext === ext))
// }
export const createDownloadInfo = (musicInfo: LX.Music.MusicInfoOnline, type: LX.Quality, fileName: string, savePath: string, qualityList: LX.QualityList) => {
type = getMusicType(musicInfo, type, qualityList)
let ext = getExt(type)
const key = `${musicInfo.id}_${type}_${ext}`
// if (checkExistList(list, musicInfo, type, ext)) return null
const downloadInfo: LX.Download.ListItem = {
id: key,
isComplate: false,
status: DOWNLOAD_STATUS.WAITING,
statusText: '待下载',
downloaded: 0,
total: 0,
progress: 0,
speed: '',
metadata: {
musicInfo,
url: null,
quality: type,
ext,
filePath: '',
fileName: filterFileName(`${fileName
.replace('歌名', musicInfo.name)
.replace('歌手', musicInfo.singer)}.${ext}`),
},
}
downloadInfo.metadata.filePath = joinPath(savePath, downloadInfo.metadata.fileName)
// commit('addTask', downloadInfo)
// 删除同路径下的同名文件
// TODO
// deleteFile(downloadInfo.metadata.filePath)
// .catch(err => {
// if (err.code !== 'ENOENT') return commit('setStatusText', { downloadInfo, text: '文件删除失败' })
// })
return downloadInfo
}