lcluster 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -9
- package/built-in-templates/all-filters.yml +42 -0
- package/built-in-templates/robust.yml +31 -0
- package/built-in-templates/youtube-only.yml +31 -0
- package/package.json +1 -1
- package/src/cli.js +1 -1
- package/src/main.js +1 -1
- package/src/tui/screens/Dashboard.jsx +1 -1
- package/src/tui/screens/Settings.jsx +5 -6
- package/src/tui/screens/Templates.jsx +88 -12
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<p>A powerful Lavalink cluster manager for your terminal.</p>
|
|
5
5
|
|
|
6
6
|
<p>
|
|
7
|
-
<img src="https://img.shields.io/badge/version-1.0.
|
|
7
|
+
<img src="https://img.shields.io/badge/version-1.0.1-blue" />
|
|
8
8
|
<img src="https://img.shields.io/badge/node-%3E%3D18.0.0-green" />
|
|
9
9
|
<img src="https://img.shields.io/badge/license-GPLv3-purple" />
|
|
10
10
|
<img src="https://img.shields.io/badge/built%20with-Claude%20AI-orange" />
|
|
@@ -111,7 +111,7 @@ Lavalink v4 compatible client.
|
|
|
111
111
|
|
|
112
112
|
```
|
|
113
113
|
╔═════════════════════════════════════════════════════════════════════╗
|
|
114
|
-
║ ⬡ lcluster v1.0.
|
|
114
|
+
║ ⬡ lcluster v1.0.1 ● 2 online ⚠ 1 warn gateway :2333 ● ║
|
|
115
115
|
╚═════════════════════════════════════════════════════════════════════╝
|
|
116
116
|
|
|
117
117
|
┌─ nodes (3/5) ──────────────────────────────────── [↑↓ scroll] ─┐
|
|
@@ -154,13 +154,6 @@ Full architectural guides, TUI maps, setup instructions, and deployment strategi
|
|
|
154
154
|
|
|
155
155
|
---
|
|
156
156
|
|
|
157
|
-
## Roadmap
|
|
158
|
-
|
|
159
|
-
- [x] v1.0.0 — Core cluster manager, TUI dashboard, gateway, alerts
|
|
160
|
-
- [ ] v1.0.1 — Custom Discord bot integration with token support
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
157
|
## Credits
|
|
165
158
|
|
|
166
159
|
lcluster was designed and built by **Ram Krishna** with architecture,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
server:
|
|
2
|
+
port: 2333
|
|
3
|
+
address: 0.0.0.0
|
|
4
|
+
|
|
5
|
+
lavalink:
|
|
6
|
+
server:
|
|
7
|
+
password: "youshallnotpass"
|
|
8
|
+
sources:
|
|
9
|
+
youtube: true
|
|
10
|
+
bandcamp: true
|
|
11
|
+
soundcloud: true
|
|
12
|
+
twitch: true
|
|
13
|
+
vimeo: true
|
|
14
|
+
mixer: true
|
|
15
|
+
http: true
|
|
16
|
+
local: false
|
|
17
|
+
filters:
|
|
18
|
+
volume: true
|
|
19
|
+
equalizer: true
|
|
20
|
+
karaoke: true
|
|
21
|
+
timescale: true
|
|
22
|
+
tremolo: true
|
|
23
|
+
vibrato: true
|
|
24
|
+
distortion: true
|
|
25
|
+
rotation: true
|
|
26
|
+
channelMix: true
|
|
27
|
+
lowPass: true
|
|
28
|
+
bufferDurationMs: 400
|
|
29
|
+
youtubePlaylistLoadLimit: 6
|
|
30
|
+
playerUpdateInterval: 5
|
|
31
|
+
youtubeSearchEnabled: true
|
|
32
|
+
soundcloudSearchEnabled: true
|
|
33
|
+
gc-warnings: true
|
|
34
|
+
|
|
35
|
+
metrics:
|
|
36
|
+
prometheus:
|
|
37
|
+
enabled: false
|
|
38
|
+
endpoint: /metrics
|
|
39
|
+
|
|
40
|
+
sentry:
|
|
41
|
+
dsn: ""
|
|
42
|
+
environment: ""
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
server:
|
|
2
|
+
port: 2333
|
|
3
|
+
address: 0.0.0.0
|
|
4
|
+
|
|
5
|
+
lavalink:
|
|
6
|
+
server:
|
|
7
|
+
password: "youshallnotpass"
|
|
8
|
+
sources:
|
|
9
|
+
youtube: true
|
|
10
|
+
bandcamp: true
|
|
11
|
+
soundcloud: true
|
|
12
|
+
twitch: true
|
|
13
|
+
vimeo: true
|
|
14
|
+
mixer: true
|
|
15
|
+
http: true
|
|
16
|
+
local: false
|
|
17
|
+
bufferDurationMs: 400
|
|
18
|
+
youtubePlaylistLoadLimit: 6
|
|
19
|
+
playerUpdateInterval: 5
|
|
20
|
+
youtubeSearchEnabled: true
|
|
21
|
+
soundcloudSearchEnabled: true
|
|
22
|
+
gc-warnings: true
|
|
23
|
+
|
|
24
|
+
metrics:
|
|
25
|
+
prometheus:
|
|
26
|
+
enabled: false
|
|
27
|
+
endpoint: /metrics
|
|
28
|
+
|
|
29
|
+
sentry:
|
|
30
|
+
dsn: ""
|
|
31
|
+
environment: ""
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
server:
|
|
2
|
+
port: 2333
|
|
3
|
+
address: 0.0.0.0
|
|
4
|
+
|
|
5
|
+
lavalink:
|
|
6
|
+
server:
|
|
7
|
+
password: "youshallnotpass"
|
|
8
|
+
sources:
|
|
9
|
+
youtube: true
|
|
10
|
+
bandcamp: false
|
|
11
|
+
soundcloud: false
|
|
12
|
+
twitch: false
|
|
13
|
+
vimeo: false
|
|
14
|
+
mixer: false
|
|
15
|
+
http: false
|
|
16
|
+
local: false
|
|
17
|
+
bufferDurationMs: 400
|
|
18
|
+
youtubePlaylistLoadLimit: 6
|
|
19
|
+
playerUpdateInterval: 5
|
|
20
|
+
youtubeSearchEnabled: true
|
|
21
|
+
soundcloudSearchEnabled: true
|
|
22
|
+
gc-warnings: true
|
|
23
|
+
|
|
24
|
+
metrics:
|
|
25
|
+
prometheus:
|
|
26
|
+
enabled: false
|
|
27
|
+
endpoint: /metrics
|
|
28
|
+
|
|
29
|
+
sentry:
|
|
30
|
+
dsn: ""
|
|
31
|
+
environment: ""
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
package/src/main.js
CHANGED
|
@@ -17,7 +17,7 @@ export default function Dashboard({ nodes, gatewayPort, uptimeStart, onAction })
|
|
|
17
17
|
<Box flexDirection="column" paddingX={2} paddingY={1} width="100%" height="100%" backgroundColor={theme.background}>
|
|
18
18
|
<Border borderColor={theme.border}>
|
|
19
19
|
<Box flexDirection="row" justifyContent="space-between" width="100%">
|
|
20
|
-
<Text color={theme.accent}> ⬡ lcluster v1.0.
|
|
20
|
+
<Text color={theme.accent}> ⬡ lcluster v1.0.1</Text>
|
|
21
21
|
<Box flexDirection="row" gap={2}>
|
|
22
22
|
<Text color={theme.online}>● {nodes.filter(n => n.status === 'online').length} online</Text>
|
|
23
23
|
<Text color={theme.degraded}>⚠ {nodes.filter(n => n.status === 'degraded' || n.status === 'reconnecting').length} warn</Text>
|
|
@@ -86,8 +86,7 @@ export default function Settings({ onBack }) {
|
|
|
86
86
|
<Box flexDirection="column" width="100%" height="100%" backgroundColor={theme.background}>
|
|
87
87
|
<Border title="settings" borderColor={theme.border} flexGrow={1}>
|
|
88
88
|
<Box flexDirection="column" paddingX={1} marginY={1}>
|
|
89
|
-
|
|
90
|
-
</Box>
|
|
89
|
+
<Text color={theme.textDim}>APPEARANCE</Text>
|
|
91
90
|
<Box marginBottom={1}>
|
|
92
91
|
<Text color={theme.textDim}>Theme </Text>
|
|
93
92
|
<Text color={field === 0 ? theme.text : theme.textDim}>
|
|
@@ -140,15 +139,15 @@ export default function Settings({ onBack }) {
|
|
|
140
139
|
<Box flexDirection="column" paddingX={1} marginTop={1}>
|
|
141
140
|
<Text color={theme.borderDim}>{'─'.repeat(45)}</Text>
|
|
142
141
|
<Box marginTop={1} flexDirection="column">
|
|
143
|
-
<Text color={theme.text} bold>lcluster v1.0.
|
|
142
|
+
<Text color={theme.text} bold>lcluster v1.0.1</Text>
|
|
144
143
|
<Text color={theme.textDim}>Built by <Text color={theme.text}>Ram Krishna</Text> & <Text color={theme.text}>Claude (Anthropic AI)</Text></Text>
|
|
145
144
|
<Text color={theme.textDim}>This project was designed and built with the help of AI.</Text>
|
|
146
145
|
</Box>
|
|
147
146
|
</Box>
|
|
148
147
|
</Border >
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
<Box marginTop={1}>
|
|
149
|
+
<Text color={theme.textDim}>[↑↓] fields [←→] change theme [enter] save [q/esc] cancel</Text>
|
|
150
|
+
</Box>
|
|
152
151
|
</Box >
|
|
153
152
|
);
|
|
154
153
|
}
|
|
@@ -2,18 +2,70 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
3
|
import { getTheme } from '../theme/index.js';
|
|
4
4
|
import Border from '../components/Border.jsx';
|
|
5
|
-
import { listTemplates } from '../../templates/manager.js';
|
|
5
|
+
import { listTemplates, deleteTemplate, saveTemplate } from '../../templates/manager.js';
|
|
6
|
+
import TextInput from 'ink-text-input';
|
|
6
7
|
|
|
7
8
|
export default function Templates({ onBack }) {
|
|
8
9
|
const theme = getTheme();
|
|
9
|
-
const [templates] = useState(() => listTemplates());
|
|
10
|
+
const [templates, setTemplates] = useState(() => listTemplates());
|
|
10
11
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
12
|
+
const [showCreate, setShowCreate] = useState(false);
|
|
13
|
+
const [newName, setNewName] = useState('');
|
|
14
|
+
const [error, setError] = useState('');
|
|
15
|
+
|
|
16
|
+
const refresh = () => setTemplates(listTemplates());
|
|
11
17
|
|
|
12
18
|
useInput((input, key) => {
|
|
19
|
+
if (showCreate) {
|
|
20
|
+
if (key.escape) {
|
|
21
|
+
setShowCreate(false);
|
|
22
|
+
setNewName('');
|
|
23
|
+
setError('');
|
|
24
|
+
}
|
|
25
|
+
if (key.return) {
|
|
26
|
+
if (!newName || newName.length < 2) {
|
|
27
|
+
setError('Name too short');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const finalName = newName.endsWith('.yml') ? newName : newName + '.yml';
|
|
31
|
+
if (templates.find(t => t.name === finalName)) {
|
|
32
|
+
setError('Template already exists');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Create default baseline
|
|
37
|
+
const baseContent = `server:\n port: 2333\n address: 0.0.0.0\n\nlavalink:\n server:\n password: "youshallnotpass"\n sources:\n youtube: true\n`;
|
|
38
|
+
saveTemplate(finalName, baseContent);
|
|
39
|
+
setShowCreate(false);
|
|
40
|
+
setNewName('');
|
|
41
|
+
setError('');
|
|
42
|
+
refresh();
|
|
43
|
+
setSelectedIndex(templates.length); // point back to add new button usually
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
13
48
|
if (key.upArrow) setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
14
49
|
if (key.downArrow) setSelectedIndex(Math.min(templates.length, selectedIndex + 1));
|
|
15
50
|
if (input === 'q' || key.escape) onBack();
|
|
16
|
-
|
|
51
|
+
|
|
52
|
+
// Delete template
|
|
53
|
+
if (input === 'd' && selectedIndex < templates.length) {
|
|
54
|
+
const t = templates[selectedIndex];
|
|
55
|
+
if (t.builtin) {
|
|
56
|
+
setError('Cannot delete built-in template');
|
|
57
|
+
setTimeout(() => setError(''), 2000);
|
|
58
|
+
} else {
|
|
59
|
+
deleteTemplate(t.name);
|
|
60
|
+
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
61
|
+
refresh();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Add new
|
|
66
|
+
if (key.return && selectedIndex === templates.length) {
|
|
67
|
+
setShowCreate(true);
|
|
68
|
+
}
|
|
17
69
|
});
|
|
18
70
|
|
|
19
71
|
return (
|
|
@@ -21,21 +73,45 @@ export default function Templates({ onBack }) {
|
|
|
21
73
|
<Border title="templates" borderColor={theme.border} flexGrow={1}>
|
|
22
74
|
<Box flexDirection="column" paddingX={1} flexGrow={1}>
|
|
23
75
|
{templates.map((t, i) => (
|
|
24
|
-
<Text key={t.name} color={selectedIndex === i ? theme.text : theme.textDim} bold={selectedIndex === i}>
|
|
25
|
-
{selectedIndex === i ? <Text color={theme.accent}>▶ </Text> : ' '}
|
|
76
|
+
<Text key={t.name} color={selectedIndex === i && !showCreate ? theme.text : theme.textDim} bold={selectedIndex === i && !showCreate}>
|
|
77
|
+
{selectedIndex === i && !showCreate ? <Text color={theme.accent}>▶ </Text> : ' '}
|
|
26
78
|
{t.name.padEnd(20)} {t.builtin && <Text color={theme.textDim}>(built-in)</Text>}
|
|
27
79
|
</Text>
|
|
28
80
|
))}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
81
|
+
|
|
82
|
+
{!showCreate && (
|
|
83
|
+
<Box marginTop={1}>
|
|
84
|
+
<Text color={selectedIndex === templates.length ? theme.text : theme.textDim} bold={selectedIndex === templates.length}>
|
|
85
|
+
{selectedIndex === templates.length ? <Text color={theme.accent}>▶ </Text> : ' '}
|
|
86
|
+
+ Add new template
|
|
87
|
+
</Text>
|
|
88
|
+
</Box>
|
|
89
|
+
)}
|
|
90
|
+
|
|
91
|
+
{showCreate && (
|
|
92
|
+
<Box marginTop={1} flexDirection="column">
|
|
93
|
+
<Box>
|
|
94
|
+
<Text color={theme.accent}>▶ </Text>
|
|
95
|
+
<Text color={theme.text}>Name: </Text>
|
|
96
|
+
<TextInput value={newName} onChange={setNewName} focus={showCreate} />
|
|
97
|
+
</Box>
|
|
98
|
+
<Text color={theme.textDim}>Press [Enter] to create, [Esc] to cancel. Edit manually in ~/.lcluster/templates</Text>
|
|
99
|
+
</Box>
|
|
100
|
+
)}
|
|
101
|
+
|
|
102
|
+
{error && (
|
|
103
|
+
<Box marginTop={1}>
|
|
104
|
+
<Text color={theme.error}>⚠ {error}</Text>
|
|
105
|
+
</Box>
|
|
106
|
+
)}
|
|
35
107
|
</Box>
|
|
36
108
|
</Border>
|
|
37
109
|
<Box marginTop={1}>
|
|
38
|
-
|
|
110
|
+
{showCreate ? (
|
|
111
|
+
<Text color={theme.textDim}>[enter] create [esc] cancel</Text>
|
|
112
|
+
) : (
|
|
113
|
+
<Text color={theme.textDim}>[↑↓] navigate [enter] create new [d] delete (user only) [q] back</Text>
|
|
114
|
+
)}
|
|
39
115
|
</Box>
|
|
40
116
|
</Box>
|
|
41
117
|
);
|