ziplayer 0.1.3 → 0.1.4
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 +212 -212
- package/dist/plugins/SoundCloudPlugin.d.ts +22 -0
- package/dist/plugins/SoundCloudPlugin.d.ts.map +1 -0
- package/dist/plugins/SoundCloudPlugin.js +171 -0
- package/dist/plugins/SoundCloudPlugin.js.map +1 -0
- package/dist/plugins/SpotifyPlugin.d.ts +26 -0
- package/dist/plugins/SpotifyPlugin.d.ts.map +1 -0
- package/dist/plugins/SpotifyPlugin.js +183 -0
- package/dist/plugins/SpotifyPlugin.js.map +1 -0
- package/dist/plugins/YouTubePlugin.d.ts +25 -0
- package/dist/plugins/YouTubePlugin.d.ts.map +1 -0
- package/dist/plugins/YouTubePlugin.js +314 -0
- package/dist/plugins/YouTubePlugin.js.map +1 -0
- package/dist/structures/Player.d.ts +23 -13
- package/dist/structures/Player.d.ts.map +1 -1
- package/dist/structures/Player.js +129 -54
- package/dist/structures/Player.js.map +1 -1
- package/package.json +45 -45
- package/src/extensions/BaseExtension.ts +35 -35
- package/src/extensions/index.ts +32 -32
- package/src/index.ts +16 -16
- package/src/plugins/BasePlugin.ts +26 -26
- package/src/plugins/index.ts +32 -32
- package/src/structures/Player.ts +1828 -1747
- package/src/structures/PlayerManager.ts +411 -411
- package/src/structures/Queue.ts +354 -354
- package/src/types/index.ts +470 -470
- package/src/utils/timeout.ts +10 -10
- package/tsconfig.json +23 -23
package/README.md
CHANGED
|
@@ -1,212 +1,212 @@
|
|
|
1
|
-
<img width="1175" height="305" alt="logo" src="https://
|
|
2
|
-
|
|
3
|
-
# ziplayer
|
|
4
|
-
|
|
5
|
-
A modular Discord voice player with plugin system for @discordjs/voice.
|
|
6
|
-
|
|
7
|
-
## Features
|
|
8
|
-
|
|
9
|
-
- 🎵 **Plugin-based architecture** - Easy to extend with new sources
|
|
10
|
-
- 🎶 **Multiple source support** - YouTube, SoundCloud, Spotify (with fallback)
|
|
11
|
-
- 🔊 **Queue management** - Add, remove, shuffle, clear
|
|
12
|
-
- 🎚️ **Volume control** - 0-200% volume range
|
|
13
|
-
- ⏯️ **Playback control** - Play, pause, resume, stop, skip
|
|
14
|
-
- 🔁 **Auto play** - Automatically replay the queue when it ends
|
|
15
|
-
- 🔂 **Loop control** - Repeat a single track or the entire queue
|
|
16
|
-
- 📊 **Progress bar** - Display playback progress with customizable icons
|
|
17
|
-
- 🔔 **Event-driven** - Rich event system for all player actions
|
|
18
|
-
- 🎭 **Multi-guild support** - Manage players across multiple Discord servers
|
|
19
|
-
- 🗃️ **User data** - Attach custom data to each player for later use
|
|
20
|
-
- 🔌 **Lavalink** - Support manage an external Lavalink JVM node
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npm install ziplayer @ziplayer/plugin @ziplayer/extension @discordjs/voice discord.js
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Quick Start
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
import { PlayerManager } from "ziplayer";
|
|
32
|
-
import { SoundCloudPlugin, YouTubePlugin, SpotifyPlugin } from "@ziplayer/plugin";
|
|
33
|
-
import { voiceExt } from "@ziplayer/extension";
|
|
34
|
-
|
|
35
|
-
const manager = new PlayerManager({
|
|
36
|
-
plugins: [new SoundCloudPlugin(), new YouTubePlugin(), new SpotifyPlugin()],
|
|
37
|
-
extensions: [
|
|
38
|
-
new voiceExt(null, {
|
|
39
|
-
lang: "vi-VN",
|
|
40
|
-
minimalVoiceMessageDuration: 1,
|
|
41
|
-
postSilenceDelayMs: 2000,
|
|
42
|
-
}),
|
|
43
|
-
],
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// Create player
|
|
47
|
-
const player = manager.create(guildId, {
|
|
48
|
-
leaveOnEnd: true,
|
|
49
|
-
leaveTimeout: 30000,
|
|
50
|
-
userdata: { channel: textChannel }, // store channel for events
|
|
51
|
-
// Choose extensions for this player (by name or instances)
|
|
52
|
-
extensions: ["voiceExt"],
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Connect and play
|
|
56
|
-
await player.connect(voiceChannel);
|
|
57
|
-
await player.play("Never Gonna Give You Up", userId);
|
|
58
|
-
|
|
59
|
-
// Play a full YouTube playlist
|
|
60
|
-
await player.play("https://www.youtube.com/playlist?list=PL123", userId);
|
|
61
|
-
|
|
62
|
-
// Enable autoplay
|
|
63
|
-
player.queue.autoPlay(true);
|
|
64
|
-
|
|
65
|
-
// Play a full SoundCloud playlist
|
|
66
|
-
await player.play("https://soundcloud.com/artist/sets/playlist", userId);
|
|
67
|
-
|
|
68
|
-
// Events
|
|
69
|
-
player.on("willPlay", (player, track) => {
|
|
70
|
-
console.log(`Up next: ${track.title}`);
|
|
71
|
-
});
|
|
72
|
-
player.on("trackStart", (player, track) => {
|
|
73
|
-
console.log(`Now playing: ${track.title}`);
|
|
74
|
-
player.userdata?.channel?.send(`Now playing: ${track.title}`);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// Receive transcripts
|
|
78
|
-
manager.on("voiceCreate", (player, evt) => {
|
|
79
|
-
console.log(`User ${evt.userId} said: ${evt.content}`);
|
|
80
|
-
});
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### TTS (Interrupt Mode)
|
|
84
|
-
|
|
85
|
-
Play short text-to-speech messages without losing music progress. The player pauses music, plays TTS on a dedicated AudioPlayer,
|
|
86
|
-
then resumes.
|
|
87
|
-
|
|
88
|
-
- Requirements: `@ziplayer/plugin` with `TTSPlugin` installed and registered in `PlayerManager`.
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
import { PlayerManager } from "ziplayer";
|
|
92
|
-
import { TTSPlugin, YouTubePlugin, SoundCloudPlugin, SpotifyPlugin } from "@ziplayer/plugin";
|
|
93
|
-
|
|
94
|
-
const manager = new PlayerManager({
|
|
95
|
-
plugins: [new TTSPlugin({ defaultLang: "vi" }), new YouTubePlugin(), new SoundCloudPlugin(), new SpotifyPlugin()],
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// Create a player with TTS interrupt enabled
|
|
99
|
-
const player = manager.create(guildId, {
|
|
100
|
-
tts: {
|
|
101
|
-
createPlayer: true, // pre-create the internal TTS AudioPlayer
|
|
102
|
-
interrupt: true, // pause music, swap to TTS, then resume
|
|
103
|
-
volume: 1, // 1 => 100%
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
await player.connect(voiceChannel);
|
|
108
|
-
|
|
109
|
-
// Trigger TTS by playing a TTS query (depends on your TTS plugin)
|
|
110
|
-
await player.play("tts: xin chào mọi người", userId);
|
|
111
|
-
|
|
112
|
-
// Listen to TTS lifecycle events
|
|
113
|
-
manager.on("ttsStart", (plr, { track }) => console.log("TTS start", track?.title));
|
|
114
|
-
manager.on("ttsEnd", (plr) => console.log("TTS end"));
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Notes
|
|
118
|
-
|
|
119
|
-
- The detection uses track.source that includes "tts" or query starting with `tts:`.
|
|
120
|
-
- If you need more control, call `player.interruptWithTTSTrack(track)` after building a TTS track via your plugin.
|
|
121
|
-
- For CPU-heavy TTS generation, consider offloading to `worker_threads` or a separate process and pass a stream/buffer to the
|
|
122
|
-
plugin.
|
|
123
|
-
|
|
124
|
-
### Player Lifecycle Overview
|
|
125
|
-
|
|
126
|
-
```
|
|
127
|
-
PlayerManager.create(guild, opts)
|
|
128
|
-
│
|
|
129
|
-
▼
|
|
130
|
-
[Player constructor]
|
|
131
|
-
- setup event listeners
|
|
132
|
-
- freeze ExtensionContext { player, manager }
|
|
133
|
-
- register plugins
|
|
134
|
-
│
|
|
135
|
-
▼
|
|
136
|
-
attachExtension(ext)
|
|
137
|
-
- set ext.player
|
|
138
|
-
- ext.onRegister?(context)
|
|
139
|
-
- ext.active?(...) → false ⇒ detach
|
|
140
|
-
│
|
|
141
|
-
▼
|
|
142
|
-
player.play(query, by)
|
|
143
|
-
- runBeforePlayHooks → extensions may mutate query/tracks/start Lavalink
|
|
144
|
-
- resolve track list / queue updates / TTS interrupt check
|
|
145
|
-
- extensionsProvideStream → extension stream overrides plugin pipeline
|
|
146
|
-
- plugin.getStream / getFallback
|
|
147
|
-
│
|
|
148
|
-
▼
|
|
149
|
-
Audio playback
|
|
150
|
-
- trackStart / queue events emitted
|
|
151
|
-
- runAfterPlayHooks with final outcome
|
|
152
|
-
│
|
|
153
|
-
▼
|
|
154
|
-
player.destroy()
|
|
155
|
-
- stop audio/voice / clear queue & plugins
|
|
156
|
-
- ext.onDestroy?(context) for each attached extension
|
|
157
|
-
- emit playerDestroy & cleanup references
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
This diagram shows how custom extensions (voice, lyrics, Lavalink, etc.) integrate across the full player lifecycle and where
|
|
161
|
-
their hooks are invoked.
|
|
162
|
-
|
|
163
|
-
### Lavalink Process
|
|
164
|
-
|
|
165
|
-
Use `lavalinkExt` when you need ZiPlayer to manage an external Lavalink JVM node. The extension starts, stops, and optionally
|
|
166
|
-
restarts the Lavalink jar and forwards lifecycle events through the manager/player.
|
|
167
|
-
|
|
168
|
-
```ts
|
|
169
|
-
import { PlayerManager } from "ziplayer";
|
|
170
|
-
import { lavalinkExt } from "@ziplayer/extension";
|
|
171
|
-
|
|
172
|
-
const lavalink = new lavalinkExt(null, {
|
|
173
|
-
nodes: [
|
|
174
|
-
{
|
|
175
|
-
identifier: "locallavalink",
|
|
176
|
-
password: "youshallnotpass",
|
|
177
|
-
host: "localhost",
|
|
178
|
-
port: 2333,
|
|
179
|
-
secure: false,
|
|
180
|
-
},
|
|
181
|
-
],
|
|
182
|
-
client: client,
|
|
183
|
-
searchPrefix: "scsearch",
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
const manager = new PlayerManager({
|
|
187
|
-
extensions: ["lavalinkExt"],
|
|
188
|
-
});
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
## Events
|
|
192
|
-
|
|
193
|
-
All player events are forwarded through the PlayerManager:
|
|
194
|
-
|
|
195
|
-
- `trackStart` - When a track starts playing
|
|
196
|
-
- `willPlay` - Before a track begins playing
|
|
197
|
-
- `trackEnd` - When a track finishes
|
|
198
|
-
- `queueEnd` - When the queue is empty
|
|
199
|
-
- `playerError` - When an error occurs
|
|
200
|
-
- `queueAdd` - When a track is added
|
|
201
|
-
- `volumeChange` - When volume changes
|
|
202
|
-
- And more...
|
|
203
|
-
|
|
204
|
-
## Useful Links
|
|
205
|
-
|
|
206
|
-
[Example](https://github.com/ZiProject/ZiPlayer/tree/main/examples) | [Repo](https://github.com/ZiProject/ZiPlayer) |
|
|
207
|
-
[Package](https://www.npmjs.com/package/ziplayer) | [Plugin](https://www.npmjs.com/package/@ziplayer/plugin) |
|
|
208
|
-
[Extension](https://www.npmjs.com/package/@ziplayer/extension)
|
|
209
|
-
|
|
210
|
-
## License
|
|
211
|
-
|
|
212
|
-
MIT License
|
|
1
|
+
<img width="1175" height="305" alt="logo" src="https://raw.githubusercontent.com/ZiProject/ZiPlayer/refs/heads/main/publish/logo.png" />
|
|
2
|
+
|
|
3
|
+
# ziplayer
|
|
4
|
+
|
|
5
|
+
A modular Discord voice player with plugin system for @discordjs/voice.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🎵 **Plugin-based architecture** - Easy to extend with new sources
|
|
10
|
+
- 🎶 **Multiple source support** - YouTube, SoundCloud, Spotify (with fallback)
|
|
11
|
+
- 🔊 **Queue management** - Add, remove, shuffle, clear
|
|
12
|
+
- 🎚️ **Volume control** - 0-200% volume range
|
|
13
|
+
- ⏯️ **Playback control** - Play, pause, resume, stop, skip
|
|
14
|
+
- 🔁 **Auto play** - Automatically replay the queue when it ends
|
|
15
|
+
- 🔂 **Loop control** - Repeat a single track or the entire queue
|
|
16
|
+
- 📊 **Progress bar** - Display playback progress with customizable icons
|
|
17
|
+
- 🔔 **Event-driven** - Rich event system for all player actions
|
|
18
|
+
- 🎭 **Multi-guild support** - Manage players across multiple Discord servers
|
|
19
|
+
- 🗃️ **User data** - Attach custom data to each player for later use
|
|
20
|
+
- 🔌 **Lavalink** - Support manage an external Lavalink JVM node
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install ziplayer @ziplayer/plugin @ziplayer/extension @discordjs/voice discord.js
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { PlayerManager } from "ziplayer";
|
|
32
|
+
import { SoundCloudPlugin, YouTubePlugin, SpotifyPlugin } from "@ziplayer/plugin";
|
|
33
|
+
import { voiceExt } from "@ziplayer/extension";
|
|
34
|
+
|
|
35
|
+
const manager = new PlayerManager({
|
|
36
|
+
plugins: [new SoundCloudPlugin(), new YouTubePlugin(), new SpotifyPlugin()],
|
|
37
|
+
extensions: [
|
|
38
|
+
new voiceExt(null, {
|
|
39
|
+
lang: "vi-VN",
|
|
40
|
+
minimalVoiceMessageDuration: 1,
|
|
41
|
+
postSilenceDelayMs: 2000,
|
|
42
|
+
}),
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Create player
|
|
47
|
+
const player = await manager.create(guildId, {
|
|
48
|
+
leaveOnEnd: true,
|
|
49
|
+
leaveTimeout: 30000,
|
|
50
|
+
userdata: { channel: textChannel }, // store channel for events
|
|
51
|
+
// Choose extensions for this player (by name or instances)
|
|
52
|
+
extensions: ["voiceExt"],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Connect and play
|
|
56
|
+
await player.connect(voiceChannel);
|
|
57
|
+
await player.play("Never Gonna Give You Up", userId);
|
|
58
|
+
|
|
59
|
+
// Play a full YouTube playlist
|
|
60
|
+
await player.play("https://www.youtube.com/playlist?list=PL123", userId);
|
|
61
|
+
|
|
62
|
+
// Enable autoplay
|
|
63
|
+
player.queue.autoPlay(true);
|
|
64
|
+
|
|
65
|
+
// Play a full SoundCloud playlist
|
|
66
|
+
await player.play("https://soundcloud.com/artist/sets/playlist", userId);
|
|
67
|
+
|
|
68
|
+
// Events
|
|
69
|
+
player.on("willPlay", (player, track) => {
|
|
70
|
+
console.log(`Up next: ${track.title}`);
|
|
71
|
+
});
|
|
72
|
+
player.on("trackStart", (player, track) => {
|
|
73
|
+
console.log(`Now playing: ${track.title}`);
|
|
74
|
+
player.userdata?.channel?.send(`Now playing: ${track.title}`);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Receive transcripts
|
|
78
|
+
manager.on("voiceCreate", (player, evt) => {
|
|
79
|
+
console.log(`User ${evt.userId} said: ${evt.content}`);
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### TTS (Interrupt Mode)
|
|
84
|
+
|
|
85
|
+
Play short text-to-speech messages without losing music progress. The player pauses music, plays TTS on a dedicated AudioPlayer,
|
|
86
|
+
then resumes.
|
|
87
|
+
|
|
88
|
+
- Requirements: `@ziplayer/plugin` with `TTSPlugin` installed and registered in `PlayerManager`.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { PlayerManager } from "ziplayer";
|
|
92
|
+
import { TTSPlugin, YouTubePlugin, SoundCloudPlugin, SpotifyPlugin } from "@ziplayer/plugin";
|
|
93
|
+
|
|
94
|
+
const manager = new PlayerManager({
|
|
95
|
+
plugins: [new TTSPlugin({ defaultLang: "vi" }), new YouTubePlugin(), new SoundCloudPlugin(), new SpotifyPlugin()],
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Create a player with TTS interrupt enabled
|
|
99
|
+
const player = await manager.create(guildId, {
|
|
100
|
+
tts: {
|
|
101
|
+
createPlayer: true, // pre-create the internal TTS AudioPlayer
|
|
102
|
+
interrupt: true, // pause music, swap to TTS, then resume
|
|
103
|
+
volume: 1, // 1 => 100%
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
await player.connect(voiceChannel);
|
|
108
|
+
|
|
109
|
+
// Trigger TTS by playing a TTS query (depends on your TTS plugin)
|
|
110
|
+
await player.play("tts: xin chào mọi người", userId);
|
|
111
|
+
|
|
112
|
+
// Listen to TTS lifecycle events
|
|
113
|
+
manager.on("ttsStart", (plr, { track }) => console.log("TTS start", track?.title));
|
|
114
|
+
manager.on("ttsEnd", (plr) => console.log("TTS end"));
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Notes
|
|
118
|
+
|
|
119
|
+
- The detection uses track.source that includes "tts" or query starting with `tts:`.
|
|
120
|
+
- If you need more control, call `player.interruptWithTTSTrack(track)` after building a TTS track via your plugin.
|
|
121
|
+
- For CPU-heavy TTS generation, consider offloading to `worker_threads` or a separate process and pass a stream/buffer to the
|
|
122
|
+
plugin.
|
|
123
|
+
|
|
124
|
+
### Player Lifecycle Overview
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
PlayerManager.create(guild, opts)
|
|
128
|
+
│
|
|
129
|
+
▼
|
|
130
|
+
[Player constructor]
|
|
131
|
+
- setup event listeners
|
|
132
|
+
- freeze ExtensionContext { player, manager }
|
|
133
|
+
- register plugins
|
|
134
|
+
│
|
|
135
|
+
▼
|
|
136
|
+
attachExtension(ext)
|
|
137
|
+
- set ext.player
|
|
138
|
+
- ext.onRegister?(context)
|
|
139
|
+
- ext.active?(...) → false ⇒ detach
|
|
140
|
+
│
|
|
141
|
+
▼
|
|
142
|
+
player.play(query, by)
|
|
143
|
+
- runBeforePlayHooks → extensions may mutate query/tracks/start Lavalink
|
|
144
|
+
- resolve track list / queue updates / TTS interrupt check
|
|
145
|
+
- extensionsProvideStream → extension stream overrides plugin pipeline
|
|
146
|
+
- plugin.getStream / getFallback
|
|
147
|
+
│
|
|
148
|
+
▼
|
|
149
|
+
Audio playback
|
|
150
|
+
- trackStart / queue events emitted
|
|
151
|
+
- runAfterPlayHooks with final outcome
|
|
152
|
+
│
|
|
153
|
+
▼
|
|
154
|
+
player.destroy()
|
|
155
|
+
- stop audio/voice / clear queue & plugins
|
|
156
|
+
- ext.onDestroy?(context) for each attached extension
|
|
157
|
+
- emit playerDestroy & cleanup references
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
This diagram shows how custom extensions (voice, lyrics, Lavalink, etc.) integrate across the full player lifecycle and where
|
|
161
|
+
their hooks are invoked.
|
|
162
|
+
|
|
163
|
+
### Lavalink Process
|
|
164
|
+
|
|
165
|
+
Use `lavalinkExt` when you need ZiPlayer to manage an external Lavalink JVM node. The extension starts, stops, and optionally
|
|
166
|
+
restarts the Lavalink jar and forwards lifecycle events through the manager/player.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { PlayerManager } from "ziplayer";
|
|
170
|
+
import { lavalinkExt } from "@ziplayer/extension";
|
|
171
|
+
|
|
172
|
+
const lavalink = new lavalinkExt(null, {
|
|
173
|
+
nodes: [
|
|
174
|
+
{
|
|
175
|
+
identifier: "locallavalink",
|
|
176
|
+
password: "youshallnotpass",
|
|
177
|
+
host: "localhost",
|
|
178
|
+
port: 2333,
|
|
179
|
+
secure: false,
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
client: client,
|
|
183
|
+
searchPrefix: "scsearch",
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const manager = new PlayerManager({
|
|
187
|
+
extensions: ["lavalinkExt"],
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Events
|
|
192
|
+
|
|
193
|
+
All player events are forwarded through the PlayerManager:
|
|
194
|
+
|
|
195
|
+
- `trackStart` - When a track starts playing
|
|
196
|
+
- `willPlay` - Before a track begins playing
|
|
197
|
+
- `trackEnd` - When a track finishes
|
|
198
|
+
- `queueEnd` - When the queue is empty
|
|
199
|
+
- `playerError` - When an error occurs
|
|
200
|
+
- `queueAdd` - When a track is added
|
|
201
|
+
- `volumeChange` - When volume changes
|
|
202
|
+
- And more...
|
|
203
|
+
|
|
204
|
+
## Useful Links
|
|
205
|
+
|
|
206
|
+
[Example](https://github.com/ZiProject/ZiPlayer/tree/main/examples) | [Repo](https://github.com/ZiProject/ZiPlayer) |
|
|
207
|
+
[Package](https://www.npmjs.com/package/ziplayer) | [Plugin](https://www.npmjs.com/package/@ziplayer/plugin) |
|
|
208
|
+
[Extension](https://www.npmjs.com/package/@ziplayer/extension)
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT License
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BasePlugin } from "./BasePlugin";
|
|
2
|
+
import { Track, SearchResult, StreamInfo } from "../types";
|
|
3
|
+
export declare class SoundCloudPlugin extends BasePlugin {
|
|
4
|
+
name: string;
|
|
5
|
+
version: string;
|
|
6
|
+
private client;
|
|
7
|
+
private ready;
|
|
8
|
+
constructor();
|
|
9
|
+
private init;
|
|
10
|
+
canHandle(query: string): boolean;
|
|
11
|
+
validate(url: string): boolean;
|
|
12
|
+
search(query: string, requestedBy: string): Promise<SearchResult>;
|
|
13
|
+
getStream(track: Track): Promise<StreamInfo>;
|
|
14
|
+
getRelatedTracks(trackURL: string | number, opts?: {
|
|
15
|
+
limit?: number;
|
|
16
|
+
offset?: number;
|
|
17
|
+
history?: Track[];
|
|
18
|
+
}): Promise<Track[]>;
|
|
19
|
+
getFallback(track: Track): Promise<StreamInfo>;
|
|
20
|
+
extractPlaylist(url: string, requestedBy: string): Promise<Track[]>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=SoundCloudPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SoundCloudPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/SoundCloudPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAI3D,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,IAAI,SAAgB;IACpB,OAAO,SAAW;IAClB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,KAAK,CAAgB;;YAOf,IAAI;IAKlB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAOjC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIxB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAsEjE,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;IAmB5C,gBAAgB,CACpB,QAAQ,EAAE,MAAM,GAAG,MAAM,EACzB,IAAI,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAA;KAAO,GAChE,OAAO,CAAC,KAAK,EAAE,CAAC;IAoCb,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;IAS9C,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;CAsB1E"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SoundCloudPlugin = void 0;
|
|
4
|
+
const BasePlugin_1 = require("./BasePlugin");
|
|
5
|
+
const SoundCloud = require("@zibot/scdl");
|
|
6
|
+
class SoundCloudPlugin extends BasePlugin_1.BasePlugin {
|
|
7
|
+
constructor() {
|
|
8
|
+
super();
|
|
9
|
+
this.name = "soundcloud";
|
|
10
|
+
this.version = "1.0.0";
|
|
11
|
+
this.ready = this.init();
|
|
12
|
+
}
|
|
13
|
+
async init() {
|
|
14
|
+
this.client = new SoundCloud({ init: false });
|
|
15
|
+
await this.client.init();
|
|
16
|
+
}
|
|
17
|
+
canHandle(query) {
|
|
18
|
+
return (query.includes("soundcloud.com") ||
|
|
19
|
+
(!query.startsWith("http") && !query.includes("youtube")));
|
|
20
|
+
}
|
|
21
|
+
validate(url) {
|
|
22
|
+
return url.includes("soundcloud.com");
|
|
23
|
+
}
|
|
24
|
+
async search(query, requestedBy) {
|
|
25
|
+
await this.ready;
|
|
26
|
+
try {
|
|
27
|
+
if (query.includes("soundcloud.com")) {
|
|
28
|
+
try {
|
|
29
|
+
const info = await this.client.getTrackDetails(query);
|
|
30
|
+
const track = {
|
|
31
|
+
id: info.id.toString(),
|
|
32
|
+
title: info.title,
|
|
33
|
+
url: info.permalink_url || query,
|
|
34
|
+
duration: info.duration,
|
|
35
|
+
thumbnail: info.artwork_url,
|
|
36
|
+
requestedBy,
|
|
37
|
+
source: this.name,
|
|
38
|
+
metadata: {
|
|
39
|
+
author: info.user?.username,
|
|
40
|
+
plays: info.playback_count,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
return { tracks: [track] };
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
const playlist = await this.client.getPlaylistDetails(query);
|
|
47
|
+
const tracks = playlist.tracks.map((t) => ({
|
|
48
|
+
id: t.id.toString(),
|
|
49
|
+
title: t.title,
|
|
50
|
+
url: t.permalink_url,
|
|
51
|
+
duration: t.duration,
|
|
52
|
+
thumbnail: t.artwork_url || playlist.artwork_url,
|
|
53
|
+
requestedBy,
|
|
54
|
+
source: this.name,
|
|
55
|
+
metadata: {
|
|
56
|
+
author: t.user?.username,
|
|
57
|
+
plays: t.playback_count,
|
|
58
|
+
playlist: playlist.id?.toString(),
|
|
59
|
+
},
|
|
60
|
+
}));
|
|
61
|
+
return {
|
|
62
|
+
tracks,
|
|
63
|
+
playlist: {
|
|
64
|
+
name: playlist.title,
|
|
65
|
+
url: playlist.permalink_url || query,
|
|
66
|
+
thumbnail: playlist.artwork_url,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const results = await this.client.searchTracks({ query, limit: 15 });
|
|
72
|
+
const tracks = results.slice(0, 10).map((track) => ({
|
|
73
|
+
id: track.id.toString(),
|
|
74
|
+
title: track.title,
|
|
75
|
+
url: track.permalink_url,
|
|
76
|
+
duration: track.duration,
|
|
77
|
+
thumbnail: track.artwork_url,
|
|
78
|
+
requestedBy,
|
|
79
|
+
source: this.name,
|
|
80
|
+
metadata: {
|
|
81
|
+
author: track.user?.username,
|
|
82
|
+
plays: track.playback_count,
|
|
83
|
+
},
|
|
84
|
+
}));
|
|
85
|
+
return { tracks };
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
throw new Error(`SoundCloud search failed: ${error?.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async getStream(track) {
|
|
92
|
+
await this.ready;
|
|
93
|
+
try {
|
|
94
|
+
const stream = await this.client.downloadTrack(track.url);
|
|
95
|
+
if (!stream) {
|
|
96
|
+
throw new Error("SoundCloud download returned null");
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
stream,
|
|
100
|
+
type: "arbitrary",
|
|
101
|
+
metadata: track.metadata,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw new Error(`Failed to get SoundCloud stream: ${error.message}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async getRelatedTracks(trackURL, opts = {}) {
|
|
109
|
+
await this.ready;
|
|
110
|
+
try {
|
|
111
|
+
const tracks = await this.client.getRelatedTracks(trackURL, {
|
|
112
|
+
limit: 30,
|
|
113
|
+
filter: "tracks",
|
|
114
|
+
});
|
|
115
|
+
if (!tracks || !tracks?.length) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
const relatedfilter = tracks.filter((tr) => !(opts?.history ?? []).some((t) => t.url === tr.permalink_url));
|
|
119
|
+
const related = relatedfilter.slice(0, opts.limit || 1);
|
|
120
|
+
return related.map((t) => ({
|
|
121
|
+
id: t.id.toString(),
|
|
122
|
+
title: t.title,
|
|
123
|
+
url: t.permalink_url,
|
|
124
|
+
duration: t.duration,
|
|
125
|
+
thumbnail: t.artwork_url,
|
|
126
|
+
requestedBy: "auto",
|
|
127
|
+
source: this.name,
|
|
128
|
+
metadata: {
|
|
129
|
+
author: t.user?.username,
|
|
130
|
+
plays: t.playback_count,
|
|
131
|
+
},
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async getFallback(track) {
|
|
139
|
+
const trackfall = await this.search(track.title, track.requestedBy);
|
|
140
|
+
const fallbackTrack = trackfall.tracks?.[0];
|
|
141
|
+
if (!fallbackTrack) {
|
|
142
|
+
throw new Error(`No fallback track found for ${track.title}`);
|
|
143
|
+
}
|
|
144
|
+
return await this.getStream(fallbackTrack);
|
|
145
|
+
}
|
|
146
|
+
async extractPlaylist(url, requestedBy) {
|
|
147
|
+
await this.ready;
|
|
148
|
+
try {
|
|
149
|
+
const playlist = await this.client.getPlaylistDetails(url);
|
|
150
|
+
return playlist.tracks.map((t) => ({
|
|
151
|
+
id: t.id.toString(),
|
|
152
|
+
title: t.title,
|
|
153
|
+
url: t.permalink_url,
|
|
154
|
+
duration: t.duration,
|
|
155
|
+
thumbnail: t.artwork_url || playlist.artwork_url,
|
|
156
|
+
requestedBy,
|
|
157
|
+
source: this.name,
|
|
158
|
+
metadata: {
|
|
159
|
+
author: t.user?.username,
|
|
160
|
+
plays: t.playback_count,
|
|
161
|
+
playlist: playlist.id?.toString(),
|
|
162
|
+
},
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
exports.SoundCloudPlugin = SoundCloudPlugin;
|
|
171
|
+
//# sourceMappingURL=SoundCloudPlugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SoundCloudPlugin.js","sourceRoot":"","sources":["../../src/plugins/SoundCloudPlugin.ts"],"names":[],"mappings":";;;AAAA,6CAA0C;AAG1C,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AAE1C,MAAa,gBAAiB,SAAQ,uBAAU;IAM9C;QACE,KAAK,EAAE,CAAC;QANV,SAAI,GAAG,YAAY,CAAC;QACpB,YAAO,GAAG,OAAO,CAAC;QAMhB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9C,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,SAAS,CAAC,KAAa;QACrB,OAAO,CACL,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YAChC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAC1D,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,GAAW;QAClB,OAAO,GAAG,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,WAAmB;QAC7C,MAAM,IAAI,CAAC,KAAK,CAAC;QAEjB,IAAI,CAAC;YACH,IAAI,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBACrC,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;oBACtD,MAAM,KAAK,GAAU;wBACnB,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE;wBACtB,KAAK,EAAE,IAAI,CAAC,KAAK;wBACjB,GAAG,EAAE,IAAI,CAAC,aAAa,IAAI,KAAK;wBAChC,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,SAAS,EAAE,IAAI,CAAC,WAAW;wBAC3B,WAAW;wBACX,MAAM,EAAE,IAAI,CAAC,IAAI;wBACjB,QAAQ,EAAE;4BACR,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ;4BAC3B,KAAK,EAAE,IAAI,CAAC,cAAc;yBAC3B;qBACF,CAAC;oBACF,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7B,CAAC;gBAAC,MAAM,CAAC;oBACP,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;oBAC7D,MAAM,MAAM,GAAY,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;wBACvD,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE;wBACnB,KAAK,EAAE,CAAC,CAAC,KAAK;wBACd,GAAG,EAAE,CAAC,CAAC,aAAa;wBACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;wBACpB,SAAS,EAAE,CAAC,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW;wBAChD,WAAW;wBACX,MAAM,EAAE,IAAI,CAAC,IAAI;wBACjB,QAAQ,EAAE;4BACR,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ;4BACxB,KAAK,EAAE,CAAC,CAAC,cAAc;4BACvB,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE;yBAClC;qBACF,CAAC,CAAC,CAAC;oBAEJ,OAAO;wBACL,MAAM;wBACN,QAAQ,EAAE;4BACR,IAAI,EAAE,QAAQ,CAAC,KAAK;4BACpB,GAAG,EAAE,QAAQ,CAAC,aAAa,IAAI,KAAK;4BACpC,SAAS,EAAE,QAAQ,CAAC,WAAW;yBAChC;qBACF,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACrE,MAAM,MAAM,GAAY,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAU,EAAE,EAAE,CAAC,CAAC;gBAChE,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE;gBACvB,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,GAAG,EAAE,KAAK,CAAC,aAAa;gBACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,SAAS,EAAE,KAAK,CAAC,WAAW;gBAC5B,WAAW;gBACX,MAAM,EAAE,IAAI,CAAC,IAAI;gBACjB,QAAQ,EAAE;oBACR,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ;oBAC5B,KAAK,EAAE,KAAK,CAAC,cAAc;iBAC5B;aACF,CAAC,CAAC,CAAC;YAEJ,OAAO,EAAE,MAAM,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,6BAA6B,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAY;QAC1B,MAAM,IAAI,CAAC,KAAK,CAAC;QAEjB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC1D,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;YACvD,CAAC;YAED,OAAO;gBACL,MAAM;gBACN,IAAI,EAAE,WAAW;gBACjB,QAAQ,EAAE,KAAK,CAAC,QAAQ;aACzB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,oCAAoC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,QAAyB,EACzB,OAA+D,EAAE;QAEjE,MAAM,IAAI,CAAC,KAAK,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE;gBAC1D,KAAK,EAAE,EAAE;gBACT,MAAM,EAAE,QAAQ;aACjB,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;gBAC/B,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CACjC,CAAC,EAAO,EAAE,EAAE,CACV,CAAC,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,aAAa,CAAC,CACjE,CAAC;YAEF,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;YAExD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;gBAC9B,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE;gBACnB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,GAAG,EAAE,CAAC,CAAC,aAAa;gBACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,SAAS,EAAE,CAAC,CAAC,WAAW;gBACxB,WAAW,EAAE,MAAM;gBACnB,MAAM,EAAE,IAAI,CAAC,IAAI;gBACjB,QAAQ,EAAE;oBACR,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ;oBACxB,KAAK,EAAE,CAAC,CAAC,cAAc;iBACxB;aACF,CAAC,CAAC,CAAC;QACN,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAY;QAC5B,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;QACpE,MAAM,aAAa,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,+BAA+B,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,GAAW,EAAE,WAAmB;QACpD,MAAM,IAAI,CAAC,KAAK,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;YAC3D,OAAO,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;gBACtC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE;gBACnB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,GAAG,EAAE,CAAC,CAAC,aAAa;gBACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,SAAS,EAAE,CAAC,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW;gBAChD,WAAW;gBACX,MAAM,EAAE,IAAI,CAAC,IAAI;gBACjB,QAAQ,EAAE;oBACR,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ;oBACxB,KAAK,EAAE,CAAC,CAAC,cAAc;oBACvB,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE;iBAClC;aACF,CAAC,CAAC,CAAC;QACN,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AA1LD,4CA0LC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BasePlugin } from "./BasePlugin";
|
|
2
|
+
import { Track, SearchResult, StreamInfo } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* This minimal Spotify plugin:
|
|
5
|
+
* - Parses Spotify URLs/URIs (track/playlist/album)
|
|
6
|
+
* - Uses Spotify's public oEmbed endpoint to fetch *display metadata* (no auth, no SDK)
|
|
7
|
+
* - Does NOT provide audio streams (player is expected to redirect/fallback upstream)
|
|
8
|
+
* - Does NOT expand playlists/albums (no SDK; oEmbed doesn't enumerate items)
|
|
9
|
+
*/
|
|
10
|
+
export declare class SpotifyPlugin extends BasePlugin {
|
|
11
|
+
name: string;
|
|
12
|
+
version: string;
|
|
13
|
+
canHandle(query: string): boolean;
|
|
14
|
+
validate(url: string): boolean;
|
|
15
|
+
search(query: string, requestedBy: string): Promise<SearchResult>;
|
|
16
|
+
extractPlaylist(_input: string, _requestedBy: string): Promise<Track[]>;
|
|
17
|
+
extractAlbum(_input: string, _requestedBy: string): Promise<Track[]>;
|
|
18
|
+
getStream(_track: Track): Promise<StreamInfo>;
|
|
19
|
+
private identifyKind;
|
|
20
|
+
private extractId;
|
|
21
|
+
private buildTrackFromUrlOrUri;
|
|
22
|
+
private buildHeaderItem;
|
|
23
|
+
private toShareUrl;
|
|
24
|
+
private fetchOEmbed;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=SpotifyPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpotifyPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/SpotifyPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3D;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAC3C,IAAI,SAAa;IACjB,OAAO,SAAW;IAElB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAWjC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAUxB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAyBjE,eAAe,CACnB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,KAAK,EAAE,CAAC;IAIb,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAIpE,SAAS,CAAC,MAAM,EAAE,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;IAInD,OAAO,CAAC,YAAY;IAsBpB,OAAO,CAAC,SAAS;YAeH,sBAAsB;YA6BtB,eAAe;IA8B7B,OAAO,CAAC,UAAU;YAeJ,WAAW;CAkB1B"}
|