mirror of
https://github.com/qier222/YesPlayMusic.git
synced 2024-11-22 10:56:23 +08:00
feat: updates
This commit is contained in:
parent
c6c59b2cd9
commit
7ce516877e
6
.github/workflows/build.yaml
vendored
6
.github/workflows/build.yaml
vendored
|
@ -71,20 +71,20 @@ jobs:
|
||||||
- name: Upload Artifact (macOS)
|
- name: Upload Artifact (macOS)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: R3Play-mac
|
name: R3PLAY-mac
|
||||||
path: release/*-universal.dmg
|
path: release/*-universal.dmg
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|
||||||
- name: Upload Artifact (Windows)
|
- name: Upload Artifact (Windows)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: R3Play-win
|
name: R3PLAY-win
|
||||||
path: release/*x64-Setup.exe
|
path: release/*x64-Setup.exe
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|
||||||
- name: Upload Artifact (Linux)
|
- name: Upload Artifact (Linux)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: R3Play-linux
|
name: R3PLAY-linux
|
||||||
path: release/*.AppImage
|
path: release/*.AppImage
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|
4
.vercelignore
Normal file
4
.vercelignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/**/*/node_modules
|
||||||
|
tmp
|
||||||
|
/**/*/dist
|
||||||
|
/packages/desktop/release
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "r3play",
|
"name": "r3play",
|
||||||
"productName": "R3Play",
|
"productName": "R3PLAY",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"description": "A nifty third-party NetEase Music player",
|
"description": "A nifty third-party NetEase Music player",
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@7.0.0",
|
"packageManager": "pnpm@7.20.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "turbo run post-install --parallel --no-cache",
|
"install": "turbo run post-install --parallel --no-cache",
|
||||||
"build": "cross-env-shell IS_ELECTRON=yes turbo run build",
|
"build": "cross-env-shell IS_ELECTRON=yes turbo run build",
|
||||||
|
|
31
packages/desktop/main/appServer/appServer.ts
Normal file
31
packages/desktop/main/appServer/appServer.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import fastify from 'fastify'
|
||||||
|
import fastifyStatic from '@fastify/static'
|
||||||
|
import path from 'path'
|
||||||
|
import fastifyCookie from '@fastify/cookie'
|
||||||
|
import { isProd } from '../env'
|
||||||
|
import log from '../log'
|
||||||
|
import netease from './routes/netease'
|
||||||
|
import appleMusic from './routes/r3play/appleMusic'
|
||||||
|
|
||||||
|
const server = fastify({
|
||||||
|
ignoreTrailingSlash: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
server.register(fastifyCookie)
|
||||||
|
if (isProd) {
|
||||||
|
server.register(fastifyStatic, {
|
||||||
|
root: path.join(__dirname, '../web'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
server.register(netease, { prefix: '/netease' })
|
||||||
|
server.register(appleMusic)
|
||||||
|
|
||||||
|
const port = Number(
|
||||||
|
isProd
|
||||||
|
? process.env.ELECTRON_WEB_SERVER_PORT || 42710
|
||||||
|
: process.env.ELECTRON_DEV_NETEASE_API_PORT || 30001
|
||||||
|
)
|
||||||
|
server.listen({ port })
|
||||||
|
|
||||||
|
log.info(`[appServer] http server listening on port ${port}`)
|
58
packages/desktop/main/appServer/routes/netease.ts
Normal file
58
packages/desktop/main/appServer/routes/netease.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import { APIs } from '@/shared/CacheAPIs'
|
||||||
|
import { pathCase, snakeCase } from 'change-case'
|
||||||
|
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'
|
||||||
|
import NeteaseCloudMusicApi from 'NeteaseCloudMusicApi'
|
||||||
|
import cache from '../../cache'
|
||||||
|
|
||||||
|
async function netease(fastify: FastifyInstance) {
|
||||||
|
const getHandler = (name: string, neteaseApi: (params: any) => any) => {
|
||||||
|
return async (
|
||||||
|
req: FastifyRequest<{ Querystring: { [key: string]: string } }>,
|
||||||
|
reply: FastifyReply
|
||||||
|
) => {
|
||||||
|
// Get track details from cache
|
||||||
|
if (name === APIs.Track) {
|
||||||
|
const cacheData = cache.get(name, req.query)
|
||||||
|
if (cacheData) {
|
||||||
|
return cache
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request netease api
|
||||||
|
try {
|
||||||
|
const result = await neteaseApi({
|
||||||
|
...req.query,
|
||||||
|
cookie: req.cookies,
|
||||||
|
})
|
||||||
|
|
||||||
|
cache.set(name, result.body, req.query)
|
||||||
|
return reply.send(result.body)
|
||||||
|
} catch (error: any) {
|
||||||
|
if ([400, 301].includes(error.status)) {
|
||||||
|
return reply.status(error.status).send(error.body)
|
||||||
|
}
|
||||||
|
return reply.status(500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 循环注册NeteaseCloudMusicApi所有接口
|
||||||
|
Object.entries(NeteaseCloudMusicApi).forEach(([nameInSnakeCase, neteaseApi]: [string, any]) => {
|
||||||
|
// 例外
|
||||||
|
if (
|
||||||
|
['serveNcmApi', 'getModulesDefinitions', snakeCase(APIs.SongUrl)].includes(nameInSnakeCase)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = pathCase(nameInSnakeCase)
|
||||||
|
const handler = getHandler(name, neteaseApi)
|
||||||
|
|
||||||
|
fastify.get(`/${name}`, handler)
|
||||||
|
fastify.post(`/${name}`, handler)
|
||||||
|
})
|
||||||
|
|
||||||
|
fastify.get('/', () => 'NeteaseCloudMusicApi')
|
||||||
|
}
|
||||||
|
|
||||||
|
export default netease
|
12
packages/desktop/main/appServer/routes/r3play/appleMusic.ts
Normal file
12
packages/desktop/main/appServer/routes/r3play/appleMusic.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { FastifyInstance } from 'fastify'
|
||||||
|
import proxy from '@fastify/http-proxy'
|
||||||
|
|
||||||
|
async function appleMusic(fastify: FastifyInstance) {
|
||||||
|
fastify.register(proxy, {
|
||||||
|
upstream: 'http://168.138.174.244:35530/',
|
||||||
|
prefix: '/r3play/apple-music',
|
||||||
|
rewritePrefix: '/apple-music',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default appleMusic
|
|
@ -1,90 +0,0 @@
|
||||||
import log from './log'
|
|
||||||
import axios from 'axios'
|
|
||||||
import { AppleMusicAlbum, AppleMusicArtist } from '@/shared/AppleMusic'
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
Authority: 'amp-api.music.apple.com',
|
|
||||||
Accept: '*/*',
|
|
||||||
Authorization:
|
|
||||||
'Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IldlYlBsYXlLaWQifQ.eyJpc3MiOiJBTVBXZWJQbGF5IiwiaWF0IjoxNjYxNDQwNDMyLCJleHAiOjE2NzY5OTI0MzIsInJvb3RfaHR0cHNfb3JpZ2luIjpbImFwcGxlLmNvbSJdfQ.z4BMv9_O4MpMK2iFhYkDqPsx53soPSnlXXK3jm99pHqGOrZADvTgEUw2U7_B1W0MAtFiWBYhYcGvWrzaOig6Bw',
|
|
||||||
Referer: 'https://music.apple.com/',
|
|
||||||
'Sec-Fetch-Dest': 'empty',
|
|
||||||
'Sec-Fetch-Mode': 'cors',
|
|
||||||
'Sec-Fetch-Site': 'cross-site',
|
|
||||||
'User-Agent':
|
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Cider/1.5.1 Chrome/100.0.4896.160 Electron/18.3.3 Safari/537.36',
|
|
||||||
'Accept-Encoding': 'gzip',
|
|
||||||
Origin: 'https://music.apple.com',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getAlbum = async ({
|
|
||||||
name,
|
|
||||||
artist,
|
|
||||||
}: {
|
|
||||||
name: string
|
|
||||||
artist: string
|
|
||||||
}): Promise<AppleMusicAlbum | undefined> => {
|
|
||||||
const keyword = `${artist} ${name}`
|
|
||||||
log.debug(`[appleMusic] getAlbum: ${keyword}`)
|
|
||||||
const searchResult = await axios({
|
|
||||||
method: 'GET',
|
|
||||||
headers,
|
|
||||||
url: 'https://amp-api.music.apple.com/v1/catalog/us/search',
|
|
||||||
params: {
|
|
||||||
term: keyword,
|
|
||||||
types: 'albums',
|
|
||||||
'fields[albums]': 'artistName,name,editorialVideo,editorialNotes',
|
|
||||||
platform: 'web',
|
|
||||||
limit: '5',
|
|
||||||
l: 'en-us', // TODO: get from settings
|
|
||||||
},
|
|
||||||
}).catch(e => {
|
|
||||||
log.debug('[appleMusic] Search album error', e)
|
|
||||||
})
|
|
||||||
|
|
||||||
const albums: AppleMusicAlbum[] | undefined =
|
|
||||||
searchResult?.data?.results?.albums?.data
|
|
||||||
|
|
||||||
const album =
|
|
||||||
albums?.find(
|
|
||||||
a =>
|
|
||||||
a.attributes.name.toLowerCase() === name.toLowerCase() &&
|
|
||||||
a.attributes.artistName.toLowerCase() === artist.toLowerCase()
|
|
||||||
) || albums?.[0]
|
|
||||||
if (!album) {
|
|
||||||
log.debug('[appleMusic] No album found on apple music')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return album
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getArtist = async (
|
|
||||||
name: string
|
|
||||||
): Promise<AppleMusicArtist | undefined> => {
|
|
||||||
const searchResult = await axios({
|
|
||||||
method: 'GET',
|
|
||||||
url: 'https://amp-api.music.apple.com/v1/catalog/us/search',
|
|
||||||
headers,
|
|
||||||
params: {
|
|
||||||
term: name,
|
|
||||||
types: 'artists',
|
|
||||||
'fields[artists]': 'url,name,artwork,editorialVideo,artistBio',
|
|
||||||
'omit[resource:artists]': 'relationships',
|
|
||||||
platform: 'web',
|
|
||||||
limit: '1',
|
|
||||||
l: 'en-us', // TODO: get from settings
|
|
||||||
with: 'serverBubbles',
|
|
||||||
},
|
|
||||||
}).catch(e => {
|
|
||||||
log.debug('[appleMusic] Search artist error', e)
|
|
||||||
})
|
|
||||||
|
|
||||||
const artist = searchResult?.data?.results?.artist?.data?.[0]
|
|
||||||
if (
|
|
||||||
artist &&
|
|
||||||
artist?.attributes?.name?.toLowerCase() === name.toLowerCase()
|
|
||||||
) {
|
|
||||||
return artist
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -147,9 +147,7 @@ class Cache {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case APIs.Track: {
|
case APIs.Track: {
|
||||||
const ids: number[] = params?.ids
|
const ids: number[] = params?.ids.split(',').map((id: string) => Number(id))
|
||||||
.split(',')
|
|
||||||
.map((id: string) => Number(id))
|
|
||||||
if (ids.length === 0) return
|
if (ids.length === 0) return
|
||||||
|
|
||||||
if (ids.includes(NaN)) return
|
if (ids.includes(NaN)) return
|
||||||
|
@ -252,17 +250,6 @@ class Cache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getForExpress(api: string, req: Request) {
|
|
||||||
// Get track detail cache
|
|
||||||
if (api === APIs.Track) {
|
|
||||||
const cache = this.get(api, req.query)
|
|
||||||
if (cache) {
|
|
||||||
log.debug(`[cache] Cache hit for ${req.path}`)
|
|
||||||
return cache
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getAudio(fileName: string, res: Response) {
|
getAudio(fileName: string, res: Response) {
|
||||||
if (!fileName) {
|
if (!fileName) {
|
||||||
return res.status(400).send({ error: 'No filename provided' })
|
return res.status(400).send({ error: 'No filename provided' })
|
||||||
|
@ -281,10 +268,7 @@ class Cache {
|
||||||
.status(206)
|
.status(206)
|
||||||
.setHeader('Accept-Ranges', 'bytes')
|
.setHeader('Accept-Ranges', 'bytes')
|
||||||
.setHeader('Connection', 'keep-alive')
|
.setHeader('Connection', 'keep-alive')
|
||||||
.setHeader(
|
.setHeader('Content-Range', `bytes 0-${audio.byteLength - 1}/${audio.byteLength}`)
|
||||||
'Content-Range',
|
|
||||||
`bytes 0-${audio.byteLength - 1}/${audio.byteLength}`
|
|
||||||
)
|
|
||||||
.send(audio)
|
.send(audio)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).send({ error })
|
res.status(500).send({ error })
|
||||||
|
@ -301,8 +285,7 @@ class Cache {
|
||||||
}
|
}
|
||||||
|
|
||||||
const meta = await musicMetadata.parseBuffer(buffer)
|
const meta = await musicMetadata.parseBuffer(buffer)
|
||||||
const br =
|
const br = meta?.format?.codec === 'OPUS' ? 165000 : meta.format.bitrate ?? 0
|
||||||
meta?.format?.codec === 'OPUS' ? 165000 : meta.format.bitrate ?? 0
|
|
||||||
const type =
|
const type =
|
||||||
{
|
{
|
||||||
'MPEG 1 Layer 3': 'mp3',
|
'MPEG 1 Layer 3': 'mp3',
|
||||||
|
|
|
@ -77,10 +77,7 @@ const readSqlFile = (filename: string) => {
|
||||||
|
|
||||||
class DB {
|
class DB {
|
||||||
sqlite: SQLite3.Database
|
sqlite: SQLite3.Database
|
||||||
dbFilePath: string = path.resolve(
|
dbFilePath: string = path.resolve(app.getPath('userData'), './api_cache/db.sqlite')
|
||||||
app.getPath('userData'),
|
|
||||||
'./api_cache/db.sqlite'
|
|
||||||
)
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
log.info('[db] Initializing database...')
|
log.info('[db] Initializing database...')
|
||||||
|
@ -109,14 +106,8 @@ class DB {
|
||||||
`../../bin/better_sqlite3_${os.platform}_${os.arch}.node`
|
`../../bin/better_sqlite3_${os.platform}_${os.arch}.node`
|
||||||
)
|
)
|
||||||
const prodBinPaths = {
|
const prodBinPaths = {
|
||||||
darwin: path.resolve(
|
darwin: path.resolve(app.getPath('exe'), `../../Resources/bin/better_sqlite3.node`),
|
||||||
app.getPath('exe'),
|
win32: path.resolve(app.getPath('exe'), `../resources/bin/better_sqlite3.node`),
|
||||||
`../../Resources/bin/better_sqlite3.node`
|
|
||||||
),
|
|
||||||
win32: path.resolve(
|
|
||||||
app.getPath('exe'),
|
|
||||||
`../resources/bin/better_sqlite3.node`
|
|
||||||
),
|
|
||||||
linux: '',
|
linux: '',
|
||||||
}
|
}
|
||||||
return isProd
|
return isProd
|
||||||
|
@ -128,6 +119,7 @@ class DB {
|
||||||
log.info('[db] Initializing database tables...')
|
log.info('[db] Initializing database tables...')
|
||||||
const init = readSqlFile('init.sql')
|
const init = readSqlFile('init.sql')
|
||||||
this.sqlite.exec(init)
|
this.sqlite.exec(init)
|
||||||
|
this.sqlite.pragma('journal_mode=WAL')
|
||||||
log.info('[db] Database tables initialized.')
|
log.info('[db] Database tables initialized.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,9 +159,7 @@ class DB {
|
||||||
table: T,
|
table: T,
|
||||||
key: TablesStructures[T]['id']
|
key: TablesStructures[T]['id']
|
||||||
): TablesStructures[T] | undefined {
|
): TablesStructures[T] | undefined {
|
||||||
return this.sqlite
|
return this.sqlite.prepare(`SELECT * FROM ${table} WHERE id = ? LIMIT 1`).get(key)
|
||||||
.prepare(`SELECT * FROM ${table} WHERE id = ? LIMIT 1`)
|
|
||||||
.get(key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
findMany<T extends TableNames>(
|
findMany<T extends TableNames>(
|
||||||
|
@ -184,11 +174,7 @@ class DB {
|
||||||
return this.sqlite.prepare(`SELECT * FROM ${table}`).all()
|
return this.sqlite.prepare(`SELECT * FROM ${table}`).all()
|
||||||
}
|
}
|
||||||
|
|
||||||
create<T extends TableNames>(
|
create<T extends TableNames>(table: T, data: TablesStructures[T], skipWhenExist: boolean = true) {
|
||||||
table: T,
|
|
||||||
data: TablesStructures[T],
|
|
||||||
skipWhenExist: boolean = true
|
|
||||||
) {
|
|
||||||
if (skipWhenExist && db.find(table, data.id)) return
|
if (skipWhenExist && db.find(table, data.id)) return
|
||||||
return this.sqlite.prepare(`INSERT INTO ${table} VALUES (?)`).run(data)
|
return this.sqlite.prepare(`INSERT INTO ${table} VALUES (?)`).run(data)
|
||||||
}
|
}
|
||||||
|
@ -202,9 +188,7 @@ class DB {
|
||||||
.map(key => `:${key}`)
|
.map(key => `:${key}`)
|
||||||
.join(', ')
|
.join(', ')
|
||||||
const insert = this.sqlite.prepare(
|
const insert = this.sqlite.prepare(
|
||||||
`INSERT ${
|
`INSERT ${skipWhenExist ? 'OR IGNORE' : ''} INTO ${table} VALUES (${valuesQuery})`
|
||||||
skipWhenExist ? 'OR IGNORE' : ''
|
|
||||||
} INTO ${table} VALUES (${valuesQuery})`
|
|
||||||
)
|
)
|
||||||
const insertMany = this.sqlite.transaction((rows: any[]) => {
|
const insertMany = this.sqlite.transaction((rows: any[]) => {
|
||||||
rows.forEach((row: any) => insert.run(row))
|
rows.forEach((row: any) => insert.run(row))
|
||||||
|
@ -216,18 +200,14 @@ class DB {
|
||||||
const valuesQuery = Object.keys(data)
|
const valuesQuery = Object.keys(data)
|
||||||
.map(key => `:${key}`)
|
.map(key => `:${key}`)
|
||||||
.join(', ')
|
.join(', ')
|
||||||
return this.sqlite
|
return this.sqlite.prepare(`INSERT OR REPLACE INTO ${table} VALUES (${valuesQuery})`).run(data)
|
||||||
.prepare(`INSERT OR REPLACE INTO ${table} VALUES (${valuesQuery})`)
|
|
||||||
.run(data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
upsertMany<T extends TableNames>(table: T, data: TablesStructures[T][]) {
|
upsertMany<T extends TableNames>(table: T, data: TablesStructures[T][]) {
|
||||||
const valuesQuery = Object.keys(data[0])
|
const valuesQuery = Object.keys(data[0])
|
||||||
.map(key => `:${key}`)
|
.map(key => `:${key}`)
|
||||||
.join(', ')
|
.join(', ')
|
||||||
const upsert = this.sqlite.prepare(
|
const upsert = this.sqlite.prepare(`INSERT OR REPLACE INTO ${table} VALUES (${valuesQuery})`)
|
||||||
`INSERT OR REPLACE INTO ${table} VALUES (${valuesQuery})`
|
|
||||||
)
|
|
||||||
const upsertMany = this.sqlite.transaction((rows: any[]) => {
|
const upsertMany = this.sqlite.transaction((rows: any[]) => {
|
||||||
rows.forEach((row: any) => upsert.run(row))
|
rows.forEach((row: any) => upsert.run(row))
|
||||||
})
|
})
|
||||||
|
@ -238,10 +218,7 @@ class DB {
|
||||||
return this.sqlite.prepare(`DELETE FROM ${table} WHERE id = ?`).run(key)
|
return this.sqlite.prepare(`DELETE FROM ${table} WHERE id = ?`).run(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteMany<T extends TableNames>(
|
deleteMany<T extends TableNames>(table: T, keys: TablesStructures[T]['id'][]) {
|
||||||
table: T,
|
|
||||||
keys: TablesStructures[T]['id'][]
|
|
||||||
) {
|
|
||||||
const idsQuery = keys.map(key => `id = ${key}`).join(' OR ')
|
const idsQuery = keys.map(key => `id = ${key}`).join(' OR ')
|
||||||
return this.sqlite.prepare(`DELETE FROM ${table} WHERE ${idsQuery}`).run()
|
return this.sqlite.prepare(`DELETE FROM ${table} WHERE ${idsQuery}`).run()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,4 @@ export const isProd = process.env.NODE_ENV === 'production'
|
||||||
export const isWindows = process.platform === 'win32'
|
export const isWindows = process.platform === 'win32'
|
||||||
export const isMac = process.platform === 'darwin'
|
export const isMac = process.platform === 'darwin'
|
||||||
export const isLinux = process.platform === 'linux'
|
export const isLinux = process.platform === 'linux'
|
||||||
export const appName = 'R3Play'
|
export const appName = 'R3PLAY'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import './preload' // must be first
|
import './preload' // must be first
|
||||||
import './sentry'
|
import './sentry'
|
||||||
import './server'
|
// import './server'
|
||||||
import { BrowserWindow, BrowserWindowConstructorOptions, app, shell } from 'electron'
|
import { BrowserWindow, BrowserWindowConstructorOptions, app, shell } from 'electron'
|
||||||
import { release } from 'os'
|
import { release } from 'os'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
@ -12,8 +12,7 @@ import { createTaskbar, Thumbar } from './windowsTaskbar'
|
||||||
import { createMenu } from './menu'
|
import { createMenu } from './menu'
|
||||||
import { isDev, isWindows, isLinux, isMac, appName } from './env'
|
import { isDev, isWindows, isLinux, isMac, appName } from './env'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
// import './surrealdb'
|
import './appServer/appServer'
|
||||||
// import Airplay from './airplay'
|
|
||||||
|
|
||||||
class Main {
|
class Main {
|
||||||
win: BrowserWindow | null = null
|
win: BrowserWindow | null = null
|
||||||
|
@ -83,10 +82,12 @@ class Main {
|
||||||
width: store.get('window.width'),
|
width: store.get('window.width'),
|
||||||
height: store.get('window.height'),
|
height: store.get('window.height'),
|
||||||
minWidth: 1240,
|
minWidth: 1240,
|
||||||
minHeight: 848,
|
minHeight: 800,
|
||||||
titleBarStyle: isMac ? 'customButtonsOnHover' : 'hidden',
|
titleBarStyle: isMac ? 'customButtonsOnHover' : 'hidden',
|
||||||
trafficLightPosition: { x: 24, y: 24 },
|
trafficLightPosition: { x: 24, y: 24 },
|
||||||
frame: false,
|
frame: false,
|
||||||
|
fullscreenable: true,
|
||||||
|
resizable: true,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0)',
|
backgroundColor: 'rgba(0, 0, 0, 0)',
|
||||||
show: false,
|
show: false,
|
||||||
|
|
|
@ -12,7 +12,6 @@ import { Thumbar } from './windowsTaskbar'
|
||||||
import fastFolderSize from 'fast-folder-size'
|
import fastFolderSize from 'fast-folder-size'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import prettyBytes from 'pretty-bytes'
|
import prettyBytes from 'pretty-bytes'
|
||||||
import { getArtist, getAlbum } from './appleMusic'
|
|
||||||
|
|
||||||
const on = <T extends keyof IpcChannelsParams>(
|
const on = <T extends keyof IpcChannelsParams>(
|
||||||
channel: T,
|
channel: T,
|
||||||
|
@ -23,10 +22,7 @@ const on = <T extends keyof IpcChannelsParams>(
|
||||||
|
|
||||||
const handle = <T extends keyof IpcChannelsParams>(
|
const handle = <T extends keyof IpcChannelsParams>(
|
||||||
channel: T,
|
channel: T,
|
||||||
listener: (
|
listener: (event: Electron.IpcMainInvokeEvent, params: IpcChannelsParams[T]) => void
|
||||||
event: Electron.IpcMainInvokeEvent,
|
|
||||||
params: IpcChannelsParams[T]
|
|
||||||
) => void
|
|
||||||
) => {
|
) => {
|
||||||
return ipcMain.handle(channel, listener)
|
return ipcMain.handle(channel, listener)
|
||||||
}
|
}
|
||||||
|
@ -162,49 +158,46 @@ function initOtherIpcMain() {
|
||||||
* 获取音频缓存文件夹大小
|
* 获取音频缓存文件夹大小
|
||||||
*/
|
*/
|
||||||
on(IpcChannels.GetAudioCacheSize, event => {
|
on(IpcChannels.GetAudioCacheSize, event => {
|
||||||
fastFolderSize(
|
fastFolderSize(path.join(app.getPath('userData'), './audio_cache'), (error, bytes) => {
|
||||||
path.join(app.getPath('userData'), './audio_cache'),
|
if (error) throw error
|
||||||
(error, bytes) => {
|
|
||||||
if (error) throw error
|
|
||||||
|
|
||||||
event.returnValue = prettyBytes(bytes ?? 0)
|
event.returnValue = prettyBytes(bytes ?? 0)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从Apple Music获取专辑信息
|
* 从Apple Music获取专辑信息
|
||||||
*/
|
*/
|
||||||
handle(
|
// handle(
|
||||||
IpcChannels.GetAlbumFromAppleMusic,
|
// IpcChannels.GetAlbumFromAppleMusic,
|
||||||
async (event, { id, name, artist }) => {
|
// async (event, { id, name, artist }) => {
|
||||||
const fromCache = cache.get(APIs.AppleMusicAlbum, { id })
|
// const fromCache = cache.get(APIs.AppleMusicAlbum, { id })
|
||||||
if (fromCache) {
|
// if (fromCache) {
|
||||||
return fromCache === 'no' ? undefined : fromCache
|
// return fromCache === 'no' ? undefined : fromCache
|
||||||
}
|
// }
|
||||||
|
|
||||||
const fromApple = await getAlbum({ name, artist })
|
// const fromApple = await getAlbum({ name, artist })
|
||||||
cache.set(APIs.AppleMusicAlbum, { id, album: fromApple })
|
// cache.set(APIs.AppleMusicAlbum, { id, album: fromApple })
|
||||||
return fromApple
|
// return fromApple
|
||||||
}
|
// }
|
||||||
)
|
// )
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 从Apple Music获取歌手信息
|
// * 从Apple Music获取歌手信息
|
||||||
**/
|
// **/
|
||||||
handle(IpcChannels.GetArtistFromAppleMusic, async (event, { id, name }) => {
|
// handle(IpcChannels.GetArtistFromAppleMusic, async (event, { id, name }) => {
|
||||||
const fromApple = await getArtist(name)
|
// const fromApple = await getArtist(name)
|
||||||
cache.set(APIs.AppleMusicArtist, { id, artist: fromApple })
|
// cache.set(APIs.AppleMusicArtist, { id, artist: fromApple })
|
||||||
return fromApple
|
// return fromApple
|
||||||
})
|
// })
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 从缓存读取Apple Music歌手信息
|
// * 从缓存读取Apple Music歌手信息
|
||||||
*/
|
// */
|
||||||
on(IpcChannels.GetArtistFromAppleMusic, (event, { id }) => {
|
// on(IpcChannels.GetArtistFromAppleMusic, (event, { id }) => {
|
||||||
const artist = cache.get(APIs.AppleMusicArtist, id)
|
// const artist = cache.get(APIs.AppleMusicArtist, id)
|
||||||
event.returnValue = artist === 'no' ? undefined : artist
|
// event.returnValue = artist === 'no' ? undefined : artist
|
||||||
})
|
// })
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 退出登陆
|
* 退出登陆
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { appName, isProd } from './env'
|
||||||
import { APIs } from '@/shared/CacheAPIs'
|
import { APIs } from '@/shared/CacheAPIs'
|
||||||
import history from 'connect-history-api-fallback'
|
import history from 'connect-history-api-fallback'
|
||||||
import { db, Tables } from './db'
|
import { db, Tables } from './db'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
port = Number(
|
port = Number(
|
||||||
|
@ -30,12 +31,32 @@ class Server {
|
||||||
this.app.use(cookieParser())
|
this.app.use(cookieParser())
|
||||||
this.app.use(fileUpload())
|
this.app.use(fileUpload())
|
||||||
this.getAudioUrlHandler()
|
this.getAudioUrlHandler()
|
||||||
|
this.r3playApiHandler()
|
||||||
this.neteaseHandler()
|
this.neteaseHandler()
|
||||||
this.cacheAudioHandler()
|
this.cacheAudioHandler()
|
||||||
this.serveStaticForProduction()
|
this.serveStaticForProduction()
|
||||||
this.listen()
|
this.listen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r3playApiHandler() {
|
||||||
|
this.app.get(`/r3play/apple-music/:endpoint`, async (req: Request, res: Response) => {
|
||||||
|
log.debug(`[server] Handling request: ${req.path}`)
|
||||||
|
const { endpoint } = req.params
|
||||||
|
const { query } = req
|
||||||
|
axios
|
||||||
|
.get(`https://r3play.app/r3play/apple-music/${endpoint}`, {
|
||||||
|
params: query,
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
console.log(response.data)
|
||||||
|
res.send(response.data)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
neteaseHandler() {
|
neteaseHandler() {
|
||||||
Object.entries(this.netease).forEach(([name, handler]: [string, any]) => {
|
Object.entries(this.netease).forEach(([name, handler]: [string, any]) => {
|
||||||
// 例外处理
|
// 例外处理
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "desktop",
|
"name": "desktop",
|
||||||
"productName": "R3Play",
|
"productName": "R3PLAY",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"main": "./main/index.js",
|
"main": "./main/index.js",
|
||||||
|
@ -20,8 +20,10 @@
|
||||||
"node": "^14.13.1 || >=16.0.0"
|
"node": "^14.13.1 || >=16.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fastify/cookie": "^8.3.0",
|
||||||
|
"@fastify/http-proxy": "^8.4.0",
|
||||||
"@sentry/electron": "^3.0.7",
|
"@sentry/electron": "^3.0.7",
|
||||||
"NeteaseCloudMusicApi": "^4.8.4",
|
"NeteaseCloudMusicApi": "^4.8.7",
|
||||||
"better-sqlite3": "8.0.1",
|
"better-sqlite3": "8.0.1",
|
||||||
"change-case": "^4.1.2",
|
"change-case": "^4.1.2",
|
||||||
"compare-versions": "^4.1.3",
|
"compare-versions": "^4.1.3",
|
||||||
|
@ -57,9 +59,9 @@
|
||||||
"ora": "^6.1.2",
|
"ora": "^6.1.2",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
"prettier": "*",
|
"prettier": "*",
|
||||||
|
"tsx": "*",
|
||||||
"typescript": "*",
|
"typescript": "*",
|
||||||
"vitest": "^0.20.3",
|
"vitest": "^0.20.3",
|
||||||
"wait-on": "^7.0.1",
|
"wait-on": "^7.0.1"
|
||||||
"tsx": "*"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,13 +32,10 @@ const options = {
|
||||||
define: envForEsbuild,
|
define: envForEsbuild,
|
||||||
minify: true,
|
minify: true,
|
||||||
external: [
|
external: [
|
||||||
...builtinModules.filter(
|
...builtinModules.filter(x => !/^_|^(internal|v8|node-inspect)\/|\//.test(x)),
|
||||||
x => !/^_|^(internal|v8|node-inspect)\/|\//.test(x)
|
|
||||||
),
|
|
||||||
'electron',
|
'electron',
|
||||||
'NeteaseCloudMusicApi',
|
'NeteaseCloudMusicApi',
|
||||||
'better-sqlite3',
|
'better-sqlite3',
|
||||||
// '@unblockneteasemusic/rust-napi',
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,9 +52,7 @@ const runApp = () => {
|
||||||
if (argv.watch) {
|
if (argv.watch) {
|
||||||
waitOn(
|
waitOn(
|
||||||
{
|
{
|
||||||
resources: [
|
resources: [`http://127.0.0.1:${process.env.ELECTRON_WEB_SERVER_PORT}/index.html`],
|
||||||
`http://127.0.0.1:${process.env.ELECTRON_WEB_SERVER_PORT}/index.html`,
|
|
||||||
],
|
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
},
|
},
|
||||||
err => {
|
err => {
|
||||||
|
@ -101,11 +96,7 @@ if (argv.watch) {
|
||||||
console.log(TAG, pc.green('Main Process Build Succeeded.'))
|
console.log(TAG, pc.green('Main Process Build Succeeded.'))
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.log(
|
console.log(`\n${TAG} ${pc.red('Main Process Build Failed')}\n`, error, '\n')
|
||||||
`\n${TAG} ${pc.red('Main Process Build Failed')}\n`,
|
|
||||||
error,
|
|
||||||
'\n'
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
spinner.stop()
|
spinner.stop()
|
||||||
|
|
5
packages/server/.env.example
Normal file
5
packages/server/.env.example
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
APPLE_MUSIC_TOKEN = Bearer xxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
|
# If you want to use a different database provider like SQLite/PostgreSQL,
|
||||||
|
# you will need to change the 'provider' in /prisma/schema.prisma
|
||||||
|
DATABASE_URL='mysql://USER:PASSWORD@HOST:PORT/DATABASE'
|
|
@ -1,4 +0,0 @@
|
||||||
test-env: [
|
|
||||||
TS_NODE_FILES=true,
|
|
||||||
TS_NODE_PROJECT=./test/tsconfig.json
|
|
||||||
]
|
|
|
@ -1,41 +0,0 @@
|
||||||
# fly.toml file generated for ypm on 2022-09-06T00:57:21+08:00
|
|
||||||
|
|
||||||
app = "ypm"
|
|
||||||
kill_signal = "SIGINT"
|
|
||||||
kill_timeout = 5
|
|
||||||
processes = ["npm run start"]
|
|
||||||
|
|
||||||
[build]
|
|
||||||
builder = "heroku/buildpacks:20"
|
|
||||||
|
|
||||||
[env]
|
|
||||||
PORT = "8080"
|
|
||||||
|
|
||||||
[experimental]
|
|
||||||
allowed_public_ports = []
|
|
||||||
auto_rollback = true
|
|
||||||
|
|
||||||
[[services]]
|
|
||||||
http_checks = []
|
|
||||||
internal_port = 35530
|
|
||||||
processes = ["app"]
|
|
||||||
protocol = "tcp"
|
|
||||||
script_checks = []
|
|
||||||
[services.concurrency]
|
|
||||||
hard_limit = 25
|
|
||||||
soft_limit = 20
|
|
||||||
type = "connections"
|
|
||||||
|
|
||||||
[[services.ports]]
|
|
||||||
handlers = ["http"]
|
|
||||||
port = 80
|
|
||||||
|
|
||||||
[[services.ports]]
|
|
||||||
handlers = ["tls", "http"]
|
|
||||||
port = 443
|
|
||||||
|
|
||||||
[[services.tcp_checks]]
|
|
||||||
grace_period = "1s"
|
|
||||||
interval = "15s"
|
|
||||||
restart_limit = 0
|
|
||||||
timeout = "2s"
|
|
5685
packages/server/package-lock.json
generated
Normal file
5685
packages/server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -3,15 +3,11 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "This project was bootstrapped with Fastify-CLI.",
|
"description": "This project was bootstrapped with Fastify-CLI.",
|
||||||
"main": "app.ts",
|
"main": "app.ts",
|
||||||
"directories": {
|
|
||||||
"test": "test"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "npm run build:ts && tsc -p test/tsconfig.json && tap --ts \"test/**/*.test.ts\"",
|
|
||||||
"start": "fastify start --port 35530 --address 0.0.0.0 -l info dist/app.js",
|
"start": "fastify start --port 35530 --address 0.0.0.0 -l info dist/app.js",
|
||||||
"build:ts": "tsc",
|
"build": "tsc",
|
||||||
"watch:ts": "tsc -w",
|
"watch": "tsc -w",
|
||||||
"dev": "npm run build:ts && concurrently -k -p \"[{name}]\" -n \"TypeScript,App\" -c \"yellow.bold,cyan.bold\" \"npm:watch:ts\" \"npm:dev:start\"",
|
"dev": "npm run build && concurrently -k -p \"[{name}]\" -n \"TypeScript,App\" -c \"yellow.bold,cyan.bold\" \"npm:watch\" \"npm:dev:start\"",
|
||||||
"dev:start": "fastify start --ignore-watch=.ts$ -w --port 35530 --address 0.0.0.0 -l info -P dist/app.js"
|
"dev:start": "fastify start --ignore-watch=.ts$ -w --port 35530 --address 0.0.0.0 -l info -P dist/app.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
|
@ -20,18 +16,20 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/autoload": "^5.0.0",
|
"@fastify/autoload": "^5.0.0",
|
||||||
"@fastify/sensible": "^4.1.0",
|
"@fastify/sensible": "^4.1.0",
|
||||||
|
"@fastify/static": "^6.6.1",
|
||||||
|
"@prisma/client": "^4.8.1",
|
||||||
|
"NeteaseCloudMusicApi": "^4.8.7",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"fastify": "^4.0.0",
|
"fastify": "^4.5.3",
|
||||||
"fastify-cli": "^4.4.0",
|
"fastify-cli": "^4.4.0",
|
||||||
"fastify-plugin": "^3.0.0"
|
"fastify-plugin": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.0",
|
||||||
"@types/tap": "^15.0.5",
|
|
||||||
"concurrently": "^7.0.0",
|
"concurrently": "^7.0.0",
|
||||||
"fastify-tsconfig": "^1.0.1",
|
"fastify-tsconfig": "^1.0.1",
|
||||||
"tap": "^16.1.0",
|
"prisma": "^4.8.1",
|
||||||
"ts-node": "^10.4.0",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.5.4"
|
"typescript": "^4.9.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
45
packages/server/prisma/schema.prisma
Normal file
45
packages/server/prisma/schema.prisma
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "mysql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
relationMode = "prisma"
|
||||||
|
}
|
||||||
|
|
||||||
|
model Album {
|
||||||
|
id Int @id @unique
|
||||||
|
neteaseId Int @unique
|
||||||
|
name String @db.Text
|
||||||
|
neteaseName String @db.Text
|
||||||
|
artistName String @db.Text
|
||||||
|
neteaseArtistName String @db.Text
|
||||||
|
copyright String? @db.Text
|
||||||
|
editorialVideo String? @db.Text
|
||||||
|
artwork String? @db.Text
|
||||||
|
editorialNote AlbumEditorialNote?
|
||||||
|
}
|
||||||
|
|
||||||
|
model AlbumEditorialNote {
|
||||||
|
id Int @id @unique
|
||||||
|
album Album @relation(fields: [id], references: [id])
|
||||||
|
en_US String? @map("en-US") @db.Text
|
||||||
|
zh_CN String? @map("zh-CN") @db.Text
|
||||||
|
}
|
||||||
|
|
||||||
|
model Artist {
|
||||||
|
id Int @id @unique
|
||||||
|
neteaseId Int @unique
|
||||||
|
name String @db.Text
|
||||||
|
artwork String? @db.Text
|
||||||
|
editorialVideo String? @db.Text
|
||||||
|
artistBio ArtistBio?
|
||||||
|
}
|
||||||
|
|
||||||
|
model ArtistBio {
|
||||||
|
id Int @id @unique
|
||||||
|
artist Artist @relation(fields: [id], references: [id])
|
||||||
|
en_US String? @map("en-US") @db.Text
|
||||||
|
zh_CN String? @map("zh-CN") @db.Text
|
||||||
|
}
|
|
@ -2,28 +2,12 @@ import { join } from 'path'
|
||||||
import AutoLoad, { AutoloadPluginOptions } from '@fastify/autoload'
|
import AutoLoad, { AutoloadPluginOptions } from '@fastify/autoload'
|
||||||
import { FastifyPluginAsync } from 'fastify'
|
import { FastifyPluginAsync } from 'fastify'
|
||||||
|
|
||||||
export type AppOptions = {
|
const app: FastifyPluginAsync<AutoloadPluginOptions> = async (fastify, opts) => {
|
||||||
// Place your custom options for app below here.
|
|
||||||
} & Partial<AutoloadPluginOptions>
|
|
||||||
|
|
||||||
const app: FastifyPluginAsync<AppOptions> = async (
|
|
||||||
fastify,
|
|
||||||
opts
|
|
||||||
): Promise<void> => {
|
|
||||||
// Place here your custom code!
|
|
||||||
|
|
||||||
// Do not touch the following lines
|
|
||||||
|
|
||||||
// This loads all plugins defined in plugins
|
|
||||||
// those should be support plugins that are reused
|
|
||||||
// through your application
|
|
||||||
void fastify.register(AutoLoad, {
|
void fastify.register(AutoLoad, {
|
||||||
dir: join(__dirname, 'plugins'),
|
dir: join(__dirname, 'plugins'),
|
||||||
options: opts,
|
options: opts,
|
||||||
})
|
})
|
||||||
|
|
||||||
// This loads all plugins defined in routes
|
|
||||||
// define your routes in one of these
|
|
||||||
void fastify.register(AutoLoad, {
|
void fastify.register(AutoLoad, {
|
||||||
dir: join(__dirname, 'routes'),
|
dir: join(__dirname, 'routes'),
|
||||||
options: opts,
|
options: opts,
|
||||||
|
|
25
packages/server/src/plugins/prismaPlugin.ts
Normal file
25
packages/server/src/plugins/prismaPlugin.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import fp from 'fastify-plugin'
|
||||||
|
import { FastifyPluginAsync } from 'fastify'
|
||||||
|
import { PrismaClient } from '@prisma/client'
|
||||||
|
|
||||||
|
// Use TypeScript module augmentation to declare the type of server.prisma to be PrismaClient
|
||||||
|
declare module 'fastify' {
|
||||||
|
interface FastifyInstance {
|
||||||
|
prisma: PrismaClient
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const prismaPlugin: FastifyPluginAsync = fp(async (server, options) => {
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
await prisma.$connect()
|
||||||
|
|
||||||
|
// Make Prisma Client available through the fastify server instance: server.prisma
|
||||||
|
server.decorate('prisma', prisma)
|
||||||
|
|
||||||
|
server.addHook('onClose', async server => {
|
||||||
|
await server.prisma.$disconnect()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export default prismaPlugin
|
|
@ -1,24 +1,65 @@
|
||||||
import { FastifyPluginAsync } from 'fastify'
|
import { FastifyPluginAsync } from 'fastify'
|
||||||
import appleMusicRequest from '../../utils/appleMusicRequest'
|
import appleMusicRequest from '../../utils/appleMusicRequest'
|
||||||
|
import { album as getAlbum } from 'NeteaseCloudMusicApi'
|
||||||
|
|
||||||
const example: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
type ResponseSchema = {
|
||||||
|
id: number
|
||||||
|
neteaseId: number
|
||||||
|
name: string
|
||||||
|
artistName: string
|
||||||
|
editorialVideo: string
|
||||||
|
artwork: string
|
||||||
|
copyright: string
|
||||||
|
neteaseName: string
|
||||||
|
neteaseArtistName: string
|
||||||
|
editorialNote: {
|
||||||
|
en_US: string
|
||||||
|
zh_CN: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const album: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||||
fastify.get<{
|
fastify.get<{
|
||||||
Querystring: {
|
Querystring: {
|
||||||
name: string
|
neteaseId: string
|
||||||
artist: string
|
lang?: 'zh-CN' | 'en-US'
|
||||||
lang: 'zh-CN' | 'en-US'
|
|
||||||
}
|
}
|
||||||
}>('/album', async function (request, reply) {
|
}>('/album', opts, async function (request, reply): Promise<ResponseSchema | undefined> {
|
||||||
const { name, lang, artist } = request.query
|
const { neteaseId: neteaseIdString, lang = 'en-US' } = request.query
|
||||||
|
|
||||||
|
// validate neteaseAlbumID
|
||||||
|
const neteaseId = Number(neteaseIdString)
|
||||||
|
if (isNaN(neteaseId)) {
|
||||||
|
reply.code(400).send('params "neteaseId" is required')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get from database
|
||||||
|
const fromDB = await fastify.prisma.album.findFirst({
|
||||||
|
where: { neteaseId: neteaseId },
|
||||||
|
include: { editorialNote: { select: { en_US: true, zh_CN: true } } },
|
||||||
|
})
|
||||||
|
if (fromDB) {
|
||||||
|
return fromDB as ResponseSchema
|
||||||
|
}
|
||||||
|
|
||||||
|
// get from netease
|
||||||
|
const { body: neteaseAlbum } = (await getAlbum({ id: neteaseId })) as any
|
||||||
|
const artist = neteaseAlbum?.album?.artist?.name
|
||||||
|
const albumName = neteaseAlbum?.album?.name
|
||||||
|
if (!artist || !albumName) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get from apple
|
||||||
const fromApple = await appleMusicRequest({
|
const fromApple = await appleMusicRequest({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '/search',
|
url: '/search',
|
||||||
params: {
|
params: {
|
||||||
term: name,
|
term: `${artist} ${albumName}`,
|
||||||
types: 'albums',
|
types: 'albums',
|
||||||
'fields[albums]': 'artistName,name,editorialVideo,editorialNotes',
|
'fields[albums]': 'artistName,artwork,name,copyright,editorialVideo,editorialNotes',
|
||||||
limit: '1',
|
limit: '10',
|
||||||
l: lang.toLowerCase(),
|
l: lang.toLowerCase(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -27,12 +68,59 @@ const example: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||||
const album =
|
const album =
|
||||||
albums?.find(
|
albums?.find(
|
||||||
(a: any) =>
|
(a: any) =>
|
||||||
a.attributes.name.toLowerCase() === name.toLowerCase() &&
|
a.attributes.name.toLowerCase() === albumName.toLowerCase() &&
|
||||||
a.attributes.artistName.toLowerCase() === artist.toLowerCase()
|
a.attributes.artistName.toLowerCase() === artist.toLowerCase()
|
||||||
) || albums?.[0]
|
) || albums?.[0]
|
||||||
|
if (!album) return
|
||||||
|
|
||||||
return album
|
// get editorialNote
|
||||||
|
const editorialNote = {
|
||||||
|
en_US: lang === 'en-US' ? album.attributes.editorialNotes?.standard : '',
|
||||||
|
zh_CN: lang === 'zh-CN' ? album.attributes.editorialNotes?.standard : '',
|
||||||
|
}
|
||||||
|
const otherLangEditorialNoteResult = await appleMusicRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: `/albums/${album.id}`,
|
||||||
|
params: {
|
||||||
|
'fields[albums]': 'editorialNotes',
|
||||||
|
'omit[resource:albums]': 'relationships',
|
||||||
|
l: lang === 'zh-CN' ? 'en-US' : 'zh-CN',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const otherLangEditorialNote =
|
||||||
|
otherLangEditorialNoteResult?.data?.[0]?.attributes?.editorialNotes?.standard
|
||||||
|
if (lang === 'zh-CN') {
|
||||||
|
editorialNote.en_US = otherLangEditorialNote
|
||||||
|
} else if (lang === 'en-US') {
|
||||||
|
editorialNote.zh_CN = otherLangEditorialNote
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: ResponseSchema = {
|
||||||
|
id: Number(album.id),
|
||||||
|
neteaseId: Number(neteaseId),
|
||||||
|
name: album.attributes.name,
|
||||||
|
artistName: album.attributes.artistName,
|
||||||
|
editorialVideo: album.attributes.editorialVideo?.motionDetailSquare?.video,
|
||||||
|
artwork: album.attributes.artwork?.url,
|
||||||
|
editorialNote,
|
||||||
|
copyright: album.attributes.copyright,
|
||||||
|
neteaseName: albumName,
|
||||||
|
neteaseArtistName: artist,
|
||||||
|
}
|
||||||
|
reply.send(data)
|
||||||
|
|
||||||
|
// save to database
|
||||||
|
await fastify.prisma.album
|
||||||
|
.create({
|
||||||
|
data: {
|
||||||
|
...data,
|
||||||
|
editorialNote: { create: editorialNote },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch(e => console.error(e))
|
||||||
|
|
||||||
|
return
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default example
|
export default album
|
||||||
|
|
|
@ -1,46 +1,112 @@
|
||||||
import { FastifyPluginAsync } from 'fastify'
|
import { FastifyPluginAsync } from 'fastify'
|
||||||
import appleMusicRequest from '../../utils/appleMusicRequest'
|
import appleMusicRequest from '../../utils/appleMusicRequest'
|
||||||
|
import { artist_detail as getArtistDetail } from 'NeteaseCloudMusicApi'
|
||||||
|
|
||||||
const example: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
type ResponseSchema = {
|
||||||
|
id: number
|
||||||
|
neteaseId: number
|
||||||
|
editorialVideo: string
|
||||||
|
artwork: string
|
||||||
|
name: string
|
||||||
|
artistBio: {
|
||||||
|
en_US: string
|
||||||
|
zh_CN: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const artist: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||||
fastify.get<{
|
fastify.get<{
|
||||||
Querystring: {
|
Querystring: {
|
||||||
name: string
|
neteaseId: string
|
||||||
lang: 'zh-CN' | 'en-US'
|
lang?: 'zh-CN' | 'en-US'
|
||||||
}
|
}
|
||||||
}>('/artist', async function (request, reply) {
|
}>('/artist', async function (request, reply): Promise<ResponseSchema | undefined> {
|
||||||
const { name, lang } = request.query
|
const { neteaseId: neteaseIdString, lang = 'en-US' } = request.query
|
||||||
|
|
||||||
if (!name) {
|
// validate neteaseId
|
||||||
return {
|
const neteaseId = Number(neteaseIdString)
|
||||||
code: 400,
|
if (isNaN(neteaseId)) {
|
||||||
message: 'params "name" is required',
|
reply.code(400).send('params "neteaseId" is required')
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get from database
|
||||||
|
const fromDB = await fastify.prisma.artist.findFirst({
|
||||||
|
where: { neteaseId: neteaseId },
|
||||||
|
include: { artistBio: { select: { en_US: true, zh_CN: true } } },
|
||||||
|
})
|
||||||
|
if (fromDB) {
|
||||||
|
return fromDB as ResponseSchema
|
||||||
|
}
|
||||||
|
|
||||||
|
// get from netease
|
||||||
|
const { body: neteaseArtist } = (await getArtistDetail({ id: neteaseId })) as any
|
||||||
|
const artistName = neteaseArtist?.data?.artist?.name
|
||||||
|
if (!artistName) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const fromApple = await appleMusicRequest({
|
const fromApple = await appleMusicRequest({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '/search',
|
url: '/search',
|
||||||
params: {
|
params: {
|
||||||
term: name,
|
term: artistName,
|
||||||
types: 'artists',
|
types: 'artists',
|
||||||
'fields[artists]': 'url,name,artwork,editorialVideo,artistBio',
|
'fields[artists]': 'url,name,artwork,editorialVideo,artistBio',
|
||||||
'omit[resource:artists]': 'relationships',
|
'omit[resource:artists]': 'relationships',
|
||||||
platform: 'web',
|
platform: 'web',
|
||||||
limit: '1',
|
limit: '5',
|
||||||
l: lang?.toLowerCase() || 'en-us',
|
l: lang?.toLowerCase(),
|
||||||
with: 'serverBubbles',
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const artist = fromApple?.results?.artist?.data?.[0]
|
const artist = fromApple?.results?.artist?.data?.[0]
|
||||||
|
if (artist?.attributes?.name?.toLowerCase() !== artistName.toLowerCase()) {
|
||||||
if (
|
return
|
||||||
artist &&
|
|
||||||
artist?.attributes?.name?.toLowerCase() === name.toLowerCase()
|
|
||||||
) {
|
|
||||||
return artist
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get ArtistBio
|
||||||
|
const artistBio = {
|
||||||
|
en_US: lang === 'en-US' ? artist.attributes.artistBio : '',
|
||||||
|
zh_CN: lang === 'zh-CN' ? artist.attributes.artistBio : '',
|
||||||
|
}
|
||||||
|
const otherLangArtistBioResult = await appleMusicRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: `/artists/${artist.id}`,
|
||||||
|
params: {
|
||||||
|
'fields[artists]': 'artistBio',
|
||||||
|
'omit[resource:artists]': 'relationships',
|
||||||
|
l: lang === 'zh-CN' ? 'en-US' : 'zh-CN',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const otherLangArtistBio = otherLangArtistBioResult?.data?.[0]?.attributes?.artistBio
|
||||||
|
if (lang === 'zh-CN') {
|
||||||
|
artistBio.en_US = otherLangArtistBio
|
||||||
|
} else if (lang === 'en-US') {
|
||||||
|
artistBio.zh_CN = otherLangArtistBio
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: ResponseSchema = {
|
||||||
|
id: Number(artist.id),
|
||||||
|
neteaseId: neteaseId,
|
||||||
|
name: artist.attributes.name,
|
||||||
|
artistBio,
|
||||||
|
editorialVideo: artist?.attributes.editorialVideo?.motionArtistSquare1x1?.video,
|
||||||
|
artwork: artist?.attributes?.artwork?.url,
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.send(data)
|
||||||
|
|
||||||
|
// save to database
|
||||||
|
await fastify.prisma.artist
|
||||||
|
.create({
|
||||||
|
data: {
|
||||||
|
...data,
|
||||||
|
artistBio: { create: artistBio },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch(e => console.error(e))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default example
|
export default artist
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { FastifyPluginAsync } from 'fastify'
|
import { FastifyPluginAsync } from 'fastify'
|
||||||
|
|
||||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||||
fastify.get('/', async function (request, reply) {
|
fastify.get('/', async (request, reply) => {
|
||||||
return { root: true }
|
return 'R3PLAY server is running!'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
// This file contains code that we reuse between our tests.
|
|
||||||
import Fastify from 'fastify'
|
|
||||||
import fp from 'fastify-plugin'
|
|
||||||
import App from '../src/app'
|
|
||||||
import * as tap from 'tap'
|
|
||||||
|
|
||||||
export type Test = typeof tap['Test']['prototype']
|
|
||||||
|
|
||||||
// Fill in this config with all the configurations
|
|
||||||
// needed for testing the application
|
|
||||||
async function config() {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Automatically build and tear down our instance
|
|
||||||
async function build(t: Test) {
|
|
||||||
const app = Fastify()
|
|
||||||
|
|
||||||
// fastify-plugin ensures that all decorators
|
|
||||||
// are exposed for testing purposes, this is
|
|
||||||
// different from the production setup
|
|
||||||
void app.register(fp(App), await config())
|
|
||||||
|
|
||||||
await app.ready()
|
|
||||||
|
|
||||||
// Tear down our app after we are done
|
|
||||||
t.teardown(() => void app.close())
|
|
||||||
|
|
||||||
return app
|
|
||||||
}
|
|
||||||
|
|
||||||
export { config, build }
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { test } from 'tap'
|
|
||||||
import Fastify from 'fastify'
|
|
||||||
import Support from '../../src/plugins/support'
|
|
||||||
|
|
||||||
test('support works standalone', async t => {
|
|
||||||
const fastify = Fastify()
|
|
||||||
void fastify.register(Support)
|
|
||||||
await fastify.ready()
|
|
||||||
|
|
||||||
t.equal(fastify.someSupport(), 'hugs')
|
|
||||||
})
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { test } from 'tap'
|
|
||||||
import { build } from '../helper'
|
|
||||||
|
|
||||||
test('example is loaded', async t => {
|
|
||||||
const app = await build(t)
|
|
||||||
|
|
||||||
const res = await app.inject({
|
|
||||||
url: '/example',
|
|
||||||
})
|
|
||||||
|
|
||||||
t.equal(res.payload, 'this is an example')
|
|
||||||
})
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { test } from 'tap'
|
|
||||||
import { build } from '../helper'
|
|
||||||
|
|
||||||
test('default root route', async t => {
|
|
||||||
const app = await build(t)
|
|
||||||
|
|
||||||
const res = await app.inject({
|
|
||||||
url: '/',
|
|
||||||
})
|
|
||||||
t.same(JSON.parse(res.payload), { root: true })
|
|
||||||
})
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": ".",
|
|
||||||
"noEmit": true
|
|
||||||
},
|
|
||||||
"include": ["../src/**/*.ts", "**/*.ts"]
|
|
||||||
}
|
|
32
packages/shared/api/AppleMusic.ts
Normal file
32
packages/shared/api/AppleMusic.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
interface FetchAppleMusicAlbumParams {
|
||||||
|
neteaseId: number | string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FetchAppleMusicAlbumResponse {
|
||||||
|
id: number
|
||||||
|
neteaseId: number
|
||||||
|
name: string
|
||||||
|
artistName: string
|
||||||
|
editorialVideo: string
|
||||||
|
artwork: string
|
||||||
|
editorialNote: {
|
||||||
|
en_US: string
|
||||||
|
zh_CN: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FetchAppleMusicArtistParams {
|
||||||
|
neteaseId: number | string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FetchAppleMusicArtistResponse {
|
||||||
|
id: number
|
||||||
|
neteaseId: number
|
||||||
|
editorialVideo: string
|
||||||
|
artwork: string
|
||||||
|
name: string
|
||||||
|
artistBio: {
|
||||||
|
en_US: string
|
||||||
|
zh_CN: string
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ export interface FetchTracksResponse {
|
||||||
|
|
||||||
export interface FetchAudioSourceParams {
|
export interface FetchAudioSourceParams {
|
||||||
id: number
|
id: number
|
||||||
br?: number // bitrate, default 999000,320000 = 320kbps
|
level?: 'standard' | 'higher' | 'exhigh' | 'lossless' | 'hires' // 128kbps 192kbps 320kbps Lossless Hi-Res
|
||||||
}
|
}
|
||||||
export interface FetchAudioSourceResponse {
|
export interface FetchAudioSourceResponse {
|
||||||
code: number
|
code: number
|
||||||
|
|
1
packages/web/.gitignore
vendored
Normal file
1
packages/web/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.vercel
|
|
@ -8,11 +8,12 @@ import ScrollRestoration from '@/web/components/ScrollRestoration'
|
||||||
import Toaster from './components/Toaster'
|
import Toaster from './components/Toaster'
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const isMobile = useIsMobile()
|
// const isMobile = useIsMobile()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
{isMobile ? <LayoutMobile /> : <Layout />}
|
{/* {isMobile ? <LayoutMobile /> : <Layout />} */}
|
||||||
|
<Layout />
|
||||||
<Toaster />
|
<Toaster />
|
||||||
<ScrollRestoration />
|
<ScrollRestoration />
|
||||||
<IpcRendererReact />
|
<IpcRendererReact />
|
||||||
|
|
25
packages/web/api/appleMusic.ts
Normal file
25
packages/web/api/appleMusic.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import request from '../utils/request'
|
||||||
|
|
||||||
|
// AppleMusic专辑
|
||||||
|
export function fetchAppleMusicAlbum(
|
||||||
|
params: FetchAppleMusicAlbumParams
|
||||||
|
): Promise<FetchAppleMusicAlbumResponse> {
|
||||||
|
return request({
|
||||||
|
url: '/r3play/apple-music/album',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
baseURL: '/',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppleMusic艺人
|
||||||
|
export function fetchAppleMusicArtist(
|
||||||
|
params: FetchAppleMusicArtistParams
|
||||||
|
): Promise<FetchAppleMusicArtistResponse> {
|
||||||
|
return request({
|
||||||
|
url: '/r3play/apple-music/artist',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
baseURL: '/',
|
||||||
|
})
|
||||||
|
}
|
19
packages/web/api/hooks/useAppleMusicAlbum.ts
Normal file
19
packages/web/api/hooks/useAppleMusicAlbum.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import { fetchAppleMusicAlbum } from '../appleMusic'
|
||||||
|
|
||||||
|
const useAppleMusicAlbum = (id: string | number) => {
|
||||||
|
return useQuery(
|
||||||
|
['useAppleMusicAlbum', id],
|
||||||
|
async () => {
|
||||||
|
if (!id) return
|
||||||
|
return fetchAppleMusicAlbum({ neteaseId: id })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!id,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
refetchInterval: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useAppleMusicAlbum
|
19
packages/web/api/hooks/useAppleMusicArtist.ts
Normal file
19
packages/web/api/hooks/useAppleMusicArtist.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import { fetchAppleMusicArtist } from '../appleMusic'
|
||||||
|
|
||||||
|
const useAppleMusicArtist = (id: string | number) => {
|
||||||
|
return useQuery(
|
||||||
|
['useAppleMusicArtist', id],
|
||||||
|
async () => {
|
||||||
|
if (!id) return
|
||||||
|
return fetchAppleMusicArtist({ neteaseId: id })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!id,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
refetchInterval: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useAppleMusicArtist
|
|
@ -11,9 +11,7 @@ import {
|
||||||
} from '@/shared/api/Track'
|
} from '@/shared/api/Track'
|
||||||
|
|
||||||
// 获取歌曲详情
|
// 获取歌曲详情
|
||||||
export function fetchTracks(
|
export function fetchTracks(params: FetchTracksParams): Promise<FetchTracksResponse> {
|
||||||
params: FetchTracksParams
|
|
||||||
): Promise<FetchTracksResponse> {
|
|
||||||
return request({
|
return request({
|
||||||
url: '/song/detail',
|
url: '/song/detail',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
@ -28,16 +26,18 @@ export function fetchAudioSource(
|
||||||
params: FetchAudioSourceParams
|
params: FetchAudioSourceParams
|
||||||
): Promise<FetchAudioSourceResponse> {
|
): Promise<FetchAudioSourceResponse> {
|
||||||
return request({
|
return request({
|
||||||
url: '/song/url',
|
url: '/song/url/v1',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params,
|
params: {
|
||||||
|
level: 'exhigh',
|
||||||
|
...params,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取歌词
|
// 获取歌词
|
||||||
export function fetchLyric(
|
export function fetchLyric(params: FetchLyricParams): Promise<FetchLyricResponse> {
|
||||||
params: FetchLyricParams
|
|
||||||
): Promise<FetchLyricResponse> {
|
|
||||||
return request({
|
return request({
|
||||||
url: '/lyric',
|
url: '/lyric',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
@ -46,9 +46,7 @@ export function fetchLyric(
|
||||||
}
|
}
|
||||||
|
|
||||||
// 收藏歌曲
|
// 收藏歌曲
|
||||||
export function likeATrack(
|
export function likeATrack(params: LikeATrackParams): Promise<LikeATrackResponse> {
|
||||||
params: LikeATrackParams
|
|
||||||
): Promise<LikeATrackResponse> {
|
|
||||||
return request({
|
return request({
|
||||||
url: '/like',
|
url: '/like',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
|
|
@ -28,12 +28,7 @@ const ArtistInline = ({
|
||||||
if (!artists) return <div></div>
|
if (!artists) return <div></div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={cx(!className?.includes('line-clamp') && 'line-clamp-1', className)}>
|
||||||
className={cx(
|
|
||||||
!className?.includes('line-clamp') && 'line-clamp-1',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{artists.map((artist, index) => (
|
{artists.map((artist, index) => (
|
||||||
<span key={`${artist.id}-${artist.name}`}>
|
<span key={`${artist.id}-${artist.name}`}>
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { prefetchAlbum } from '@/web/api/hooks/useAlbum'
|
||||||
import { prefetchPlaylist } from '@/web/api/hooks/usePlaylist'
|
import { prefetchPlaylist } from '@/web/api/hooks/usePlaylist'
|
||||||
import { memo, useCallback } from 'react'
|
import { memo, useCallback } from 'react'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import ArtistInline from './ArtistsInLine'
|
import ArtistInline from './ArtistsInline'
|
||||||
|
|
||||||
type ItemTitle = undefined | 'name'
|
type ItemTitle = undefined | 'name'
|
||||||
type ItemSubTitle = undefined | 'artist' | 'year'
|
type ItemSubTitle = undefined | 'artist' | 'year'
|
||||||
|
@ -56,15 +56,9 @@ const Album = ({
|
||||||
onMouseOver={prefetch}
|
onMouseOver={prefetch}
|
||||||
/>
|
/>
|
||||||
{title && (
|
{title && (
|
||||||
<div className='line-clamp-2 mt-2 text-14 font-medium text-neutral-300'>
|
<div className='line-clamp-2 mt-2 text-14 font-medium text-neutral-300'>{title}</div>
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{subtitle && (
|
|
||||||
<div className='mt-1 text-14 font-medium text-neutral-700'>
|
|
||||||
{subtitle}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
{subtitle && <div className='mt-1 text-14 font-medium text-neutral-700'>{subtitle}</div>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -107,21 +101,12 @@ const CoverRow = ({
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
{title && (
|
{title && <h4 className='mb-6 text-14 font-bold uppercase dark:text-neutral-300'>{title}</h4>}
|
||||||
<h4 className='mb-6 text-14 font-bold uppercase dark:text-neutral-300'>
|
|
||||||
{title}
|
|
||||||
</h4>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Items */}
|
{/* Items */}
|
||||||
<div className='grid grid-cols-3 gap-4 lg:gap-6 xl:grid-cols-4 2xl:grid-cols-5'>
|
<div className='grid grid-cols-3 gap-4 lg:gap-6 xl:grid-cols-4 2xl:grid-cols-5'>
|
||||||
{albums?.map(album => (
|
{albums?.map(album => (
|
||||||
<Album
|
<Album key={album.id} album={album} itemTitle={itemTitle} itemSubtitle={itemSubtitle} />
|
||||||
key={album.id}
|
|
||||||
album={album}
|
|
||||||
itemTitle={itemTitle}
|
|
||||||
itemSubtitle={itemSubtitle}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
{playlists?.map(playlist => (
|
{playlists?.map(playlist => (
|
||||||
<Playlist key={playlist.id} playlist={playlist} />
|
<Playlist key={playlist.id} playlist={playlist} />
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Main from '@/web/components/Main'
|
||||||
import Player from '@/web/components/Player'
|
import Player from '@/web/components/Player'
|
||||||
import MenuBar from '@/web/components/MenuBar'
|
import MenuBar from '@/web/components/MenuBar'
|
||||||
import Topbar from '@/web/components/Topbar/TopbarDesktop'
|
import Topbar from '@/web/components/Topbar/TopbarDesktop'
|
||||||
import { cx } from '@emotion/css'
|
import { css, cx } from '@emotion/css'
|
||||||
import player from '@/web/states/player'
|
import player from '@/web/states/player'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import Login from './Login'
|
import Login from './Login'
|
||||||
|
@ -22,7 +22,10 @@ const Layout = () => {
|
||||||
id='layout'
|
id='layout'
|
||||||
className={cx(
|
className={cx(
|
||||||
'relative grid h-screen select-none overflow-hidden bg-white dark:bg-black',
|
'relative grid h-screen select-none overflow-hidden bg-white dark:bg-black',
|
||||||
window.env?.isElectron && !fullscreen && 'rounded-24'
|
window.env?.isElectron && !fullscreen && 'rounded-24',
|
||||||
|
css`
|
||||||
|
min-width: 720px;
|
||||||
|
`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<BlurBackground />
|
<BlurBackground />
|
||||||
|
@ -40,9 +43,7 @@ const Layout = () => {
|
||||||
|
|
||||||
{(window.env?.isWindows ||
|
{(window.env?.isWindows ||
|
||||||
window.env?.isLinux ||
|
window.env?.isLinux ||
|
||||||
window.localStorage.getItem('showWindowsTitleBar') === 'true') && (
|
window.localStorage.getItem('showWindowsTitleBar') === 'true') && <TitleBar />}
|
||||||
<TitleBar />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<ContextMenus />
|
<ContextMenus />
|
||||||
|
|
||||||
|
|
|
@ -23,25 +23,19 @@ const LayoutMobile = () => {
|
||||||
<Router />
|
<Router />
|
||||||
</main>
|
</main>
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx('fixed bottom-0 left-0 right-0 z-20 pt-3 dark:bg-black')}
|
||||||
'fixed bottom-0 left-0 right-0 z-20 pt-3 dark:bg-black',
|
style={{
|
||||||
css`
|
paddingBottom: `calc(
|
||||||
padding-bottom: calc(
|
|
||||||
${isIosPwa ? '24px' : 'env(safe-area-inset-bottom)'} + 0.75rem
|
${isIosPwa ? '24px' : 'env(safe-area-inset-bottom)'} + 0.75rem
|
||||||
);
|
)`,
|
||||||
`
|
}}
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{showPlayer && (
|
{showPlayer && (
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx('absolute left-7 right-7 z-20')}
|
||||||
'absolute left-7 right-7 z-20',
|
style={{
|
||||||
css`
|
top: `calc(-100% - 6px + ${isIosPwa ? '24px' : 'env(safe-area-inset-bottom)'})`,
|
||||||
top: calc(
|
}}
|
||||||
-100% - 6px + ${isIosPwa ? '24px' : 'env(safe-area-inset-bottom)'}
|
|
||||||
);
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<Player />
|
<Player />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,13 +11,7 @@ import persistedUiStates from '@/web/states/persistedUiStates'
|
||||||
import useUser from '@/web/api/hooks/useUser'
|
import useUser from '@/web/api/hooks/useUser'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const OR = ({
|
const OR = ({ children, onClick }: { children: React.ReactNode; onClick: () => void }) => {
|
||||||
children,
|
|
||||||
onClick,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
onClick: () => void
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -125,10 +119,9 @@ const Login = () => {
|
||||||
<motion.div
|
<motion.div
|
||||||
animate={animateCard}
|
animate={animateCard}
|
||||||
className={cx(
|
className={cx(
|
||||||
'relative rounded-48 bg-white/10 p-9',
|
'relative h-fit rounded-48 bg-white/10 p-9',
|
||||||
css`
|
css`
|
||||||
width: 392px;
|
width: 392px;
|
||||||
height: fit-content;
|
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -136,9 +129,7 @@ const Login = () => {
|
||||||
{cardType === 'phone/email' && <LoginWithPhoneOrEmail />}
|
{cardType === 'phone/email' && <LoginWithPhoneOrEmail />}
|
||||||
|
|
||||||
<OR onClick={handleSwitchCard}>
|
<OR onClick={handleSwitchCard}>
|
||||||
{cardType === 'qrCode'
|
{cardType === 'qrCode' ? t`auth.use-phone-or-email` : t`auth.scan-qr-code`}
|
||||||
? t`auth.use-phone-or-email`
|
|
||||||
: t`auth.scan-qr-code`}
|
|
||||||
</OR>
|
</OR>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import useUserLikedTracksIDs, {
|
import useUserLikedTracksIDs, { useMutationLikeATrack } from '@/web/api/hooks/useUserLikedTracksIDs'
|
||||||
useMutationLikeATrack,
|
|
||||||
} from '@/web/api/hooks/useUserLikedTracksIDs'
|
|
||||||
import player from '@/web/states/player'
|
import player from '@/web/states/player'
|
||||||
import { resizeImage } from '@/web/utils/common'
|
import { resizeImage } from '@/web/utils/common'
|
||||||
|
|
||||||
|
@ -30,8 +28,7 @@ const PlayingTrack = () => {
|
||||||
[playerSnapshot.trackListSource]
|
[playerSnapshot.trackListSource]
|
||||||
)
|
)
|
||||||
|
|
||||||
const hasListSource =
|
const hasListSource = playerSnapshot.mode !== PlayerMode.FM && trackListSource?.type
|
||||||
playerSnapshot.mode !== PlayerMode.FM && trackListSource?.type
|
|
||||||
|
|
||||||
const toTrackListSource = () => {
|
const toTrackListSource = () => {
|
||||||
if (!hasListSource) return
|
if (!hasListSource) return
|
||||||
|
@ -76,16 +73,10 @@ const LikeButton = ({ track }: { track: Track | undefined | null }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mr-1 '>
|
<div className='mr-1 '>
|
||||||
<IconButton
|
<IconButton onClick={() => track?.id && mutationLikeATrack.mutate(track.id)}>
|
||||||
onClick={() => track?.id && mutationLikeATrack.mutate(track.id)}
|
|
||||||
>
|
|
||||||
<Icon
|
<Icon
|
||||||
className='h-6 w-6 text-white'
|
className='h-6 w-6 text-white'
|
||||||
name={
|
name={track?.id && userLikedSongs?.ids?.includes(track.id) ? 'heart' : 'heart-outline'}
|
||||||
track?.id && userLikedSongs?.ids?.includes(track.id)
|
|
||||||
? 'heart'
|
|
||||||
: 'heart-outline'
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
@ -101,10 +92,7 @@ const Controls = () => {
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center justify-center gap-2 text-white'>
|
<div className='flex items-center justify-center gap-2 text-white'>
|
||||||
{mode === PlayerMode.TrackList && (
|
{mode === PlayerMode.TrackList && (
|
||||||
<IconButton
|
<IconButton onClick={() => track && player.prevTrack()} disabled={!track}>
|
||||||
onClick={() => track && player.prevTrack()}
|
|
||||||
disabled={!track}
|
|
||||||
>
|
|
||||||
<Icon className='h-6 w-6' name='previous' />
|
<Icon className='h-6 w-6' name='previous' />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
|
@ -120,11 +108,7 @@ const Controls = () => {
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
className='h-7 w-7'
|
className='h-7 w-7'
|
||||||
name={
|
name={[PlayerState.Playing, PlayerState.Loading].includes(state) ? 'pause' : 'play'}
|
||||||
[PlayerState.Playing, PlayerState.Loading].includes(state)
|
|
||||||
? 'pause'
|
|
||||||
: 'play'
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton onClick={() => track && player.nextTrack()} disabled={!track}>
|
<IconButton onClick={() => track && player.nextTrack()} disabled={!track}>
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css'
|
||||||
import player from '@/web/states/player'
|
import player from '@/web/states/player'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import { AnimatePresence, motion } from 'framer-motion'
|
import { AnimatePresence, motion } from 'framer-motion'
|
||||||
import ArtistInline from '@/web/components/ArtistsInline'
|
import ArtistInline from '../ArtistsInline'
|
||||||
import persistedUiStates from '@/web/states/persistedUiStates'
|
import persistedUiStates from '@/web/states/persistedUiStates'
|
||||||
import Controls from './Controls'
|
import Controls from './Controls'
|
||||||
import Cover from './Cover'
|
import Cover from './Cover'
|
||||||
|
@ -34,9 +34,7 @@ const NowPlaying = () => {
|
||||||
{/* Info & Controls */}
|
{/* Info & Controls */}
|
||||||
<div className='m-3 flex flex-col items-center rounded-20 bg-white/60 p-5 font-medium backdrop-blur-3xl dark:bg-black/70'>
|
<div className='m-3 flex flex-col items-center rounded-20 bg-white/60 p-5 font-medium backdrop-blur-3xl dark:bg-black/70'>
|
||||||
{/* Track Info */}
|
{/* Track Info */}
|
||||||
<div className='line-clamp-1 text-lg text-black dark:text-white'>
|
<div className='line-clamp-1 text-lg text-black dark:text-white'>{track?.name}</div>
|
||||||
{track?.name}
|
|
||||||
</div>
|
|
||||||
<ArtistInline
|
<ArtistInline
|
||||||
artists={track?.ar || []}
|
artists={track?.ar || []}
|
||||||
className='text-black/30 dark:text-white/30'
|
className='text-black/30 dark:text-white/30'
|
||||||
|
|
|
@ -5,6 +5,7 @@ const Tabs = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
|
style,
|
||||||
}: {
|
}: {
|
||||||
tabs: {
|
tabs: {
|
||||||
id: string
|
id: string
|
||||||
|
@ -13,9 +14,10 @@ const Tabs = ({
|
||||||
value: string
|
value: string
|
||||||
onChange: (id: string) => void
|
onChange: (id: string) => void
|
||||||
className?: string
|
className?: string
|
||||||
|
style?: React.CSSProperties
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={cx('no-scrollbar flex overflow-y-auto', className)}>
|
<div className={cx('no-scrollbar flex overflow-y-auto', className)} style={style}>
|
||||||
{tabs.map(tab => (
|
{tabs.map(tab => (
|
||||||
<div
|
<div
|
||||||
key={tab.id}
|
key={tab.id}
|
||||||
|
|
|
@ -30,11 +30,9 @@ const Background = () => {
|
||||||
transition={{ ease }}
|
transition={{ ease }}
|
||||||
className={cx(
|
className={cx(
|
||||||
'absolute inset-0 z-0 bg-contain bg-repeat-x',
|
'absolute inset-0 z-0 bg-contain bg-repeat-x',
|
||||||
window.env?.isElectron && 'rounded-t-24',
|
window.env?.isElectron && 'rounded-t-24'
|
||||||
css`
|
|
||||||
background-image: url(${topbarBackground});
|
|
||||||
`
|
|
||||||
)}
|
)}
|
||||||
|
style={{ backgroundImage: `url(${topbarBackground})` }}
|
||||||
></motion.div>
|
></motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
|
@ -67,14 +67,14 @@ const Info = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{!isMobile && (
|
{!isMobile && description && (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
className='line-clamp-3 mt-6 whitespace-pre-wrap text-14 font-bold dark:text-white/40'
|
className='line-clamp-3 mt-6 whitespace-pre-wrap text-14 font-bold dark:text-white/40'
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: description || '',
|
__html: description,
|
||||||
}}
|
}}
|
||||||
></motion.div>
|
></motion.div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { IpcChannels } from '@/shared/IpcChannels'
|
|
||||||
import { useQuery } from '@tanstack/react-query'
|
|
||||||
|
|
||||||
export default function useAppleMusicAlbum(props: {
|
|
||||||
id?: number
|
|
||||||
name?: string
|
|
||||||
artist?: string
|
|
||||||
}) {
|
|
||||||
const { id, name, artist } = props
|
|
||||||
return useQuery(
|
|
||||||
['useAppleMusicAlbum', props],
|
|
||||||
async () => {
|
|
||||||
if (!id || !name || !artist) return
|
|
||||||
return window.ipcRenderer?.invoke(IpcChannels.GetAlbumFromAppleMusic, {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
artist,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: !!id && !!name && !!artist,
|
|
||||||
refetchOnWindowFocus: false,
|
|
||||||
refetchInterval: false,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
import { AppleMusicArtist } from '@/shared/AppleMusic'
|
|
||||||
import { APIs } from '@/shared/CacheAPIs'
|
|
||||||
import { IpcChannels } from '@/shared/IpcChannels'
|
|
||||||
import { useQuery } from '@tanstack/react-query'
|
|
||||||
|
|
||||||
export default function useAppleMusicArtist(props: {
|
|
||||||
id?: number
|
|
||||||
name?: string
|
|
||||||
}) {
|
|
||||||
const { id, name } = props
|
|
||||||
return useQuery(
|
|
||||||
['useAppleMusicArtist', props],
|
|
||||||
async () => {
|
|
||||||
if (!id || !name) return
|
|
||||||
|
|
||||||
const cache = await window.ipcRenderer?.invoke(IpcChannels.GetApiCache, {
|
|
||||||
api: APIs.AppleMusicArtist,
|
|
||||||
query: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if (cache) return cache
|
|
||||||
|
|
||||||
return window.ipcRenderer?.invoke(IpcChannels.GetArtistFromAppleMusic, {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: !!id && !!name,
|
|
||||||
refetchOnWindowFocus: false,
|
|
||||||
refetchInterval: false,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@
|
||||||
content="script-src 'self' 'unsafe-inline' www.googletagmanager.com blob:;" />
|
content="script-src 'self' 'unsafe-inline' www.googletagmanager.com blob:;" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<title>R3Play</title>
|
<title>R3PLAY</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import useAlbum from '@/web/api/hooks/useAlbum'
|
import useAlbum from '@/web/api/hooks/useAlbum'
|
||||||
import useUserAlbums, {
|
import useUserAlbums, { useMutationLikeAAlbum } from '@/web/api/hooks/useUserAlbums'
|
||||||
useMutationLikeAAlbum,
|
|
||||||
} from '@/web/api/hooks/useUserAlbums'
|
|
||||||
import Icon from '@/web/components/Icon'
|
import Icon from '@/web/components/Icon'
|
||||||
import TrackListHeader from '@/web/components/TrackListHeader'
|
import TrackListHeader from '@/web/components/TrackListHeader'
|
||||||
import useAppleMusicAlbum from '@/web/hooks/useAppleMusicAlbum'
|
|
||||||
import useVideoCover from '@/web/hooks/useVideoCover'
|
import useVideoCover from '@/web/hooks/useVideoCover'
|
||||||
import player from '@/web/states/player'
|
import player from '@/web/states/player'
|
||||||
import { formatDuration } from '@/web/utils/common'
|
import { formatDuration } from '@/web/utils/common'
|
||||||
|
@ -13,6 +10,7 @@ import { useMemo } from 'react'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import useAppleMusicAlbum from '@/web/api/hooks/useAppleMusicAlbum'
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const { t, i18n } = useTranslation()
|
const { t, i18n } = useTranslation()
|
||||||
|
@ -25,51 +23,31 @@ const Header = () => {
|
||||||
})
|
})
|
||||||
const album = useMemo(() => albumRaw?.album, [albumRaw])
|
const album = useMemo(() => albumRaw?.album, [albumRaw])
|
||||||
|
|
||||||
const { data: albumFromApple, isLoading: isLoadingAlbumFromApple } =
|
const { data: appleMusicAlbum, isLoading: isLoadingAppleMusicAlbum } = useAppleMusicAlbum(
|
||||||
useAppleMusicAlbum({
|
album?.id || 0
|
||||||
id: album?.id,
|
)
|
||||||
name: album?.name,
|
|
||||||
artist: album?.artist.name,
|
|
||||||
})
|
|
||||||
|
|
||||||
const { data: videoCoverFromRemote } = useVideoCover({
|
|
||||||
id: album?.id,
|
|
||||||
name: album?.name,
|
|
||||||
artist: album?.artist.name,
|
|
||||||
enabled: !window.env?.isElectron,
|
|
||||||
})
|
|
||||||
|
|
||||||
// For <Cover />
|
// For <Cover />
|
||||||
const cover = album?.picUrl
|
const cover = album?.picUrl
|
||||||
const videoCover =
|
const videoCover = appleMusicAlbum?.editorialVideo
|
||||||
albumFromApple?.attributes?.editorialVideo?.motionSquareVideo1x1?.video ||
|
|
||||||
videoCoverFromRemote?.video
|
|
||||||
|
|
||||||
// For <Info />
|
// For <Info />
|
||||||
const title = album?.name
|
const title = album?.name
|
||||||
const creatorName = album?.artist.name
|
const creatorName = album?.artist.name
|
||||||
const creatorLink = `/artist/${album?.artist.id}`
|
const creatorLink = `/artist/${album?.artist.id}`
|
||||||
const description = isLoadingAlbumFromApple
|
const description = isLoadingAppleMusicAlbum
|
||||||
? ''
|
? ''
|
||||||
: albumFromApple?.attributes?.editorialNotes?.standard || album?.description
|
: appleMusicAlbum?.editorialNote?.[i18n.language.replace('-', '_')] || album?.description
|
||||||
const extraInfo = useMemo(() => {
|
const extraInfo = useMemo(() => {
|
||||||
const duration = album?.songs?.reduce((acc, cur) => acc + cur.dt, 0) || 0
|
const duration = album?.songs?.reduce((acc, cur) => acc + cur.dt, 0) || 0
|
||||||
const albumDuration = formatDuration(
|
const albumDuration = formatDuration(duration, i18n.language, 'hh[hr] mm[min]')
|
||||||
duration,
|
|
||||||
i18n.language,
|
|
||||||
'hh[hr] mm[min]'
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{album?.mark === 1056768 && (
|
{album?.mark === 1056768 && (
|
||||||
<Icon
|
<Icon name='explicit' className='mb-px mr-1 h-3 w-3 lg:h-3.5 lg:w-3.5' />
|
||||||
name='explicit'
|
|
||||||
className='mb-px mr-1 h-3 w-3 lg:h-3.5 lg:w-3.5'
|
|
||||||
/>
|
|
||||||
)}{' '}
|
)}{' '}
|
||||||
{dayjs(album?.publishTime || 0).year()} ·{' '}
|
{dayjs(album?.publishTime || 0).year()} ·{' '}
|
||||||
{t('common.track-with-count', { count: album?.songs?.length })},{' '}
|
{t('common.track-with-count', { count: album?.songs?.length })}, {albumDuration}
|
||||||
{albumDuration}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}, [album?.mark, album?.publishTime, album?.songs, i18n.language, t])
|
}, [album?.mark, album?.publishTime, album?.songs, i18n.language, t])
|
||||||
|
|
|
@ -8,6 +8,8 @@ const ArtistVideos = () => {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const { data: videos } = useArtistMV({ id: Number(params.id) || 0 })
|
const { data: videos } = useArtistMV({ id: Number(params.id) || 0 })
|
||||||
|
|
||||||
|
if (!videos?.mvs?.length) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='mb-6 mt-10 text-12 font-medium uppercase text-neutral-300'>
|
<div className='mb-6 mt-10 text-12 font-medium uppercase text-neutral-300'>
|
||||||
|
@ -16,17 +18,12 @@ const ArtistVideos = () => {
|
||||||
|
|
||||||
<div className='grid grid-cols-3 gap-6'>
|
<div className='grid grid-cols-3 gap-6'>
|
||||||
{videos?.mvs?.slice(0, 6)?.map(video => (
|
{videos?.mvs?.slice(0, 6)?.map(video => (
|
||||||
<div
|
<div key={video.id} onClick={() => (uiStates.playingVideoID = video.id)}>
|
||||||
key={video.id}
|
|
||||||
onClick={() => (uiStates.playingVideoID = video.id)}
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
src={video.imgurl16v9}
|
src={video.imgurl16v9}
|
||||||
className='aspect-video w-full rounded-24 border border-white/5 object-contain'
|
className='aspect-video w-full rounded-24 border border-white/5 object-contain'
|
||||||
/>
|
/>
|
||||||
<div className='mt-2 text-12 font-medium text-neutral-600'>
|
<div className='mt-2 text-12 font-medium text-neutral-600'>{video.name}</div>
|
||||||
{video.name}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,23 +1,16 @@
|
||||||
import useIsMobile from '@/web/hooks/useIsMobile'
|
import useIsMobile from '@/web/hooks/useIsMobile'
|
||||||
import useAppleMusicArtist from '@/web/hooks/useAppleMusicArtist'
|
import useAppleMusicArtist from '@/web/api/hooks/useAppleMusicArtist'
|
||||||
import { cx, css } from '@emotion/css'
|
import { cx, css } from '@emotion/css'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import i18next from 'i18next'
|
||||||
|
|
||||||
const ArtistInfo = ({
|
const ArtistInfo = ({ artist, isLoading }: { artist?: Artist; isLoading: boolean }) => {
|
||||||
artist,
|
const { t, i18n } = useTranslation()
|
||||||
isLoading,
|
|
||||||
}: {
|
|
||||||
artist?: Artist
|
|
||||||
isLoading: boolean
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
const isMobile = useIsMobile()
|
const isMobile = useIsMobile()
|
||||||
const { data: artistFromApple, isLoading: isLoadingArtistFromApple } =
|
const { data: artistFromApple, isLoading: isLoadingArtistFromApple } = useAppleMusicArtist(
|
||||||
useAppleMusicArtist({
|
artist?.id || 0
|
||||||
id: artist?.id,
|
)
|
||||||
name: artist?.name,
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -27,9 +20,7 @@ const ArtistInfo = ({
|
||||||
<span className='rounded-full bg-white/10'>PLACEHOLDER</span>
|
<span className='rounded-full bg-white/10'>PLACEHOLDER</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className='text-28 font-semibold text-white/70 lg:text-32'>
|
<div className='text-28 font-semibold text-white/70 lg:text-32'>{artist?.name}</div>
|
||||||
{artist?.name}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Type */}
|
{/* Type */}
|
||||||
|
@ -38,9 +29,7 @@ const ArtistInfo = ({
|
||||||
<span className='rounded-full bg-white/10'>Artist</span>
|
<span className='rounded-full bg-white/10'>Artist</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className='mt-2.5 text-24 font-medium text-white/40 lg:mt-6'>
|
<div className='mt-2.5 text-24 font-medium text-white/40 lg:mt-6'>Artist</div>
|
||||||
Artist
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Counts */}
|
{/* Counts */}
|
||||||
|
@ -67,9 +56,7 @@ const ArtistInfo = ({
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className='rounded-full bg-white/10'>
|
<span className='rounded-full bg-white/10'>PLACEHOLDER1234567890</span>
|
||||||
PLACEHOLDER1234567890
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
|
@ -80,7 +67,7 @@ const ArtistInfo = ({
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{artistFromApple?.attributes?.artistBio || artist?.briefDesc}
|
{artistFromApple?.artistBio?.[i18n.language.replace('-', '_')] || artist?.briefDesc}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,25 +1,18 @@
|
||||||
import { isIOS, isSafari, resizeImage } from '@/web/utils/common'
|
import { resizeImage } from '@/web/utils/common'
|
||||||
import Image from '@/web/components/Image'
|
import Image from '@/web/components/Image'
|
||||||
import { cx, css } from '@emotion/css'
|
import { cx, css } from '@emotion/css'
|
||||||
import useAppleMusicArtist from '@/web/hooks/useAppleMusicArtist'
|
import useAppleMusicArtist from '@/web/api/hooks/useAppleMusicArtist'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect } from 'react'
|
||||||
import Hls from 'hls.js'
|
|
||||||
import { motion } from 'framer-motion'
|
|
||||||
import uiStates from '@/web/states/uiStates'
|
import uiStates from '@/web/states/uiStates'
|
||||||
import VideoCover from '@/web/components/VideoCover'
|
import VideoCover from '@/web/components/VideoCover'
|
||||||
|
|
||||||
const Cover = ({ artist }: { artist?: Artist }) => {
|
const Cover = ({ artist }: { artist?: Artist }) => {
|
||||||
const { data: artistFromApple, isLoading: isLoadingArtistFromApple } =
|
const { data: artistFromApple, isLoading: isLoadingArtistFromApple } = useAppleMusicArtist(
|
||||||
useAppleMusicArtist({
|
artist?.id || 0
|
||||||
id: artist?.id,
|
)
|
||||||
name: artist?.name,
|
|
||||||
})
|
|
||||||
|
|
||||||
const video =
|
const video = artistFromApple?.editorialVideo
|
||||||
artistFromApple?.attributes?.editorialVideo?.motionArtistSquare1x1?.video
|
const cover = isLoadingArtistFromApple ? '' : artistFromApple?.artwork || artist?.img1v1Url || ''
|
||||||
const cover = isLoadingArtistFromApple
|
|
||||||
? ''
|
|
||||||
: artistFromApple?.attributes?.artwork?.url || artist?.img1v1Url
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (cover) uiStates.blurBackgroundImage = cover
|
if (cover) uiStates.blurBackgroundImage = cover
|
||||||
|
@ -40,14 +33,7 @@ const Cover = ({ artist }: { artist?: Artist }) => {
|
||||||
'aspect-square h-full w-full lg:z-10',
|
'aspect-square h-full w-full lg:z-10',
|
||||||
video ? 'opacity-0' : 'opacity-100'
|
video ? 'opacity-0' : 'opacity-100'
|
||||||
)}
|
)}
|
||||||
src={resizeImage(
|
src={resizeImage(isLoadingArtistFromApple ? '' : cover, 'lg')}
|
||||||
isLoadingArtistFromApple
|
|
||||||
? ''
|
|
||||||
: artistFromApple?.attributes?.artwork?.url ||
|
|
||||||
artist?.img1v1Url ||
|
|
||||||
'',
|
|
||||||
'lg'
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{video && <VideoCover source={video} />}
|
{video && <VideoCover source={video} />}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import Tabs from '@/web/components/Tabs'
|
import Tabs from '@/web/components/Tabs'
|
||||||
import {
|
import { fetchDailyRecommendPlaylists, fetchRecommendedPlaylists } from '@/web/api/playlist'
|
||||||
fetchDailyRecommendPlaylists,
|
|
||||||
fetchRecommendedPlaylists,
|
|
||||||
} from '@/web/api/playlist'
|
|
||||||
import { PlaylistApiNames } from '@/shared/api/Playlists'
|
import { PlaylistApiNames } from '@/shared/api/Playlists'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
@ -33,10 +30,7 @@ const Recommend = () => {
|
||||||
const playlists =
|
const playlists =
|
||||||
isLoadingDaily || isLoading
|
isLoadingDaily || isLoading
|
||||||
? []
|
? []
|
||||||
: [
|
: [...(dailyRecommendPlaylists?.recommend || []), ...(recommendedPlaylists?.result || [])]
|
||||||
...(dailyRecommendPlaylists?.recommend || []),
|
|
||||||
...(recommendedPlaylists?.result || []),
|
|
||||||
]
|
|
||||||
|
|
||||||
return <CoverRowVirtual playlists={playlists} />
|
return <CoverRowVirtual playlists={playlists} />
|
||||||
|
|
||||||
|
@ -69,10 +63,12 @@ const Browse = () => {
|
||||||
'pointer-events-none fixed top-0 left-10 z-10 hidden lg:block',
|
'pointer-events-none fixed top-0 left-10 z-10 hidden lg:block',
|
||||||
css`
|
css`
|
||||||
height: 230px;
|
height: 230px;
|
||||||
right: ${playerWidth + 32}px;
|
|
||||||
background-image: url(${topbarBackground});
|
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
|
style={{
|
||||||
|
right: `${playerWidth + 32}px`,
|
||||||
|
backgroundImage: `url(${topbarBackground})`,
|
||||||
|
}}
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
|
|
|
@ -17,13 +17,12 @@ import { throttle } from 'lodash-es'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import VideoRow from '@/web/components/VideoRow'
|
import VideoRow from '@/web/components/VideoRow'
|
||||||
import useUserVideos from '@/web/api/hooks/useUserVideos'
|
import useUserVideos from '@/web/api/hooks/useUserVideos'
|
||||||
|
import persistedUiStates from '@/web/states/persistedUiStates'
|
||||||
|
|
||||||
const Albums = () => {
|
const Albums = () => {
|
||||||
const { data: albums } = useUserAlbums()
|
const { data: albums } = useUserAlbums()
|
||||||
|
|
||||||
return (
|
return <CoverRow albums={albums?.data} itemTitle='name' itemSubtitle='artist' />
|
||||||
<CoverRow albums={albums?.data} itemTitle='name' itemSubtitle='artist' />
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Playlists = () => {
|
const Playlists = () => {
|
||||||
|
@ -65,9 +64,8 @@ const CollectionTabs = ({ showBg }: { showBg: boolean }) => {
|
||||||
]
|
]
|
||||||
|
|
||||||
const { librarySelectedTab: selectedTab } = useSnapshot(uiStates)
|
const { librarySelectedTab: selectedTab } = useSnapshot(uiStates)
|
||||||
const setSelectedTab = (
|
const { minimizePlayer } = useSnapshot(persistedUiStates)
|
||||||
id: 'playlists' | 'albums' | 'artists' | 'videos'
|
const setSelectedTab = (id: 'playlists' | 'albums' | 'artists' | 'videos') => {
|
||||||
) => {
|
|
||||||
uiStates.librarySelectedTab = id
|
uiStates.librarySelectedTab = id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,13 +79,16 @@ const CollectionTabs = ({ showBg }: { showBg: boolean }) => {
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
exit={{ opacity: 0 }}
|
exit={{ opacity: 0 }}
|
||||||
className={cx(
|
className={cx(
|
||||||
'pointer-events-none fixed top-0 left-10 z-10 hidden lg:block',
|
'pointer-events-none fixed top-0 right-0 left-10 z-10 hidden lg:block',
|
||||||
css`
|
css`
|
||||||
height: 230px;
|
height: 230px;
|
||||||
right: ${playerWidth + 32}px;
|
background-repeat: repeat;
|
||||||
background-image: url(${topbarBackground});
|
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
|
style={{
|
||||||
|
right: `${minimizePlayer ? 0 : playerWidth + 32}px`,
|
||||||
|
backgroundImage: `url(${topbarBackground})`,
|
||||||
|
}}
|
||||||
></motion.div>
|
></motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
@ -99,12 +100,10 @@ const CollectionTabs = ({ showBg }: { showBg: boolean }) => {
|
||||||
setSelectedTab(id)
|
setSelectedTab(id)
|
||||||
scrollToBottom(true)
|
scrollToBottom(true)
|
||||||
}}
|
}}
|
||||||
className={cx(
|
className={cx('sticky z-10 -mb-10 px-2.5 lg:px-0')}
|
||||||
'sticky z-10 -mb-10 px-2.5 lg:px-0',
|
style={{
|
||||||
css`
|
top: `${topbarHeight}px`,
|
||||||
top: ${topbarHeight}px;
|
}}
|
||||||
`
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -114,8 +113,7 @@ const Collections = () => {
|
||||||
const { librarySelectedTab: selectedTab } = useSnapshot(uiStates)
|
const { librarySelectedTab: selectedTab } = useSnapshot(uiStates)
|
||||||
|
|
||||||
const observePoint = useRef<HTMLDivElement | null>(null)
|
const observePoint = useRef<HTMLDivElement | null>(null)
|
||||||
const { onScreen: isScrollReachBottom } =
|
const { onScreen: isScrollReachBottom } = useIntersectionObserver(observePoint)
|
||||||
useIntersectionObserver(observePoint)
|
|
||||||
|
|
||||||
const onScroll = throttle(() => {
|
const onScroll = throttle(() => {
|
||||||
if (isScrollReachBottom) return
|
if (isScrollReachBottom) return
|
||||||
|
@ -126,13 +124,11 @@ const Collections = () => {
|
||||||
<div>
|
<div>
|
||||||
<CollectionTabs showBg={isScrollReachBottom} />
|
<CollectionTabs showBg={isScrollReachBottom} />
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx('no-scrollbar overflow-y-auto px-2.5 pt-16 pb-16 lg:px-0')}
|
||||||
'no-scrollbar overflow-y-auto px-2.5 pt-16 pb-16 lg:px-0',
|
|
||||||
css`
|
|
||||||
height: calc(100vh - ${topbarHeight}px);
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
|
style={{
|
||||||
|
height: `calc(100vh - ${topbarHeight}px)`,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{selectedTab === 'albums' && <Albums />}
|
{selectedTab === 'albums' && <Albums />}
|
||||||
{selectedTab === 'playlists' && <Playlists />}
|
{selectedTab === 'playlists' && <Playlists />}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export const appName = 'R3Play'
|
export const appName = 'R3PLAY'
|
||||||
|
|
||||||
// 动画曲线
|
// 动画曲线
|
||||||
export const ease: [number, number, number, number] = [0.4, 0, 0.2, 1]
|
export const ease: [number, number, number, number] = [0.4, 0, 0.2, 1]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/// <reference types="vitest" />
|
/// <reference types="vitest" />
|
||||||
import react from '@vitejs/plugin-react-swc'
|
import react from '@vitejs/plugin-react-swc'
|
||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv'
|
||||||
import path, { join } from 'path'
|
import { join } from 'path'
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
@ -9,7 +9,7 @@ import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import filenamesToType from './vitePluginFilenamesToType'
|
import filenamesToType from './vitePluginFilenamesToType'
|
||||||
import { appName } from './utils/const'
|
import { appName } from './utils/const'
|
||||||
|
|
||||||
dotenv.config({ path: path.resolve(process.cwd(), '../../.env') })
|
dotenv.config({ path: join(__dirname, '../../.env') })
|
||||||
const IS_ELECTRON = process.env.IS_ELECTRON
|
const IS_ELECTRON = process.env.IS_ELECTRON
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,33 +37,33 @@ export default defineConfig({
|
||||||
/**
|
/**
|
||||||
* @see https://vite-plugin-pwa.netlify.app/guide/generate.html
|
* @see https://vite-plugin-pwa.netlify.app/guide/generate.html
|
||||||
*/
|
*/
|
||||||
// VitePWA({
|
VitePWA({
|
||||||
// registerType: 'autoUpdate',
|
registerType: 'autoUpdate',
|
||||||
// manifest: {
|
manifest: {
|
||||||
// name: appName,
|
name: appName,
|
||||||
// short_name: appName,
|
short_name: appName,
|
||||||
// description: 'Description of your app',
|
description: 'Description of your app',
|
||||||
// theme_color: '#000',
|
theme_color: '#000',
|
||||||
// icons: [
|
icons: [
|
||||||
// {
|
{
|
||||||
// src: 'pwa-192x192.png',
|
src: 'pwa-192x192.png',
|
||||||
// sizes: '192x192',
|
sizes: '192x192',
|
||||||
// type: 'image/png',
|
type: 'image/png',
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// src: 'pwa-512x512.png',
|
src: 'pwa-512x512.png',
|
||||||
// sizes: '512x512',
|
sizes: '512x512',
|
||||||
// type: 'image/png',
|
type: 'image/png',
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// src: 'pwa-512x512.png',
|
src: 'pwa-512x512.png',
|
||||||
// sizes: '512x512',
|
sizes: '512x512',
|
||||||
// type: 'image/png',
|
type: 'image/png',
|
||||||
// purpose: 'any maskable',
|
purpose: 'any maskable',
|
||||||
// },
|
},
|
||||||
// ],
|
],
|
||||||
// },
|
},
|
||||||
// }),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see https://github.com/vbenjs/vite-plugin-svg-icons
|
* @see https://github.com/vbenjs/vite-plugin-svg-icons
|
||||||
|
@ -90,27 +90,36 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: Number(process.env['ELECTRON_WEB_SERVER_PORT'] || 42710),
|
port: Number(process.env.ELECTRON_WEB_SERVER_PORT || 42710),
|
||||||
strictPort: IS_ELECTRON ? true : false,
|
strictPort: IS_ELECTRON ? true : false,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/netease/': {
|
'/netease/': {
|
||||||
// target: `http://192.168.50.111:${
|
|
||||||
target: `http://127.0.0.1:${process.env.ELECTRON_DEV_NETEASE_API_PORT || 30001}`,
|
target: `http://127.0.0.1:${process.env.ELECTRON_DEV_NETEASE_API_PORT || 30001}`,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: path => (IS_ELECTRON ? path : path.replace(/^\/netease/, '')),
|
rewrite: path => (IS_ELECTRON ? path : path.replace(/^\/netease/, '')),
|
||||||
},
|
},
|
||||||
[`/${appName.toLowerCase()}/video-cover`]: {
|
'/r3play/': {
|
||||||
target: `http://168.138.40.199:51324`,
|
|
||||||
changeOrigin: true,
|
|
||||||
},
|
|
||||||
[`/${appName.toLowerCase()}/`]: {
|
|
||||||
target: `http://127.0.0.1:${process.env.ELECTRON_DEV_NETEASE_API_PORT || 30001}`,
|
target: `http://127.0.0.1:${process.env.ELECTRON_DEV_NETEASE_API_PORT || 30001}`,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
|
// [`/${appName.toLowerCase()}/apple-music/`]: {
|
||||||
|
// target: `http://168.138.174.244:35530/`,
|
||||||
|
// changeOrigin: true,
|
||||||
|
// rewrite: path => path.replace(/^\/r3play/, ''),
|
||||||
|
// },
|
||||||
|
// [`/${appName.toLowerCase()}/`]: {
|
||||||
|
// target: `http://127.0.0.1:${process.env.ELECTRON_DEV_NETEASE_API_PORT || 30001}`,
|
||||||
|
// changeOrigin: true,
|
||||||
|
// },
|
||||||
|
// '/': {
|
||||||
|
// target: `http://127.0.0.1:${process.env.ELECTRON_DEV_NETEASE_API_PORT || 30001}`,
|
||||||
|
// changeOrigin: true,
|
||||||
|
// // rewrite: path => (IS_ELECTRON ? path : path.replace(/^\/netease/, '')),
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
port: Number(process.env['ELECTRON_WEB_SERVER_PORT'] || 42710),
|
port: Number(process.env.ELECTRON_WEB_SERVER_PORT || 42710),
|
||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
environment: 'jsdom',
|
environment: 'jsdom',
|
||||||
|
|
545
pnpm-lock.yaml
545
pnpm-lock.yaml
|
@ -22,13 +22,15 @@ importers:
|
||||||
|
|
||||||
packages/desktop:
|
packages/desktop:
|
||||||
specifiers:
|
specifiers:
|
||||||
|
'@fastify/cookie': ^8.3.0
|
||||||
|
'@fastify/http-proxy': ^8.4.0
|
||||||
'@sentry/electron': ^3.0.7
|
'@sentry/electron': ^3.0.7
|
||||||
'@types/better-sqlite3': ^7.6.3
|
'@types/better-sqlite3': ^7.6.3
|
||||||
'@types/cookie-parser': ^1.4.3
|
'@types/cookie-parser': ^1.4.3
|
||||||
'@types/express': ^4.17.15
|
'@types/express': ^4.17.15
|
||||||
'@types/express-fileupload': ^1.2.3
|
'@types/express-fileupload': ^1.2.3
|
||||||
'@vitest/ui': ^0.20.3
|
'@vitest/ui': ^0.20.3
|
||||||
NeteaseCloudMusicApi: ^4.8.4
|
NeteaseCloudMusicApi: ^4.8.7
|
||||||
axios: ^1.2.1
|
axios: ^1.2.1
|
||||||
better-sqlite3: 8.0.1
|
better-sqlite3: 8.0.1
|
||||||
change-case: ^4.1.2
|
change-case: ^4.1.2
|
||||||
|
@ -62,8 +64,10 @@ importers:
|
||||||
wait-on: ^7.0.1
|
wait-on: ^7.0.1
|
||||||
zx: ^7.1.1
|
zx: ^7.1.1
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@fastify/cookie': 8.3.0
|
||||||
|
'@fastify/http-proxy': 8.4.0
|
||||||
'@sentry/electron': 3.0.7
|
'@sentry/electron': 3.0.7
|
||||||
NeteaseCloudMusicApi: 4.8.4
|
NeteaseCloudMusicApi: 4.8.7
|
||||||
better-sqlite3: 8.0.1
|
better-sqlite3: 8.0.1
|
||||||
change-case: 4.1.2
|
change-case: 4.1.2
|
||||||
compare-versions: 4.1.3
|
compare-versions: 4.1.3
|
||||||
|
@ -107,32 +111,36 @@ importers:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@fastify/autoload': ^5.0.0
|
'@fastify/autoload': ^5.0.0
|
||||||
'@fastify/sensible': ^4.1.0
|
'@fastify/sensible': ^4.1.0
|
||||||
|
'@fastify/static': ^6.6.1
|
||||||
|
'@prisma/client': ^4.8.1
|
||||||
'@types/node': ^18.0.0
|
'@types/node': ^18.0.0
|
||||||
'@types/tap': ^15.0.5
|
NeteaseCloudMusicApi: ^4.8.7
|
||||||
axios: ^0.27.2
|
axios: ^0.27.2
|
||||||
concurrently: ^7.0.0
|
concurrently: ^7.0.0
|
||||||
fastify: ^4.0.0
|
fastify: ^4.5.3
|
||||||
fastify-cli: ^4.4.0
|
fastify-cli: ^4.4.0
|
||||||
fastify-plugin: ^3.0.0
|
fastify-plugin: ^3.0.0
|
||||||
fastify-tsconfig: ^1.0.1
|
fastify-tsconfig: ^1.0.1
|
||||||
tap: ^16.1.0
|
prisma: ^4.8.1
|
||||||
ts-node: ^10.4.0
|
ts-node: ^10.9.1
|
||||||
typescript: ^4.5.4
|
typescript: ^4.9.4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@fastify/autoload': 5.3.1
|
'@fastify/autoload': 5.3.1
|
||||||
'@fastify/sensible': 4.1.0
|
'@fastify/sensible': 4.1.0
|
||||||
|
'@fastify/static': 6.6.1
|
||||||
|
'@prisma/client': 4.8.1_prisma@4.8.1
|
||||||
|
NeteaseCloudMusicApi: 4.8.7
|
||||||
axios: 0.27.2
|
axios: 0.27.2
|
||||||
fastify: 4.5.3
|
fastify: 4.5.3
|
||||||
fastify-cli: 4.4.0
|
fastify-cli: 4.4.0
|
||||||
fastify-plugin: 3.0.1
|
fastify-plugin: 3.0.1
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/node': 18.6.4
|
'@types/node': 18.6.4
|
||||||
'@types/tap': 15.0.7
|
|
||||||
concurrently: 7.3.0
|
concurrently: 7.3.0
|
||||||
fastify-tsconfig: 1.0.1
|
fastify-tsconfig: 1.0.1
|
||||||
tap: 16.3.0_6oasmw356qmm23djlsjgkwvrtm
|
prisma: 4.8.1
|
||||||
ts-node: 10.9.1_hn66opzbaneygq52jmwjxha6su
|
ts-node: 10.9.1_pueorpcrh7oksxdwl2xjxpw4x4
|
||||||
typescript: 4.7.4
|
typescript: 4.9.4
|
||||||
|
|
||||||
packages/web:
|
packages/web:
|
||||||
specifiers:
|
specifiers:
|
||||||
|
@ -158,7 +166,7 @@ importers:
|
||||||
'@types/qrcode': ^1.4.2
|
'@types/qrcode': ^1.4.2
|
||||||
'@types/react': ^18.0.15
|
'@types/react': ^18.0.15
|
||||||
'@types/react-dom': ^18.0.6
|
'@types/react-dom': ^18.0.6
|
||||||
'@vitejs/plugin-react-swc': ^3.0.0
|
'@vitejs/plugin-react-swc': ^3.0.1
|
||||||
'@vitest/ui': ^0.26.3
|
'@vitest/ui': ^0.26.3
|
||||||
ahooks: ^3.7.4
|
ahooks: ^3.7.4
|
||||||
autoprefixer: ^10.4.13
|
autoprefixer: ^10.4.13
|
||||||
|
@ -3153,6 +3161,11 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@fastify/accept-negotiator/1.1.0:
|
||||||
|
resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@fastify/ajv-compiler/3.2.0:
|
/@fastify/ajv-compiler/3.2.0:
|
||||||
resolution: {integrity: sha512-JrqgKmZoh1AJojDZk699DupQ9+tz5gSy7/w+5DrkXy5whM5IcqdV3SjG5qnOqgVJT1nPtUMDY0xYus2j6vwJiw==}
|
resolution: {integrity: sha512-JrqgKmZoh1AJojDZk699DupQ9+tz5gSy7/w+5DrkXy5whM5IcqdV3SjG5qnOqgVJT1nPtUMDY0xYus2j6vwJiw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -3167,6 +3180,13 @@ packages:
|
||||||
pkg-up: 3.1.0
|
pkg-up: 3.1.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@fastify/cookie/8.3.0:
|
||||||
|
resolution: {integrity: sha512-P9hY9GO11L20TnZ33XN3i0bt+3x0zaT7S0ohAzWO950E9PB2xnNhLYzPFJIGFi5AVN0yr5+/iZhWxeYvR6KCzg==}
|
||||||
|
dependencies:
|
||||||
|
cookie: 0.5.0
|
||||||
|
fastify-plugin: 4.5.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@fastify/deepmerge/1.1.0:
|
/@fastify/deepmerge/1.1.0:
|
||||||
resolution: {integrity: sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g==}
|
resolution: {integrity: sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -3181,6 +3201,49 @@ packages:
|
||||||
fast-json-stringify: 5.2.0
|
fast-json-stringify: 5.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@fastify/http-proxy/8.4.0:
|
||||||
|
resolution: {integrity: sha512-H8nwsmawFtKKRE6uhh1BtF1gQi/l147SmLsDGxB0HdYTHzjXz6uSQO3lEVmY7unKMzbArRjdoJQkEGpScszdSw==}
|
||||||
|
dependencies:
|
||||||
|
'@fastify/reply-from': 8.3.1
|
||||||
|
ws: 8.11.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bufferutil
|
||||||
|
- utf-8-validate
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@fastify/reply-from/8.3.1:
|
||||||
|
resolution: {integrity: sha512-fRByAvTMXuBuYIimcinukOB3YdmqtYPeoybXIBNY0aPVgetHkmCVffBo/M4pEOib9Pes8wuoYL4VawI65aHl4w==}
|
||||||
|
dependencies:
|
||||||
|
'@fastify/error': 3.0.0
|
||||||
|
end-of-stream: 1.4.4
|
||||||
|
fast-querystring: 1.1.0
|
||||||
|
fastify-plugin: 4.5.0
|
||||||
|
pump: 3.0.0
|
||||||
|
tiny-lru: 10.0.1
|
||||||
|
undici: 5.15.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@fastify/send/1.0.0:
|
||||||
|
resolution: {integrity: sha512-jnj8ONIXiOLv4kPn5O7T4oSD5+ymhOg4dKHW3rnYkB/1PJ1942UH1/trvMUIr+fn1dJ20oatpWycZDkPiLcWfg==}
|
||||||
|
engines: {node: '>= 0.8.0'}
|
||||||
|
dependencies:
|
||||||
|
debug: 4.3.4
|
||||||
|
depd: 2.0.0
|
||||||
|
destroy: 1.2.0
|
||||||
|
encodeurl: 1.0.2
|
||||||
|
escape-html: 1.0.3
|
||||||
|
etag: 1.8.1
|
||||||
|
fresh: 0.5.2
|
||||||
|
http-errors: 2.0.0
|
||||||
|
mime: 1.6.0
|
||||||
|
ms: 2.1.3
|
||||||
|
on-finished: 2.4.1
|
||||||
|
range-parser: 1.2.1
|
||||||
|
statuses: 2.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@fastify/sensible/4.1.0:
|
/@fastify/sensible/4.1.0:
|
||||||
resolution: {integrity: sha512-8TBlmCK055y6WO9jZlndmceB9x8NyNcLEbnJtdu44zelfmY1ebBMSB7MOqyMteyDvpSMq3CVaPknBu35d9FRlA==}
|
resolution: {integrity: sha512-8TBlmCK055y6WO9jZlndmceB9x8NyNcLEbnJtdu44zelfmY1ebBMSB7MOqyMteyDvpSMq3CVaPknBu35d9FRlA==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
|
@ -3194,6 +3257,20 @@ packages:
|
||||||
vary: 1.1.2
|
vary: 1.1.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@fastify/static/6.6.1:
|
||||||
|
resolution: {integrity: sha512-sylhlmhclqwkyZy/SD5wzd4yjmMuqW8cRmfnuPXPhftZuEwJ8G2apm0kECQRnHJnk+W3Ksx2fpIHHcthzxNRTA==}
|
||||||
|
dependencies:
|
||||||
|
'@fastify/accept-negotiator': 1.1.0
|
||||||
|
'@fastify/send': 1.0.0
|
||||||
|
content-disposition: 0.5.4
|
||||||
|
fastify-plugin: 4.5.0
|
||||||
|
glob: 8.0.3
|
||||||
|
p-limit: 3.1.0
|
||||||
|
readable-stream: 4.1.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@gar/promisify/1.1.3:
|
/@gar/promisify/1.1.3:
|
||||||
resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
|
resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -3582,6 +3659,28 @@ packages:
|
||||||
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
|
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@prisma/client/4.8.1_prisma@4.8.1:
|
||||||
|
resolution: {integrity: sha512-d4xhZhETmeXK/yZ7K0KcVOzEfI5YKGGEr4F5SBV04/MU4ncN/HcE28sy3e4Yt8UFW0ZuImKFQJE+9rWt9WbGSQ==}
|
||||||
|
engines: {node: '>=14.17'}
|
||||||
|
requiresBuild: true
|
||||||
|
peerDependencies:
|
||||||
|
prisma: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
prisma:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@prisma/engines-version': 4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe
|
||||||
|
prisma: 4.8.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@prisma/engines-version/4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe:
|
||||||
|
resolution: {integrity: sha512-MHSOSexomRMom8QN4t7bu87wPPD+pa+hW9+71JnVcF3DqyyO/ycCLhRL1we3EojRpZxKvuyGho2REQsMCvxcJw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@prisma/engines/4.8.1:
|
||||||
|
resolution: {integrity: sha512-93tctjNXcIS+i/e552IO6tqw17sX8liivv8WX9lDMCpEEe3ci+nT9F+1oHtAafqruXLepKF80i/D20Mm+ESlOw==}
|
||||||
|
requiresBuild: true
|
||||||
|
|
||||||
/@remix-run/router/1.2.1:
|
/@remix-run/router/1.2.1:
|
||||||
resolution: {integrity: sha512-XiY0IsyHR+DXYS5vBxpoBe/8veTeoRpMHP+vDosLZxL5bnpetzI0igkxkLZS235ldLzyfkxF+2divEwWHP3vMQ==}
|
resolution: {integrity: sha512-XiY0IsyHR+DXYS5vBxpoBe/8veTeoRpMHP+vDosLZxL5bnpetzI0igkxkLZS235ldLzyfkxF+2divEwWHP3vMQ==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
@ -5810,12 +5909,6 @@ packages:
|
||||||
'@types/node': 18.6.4
|
'@types/node': 18.6.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/tap/15.0.7:
|
|
||||||
resolution: {integrity: sha512-TTMajw4gxQfFgYbhXhy/Tb2OiNcwS+4oP/9yp1/GdU0pFJo3wtnkYhRgmQy39ksh+rnoa0VrPHJ4Tuv2cLNQ5A==}
|
|
||||||
dependencies:
|
|
||||||
'@types/node': 18.6.4
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/tapable/1.0.8:
|
/@types/tapable/1.0.8:
|
||||||
resolution: {integrity: sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==}
|
resolution: {integrity: sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -6194,12 +6287,12 @@ packages:
|
||||||
resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
|
resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/NeteaseCloudMusicApi/4.8.4:
|
/NeteaseCloudMusicApi/4.8.7:
|
||||||
resolution: {integrity: sha512-PUrbSB/gcIFmsitzxJ5UO1RAjR8FQGQiI6zLBqf0D1+zI8baSEQhw6IxGNoyaeraMSzg7d2k5W05vGcach7qTw==}
|
resolution: {integrity: sha512-f+Z/lar+IOcrzGeDKHNQaTnlNE7nLRiD9Nb/KoLmKC32aUnnCerFTFV2/ZWlEC4YokP/4mXOZXvo8wXA8PQhVA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
axios: 0.24.0
|
axios: 1.2.2
|
||||||
express: 4.18.2
|
express: 4.18.2
|
||||||
express-fileupload: 1.4.0
|
express-fileupload: 1.4.0
|
||||||
md5: 2.3.0
|
md5: 2.3.0
|
||||||
|
@ -6561,13 +6654,6 @@ packages:
|
||||||
resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==}
|
resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/append-transform/2.0.0:
|
|
||||||
resolution: {integrity: sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
default-require-extensions: 3.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/aproba/1.2.0:
|
/aproba/1.2.0:
|
||||||
resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==}
|
resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -6578,6 +6664,7 @@ packages:
|
||||||
|
|
||||||
/archy/1.0.0:
|
/archy/1.0.0:
|
||||||
resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==}
|
resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/are-we-there-yet/2.0.0:
|
/are-we-there-yet/2.0.0:
|
||||||
resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
|
resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
|
||||||
|
@ -6802,11 +6889,6 @@ packages:
|
||||||
engines: {node: '>=0.12.0'}
|
engines: {node: '>=0.12.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/async-hook-domain/2.0.4:
|
|
||||||
resolution: {integrity: sha512-14LjCmlK1PK8eDtTezR6WX8TMaYNIzBIsd2D1sGoGjgx0BuNMMoSdk7i/drlbtamy0AWv9yv2tkB+ASdmeqFIw==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/async/3.2.4:
|
/async/3.2.4:
|
||||||
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
|
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -6874,14 +6956,6 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/axios/0.24.0:
|
|
||||||
resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==}
|
|
||||||
dependencies:
|
|
||||||
follow-redirects: 1.15.1
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- debug
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/axios/0.27.2:
|
/axios/0.27.2:
|
||||||
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
|
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -7139,11 +7213,6 @@ packages:
|
||||||
chainsaw: 0.1.0
|
chainsaw: 0.1.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/bind-obj-methods/3.0.0:
|
|
||||||
resolution: {integrity: sha512-nLEaaz3/sEzNSyPWRsN9HNsqwk1AUyECtGj+XwGdIi3xABnEqecvXtIJ0wehQXuuER5uZ/5fTs2usONgYjG+iw==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/bindings/1.5.0:
|
/bindings/1.5.0:
|
||||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -7591,16 +7660,6 @@ packages:
|
||||||
responselike: 2.0.1
|
responselike: 2.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/caching-transform/4.0.0:
|
|
||||||
resolution: {integrity: sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
hasha: 5.2.2
|
|
||||||
make-dir: 3.1.0
|
|
||||||
package-hash: 4.0.0
|
|
||||||
write-file-atomic: 3.0.3
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/call-bind/1.0.2:
|
/call-bind/1.0.2:
|
||||||
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
|
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -7925,6 +7984,7 @@ packages:
|
||||||
string-width: 4.2.3
|
string-width: 4.2.3
|
||||||
strip-ansi: 6.0.1
|
strip-ansi: 6.0.1
|
||||||
wrap-ansi: 6.2.0
|
wrap-ansi: 6.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/cliui/7.0.4:
|
/cliui/7.0.4:
|
||||||
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
||||||
|
@ -8705,13 +8765,6 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/default-require-extensions/3.0.0:
|
|
||||||
resolution: {integrity: sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
strip-bom: 4.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/defaults/1.0.3:
|
/defaults/1.0.3:
|
||||||
resolution: {integrity: sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==}
|
resolution: {integrity: sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -9390,6 +9443,7 @@ packages:
|
||||||
/es6-error/4.1.1:
|
/es6-error/4.1.1:
|
||||||
resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
|
resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/es6-shim/0.35.6:
|
/es6-shim/0.35.6:
|
||||||
resolution: {integrity: sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA==}
|
resolution: {integrity: sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA==}
|
||||||
|
@ -9854,11 +9908,6 @@ packages:
|
||||||
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
||||||
engines: {node: '>=0.8.0'}
|
engines: {node: '>=0.8.0'}
|
||||||
|
|
||||||
/escape-string-regexp/2.0.0:
|
|
||||||
resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/escape-string-regexp/4.0.0:
|
/escape-string-regexp/4.0.0:
|
||||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -10128,10 +10177,6 @@ packages:
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/events-to-array/1.1.2:
|
|
||||||
resolution: {integrity: sha512-inRWzRY7nG+aXZxBzEqYKB3HPgwflZRopAjDCHv0whhRx+MTUr1ei0ICZUypdyE0HRm4L2d5VEcIqLD6yl+BFA==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/events/3.3.0:
|
/events/3.3.0:
|
||||||
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
||||||
engines: {node: '>=0.8.x'}
|
engines: {node: '>=0.8.x'}
|
||||||
|
@ -10299,6 +10344,10 @@ packages:
|
||||||
resolution: {integrity: sha512-LDzYKNTHhD+XOp8wGMuCkY4eTxFZOOycmpwLBiuF3r3OjOmZnURRD8t2dUAbmKuXGbo/MGggwbSjcBdp8QT0+g==}
|
resolution: {integrity: sha512-LDzYKNTHhD+XOp8wGMuCkY4eTxFZOOycmpwLBiuF3r3OjOmZnURRD8t2dUAbmKuXGbo/MGggwbSjcBdp8QT0+g==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/fast-decode-uri-component/1.0.1:
|
||||||
|
resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fast-deep-equal/3.1.3:
|
/fast-deep-equal/3.1.3:
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
|
|
||||||
|
@ -10368,6 +10417,12 @@ packages:
|
||||||
/fast-levenshtein/2.0.6:
|
/fast-levenshtein/2.0.6:
|
||||||
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
||||||
|
|
||||||
|
/fast-querystring/1.1.0:
|
||||||
|
resolution: {integrity: sha512-LWkjBCZlxjnSanuPpZ6mHswjy8hQv3VcPJsQB3ltUF2zjvrycr0leP3TSTEEfvQ1WEMSRl5YNsGqaft9bjLqEw==}
|
||||||
|
dependencies:
|
||||||
|
fast-decode-uri-component: 1.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fast-redact/3.1.2:
|
/fast-redact/3.1.2:
|
||||||
resolution: {integrity: sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==}
|
resolution: {integrity: sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -10417,6 +10472,10 @@ packages:
|
||||||
resolution: {integrity: sha512-qKcDXmuZadJqdTm6vlCqioEbyewF60b/0LOFCcYN1B6BIZGlYJumWWOYs70SFYLDAH4YqdE1cxH/RKMG7rFxgA==}
|
resolution: {integrity: sha512-qKcDXmuZadJqdTm6vlCqioEbyewF60b/0LOFCcYN1B6BIZGlYJumWWOYs70SFYLDAH4YqdE1cxH/RKMG7rFxgA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/fastify-plugin/4.5.0:
|
||||||
|
resolution: {integrity: sha512-79ak0JxddO0utAXAQ5ccKhvs6vX2MGyHHMMsmZkBANrq3hXc1CHzvNPHOcvTsVMEPl5I+NT+RO4YKMGehOfSIg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fastify-tsconfig/1.0.1:
|
/fastify-tsconfig/1.0.1:
|
||||||
resolution: {integrity: sha512-BXkTG3JYcjJb3xX5R5FcE9ciscV/h7YtmnkiSaNAONd1g6ooMSN/4GWfhA8hnS6SRZFYBBxsn8719Mj9lbCOtA==}
|
resolution: {integrity: sha512-BXkTG3JYcjJb3xX5R5FcE9ciscV/h7YtmnkiSaNAONd1g6ooMSN/4GWfhA8hnS6SRZFYBBxsn8719Mj9lbCOtA==}
|
||||||
engines: {node: '>=10.4.0'}
|
engines: {node: '>=10.4.0'}
|
||||||
|
@ -10622,10 +10681,6 @@ packages:
|
||||||
path-exists: 4.0.0
|
path-exists: 4.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/findit/2.0.0:
|
|
||||||
resolution: {integrity: sha512-ENZS237/Hr8bjczn5eKuBohLgaD0JyUd0arxretR1f9RO46vZHA1b2y0VorgGV3WaOT3c+78P8h7v4JGJ1i/rg==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/flat-cache/3.0.4:
|
/flat-cache/3.0.4:
|
||||||
resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
|
resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
|
||||||
engines: {node: ^10.12.0 || >=12.0.0}
|
engines: {node: ^10.12.0 || >=12.0.0}
|
||||||
|
@ -10801,18 +10856,10 @@ packages:
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.7
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/fromentries/1.3.2:
|
|
||||||
resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/fs-constants/1.0.0:
|
/fs-constants/1.0.0:
|
||||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/fs-exists-cached/1.0.0:
|
|
||||||
resolution: {integrity: sha512-kSxoARUDn4F2RPXX48UXnaFKwVU7Ivd/6qpzZL29MCDmr9sTvybv4gFCp+qaI4fM9m0z9fgz/yJvi56GAz+BZg==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/fs-extra/10.1.0:
|
/fs-extra/10.1.0:
|
||||||
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
|
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
@ -10902,10 +10949,6 @@ packages:
|
||||||
/function-bind/1.1.1:
|
/function-bind/1.1.1:
|
||||||
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||||
|
|
||||||
/function-loop/2.0.1:
|
|
||||||
resolution: {integrity: sha512-ktIR+O6i/4h+j/ZhZJNdzeI4i9lEPeEK6UPR2EVyTVBqOwcU3Za9xYKLH64ZR9HmcROyRrOkizNyjjtWJzDDkQ==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/function.prototype.name/1.1.5:
|
/function.prototype.name/1.1.5:
|
||||||
resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==}
|
resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
@ -11365,14 +11408,6 @@ packages:
|
||||||
minimalistic-assert: 1.0.1
|
minimalistic-assert: 1.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/hasha/5.2.2:
|
|
||||||
resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
is-stream: 2.0.1
|
|
||||||
type-fest: 0.8.1
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/hast-to-hyperscript/9.0.1:
|
/hast-to-hyperscript/9.0.1:
|
||||||
resolution: {integrity: sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==}
|
resolution: {integrity: sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -12285,25 +12320,6 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/istanbul-lib-hook/3.0.0:
|
|
||||||
resolution: {integrity: sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
append-transform: 2.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/istanbul-lib-instrument/4.0.3:
|
|
||||||
resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/core': 7.20.12
|
|
||||||
'@istanbuljs/schema': 0.1.3
|
|
||||||
istanbul-lib-coverage: 3.2.0
|
|
||||||
semver: 6.3.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/istanbul-lib-instrument/5.2.0:
|
/istanbul-lib-instrument/5.2.0:
|
||||||
resolution: {integrity: sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==}
|
resolution: {integrity: sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -12317,18 +12333,6 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/istanbul-lib-processinfo/2.0.3:
|
|
||||||
resolution: {integrity: sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
archy: 1.0.0
|
|
||||||
cross-spawn: 7.0.3
|
|
||||||
istanbul-lib-coverage: 3.2.0
|
|
||||||
p-map: 3.0.0
|
|
||||||
rimraf: 3.0.2
|
|
||||||
uuid: 8.3.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/istanbul-lib-report/3.0.0:
|
/istanbul-lib-report/3.0.0:
|
||||||
resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==}
|
resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -12338,17 +12342,6 @@ packages:
|
||||||
supports-color: 7.2.0
|
supports-color: 7.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/istanbul-lib-source-maps/4.0.1:
|
|
||||||
resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dependencies:
|
|
||||||
debug: 4.3.4
|
|
||||||
istanbul-lib-coverage: 3.2.0
|
|
||||||
source-map: 0.6.1
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/istanbul-reports/3.1.5:
|
/istanbul-reports/3.1.5:
|
||||||
resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==}
|
resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -12368,13 +12361,6 @@ packages:
|
||||||
iterate-iterator: 1.0.2
|
iterate-iterator: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/jackspeak/1.4.1:
|
|
||||||
resolution: {integrity: sha512-npN8f+M4+IQ8xD3CcWi3U62VQwKlT3Tj4GxbdT/fYTmeogD9eBF9OFdpoFG/VPNoshRjPUijdkp/p2XrzUHaVg==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
cliui: 7.0.4
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/jake/10.8.5:
|
/jake/10.8.5:
|
||||||
resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
|
resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -12731,25 +12717,6 @@ packages:
|
||||||
type-check: 0.4.0
|
type-check: 0.4.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/libtap/1.4.0:
|
|
||||||
resolution: {integrity: sha512-STLFynswQ2A6W14JkabgGetBNk6INL1REgJ9UeNKw5llXroC2cGLgKTqavv0sl8OLVztLLipVKMcQ7yeUcqpmg==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dependencies:
|
|
||||||
async-hook-domain: 2.0.4
|
|
||||||
bind-obj-methods: 3.0.0
|
|
||||||
diff: 4.0.2
|
|
||||||
function-loop: 2.0.1
|
|
||||||
minipass: 3.3.4
|
|
||||||
own-or: 1.0.0
|
|
||||||
own-or-env: 1.0.2
|
|
||||||
signal-exit: 3.0.7
|
|
||||||
stack-utils: 2.0.5
|
|
||||||
tap-parser: 11.0.1
|
|
||||||
tap-yaml: 1.0.0
|
|
||||||
tcompare: 5.0.7
|
|
||||||
trivial-deferred: 1.0.1
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/lie/3.3.0:
|
/lie/3.3.0:
|
||||||
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
|
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -12849,10 +12816,6 @@ packages:
|
||||||
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/lodash.flattendeep/4.4.0:
|
|
||||||
resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/lodash.merge/4.6.2:
|
/lodash.merge/4.6.2:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -13740,13 +13703,6 @@ packages:
|
||||||
vm-browserify: 1.1.2
|
vm-browserify: 1.1.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/node-preload/0.2.1:
|
|
||||||
resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
process-on-spawn: 1.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/node-releases/2.0.6:
|
/node-releases/2.0.6:
|
||||||
resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
|
resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -13846,42 +13802,6 @@ packages:
|
||||||
resolution: {integrity: sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==}
|
resolution: {integrity: sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/nyc/15.1.0:
|
|
||||||
resolution: {integrity: sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==}
|
|
||||||
engines: {node: '>=8.9'}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
'@istanbuljs/load-nyc-config': 1.1.0
|
|
||||||
'@istanbuljs/schema': 0.1.3
|
|
||||||
caching-transform: 4.0.0
|
|
||||||
convert-source-map: 1.8.0
|
|
||||||
decamelize: 1.2.0
|
|
||||||
find-cache-dir: 3.3.2
|
|
||||||
find-up: 4.1.0
|
|
||||||
foreground-child: 2.0.0
|
|
||||||
get-package-type: 0.1.0
|
|
||||||
glob: 7.2.3
|
|
||||||
istanbul-lib-coverage: 3.2.0
|
|
||||||
istanbul-lib-hook: 3.0.0
|
|
||||||
istanbul-lib-instrument: 4.0.3
|
|
||||||
istanbul-lib-processinfo: 2.0.3
|
|
||||||
istanbul-lib-report: 3.0.0
|
|
||||||
istanbul-lib-source-maps: 4.0.1
|
|
||||||
istanbul-reports: 3.1.5
|
|
||||||
make-dir: 3.1.0
|
|
||||||
node-preload: 0.2.1
|
|
||||||
p-map: 3.0.0
|
|
||||||
process-on-spawn: 1.0.0
|
|
||||||
resolve-from: 5.0.0
|
|
||||||
rimraf: 3.0.2
|
|
||||||
signal-exit: 3.0.7
|
|
||||||
spawn-wrap: 2.0.0
|
|
||||||
test-exclude: 6.0.0
|
|
||||||
yargs: 15.4.1
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/object-assign/4.1.1:
|
/object-assign/4.1.1:
|
||||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
@ -14045,11 +13965,6 @@ packages:
|
||||||
is-wsl: 2.2.0
|
is-wsl: 2.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/opener/1.5.2:
|
|
||||||
resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==}
|
|
||||||
hasBin: true
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/optionator/0.8.3:
|
/optionator/0.8.3:
|
||||||
resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==}
|
resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
@ -14113,16 +14028,6 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/own-or-env/1.0.2:
|
|
||||||
resolution: {integrity: sha512-NQ7v0fliWtK7Lkb+WdFqe6ky9XAzYmlkXthQrBbzlYbmFKoAYbDDcwmOm6q8kOuwSRXW8bdL5ORksploUJmWgw==}
|
|
||||||
dependencies:
|
|
||||||
own-or: 1.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/own-or/1.0.0:
|
|
||||||
resolution: {integrity: sha512-NfZr5+Tdf6MB8UI9GLvKRs4cXY8/yB0w3xtt84xFdWy8hkGjn+JFc60VhzS/hFRfbyxFcGYMTjnF4Me+RbbqrA==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/p-all/2.1.0:
|
/p-all/2.1.0:
|
||||||
resolution: {integrity: sha512-HbZxz5FONzz/z2gJfk6bFca0BCiSRF8jU3yCsWOen/vR6lZjfPOu/e7L3uFzTW1i0H8TlC3vqQstEJPQL4/uLA==}
|
resolution: {integrity: sha512-HbZxz5FONzz/z2gJfk6bFca0BCiSRF8jU3yCsWOen/vR6lZjfPOu/e7L3uFzTW1i0H8TlC3vqQstEJPQL4/uLA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -14165,7 +14070,6 @@ packages:
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dependencies:
|
dependencies:
|
||||||
yocto-queue: 0.1.0
|
yocto-queue: 0.1.0
|
||||||
dev: true
|
|
||||||
|
|
||||||
/p-locate/3.0.0:
|
/p-locate/3.0.0:
|
||||||
resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
|
resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
|
||||||
|
@ -14242,16 +14146,6 @@ packages:
|
||||||
netmask: 2.0.2
|
netmask: 2.0.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/package-hash/4.0.0:
|
|
||||||
resolution: {integrity: sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
graceful-fs: 4.2.10
|
|
||||||
hasha: 5.2.2
|
|
||||||
lodash.flattendeep: 4.4.0
|
|
||||||
release-zalgo: 1.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/pako/1.0.11:
|
/pako/1.0.11:
|
||||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -14909,16 +14803,17 @@ packages:
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/prisma/4.8.1:
|
||||||
|
resolution: {integrity: sha512-ZMLnSjwulIeYfaU1O6/LF6PEJzxN5par5weykxMykS9Z6ara/j76JH3Yo2AH3bgJbPN4Z6NeCK9s5fDkzf33cg==}
|
||||||
|
engines: {node: '>=14.17'}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
'@prisma/engines': 4.8.1
|
||||||
|
|
||||||
/process-nextick-args/2.0.1:
|
/process-nextick-args/2.0.1:
|
||||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||||
|
|
||||||
/process-on-spawn/1.0.0:
|
|
||||||
resolution: {integrity: sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
fromentries: 1.3.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/process-warning/2.0.0:
|
/process-warning/2.0.0:
|
||||||
resolution: {integrity: sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==}
|
resolution: {integrity: sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -15637,13 +15532,6 @@ packages:
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/release-zalgo/1.0.0:
|
|
||||||
resolution: {integrity: sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==}
|
|
||||||
engines: {node: '>=4'}
|
|
||||||
dependencies:
|
|
||||||
es6-error: 4.1.1
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/remark-external-links/8.0.0:
|
/remark-external-links/8.0.0:
|
||||||
resolution: {integrity: sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA==}
|
resolution: {integrity: sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -15754,6 +15642,7 @@ packages:
|
||||||
|
|
||||||
/require-main-filename/2.0.0:
|
/require-main-filename/2.0.0:
|
||||||
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
|
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/requires-port/1.0.0:
|
/requires-port/1.0.0:
|
||||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||||
|
@ -16427,6 +16316,7 @@ packages:
|
||||||
/source-map/0.6.1:
|
/source-map/0.6.1:
|
||||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
requiresBuild: true
|
||||||
|
|
||||||
/source-map/0.7.4:
|
/source-map/0.7.4:
|
||||||
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
|
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
|
||||||
|
@ -16452,18 +16342,6 @@ packages:
|
||||||
resolution: {integrity: sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==}
|
resolution: {integrity: sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/spawn-wrap/2.0.0:
|
|
||||||
resolution: {integrity: sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
foreground-child: 2.0.0
|
|
||||||
is-windows: 1.0.2
|
|
||||||
make-dir: 3.1.0
|
|
||||||
rimraf: 3.0.2
|
|
||||||
signal-exit: 3.0.7
|
|
||||||
which: 2.0.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/spdx-correct/3.1.1:
|
/spdx-correct/3.1.1:
|
||||||
resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
|
resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -16550,13 +16428,6 @@ packages:
|
||||||
stackframe: 1.3.4
|
stackframe: 1.3.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/stack-utils/2.0.5:
|
|
||||||
resolution: {integrity: sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dependencies:
|
|
||||||
escape-string-regexp: 2.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/stackframe/1.3.4:
|
/stackframe/1.3.4:
|
||||||
resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==}
|
resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==}
|
||||||
|
|
||||||
|
@ -16769,11 +16640,6 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/strip-bom/4.0.0:
|
|
||||||
resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/strip-comments/2.0.1:
|
/strip-comments/2.0.1:
|
||||||
resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==}
|
resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -17025,91 +16891,6 @@ packages:
|
||||||
- ts-node
|
- ts-node
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/tap-mocha-reporter/5.0.3:
|
|
||||||
resolution: {integrity: sha512-6zlGkaV4J+XMRFkN0X+yuw6xHbE9jyCZ3WUKfw4KxMyRGOpYSRuuQTRJyWX88WWuLdVTuFbxzwXhXuS2XE6o0g==}
|
|
||||||
engines: {node: '>= 8'}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
color-support: 1.1.3
|
|
||||||
debug: 4.3.4
|
|
||||||
diff: 4.0.2
|
|
||||||
escape-string-regexp: 2.0.0
|
|
||||||
glob: 7.2.3
|
|
||||||
tap-parser: 11.0.1
|
|
||||||
tap-yaml: 1.0.0
|
|
||||||
unicode-length: 2.1.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/tap-parser/11.0.1:
|
|
||||||
resolution: {integrity: sha512-5ow0oyFOnXVSALYdidMX94u0GEjIlgc/BPFYLx0yRh9hb8+cFGNJqJzDJlUqbLOwx8+NBrIbxCWkIQi7555c0w==}
|
|
||||||
engines: {node: '>= 8'}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
events-to-array: 1.1.2
|
|
||||||
minipass: 3.3.4
|
|
||||||
tap-yaml: 1.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/tap-yaml/1.0.0:
|
|
||||||
resolution: {integrity: sha512-Rxbx4EnrWkYk0/ztcm5u3/VznbyFJpyXO12dDBHKWiDVxy7O2Qw6MRrwO5H6Ww0U5YhRY/4C/VzWmFPhBQc4qQ==}
|
|
||||||
dependencies:
|
|
||||||
yaml: 1.10.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/tap/16.3.0_6oasmw356qmm23djlsjgkwvrtm:
|
|
||||||
resolution: {integrity: sha512-J9GffPUAbX6FnWbQ/jj7ktzd9nnDFP1fH44OzidqOmxUfZ1hPLMOvpS99LnDiP0H2mO8GY3kGN5XoY0xIKbNFA==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
hasBin: true
|
|
||||||
peerDependencies:
|
|
||||||
coveralls: ^3.1.1
|
|
||||||
flow-remove-types: '>=2.112.0'
|
|
||||||
ts-node: '>=8.5.2'
|
|
||||||
typescript: '>=3.7.2'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
coveralls:
|
|
||||||
optional: true
|
|
||||||
flow-remove-types:
|
|
||||||
optional: true
|
|
||||||
ts-node:
|
|
||||||
optional: true
|
|
||||||
typescript:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
chokidar: 3.5.3
|
|
||||||
findit: 2.0.0
|
|
||||||
foreground-child: 2.0.0
|
|
||||||
fs-exists-cached: 1.0.0
|
|
||||||
glob: 7.2.3
|
|
||||||
isexe: 2.0.0
|
|
||||||
istanbul-lib-processinfo: 2.0.3
|
|
||||||
jackspeak: 1.4.1
|
|
||||||
libtap: 1.4.0
|
|
||||||
minipass: 3.3.4
|
|
||||||
mkdirp: 1.0.4
|
|
||||||
nyc: 15.1.0
|
|
||||||
opener: 1.5.2
|
|
||||||
rimraf: 3.0.2
|
|
||||||
signal-exit: 3.0.7
|
|
||||||
source-map-support: 0.5.21
|
|
||||||
tap-mocha-reporter: 5.0.3
|
|
||||||
tap-parser: 11.0.1
|
|
||||||
tap-yaml: 1.0.0
|
|
||||||
tcompare: 5.0.7
|
|
||||||
ts-node: 10.9.1_hn66opzbaneygq52jmwjxha6su
|
|
||||||
typescript: 4.7.4
|
|
||||||
which: 2.0.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
|
||||||
bundledDependencies:
|
|
||||||
- ink
|
|
||||||
- treport
|
|
||||||
- '@types/react'
|
|
||||||
- '@isaacs/import-jsx'
|
|
||||||
- react
|
|
||||||
|
|
||||||
/tapable/1.1.3:
|
/tapable/1.1.3:
|
||||||
resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
|
resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -17152,13 +16933,6 @@ packages:
|
||||||
yallist: 4.0.0
|
yallist: 4.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/tcompare/5.0.7:
|
|
||||||
resolution: {integrity: sha512-d9iddt6YYGgyxJw5bjsN7UJUO1kGOtjSlNy/4PoGYAjQS5pAT/hzIoLf1bZCw+uUxRmZJh7Yy1aA7xKVRT9B4w==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dependencies:
|
|
||||||
diff: 4.0.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/telejson/6.0.8:
|
/telejson/6.0.8:
|
||||||
resolution: {integrity: sha512-nerNXi+j8NK1QEfBHtZUN/aLdDcyupA//9kAboYLrtzZlPLpUfqbVGWb9zz91f/mIjRbAYhbgtnJHY8I1b5MBg==}
|
resolution: {integrity: sha512-nerNXi+j8NK1QEfBHtZUN/aLdDcyupA//9kAboYLrtzZlPLpUfqbVGWb9zz91f/mIjRbAYhbgtnJHY8I1b5MBg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -17341,6 +17115,11 @@ packages:
|
||||||
setimmediate: 1.0.5
|
setimmediate: 1.0.5
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/tiny-lru/10.0.1:
|
||||||
|
resolution: {integrity: sha512-Vst+6kEsWvb17Zpz14sRJV/f8bUWKhqm6Dc+v08iShmIJ/WxqWytHzCTd6m88pS33rE2zpX34TRmOpAJPloNCA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/tiny-lru/8.0.2:
|
/tiny-lru/8.0.2:
|
||||||
resolution: {integrity: sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==}
|
resolution: {integrity: sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -17512,10 +17291,6 @@ packages:
|
||||||
resolution: {integrity: sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==}
|
resolution: {integrity: sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/trivial-deferred/1.0.1:
|
|
||||||
resolution: {integrity: sha512-dagAKX7vaesNNAwOc9Np9C2mJ+7YopF4lk+jE2JML9ta4kZ91Y6UruJNH65bLRYoUROD8EY+Pmi44qQWwXR7sw==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/trough/1.0.5:
|
/trough/1.0.5:
|
||||||
resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==}
|
resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -17535,7 +17310,7 @@ packages:
|
||||||
resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==}
|
resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/ts-node/10.9.1_hn66opzbaneygq52jmwjxha6su:
|
/ts-node/10.9.1_pueorpcrh7oksxdwl2xjxpw4x4:
|
||||||
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
|
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -17561,7 +17336,7 @@ packages:
|
||||||
create-require: 1.1.1
|
create-require: 1.1.1
|
||||||
diff: 4.0.2
|
diff: 4.0.2
|
||||||
make-error: 1.3.6
|
make-error: 1.3.6
|
||||||
typescript: 4.7.4
|
typescript: 4.9.4
|
||||||
v8-compile-cache-lib: 3.0.1
|
v8-compile-cache-lib: 3.0.1
|
||||||
yn: 3.1.1
|
yn: 3.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -17783,6 +17558,13 @@ packages:
|
||||||
which-boxed-primitive: 1.0.2
|
which-boxed-primitive: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/undici/5.15.1:
|
||||||
|
resolution: {integrity: sha512-XLk8g0WAngdvFqTI+VKfBtM4YWXgdxkf1WezC771Es0Dd+Pm1KmNx8t93WTC+Hh9tnghmVxkclU1HN+j+CvIUA==}
|
||||||
|
engines: {node: '>=12.18'}
|
||||||
|
dependencies:
|
||||||
|
busboy: 1.6.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/unfetch/4.2.0:
|
/unfetch/4.2.0:
|
||||||
resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==}
|
resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -17799,12 +17581,6 @@ packages:
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/unicode-length/2.1.0:
|
|
||||||
resolution: {integrity: sha512-4bV582zTV9Q02RXBxSUMiuN/KHo5w4aTojuKTNT96DIKps/SIawFp7cS5Mu25VuY1AioGXrmYyzKZUzh8OqoUw==}
|
|
||||||
dependencies:
|
|
||||||
punycode: 2.1.1
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/unicode-match-property-ecmascript/2.0.0:
|
/unicode-match-property-ecmascript/2.0.0:
|
||||||
resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
|
resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
@ -18102,11 +17878,6 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/uuid/8.3.2:
|
|
||||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
|
||||||
hasBin: true
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/v8-compile-cache-lib/3.0.1:
|
/v8-compile-cache-lib/3.0.1:
|
||||||
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -18725,6 +18496,7 @@ packages:
|
||||||
|
|
||||||
/which-module/2.0.0:
|
/which-module/2.0.0:
|
||||||
resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==}
|
resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/which/1.3.1:
|
/which/1.3.1:
|
||||||
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
|
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
|
||||||
|
@ -18926,6 +18698,7 @@ packages:
|
||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
string-width: 4.2.3
|
string-width: 4.2.3
|
||||||
strip-ansi: 6.0.1
|
strip-ansi: 6.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/wrap-ansi/7.0.0:
|
/wrap-ansi/7.0.0:
|
||||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||||
|
@ -18958,7 +18731,6 @@ packages:
|
||||||
optional: true
|
optional: true
|
||||||
utf-8-validate:
|
utf-8-validate:
|
||||||
optional: true
|
optional: true
|
||||||
dev: true
|
|
||||||
|
|
||||||
/ws/8.8.1:
|
/ws/8.8.1:
|
||||||
resolution: {integrity: sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==}
|
resolution: {integrity: sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==}
|
||||||
|
@ -19036,6 +18808,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
camelcase: 5.3.1
|
camelcase: 5.3.1
|
||||||
decamelize: 1.2.0
|
decamelize: 1.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/yargs-parser/20.2.9:
|
/yargs-parser/20.2.9:
|
||||||
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
|
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
|
||||||
|
@ -19060,6 +18833,7 @@ packages:
|
||||||
which-module: 2.0.0
|
which-module: 2.0.0
|
||||||
y18n: 4.0.3
|
y18n: 4.0.3
|
||||||
yargs-parser: 18.1.3
|
yargs-parser: 18.1.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
/yargs/16.2.0:
|
/yargs/16.2.0:
|
||||||
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
|
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
|
||||||
|
@ -19101,7 +18875,6 @@ packages:
|
||||||
/yocto-queue/0.1.0:
|
/yocto-queue/0.1.0:
|
||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/zwitch/1.0.5:
|
/zwitch/1.0.5:
|
||||||
resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==}
|
resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{
|
{
|
||||||
|
"buildCommand": "pnpm -F web build --debug",
|
||||||
|
"devCommand": "pnpm -F web dev",
|
||||||
|
"installCommand": "pnpm -F web install",
|
||||||
|
"outputDirectory": "packages/web/dist",
|
||||||
|
"framework": "vite",
|
||||||
"github": {
|
"github": {
|
||||||
"silent": true
|
"silent": true
|
||||||
},
|
},
|
||||||
|
@ -6,6 +11,14 @@
|
||||||
{
|
{
|
||||||
"source": "/netease/:match*",
|
"source": "/netease/:match*",
|
||||||
"destination": "https://your-netease-api.example.com/:match*"
|
"destination": "https://your-netease-api.example.com/:match*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "/r3play/:match*",
|
||||||
|
"destination": "http://168.138.40.199:51324/r3play/:match*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "/(.*)",
|
||||||
|
"destination": "/"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
16
vercel.json
16
vercel.json
|
@ -1,16 +1,24 @@
|
||||||
{
|
{
|
||||||
|
"buildCommand": "pnpm -F web build",
|
||||||
|
"devCommand": "pnpm -F web dev",
|
||||||
|
"installCommand": "pnpm -F web install",
|
||||||
|
"outputDirectory": "packages/web/dist",
|
||||||
|
"framework": "vite",
|
||||||
"github": {
|
"github": {
|
||||||
"silent": true
|
"silent": true
|
||||||
},
|
},
|
||||||
"rewrites": [
|
"rewrites": [
|
||||||
{
|
{
|
||||||
"source": "/netease/:match*",
|
"source": "/netease/:match*",
|
||||||
"destination": "https://netease-cloud-music-api-kohl-one-98.vercel.app/:match*"
|
"destination": "http://168.138.174.244:30001/:match*"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "/yesplaymusic/:match*",
|
"source": "/r3play/:match*",
|
||||||
"destination": "http://168.138.40.199:51324/yesplaymusic/:match*"
|
"destination": "http://168.138.174.244:35530/:match*"
|
||||||
},
|
},
|
||||||
{ "source": "/(.*)", "destination": "/" }
|
{
|
||||||
|
"source": "/(.*)",
|
||||||
|
"destination": "/"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user