Skip to content

Commit b62ccfe

Browse files
iryisJellyBrick
andauthored
feat(amuse): song query api (add amuse plugin) (#2723)
Co-authored-by: JellyBrick <[email protected]>
1 parent 237dde9 commit b62ccfe

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed

src/i18n/resources/en.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,13 @@
279279
},
280280
"name": "Ambient Mode"
281281
},
282+
"amuse": {
283+
"description": "Adds YouTube Music support for the Amuse now playing widget by 6K Labs",
284+
"name": "Amuse",
285+
"response": {
286+
"query": "Amuse API server is running. GET /query to get song info."
287+
}
288+
},
282289
"api-server": {
283290
"description": "Adds an API server to control the player",
284291
"dialog": {

src/plugins/amuse/backend.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { t } from 'i18next';
2+
3+
import { type Context, Hono } from 'hono';
4+
import { cors } from 'hono/cors';
5+
import { serve } from '@hono/node-server';
6+
7+
import registerCallback, { type SongInfo } from '@/providers/song-info';
8+
import { createBackend } from '@/utils';
9+
10+
import type { AmuseSongInfo } from './types';
11+
12+
const amusePort = 9863;
13+
14+
const formatSongInfo = (info: SongInfo) => {
15+
const formattedSongInfo: AmuseSongInfo = {
16+
player: {
17+
hasSong: !!(info.artist && info.title),
18+
isPaused: info.isPaused ?? false,
19+
seekbarCurrentPosition: info.elapsedSeconds ?? 0,
20+
},
21+
track: {
22+
duration: info.songDuration,
23+
title: info.title,
24+
author: info.artist,
25+
cover: info.imageSrc ?? '',
26+
url: info.url ?? '',
27+
id: info.videoId,
28+
isAdvertisement: false,
29+
},
30+
};
31+
return formattedSongInfo;
32+
};
33+
34+
export default createBackend({
35+
currentSongInfo: {} as SongInfo,
36+
app: null as Hono | null,
37+
server: null as ReturnType<typeof serve> | null,
38+
start() {
39+
registerCallback((songInfo) => {
40+
this.currentSongInfo = songInfo;
41+
});
42+
43+
this.app = new Hono();
44+
this.app.use('*', cors());
45+
this.app.get('/', (ctx) =>
46+
ctx.body(t('plugins.amuse.response.query'), 200),
47+
);
48+
49+
const queryAndApiHandler = (ctx: Context) => {
50+
return ctx.json(formatSongInfo(this.currentSongInfo), 200);
51+
};
52+
53+
this.app.get('/query', queryAndApiHandler);
54+
this.app.get('/api', queryAndApiHandler);
55+
56+
try {
57+
this.server = serve({
58+
fetch: this.app.fetch.bind(this.app),
59+
port: amusePort,
60+
});
61+
} catch (err) {
62+
console.error(err);
63+
}
64+
},
65+
66+
stop() {
67+
if (this.server) {
68+
this.server?.close();
69+
}
70+
},
71+
});

src/plugins/amuse/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { createPlugin } from '@/utils';
2+
import backend from './backend';
3+
import { t } from '@/i18n';
4+
5+
export interface MusicWidgetConfig {
6+
enabled: boolean;
7+
}
8+
9+
export const defaultConfig: MusicWidgetConfig = {
10+
enabled: false,
11+
};
12+
13+
export default createPlugin({
14+
name: () => t('plugins.amuse.name'),
15+
description: () => t('plugins.amuse.description'),
16+
addedVersion: '3.7.X',
17+
restartNeeded: true,
18+
config: defaultConfig,
19+
backend,
20+
});

src/plugins/amuse/types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export interface PlayerInfo {
2+
hasSong: boolean;
3+
isPaused: boolean;
4+
seekbarCurrentPosition: number;
5+
}
6+
7+
export interface TrackInfo {
8+
author: string;
9+
title: string;
10+
cover: string;
11+
duration: number;
12+
url: string;
13+
id: string;
14+
isAdvertisement: boolean;
15+
}
16+
17+
export interface AmuseSongInfo {
18+
player: PlayerInfo;
19+
track: TrackInfo;
20+
}

0 commit comments

Comments
 (0)