mirror of
https://github.com/timeshiftsauce/CeruMusic.git
synced 2025-11-25 03:15:07 +08:00
1. 支持暗黑主题 2. 调整插件页面ui
This commit is contained in:
@@ -5,6 +5,11 @@ export default defineConfig({
|
||||
lang: 'zh-CN',
|
||||
title: 'Ceru Music',
|
||||
base: '/',
|
||||
head: [
|
||||
['link', { rel: 'icon', href: '/logo.svg' }],
|
||||
['meta', { name: 'author', href: '时迁酱,无聊的霜霜,star' }],
|
||||
['meta', { name: 'keywords', content: 'Ceru Music,音乐播放器,音乐播放器工具,音乐播放器软件,音乐播放器下载,音乐播放器下载地址,澜音播放器,免费的音乐播放器,cerumusic,时迁酱,周晨鹭,无聊的霜霜,star,洛雪音乐,洛雪'}]
|
||||
],
|
||||
description:
|
||||
'Ceru Music 是基于 Electron 和 Vue 开发的跨平台桌面音乐播放器工具,一个跨平台的音乐播放器应用,支持基于合规插件获取公开音乐信息与播放功能。',
|
||||
markdown: {
|
||||
@@ -77,8 +82,7 @@ export default defineConfig({
|
||||
sitemap: {
|
||||
hostname: 'https://ceru.docs.shiqianjiang.cn'
|
||||
},
|
||||
lastUpdated: true,
|
||||
head: [['link', { rel: 'icon', href: '/logo.svg' }]]
|
||||
lastUpdated: true
|
||||
})
|
||||
console.log(process.env.BASE_URL_DOCS)
|
||||
// Smooth scrolling functions
|
||||
|
||||
@@ -4,7 +4,6 @@ import { join } from 'path'
|
||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||
import icon from '../../resources/logo.png?asset'
|
||||
import path from 'node:path'
|
||||
import musicService from './services/music'
|
||||
import pluginService from './services/plugin'
|
||||
import aiEvents from './events/ai'
|
||||
import './services/musicSdk/index'
|
||||
@@ -270,10 +269,6 @@ ipcMain.handle('service-plugin-uninstallPlugin', async (_, pluginId): Promise<an
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('service-music-request', async (_, api, args) => {
|
||||
return await musicService.request(api, args)
|
||||
})
|
||||
|
||||
// 获取应用版本号
|
||||
ipcMain.handle('get-app-version', () => {
|
||||
return app.getVersion()
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
import { MusicServiceBase, ServiceNamesType, ServiceArgsType } from './service-base'
|
||||
import {
|
||||
GetToplistArgs,
|
||||
SearchArgs,
|
||||
GetLyricArgs,
|
||||
GetSongDetailArgs,
|
||||
GetSongUrlArgs,
|
||||
GetToplistDetailArgs,
|
||||
GetListSongsArgs,
|
||||
DownloadSingleSongArgs
|
||||
} from './service-base'
|
||||
import { netEaseService } from './net-ease-service'
|
||||
import { AxiosError } from 'axios'
|
||||
|
||||
const musicService: MusicServiceBase = netEaseService
|
||||
|
||||
type Response = {
|
||||
success: boolean
|
||||
data?: any
|
||||
error?: any
|
||||
}
|
||||
|
||||
async function request(api: ServiceNamesType, args: ServiceArgsType): Promise<any> {
|
||||
const res: Response = { success: false }
|
||||
try {
|
||||
switch (api) {
|
||||
case 'search':
|
||||
res.data = await musicService.search(args as SearchArgs)
|
||||
break
|
||||
case 'getSongDetail':
|
||||
res.data = await musicService.getSongDetail(args as GetSongDetailArgs)
|
||||
break
|
||||
case 'getSongUrl':
|
||||
res.data = await musicService.getSongUrl(args as GetSongUrlArgs)
|
||||
break
|
||||
case 'getLyric':
|
||||
res.data = await musicService.getLyric(args as GetLyricArgs)
|
||||
break
|
||||
case 'getToplist':
|
||||
res.data = await musicService.getToplist(args as GetToplistArgs)
|
||||
break
|
||||
case 'getToplistDetail':
|
||||
res.data = await musicService.getToplistDetail(args as GetToplistDetailArgs)
|
||||
break
|
||||
case 'getListSongs':
|
||||
res.data = await musicService.getListSongs(args as GetListSongsArgs)
|
||||
break
|
||||
case 'downloadSingleSong':
|
||||
res.data = await musicService.downloadSingleSong(args as DownloadSingleSongArgs)
|
||||
break
|
||||
default:
|
||||
throw new Error(`未知的方法: ${api}`)
|
||||
}
|
||||
|
||||
res.success = true
|
||||
} catch (error: any) {
|
||||
if (error instanceof AxiosError) {
|
||||
error.message = '网络错误'
|
||||
}
|
||||
|
||||
console.error('请求失败: ', error)
|
||||
res.error = error
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
export default { request }
|
||||
// netEaseService
|
||||
// .search({
|
||||
// keyword: '稻香',
|
||||
// type: 1,
|
||||
// limit: 25
|
||||
// })
|
||||
// .then((res) => {
|
||||
// console.log(res)
|
||||
// })
|
||||
@@ -1,398 +0,0 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import fsPromise from 'fs/promises'
|
||||
|
||||
import NeteaseCloudMusicApi from 'NeteaseCloudMusicApi'
|
||||
import { axiosClient, MusicServiceBase } from './service-base'
|
||||
import { pipeline } from 'node:stream/promises'
|
||||
import pluginService from '../plugin'
|
||||
import musicSdk from '../../utils/musicSdk'
|
||||
|
||||
import {
|
||||
SearchArgs,
|
||||
GetSongDetailArgs,
|
||||
GetSongUrlArgs,
|
||||
GetToplistDetailArgs,
|
||||
GetListSongsArgs,
|
||||
GetLyricArgs,
|
||||
GetToplistArgs,
|
||||
DownloadSingleSongArgs
|
||||
} from './service-base'
|
||||
|
||||
import { SongDetailResponse, SongResponse } from './service-base'
|
||||
|
||||
import { fieldsSelector } from '../../utils/object'
|
||||
import { getAppDirPath } from '../../utils/path'
|
||||
|
||||
// 音乐源映射
|
||||
const MUSIC_SOURCES = {
|
||||
kg: 'kg', // 酷狗音乐
|
||||
wy: 'wy', // 网易云音乐
|
||||
tx: 'tx', // QQ音乐
|
||||
kw: 'kw', // 酷我音乐
|
||||
mg: 'mg' // 咪咕音乐
|
||||
}
|
||||
|
||||
// 扩展搜索参数接口
|
||||
interface ExtendedSearchArgs extends SearchArgs {
|
||||
source?: string // 音乐源参数 kg|wy|tx|kw|mg
|
||||
}
|
||||
|
||||
// 扩展歌曲详情参数接口
|
||||
interface ExtendedGetSongDetailArgs extends GetSongDetailArgs {
|
||||
source?: string
|
||||
}
|
||||
|
||||
// 扩展歌词参数接口
|
||||
interface ExtendedGetLyricArgs extends GetLyricArgs {
|
||||
source?: string
|
||||
}
|
||||
|
||||
const baseUrl: string = 'https://music.163.com'
|
||||
const baseTwoUrl: string = 'https://www.lihouse.xyz/coco_widget'
|
||||
|
||||
const fileLock: Record<string, boolean> = {}
|
||||
|
||||
/**
|
||||
* 获取支持的音乐源列表
|
||||
*/
|
||||
export const getSupportedSources = () => {
|
||||
return Object.keys(MUSIC_SOURCES).map((key) => ({
|
||||
id: key,
|
||||
name: getSourceName(key),
|
||||
available: !!musicSdk[key]
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取音乐源名称
|
||||
*/
|
||||
const getSourceName = (source: string): string => {
|
||||
const sourceNames = {
|
||||
kg: '酷狗音乐',
|
||||
wy: '网易云音乐',
|
||||
tx: 'QQ音乐',
|
||||
kw: '酷我音乐',
|
||||
mg: '咪咕音乐'
|
||||
}
|
||||
return sourceNames[source] || source
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能音乐匹配(使用musicSdk的findMusic功能)
|
||||
*/
|
||||
export const findMusic = async (musicInfo: {
|
||||
name: string
|
||||
singer?: string
|
||||
albumName?: string
|
||||
interval?: string
|
||||
source?: string
|
||||
}) => {
|
||||
try {
|
||||
return await musicSdk.findMusic(musicInfo)
|
||||
} catch (error) {
|
||||
console.error('智能音乐匹配失败:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export const netEaseService: MusicServiceBase = {
|
||||
async search({
|
||||
type,
|
||||
keyword,
|
||||
offset,
|
||||
limit,
|
||||
source
|
||||
}: ExtendedSearchArgs): Promise<SongResponse> {
|
||||
// 如果指定了音乐源且不是网易云,使用对应的musicSdk
|
||||
if (source && source !== 'wy' && MUSIC_SOURCES[source]) {
|
||||
try {
|
||||
const sourceModule = musicSdk[source]
|
||||
if (sourceModule && sourceModule.musicSearch) {
|
||||
const page = Math.floor((offset || 0) / (limit || 25)) + 1
|
||||
const result = await sourceModule.musicSearch.search(keyword, page, limit || 25)
|
||||
|
||||
// 转换为统一格式
|
||||
return {
|
||||
songs: result.list || [],
|
||||
songCount: result.total || result.list?.length || 0
|
||||
}
|
||||
} else {
|
||||
throw new Error(`不支持的音乐源: ${source}`)
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(`${source}音乐源搜索失败:`, error)
|
||||
// 如果指定源失败,回退到网易云
|
||||
console.log('回退到网易云音乐搜索')
|
||||
}
|
||||
}
|
||||
|
||||
// 默认使用网易云音乐搜索
|
||||
return await axiosClient
|
||||
.get(`${baseUrl}/api/search/get/web`, {
|
||||
params: {
|
||||
s: keyword,
|
||||
type: type,
|
||||
limit: limit,
|
||||
offset: offset ?? 0
|
||||
}
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.code !== 200) {
|
||||
console.error(data)
|
||||
throw new Error(data.msg)
|
||||
}
|
||||
return data.result
|
||||
})
|
||||
},
|
||||
async getSongDetail({ ids, source }: ExtendedGetSongDetailArgs): Promise<SongDetailResponse> {
|
||||
// 如果指定了音乐源且不是网易云,使用对应的musicSdk
|
||||
if (source && source !== 'wy' && MUSIC_SOURCES[source]) {
|
||||
try {
|
||||
const sourceModule = musicSdk[source]
|
||||
if (sourceModule && sourceModule.musicInfo) {
|
||||
// 对于多个ID,并行获取详情
|
||||
const promises = ids.map((id) => sourceModule.musicInfo.getMusicInfo(id))
|
||||
const results = await Promise.all(promises)
|
||||
return results.filter((result: any) => result) // 过滤掉失败的结果
|
||||
} else {
|
||||
throw new Error(`不支持的音乐源: ${source}`)
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(`${source}音乐源获取歌曲详情失败:`, error)
|
||||
// 如果指定源失败,回退到网易云
|
||||
console.log('回退到网易云音乐获取歌曲详情')
|
||||
}
|
||||
}
|
||||
|
||||
// 默认使用网易云音乐
|
||||
return await axiosClient
|
||||
.get(`${baseUrl}/api/song/detail?ids=[${ids.join(',')}]`)
|
||||
.then(({ data }) => {
|
||||
if (data.code !== 200) {
|
||||
console.error(data)
|
||||
throw new Error(data.msg)
|
||||
}
|
||||
|
||||
return data.songs
|
||||
})
|
||||
},
|
||||
async getSongUrl({ id, pluginId, quality, source }: GetSongUrlArgs): Promise<any> {
|
||||
// 如果提供了插件ID、音质和音乐源,则使用插件获取音乐URL
|
||||
if (pluginId && (quality || source)) {
|
||||
try {
|
||||
// 获取插件实例
|
||||
const plugin = pluginService.getPluginById(pluginId)
|
||||
if (!plugin) {
|
||||
throw new Error(`未找到ID为 ${pluginId} 的插件`)
|
||||
}
|
||||
|
||||
// 准备音乐信息对象,确保符合MusicInfo类型要求
|
||||
const musicInfo = {
|
||||
songmid: id as unknown as number,
|
||||
singer: '',
|
||||
name: '',
|
||||
albumName: '',
|
||||
albumId: 0,
|
||||
source: source || 'wy',
|
||||
interval: '',
|
||||
img: '',
|
||||
lrc: null,
|
||||
types: [],
|
||||
_types: {},
|
||||
typeUrl: {}
|
||||
}
|
||||
|
||||
// 调用插件的getMusicUrl方法获取音乐URL
|
||||
const url: string = await plugin.getMusicUrl(
|
||||
source || 'wy',
|
||||
musicInfo,
|
||||
quality || 'standard'
|
||||
)
|
||||
|
||||
// 构建返回对象
|
||||
return { url }
|
||||
} catch (error: any) {
|
||||
console.error('通过插件获取音乐URL失败:', error)
|
||||
throw new Error(`插件获取音乐URL失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有提供插件信息或插件调用失败,则使用默认方法获取
|
||||
return await axiosClient.get(`${baseTwoUrl}/music_resource/id/${id}`).then(({ data }) => {
|
||||
if (!data.status) {
|
||||
throw new Error('歌曲不存在')
|
||||
}
|
||||
|
||||
return data.song_data
|
||||
})
|
||||
},
|
||||
async getLyric({ id, lv, yv, tv, source }: ExtendedGetLyricArgs): Promise<any> {
|
||||
// 如果指定了音乐源且不是网易云,使用对应的musicSdk
|
||||
if (source && source !== 'wy' && MUSIC_SOURCES[source]) {
|
||||
try {
|
||||
const sourceModule = musicSdk[source]
|
||||
if (sourceModule && sourceModule.getLyric) {
|
||||
// 构建歌曲信息对象,不同源可能需要不同的参数
|
||||
const songInfo = { id, songmid: id, hash: id }
|
||||
const result = await sourceModule.getLyric(songInfo)
|
||||
|
||||
// 转换为统一格式
|
||||
return {
|
||||
lrc: { lyric: result.lyric || '' },
|
||||
tlyric: { lyric: result.tlyric || '' },
|
||||
yrc: { lyric: result.yrc || '' }
|
||||
}
|
||||
} else {
|
||||
throw new Error(`不支持的音乐源: ${source}`)
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(`${source}音乐源获取歌词失败:`, error)
|
||||
// 如果指定源失败,回退到网易云
|
||||
console.log('回退到网易云音乐获取歌词')
|
||||
}
|
||||
}
|
||||
|
||||
// 默认使用网易云音乐
|
||||
const optionalParams: any = {}
|
||||
if (lv) {
|
||||
optionalParams.lv = -1
|
||||
}
|
||||
if (yv) {
|
||||
optionalParams.yv = -1
|
||||
}
|
||||
if (tv) {
|
||||
optionalParams.tv = -1
|
||||
}
|
||||
|
||||
return await axiosClient
|
||||
.get(`${baseUrl}/api/song/lyric`, {
|
||||
params: {
|
||||
id: id,
|
||||
...optionalParams
|
||||
}
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.code !== 200) {
|
||||
console.error(data)
|
||||
throw Error(data.msg)
|
||||
}
|
||||
|
||||
const requiredFields = ['lyricUser', 'lrc', 'tlyric', 'yrc']
|
||||
return fieldsSelector(data, requiredFields)
|
||||
})
|
||||
},
|
||||
async getToplist({}: GetToplistArgs): Promise<any> {
|
||||
return await NeteaseCloudMusicApi.toplist({})
|
||||
.then(({ body: data }) => {
|
||||
return data.list
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.error({
|
||||
code: err.body?.code,
|
||||
msg: err.body?.msg?.message
|
||||
})
|
||||
throw err.body?.msg ?? err
|
||||
})
|
||||
},
|
||||
async getToplistDetail({}: GetToplistDetailArgs): Promise<any> {
|
||||
return await NeteaseCloudMusicApi.toplist_detail({})
|
||||
.then(({ body: data }) => {
|
||||
return data.list
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.error({
|
||||
code: err.body?.code,
|
||||
msg: err.body?.msg?.message
|
||||
})
|
||||
throw err.body?.msg ?? err
|
||||
})
|
||||
},
|
||||
async getListSongs(args: GetListSongsArgs): Promise<any> {
|
||||
return await NeteaseCloudMusicApi.playlist_track_all(args)
|
||||
.then(({ body: data }) => {
|
||||
const requiredFields = ['songs', 'privileges']
|
||||
return fieldsSelector(data, requiredFields)
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.error({
|
||||
code: err.body?.code,
|
||||
msg: err.body?.msg?.message
|
||||
})
|
||||
throw err.body?.msg ?? err
|
||||
})
|
||||
},
|
||||
async downloadSingleSong({
|
||||
id,
|
||||
name,
|
||||
artist,
|
||||
pluginId,
|
||||
source,
|
||||
quality
|
||||
}: DownloadSingleSongArgs) {
|
||||
const { url } = await this.getSongUrl({ id, pluginId, source, quality })
|
||||
|
||||
// 从URL中提取文件扩展名,如果没有则默认为mp3
|
||||
const getFileExtension = (url: string): string => {
|
||||
try {
|
||||
const urlObj = new URL(url)
|
||||
const pathname = urlObj.pathname
|
||||
const lastDotIndex = pathname.lastIndexOf('.')
|
||||
if (lastDotIndex !== -1 && lastDotIndex < pathname.length - 1) {
|
||||
const extension = pathname.substring(lastDotIndex + 1).toLowerCase()
|
||||
// 验证是否为常见的音频格式
|
||||
const validExtensions = ['mp3', 'flac', 'wav', 'aac', 'm4a', 'ogg', 'wma']
|
||||
if (validExtensions.includes(extension)) {
|
||||
return extension
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('解析URL失败,使用默认扩展名:', error)
|
||||
}
|
||||
return 'mp3' // 默认扩展名
|
||||
}
|
||||
|
||||
const fileExtension = getFileExtension(url)
|
||||
const songPath = path.join(
|
||||
getAppDirPath(),
|
||||
'download',
|
||||
'songs',
|
||||
`${name}-${artist}-${id}.${fileExtension}`
|
||||
.replace(/[/\\:*?"<>|]/g, '')
|
||||
.replace(/^\.+/, '')
|
||||
.replace(/\.+$/, '')
|
||||
.trim()
|
||||
)
|
||||
|
||||
if (fileLock[songPath]) {
|
||||
throw new Error('歌曲正在下载中')
|
||||
} else {
|
||||
fileLock[songPath] = true
|
||||
}
|
||||
|
||||
try {
|
||||
if (fs.existsSync(songPath)) {
|
||||
return {
|
||||
message: '歌曲已存在'
|
||||
}
|
||||
}
|
||||
|
||||
await fsPromise.mkdir(path.dirname(songPath), { recursive: true })
|
||||
|
||||
const songDataRes = await axiosClient({
|
||||
method: 'GET',
|
||||
url: url,
|
||||
responseType: 'stream'
|
||||
})
|
||||
|
||||
await pipeline(songDataRes.data, fs.createWriteStream(songPath))
|
||||
} finally {
|
||||
delete fileLock[songPath]
|
||||
}
|
||||
|
||||
return {
|
||||
message: '下载成功',
|
||||
path: songPath
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
import axios, { AxiosInstance } from 'axios'
|
||||
|
||||
const timeout: number = 5000
|
||||
|
||||
const mobileHeaders = {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148'
|
||||
}
|
||||
|
||||
const axiosClient: AxiosInstance = axios.create({
|
||||
timeout: timeout
|
||||
})
|
||||
|
||||
type SearchArgs = {
|
||||
type: number
|
||||
keyword: string
|
||||
offset?: number
|
||||
limit: number
|
||||
source?: string
|
||||
}
|
||||
|
||||
type GetSongDetailArgs = {
|
||||
ids: string[]
|
||||
}
|
||||
|
||||
type GetSongUrlArgs = {
|
||||
id: string
|
||||
pluginId?: string // 插件ID
|
||||
quality?: string // 音质
|
||||
source?: string // 音乐源(wy, tx等)
|
||||
}
|
||||
|
||||
type GetLyricArgs = {
|
||||
id: string
|
||||
lv?: boolean
|
||||
yv?: boolean // 获取逐字歌词
|
||||
tv?: boolean // 获取歌词翻译
|
||||
}
|
||||
|
||||
type GetToplistArgs = Record<string, never>
|
||||
|
||||
type GetToplistDetailArgs = Record<string, never>
|
||||
|
||||
type GetListSongsArgs = {
|
||||
id: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
type DownloadSingleSongArgs = {
|
||||
id: string
|
||||
name: string
|
||||
artist: string
|
||||
pluginId?: string
|
||||
quality?: string
|
||||
source?: string
|
||||
}
|
||||
|
||||
type ServiceNamesType =
|
||||
| 'search'
|
||||
| 'getSongDetail'
|
||||
| 'getSongUrl'
|
||||
| 'getLyric'
|
||||
| 'getToplist'
|
||||
| 'getToplistDetail'
|
||||
| 'getListSongs'
|
||||
| 'downloadSingleSong'
|
||||
|
||||
type ServiceArgsType =
|
||||
| SearchArgs
|
||||
| GetSongDetailArgs
|
||||
| GetSongUrlArgs
|
||||
| GetLyricArgs
|
||||
| GetToplistArgs
|
||||
| GetToplistDetailArgs
|
||||
| GetListSongsArgs
|
||||
| DownloadSingleSongArgs
|
||||
|
||||
interface Artist {
|
||||
id: number
|
||||
name: string
|
||||
picUrl: string | null
|
||||
alias: string[]
|
||||
albumSize: number
|
||||
picId: number
|
||||
fansGroup: null
|
||||
img1v1Url: string
|
||||
img1v1: number
|
||||
trans: null
|
||||
}
|
||||
|
||||
interface Album {
|
||||
id: number
|
||||
name: string
|
||||
artist: {
|
||||
id: number
|
||||
name: string
|
||||
picUrl: string | null
|
||||
alias: string[]
|
||||
albumSize: number
|
||||
picId: number
|
||||
fansGroup: null
|
||||
img1v1Url: string
|
||||
img1v1: number
|
||||
trans: null
|
||||
}
|
||||
publishTime: number
|
||||
size: number
|
||||
copyrightId: number
|
||||
status: number
|
||||
picId: number
|
||||
alia?: string[]
|
||||
mark: number
|
||||
}
|
||||
|
||||
interface Song {
|
||||
id: number
|
||||
name: string
|
||||
artists: Artist[]
|
||||
album: Album
|
||||
duration: number
|
||||
copyrightId: number
|
||||
status: number
|
||||
alias: string[]
|
||||
rtype: number
|
||||
ftype: number
|
||||
mvid: number
|
||||
fee: number
|
||||
rUrl: null
|
||||
mark: number
|
||||
transNames?: string[]
|
||||
}
|
||||
|
||||
interface SongResponse {
|
||||
songs: Song[]
|
||||
songCount: number
|
||||
}
|
||||
interface AlbumDetail {
|
||||
name: string
|
||||
id: number
|
||||
type: string
|
||||
size: number
|
||||
picId: number
|
||||
blurPicUrl: string
|
||||
companyId: number
|
||||
pic: number
|
||||
picUrl: string
|
||||
publishTime: number
|
||||
description: string
|
||||
tags: string
|
||||
company: string
|
||||
briefDesc: string
|
||||
artist: {
|
||||
name: string
|
||||
id: number
|
||||
picId: number
|
||||
img1v1Id: number
|
||||
briefDesc: string
|
||||
picUrl: string
|
||||
img1v1Url: string
|
||||
albumSize: number
|
||||
alias: string[]
|
||||
trans: string
|
||||
musicSize: number
|
||||
topicPerson: number
|
||||
}
|
||||
songs: any[]
|
||||
alias: string[]
|
||||
status: number
|
||||
copyrightId: number
|
||||
commentThreadId: string
|
||||
artists: Artist[]
|
||||
subType: string
|
||||
transName: null
|
||||
onSale: boolean
|
||||
mark: number
|
||||
gapless: number
|
||||
dolbyMark: number
|
||||
}
|
||||
interface MusicQuality {
|
||||
name: null
|
||||
id: number
|
||||
size: number
|
||||
extension: string
|
||||
sr: number
|
||||
dfsId: number
|
||||
bitrate: number
|
||||
playTime: number
|
||||
volumeDelta: number
|
||||
}
|
||||
interface SongDetail {
|
||||
name: string
|
||||
id: number
|
||||
position: number
|
||||
alias: string[]
|
||||
status: number
|
||||
fee: number
|
||||
copyrightId: number
|
||||
disc: string
|
||||
no: number
|
||||
artists: Artist[]
|
||||
album: AlbumDetail
|
||||
starred: boolean
|
||||
popularity: number
|
||||
score: number
|
||||
starredNum: number
|
||||
duration: number
|
||||
playedNum: number
|
||||
dayPlays: number
|
||||
hearTime: number
|
||||
sqMusic: MusicQuality
|
||||
hrMusic: null
|
||||
ringtone: null
|
||||
crbt: null
|
||||
audition: null
|
||||
copyFrom: string
|
||||
commentThreadId: string
|
||||
rtUrl: null
|
||||
ftype: number
|
||||
rtUrls: any[]
|
||||
copyright: number
|
||||
transName: null
|
||||
sign: null
|
||||
mark: number
|
||||
originCoverType: number
|
||||
originSongSimpleData: null
|
||||
single: number
|
||||
noCopyrightRcmd: null
|
||||
hMusic: MusicQuality
|
||||
mMusic: MusicQuality
|
||||
lMusic: MusicQuality
|
||||
bMusic: MusicQuality
|
||||
mvid: number
|
||||
mp3Url: null
|
||||
rtype: number
|
||||
rurl: null
|
||||
}
|
||||
|
||||
interface SongDetailResponse {
|
||||
songs: SongDetail[]
|
||||
equalizers: Record<string, unknown>
|
||||
code: number
|
||||
}
|
||||
interface SongUrlResponse {
|
||||
id: number
|
||||
url: string // 歌曲地址
|
||||
name: string
|
||||
artist: string
|
||||
pic: string //封面图片
|
||||
}
|
||||
|
||||
interface MusicServiceBase {
|
||||
search({ type, keyword, offset, limit }: SearchArgs): Promise<SongResponse>
|
||||
getSongDetail({ ids }: GetSongDetailArgs): Promise<SongDetailResponse>
|
||||
getSongUrl({ id, pluginId, quality, source }: GetSongUrlArgs): Promise<SongUrlResponse>
|
||||
getLyric({ id, lv, yv, tv }: GetLyricArgs): Promise<any>
|
||||
getToplist({}: GetToplistArgs): Promise<any>
|
||||
getToplistDetail({}: GetToplistDetailArgs): Promise<any>
|
||||
getListSongs({ id, limit, offset }: GetListSongsArgs): Promise<any>
|
||||
downloadSingleSong({ id }: DownloadSingleSongArgs): Promise<any>
|
||||
}
|
||||
|
||||
export type { MusicServiceBase, ServiceNamesType, ServiceArgsType }
|
||||
export type {
|
||||
SearchArgs,
|
||||
GetSongDetailArgs,
|
||||
GetSongUrlArgs,
|
||||
GetLyricArgs,
|
||||
GetToplistArgs,
|
||||
GetToplistDetailArgs,
|
||||
GetListSongsArgs,
|
||||
DownloadSingleSongArgs
|
||||
}
|
||||
export type { SongResponse, SongDetailResponse, SongUrlResponse }
|
||||
|
||||
export { mobileHeaders, axiosClient }
|
||||
1
src/preload/index.d.ts
vendored
1
src/preload/index.d.ts
vendored
@@ -11,7 +11,6 @@ interface CustomAPI {
|
||||
onMusicCtrl: (callback: (event: Event, args: any) => void) => () => void
|
||||
|
||||
music: {
|
||||
request: (api: string, args: any) => Promise<any>
|
||||
requestSdk: <T extends keyof MainApi>(
|
||||
method: T,
|
||||
args: {
|
||||
|
||||
@@ -29,7 +29,6 @@ const api = {
|
||||
},
|
||||
// 音乐相关方法
|
||||
music: {
|
||||
request: (api: string, args: any) => ipcRenderer.invoke('service-music-request', api, args),
|
||||
requestSdk: (api: string, args: any) =>
|
||||
ipcRenderer.invoke('service-music-sdk-request', api, args)
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user