mirror of
https://github.com/GiriNeko/YesPlayMusic.git
synced 2025-12-16 21:28:06 +00:00
feat: updates
This commit is contained in:
parent
7ce516877e
commit
ccebe0a67a
74 changed files with 56065 additions and 2810 deletions
|
|
@ -1,24 +1,28 @@
|
|||
import fastify from 'fastify'
|
||||
import fastifyStatic from '@fastify/static'
|
||||
import path from 'path'
|
||||
import fastifyCookie from '@fastify/cookie'
|
||||
import fastifyMultipart from '@fastify/multipart'
|
||||
import fastifyStatic from '@fastify/static'
|
||||
import fastify from 'fastify'
|
||||
import path from 'path'
|
||||
import { isProd } from '../env'
|
||||
import log from '../log'
|
||||
import netease from './routes/netease'
|
||||
import netease from './routes/netease/netease'
|
||||
import appleMusic from './routes/r3play/appleMusic'
|
||||
import audio from './routes/r3play/audio'
|
||||
|
||||
const server = fastify({
|
||||
ignoreTrailingSlash: true,
|
||||
})
|
||||
|
||||
server.register(fastifyCookie)
|
||||
server.register(fastifyMultipart)
|
||||
if (isProd) {
|
||||
server.register(fastifyStatic, {
|
||||
root: path.join(__dirname, '../web'),
|
||||
})
|
||||
}
|
||||
|
||||
server.register(netease, { prefix: '/netease' })
|
||||
server.register(netease)
|
||||
server.register(audio)
|
||||
server.register(appleMusic)
|
||||
|
||||
const port = Number(
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import log from '@/desktop/main/log'
|
||||
import { CacheAPIs } from '@/shared/CacheAPIs'
|
||||
import { pathCase, snakeCase } from 'change-case'
|
||||
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'
|
||||
import NeteaseCloudMusicApi from 'NeteaseCloudMusicApi'
|
||||
import cache from '../../cache'
|
||||
import cache from '../../../cache'
|
||||
|
||||
async function netease(fastify: FastifyInstance) {
|
||||
const getHandler = (name: string, neteaseApi: (params: any) => any) => {
|
||||
|
|
@ -10,11 +11,11 @@ async function netease(fastify: FastifyInstance) {
|
|||
req: FastifyRequest<{ Querystring: { [key: string]: string } }>,
|
||||
reply: FastifyReply
|
||||
) => {
|
||||
// Get track details from cache
|
||||
if (name === APIs.Track) {
|
||||
const cacheData = cache.get(name, req.query)
|
||||
// // Get track details from cache
|
||||
if (name === CacheAPIs.Track) {
|
||||
const cacheData = await cache.get(name, req.query as any)
|
||||
if (cacheData) {
|
||||
return cache
|
||||
return cacheData
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -25,7 +26,8 @@ async function netease(fastify: FastifyInstance) {
|
|||
cookie: req.cookies,
|
||||
})
|
||||
|
||||
cache.set(name, result.body, req.query)
|
||||
cache.set(name as CacheAPIs, result.body, req.query)
|
||||
|
||||
return reply.send(result.body)
|
||||
} catch (error: any) {
|
||||
if ([400, 301].includes(error.status)) {
|
||||
|
|
@ -40,7 +42,9 @@ async function netease(fastify: FastifyInstance) {
|
|||
Object.entries(NeteaseCloudMusicApi).forEach(([nameInSnakeCase, neteaseApi]: [string, any]) => {
|
||||
// 例外
|
||||
if (
|
||||
['serveNcmApi', 'getModulesDefinitions', snakeCase(APIs.SongUrl)].includes(nameInSnakeCase)
|
||||
['serveNcmApi', 'getModulesDefinitions', snakeCase(CacheAPIs.SongUrl)].includes(
|
||||
nameInSnakeCase
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
|
@ -48,11 +52,11 @@ async function netease(fastify: FastifyInstance) {
|
|||
const name = pathCase(nameInSnakeCase)
|
||||
const handler = getHandler(name, neteaseApi)
|
||||
|
||||
fastify.get(`/${name}`, handler)
|
||||
fastify.post(`/${name}`, handler)
|
||||
fastify.get(`/netease/${name}`, handler)
|
||||
fastify.post(`/netease/${name}`, handler)
|
||||
})
|
||||
|
||||
fastify.get('/', () => 'NeteaseCloudMusicApi')
|
||||
fastify.get('/netease', () => 'NeteaseCloudMusicApi')
|
||||
}
|
||||
|
||||
export default netease
|
||||
212
packages/desktop/main/appServer/routes/r3play/audio.ts
Normal file
212
packages/desktop/main/appServer/routes/r3play/audio.ts
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'
|
||||
import NeteaseCloudMusicApi, { SoundQualityType } from 'NeteaseCloudMusicApi'
|
||||
import prisma from '@/desktop/main/prisma'
|
||||
import { app } from 'electron'
|
||||
import log from '@/desktop/main/log'
|
||||
import { appName } from '@/desktop/main/env'
|
||||
import cache from '@/desktop/main/cache'
|
||||
import fs from 'fs'
|
||||
import youtube from '@/desktop/main/youtube'
|
||||
import { CacheAPIs } from '@/shared/CacheAPIs'
|
||||
import { FetchTracksResponse } from '@/shared/api/Track'
|
||||
|
||||
const getAudioFromCache = async (id: number) => {
|
||||
// get from cache
|
||||
const cache = await prisma.audio.findUnique({ where: { id } })
|
||||
if (!cache) return
|
||||
|
||||
const audioFileName = `${cache.id}-${cache.bitRate}.${cache.format}`
|
||||
|
||||
const isAudioFileExists = fs.existsSync(`${app.getPath('userData')}/audio_cache/${audioFileName}`)
|
||||
if (!isAudioFileExists) return
|
||||
|
||||
log.debug(`[server] Audio cache hit ${id}`)
|
||||
|
||||
return {
|
||||
data: [
|
||||
{
|
||||
source: cache.source,
|
||||
id: cache.id,
|
||||
url: `http://127.0.0.1:${
|
||||
process.env.ELECTRON_WEB_SERVER_PORT
|
||||
}/${appName.toLowerCase()}/audio/${audioFileName}`,
|
||||
br: cache.bitRate,
|
||||
size: 0,
|
||||
md5: '',
|
||||
code: 200,
|
||||
expi: 0,
|
||||
type: cache.format,
|
||||
gain: 0,
|
||||
fee: 8,
|
||||
uf: null,
|
||||
payed: 0,
|
||||
flag: 4,
|
||||
canExtend: false,
|
||||
freeTrialInfo: null,
|
||||
level: 'standard',
|
||||
encodeType: cache.format,
|
||||
freeTrialPrivilege: {
|
||||
resConsumable: false,
|
||||
userConsumable: false,
|
||||
listenType: null,
|
||||
},
|
||||
freeTimeTrialPrivilege: {
|
||||
resConsumable: false,
|
||||
userConsumable: false,
|
||||
type: 0,
|
||||
remainTime: 0,
|
||||
},
|
||||
urlSource: 0,
|
||||
},
|
||||
],
|
||||
code: 200,
|
||||
}
|
||||
}
|
||||
|
||||
const getAudioFromYouTube = async (id: number) => {
|
||||
let fetchTrackResult: FetchTracksResponse | undefined = await cache.get(CacheAPIs.Track, {
|
||||
ids: String(id),
|
||||
})
|
||||
if (!fetchTrackResult) {
|
||||
log.info(`[audio] getAudioFromYouTube no fetchTrackResult, fetch from netease api`)
|
||||
fetchTrackResult = (await NeteaseCloudMusicApi.song_detail({
|
||||
ids: String(id),
|
||||
})) as unknown as FetchTracksResponse
|
||||
}
|
||||
const track = fetchTrackResult?.songs?.[0]
|
||||
if (!track) return
|
||||
|
||||
const data = await youtube.matchTrack(track.ar[0].name, track.name)
|
||||
if (!data) return
|
||||
return {
|
||||
data: [
|
||||
{
|
||||
source: 'youtube',
|
||||
id,
|
||||
url: data.url,
|
||||
br: data.bitRate,
|
||||
size: 0,
|
||||
md5: '',
|
||||
code: 200,
|
||||
expi: 0,
|
||||
type: 'opus',
|
||||
gain: 0,
|
||||
fee: 8,
|
||||
uf: null,
|
||||
payed: 0,
|
||||
flag: 4,
|
||||
canExtend: false,
|
||||
freeTrialInfo: null,
|
||||
level: 'standard',
|
||||
encodeType: 'opus',
|
||||
freeTrialPrivilege: {
|
||||
resConsumable: false,
|
||||
userConsumable: false,
|
||||
listenType: null,
|
||||
},
|
||||
freeTimeTrialPrivilege: {
|
||||
resConsumable: false,
|
||||
userConsumable: false,
|
||||
type: 0,
|
||||
remainTime: 0,
|
||||
},
|
||||
urlSource: 0,
|
||||
r3play: {
|
||||
youtube: data,
|
||||
},
|
||||
},
|
||||
],
|
||||
code: 200,
|
||||
}
|
||||
}
|
||||
|
||||
async function audio(fastify: FastifyInstance) {
|
||||
// 劫持网易云的song/url api,将url替换成缓存的音频文件url
|
||||
fastify.get(
|
||||
'/netease/song/url/v1',
|
||||
async (
|
||||
req: FastifyRequest<{ Querystring: { id: string | number; level: SoundQualityType } }>,
|
||||
reply
|
||||
) => {
|
||||
const id = Number(req.query.id) || 0
|
||||
if (!id || isNaN(id)) {
|
||||
return reply.status(400).send({
|
||||
code: 400,
|
||||
msg: 'id is required or id is invalid',
|
||||
})
|
||||
}
|
||||
|
||||
const cache = await getAudioFromCache(id)
|
||||
if (cache) {
|
||||
return cache
|
||||
}
|
||||
|
||||
const { body: fromNetease }: { body: any } = await NeteaseCloudMusicApi.song_url_v1({
|
||||
...req.query,
|
||||
cookie: req.cookies as unknown as any,
|
||||
})
|
||||
if (
|
||||
fromNetease?.code === 200 &&
|
||||
!fromNetease?.data?.[0]?.freeTrialInfo &&
|
||||
fromNetease?.data?.[0]?.url
|
||||
) {
|
||||
reply.status(200).send(fromNetease)
|
||||
return
|
||||
}
|
||||
|
||||
const fromYoutube = getAudioFromYouTube(id)
|
||||
if (fromYoutube) {
|
||||
return fromYoutube
|
||||
}
|
||||
|
||||
// 是试听歌曲就把url删掉
|
||||
if (fromNetease?.data?.[0].freeTrialInfo) {
|
||||
fromNetease.data[0].url = ''
|
||||
}
|
||||
|
||||
reply.status(fromNetease?.code ?? 500).send(fromNetease)
|
||||
}
|
||||
)
|
||||
|
||||
// 获取缓存的音频数据
|
||||
fastify.get(
|
||||
`/${appName.toLowerCase()}/audio/:filename`,
|
||||
(req: FastifyRequest<{ Params: { filename: string } }>, reply) => {
|
||||
const filename = req.params.filename
|
||||
cache.getAudio(filename, reply)
|
||||
}
|
||||
)
|
||||
|
||||
// 缓存音频数据
|
||||
fastify.post(
|
||||
`/${appName.toLowerCase()}/audio/:id`,
|
||||
async (
|
||||
req: FastifyRequest<{ Params: { id: string }; Querystring: { url: string } }>,
|
||||
reply
|
||||
) => {
|
||||
const id = Number(req.params.id)
|
||||
const { url } = req.query
|
||||
if (isNaN(id)) {
|
||||
return reply.status(400).send({ error: 'Invalid param id' })
|
||||
}
|
||||
if (!url) {
|
||||
return reply.status(400).send({ error: 'Invalid query url' })
|
||||
}
|
||||
|
||||
const data = await req.file()
|
||||
|
||||
if (!data?.file) {
|
||||
return reply.status(400).send({ error: 'No file' })
|
||||
}
|
||||
|
||||
try {
|
||||
await cache.setAudio(await data.toBuffer(), { id, url })
|
||||
reply.status(200).send('Audio cached!')
|
||||
} catch (error) {
|
||||
reply.status(500).send({ error })
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export default audio
|
||||
Loading…
Add table
Add a link
Reference in a new issue