mirror of
https://github.com/GiriNeko/YesPlayMusic.git
synced 2025-12-16 21:28:06 +00:00
feat: updates
This commit is contained in:
parent
08abf8229f
commit
fb21405bf9
33 changed files with 699 additions and 361 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -81,6 +81,7 @@ typings/
|
||||||
# ----
|
# ----
|
||||||
dist
|
dist
|
||||||
**/.tmp
|
**/.tmp
|
||||||
|
/tmp
|
||||||
release
|
release
|
||||||
.DS_Store
|
.DS_Store
|
||||||
dist-ssr
|
dist-ssr
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@
|
||||||
"realm": "^10.13.0"
|
"realm": "^10.13.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^3.2.0",
|
|
||||||
"@types/cookie-parser": "^1.4.2",
|
"@types/cookie-parser": "^1.4.2",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
|
"@types/express-fileupload": "^1.2.2",
|
||||||
"@types/howler": "^2.2.6",
|
"@types/howler": "^2.2.6",
|
||||||
"@types/js-cookie": "^3.0.1",
|
"@types/js-cookie": "^3.0.1",
|
||||||
"@types/lodash-es": "^4.17.6",
|
"@types/lodash-es": "^4.17.6",
|
||||||
|
|
@ -60,10 +60,13 @@
|
||||||
"eslint": "^8.11.0",
|
"eslint": "^8.11.0",
|
||||||
"eslint-plugin-react": "^7.29.4",
|
"eslint-plugin-react": "^7.29.4",
|
||||||
"eslint-plugin-react-hooks": "^4.3.0",
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
|
"express-fileupload": "^1.3.1",
|
||||||
|
"fast-folder-size": "^1.6.1",
|
||||||
"howler": "^2.2.3",
|
"howler": "^2.2.3",
|
||||||
"js-cookie": "^3.0.1",
|
"js-cookie": "^3.0.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
|
"music-metadata": "^7.12.2",
|
||||||
"postcss": "^8.4.12",
|
"postcss": "^8.4.12",
|
||||||
"prettier": "2.5.1",
|
"prettier": "2.5.1",
|
||||||
"prettier-plugin-tailwindcss": "^0.1.8",
|
"prettier-plugin-tailwindcss": "^0.1.8",
|
||||||
|
|
|
||||||
267
packages/main/cache.ts
Normal file
267
packages/main/cache.ts
Normal file
|
|
@ -0,0 +1,267 @@
|
||||||
|
import { db, ModelNames, realm } from './database'
|
||||||
|
import type { FetchTracksResponse } from '../renderer/src/api/track'
|
||||||
|
import { app, ipcMain } from 'electron'
|
||||||
|
import { Request, Response } from 'express'
|
||||||
|
import logger from './logger'
|
||||||
|
import fs from 'fs'
|
||||||
|
import * as musicMetadata from 'music-metadata'
|
||||||
|
|
||||||
|
export async function setCache(api: string, data: any, query: any) {
|
||||||
|
switch (api) {
|
||||||
|
case 'user/account':
|
||||||
|
case 'personalized':
|
||||||
|
case 'likelist': {
|
||||||
|
if (!data) return
|
||||||
|
db.set(ModelNames.ACCOUNT_DATA, api, data)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'user/playlist': {
|
||||||
|
if (!data.playlist) return
|
||||||
|
db.set(ModelNames.USER_PLAYLISTS, Number(query.uid), data)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'song/detail': {
|
||||||
|
if (!data.songs) return
|
||||||
|
const tracks = (data as FetchTracksResponse).songs
|
||||||
|
db.batchSet(
|
||||||
|
ModelNames.TRACK,
|
||||||
|
tracks.map(t => ({
|
||||||
|
id: t.id,
|
||||||
|
json: JSON.stringify(t),
|
||||||
|
updateAt: Date.now(),
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'album': {
|
||||||
|
if (!data.album) return
|
||||||
|
db.set(ModelNames.ALBUM, Number(data.album.id), data)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'playlist/detail': {
|
||||||
|
if (!data.playlist) return
|
||||||
|
db.set(ModelNames.PLAYLIST, Number(data.playlist.id), data)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'artist/album': {
|
||||||
|
if (!data.hotAlbums) return
|
||||||
|
db.set(ModelNames.ARTIST_ALBUMS, Number(data.artist.id), data)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the cache is expired
|
||||||
|
* @param updateAt from database, milliseconds
|
||||||
|
* @param staleTime minutes
|
||||||
|
*/
|
||||||
|
const isCacheExpired = (updateAt: number, staleTime: number) => {
|
||||||
|
return Date.now() - updateAt > staleTime * 1000 * 60
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCache(
|
||||||
|
api: string,
|
||||||
|
query: any,
|
||||||
|
checkIsExpired: boolean = false
|
||||||
|
): any {
|
||||||
|
switch (api) {
|
||||||
|
case 'user/account':
|
||||||
|
case 'personalized':
|
||||||
|
case 'likelist': {
|
||||||
|
const data = db.get(ModelNames.ACCOUNT_DATA, api) as any
|
||||||
|
if (data?.json) return JSON.parse(data.json)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'user/playlist': {
|
||||||
|
if (!query.uid) return
|
||||||
|
const userPlaylists = db.get(
|
||||||
|
ModelNames.USER_PLAYLISTS,
|
||||||
|
Number(query?.uid)
|
||||||
|
) as any
|
||||||
|
if (userPlaylists?.json) return JSON.parse(userPlaylists.json)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'song/detail': {
|
||||||
|
const ids: string[] = query?.ids.split(',')
|
||||||
|
const idsQuery = ids.map(id => `id = ${id}`).join(' OR ')
|
||||||
|
const tracksRaw = realm
|
||||||
|
.objects(ModelNames.TRACK)
|
||||||
|
.filtered(`(${idsQuery})`)
|
||||||
|
if (tracksRaw.length !== ids.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const tracks = ids.map(id => {
|
||||||
|
const track = tracksRaw.find(t => t.id === Number(id)) as any
|
||||||
|
return JSON.parse(track.json)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
songs: tracks,
|
||||||
|
privileges: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'album': {
|
||||||
|
if (!query?.id) return
|
||||||
|
const album = db.get(ModelNames.ALBUM, Number(query?.id)) as any
|
||||||
|
if (checkIsExpired && isCacheExpired(album?.updateAt, 24 * 60)) return
|
||||||
|
if (album?.json) return JSON.parse(album.json)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'playlist/detail': {
|
||||||
|
if (!query?.id) return
|
||||||
|
const playlist = db.get(ModelNames.PLAYLIST, Number(query?.id)) as any
|
||||||
|
if (checkIsExpired && isCacheExpired(playlist?.updateAt, 10)) return
|
||||||
|
if (playlist?.json) return JSON.parse(playlist.json)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'artist/album': {
|
||||||
|
if (!query?.id) return
|
||||||
|
const artistAlbums = db.get(
|
||||||
|
ModelNames.ARTIST_ALBUMS,
|
||||||
|
Number(query?.id)
|
||||||
|
) as any
|
||||||
|
if (checkIsExpired && isCacheExpired(artistAlbums?.updateAt, 30)) return
|
||||||
|
if (artistAlbums?.json) return JSON.parse(artistAlbums.json)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCacheForExpress(api: string, req: Request) {
|
||||||
|
// Get track detail cache
|
||||||
|
if (api === 'song/detail') {
|
||||||
|
const cache = getCache(api, req.query)
|
||||||
|
if (cache) {
|
||||||
|
logger.info(`[cache] Cache hit for ${req.path}`)
|
||||||
|
return cache
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get audio cache if API is song/detail
|
||||||
|
if (api === 'song/url') {
|
||||||
|
const cache = db.get(ModelNames.AUDIO, Number(req.query.id)) as any
|
||||||
|
if (!cache) return
|
||||||
|
|
||||||
|
const audioFileName = `${cache.id}-${cache.br}.${cache.type}`
|
||||||
|
|
||||||
|
const isAudioFileExists = fs.existsSync(
|
||||||
|
`${app.getPath('userData')}/audio_cache/${audioFileName}`
|
||||||
|
)
|
||||||
|
if (!isAudioFileExists) return
|
||||||
|
|
||||||
|
logger.info(`[cache] Audio cache hit for ${req.path}`)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
source: cache.source,
|
||||||
|
id: cache.id,
|
||||||
|
url: `http://127.0.0.1:42710/yesplaymusic/audio/${audioFileName}`,
|
||||||
|
br: cache.br,
|
||||||
|
size: 0,
|
||||||
|
md5: '',
|
||||||
|
code: 200,
|
||||||
|
expi: 0,
|
||||||
|
type: cache.type,
|
||||||
|
gain: 0,
|
||||||
|
fee: 8,
|
||||||
|
uf: null,
|
||||||
|
payed: 0,
|
||||||
|
flag: 4,
|
||||||
|
canExtend: false,
|
||||||
|
freeTrialInfo: null,
|
||||||
|
level: 'standard',
|
||||||
|
encodeType: cache.type,
|
||||||
|
freeTrialPrivilege: {
|
||||||
|
resConsumable: false,
|
||||||
|
userConsumable: false,
|
||||||
|
listenType: null,
|
||||||
|
},
|
||||||
|
freeTimeTrialPrivilege: {
|
||||||
|
resConsumable: false,
|
||||||
|
userConsumable: false,
|
||||||
|
type: 0,
|
||||||
|
remainTime: 0,
|
||||||
|
},
|
||||||
|
urlSource: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
code: 200,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAudioCache(fileName: string, res: Response) {
|
||||||
|
if (!fileName) {
|
||||||
|
return res.status(400).send({ error: 'No filename provided' })
|
||||||
|
}
|
||||||
|
const id = Number(fileName.split('-')[0])
|
||||||
|
|
||||||
|
try {
|
||||||
|
const path = `${app.getPath('userData')}/audio_cache/${fileName}`
|
||||||
|
const audio = fs.readFileSync(path)
|
||||||
|
if (audio.byteLength === 0) {
|
||||||
|
db.delete(ModelNames.AUDIO, Number(id))
|
||||||
|
fs.unlinkSync(path)
|
||||||
|
return res.status(404).send({ error: 'Audio not found' })
|
||||||
|
}
|
||||||
|
res.send(audio)
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).send({ error })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache audio info local folder
|
||||||
|
export async function cacheAudio(
|
||||||
|
buffer: Buffer,
|
||||||
|
{ id, source }: { id: number; source: string }
|
||||||
|
) {
|
||||||
|
const path = `${app.getPath('userData')}/audio_cache`
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.statSync(path)
|
||||||
|
} catch (e) {
|
||||||
|
fs.mkdirSync(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const meta = await musicMetadata.parseBuffer(buffer)
|
||||||
|
const br = meta.format.bitrate
|
||||||
|
const type = {
|
||||||
|
'MPEG 1 Layer 3': 'mp3',
|
||||||
|
'Ogg Vorbis': 'ogg',
|
||||||
|
AAC: 'm4a',
|
||||||
|
FLAC: 'flac',
|
||||||
|
unknown: 'unknown',
|
||||||
|
}[meta.format.codec ?? 'unknown']
|
||||||
|
|
||||||
|
await fs.writeFile(`${path}/${id}-${br}.${type}`, buffer, error => {
|
||||||
|
if (error) {
|
||||||
|
return logger.error(`[cache] cacheAudio failed: ${error}`)
|
||||||
|
}
|
||||||
|
logger.info(`Audio file ${id}-${br}.${type} cached!`)
|
||||||
|
|
||||||
|
realm.write(() => {
|
||||||
|
realm.create(
|
||||||
|
ModelNames.AUDIO,
|
||||||
|
{
|
||||||
|
id: Number(id),
|
||||||
|
type,
|
||||||
|
br,
|
||||||
|
source,
|
||||||
|
updateAt: Date.now(),
|
||||||
|
},
|
||||||
|
'modified'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.info(`[cache] cacheAudio ${id}-${br}.${type}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.on('getApiCacheSync', (event, args) => {
|
||||||
|
const { api, query } = args
|
||||||
|
const data = getCache(api, query, false)
|
||||||
|
event.returnValue = data
|
||||||
|
})
|
||||||
|
|
@ -1,48 +1,78 @@
|
||||||
import Realm from 'realm'
|
import Realm from 'realm'
|
||||||
import type { FetchTracksResponse } from '../renderer/src/api/track'
|
import path from 'path'
|
||||||
import type { FetchAlbumResponse } from '../renderer/src/api/album'
|
import { app } from 'electron'
|
||||||
|
|
||||||
enum ModelNames {
|
export enum ModelNames {
|
||||||
|
ACCOUNT_DATA = 'AccountData',
|
||||||
TRACK = 'Track',
|
TRACK = 'Track',
|
||||||
ALBUM = 'Album',
|
ALBUM = 'Album',
|
||||||
ARTIST = 'Artist',
|
ARTIST = 'Artist',
|
||||||
PLAYLIST = 'Playlist',
|
PLAYLIST = 'Playlist',
|
||||||
|
ARTIST_ALBUMS = 'ArtistAlbums',
|
||||||
|
USER_PLAYLISTS = 'UserPlaylists',
|
||||||
|
AUDIO = 'Audio',
|
||||||
}
|
}
|
||||||
|
|
||||||
const universalProperties = {
|
export enum AudioSources {
|
||||||
|
NETEASE = 'netease',
|
||||||
|
KUWO = 'kuwo',
|
||||||
|
QQ = 'qq',
|
||||||
|
KUGOU = 'kugou',
|
||||||
|
YOUTUBE = 'youtube',
|
||||||
|
MIGU = 'migu',
|
||||||
|
JOOX = 'joox',
|
||||||
|
BILIBILI = 'bilibili',
|
||||||
|
}
|
||||||
|
|
||||||
|
const RegularSchemas = [
|
||||||
|
ModelNames.USER_PLAYLISTS,
|
||||||
|
ModelNames.ARTIST_ALBUMS,
|
||||||
|
ModelNames.PLAYLIST,
|
||||||
|
ModelNames.ALBUM,
|
||||||
|
ModelNames.TRACK,
|
||||||
|
].map(name => ({
|
||||||
|
primaryKey: 'id',
|
||||||
|
name,
|
||||||
|
properties: {
|
||||||
id: 'int',
|
id: 'int',
|
||||||
json: 'string',
|
json: 'string',
|
||||||
updateAt: 'int',
|
updateAt: 'int',
|
||||||
}
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
const TrackSchema = {
|
export const realm = new Realm({
|
||||||
name: ModelNames.TRACK,
|
path: path.resolve(app.getPath('userData'), './api_cache/db.realm'),
|
||||||
properties: universalProperties,
|
schema: [
|
||||||
|
...RegularSchemas,
|
||||||
|
{
|
||||||
|
name: ModelNames.ACCOUNT_DATA,
|
||||||
|
properties: {
|
||||||
|
id: 'string',
|
||||||
|
json: 'string',
|
||||||
|
updateAt: 'int',
|
||||||
|
},
|
||||||
primaryKey: 'id',
|
primaryKey: 'id',
|
||||||
}
|
},
|
||||||
|
{
|
||||||
const AlbumSchema = {
|
name: ModelNames.AUDIO,
|
||||||
name: ModelNames.ALBUM,
|
properties: {
|
||||||
properties: universalProperties,
|
id: 'int',
|
||||||
|
br: 'int',
|
||||||
|
type: 'string',
|
||||||
|
source: 'string',
|
||||||
|
updateAt: 'int',
|
||||||
|
},
|
||||||
primaryKey: 'id',
|
primaryKey: 'id',
|
||||||
}
|
},
|
||||||
|
],
|
||||||
const PlaylistSchema = {
|
|
||||||
name: ModelNames.PLAYLIST,
|
|
||||||
properties: universalProperties,
|
|
||||||
primaryKey: 'id',
|
|
||||||
}
|
|
||||||
|
|
||||||
const realm = new Realm({
|
|
||||||
path: './.tmp/db.realm',
|
|
||||||
schema: [TrackSchema, AlbumSchema, PlaylistSchema],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const database = {
|
export const db = {
|
||||||
get: (model: ModelNames, key: number) => {
|
get: (model: ModelNames, key: number | string) => {
|
||||||
return realm.objectForPrimaryKey(model, key)
|
return realm.objectForPrimaryKey(model, key)
|
||||||
},
|
},
|
||||||
set: (model: ModelNames, key: number, value: any) => {
|
set: (model: ModelNames, key: number | string, value: any) => {
|
||||||
|
realm.write(() => {
|
||||||
realm.create(
|
realm.create(
|
||||||
model,
|
model,
|
||||||
{
|
{
|
||||||
|
|
@ -52,81 +82,16 @@ export const database = {
|
||||||
},
|
},
|
||||||
'modified'
|
'modified'
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
batchSet: (model: ModelNames, items: any[]) => {
|
||||||
|
realm.write(() => {
|
||||||
|
items.forEach(item => {
|
||||||
|
realm.create(model, item, 'modified')
|
||||||
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
delete: (model: ModelNames, key: number) => {
|
delete: (model: ModelNames, key: number) => {
|
||||||
realm.delete(realm.objectForPrimaryKey(model, key))
|
realm.delete(realm.objectForPrimaryKey(model, key))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setTracks(data: FetchTracksResponse) {
|
|
||||||
if (!data.songs) return
|
|
||||||
const tracks = data.songs
|
|
||||||
realm.write(() => {
|
|
||||||
tracks.forEach(track => {
|
|
||||||
database.set(ModelNames.TRACK, track.id, track)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setCache(api: string, data: any) {
|
|
||||||
switch (api) {
|
|
||||||
case 'song_detail': {
|
|
||||||
setTracks(data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case 'album': {
|
|
||||||
if (!data.album) return
|
|
||||||
realm.write(() => {
|
|
||||||
database.set(ModelNames.ALBUM, Number(data.album.id), data)
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case 'playlist_detail': {
|
|
||||||
if (!data.playlist) return
|
|
||||||
realm.write(() => {
|
|
||||||
database.set(ModelNames.PLAYLIST, Number(data.playlist.id), data)
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCache(api: string, query: any) {
|
|
||||||
switch (api) {
|
|
||||||
case 'song_detail': {
|
|
||||||
const ids: string[] = query?.ids.split(',')
|
|
||||||
const idsQuery = ids.map(id => `id = ${id}`).join(' OR ')
|
|
||||||
const tracksRaw = realm
|
|
||||||
.objects(ModelNames.TRACK)
|
|
||||||
.filtered(`(${idsQuery})`)
|
|
||||||
if (tracksRaw.length !== ids.length) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const tracks = tracksRaw.map(track => JSON.parse(track.json))
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: 200,
|
|
||||||
songs: tracks,
|
|
||||||
privileges: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'album': {
|
|
||||||
if (!query?.id) return
|
|
||||||
const album = realm.objectForPrimaryKey(
|
|
||||||
ModelNames.ALBUM,
|
|
||||||
Number(query?.id)
|
|
||||||
)?.json
|
|
||||||
if (album) return JSON.parse(album)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case 'playlist_detail': {
|
|
||||||
if (!query?.id) return
|
|
||||||
const playlist = realm.objectForPrimaryKey(
|
|
||||||
ModelNames.PLAYLIST,
|
|
||||||
Number(query?.id)
|
|
||||||
)?.json
|
|
||||||
if (playlist) return JSON.parse(playlist)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import './preload' // must be first
|
||||||
import {
|
import {
|
||||||
BrowserWindow,
|
BrowserWindow,
|
||||||
BrowserWindowConstructorOptions,
|
BrowserWindowConstructorOptions,
|
||||||
|
|
@ -6,7 +7,7 @@ import {
|
||||||
} from 'electron'
|
} from 'electron'
|
||||||
import Store from 'electron-store'
|
import Store from 'electron-store'
|
||||||
import { release } from 'os'
|
import { release } from 'os'
|
||||||
import { join } from 'path'
|
import path, { join } from 'path'
|
||||||
import logger from './logger'
|
import logger from './logger'
|
||||||
import './server'
|
import './server'
|
||||||
import './database'
|
import './database'
|
||||||
|
|
@ -96,6 +97,7 @@ async function createWindow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
app.whenReady().then(async () => {
|
app.whenReady().then(async () => {
|
||||||
|
logger.info('[index] app ready')
|
||||||
createWindow()
|
createWindow()
|
||||||
|
|
||||||
// Install devtool extension
|
// Install devtool extension
|
||||||
|
|
@ -107,10 +109,10 @@ app.whenReady().then(async () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
} = require('electron-devtools-installer')
|
} = require('electron-devtools-installer')
|
||||||
installExtension(REACT_DEVELOPER_TOOLS.id).catch(err =>
|
installExtension(REACT_DEVELOPER_TOOLS.id).catch(err =>
|
||||||
console.log('An error occurred: ', err)
|
logger.info('An error occurred: ', err)
|
||||||
)
|
)
|
||||||
installExtension(REDUX_DEVTOOLS.id).catch(err =>
|
installExtension(REDUX_DEVTOOLS.id).catch(err =>
|
||||||
console.log('An error occurred: ', err)
|
logger.info('An error occurred: ', err)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
17
packages/main/preload.ts
Normal file
17
packages/main/preload.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import logger from './logger'
|
||||||
|
import path from 'path'
|
||||||
|
import { app } from 'electron'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
const isDev = !app.isPackaged
|
||||||
|
|
||||||
|
if (isDev) {
|
||||||
|
const devUserDataPath = path.resolve(process.cwd(), './tmp/userData')
|
||||||
|
try {
|
||||||
|
fs.statSync(devUserDataPath)
|
||||||
|
} catch (e) {
|
||||||
|
fs.mkdirSync(devUserDataPath)
|
||||||
|
}
|
||||||
|
app.setPath('appData', devUserDataPath)
|
||||||
|
}
|
||||||
|
logger.info(`[index] userData path: ${app.getPath('userData')}`)
|
||||||
|
|
@ -2,27 +2,32 @@ import { pathCase } from 'change-case'
|
||||||
import cookieParser from 'cookie-parser'
|
import cookieParser from 'cookie-parser'
|
||||||
import express, { Request, Response } from 'express'
|
import express, { Request, Response } from 'express'
|
||||||
import logger from './logger'
|
import logger from './logger'
|
||||||
import { getCache, setCache } from './database'
|
import {
|
||||||
|
setCache,
|
||||||
|
getCacheForExpress,
|
||||||
|
cacheAudio,
|
||||||
|
getAudioCache,
|
||||||
|
} from './cache'
|
||||||
|
import fileUpload from 'express-fileupload'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const neteaseApi = require('NeteaseCloudMusicApi') as (params: any) => any[]
|
const neteaseApi = require('NeteaseCloudMusicApi') as (params: any) => any[]
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
app.use(cookieParser())
|
app.use(cookieParser())
|
||||||
const port = Number(process.env['ELECTRON_DEV_NETEASE_API_PORT'] ?? 3000)
|
app.use(fileUpload())
|
||||||
|
|
||||||
Object.entries(neteaseApi).forEach(([name, handler]) => {
|
Object.entries(neteaseApi).forEach(([name, handler]) => {
|
||||||
if (['serveNcmApi', 'getModulesDefinitions'].includes(name)) return
|
if (['serveNcmApi', 'getModulesDefinitions'].includes(name)) return
|
||||||
|
|
||||||
|
name = pathCase(name)
|
||||||
|
|
||||||
const wrappedHandler = async (req: Request, res: Response) => {
|
const wrappedHandler = async (req: Request, res: Response) => {
|
||||||
logger.info(`[server] Handling request: ${req.path}`)
|
logger.info(`[server] Handling request: ${req.path}`)
|
||||||
|
|
||||||
// Get from cache
|
// Get from cache
|
||||||
const cacheResult = getCache(name, req.query)
|
const cache = await getCacheForExpress(name, req)
|
||||||
if (cacheResult) {
|
if (cache) return res.json(cache)
|
||||||
logger.info(`[server] Cache hit for ${req.path}`)
|
|
||||||
return res.json(cacheResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request netease api
|
// Request netease api
|
||||||
try {
|
try {
|
||||||
|
|
@ -30,18 +35,54 @@ Object.entries(neteaseApi).forEach(([name, handler]) => {
|
||||||
...req.query,
|
...req.query,
|
||||||
cookie: `MUSIC_U=${req.cookies['MUSIC_U']}`,
|
cookie: `MUSIC_U=${req.cookies['MUSIC_U']}`,
|
||||||
})
|
})
|
||||||
res.send(result.body)
|
|
||||||
setCache(name, result.body)
|
setCache(name, result.body, req.query)
|
||||||
|
return res.send(result.body)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).send(error)
|
return res.status(500).send(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const neteasePath = `/netease/${pathCase(name)}`
|
app.get(`/netease/${name}`, wrappedHandler)
|
||||||
app.get(neteasePath, wrappedHandler)
|
app.post(`/netease/${name}`, wrappedHandler)
|
||||||
app.post(neteasePath, wrappedHandler)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Cache audio
|
||||||
|
app.get(
|
||||||
|
'/yesplaymusic/audio/:filename',
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
getAudioCache(req.params.filename, res)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
app.post('/yesplaymusic/audio/:id', async (req: Request, res: Response) => {
|
||||||
|
const id = Number(req.params.id)
|
||||||
|
const { url } = req.query
|
||||||
|
if (isNaN(id)) {
|
||||||
|
return res.status(400).send({ error: 'Invalid param id' })
|
||||||
|
}
|
||||||
|
if (!url) {
|
||||||
|
return res.status(400).send({ error: 'Invalid query url' })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.files || Object.keys(req.files).length === 0 || !req.files.file) {
|
||||||
|
return res.status(400).send('No audio were uploaded.')
|
||||||
|
}
|
||||||
|
if ('length' in req.files.file) {
|
||||||
|
return res.status(400).send('Only can upload one audio at a time.')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await cacheAudio(req.files.file.data, {
|
||||||
|
id: id,
|
||||||
|
source: 'netease',
|
||||||
|
})
|
||||||
|
res.status(200).send('Audio cached!')
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).send({ error })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const port = Number(process.env['ELECTRON_DEV_NETEASE_API_PORT'] ?? 3000)
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
logger.info(`[server] API server listening on port ${port}`)
|
logger.info(`[server] API server listening on port ${port}`)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export enum AlbumApiNames {
|
||||||
export interface FetchAlbumParams {
|
export interface FetchAlbumParams {
|
||||||
id: number
|
id: number
|
||||||
}
|
}
|
||||||
interface FetchAlbumResponse {
|
export interface FetchAlbumResponse {
|
||||||
code: number
|
code: number
|
||||||
resourceState: boolean
|
resourceState: boolean
|
||||||
album: Album
|
album: Album
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export enum ArtistApiNames {
|
||||||
export interface FetchArtistParams {
|
export interface FetchArtistParams {
|
||||||
id: number
|
id: number
|
||||||
}
|
}
|
||||||
interface FetchArtistResponse {
|
export interface FetchArtistResponse {
|
||||||
code: number
|
code: number
|
||||||
more: boolean
|
more: boolean
|
||||||
artist: Artist
|
artist: Artist
|
||||||
|
|
@ -34,7 +34,7 @@ export interface FetchArtistAlbumsParams {
|
||||||
limit?: number // default: 50
|
limit?: number // default: 50
|
||||||
offset?: number // default: 0
|
offset?: number // default: 0
|
||||||
}
|
}
|
||||||
interface FetchArtistAlbumsResponse {
|
export interface FetchArtistAlbumsResponse {
|
||||||
code: number
|
code: number
|
||||||
hotAlbums: Album[]
|
hotAlbums: Album[]
|
||||||
more: boolean
|
more: boolean
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export interface FetchPlaylistParams {
|
||||||
id: number
|
id: number
|
||||||
s?: number // 歌单最近的 s 个收藏者
|
s?: number // 歌单最近的 s 个收藏者
|
||||||
}
|
}
|
||||||
interface FetchPlaylistResponse {
|
export interface FetchPlaylistResponse {
|
||||||
code: number
|
code: number
|
||||||
playlist: Playlist
|
playlist: Playlist
|
||||||
privileges: unknown // TODO: unknown type
|
privileges: unknown // TODO: unknown type
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ export interface FetchAudioSourceParams {
|
||||||
id: number
|
id: number
|
||||||
br?: number // bitrate, default 999000,320000 = 320kbps
|
br?: number // bitrate, default 999000,320000 = 320kbps
|
||||||
}
|
}
|
||||||
interface FetchAudioSourceResponse {
|
export interface FetchAudioSourceResponse {
|
||||||
code: number
|
code: number
|
||||||
data: {
|
data: {
|
||||||
br: number
|
br: number
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ export interface FetchUserPlaylistsParams {
|
||||||
offset: number
|
offset: number
|
||||||
limit?: number // default 30
|
limit?: number // default 30
|
||||||
}
|
}
|
||||||
interface FetchUserPlaylistsResponse {
|
export interface FetchUserPlaylistsResponse {
|
||||||
code: number
|
code: number
|
||||||
more: false
|
more: false
|
||||||
version: string
|
version: string
|
||||||
|
|
@ -116,7 +116,7 @@ export function fetchUserPlaylists(
|
||||||
export interface FetchUserLikedSongsIDsParams {
|
export interface FetchUserLikedSongsIDsParams {
|
||||||
uid: number
|
uid: number
|
||||||
}
|
}
|
||||||
interface FetchUserLikedSongsIDsResponse {
|
export interface FetchUserLikedSongsIDsResponse {
|
||||||
code: number
|
code: number
|
||||||
checkPoint: number
|
checkPoint: number
|
||||||
ids: number[]
|
ids: number[]
|
||||||
|
|
|
||||||
29
packages/renderer/src/api/yesplaymusic.ts
Normal file
29
packages/renderer/src/api/yesplaymusic.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import axios, { AxiosInstance } from 'axios'
|
||||||
|
|
||||||
|
const baseURL = String(
|
||||||
|
import.meta.env.DEV ? '/yesplaymusic' : `http://127.0.0.1:42710/yesplaymusic`
|
||||||
|
)
|
||||||
|
|
||||||
|
const request: AxiosInstance = axios.create({
|
||||||
|
baseURL,
|
||||||
|
withCredentials: true,
|
||||||
|
timeout: 15000,
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function cacheAudio(id: number, audio: string) {
|
||||||
|
const file = await axios.get(audio, { responseType: 'arraybuffer' })
|
||||||
|
if (file.status !== 200) return
|
||||||
|
|
||||||
|
const formData = new FormData()
|
||||||
|
const blob = new Blob([file.data], { type: 'multipart/form-data' })
|
||||||
|
formData.append('file', blob)
|
||||||
|
|
||||||
|
request.post(`/audio/${id}`, formData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
url: audio,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ import Skeleton from '@/components/Skeleton'
|
||||||
import SvgIcon from '@/components/SvgIcon'
|
import SvgIcon from '@/components/SvgIcon'
|
||||||
import { prefetchAlbum } from '@/hooks/useAlbum'
|
import { prefetchAlbum } from '@/hooks/useAlbum'
|
||||||
import { prefetchPlaylist } from '@/hooks/usePlaylist'
|
import { prefetchPlaylist } from '@/hooks/usePlaylist'
|
||||||
import { formatDate, resizeImage } from '@/utils/common'
|
import { formatDate, resizeImage, scrollToTop } from '@/utils/common'
|
||||||
|
|
||||||
export enum Subtitle {
|
export enum Subtitle {
|
||||||
COPYWRITER = 'copywriter',
|
COPYWRITER = 'copywriter',
|
||||||
|
|
@ -111,6 +111,7 @@ const CoverRow = ({
|
||||||
if (playlists) navigate(`/playlist/${id}`)
|
if (playlists) navigate(`/playlist/${id}`)
|
||||||
if (artists) navigate(`/artist/${id}`)
|
if (artists) navigate(`/artist/${id}`)
|
||||||
if (navigateCallback) navigateCallback()
|
if (navigateCallback) navigateCallback()
|
||||||
|
scrollToTop()
|
||||||
}
|
}
|
||||||
|
|
||||||
const prefetch = (id: number) => {
|
const prefetch = (id: number) => {
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,11 @@ const MediaControls = () => {
|
||||||
>
|
>
|
||||||
<SvgIcon
|
<SvgIcon
|
||||||
className='h-[1.5rem] w-[1.5rem] '
|
className='h-[1.5rem] w-[1.5rem] '
|
||||||
name={state === PlayerState.PLAYING ? 'pause' : 'play'}
|
name={
|
||||||
|
[PlayerState.PLAYING, PlayerState.LOADING].includes(state)
|
||||||
|
? 'pause'
|
||||||
|
: 'play'
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton onClick={() => track && player.nextTrack()} disabled={!track}>
|
<IconButton onClick={() => track && player.nextTrack()} disabled={!track}>
|
||||||
|
|
@ -125,6 +129,7 @@ const Progress = () => {
|
||||||
() => playerSnapshot.progress,
|
() => playerSnapshot.progress,
|
||||||
[playerSnapshot.progress]
|
[playerSnapshot.progress]
|
||||||
)
|
)
|
||||||
|
const state = useMemo(() => playerSnapshot.state, [playerSnapshot.state])
|
||||||
const track = useMemo(() => playerSnapshot.track, [playerSnapshot.track])
|
const track = useMemo(() => playerSnapshot.track, [playerSnapshot.track])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -133,7 +138,11 @@ const Progress = () => {
|
||||||
<Slider
|
<Slider
|
||||||
min={0}
|
min={0}
|
||||||
max={(track.dt ?? 0) / 1000}
|
max={(track.dt ?? 0) / 1000}
|
||||||
value={progress}
|
value={
|
||||||
|
state === PlayerState.PLAYING || state === PlayerState.PAUSED
|
||||||
|
? progress
|
||||||
|
: 0
|
||||||
|
}
|
||||||
onChange={value => {
|
onChange={value => {
|
||||||
player.progress = value
|
player.progress = value
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { NavLink } from 'react-router-dom'
|
import { NavLink } from 'react-router-dom'
|
||||||
import SvgIcon from '@/components/SvgIcon'
|
import SvgIcon from '@/components/SvgIcon'
|
||||||
import { prefetchPlaylist } from '@/hooks/usePlaylist'
|
|
||||||
import useUser from '@/hooks/useUser'
|
import useUser from '@/hooks/useUser'
|
||||||
import useUserPlaylists from '@/hooks/useUserPlaylists'
|
import useUserPlaylists from '@/hooks/useUserPlaylists'
|
||||||
import { scrollToTop } from '@/utils/common'
|
import { scrollToTop } from '@/utils/common'
|
||||||
|
import { prefetchPlaylist } from '@/hooks/usePlaylist'
|
||||||
|
|
||||||
interface Tab {
|
interface Tab {
|
||||||
name: string
|
name: string
|
||||||
|
|
@ -70,10 +70,10 @@ const Playlists = () => {
|
||||||
<div className='mb-16 overflow-auto pb-2'>
|
<div className='mb-16 overflow-auto pb-2'>
|
||||||
{playlists?.playlist?.map(playlist => (
|
{playlists?.playlist?.map(playlist => (
|
||||||
<NavLink
|
<NavLink
|
||||||
|
onMouseOver={() => prefetchPlaylist({ id: playlist.id })}
|
||||||
key={playlist.id}
|
key={playlist.id}
|
||||||
onClick={() => scrollToTop()}
|
onClick={() => scrollToTop()}
|
||||||
to={`/playlist/${playlist.id}`}
|
to={`/playlist/${playlist.id}`}
|
||||||
onMouseOver={() => prefetchPlaylist({ id: playlist.id })}
|
|
||||||
className={({ isActive }: { isActive: boolean }) =>
|
className={({ isActive }: { isActive: boolean }) =>
|
||||||
classNames(
|
classNames(
|
||||||
'btn-hover-animation line-clamp-1 my-px mx-3 flex cursor-default items-center rounded-lg px-3 py-[0.38rem] text-sm text-black opacity-70 transition-colors duration-200 after:scale-[0.97] after:bg-black/[.06] dark:text-white dark:after:bg-white/20',
|
'btn-hover-animation line-clamp-1 my-px mx-3 flex cursor-default items-center rounded-lg px-3 py-[0.38rem] text-sm text-black opacity-70 transition-colors duration-200 after:scale-[0.97] after:bg-black/[.06] dark:text-white dark:after:bg-white/20',
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@ const Slider = ({
|
||||||
onlyCallOnChangeAfterDragEnded?: boolean
|
onlyCallOnChangeAfterDragEnded?: boolean
|
||||||
orientation?: 'horizontal' | 'vertical'
|
orientation?: 'horizontal' | 'vertical'
|
||||||
}) => {
|
}) => {
|
||||||
console.log('[Slider.tsx] rendering')
|
|
||||||
|
|
||||||
const sliderRef = useRef<HTMLInputElement>(null)
|
const sliderRef = useRef<HTMLInputElement>(null)
|
||||||
const [isDragging, setIsDragging] = useState(false)
|
const [isDragging, setIsDragging] = useState(false)
|
||||||
const [draggingValue, setDraggingValue] = useState(value)
|
const [draggingValue, setDraggingValue] = useState(value)
|
||||||
|
|
@ -133,7 +131,7 @@ const Slider = ({
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'absolute h-[2px] group-hover:bg-brand-500',
|
'absolute h-[2px] group-hover:bg-brand-500',
|
||||||
isDragging ? 'bg-brand-500' : 'bg-gray-300 dark:bg-gray-400'
|
isDragging ? 'bg-brand-500' : 'bg-gray-300 dark:bg-gray-600'
|
||||||
)}
|
)}
|
||||||
style={usedTrackStyle}
|
style={usedTrackStyle}
|
||||||
></div>
|
></div>
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
import style from './Slider.module.scss'
|
|
||||||
|
|
||||||
const Slider = () => {
|
|
||||||
const [value, setValue] = useState(50)
|
|
||||||
|
|
||||||
const thumbStyle = useMemo(
|
|
||||||
() => ({
|
|
||||||
left: `${value}%`,
|
|
||||||
transform: `translate(-${value}%, -9px)`,
|
|
||||||
}),
|
|
||||||
[value]
|
|
||||||
)
|
|
||||||
|
|
||||||
const usedTrackStyle = useMemo(
|
|
||||||
() => ({
|
|
||||||
width: `${value}%`,
|
|
||||||
}),
|
|
||||||
[value]
|
|
||||||
)
|
|
||||||
|
|
||||||
const onDragging = false
|
|
||||||
|
|
||||||
const [isHover, setIsHover] = useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
onMouseEnter={() => setIsHover(true)}
|
|
||||||
onMouseLeave={() => setIsHover(false)}
|
|
||||||
>
|
|
||||||
<div className='absolute h-[2px] w-full bg-gray-500 bg-opacity-10'></div>
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
'absolute h-[2px]',
|
|
||||||
onDragging || isHover ? 'bg-brand-500' : 'bg-gray-500'
|
|
||||||
)}
|
|
||||||
style={usedTrackStyle}
|
|
||||||
></div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
'absolute flex h-5 w-5 items-center justify-center rounded-full bg-brand-500 bg-opacity-20',
|
|
||||||
!onDragging && !isHover && 'opacity-0'
|
|
||||||
)}
|
|
||||||
style={thumbStyle}
|
|
||||||
>
|
|
||||||
<div className='absolute h-2 w-2 rounded-full bg-brand-500'></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input
|
|
||||||
type='range'
|
|
||||||
min='0'
|
|
||||||
max='100'
|
|
||||||
value={value}
|
|
||||||
onChange={e => setValue(Number(e.target.value))}
|
|
||||||
className='absolute h-[2px] w-full appearance-none opacity-0'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Slider
|
|
||||||
|
|
@ -71,7 +71,7 @@ const Track = memo(
|
||||||
!isSkeleton && {
|
!isSkeleton && {
|
||||||
'btn-hover-animation after:bg-gray-100 dark:after:bg-white/[.08]':
|
'btn-hover-animation after:bg-gray-100 dark:after:bg-white/[.08]':
|
||||||
!isHighlight,
|
!isHighlight,
|
||||||
'bg-brand-100 dark:bg-gray-800': isHighlight,
|
'bg-brand-50 dark:bg-gray-800': isHighlight,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -122,7 +122,8 @@ const Track = memo(
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'ml-1',
|
'ml-1',
|
||||||
isHighlight ? 'text-brand-500/[.8]' : 'text-gray-400'
|
isHighlight ? 'text-brand-500/[.8]' : 'text-gray-400'
|
||||||
)}>
|
)}
|
||||||
|
>
|
||||||
({subtitle})
|
({subtitle})
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { NavLink } from 'react-router-dom'
|
||||||
import ArtistInline from '@/components/ArtistsInline'
|
import ArtistInline from '@/components/ArtistsInline'
|
||||||
import Skeleton from '@/components/Skeleton'
|
import Skeleton from '@/components/Skeleton'
|
||||||
import SvgIcon from '@/components/SvgIcon'
|
import SvgIcon from '@/components/SvgIcon'
|
||||||
import { prefetchAlbum } from '@/hooks/useAlbum'
|
|
||||||
import useUser from '@/hooks/useUser'
|
import useUser from '@/hooks/useUser'
|
||||||
import useUserLikedSongsIDs from '@/hooks/useUserLikedSongsIDs'
|
import useUserLikedSongsIDs from '@/hooks/useUserLikedSongsIDs'
|
||||||
import { formatDuration, resizeImage } from '@/utils/common'
|
import { formatDuration, resizeImage } from '@/utils/common'
|
||||||
|
|
@ -31,8 +30,10 @@ const Track = memo(
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'group grid w-full rounded-xl after:scale-[.98] after:rounded-xl dark:after:bg-white/[.08]',
|
'group grid w-full rounded-xl after:scale-[.98] after:rounded-xl dark:after:bg-white/[.08]',
|
||||||
'grid-cols-12 p-2 pr-4',
|
'grid-cols-12 p-2 pr-4',
|
||||||
!isSkeleton && !isPlaying && 'btn-hover-animation after:bg-gray-100 dark:after:bg-white/[.08]',
|
!isSkeleton &&
|
||||||
!isSkeleton && isPlaying && 'bg-brand-100 dark:bg-gray-800'
|
!isPlaying &&
|
||||||
|
'btn-hover-animation after:bg-gray-100 dark:after:bg-white/[.08]',
|
||||||
|
!isSkeleton && isPlaying && 'bg-brand-50 dark:bg-gray-800'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* Track info */}
|
{/* Track info */}
|
||||||
|
|
@ -78,7 +79,9 @@ const Track = memo(
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'text-sm',
|
'text-sm',
|
||||||
isPlaying ? 'text-brand-500' : 'text-gray-600 dark:text-gray-400'
|
isPlaying
|
||||||
|
? 'text-brand-500'
|
||||||
|
: 'text-gray-600 dark:text-gray-400'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isSkeleton ? (
|
{isSkeleton ? (
|
||||||
|
|
@ -98,7 +101,6 @@ const Track = memo(
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<NavLink
|
<NavLink
|
||||||
to={`/album/${track.al.id}`}
|
to={`/album/${track.al.id}`}
|
||||||
onMouseOver={() => prefetchAlbum({ id: track.al.id })}
|
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'hover:underline',
|
'hover:underline',
|
||||||
isPlaying && 'text-brand-500'
|
isPlaying && 'text-brand-500'
|
||||||
|
|
@ -137,7 +139,9 @@ const Track = memo(
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'min-w-[2.5rem] text-right',
|
'min-w-[2.5rem] text-right',
|
||||||
isPlaying ? 'text-brand-500' : 'text-gray-600 dark:text-gray-400'
|
isPlaying
|
||||||
|
? 'text-brand-500'
|
||||||
|
: 'text-gray-600 dark:text-gray-400'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{formatDuration(track.dt, 'en', 'hh:mm:ss')}
|
{formatDuration(track.dt, 'en', 'hh:mm:ss')}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { fetchAlbum } from '@/api/album'
|
import { fetchAlbum } from '@/api/album'
|
||||||
import { AlbumApiNames } from '@/api/album'
|
import { AlbumApiNames } from '@/api/album'
|
||||||
import type { FetchAlbumParams } from '@/api/album'
|
import type { FetchAlbumParams, FetchAlbumResponse } from '@/api/album'
|
||||||
import reactQueryClient from '@/utils/reactQueryClient'
|
import reactQueryClient from '@/utils/reactQueryClient'
|
||||||
|
|
||||||
const fetch = async (params: FetchAlbumParams, noCache?: boolean) => {
|
const fetch = async (params: FetchAlbumParams, noCache?: boolean) => {
|
||||||
|
|
@ -17,7 +17,14 @@ export default function useAlbum(params: FetchAlbumParams, noCache?: boolean) {
|
||||||
() => fetch(params, noCache),
|
() => fetch(params, noCache),
|
||||||
{
|
{
|
||||||
enabled: !!params.id,
|
enabled: !!params.id,
|
||||||
staleTime: Infinity,
|
staleTime: 24 * 60 * 60 * 1000, // 24 hours
|
||||||
|
placeholderData: (): FetchAlbumResponse =>
|
||||||
|
window.ipcRenderer.sendSync('getApiCacheSync', {
|
||||||
|
api: 'album',
|
||||||
|
query: {
|
||||||
|
id: params.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import { fetchArtistAlbums } from '@/api/artist'
|
import { fetchArtistAlbums } from '@/api/artist'
|
||||||
import { ArtistApiNames } from '@/api/artist'
|
import { ArtistApiNames } from '@/api/artist'
|
||||||
import type { FetchArtistAlbumsParams } from '@/api/artist'
|
import type {
|
||||||
|
FetchArtistAlbumsParams,
|
||||||
|
FetchArtistAlbumsResponse,
|
||||||
|
} from '@/api/artist'
|
||||||
|
|
||||||
export default function useUserAlbums(params: FetchArtistAlbumsParams) {
|
export default function useUserAlbums(params: FetchArtistAlbumsParams) {
|
||||||
return useQuery(
|
return useQuery(
|
||||||
|
|
@ -12,6 +15,13 @@ export default function useUserAlbums(params: FetchArtistAlbumsParams) {
|
||||||
{
|
{
|
||||||
enabled: !!params.id && params.id !== 0,
|
enabled: !!params.id && params.id !== 0,
|
||||||
staleTime: 3600000,
|
staleTime: 3600000,
|
||||||
|
placeholderData: (): FetchArtistAlbumsResponse =>
|
||||||
|
window.ipcRenderer.sendSync('getApiCacheSync', {
|
||||||
|
api: 'artist/album',
|
||||||
|
query: {
|
||||||
|
id: params.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { fetchPlaylist } from '@/api/playlist'
|
import { fetchPlaylist } from '@/api/playlist'
|
||||||
import { PlaylistApiNames } from '@/api/playlist'
|
import { PlaylistApiNames } from '@/api/playlist'
|
||||||
import type { FetchPlaylistParams } from '@/api/playlist'
|
import type { FetchPlaylistParams, FetchPlaylistResponse } from '@/api/playlist'
|
||||||
import reactQueryClient from '@/utils/reactQueryClient'
|
import reactQueryClient from '@/utils/reactQueryClient'
|
||||||
|
|
||||||
const fetch = (params: FetchPlaylistParams, noCache?: boolean) => {
|
const fetch = (params: FetchPlaylistParams, noCache?: boolean) => {
|
||||||
|
|
@ -16,7 +16,14 @@ export default function usePlaylist(
|
||||||
() => fetch(params, noCache),
|
() => fetch(params, noCache),
|
||||||
{
|
{
|
||||||
enabled: !!(params.id && params.id > 0 && !isNaN(Number(params.id))),
|
enabled: !!(params.id && params.id > 0 && !isNaN(Number(params.id))),
|
||||||
staleTime: 3600000,
|
staleTime: 60 * 60 * 1000, // 1 hour
|
||||||
|
placeholderData: (): FetchPlaylistResponse | undefined =>
|
||||||
|
window.ipcRenderer.sendSync('getApiCacheSync', {
|
||||||
|
api: 'playlist/detail',
|
||||||
|
query: {
|
||||||
|
id: params.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
import { TrackApiNames, fetchAudioSource, fetchTracks } from '@/api/track'
|
import { TrackApiNames, fetchAudioSource, fetchTracks } from '@/api/track'
|
||||||
import type { FetchAudioSourceParams, FetchTracksParams } from '@/api/track'
|
import type {
|
||||||
|
FetchAudioSourceParams,
|
||||||
|
FetchTracksParams,
|
||||||
|
FetchTracksResponse,
|
||||||
|
} from '@/api/track'
|
||||||
import reactQueryClient from '@/utils/reactQueryClient'
|
import reactQueryClient from '@/utils/reactQueryClient'
|
||||||
|
|
||||||
export default function useTracks(params: FetchTracksParams) {
|
export default function useTracks(params: FetchTracksParams) {
|
||||||
|
|
@ -12,6 +16,13 @@ export default function useTracks(params: FetchTracksParams) {
|
||||||
enabled: params.ids.length !== 0,
|
enabled: params.ids.length !== 0,
|
||||||
refetchInterval: false,
|
refetchInterval: false,
|
||||||
staleTime: Infinity,
|
staleTime: Infinity,
|
||||||
|
initialData: (): FetchTracksResponse | undefined =>
|
||||||
|
window.ipcRenderer.sendSync('getApiCacheSync', {
|
||||||
|
api: 'song/detail',
|
||||||
|
query: {
|
||||||
|
ids: params.ids.join(','),
|
||||||
|
},
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +48,7 @@ export function fetchAudioSourceWithReactQuery(params: FetchAudioSourceParams) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
retry: 3,
|
retry: 3,
|
||||||
staleTime: 1200000,
|
staleTime: 0, // TODO: Web版1小时缓存
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
import { fetchUserAccount } from '@/api/user'
|
import { fetchUserAccount, fetchUserAccountResponse } from '@/api/user'
|
||||||
import { UserApiNames } from '@/api/user'
|
import { UserApiNames } from '@/api/user'
|
||||||
|
|
||||||
export default function useUser() {
|
export default function useUser() {
|
||||||
return useQuery(UserApiNames.FETCH_USER_ACCOUNT, fetchUserAccount, {
|
return useQuery(UserApiNames.FETCH_USER_ACCOUNT, fetchUserAccount, {
|
||||||
refetchOnWindowFocus: true,
|
refetchOnWindowFocus: true,
|
||||||
|
placeholderData: (): fetchUserAccountResponse | undefined =>
|
||||||
|
window.ipcRenderer.sendSync('getApiCacheSync', {
|
||||||
|
api: 'user/account',
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
import type { FetchUserLikedSongsIDsParams } from '@/api/user'
|
import type {
|
||||||
|
FetchUserLikedSongsIDsParams,
|
||||||
|
FetchUserLikedSongsIDsResponse,
|
||||||
|
} from '@/api/user'
|
||||||
import { UserApiNames, fetchUserLikedSongsIDs } from '@/api/user'
|
import { UserApiNames, fetchUserLikedSongsIDs } from '@/api/user'
|
||||||
|
|
||||||
export default function useUserLikedSongsIDs(
|
export default function useUserLikedSongsIDs(
|
||||||
|
|
@ -10,6 +13,13 @@ export default function useUserLikedSongsIDs(
|
||||||
{
|
{
|
||||||
enabled: !!(params.uid && params.uid !== 0),
|
enabled: !!(params.uid && params.uid !== 0),
|
||||||
refetchOnWindowFocus: true,
|
refetchOnWindowFocus: true,
|
||||||
|
placeholderData: (): FetchUserLikedSongsIDsResponse | undefined =>
|
||||||
|
window.ipcRenderer.sendSync('getApiCacheSync', {
|
||||||
|
api: 'likelist',
|
||||||
|
query: {
|
||||||
|
uid: params.uid,
|
||||||
|
},
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
import type { FetchUserPlaylistsParams } from '@/api/user'
|
import type {
|
||||||
|
FetchUserPlaylistsParams,
|
||||||
|
FetchUserPlaylistsResponse,
|
||||||
|
} from '@/api/user'
|
||||||
import { UserApiNames, fetchUserPlaylists } from '@/api/user'
|
import { UserApiNames, fetchUserPlaylists } from '@/api/user'
|
||||||
|
|
||||||
export default function useUserPlaylists(params: FetchUserPlaylistsParams) {
|
export default function useUserPlaylists(params: FetchUserPlaylistsParams) {
|
||||||
|
|
@ -14,6 +17,13 @@ export default function useUserPlaylists(params: FetchUserPlaylistsParams) {
|
||||||
params.uid !== 0 &&
|
params.uid !== 0 &&
|
||||||
params.offset !== undefined
|
params.offset !== undefined
|
||||||
),
|
),
|
||||||
|
placeholderData: (): FetchUserPlaylistsResponse =>
|
||||||
|
window.ipcRenderer.sendSync('getApiCacheSync', {
|
||||||
|
api: 'user/playlist',
|
||||||
|
query: {
|
||||||
|
uid: params.uid,
|
||||||
|
},
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,16 @@ export default function Home() {
|
||||||
const {
|
const {
|
||||||
data: recommendedPlaylists,
|
data: recommendedPlaylists,
|
||||||
isLoading: isLoadingRecommendedPlaylists,
|
isLoading: isLoadingRecommendedPlaylists,
|
||||||
} = useQuery(PlaylistApiNames.FETCH_RECOMMENDED_PLAYLISTS, () => {
|
} = useQuery(
|
||||||
|
PlaylistApiNames.FETCH_RECOMMENDED_PLAYLISTS,
|
||||||
|
() => {
|
||||||
return fetchRecommendedPlaylists({})
|
return fetchRecommendedPlaylists({})
|
||||||
})
|
},
|
||||||
|
{
|
||||||
|
placeholderData: () =>
|
||||||
|
window.ipcRenderer.sendSync('getApiCacheSync', { api: 'personalized' }),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,10 @@ export function resizeImage(
|
||||||
if (!Object.keys(sizeMap).includes(size)) {
|
if (!Object.keys(sizeMap).includes(size)) {
|
||||||
console.error(`Invalid cover size: ${size}`)
|
console.error(`Invalid cover size: ${size}`)
|
||||||
}
|
}
|
||||||
return `${url}?param=${sizeMap[size]}y${sizeMap[size]}`
|
return `${url}?param=${sizeMap[size]}y${sizeMap[size]}`.replace(
|
||||||
|
'http://',
|
||||||
|
'https://'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const storage = {
|
export const storage = {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import {
|
||||||
fetchAudioSourceWithReactQuery,
|
fetchAudioSourceWithReactQuery,
|
||||||
fetchTracksWithReactQuery,
|
fetchTracksWithReactQuery,
|
||||||
} from '@/hooks/useTracks'
|
} from '@/hooks/useTracks'
|
||||||
|
import { cacheAudio } from '@/api/yesplaymusic'
|
||||||
|
|
||||||
type TrackID = number
|
type TrackID = number
|
||||||
enum TrackListSourceType {
|
enum TrackListSourceType {
|
||||||
|
|
@ -19,9 +20,10 @@ export enum Mode {
|
||||||
}
|
}
|
||||||
export enum State {
|
export enum State {
|
||||||
INITIALIZING = 'initializing',
|
INITIALIZING = 'initializing',
|
||||||
|
READY = 'ready',
|
||||||
PLAYING = 'playing',
|
PLAYING = 'playing',
|
||||||
PAUSED = 'paused',
|
PAUSED = 'paused',
|
||||||
LOADED = 'loaded',
|
LOADING = 'loading',
|
||||||
}
|
}
|
||||||
export enum RepeatMode {
|
export enum RepeatMode {
|
||||||
OFF = 'off',
|
OFF = 'off',
|
||||||
|
|
@ -107,8 +109,7 @@ export class Player {
|
||||||
|
|
||||||
private _setupProgressInterval() {
|
private _setupProgressInterval() {
|
||||||
this._progressInterval = setInterval(() => {
|
this._progressInterval = setInterval(() => {
|
||||||
this._progress = _howler.seek()
|
if (this.state === State.PLAYING) this._progress = _howler.seek()
|
||||||
console.log(this.progress)
|
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,6 +117,7 @@ export class Player {
|
||||||
* Fetch track details from Netease based on this.trackID
|
* Fetch track details from Netease based on this.trackID
|
||||||
*/
|
*/
|
||||||
private async _fetchTrack(trackID: TrackID) {
|
private async _fetchTrack(trackID: TrackID) {
|
||||||
|
this.state = State.LOADING
|
||||||
const response = await fetchTracksWithReactQuery({ ids: [trackID] })
|
const response = await fetchTracksWithReactQuery({ ids: [trackID] })
|
||||||
if (response.songs.length) {
|
if (response.songs.length) {
|
||||||
return response.songs[0]
|
return response.songs[0]
|
||||||
|
|
@ -161,6 +163,8 @@ export class Player {
|
||||||
})
|
})
|
||||||
_howler = howler
|
_howler = howler
|
||||||
this.play()
|
this.play()
|
||||||
|
this.state = State.PLAYING
|
||||||
|
_howler.once('load', () => this._cacheAudio(this.trackID, audio))
|
||||||
|
|
||||||
if (!this._progressInterval) {
|
if (!this._progressInterval) {
|
||||||
this._setupProgressInterval()
|
this._setupProgressInterval()
|
||||||
|
|
@ -177,6 +181,11 @@ export class Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _cacheAudio(id: number, audio: string) {
|
||||||
|
if (audio.includes('yesplaymusic')) return
|
||||||
|
cacheAudio(id, audio)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Play current track
|
* Play current track
|
||||||
* @param {boolean} fade fade in
|
* @param {boolean} fade fade in
|
||||||
|
|
@ -184,6 +193,7 @@ export class Player {
|
||||||
play() {
|
play() {
|
||||||
_howler.play()
|
_howler.play()
|
||||||
this.state = State.PLAYING
|
this.state = State.PLAYING
|
||||||
|
this._progress = _howler.seek()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,11 @@ export default defineConfig({
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: path => path.replace(/^\/netease/, ''),
|
rewrite: path => path.replace(/^\/netease/, ''),
|
||||||
},
|
},
|
||||||
|
'/yesplaymusic/': {
|
||||||
|
target: `http://127.0.0.1:${process.env.ELECTRON_DEV_NETEASE_API_PORT}/yesplaymusic`,
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: path => path.replace(/^\/yesplaymusic/, ''),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
234
pnpm-lock.yaml
generated
234
pnpm-lock.yaml
generated
|
|
@ -1,9 +1,9 @@
|
||||||
lockfileVersion: 5.3
|
lockfileVersion: 5.3
|
||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
'@trivago/prettier-plugin-sort-imports': ^3.2.0
|
|
||||||
'@types/cookie-parser': ^1.4.2
|
'@types/cookie-parser': ^1.4.2
|
||||||
'@types/express': ^4.17.13
|
'@types/express': ^4.17.13
|
||||||
|
'@types/express-fileupload': ^1.2.2
|
||||||
'@types/howler': ^2.2.6
|
'@types/howler': ^2.2.6
|
||||||
'@types/js-cookie': ^3.0.1
|
'@types/js-cookie': ^3.0.1
|
||||||
'@types/lodash-es': ^4.17.6
|
'@types/lodash-es': ^4.17.6
|
||||||
|
|
@ -19,6 +19,7 @@ specifiers:
|
||||||
ansi-styles: ^6.1.0
|
ansi-styles: ^6.1.0
|
||||||
autoprefixer: ^10.4.4
|
autoprefixer: ^10.4.4
|
||||||
axios: ^0.26.1
|
axios: ^0.26.1
|
||||||
|
body-parser: ^1.19.2
|
||||||
change-case: ^4.1.2
|
change-case: ^4.1.2
|
||||||
classnames: ^2.3.1
|
classnames: ^2.3.1
|
||||||
color.js: ^1.2.0
|
color.js: ^1.2.0
|
||||||
|
|
@ -37,10 +38,13 @@ specifiers:
|
||||||
eslint-plugin-react: ^7.29.4
|
eslint-plugin-react: ^7.29.4
|
||||||
eslint-plugin-react-hooks: ^4.3.0
|
eslint-plugin-react-hooks: ^4.3.0
|
||||||
express: ^4.17.3
|
express: ^4.17.3
|
||||||
|
express-fileupload: ^1.3.1
|
||||||
|
fast-folder-size: ^1.6.1
|
||||||
howler: ^2.2.3
|
howler: ^2.2.3
|
||||||
js-cookie: ^3.0.1
|
js-cookie: ^3.0.1
|
||||||
lodash-es: ^4.17.21
|
lodash-es: ^4.17.21
|
||||||
md5: ^2.3.0
|
md5: ^2.3.0
|
||||||
|
music-metadata: ^7.12.2
|
||||||
postcss: ^8.4.12
|
postcss: ^8.4.12
|
||||||
prettier: 2.5.1
|
prettier: 2.5.1
|
||||||
prettier-plugin-tailwindcss: ^0.1.8
|
prettier-plugin-tailwindcss: ^0.1.8
|
||||||
|
|
@ -74,9 +78,9 @@ dependencies:
|
||||||
realm: 10.13.0
|
realm: 10.13.0
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@trivago/prettier-plugin-sort-imports': 3.2.0_prettier@2.5.1
|
|
||||||
'@types/cookie-parser': 1.4.2
|
'@types/cookie-parser': 1.4.2
|
||||||
'@types/express': 4.17.13
|
'@types/express': 4.17.13
|
||||||
|
'@types/express-fileupload': 1.2.2
|
||||||
'@types/howler': 2.2.6
|
'@types/howler': 2.2.6
|
||||||
'@types/js-cookie': 3.0.1
|
'@types/js-cookie': 3.0.1
|
||||||
'@types/lodash-es': 4.17.6
|
'@types/lodash-es': 4.17.6
|
||||||
|
|
@ -91,6 +95,7 @@ devDependencies:
|
||||||
ansi-styles: 6.1.0
|
ansi-styles: 6.1.0
|
||||||
autoprefixer: 10.4.4_postcss@8.4.12
|
autoprefixer: 10.4.4_postcss@8.4.12
|
||||||
axios: 0.26.1
|
axios: 0.26.1
|
||||||
|
body-parser: 1.19.2
|
||||||
classnames: 2.3.1
|
classnames: 2.3.1
|
||||||
color.js: 1.2.0
|
color.js: 1.2.0
|
||||||
colord: 2.9.2
|
colord: 2.9.2
|
||||||
|
|
@ -104,10 +109,13 @@ devDependencies:
|
||||||
eslint: 8.11.0
|
eslint: 8.11.0
|
||||||
eslint-plugin-react: 7.29.4_eslint@8.11.0
|
eslint-plugin-react: 7.29.4_eslint@8.11.0
|
||||||
eslint-plugin-react-hooks: 4.3.0_eslint@8.11.0
|
eslint-plugin-react-hooks: 4.3.0_eslint@8.11.0
|
||||||
|
express-fileupload: 1.3.1
|
||||||
|
fast-folder-size: 1.6.1
|
||||||
howler: 2.2.3
|
howler: 2.2.3
|
||||||
js-cookie: 3.0.1
|
js-cookie: 3.0.1
|
||||||
lodash-es: 4.17.21
|
lodash-es: 4.17.21
|
||||||
md5: 2.3.0
|
md5: 2.3.0
|
||||||
|
music-metadata: 7.12.2
|
||||||
postcss: 8.4.12
|
postcss: 8.4.12
|
||||||
prettier: 2.5.1
|
prettier: 2.5.1
|
||||||
prettier-plugin-tailwindcss: 0.1.8_prettier@2.5.1
|
prettier-plugin-tailwindcss: 0.1.8_prettier@2.5.1
|
||||||
|
|
@ -159,30 +167,6 @@ packages:
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/core/7.13.10:
|
|
||||||
resolution: {integrity: sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
dependencies:
|
|
||||||
'@babel/code-frame': 7.16.7
|
|
||||||
'@babel/generator': 7.17.3
|
|
||||||
'@babel/helper-compilation-targets': 7.16.7_@babel+core@7.13.10
|
|
||||||
'@babel/helper-module-transforms': 7.17.6
|
|
||||||
'@babel/helpers': 7.17.2
|
|
||||||
'@babel/parser': 7.17.3
|
|
||||||
'@babel/template': 7.16.7
|
|
||||||
'@babel/traverse': 7.17.3
|
|
||||||
'@babel/types': 7.17.0
|
|
||||||
convert-source-map: 1.8.0
|
|
||||||
debug: 4.3.3
|
|
||||||
gensync: 1.0.0-beta.2
|
|
||||||
json5: 2.2.0
|
|
||||||
lodash: 4.17.21
|
|
||||||
semver: 6.3.0
|
|
||||||
source-map: 0.5.7
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@babel/core/7.17.2:
|
/@babel/core/7.17.2:
|
||||||
resolution: {integrity: sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==}
|
resolution: {integrity: sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
@ -206,14 +190,6 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/generator/7.13.9:
|
|
||||||
resolution: {integrity: sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/types': 7.17.0
|
|
||||||
jsesc: 2.5.2
|
|
||||||
source-map: 0.5.7
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@babel/generator/7.17.3:
|
/@babel/generator/7.17.3:
|
||||||
resolution: {integrity: sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==}
|
resolution: {integrity: sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
@ -230,19 +206,6 @@ packages:
|
||||||
'@babel/types': 7.17.0
|
'@babel/types': 7.17.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-compilation-targets/7.16.7_@babel+core@7.13.10:
|
|
||||||
resolution: {integrity: sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
peerDependencies:
|
|
||||||
'@babel/core': ^7.0.0
|
|
||||||
dependencies:
|
|
||||||
'@babel/compat-data': 7.17.0
|
|
||||||
'@babel/core': 7.13.10
|
|
||||||
'@babel/helper-validator-option': 7.16.7
|
|
||||||
browserslist: 4.19.1
|
|
||||||
semver: 6.3.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@babel/helper-compilation-targets/7.16.7_@babel+core@7.17.2:
|
/@babel/helper-compilation-targets/7.16.7_@babel+core@7.17.2:
|
||||||
resolution: {integrity: sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==}
|
resolution: {integrity: sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
@ -358,12 +321,6 @@ packages:
|
||||||
js-tokens: 4.0.0
|
js-tokens: 4.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/parser/7.14.6:
|
|
||||||
resolution: {integrity: sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==}
|
|
||||||
engines: {node: '>=6.0.0'}
|
|
||||||
hasBin: true
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@babel/parser/7.17.3:
|
/@babel/parser/7.17.3:
|
||||||
resolution: {integrity: sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA==}
|
resolution: {integrity: sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA==}
|
||||||
engines: {node: '>=6.0.0'}
|
engines: {node: '>=6.0.0'}
|
||||||
|
|
@ -440,22 +397,6 @@ packages:
|
||||||
'@babel/types': 7.17.0
|
'@babel/types': 7.17.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/traverse/7.13.0:
|
|
||||||
resolution: {integrity: sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/code-frame': 7.16.7
|
|
||||||
'@babel/generator': 7.17.3
|
|
||||||
'@babel/helper-function-name': 7.16.7
|
|
||||||
'@babel/helper-split-export-declaration': 7.16.7
|
|
||||||
'@babel/parser': 7.17.3
|
|
||||||
'@babel/types': 7.17.0
|
|
||||||
debug: 4.3.3
|
|
||||||
globals: 11.12.0
|
|
||||||
lodash: 4.17.21
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@babel/traverse/7.17.3:
|
/@babel/traverse/7.17.3:
|
||||||
resolution: {integrity: sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==}
|
resolution: {integrity: sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
@ -474,14 +415,6 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/types/7.13.0:
|
|
||||||
resolution: {integrity: sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/helper-validator-identifier': 7.16.7
|
|
||||||
lodash: 4.17.21
|
|
||||||
to-fast-properties: 2.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@babel/types/7.17.0:
|
/@babel/types/7.17.0:
|
||||||
resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==}
|
resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
@ -643,7 +576,6 @@ packages:
|
||||||
|
|
||||||
/@tokenizer/token/0.3.0:
|
/@tokenizer/token/0.3.0:
|
||||||
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
|
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@tootallnate/once/1.1.2:
|
/@tootallnate/once/1.1.2:
|
||||||
resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
|
resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
|
||||||
|
|
@ -655,23 +587,6 @@ packages:
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@trivago/prettier-plugin-sort-imports/3.2.0_prettier@2.5.1:
|
|
||||||
resolution: {integrity: sha512-DnwLe+z8t/dZX5xBbYZV1+C5STkyK/P6SSq3Nk6NXlJZsgvDZX2eN4ND7bMFgGV/NL/YChWzcNf6ziGba1ktQQ==}
|
|
||||||
peerDependencies:
|
|
||||||
prettier: 2.x
|
|
||||||
dependencies:
|
|
||||||
'@babel/core': 7.13.10
|
|
||||||
'@babel/generator': 7.13.9
|
|
||||||
'@babel/parser': 7.14.6
|
|
||||||
'@babel/traverse': 7.13.0
|
|
||||||
'@babel/types': 7.13.0
|
|
||||||
javascript-natural-sort: 0.7.1
|
|
||||||
lodash: 4.17.21
|
|
||||||
prettier: 2.5.1
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@trysound/sax/0.2.0:
|
/@trysound/sax/0.2.0:
|
||||||
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
|
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
@ -684,6 +599,12 @@ packages:
|
||||||
'@types/node': 14.18.10
|
'@types/node': 14.18.10
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/busboy/0.3.2:
|
||||||
|
resolution: {integrity: sha512-iEvdm9Z9KdSs/ozuh1Z7ZsXrOl8F4M/CLMXPZHr3QuJ4d6Bjn+HBMC5EMKpwpAo8oi8iK9GZfFoHaIMrrZgwVw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 14.18.10
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/connect/3.4.35:
|
/@types/connect/3.4.35:
|
||||||
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
|
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -702,6 +623,13 @@ packages:
|
||||||
'@types/ms': 0.7.31
|
'@types/ms': 0.7.31
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/express-fileupload/1.2.2:
|
||||||
|
resolution: {integrity: sha512-sWU1EVFfLsdAginKVrkwTRbRPnbn7dawxEFEBgaRDcpNFCUuksZtASaAKEhqwEIg6fSdeTyI6dIUGl3thhrypg==}
|
||||||
|
dependencies:
|
||||||
|
'@types/busboy': 0.3.2
|
||||||
|
'@types/express': 4.17.13
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/express-serve-static-core/4.17.28:
|
/@types/express-serve-static-core/4.17.28:
|
||||||
resolution: {integrity: sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==}
|
resolution: {integrity: sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -1456,6 +1384,13 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/binary/0.3.0:
|
||||||
|
resolution: {integrity: sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=}
|
||||||
|
dependencies:
|
||||||
|
buffers: 0.1.1
|
||||||
|
chainsaw: 0.1.0
|
||||||
|
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:
|
||||||
|
|
@ -1476,6 +1411,10 @@ packages:
|
||||||
bluebird: 3.7.2
|
bluebird: 3.7.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/bluebird/3.4.7:
|
||||||
|
resolution: {integrity: sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/bluebird/3.7.2:
|
/bluebird/3.7.2:
|
||||||
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
|
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -1494,7 +1433,6 @@ packages:
|
||||||
qs: 6.9.7
|
qs: 6.9.7
|
||||||
raw-body: 2.4.3
|
raw-body: 2.4.3
|
||||||
type-is: 1.6.18
|
type-is: 1.6.18
|
||||||
dev: false
|
|
||||||
|
|
||||||
/boolbase/1.0.0:
|
/boolbase/1.0.0:
|
||||||
resolution: {integrity: sha1-aN/1++YMUes3cl6p4+0xDcwed24=}
|
resolution: {integrity: sha1-aN/1++YMUes3cl6p4+0xDcwed24=}
|
||||||
|
|
@ -1619,12 +1557,22 @@ packages:
|
||||||
/buffer-from/1.1.2:
|
/buffer-from/1.1.2:
|
||||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
|
||||||
|
/buffer-indexof-polyfill/1.0.2:
|
||||||
|
resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==}
|
||||||
|
engines: {node: '>=0.10'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/buffer/5.7.1:
|
/buffer/5.7.1:
|
||||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
base64-js: 1.5.1
|
base64-js: 1.5.1
|
||||||
ieee754: 1.2.1
|
ieee754: 1.2.1
|
||||||
|
|
||||||
|
/buffers/0.1.1:
|
||||||
|
resolution: {integrity: sha1-skV5w77U1tOWru5tmorn9Ugqt7s=}
|
||||||
|
engines: {node: '>=0.2.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/builder-util-runtime/8.9.2:
|
/builder-util-runtime/8.9.2:
|
||||||
resolution: {integrity: sha512-rhuKm5vh7E0aAmT6i8aoSfEjxzdYEFX7zDApK+eNgOhjofnWb74d9SRJv0H/8nsgOkos0TZ4zxW0P8J4N7xQ2A==}
|
resolution: {integrity: sha512-rhuKm5vh7E0aAmT6i8aoSfEjxzdYEFX7zDApK+eNgOhjofnWb74d9SRJv0H/8nsgOkos0TZ4zxW0P8J4N7xQ2A==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
@ -1664,12 +1612,10 @@ packages:
|
||||||
engines: {node: '>=4.5.0'}
|
engines: {node: '>=4.5.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
dicer: 0.3.0
|
dicer: 0.3.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/bytes/3.1.2:
|
/bytes/3.1.2:
|
||||||
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/cache-base/1.0.1:
|
/cache-base/1.0.1:
|
||||||
resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==}
|
resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==}
|
||||||
|
|
@ -1751,6 +1697,12 @@ packages:
|
||||||
resolution: {integrity: sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=}
|
resolution: {integrity: sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/chainsaw/0.1.0:
|
||||||
|
resolution: {integrity: sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=}
|
||||||
|
dependencies:
|
||||||
|
traverse: 0.3.9
|
||||||
|
dev: true
|
||||||
|
|
||||||
/chalk/1.1.3:
|
/chalk/1.1.3:
|
||||||
resolution: {integrity: sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=}
|
resolution: {integrity: sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
@ -2054,7 +2006,6 @@ packages:
|
||||||
/content-type/1.0.4:
|
/content-type/1.0.4:
|
||||||
resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==}
|
resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/convert-source-map/1.8.0:
|
/convert-source-map/1.8.0:
|
||||||
resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==}
|
resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==}
|
||||||
|
|
@ -2342,7 +2293,6 @@ packages:
|
||||||
/depd/1.1.2:
|
/depd/1.1.2:
|
||||||
resolution: {integrity: sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=}
|
resolution: {integrity: sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/destroy/1.0.4:
|
/destroy/1.0.4:
|
||||||
resolution: {integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=}
|
resolution: {integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=}
|
||||||
|
|
@ -2372,7 +2322,6 @@ packages:
|
||||||
engines: {node: '>=4.5.0'}
|
engines: {node: '>=4.5.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
streamsearch: 0.1.2
|
streamsearch: 0.1.2
|
||||||
dev: false
|
|
||||||
|
|
||||||
/didyoumean/1.2.2:
|
/didyoumean/1.2.2:
|
||||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||||
|
|
@ -2536,6 +2485,12 @@ packages:
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/duplexer2/0.1.4:
|
||||||
|
resolution: {integrity: sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=}
|
||||||
|
dependencies:
|
||||||
|
readable-stream: 2.3.7
|
||||||
|
dev: true
|
||||||
|
|
||||||
/duplexer3/0.1.4:
|
/duplexer3/0.1.4:
|
||||||
resolution: {integrity: sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=}
|
resolution: {integrity: sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -2549,7 +2504,6 @@ packages:
|
||||||
|
|
||||||
/ee-first/1.1.1:
|
/ee-first/1.1.1:
|
||||||
resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=}
|
resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ejs/3.1.6:
|
/ejs/3.1.6:
|
||||||
resolution: {integrity: sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==}
|
resolution: {integrity: sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==}
|
||||||
|
|
@ -3166,7 +3120,6 @@ packages:
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
busboy: 0.3.1
|
busboy: 0.3.1
|
||||||
dev: false
|
|
||||||
|
|
||||||
/express/4.17.3:
|
/express/4.17.3:
|
||||||
resolution: {integrity: sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==}
|
resolution: {integrity: sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==}
|
||||||
|
|
@ -3259,6 +3212,14 @@ packages:
|
||||||
/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==}
|
||||||
|
|
||||||
|
/fast-folder-size/1.6.1:
|
||||||
|
resolution: {integrity: sha512-F3tRpfkAzb7TT2JNKaJUglyuRjRa+jelQD94s9OSqkfEeytLmupCqQiD+H2KoIXGtp4pB5m4zNmv5m2Ktcr+LA==}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
unzipper: 0.10.11
|
||||||
|
dev: true
|
||||||
|
|
||||||
/fast-glob/3.2.11:
|
/fast-glob/3.2.11:
|
||||||
resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==}
|
resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==}
|
||||||
engines: {node: '>=8.6.0'}
|
engines: {node: '>=8.6.0'}
|
||||||
|
|
@ -3310,7 +3271,6 @@ packages:
|
||||||
readable-web-to-node-stream: 3.0.2
|
readable-web-to-node-stream: 3.0.2
|
||||||
strtok3: 6.3.0
|
strtok3: 6.3.0
|
||||||
token-types: 4.2.0
|
token-types: 4.2.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/file-uri-to-path/1.0.0:
|
/file-uri-to-path/1.0.0:
|
||||||
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
||||||
|
|
@ -3504,6 +3464,16 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/fstream/1.0.12:
|
||||||
|
resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==}
|
||||||
|
engines: {node: '>=0.6'}
|
||||||
|
dependencies:
|
||||||
|
graceful-fs: 4.2.9
|
||||||
|
inherits: 2.0.4
|
||||||
|
mkdirp: 0.5.5
|
||||||
|
rimraf: 2.7.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ftp/0.3.10:
|
/ftp/0.3.10:
|
||||||
resolution: {integrity: sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=}
|
resolution: {integrity: sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=}
|
||||||
engines: {node: '>=0.8.0'}
|
engines: {node: '>=0.8.0'}
|
||||||
|
|
@ -3878,7 +3848,6 @@ packages:
|
||||||
setprototypeof: 1.2.0
|
setprototypeof: 1.2.0
|
||||||
statuses: 1.5.0
|
statuses: 1.5.0
|
||||||
toidentifier: 1.0.1
|
toidentifier: 1.0.1
|
||||||
dev: false
|
|
||||||
|
|
||||||
/http-proxy-agent/4.0.1:
|
/http-proxy-agent/4.0.1:
|
||||||
resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==}
|
resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==}
|
||||||
|
|
@ -3951,7 +3920,6 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer: 2.1.2
|
safer-buffer: 2.1.2
|
||||||
dev: false
|
|
||||||
|
|
||||||
/iconv-lite/0.6.3:
|
/iconv-lite/0.6.3:
|
||||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||||
|
|
@ -4349,10 +4317,6 @@ packages:
|
||||||
minimatch: 3.1.2
|
minimatch: 3.1.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/javascript-natural-sort/0.7.1:
|
|
||||||
resolution: {integrity: sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/js-base64/2.6.4:
|
/js-base64/2.6.4:
|
||||||
resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
|
resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -4547,6 +4511,10 @@ packages:
|
||||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/listenercount/1.0.1:
|
||||||
|
resolution: {integrity: sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/loader-utils/1.4.0:
|
/loader-utils/1.4.0:
|
||||||
resolution: {integrity: sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==}
|
resolution: {integrity: sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==}
|
||||||
engines: {node: '>=4.0.0'}
|
engines: {node: '>=4.0.0'}
|
||||||
|
|
@ -4674,12 +4642,10 @@ packages:
|
||||||
/media-typer/0.3.0:
|
/media-typer/0.3.0:
|
||||||
resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=}
|
resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/media-typer/1.1.0:
|
/media-typer/1.1.0:
|
||||||
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
|
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/merge-descriptors/1.0.1:
|
/merge-descriptors/1.0.1:
|
||||||
resolution: {integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=}
|
resolution: {integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=}
|
||||||
|
|
@ -4852,7 +4818,6 @@ packages:
|
||||||
token-types: 4.2.0
|
token-types: 4.2.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
|
||||||
|
|
||||||
/nano-css/5.3.4_react-dom@17.0.2+react@17.0.2:
|
/nano-css/5.3.4_react-dom@17.0.2+react@17.0.2:
|
||||||
resolution: {integrity: sha512-wfcviJB6NOxDIDfr7RFn/GlaN7I/Bhe4d39ZRCJ3xvZX60LVe2qZ+rDqM49nm4YT81gAjzS+ZklhKP/Gnfnubg==}
|
resolution: {integrity: sha512-wfcviJB6NOxDIDfr7RFn/GlaN7I/Bhe4d39ZRCJ3xvZX60LVe2qZ+rDqM49nm4YT81gAjzS+ZklhKP/Gnfnubg==}
|
||||||
|
|
@ -5103,7 +5068,6 @@ packages:
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
dependencies:
|
dependencies:
|
||||||
ee-first: 1.1.1
|
ee-first: 1.1.1
|
||||||
dev: false
|
|
||||||
|
|
||||||
/once/1.4.0:
|
/once/1.4.0:
|
||||||
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
|
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
|
||||||
|
|
@ -5303,7 +5267,6 @@ packages:
|
||||||
/peek-readable/4.1.0:
|
/peek-readable/4.1.0:
|
||||||
resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==}
|
resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/pend/1.2.0:
|
/pend/1.2.0:
|
||||||
resolution: {integrity: sha1-elfrVQpng/kRUzH89GY9XI4AelA=}
|
resolution: {integrity: sha1-elfrVQpng/kRUzH89GY9XI4AelA=}
|
||||||
|
|
@ -5592,7 +5555,6 @@ packages:
|
||||||
/qs/6.9.7:
|
/qs/6.9.7:
|
||||||
resolution: {integrity: sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==}
|
resolution: {integrity: sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==}
|
||||||
engines: {node: '>=0.6'}
|
engines: {node: '>=0.6'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/query-string/4.3.4:
|
/query-string/4.3.4:
|
||||||
resolution: {integrity: sha1-u7aTucqRXCMlFbIosaArYJBD2+s=}
|
resolution: {integrity: sha1-u7aTucqRXCMlFbIosaArYJBD2+s=}
|
||||||
|
|
@ -5628,7 +5590,6 @@ packages:
|
||||||
http-errors: 1.8.1
|
http-errors: 1.8.1
|
||||||
iconv-lite: 0.4.24
|
iconv-lite: 0.4.24
|
||||||
unpipe: 1.0.0
|
unpipe: 1.0.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/rc/1.2.8:
|
/rc/1.2.8:
|
||||||
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
||||||
|
|
@ -5798,7 +5759,6 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dependencies:
|
dependencies:
|
||||||
readable-stream: 3.6.0
|
readable-stream: 3.6.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/readdirp/3.6.0:
|
/readdirp/3.6.0:
|
||||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||||
|
|
@ -5992,6 +5952,13 @@ packages:
|
||||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/rimraf/2.7.1:
|
||||||
|
resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
glob: 7.2.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/rimraf/3.0.2:
|
/rimraf/3.0.2:
|
||||||
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
@ -6190,9 +6157,12 @@ packages:
|
||||||
split-string: 3.1.0
|
split-string: 3.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/setimmediate/1.0.5:
|
||||||
|
resolution: {integrity: sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/setprototypeof/1.2.0:
|
/setprototypeof/1.2.0:
|
||||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/shebang-command/2.0.0:
|
/shebang-command/2.0.0:
|
||||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||||
|
|
@ -6427,7 +6397,6 @@ packages:
|
||||||
/statuses/1.5.0:
|
/statuses/1.5.0:
|
||||||
resolution: {integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=}
|
resolution: {integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/stream-counter/1.0.0:
|
/stream-counter/1.0.0:
|
||||||
resolution: {integrity: sha1-kc8lac5NxQYf6816yyY5SloRR1E=}
|
resolution: {integrity: sha1-kc8lac5NxQYf6816yyY5SloRR1E=}
|
||||||
|
|
@ -6437,7 +6406,6 @@ packages:
|
||||||
/streamsearch/0.1.2:
|
/streamsearch/0.1.2:
|
||||||
resolution: {integrity: sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=}
|
resolution: {integrity: sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=}
|
||||||
engines: {node: '>=0.8.0'}
|
engines: {node: '>=0.8.0'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/strict-uri-encode/1.1.0:
|
/strict-uri-encode/1.1.0:
|
||||||
resolution: {integrity: sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=}
|
resolution: {integrity: sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=}
|
||||||
|
|
@ -6524,7 +6492,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tokenizer/token': 0.3.0
|
'@tokenizer/token': 0.3.0
|
||||||
peek-readable: 4.1.0
|
peek-readable: 4.1.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/stylis/4.0.13:
|
/stylis/4.0.13:
|
||||||
resolution: {integrity: sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==}
|
resolution: {integrity: sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==}
|
||||||
|
|
@ -6763,7 +6730,6 @@ packages:
|
||||||
/toidentifier/1.0.1:
|
/toidentifier/1.0.1:
|
||||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||||
engines: {node: '>=0.6'}
|
engines: {node: '>=0.6'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/token-types/4.2.0:
|
/token-types/4.2.0:
|
||||||
resolution: {integrity: sha512-P0rrp4wUpefLncNamWIef62J0v0kQR/GfDVji9WKY7GDCWy5YbVSrKUTam07iWPZQGy0zWNOfstYTykMmPNR7w==}
|
resolution: {integrity: sha512-P0rrp4wUpefLncNamWIef62J0v0kQR/GfDVji9WKY7GDCWy5YbVSrKUTam07iWPZQGy0zWNOfstYTykMmPNR7w==}
|
||||||
|
|
@ -6771,7 +6737,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tokenizer/token': 0.3.0
|
'@tokenizer/token': 0.3.0
|
||||||
ieee754: 1.2.1
|
ieee754: 1.2.1
|
||||||
dev: false
|
|
||||||
|
|
||||||
/tough-cookie/2.5.0:
|
/tough-cookie/2.5.0:
|
||||||
resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
|
resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
|
||||||
|
|
@ -6785,6 +6750,10 @@ packages:
|
||||||
resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=}
|
resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/traverse/0.3.9:
|
||||||
|
resolution: {integrity: sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/traverse/0.6.6:
|
/traverse/0.6.6:
|
||||||
resolution: {integrity: sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=}
|
resolution: {integrity: sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -6866,7 +6835,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
media-typer: 0.3.0
|
media-typer: 0.3.0
|
||||||
mime-types: 2.1.34
|
mime-types: 2.1.34
|
||||||
dev: false
|
|
||||||
|
|
||||||
/typedarray-to-buffer/3.1.5:
|
/typedarray-to-buffer/3.1.5:
|
||||||
resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
|
resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
|
||||||
|
|
@ -6933,7 +6901,6 @@ packages:
|
||||||
/unpipe/1.0.0:
|
/unpipe/1.0.0:
|
||||||
resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=}
|
resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/unplugin-auto-import/0.6.6_rollup@2.70.1+vite@2.8.6:
|
/unplugin-auto-import/0.6.6_rollup@2.70.1+vite@2.8.6:
|
||||||
resolution: {integrity: sha512-x3YxAI9ePoumXOakuS5YJlFkSyAkl5vJlaFZSJhSp75nH5gg8LpqQ/0Gz1/CG/JRRv+xaE1CZpEV161AqFGjEg==}
|
resolution: {integrity: sha512-x3YxAI9ePoumXOakuS5YJlFkSyAkl5vJlaFZSJhSp75nH5gg8LpqQ/0Gz1/CG/JRRv+xaE1CZpEV161AqFGjEg==}
|
||||||
|
|
@ -6996,6 +6963,21 @@ packages:
|
||||||
yaku: 0.16.7
|
yaku: 0.16.7
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/unzipper/0.10.11:
|
||||||
|
resolution: {integrity: sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==}
|
||||||
|
dependencies:
|
||||||
|
big-integer: 1.6.51
|
||||||
|
binary: 0.3.0
|
||||||
|
bluebird: 3.4.7
|
||||||
|
buffer-indexof-polyfill: 1.0.2
|
||||||
|
duplexer2: 0.1.4
|
||||||
|
fstream: 1.0.12
|
||||||
|
graceful-fs: 4.2.9
|
||||||
|
listenercount: 1.0.1
|
||||||
|
readable-stream: 2.3.7
|
||||||
|
setimmediate: 1.0.5
|
||||||
|
dev: true
|
||||||
|
|
||||||
/update-notifier/5.1.0:
|
/update-notifier/5.1.0:
|
||||||
resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==}
|
resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,4 @@ module.exports = {
|
||||||
// Tailwind CSS
|
// Tailwind CSS
|
||||||
plugins: [require('prettier-plugin-tailwindcss')],
|
plugins: [require('prettier-plugin-tailwindcss')],
|
||||||
tailwindConfig: './tailwind.config.js',
|
tailwindConfig: './tailwind.config.js',
|
||||||
|
|
||||||
// Sort import order
|
|
||||||
importOrder: ['^@/(.*)$', '^[./]'],
|
|
||||||
importOrderSeparation: false,
|
|
||||||
importOrderSortSpecifiers: true,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue