Skip to content

Commit a8762b1

Browse files
committed
Elgato look away!
1 parent 197f25f commit a8762b1

10 files changed

Lines changed: 256 additions & 3 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Adding support for the following sources:
1010
- TikTok (in beta, works on _most_ videos and **will** break all the time)
1111
- PornHub (search by prefixing with `phsearch:`)
1212
- soundgasm
13+
- streamDeckAudio files
14+
- These files are only accepted over HTTP currently
1315

1416
## Lavalink version compatibility
1517

plugin/src/main/java/com/dunctebot/lavalinkplugin/DuncteBotConfig.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public static class Sources {
2828
private boolean tiktok = true;
2929
private boolean mixcloud = true;
3030
private boolean soundgasm = true;
31+
private boolean elgato = false;
3132

3233
public boolean isGetyarn() {
3334
return getyarn;
@@ -100,5 +101,13 @@ public boolean isSoundgasm() {
100101
public void setSoundgasm(boolean soundgasm) {
101102
this.soundgasm = soundgasm;
102103
}
104+
105+
public boolean isElgato() {
106+
return elgato;
107+
}
108+
109+
public void setElgato(boolean elgato) {
110+
this.elgato = elgato;
111+
}
103112
}
104-
}
113+
}

plugin/src/main/java/com/dunctebot/lavalinkplugin/DuncteBotInjector.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.dunctebot.lavalinkplugin;
22

33
import com.dunctebot.sourcemanagers.clypit.ClypitAudioSourceManager;
4+
import com.dunctebot.sourcemanagers.elgato.streamdeck.StreamDeckAudioSourceManager;
45
import com.dunctebot.sourcemanagers.getyarn.GetyarnAudioSourceManager;
56
import com.dunctebot.sourcemanagers.mixcloud.MixcloudAudioSourceManager;
67
import com.dunctebot.sourcemanagers.ocremix.OCRemixAudioSourceManager;
@@ -82,6 +83,12 @@ public AudioPlayerManager configure(@NotNull AudioPlayerManager manager) {
8283
manager.registerSourceManager(new SoundGasmAudioSourceManager());
8384
}
8485

86+
if (this.sourcesConfig.isElgato()) {
87+
logger.warn("Elgato (.streamDeckAudio) audio source manager is not supported atm");
88+
// logger.info("Registering Elgato (.streamDeckAudio) audio source manager");
89+
// manager.registerSourceManager(new StreamDeckAudioSourceManager());
90+
}
91+
8592
return manager;
8693
}
8794
}

