Skip to content

Commit

Permalink
Elgato look away!
Browse files Browse the repository at this point in the history
  • Loading branch information
duncte123 committed Jun 30, 2024
1 parent 197f25f commit a8762b1
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 3 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Adding support for the following sources:
- TikTok (in beta, works on _most_ videos and **will** break all the time)
- PornHub (search by prefixing with `phsearch:`)
- soundgasm
- streamDeckAudio files
- These files are only accepted over HTTP currently

## Lavalink version compatibility

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public static class Sources {
private boolean tiktok = true;
private boolean mixcloud = true;
private boolean soundgasm = true;
private boolean elgato = false;

public boolean isGetyarn() {
return getyarn;
Expand Down Expand Up @@ -100,5 +101,13 @@ public boolean isSoundgasm() {
public void setSoundgasm(boolean soundgasm) {
this.soundgasm = soundgasm;
}

public boolean isElgato() {
return elgato;
}

public void setElgato(boolean elgato) {
this.elgato = elgato;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.dunctebot.lavalinkplugin;

import com.dunctebot.sourcemanagers.clypit.ClypitAudioSourceManager;
import com.dunctebot.sourcemanagers.elgato.streamdeck.StreamDeckAudioSourceManager;
import com.dunctebot.sourcemanagers.getyarn.GetyarnAudioSourceManager;
import com.dunctebot.sourcemanagers.mixcloud.MixcloudAudioSourceManager;
import com.dunctebot.sourcemanagers.ocremix.OCRemixAudioSourceManager;
Expand Down Expand Up @@ -82,6 +83,12 @@ public AudioPlayerManager configure(@NotNull AudioPlayerManager manager) {
manager.registerSourceManager(new SoundGasmAudioSourceManager());
}

if (this.sourcesConfig.isElgato()) {
logger.warn("Elgato (.streamDeckAudio) audio source manager is not supported atm");
// logger.info("Registering Elgato (.streamDeckAudio) audio source manager");
// manager.registerSourceManager(new StreamDeckAudioSourceManager());
}

return manager;
}
}
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fun VersionCatalogBuilder.common() {
fun VersionCatalogBuilder.sourceManager() {
version("slf4j-version", "2.0.9")

library("lavaplayer", "dev.arbjerg", "lavaplayer").version("2.0.3")
library("lavaplayer", "dev.arbjerg", "lavaplayer").version("2.1.2")
library("logger", "org.slf4j", "slf4j-api").versionRef("slf4j-version")
library("logger-impl", "org.slf4j", "slf4j-simple").versionRef("slf4j-version")
library("commonsIo", "commons-io", "commons-io").version("2.7")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,20 @@ protected void loadStream(LocalAudioTrackExecutor localExecutor, HttpInterface h
final String trackUrl = getPlaybackUrl();
log.debug("Starting {} track from URL: {}", manager.getSourceName(), trackUrl);
// Setting contentLength (last param) to null makes it default to Long.MAX_VALUE
try (PersistentHttpStream stream = new PersistentHttpStream(httpInterface, new URI(trackUrl), this.getTrackDuration())) {
try (
final var stream = this.wrapStream(
new PersistentHttpStream(httpInterface, new URI(trackUrl), this.getTrackDuration())
)
) {
processDelegate(createAudioTrack(this.trackInfo, stream), localExecutor);
}
}

// Helper function in case we need to wrap the http stream into something else for decoding
protected SeekableInputStream wrapStream(SeekableInputStream stream) {
return stream;
}

protected InternalAudioTrack createAudioTrack(AudioTrackInfo trackInfo, SeekableInputStream stream) {
return new Mp3AudioTrack(trackInfo, stream);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.dunctebot.sourcemanagers.elgato.streamdeck;

import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream;
import com.sedmelluq.discord.lavaplayer.track.info.AudioTrackInfoProvider;

import java.io.IOException;
import java.util.List;

// TODO: can I inject a custom probe into LP?
// See: MediaContainerRegistry
public class ElgatoInputStream extends SeekableInputStream {
private static final byte XOR_VAL = 0x5E;

private final SeekableInputStream inputStream;

public ElgatoInputStream(SeekableInputStream inputStream) {
super(inputStream.getContentLength(), inputStream.getMaxSkipDistance());
this.inputStream = inputStream;
}

@Override
public long getPosition() {
return this.inputStream.getPosition();
}

// TODO: Will this work?
@Override
protected void seekHard(long position) throws IOException {
((ElgatoInputStream) this.inputStream).seekHard(position);
}

@Override
public boolean canSeekHard() {
return this.inputStream.canSeekHard();
}

@Override
public List<AudioTrackInfoProvider> getTrackInfoProviders() {
return this.inputStream.getTrackInfoProviders();
}

@Override
public int read() throws IOException {
final var read = this.inputStream.read();

if (read == -1) {
return -1;
}

return read ^ XOR_VAL;
}

@Override
public void close() throws IOException {
super.close();
this.inputStream.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.dunctebot.sourcemanagers.elgato.streamdeck;

import com.dunctebot.sourcemanagers.AbstractDuncteBotHttpSource;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.tools.Units;
import com.sedmelluq.discord.lavaplayer.track.AudioItem;
import com.sedmelluq.discord.lavaplayer.track.AudioReference;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Locale;

// TODO: http vs local file
public class StreamDeckAudioSourceManager extends AbstractDuncteBotHttpSource {
@Override
public String getSourceName() {
return "StreamDeckAudio";
}

@Override
public AudioItem loadItem(AudioPlayerManager manager, AudioReference reference) {
try {
final var url = new URI(reference.getIdentifier());

if (url.getPath().toLowerCase(Locale.ROOT).endsWith(".streamdeckaudio")) {
final var parts = List.of(url.getPath().split("/"));
final var fileName = parts.get(parts.size() - 1);

return new StreamDeckAudioTrack(
new AudioTrackInfo(
fileName,
"Elgato",
Units.CONTENT_LENGTH_UNKNOWN,
fileName,
false,
url.toString()
),
this
);
}
} catch (URISyntaxException ignored) {
return null;
}

return null;
}

@Override
public boolean isTrackEncodable(AudioTrack track) {
return true;
}

@Override
public void encodeTrack(AudioTrack track, DataOutput output) throws IOException {
// Nothing to encode
}

@Override
public AudioTrack decodeTrack(AudioTrackInfo trackInfo, DataInput input) throws IOException {
return new StreamDeckAudioTrack(trackInfo, this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.dunctebot.sourcemanagers.elgato.streamdeck;

import com.dunctebot.sourcemanagers.Mp3Track;
import com.sedmelluq.discord.lavaplayer.container.wav.WavAudioTrack;
import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
import com.sedmelluq.discord.lavaplayer.track.InternalAudioTrack;

public class StreamDeckAudioTrack extends Mp3Track {
private final StreamDeckAudioSourceManager manager;

public StreamDeckAudioTrack(AudioTrackInfo trackInfo, StreamDeckAudioSourceManager manager) {
super(trackInfo, manager);
this.manager = manager;
}

@Override
protected SeekableInputStream wrapStream(SeekableInputStream stream) {
return new ElgatoInputStream(stream);
}

@Override
protected InternalAudioTrack createAudioTrack(AudioTrackInfo trackInfo, SeekableInputStream stream) {
return new WavAudioTrack(trackInfo, stream);
}

@Override
public String getPlaybackUrl() {
return this.trackInfo.uri;
}

@Override
protected AudioTrack makeShallowClone() {
return new StreamDeckAudioTrack(this.trackInfo, this.manager);
}

@Override
public StreamDeckAudioSourceManager getSourceManager() {
return this.manager;
}
}
55 changes: 55 additions & 0 deletions source-managers/src/test/java/LocalPlaybackTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import com.dunctebot.sourcemanagers.elgato.streamdeck.StreamDeckAudioSourceManager;
import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat;
import com.sedmelluq.discord.lavaplayer.format.AudioPlayerInputStream;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.player.FunctionalResultHandler;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

import static com.sedmelluq.discord.lavaplayer.format.StandardAudioDataFormats.COMMON_PCM_S16_BE;

public class LocalPlaybackTest {
public static void main(String[] args) throws Exception {
final var mngr = new StreamDeckAudioSourceManager();

AudioPlayerManager manager = new DefaultAudioPlayerManager();

manager.registerSourceManager(mngr);

manager.getConfiguration().setOutputFormat(COMMON_PCM_S16_BE);

AudioPlayer player = manager.createPlayer();

player.setVolume(35);

manager.loadItem(
"https://cdn.discordapp.com/attachments/340834322674089986/1242398908815118376/Fanfare_-_Show_Intro.streamDeckAudio?ex=664f0326&is=664db1a6&hm=9a4898f7301601b3bc14cfda4101aab0ed94cdfb5fe89d2a0917dc4e01514da6&",
new FunctionalResultHandler(item -> {
player.playTrack(item);
}, playlist -> {
player.playTrack(playlist.getTracks().get(0));
}, null, null)
);


AudioDataFormat format = manager.getConfiguration().getOutputFormat();
AudioInputStream stream = AudioPlayerInputStream.createStream(player, format, 10000L, false);
SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class, stream.getFormat());
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);

line.open(stream.getFormat());
line.start();

byte[] buffer = new byte[COMMON_PCM_S16_BE.maximumChunkSize()];
int chunkSize;

while ((chunkSize = stream.read(buffer)) >= 0) {
line.write(buffer, 0, chunkSize);
}
}
}
3 changes: 3 additions & 0 deletions source-managers/src/test/resources/simplelogger.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
org.slf4j.simpleLogger.defaultLogLevel=TRACE

defaultLogLevel=TRACE

0 comments on commit a8762b1

Please sign in to comment.