diff --git a/publish/changeLog.md b/publish/changeLog.md index e1d73dd7..c26d985d 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -51,4 +51,4 @@ - 新增 `globalThis.lx.currentScriptInfo` 对象,可以从这里获取解析后的脚本头部注释信息及脚本原始内容,具体可用属性看文档说明 - `globalThis.lx.version` 属性更新到 `2.0.0` - 自定义源不再使用`script`标签的形式执行,若要获取脚本原始代码字符串需从 `globalThis.lx.currentScriptInfo.rawScript` 属性获取 -- +- 自定义源新增支持`local`源的`musicUrl`、`pic`、`lyric`的获取操作详情看自定义源文档说明 diff --git a/src/common/types/user_api.d.ts b/src/common/types/user_api.d.ts index e39ece72..841bd099 100644 --- a/src/common/types/user_api.d.ts +++ b/src/common/types/user_api.d.ts @@ -1,7 +1,7 @@ declare namespace LX { namespace UserApi { type UserApiSourceInfoType = 'music' - type UserApiSourceInfoActions = 'musicUrl' + type UserApiSourceInfoActions = 'musicUrl' | 'lyric' | 'pic' interface UserApiSourceInfo { name: string diff --git a/src/main/modules/userApi/renderer/preload.js b/src/main/modules/userApi/renderer/preload.js index ac416c6d..ba1f57ba 100644 --- a/src/main/modules/userApi/renderer/preload.js +++ b/src/main/modules/userApi/renderer/preload.js @@ -23,13 +23,14 @@ const eventNames = Object.values(EVENT_NAMES) const events = { request: null, } -const allSources = ['kw', 'kg', 'tx', 'wy', 'mg'] +const allSources = ['kw', 'kg', 'tx', 'wy', 'mg', 'local'] const supportQualitys = { kw: ['128k', '320k', 'flac', 'flac24bit'], kg: ['128k', '320k', 'flac', 'flac24bit'], tx: ['128k', '320k', 'flac', 'flac24bit'], wy: ['128k', '320k', 'flac', 'flac24bit'], mg: ['128k', '320k', 'flac', 'flac24bit'], + local: [], } const supportActions = { kw: ['musicUrl'], @@ -38,6 +39,18 @@ const supportActions = { wy: ['musicUrl'], mg: ['musicUrl'], xm: ['musicUrl'], + local: ['musicUrl', 'lyric', 'pic'], +} + +const verifyLyricInfo = (info) => { + if (typeof info != 'object' || typeof info.lyric != 'string') throw new Error('failed') + if (info.lyric.length > 4096) throw new Error('failed') + return { + lyric: info.lyric, + tlyric: (typeof info.tlyric == 'string' && info.tlyric.length < 4096) ? info.tlyric : null, + mlyric: typeof info.mlyric == 'string' && info.mlyric.length < 4096 ? info.mlyric : null, + lxlyric: typeof info.lxlyric == 'string' && info.lxlyric.length < 4096 ? info.lxlyric : null, + } } const handleRequest = (context, { requestKey, data }) => { @@ -50,6 +63,7 @@ const handleRequest = (context, { requestKey, data }) => { } switch (data.action) { case 'musicUrl': + if (typeof response != 'string' || response.length > 2048 || !/^https?:/.test(response)) throw new Error('failed') sendData.result = { source: data.source, action: data.action, @@ -59,6 +73,21 @@ const handleRequest = (context, { requestKey, data }) => { }, } break + case 'lyric': + sendData.result = { + source: data.source, + action: data.action, + data: verifyLyricInfo(response), + } + break + case 'pic': + if (typeof response != 'string' || response.length > 2048 || !/^https?:/.test(response)) throw new Error('failed') + sendData.result = { + source: data.source, + action: data.action, + data: response, + } + break } sendMessage(USER_API_RENDERER_EVENT_NAME.response, sendData, true) }).catch(err => { diff --git a/src/main/worker/dbService/modules/list/index.ts b/src/main/worker/dbService/modules/list/index.ts index b53cdec1..a9570af5 100644 --- a/src/main/worker/dbService/modules/list/index.ts +++ b/src/main/worker/dbService/modules/list/index.ts @@ -79,16 +79,16 @@ export const createUserLists = (position: number, lists: LX.List.UserListInfo[]) * 覆盖列表 * @param lists 列表信息 */ -export const setUserLists = (lists: LX.List.UserListInfo[]) => { - const newUserLists: LX.DBService.UserListInfo[] = lists.map((list, index) => { - return { - ...list, - position: index, - } - }) - inertUserLists(newUserLists, true) - userLists = newUserLists -} +// const setUserLists = (lists: LX.List.UserListInfo[]) => { +// const newUserLists: LX.DBService.UserListInfo[] = lists.map((list, index) => { +// return { +// ...list, +// position: index, +// } +// }) +// inertUserLists(newUserLists, true) +// userLists = newUserLists +// } /** * 批量删除列表 diff --git a/src/renderer/core/music/local.ts b/src/renderer/core/music/local.ts index 050f2dd7..76893183 100644 --- a/src/renderer/core/music/local.ts +++ b/src/renderer/core/music/local.ts @@ -6,8 +6,11 @@ import { getLocalFilePath } from '@renderer/utils/music' import { buildLyricInfo, getCachedLyricInfo, + getOnlineOtherSourceLyricByLocal, getOnlineOtherSourceLyricInfo, getOnlineOtherSourceMusicUrl, + getOnlineOtherSourceMusicUrlByLocal, + getOnlineOtherSourcePicByLocal, getOnlineOtherSourcePicUrl, getOtherSource, } from './utils' @@ -72,6 +75,14 @@ export const getMusicUrl = async({ musicInfo, isRefresh, onToggleSource = () => const path = await getLocalFilePath(musicInfo) if (path) return encodePath(path) } + + try { + return await getOnlineOtherSourceMusicUrlByLocal(musicInfo, isRefresh).then(({ url, quality, isFromCache }) => { + if (!isFromCache) void saveMusicUrl(musicInfo, quality, url) + return url + }) + } catch {} + onToggleSource() const otherSource = await getOtherSourceByLocal(musicInfo) if (!otherSource.length) throw new Error('source not found') @@ -97,6 +108,12 @@ export const getPicUrl = async({ musicInfo, listId, isRefresh, onToggleSource = if (musicInfo.meta.picUrl) return musicInfo.meta.picUrl } + try { + return await getOnlineOtherSourcePicByLocal(musicInfo).then(({ url }) => { + return url + }) + } catch {} + onToggleSource() const otherSource = await getOtherSourceByLocal(musicInfo) if (!otherSource.length) throw new Error('source not found') @@ -126,6 +143,14 @@ export const getLyricInfo = async({ musicInfo, isRefresh, onToggleSource = () => if (lyricInfo?.lyric) return buildLyricInfo(lyricInfo) } + try { + // eslint-disable-next-line @typescript-eslint/promise-function-async + return await getOnlineOtherSourceLyricByLocal(musicInfo, isRefresh).then(({ lyricInfo, isFromCache }) => { + if (!isFromCache) void saveLyric(musicInfo, lyricInfo) + return buildLyricInfo(lyricInfo) + }) + } catch {} + onToggleSource() const otherSource = await getOtherSourceByLocal(musicInfo) if (!otherSource.length) throw new Error('source not found') diff --git a/src/renderer/core/music/utils.ts b/src/renderer/core/music/utils.ts index c7a0b451..cc6e631a 100644 --- a/src/renderer/core/music/utils.ts +++ b/src/renderer/core/music/utils.ts @@ -10,6 +10,7 @@ import { import { appSetting } from '@renderer/store/setting' import { langS2T, toNewMusicInfo, toOldMusicInfo } from '@renderer/utils' import { requestMsg } from '@renderer/utils/message' +import { apis } from '@renderer/utils/musicSdk/api-source' const getOtherSourcePromises = new Map() @@ -147,6 +148,68 @@ export const getCachedLyricInfo = async(musicInfo: LX.Music.MusicInfo): Promise< return null } +export const getOnlineOtherSourceMusicUrlByLocal = async(musicInfo: LX.Music.MusicInfoLocal, isRefresh: boolean): Promise<{ + url: string + quality: LX.Quality + isFromCache: boolean +}> => { + if (!await window.lx.apiInitPromise[0]) throw new Error('source init failed') + + const quality = '128k' + + const cachedUrl = await getStoreMusicUrl(musicInfo, quality) + if (cachedUrl && !isRefresh) return { url: cachedUrl, quality, isFromCache: true } + + let reqPromise + try { + reqPromise = apis('local').getMusicUrl(toOldMusicInfo(musicInfo), null).promise + } catch (err: any) { + reqPromise = Promise.reject(err) + } + + return reqPromise.then(({ url }: { url: string }) => { + return { url, quality, isFromCache: false } + }) +} + +export const getOnlineOtherSourceLyricByLocal = async(musicInfo: LX.Music.MusicInfoLocal, isRefresh: boolean): Promise<{ + lyricInfo: LX.Music.LyricInfo + isFromCache: boolean +}> => { + if (!await window.lx.apiInitPromise[0]) throw new Error('source init failed') + + const lyricInfo = await getCachedLyricInfo(musicInfo) + if (lyricInfo && !isRefresh) return { lyricInfo, isFromCache: true } + + let reqPromise + try { + reqPromise = apis('local').getLyric(toOldMusicInfo(musicInfo)).promise + } catch (err: any) { + reqPromise = Promise.reject(err) + } + + return reqPromise.then((lyricInfo: LX.Music.LyricInfo) => { + return { lyricInfo, isFromCache: false } + }) +} + +export const getOnlineOtherSourcePicByLocal = async(musicInfo: LX.Music.MusicInfoLocal): Promise<{ + url: string +}> => { + if (!await window.lx.apiInitPromise[0]) throw new Error('source init failed') + + let reqPromise + try { + reqPromise = apis('local').getPic(toOldMusicInfo(musicInfo)).promise + } catch (err: any) { + reqPromise = Promise.reject(err) + } + + return reqPromise.then((url: string) => { + return { url } + }) +} + export const getPlayQuality = (highQuality: boolean, musicInfo: LX.Music.MusicInfoOnline): LX.Quality => { let type: LX.Quality = '128k' let list = qualityList.value[musicInfo.source] diff --git a/src/renderer/core/useApp/useInitUserApi.ts b/src/renderer/core/useApp/useInitUserApi.ts index dfdf1672..4042667a 100644 --- a/src/renderer/core/useApp/useInitUserApi.ts +++ b/src/renderer/core/useApp/useInitUserApi.ts @@ -57,7 +57,6 @@ export default () => { // eslint-disable-next-line @typescript-eslint/promise-function-async }).then(res => { // console.log(res) - if (!/^https?:/.test(res.data.url)) return Promise.reject(new Error('Get url failed')) return { type, url: res.data.url } }).catch(async err => { console.log(err.message) @@ -66,7 +65,62 @@ export default () => { } } 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 }