settings.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fun VersionCatalogBuilder.common() {
2424
fun VersionCatalogBuilder.sourceManager() {
2525
version("slf4j-version", "2.0.9")
2626

27-
library("lavaplayer", "dev.arbjerg", "lavaplayer").version("2.0.3")
27+
library("lavaplayer", "dev.arbjerg", "lavaplayer").version("2.1.2")
2828
library("logger", "org.slf4j", "slf4j-api").versionRef("slf4j-version")
2929
library("logger-impl", "org.slf4j", "slf4j-simple").versionRef("slf4j-version")
3030
library("commonsIo", "commons-io", "commons-io").version("2.7")

source-managers/src/main/java/com/dunctebot/sourcemanagers/Mp3Track.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,20 @@ protected void loadStream(LocalAudioTrackExecutor localExecutor, HttpInterface h
5454
final String trackUrl = getPlaybackUrl();
5555
log.debug("Starting {} track from URL: {}", manager.getSourceName(), trackUrl);
5656
// Setting contentLength (last param) to null makes it default to Long.MAX_VALUE
57-
try (PersistentHttpStream stream = new PersistentHttpStream(httpInterface, new URI(trackUrl), this.getTrackDuration())) {
57+
try (
58+
final var stream = this.wrapStream(
59+
new PersistentHttpStream(httpInterface, new URI(trackUrl), this.getTrackDuration())
60+
)
61+
) {
5862
processDelegate(createAudioTrack(this.trackInfo, stream), localExecutor);
5963
}
6064
}
6165

66+
// Helper function in case we need to wrap the http stream into something else for decoding
67+
protected SeekableInputStream wrapStream(SeekableInputStream stream) {
68+
return stream;
69+
}
70+
6271
protected InternalAudioTrack createAudioTrack(AudioTrackInfo trackInfo, SeekableInputStream stream) {
6372
return new Mp3AudioTrack(trackInfo, stream);
6473
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.dunctebot.sourcemanagers.elgato.streamdeck;
2+
3+
import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream;
4+
import com.sedmelluq.discord.lavaplayer.track.info.AudioTrackInfoProvider;
5+
6+
import java.io.IOException;
7+
import java.util.List;
8+
9+
// TODO: can I inject a custom probe into LP?
10+
// See: MediaContainerRegistry
11+
public class ElgatoInputStream extends SeekableInputStream {
12+
private static final byte XOR_VAL = 0x5E;
13+
14+
private final SeekableInputStream inputStream;
15+
16+
public ElgatoInputStream(SeekableInputStream inputStream) {
17+
super(inputStream.getContentLength(), inputStream.getMaxSkipDistance());
18+
this.inputStream = inputStream;
19+
}
20+
21+
@Override
22+
public long getPosition() {
23+
return this.inputStream.getPosition();
24+
}
25+
26+
// TODO: Will this work?
27+
@Override
28+
protected void seekHard(long position) throws IOException {
29+
((ElgatoInputStream) this.inputStream).seekHard(position);
30+
}
31+
32+
@Override
33+
public boolean canSeekHard() {
34+
return this.inputStream.canSeekHard();
35+
}
36+
37+
@Override
38+
public List<AudioTrackInfoProvider> getTrackInfoProviders() {
39+
return this.inputStream.getTrackInfoProviders();
40+
}
41+
42+
@Override
43+
public int read() throws IOException {
44+
final var read = this.inputStream.read();
45+
46+
if (read == -1) {
47+
return -1;
48+
}
49+
50+
return read ^ XOR_VAL;
51+
}
52+
53+
@Override
54+
public void close() throws IOException {
55+
super.close();
56+
this.inputStream.close();
57+
}
58+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.dunctebot.sourcemanagers.elgato.streamdeck;
2+
3+
import com.dunctebot.sourcemanagers.AbstractDuncteBotHttpSource;
4+
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
5+
import com.sedmelluq.discord.lavaplayer.tools.Units;
6+
import com.sedmelluq.discord.lavaplayer.track.AudioItem;
7+
import com.sedmelluq.discord.lavaplayer.track.AudioReference;
8+
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
9+
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
10+
11+
import java.io.DataInput;
12+
import java.io.DataOutput;
13+
import java.io.IOException;
14+
import java.net.URI;
15+
import java.net.URISyntaxException;
16+
import java.util.List;
17+
import java.util.Locale;
18+
19+
// TODO: http vs local file
20+
public class StreamDeckAudioSourceManager extends AbstractDuncteBotHttpSource {
21+
@Override
22+
public String getSourceName() {
23+
return "StreamDeckAudio";
24+
}
25+
26+
@Override
27+
public AudioItem loadItem(AudioPlayerManager manager, AudioReference reference) {
28+
try {
29+
final var url = new URI(reference.getIdentifier());
30+
31+
if (url.getPath().toLowerCase(Locale.ROOT).endsWith(".streamdeckaudio")) {
32+
final var parts = List.of(url.getPath().split("/"));
33+
final var fileName = parts.get(parts.size() - 1);
34+
35+
return new StreamDeckAudioTrack(
36+
new AudioTrackInfo(
37+
fileName,
38+
"Elgato",
39+
Units.CONTENT_LENGTH_UNKNOWN,
40+
fileName,
41+
false,
42+
url.toString()
43+
),
44+
this
45+
);
46+
}
47+
} catch (URISyntaxException ignored) {
48+
return null;
49+
}
50+
51+
return null;
52+
}
53+
54+
@Override
55+
public boolean isTrackEncodable(AudioTrack track) {
56+
return true;
57+
}
58+
59+
@Override
60+
public void encodeTrack(AudioTrack track, DataOutput output) throws IOException {
61+
// Nothing to encode
62+
}
63+
64+
@Override
65+
public AudioTrack decodeTrack(AudioTrackInfo trackInfo, DataInput input) throws IOException {
66+
return new StreamDeckAudioTrack(trackInfo, this);
67+
}
68+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.dunctebot.sourcemanagers.elgato.streamdeck;
2+
3+
import com.dunctebot.sourcemanagers.Mp3Track;
4+
import com.sedmelluq.discord.lavaplayer.container.wav.WavAudioTrack;
5+
import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream;
6+
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
7+
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
8+
import com.sedmelluq.discord.lavaplayer.track.InternalAudioTrack;
9+
10+
public class StreamDeckAudioTrack extends Mp3Track {
11+
private final StreamDeckAudioSourceManager manager;
12+
13+
public StreamDeckAudioTrack(AudioTrackInfo trackInfo, StreamDeckAudioSourceManager manager) {
14+
super(trackInfo, manager);
15+
this.manager = manager;
16+
}
17+
18+
@Override
19+
protected SeekableInputStream wrapStream(SeekableInputStream stream) {
20+
return new ElgatoInputStream(stream);
21+
}
22+
23+
@Override
24+
protected InternalAudioTrack createAudioTrack(AudioTrackInfo trackInfo, SeekableInputStream stream) {
25+
return new WavAudioTrack(trackInfo, stream);
26+
}
27+
28+
@Override
29+
public String getPlaybackUrl() {
30+
return this.trackInfo.uri;
31+
}
32+
33+
@Override
34+
protected AudioTrack makeShallowClone() {
35+
return new StreamDeckAudioTrack(this.trackInfo, this.manager);
36+
}
37+
38+
@Override
39+
public StreamDeckAudioSourceManager getSourceManager() {
40+
return this.manager;
41+
}
42+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import com.dunctebot.sourcemanagers.elgato.streamdeck.StreamDeckAudioSourceManager;
2+
import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat;
3+
import com.sedmelluq.discord.lavaplayer.format.AudioPlayerInputStream;
4+
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
5+
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
6+
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
7+
import com.sedmelluq.discord.lavaplayer.player.FunctionalResultHandler;
8+
9+
import javax.sound.sampled.AudioInputStream;
10+
import javax.sound.sampled.AudioSystem;
11+
import javax.sound.sampled.DataLine;
12+
import javax.sound.sampled.SourceDataLine;
13+
14+
import static com.sedmelluq.discord.lavaplayer.format.StandardAudioDataFormats.COMMON_PCM_S16_BE;
15+
16+
public class LocalPlaybackTest {
17+
public static void main(String[] args) throws Exception {
18+
final var mngr = new StreamDeckAudioSourceManager();
19+
20+
AudioPlayerManager manager = new DefaultAudioPlayerManager();
21+
22+
manager.registerSourceManager(mngr);
23+
24+
manager.getConfiguration().setOutputFormat(COMMON_PCM_S16_BE);
25+
26+
AudioPlayer player = manager.createPlayer();
27+
28+
player.setVolume(35);
29+
30+
manager.loadItem(
31+
"https://cdn.discordapp.com/attachments/340834322674089986/1242398908815118376/Fanfare_-_Show_Intro.streamDeckAudio?ex=664f0326&is=664db1a6&hm=9a4898f7301601b3bc14cfda4101aab0ed94cdfb5fe89d2a0917dc4e01514da6&",
32+
new FunctionalResultHandler(item -> {
33+
player.playTrack(item);
34+
}, playlist -> {
35+
player.playTrack(playlist.getTracks().get(0));
36+
}, null, null)
37+
);
38+
39+
40+
AudioDataFormat format = manager.getConfiguration().getOutputFormat();
41+
AudioInputStream stream = AudioPlayerInputStream.createStream(player, format, 10000L, false);
42+
SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class, stream.getFormat());
43+
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
44+
45+
line.open(stream.getFormat());
46+
line.start();
47+
48+
byte[] buffer = new byte[COMMON_PCM_S16_BE.maximumChunkSize()];
49+
int chunkSize;
50+
51+
while ((chunkSize = stream.read(buffer)) >= 0) {
52+
line.write(buffer, 0, chunkSize);
53+
}
54+
}
55+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
org.slf4j.simpleLogger.defaultLogLevel=TRACE
2+
3+
defaultLogLevel=TRACE

0 commit comments

Comments
 (0)