feat: updates

This commit is contained in:
qier222 2023-01-28 11:54:57 +08:00
parent 7ce516877e
commit ccebe0a67a
No known key found for this signature in database
74 changed files with 56065 additions and 2810 deletions

View file

@ -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(

View file

@ -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

View 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