Skip to content

Commit

Permalink
feat: 实现了应用设置以及 web 端的录播开始时通知
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiteMinds committed Nov 4, 2022
1 parent 3f66318 commit 753145a
Show file tree
Hide file tree
Showing 28 changed files with 507 additions and 27 deletions.
4 changes: 3 additions & 1 deletion packages/electron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@
"license": "LGPL",
"dependencies": {
"@autorecord/http-server": "workspace:^",
"concurrently": "^7.4.0"
"@autorecord/shared": "workspace:^",
"concurrently": "^7.4.0",
"env-paths": "^2.0.0"
},
"devDependencies": {
"electron": "^20.1.1",
Expand Down
5 changes: 5 additions & 0 deletions packages/electron/src/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import envPaths from 'env-paths'

export const appName = process.env.AppName ?? 'live-auto-record'

export const paths = envPaths(appName, { suffix: '' })
6 changes: 5 additions & 1 deletion packages/electron/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { join } from 'path'
import { app, BrowserWindow } from 'electron'
import { startServer } from '@autorecord/http-server'
import { getSettings, setSettings } from './settings'

const isDev = process.env.npm_lifecycle_event === 'app:dev' ? true : false

startServer()
startServer({
getSettings: async () => getSettings(),
setSettings: async (newSettings) => setSettings(newSettings),
})

function createWindow() {
// Create the browser window.
Expand Down
24 changes: 24 additions & 0 deletions packages/electron/src/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import path from 'path'
import { paths } from './env'
import { readJSONFileSync, writeJSONFileSync } from './utils'

export interface Settings {
notExitOnAllWindowsClosed: boolean
noticeOnRecordStart: boolean
}

const settingsConfigPath = path.join(paths.config, 'settings.json')

const settings = readJSONFileSync<Settings>(settingsConfigPath, {
notExitOnAllWindowsClosed: true,
noticeOnRecordStart: true,
})

export function getSettings(): Settings {
return settings
}

export function setSettings(newSettings: Settings): Settings {
writeJSONFileSync(settingsConfigPath, newSettings)
return newSettings
}
38 changes: 38 additions & 0 deletions packages/electron/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import fs from 'fs'
import path from 'path'

export async function readJSONFile<T = unknown>(
filePath: string,
defaultValue: T
): Promise<T> {
if (!fs.existsSync(filePath)) return defaultValue

const buffer = await fs.promises.readFile(filePath)
return JSON.parse(buffer.toString('utf8')) as T
}

export function readJSONFileSync<T = unknown>(
filePath: string,
defaultValue: T
): T {
if (!fs.existsSync(filePath)) return defaultValue

const buffer = fs.readFileSync(filePath)
return JSON.parse(buffer.toString('utf8')) as T
}

export async function writeJSONFile<T = unknown>(
filePath: string,
json: T
): Promise<void> {
fs.mkdirSync(path.dirname(filePath), { recursive: true })
await fs.promises.writeFile(filePath, JSON.stringify(json))
}

export function writeJSONFileSync<T = unknown>(
filePath: string,
json: T
): void {
fs.mkdirSync(path.dirname(filePath), { recursive: true })
fs.writeFileSync(filePath, JSON.stringify(json))
}
1 change: 1 addition & 0 deletions packages/http-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@autorecord/bilibili-recorder": "^1.0.4",
"@autorecord/douyu-recorder": "^1.0.16",
"@autorecord/manager": "workspace:^",
"@autorecord/shared": "workspace:^",
"cors": "^2.8.5",
"env-paths": "^2.0.0",
"express": "^4.18.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/http-server/src/env.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import envPaths from 'env-paths'

// TODO: 后面得改改
// TODO: 后面得改改,或许应该在 startServer 时注入
export const appName = process.env.AppName ?? 'live-auto-record'

export const paths = envPaths(appName, { suffix: '' })
30 changes: 28 additions & 2 deletions packages/http-server/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import './prepare'
import path from 'path'
import express from 'express'
import morgan from 'morgan'
import cors from 'cors'
import { initRecorderManager } from './manager'
import { router } from './routes'
import { createRouter } from './routes'
import { initDB } from './db'
import { Settings } from '@autorecord/shared'
import { paths } from './env'
import { PickPartial, readJSONFile, writeJSONFile } from './utils'
import { ServerOpts } from './types'

export * from './routes/api_types'

export async function startServer() {
export async function startServer(
opts: PickPartial<ServerOpts, 'getSettings' | 'setSettings'> = {}
) {
const serverOpts: ServerOpts = {
getSettings: opts.getSettings ?? defaultGetSettings,
setSettings: opts.setSettings ?? defaultSetSettings,
}

console.log('initializing db')
await initDB()

Expand All @@ -26,6 +38,7 @@ export async function startServer() {
)

app.use(morgan('default'))
const router = createRouter(serverOpts)
app.use('/api', router)

const port = process.env.PORT ?? 8085
Expand All @@ -34,6 +47,19 @@ export async function startServer() {
})
}

// TODO: Opts 的默认值代码似乎不应该放这里
const settingsConfigPath = path.join(paths.config, 'settings.json')
async function defaultGetSettings() {
return readJSONFile<Settings>(settingsConfigPath, {
notExitOnAllWindowsClosed: true,
noticeOnRecordStart: true,
})
}
async function defaultSetSettings(newSettings: Settings) {
await writeJSONFile(settingsConfigPath, newSettings)
return newSettings
}

const isDirectlyRun = require.main === module
if (isDirectlyRun) {
void startServer()
Expand Down
30 changes: 29 additions & 1 deletion packages/http-server/src/routes/api_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
RecordExtraData,
RecordHandle,
} from '@autorecord/manager'
import { Settings } from '@autorecord/shared'
import { RecordModel } from '../db'
import { RecorderExtra } from '../manager'

Expand Down Expand Up @@ -130,6 +131,18 @@ export namespace API {

export type Resp = string
}

export namespace getSettings {
export interface Args {}

export type Resp = Settings
}

export namespace setSettings {
export type Args = Settings

export type Resp = Settings
}
}

export interface UpdateRecorder {
Expand All @@ -147,4 +160,19 @@ export interface RemoveRecorder {
id: ClientRecorder['id']
}

export type SSEMessage = UpdateRecorder | AddRecorder | RemoveRecorder
export interface RecordStart {
event: 'record_start'
recorder: ClientRecorder
}

export interface SettingsChange {
event: 'settings_change'
settings: Settings
}

export type SSEMessage =
| UpdateRecorder
| AddRecorder
| RemoveRecorder
| RecordStart
| SettingsChange
9 changes: 8 additions & 1 deletion packages/http-server/src/routes/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ function broadcastMessage(msg: SSEMessage) {
* 1. 在 update_recorder 时,对每个不同的 recorderId 做同 tick 内的防抖
*
* TODO: 不知道会不会有内存占用过多的问题
* TODO: 这个函数导出了的话,或许应该与 router 拆分到不同的文件
*/
const scheduleBroadcastMessage = memoizeDebounce(broadcastMessage, 0, {
export const scheduleBroadcastMessage = memoizeDebounce(broadcastMessage, 0, {
resolver: (msg) => {
if (msg.event === 'update_recorder') return msg.recorder.id
return msg
Expand All @@ -42,6 +43,12 @@ recorderManager.on('RecorderRemoved', (recorder) => {
id: recorder.id,
})
})
recorderManager.on('RecordStart', ({ recorder }) => {
scheduleBroadcastMessage({
event: 'record_start',
recorder: recorderToClient(recorder),
})
})

const router = Router()
router.get('/events', sse.init)
Expand Down
34 changes: 20 additions & 14 deletions packages/http-server/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,30 @@ import { router as recorderRoutes } from './recorder'
import { router as recordRoutes } from './record'
import { router as managerRoutes } from './manager'
import { router as eventRoutes } from './event'
import { createRouter as createSettingRouter } from './setting'
import { ServerOpts } from '../types'
// import { respond } from './utils'

const router = Router()
export function createRouter(serverOpts: ServerOpts) {
const router = Router()

router.use(recorderRoutes)
router.use(recordRoutes)
router.use(managerRoutes)
router.use(eventRoutes)
router.use(recorderRoutes)
router.use(recordRoutes)
router.use(managerRoutes)
router.use(eventRoutes)
const settingRoutes = createSettingRouter(serverOpts)
router.use(settingRoutes)

const handle: ErrorRequestHandler = (err: unknown, req, res, next) => {
console.error(err)
if (err instanceof Error) {
// respond(res, { error: err.message }).status(500)
return
const handle: ErrorRequestHandler = (err: unknown, req, res, next) => {
console.error(err)
if (err instanceof Error) {
// respond(res, { error: err.message }).status(500)
return
}

// respond(res, { error: String(err) }).status(500)
}
router.use(handle)

// respond(res, { error: String(err) }).status(500)
return router
}
router.use(handle)

export { router }
42 changes: 42 additions & 0 deletions packages/http-server/src/routes/setting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Router } from 'express'
import { Settings } from '@autorecord/shared'
import { API } from './api_types'
import { ServerOpts } from '../types'
import { scheduleBroadcastMessage } from './event'

export function createRouter(serverOpts: ServerOpts) {
const router = Router()

async function getSettings(
args: API.getSettings.Args
): Promise<API.getSettings.Resp> {
return serverOpts.getSettings()
}

async function setSettings(
args: API.setSettings.Args
): Promise<API.setSettings.Resp> {
const newSettings: Settings = args
return serverOpts.setSettings(newSettings)
}

router
.route('/settings')
.get(async (req, res) => {
res.json({ payload: await getSettings({}) })
})
.put(async (req, res) => {
// TODO: 这里先不做 schema 校验,以后再加
const args = req.body as API.setSettings.Args
const newSettings = await setSettings(args)

res.json({ payload: newSettings })

scheduleBroadcastMessage({
event: 'settings_change',
settings: newSettings,
})
})

return router
}
6 changes: 6 additions & 0 deletions packages/http-server/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Settings } from '@autorecord/shared'

export interface ServerOpts {
getSettings: () => Promise<Settings>
setSettings: (newSettings: Settings) => Promise<Settings>
}
3 changes: 3 additions & 0 deletions packages/http-server/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import {
throttle,
} from 'lodash'

export type PickPartial<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> &
Partial<Pick<T, K>>

export function assert(assertion: unknown, msg?: string): asserts assertion {
if (!assertion) {
throw new Error(msg)
Expand Down
3 changes: 2 additions & 1 deletion packages/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@types/lodash": "^4.14.186",
"@types/ramda": "^0.28.15",
"@types/string-template": "^1.0.2",
"@types/uuid": "^8.3.4"
"@types/uuid": "^8.3.4",
"typescript": "^4.8.3"
}
}
20 changes: 20 additions & 0 deletions packages/shared/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@autorecord/shared",
"version": "0.0.1",
"description": "internal utils shared across @autorecord packages",
"main": "./lib/index.js",
"private": true,
"scripts": {
"build": "tsc",
"watch": "tsc -w"
},
"files": [
"lib"
],
"repository": "https://github.com/WhiteMinds/LiveAutoRecord",
"author": "WhiteMind",
"license": "LGPL",
"devDependencies": {
"typescript": "^4.8.3"
}
}
4 changes: 4 additions & 0 deletions packages/shared/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Settings {
notExitOnAllWindowsClosed: boolean
noticeOnRecordStart: boolean
}
Loading

0 comments on commit 753145a

Please sign in to comment.