From 4cd8cfa5238346fd4128ab236889621da03e1dbb Mon Sep 17 00:00:00 2001 From: abearxiong Date: Tue, 25 Feb 2025 23:32:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9C=AC=E5=9C=B0=E9=9F=B3=E4=B9=90?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.config.json5.example | 1 + package.json | 14 +++++----- pnpm-lock.yaml | 28 +++++++++---------- src/app.ts | 2 ++ src/index.ts | 3 ++ src/libs/local-music-lib.ts | 38 +++++++++++++++++++++++++ src/libs/music-lib.ts | 37 +++++++++++++++++++++++++ src/modules/config.ts | 16 +++++++++++ src/routes/index.ts | 1 + src/routes/music/list.ts | 39 ++++++++++++++++++++++++++ src/services/index.ts | 55 +++++++++++++++++++++++++++++++++++++ src/simple-routes/upload.ts | 39 ++++++++++++++++++++++++++ 12 files changed, 252 insertions(+), 21 deletions(-) create mode 100644 src/libs/local-music-lib.ts create mode 100644 src/libs/music-lib.ts create mode 100644 src/modules/config.ts create mode 100644 src/routes/index.ts create mode 100644 src/routes/music/list.ts create mode 100644 src/services/index.ts create mode 100644 src/simple-routes/upload.ts diff --git a/app.config.json5.example b/app.config.json5.example index 80797d9..0df42c2 100644 --- a/app.config.json5.example +++ b/app.config.json5.example @@ -1,3 +1,4 @@ { port: 3000, + musicDir: '/Users/xion/dev/router-template/music' } \ No newline at end of file diff --git a/package.json b/package.json index f35c5e4..9aa1cd2 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "router", + "name": "music-server", "version": "0.0.1", "description": "", "main": "index.js", @@ -9,12 +9,12 @@ "test": "tsx test/**/*.ts", "dev:watch": "cross-env NODE_ENV=development concurrently -n \"Watch,Dev\" -c \"green,blue\" \"npm run watch\" \"sleep 1 && npm run dev\" ", "build": "rimraf dist && rollup -c rollup.config.mjs", - "deploy": "rsync -avz --delete ./dist/ --exclude='app.config.json5' light:~/apps/router/dist", + "deploy": "rsync -avz --delete ./dist/ --exclude='app.config.json5' light:~/apps/music-server/dist", "clean": "rm -rf dist", - "reload": "ssh light pm2 restart router", + "reload": "ssh light pm2 restart music-server", "pub": "npm run build && npm run deploy && npm run reload", - "deploy:nova": "rsync -avz --delete ./dist/ --exclude='app.config.json5' nova:~/apps/router/dist", - "start": "pm2 start dist/app.mjs --name router" + "deploy:nova": "rsync -avz --delete ./dist/ --exclude='app.config.json5' nova:~/apps/music-server/dist", + "start": "pm2 start dist/app.mjs --name music-server" }, "keywords": [], "author": "abearxiong ", @@ -27,7 +27,7 @@ "src" ], "dependencies": { - "@kevisual/router": "^0.0.6-alpha-5", + "@kevisual/router": "^0.0.6", "dayjs": "^1.11.13", "formidable": "^3.5.2", "json5": "^2.2.3", @@ -44,7 +44,7 @@ "@types/crypto-js": "^4.2.2", "@types/formidable": "^3.4.5", "@types/lodash-es": "^4.17.12", - "@types/node": "^22.13.4", + "@types/node": "^22.13.5", "concurrently": "^9.1.2", "cross-env": "^7.0.3", "nodemon": "^3.1.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2ebaac..7c682c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@kevisual/router': - specifier: ^0.0.6-alpha-5 - version: 0.0.6-alpha-5 + specifier: ^0.0.6 + version: 0.0.6 dayjs: specifier: ^1.11.13 version: 1.11.13 @@ -55,8 +55,8 @@ importers: specifier: ^4.17.12 version: 4.17.12 '@types/node': - specifier: ^22.13.4 - version: 22.13.4 + specifier: ^22.13.5 + version: 22.13.5 concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -261,8 +261,8 @@ packages: '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - '@kevisual/router@0.0.6-alpha-5': - resolution: {integrity: sha512-YT9cxzzFKjWyE05MYlvhuAp16ymgmwThSMHrr2PNbmnZiYgUqm3O4j8cny40lOhZB4Jy/4nQb9Ql2laL+mZ4zg==} + '@kevisual/router@0.0.6': + resolution: {integrity: sha512-7FQUY87Zy5A4V30OAggRbGpO/Asd7SUpnhHv8mlxnSFFTto25xpXmjHYp12mu/HJTsHM7RTaxVEyD1DeP44D2A==} '@kevisual/use-config@1.0.7': resolution: {integrity: sha512-2W1iXdiypugQVgjAz8AGWDVUIcBtegdzLV0FPKq1Rm065yB1EWcI0u0d6qFaAw1RWqtT8o0GT3sR3tzg7nWdjA==} @@ -493,8 +493,8 @@ packages: '@types/node-forge@1.3.11': resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} - '@types/node@22.13.4': - resolution: {integrity: sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==} + '@types/node@22.13.5': + resolution: {integrity: sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -1930,7 +1930,7 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.0': {} - '@kevisual/router@0.0.6-alpha-5': + '@kevisual/router@0.0.6': dependencies: path-to-regexp: 8.2.0 selfsigned: 2.4.1 @@ -2134,16 +2134,16 @@ snapshots: '@types/formidable@3.4.5': dependencies: - '@types/node': 22.13.4 + '@types/node': 22.13.5 '@types/fs-extra@8.1.5': dependencies: - '@types/node': 22.13.4 + '@types/node': 22.13.5 '@types/glob@7.2.0': dependencies: '@types/minimatch': 5.1.2 - '@types/node': 22.13.4 + '@types/node': 22.13.5 '@types/lodash-es@4.17.12': dependencies: @@ -2155,9 +2155,9 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 22.13.4 + '@types/node': 22.13.5 - '@types/node@22.13.4': + '@types/node@22.13.5': dependencies: undici-types: 6.20.0 diff --git a/src/app.ts b/src/app.ts index a6fd21a..cdb5124 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,8 +1,10 @@ import { App } from '@kevisual/router'; import { useContextKey, useContext } from '@kevisual/use-config/context'; +import { MusicService } from './services/index.ts'; const init = () => { return new App(); }; +export const musicService = new MusicService(); export const app = useContextKey('app', init); diff --git a/src/index.ts b/src/index.ts index 6af61ca..7691192 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,11 @@ import { app } from './app.ts'; import { useConfig } from '@kevisual/use-config'; import './demo-route.ts'; +import './routes/index.ts'; +import { middleware } from './simple-routes/upload.ts'; const config = useConfig(); app.listen(config.port, () => { console.log(`server is running at http://localhost:${config.port}`); }); +app.server.on(middleware); diff --git a/src/libs/local-music-lib.ts b/src/libs/local-music-lib.ts new file mode 100644 index 0000000..8bddf23 --- /dev/null +++ b/src/libs/local-music-lib.ts @@ -0,0 +1,38 @@ +import { exec } from 'child_process'; +import path from 'path'; + +export function playMusic(musicPath: string): void { + const musicFilePath = path.resolve(musicPath); + + let command = ''; + switch (process.platform) { + case 'win32': + command = `start ${musicFilePath}`; // Windows + break; + case 'darwin': + command = `afplay ${musicFilePath}`; // macOS + break; + case 'linux': + command = `mpg123 ${musicFilePath}`; // Linux + break; + default: + throw new Error('Unsupported platform'); + } + exec(command); +} + +export function stopMusic(): void { + switch (process.platform) { + case 'win32': + exec('taskkill /IM mpg123.exe /F'); // Windows + break; + case 'darwin': + exec('killall afplay'); // macOS + break; + case 'linux': + exec('killall mpg123'); // Linux + break; + default: + throw new Error('Unsupported platform'); + } +} diff --git a/src/libs/music-lib.ts b/src/libs/music-lib.ts new file mode 100644 index 0000000..8c2adc6 --- /dev/null +++ b/src/libs/music-lib.ts @@ -0,0 +1,37 @@ +import { exec } from 'child_process'; +import path from 'path'; + +export function playMusic(musicPath: string): void { + const musicFilePath = path.resolve(musicPath); + let command = ''; + switch (process.platform) { + case 'win32': + command = `start ${musicFilePath}`; // Windows + break; + case 'darwin': + command = `afplay ${musicFilePath}`; // macOS + break; + case 'linux': + command = `mpg123 ${musicFilePath}`; // Linux + break; + default: + throw new Error('Unsupported platform'); + } + exec(command); +} + +export function stopMusic(): void { + switch (process.platform) { + case 'win32': + exec('taskkill /IM mpg123.exe /F'); // Windows + break; + case 'darwin': + exec('killall afplay'); // macOS + break; + case 'linux': + exec('killall mpg123'); // Linux + break; + default: + throw new Error('Unsupported platform'); + } +} diff --git a/src/modules/config.ts b/src/modules/config.ts new file mode 100644 index 0000000..8670f45 --- /dev/null +++ b/src/modules/config.ts @@ -0,0 +1,16 @@ +import path from 'path'; +import { useConfig } from '@kevisual/use-config'; +import fs from 'fs'; +export const configPath = path.join(process.cwd(), 'app.config.json5'); + +export const saveConfig = (config: any) => { + fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); +}; + +export const getConfig = () => { + return useConfig(); +}; + +export const setConfig = (config: any) => { + saveConfig(config); +}; diff --git a/src/routes/index.ts b/src/routes/index.ts new file mode 100644 index 0000000..215ac97 --- /dev/null +++ b/src/routes/index.ts @@ -0,0 +1 @@ +import './music/list.ts'; \ No newline at end of file diff --git a/src/routes/music/list.ts b/src/routes/music/list.ts new file mode 100644 index 0000000..3e2c64c --- /dev/null +++ b/src/routes/music/list.ts @@ -0,0 +1,39 @@ +import { app, musicService } from '../../app.ts'; + +// 配置音乐文件夹 +// 获取音乐列表 +console.log(`http://localhost:3000/api/router?path=music&key=musicDirSet&musicDir="C:\\Users\\Administrator\\Music"`); +app + .route({ + path: 'music', + key: 'musicDirSet', + }) + .define(async (ctx) => { + const musicDir = ctx.query.musicDir; + console.log('musicDir', musicDir); + + const res = musicService.setMusicDir(musicDir); + if (res.code === 200) { + ctx.body = 'success'; + } else { + throw ctx.throw(res.code, res.message); + } + }) + .addTo(app); + +// 获取音乐列表 + +console.log(`http://localhost:3000/api/router?path=music&key=list`); +app + .route({ + path: 'music', + key: 'list', + }) + .define(async (ctx) => { + const musicList = await musicService.getMusicList(); + ctx.body = { + musicDir: musicService.musicDir, + musicList, + }; + }) + .addTo(app); diff --git a/src/services/index.ts b/src/services/index.ts new file mode 100644 index 0000000..6bacb96 --- /dev/null +++ b/src/services/index.ts @@ -0,0 +1,55 @@ +import { setConfig } from '@/modules/config.ts'; +import { useConfig, fileIsExist } from '@kevisual/use-config'; +import fs from 'fs'; +import path from 'path'; + +// 只获取文件列表 +const getMusicList = async (musicDir: string) => { + const musicList = fs.readdirSync(musicDir); + // 只获取文件列表 + const fileList = musicList.filter((item) => { + return fs.statSync(path.join(musicDir, item)).isFile(); + }); + return fileList; +}; + +const config = useConfig(); +export class MusicService { + musicDir: string; + constructor() { + this.musicDir = config.musicDir || ''; + } + setMusicDir(musicDir: string) { + musicDir = musicDir.replace(/"/g, ''); + const _musicDir = path.resolve(musicDir); + if (fileIsExist(_musicDir)) { + this.musicDir = musicDir; + setConfig({ + musicDir: _musicDir, + }); + return { + code: 200, + message: 'musicDir is set success', + }; + } else { + return { + code: 400, + message: 'musicDir is not exist', + }; + } + } + async getMusicList() { + const musicList = await getMusicList(this.musicDir); + return musicList; + } + getRealPath(file: string) { + // file 如果有空格,则需要转义 + // 如果有"" 则移除 + const realPath = path.join(this.musicDir, file.replace(/"/g, '')); + console.log('realPath', realPath); + if (fileIsExist(realPath)) { + return realPath; + } + return null; + } +} diff --git a/src/simple-routes/upload.ts b/src/simple-routes/upload.ts new file mode 100644 index 0000000..d36ebca --- /dev/null +++ b/src/simple-routes/upload.ts @@ -0,0 +1,39 @@ +import { musicService } from '@/app.ts'; +import { SimpleRouter } from '@kevisual/router/simple'; +import fs from 'fs'; +import http from 'http'; +export const simpleRouter = new SimpleRouter(); + +// http://localhost:3000/files?file="蔡依林 - 倒带.mp3" +simpleRouter.get('/files', async (req, res) => { + const url = req.url; + const urlObj = new URL(url, 'http://localhost:3000'); + const file = urlObj.searchParams.get('file'); + console.log(file); + if (!file) { + res.writeHead(500, { 'Content-Type': 'text/html' }); + res.write('File params is required\n'); + res.end(); + return; + } + if (!musicService.musicDir) { + res.writeHead(500, { 'Content-Type': 'text/html' }); + res.write('Music dir is not set\n'); + res.end(); + return; + } + const realPath = musicService.getRealPath(file); + if (realPath) { + res.writeHead(200, { 'Content-Type': 'audio/mpeg' }); + const readStream = fs.createReadStream(realPath); + readStream.pipe(res); + } else { + res.writeHead(500, { 'Content-Type': 'text/html' }); + res.write('File is not exist\n'); + res.end(); + } +}); + +export const middleware = async (req: http.IncomingMessage, res: http.ServerResponse) => { + return simpleRouter.parse(req, res); +};