feat: updates

This commit is contained in:
qier222 2023-01-07 14:39:03 +08:00
parent 884f3df41a
commit c6c59b2cd9
No known key found for this signature in database
84 changed files with 3531 additions and 2616 deletions

View file

@ -3,7 +3,8 @@ import { app } from 'electron'
import fs from 'fs'
import SQLite3 from 'better-sqlite3'
import log from './log'
import { createFileIfNotExist, dirname, isProd } from './utils'
import { createFileIfNotExist, dirname } from './utils'
import { isProd } from './env'
import pkg from '../../../package.json'
import { compare, validate } from 'compare-versions'
import os from 'os'

View file

@ -0,0 +1,6 @@
export const isDev = process.env.NODE_ENV === 'development'
export const isProd = process.env.NODE_ENV === 'production'
export const isWindows = process.platform === 'win32'
export const isMac = process.platform === 'darwin'
export const isLinux = process.platform === 'linux'
export const appName = 'R3Play'

View file

@ -1,12 +1,7 @@
import './preload' // must be first
import './sentry'
import './server'
import {
BrowserWindow,
BrowserWindowConstructorOptions,
app,
shell,
} from 'electron'
import { BrowserWindow, BrowserWindowConstructorOptions, app, shell } from 'electron'
import { release } from 'os'
import { join } from 'path'
import log from './log'
@ -15,7 +10,7 @@ import { createTray, YPMTray } from './tray'
import { IpcChannels } from '@/shared/IpcChannels'
import { createTaskbar, Thumbar } from './windowsTaskbar'
import { createMenu } from './menu'
import { isDev, isWindows, isLinux, isMac } from './utils'
import { isDev, isWindows, isLinux, isMac, appName } from './env'
import store from './store'
// import './surrealdb'
// import Airplay from './airplay'
@ -81,7 +76,7 @@ class Main {
createWindow() {
const options: BrowserWindowConstructorOptions = {
title: 'YesPlayMusic',
title: appName,
webPreferences: {
preload: join(__dirname, 'rendererPreload.js'),
},
@ -93,6 +88,7 @@ class Main {
trafficLightPosition: { x: 24, y: 24 },
frame: false,
transparent: true,
backgroundColor: 'rgba(0, 0, 0, 0)',
show: false,
}
if (store.get('window')) {
@ -132,35 +128,31 @@ class Main {
return headers
}
this.win.webContents.session.webRequest.onBeforeSendHeaders(
(details, callback) => {
const { requestHeaders, url } = details
addCORSHeaders(requestHeaders)
this.win.webContents.session.webRequest.onBeforeSendHeaders((details, callback) => {
const { requestHeaders, url } = details
addCORSHeaders(requestHeaders)
// 不加这几个 header 的话,使用 axios 加载 YouTube 音频会很慢
if (url.includes('googlevideo.com')) {
requestHeaders['Sec-Fetch-Mode'] = 'no-cors'
requestHeaders['Sec-Fetch-Dest'] = 'audio'
requestHeaders['Range'] = 'bytes=0-'
}
callback({ requestHeaders })
// 不加这几个 header 的话,使用 axios 加载 YouTube 音频会很慢
if (url.includes('googlevideo.com')) {
requestHeaders['Sec-Fetch-Mode'] = 'no-cors'
requestHeaders['Sec-Fetch-Dest'] = 'audio'
requestHeaders['Range'] = 'bytes=0-'
}
)
this.win.webContents.session.webRequest.onHeadersReceived(
(details, callback) => {
const { responseHeaders, url } = details
if (url.includes('sentry.io')) {
callback({ responseHeaders })
return
}
if (responseHeaders) {
addCORSHeaders(responseHeaders)
}
callback({ requestHeaders })
})
this.win.webContents.session.webRequest.onHeadersReceived((details, callback) => {
const { responseHeaders, url } = details
if (url.includes('sentry.io')) {
callback({ responseHeaders })
return
}
)
if (responseHeaders) {
addCORSHeaders(responseHeaders)
}
callback({ responseHeaders })
})
}
handleWindowEvents() {
@ -176,13 +168,11 @@ class Main {
})
this.win.on('enter-full-screen', () => {
this.win &&
this.win.webContents.send(IpcChannels.FullscreenStateChange, true)
this.win && this.win.webContents.send(IpcChannels.FullscreenStateChange, true)
})
this.win.on('leave-full-screen', () => {
this.win &&
this.win.webContents.send(IpcChannels.FullscreenStateChange, false)
this.win && this.win.webContents.send(IpcChannels.FullscreenStateChange, false)
})
// Save window position

View file

@ -7,7 +7,7 @@
import log from 'electron-log'
import pc from 'picocolors'
import { isDev } from './utils'
import { isDev } from './env'
Object.assign(console, log.functions)
log.variables.process = 'main'

View file

@ -6,7 +6,8 @@ import {
MenuItemConstructorOptions,
shell,
} from 'electron'
import { logsPath, isMac } from './utils'
import { isMac } from './env'
import { logsPath } from './utils'
import { exec } from 'child_process'
export const createMenu = (win: BrowserWindow) => {

View file

@ -1,10 +1,10 @@
import log from './log'
import { app } from 'electron'
import { isDev } from './env'
import {
createDirIfNotExist,
devUserDataPath,
isDev,
portableUserDataPath,
devUserDataPath,
} from './utils'
if (isDev) {

View file

@ -1,15 +1,15 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import { IpcChannels } from '@/shared/IpcChannels'
import { isLinux, isMac, isProd, isWindows } from './utils'
import { isLinux, isMac, isProd, isWindows } from './env'
const { contextBridge, ipcRenderer } = require('electron')
if (isProd) {
const log = require('electron-log')
log.transports.file.level = 'info'
log.transports.ipc.level = false
log.variables.process = 'renderer'
contextBridge.exposeInMainWorld('log', log)
}
// if (isProd) {
// const log = require('electron-log')
// log.transports.file.level = 'info'
// log.transports.ipc.level = false
// log.variables.process = 'renderer'
// contextBridge.exposeInMainWorld('log', log)
// }
contextBridge.exposeInMainWorld('ipcRenderer', {
invoke: ipcRenderer.invoke,
@ -27,8 +27,7 @@ contextBridge.exposeInMainWorld('ipcRenderer', {
contextBridge.exposeInMainWorld('env', {
isElectron: true,
isEnableTitlebar:
process.platform === 'win32' || process.platform === 'linux',
isEnableTitlebar: process.platform === 'win32' || process.platform === 'linux',
isLinux,
isMac,
isWindows,

View file

@ -1,12 +1,13 @@
import * as Sentry from '@sentry/electron'
import pkg from '../../../package.json'
import { appName } from './env'
import log from './log'
log.info(`[sentry] sentry initializing`)
Sentry.init({
dsn: 'https://2aaaa67f1c3d4d6baefafa5d58fcf340@o436528.ingest.sentry.io/6274637',
release: `yesplaymusic@${pkg.version}`,
release: `${appName}@${pkg.version}`,
environment: process.env.NODE_ENV,
// Set tracesSampleRate to 1.0 to capture 100%

View file

@ -10,7 +10,7 @@ import fs from 'fs'
import { app } from 'electron'
import type { FetchAudioSourceResponse } from '@/shared/api/Track'
import { APIs as CacheAPIs } from '@/shared/CacheAPIs'
import { isProd } from './utils'
import { appName, isProd } from './env'
import { APIs } from '@/shared/CacheAPIs'
import history from 'connect-history-api-fallback'
import { db, Tables } from './db'
@ -19,7 +19,7 @@ class Server {
port = Number(
isProd
? process.env.ELECTRON_WEB_SERVER_PORT || 42710
: process.env.ELECTRON_DEV_NETEASE_API_PORT || 3000
: process.env.ELECTRON_DEV_NETEASE_API_PORT || 30001
)
app = express()
// eslint-disable-next-line @typescript-eslint/no-var-requires
@ -39,9 +39,7 @@ class Server {
neteaseHandler() {
Object.entries(this.netease).forEach(([name, handler]: [string, any]) => {
// 例外处理
if (
['serveNcmApi', 'getModulesDefinitions', APIs.SongUrl].includes(name)
) {
if (['serveNcmApi', 'getModulesDefinitions', APIs.SongUrl].includes(name)) {
return
}
@ -103,7 +101,7 @@ class Server {
{
source: cache.source,
id: cache.id,
url: `http://127.0.0.1:42710/yesplaymusic/audio/${audioFileName}`,
url: `http://127.0.0.1:42710/${appName.toLowerCase()}/audio/${audioFileName}`,
br: cache.br,
size: 0,
md5: '',
@ -137,9 +135,7 @@ class Server {
}
}
const getFromNetease = async (
req: Request
): Promise<FetchAudioSourceResponse | undefined> => {
const getFromNetease = async (req: Request): Promise<FetchAudioSourceResponse | undefined> => {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const getSongUrl = (require('NeteaseCloudMusicApi') as any).song_url
@ -155,12 +151,10 @@ class Server {
// const unmExecutor = new UNM.Executor()
const getFromUNM = async (id: number, req: Request) => {
log.debug('[server] Fetching audio url from UNM')
let track: Track = cache.get(CacheAPIs.Track, { ids: String(id) })
?.songs?.[0]
let track: Track = cache.get(CacheAPIs.Track, { ids: String(id) })?.songs?.[0]
if (!track) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const getSongDetail = (require('NeteaseCloudMusicApi') as any)
.song_detail
const getSongDetail = (require('NeteaseCloudMusicApi') as any).song_detail
track = await getSongDetail({ ...req.query, cookie: req.cookies })
}
@ -184,14 +178,9 @@ class Server {
const sourceList = ['ytdl']
const context = {}
const matchedAudio = await unmExecutor.search(
sourceList,
trackForUNM,
context
)
const matchedAudio = await unmExecutor.search(sourceList, trackForUNM, context)
const retrievedSong = await unmExecutor.retrieve(matchedAudio, context)
const source =
retrievedSong.source === 'ytdl' ? 'youtube' : retrievedSong.source
const source = retrievedSong.source === 'ytdl' ? 'youtube' : retrievedSong.source
if (retrievedSong.url) {
log.debug(
`[server] UMN match: ${matchedAudio.song?.name} (https://youtube.com/v/${matchedAudio.song?.id})`
@ -291,45 +280,38 @@ class Server {
cacheAudioHandler() {
this.app.get(
'/yesplaymusic/audio/:filename',
`/${appName.toLowerCase()}/audio/:filename`,
async (req: Request, res: Response) => {
cache.getAudio(req.params.filename, res)
}
)
this.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 cache.setAudio(req.files.file.data, {
id,
url: String(req.query.url) || '',
})
res.status(200).send('Audio cached!')
} catch (error) {
res.status(500).send({ error })
}
this.app.post(`/${appName.toLowerCase()}/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 cache.setAudio(req.files.file.data, {
id,
url: String(req.query.url) || '',
})
res.status(200).send('Audio cached!')
} catch (error) {
res.status(500).send({ error })
}
})
}
listen() {

View file

@ -9,6 +9,7 @@ import {
} from 'electron'
import { IpcChannels } from '@/shared/IpcChannels'
import { RepeatMode } from '@/shared/playerDataTypes'
import { appName } from './env'
const iconDirRoot =
process.env.NODE_ENV === 'development'
@ -134,7 +135,7 @@ class YPMTrayImpl implements YPMTray {
this._contextMenu = Menu.buildFromTemplate(this._template)
this._updateContextMenu()
this.setTooltip('YesPlayMusic')
this.setTooltip(appName)
this._tray.on('click', () => win.show())
}

View file

@ -2,17 +2,13 @@ import fs from 'fs'
import path from 'path'
import os from 'os'
import pkg from '../../../package.json'
import { appName, isDev } from './env'
export const isDev = process.env.NODE_ENV === 'development'
export const isProd = process.env.NODE_ENV === 'production'
export const isWindows = process.platform === 'win32'
export const isMac = process.platform === 'darwin'
export const isLinux = process.platform === 'linux'
export const dirname = isDev ? process.cwd() : __dirname
export const devUserDataPath = path.resolve(process.cwd(), '../../tmp/userData')
export const portableUserDataPath = path.resolve(
process.env.PORTABLE_EXECUTABLE_DIR || '',
'./YesPlayMusic-UserData'
`./${appName.toLowerCase()}-UserData`
)
export const logsPath = {
linux: `~/.config/${pkg.productName}/logs`,