257 lines
9.4 KiB
TypeScript

import { type InitParams, onScriptAction, sendAction, type ResponseParams, type UpdateInfoParams, type RequestParams } from '@/utils/nativeModules/userApi'
import { log, setUserApiList, setUserApiStatus } from '@/core/userApi'
import settingState from '@/store/setting/state'
import BackgroundTimer from 'react-native-background-timer'
import { fetchData } from './request'
import { getUserApiList } from '@/utils/data'
import { confirmDialog, openUrl, tipDialog } from '@/utils/tools'
export default async(setting: LX.AppSetting) => {
const userApiRequestMap = new Map<string, { resolve: (value: ResponseParams['result']) => void, reject: (error: Error) => void, timeout: number }>()
const scriptRequestMap = new Map<string, { request: Promise<any>, abort: () => void }>()
const cancelRequest = (requestKey: string, message: string) => {
const target = scriptRequestMap.get(requestKey)
if (!target) return
scriptRequestMap.delete(requestKey)
target.abort()
}
const sendScriptRequest = (requestKey: string, url: string, options: RequestParams['options']) => {
let req = fetchData(url, options)
req.request.then(response => {
// console.log(response)
sendAction('response', {
error: null,
requestKey,
response,
})
}).catch(err => {
sendAction('response', {
error: err.message,
requestKey,
response: null,
})
}).finally(() => {
scriptRequestMap.delete(requestKey)
})
scriptRequestMap.set(requestKey, req)
}
const sendUserApiRequest = async(data: LX.UserApi.UserApiRequestParams) => {
const handleApiUpdate = () => {
const target = userApiRequestMap.get(data.requestKey)
if (!target) return
userApiRequestMap.delete(data.requestKey)
BackgroundTimer.clearTimeout(target.timeout)
target.reject(new Error('request failed'))
}
const requestPromise = new Promise<ResponseParams['result']>((resolve, reject) => {
userApiRequestMap.set(data.requestKey, {
resolve,
reject,
timeout: BackgroundTimer.setTimeout(() => {
const target = userApiRequestMap.get(data.requestKey)
if (!target) return
userApiRequestMap.delete(data.requestKey)
target.reject(new Error('request timeout'))
}, 20_000),
})
sendAction('request', data)
}).finally(() => {
global.state_event.off('apiSourceUpdated', handleApiUpdate)
})
global.state_event.on('apiSourceUpdated', handleApiUpdate)
return requestPromise
}
const handleUserApiResponse = ({ status, result, requestKey, errorMessage }: ResponseParams) => {
const target = userApiRequestMap.get(requestKey)
if (!target) return
userApiRequestMap.delete(requestKey)
BackgroundTimer.clearTimeout(target.timeout)
if (status) target.resolve(result)
else target.reject(new Error(errorMessage ?? 'failed'))
}
const handleStateChange = ({ status, errorMessage, info }: InitParams) => {
// console.log(status, message, info)
setUserApiStatus(status, errorMessage)
if (!info || info.id !== settingState.setting['common.apiSource']) return
if (status) {
if (info.sources) {
let apis: any = {}
let qualitys: LX.QualityList = {}
for (const [source, { actions, type, qualitys: sourceQualitys }] of Object.entries(info.sources)) {
if (type != 'music') continue
apis[source as LX.Source] = {}
for (const action of actions) {
switch (action) {
case 'musicUrl':
apis[source].getMusicUrl = (songInfo: LX.Music.MusicInfo, type: LX.Quality) => {
const requestKey = `request__${Math.random().toString().substring(2)}`
return {
canceleFn() {
// userApiRequestCancel(requestKey)
},
promise: sendUserApiRequest({
requestKey,
data: {
source,
action: 'musicUrl',
info: {
type,
musicInfo: songInfo,
},
},
// eslint-disable-next-line @typescript-eslint/promise-function-async
}).then(res => {
// console.log(res)
return { type, url: res.data.url }
}).catch(err => {
console.log(err.message)
throw err
}),
}
}
break
case 'lyric':
apis[source].getLyric = (songInfo: LX.Music.MusicInfo) => {
const requestKey = `request__${Math.random().toString().substring(2)}`
return {
canceleFn() {
// userApiRequestCancel(requestKey)
},
promise: sendUserApiRequest({
requestKey,
data: {
source,
action: 'lyric',
info: {
type,
musicInfo: songInfo,
},
},
// eslint-disable-next-line @typescript-eslint/promise-function-async
}).then(res => {
// console.log(res)
return res.data
}).catch(async err => {
console.log(err.message)
return Promise.reject(err)
}),
}
}
break
case 'pic':
apis[source].getPic = (songInfo: LX.Music.MusicInfo) => {
const requestKey = `request__${Math.random().toString().substring(2)}`
return {
canceleFn() {
// userApiRequestCancel(requestKey)
},
promise: sendUserApiRequest({
requestKey,
data: {
source,
action: 'pic',
info: {
type,
musicInfo: songInfo,
},
},
// eslint-disable-next-line @typescript-eslint/promise-function-async
}).then(res => {
// console.log(res)
return res.data
}).catch(async err => {
console.log(err.message)
return Promise.reject(err)
}),
}
}
break
default:
break
}
}
qualitys[source as LX.Source] = sourceQualitys
}
global.lx.qualityList = qualitys
global.lx.apis = apis
global.state_event.apiSourceUpdated(settingState.setting['common.apiSource'])
}
} else {
if (errorMessage) {
void tipDialog({
message: `${global.i18n.t('user_api__init_failed_alert', { name: info.name })}\n${errorMessage}`,
// selection: true,
btnText: global.i18n.t('ok'),
})
}
}
if (!global.lx.apiInitPromise[1]) global.lx.apiInitPromise[2](status)
}
const showUpdateAlert = ({ name, log, updateUrl }: UpdateInfoParams) => {
if (updateUrl) {
void confirmDialog({
message: `${global.i18n.t('user_api_update_alert', { name })}\n${log}`,
// selection: true,
// showCancel: true,
confirmButtonText: global.i18n.t('user_api_update_alert_open_url'),
cancelButtonText: global.i18n.t('close'),
}).then(confirm => {
if (!confirm) return
setTimeout(() => {
void openUrl(updateUrl)
}, 300)
})
} else {
void tipDialog({
message: `${global.i18n.t('user_api_update_alert', { name })}\n${log}`,
// selection: true,
btnText: global.i18n.t('ok'),
})
}
}
onScriptAction((event) => {
// console.log('script actuon: ', event)
switch (event.action) {
case 'init':
if ((event as unknown as { errorMessage?: string }).errorMessage) event.data.errorMessage = (event as unknown as { errorMessage: string }).errorMessage
handleStateChange(event.data)
break
case 'response':
handleUserApiResponse(event.data)
break
case 'request':
sendScriptRequest(event.data.requestKey, event.data.url, event.data.options)
break
case 'cancelRequest':
cancelRequest(event.data, 'request canceled')
break
case 'showUpdateAlert':
showUpdateAlert(event.data)
break
case 'log':
switch ((event as unknown as { type: keyof typeof log }).type) {
case 'log':
case 'info':
log.info((event as unknown as { log: string }).log)
break
case 'error':
log.error((event as unknown as { log: string }).log)
break
case 'warn':
log.warn((event as unknown as { log: string }).log)
break
default:
break
}
break
default:
break
}
})
setUserApiList(await getUserApiList())
}