Now it runs
This commit is contained in:
parent
a4d8e56a4e
commit
9e7feaaf35
8 changed files with 105 additions and 83 deletions
15
pom.xml
15
pom.xml
|
@ -21,7 +21,7 @@
|
|||
</repository>
|
||||
<!-- this one has no ipv6 -->
|
||||
<repository>
|
||||
<id>maxhenkel</id>
|
||||
<id>maxhenkel-repo</id>
|
||||
<url>https://maven.maxhenkel.de/repository/public</url>
|
||||
</repository>
|
||||
<repository>
|
||||
|
@ -62,6 +62,12 @@
|
|||
<artifactId>inventorygui</artifactId>
|
||||
<version>1.6.4-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>26.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -89,5 +95,12 @@
|
|||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
|
|
|
@ -12,6 +12,8 @@ import org.bukkit.NamespacedKey;
|
|||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public final class MusicPlugin extends JavaPlugin {
|
||||
|
@ -30,10 +32,19 @@ public final class MusicPlugin extends JavaPlugin {
|
|||
DebugLogger.logger = getLogger();
|
||||
|
||||
var service = getServer().getServicesManager().load(BukkitVoicechatService.class);
|
||||
if (service == null) {
|
||||
getLogger().severe("\"Simple Voice Chat\" is required for this plugin");
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
service.registerPlugin(new MyVoicechatPlugin(api -> VOICECHAT_API = api));
|
||||
|
||||
getDataFolder().mkdir();
|
||||
var storagePath = getDataFolder().toPath().resolve("storage");
|
||||
try {
|
||||
Files.createDirectories(storagePath);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
AUDIO_FILE_STORAGE = new AudioFileStorage(storagePath);
|
||||
DOWNLOADER = new Downloader(storagePath);
|
||||
|
@ -44,16 +55,11 @@ public final class MusicPlugin extends JavaPlugin {
|
|||
throw new RuntimeException("Failed to initialize FFmpeg", e);
|
||||
}
|
||||
|
||||
getCommand("test").setExecutor(new TestCommand());
|
||||
Objects.requireNonNull(getCommand("test"))
|
||||
.setExecutor(new TestCommand());
|
||||
|
||||
/*var mcmd = new MusicCommands(new AudioFileStorage(getDataFolder()));
|
||||
getCommand("download").setExecutor(mcmd);
|
||||
getCommand("convert").setExecutor(mcmd);
|
||||
getCommand("play").setExecutor(mcmd);
|
||||
getCommand("stop").setExecutor(mcmd);
|
||||
getCommand("pause").setExecutor(mcmd);
|
||||
getCommand("resume").setExecutor(mcmd);
|
||||
getCommand("pmp").setExecutor(mcmd);*/
|
||||
Objects.requireNonNull(getCommand("pmp"))
|
||||
.setExecutor(new MusicCommands());
|
||||
|
||||
getServer().getPluginManager().registerEvents(new ItemEvents(), this);
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.bukkit.command.CommandExecutor;
|
|||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.gagravarr.opus.OpusFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
@ -20,19 +21,18 @@ public class TestCommand implements CommandExecutor {
|
|||
private OpusFilePlayer opusFilePlayer;
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
var player = (Player) sender;
|
||||
var action = args.length > 0 ? args[0] : null;
|
||||
|
||||
if (args[0].equals("download")) {
|
||||
testDownload(player, args[1]);
|
||||
} else if (args[0].equals("convert")) {
|
||||
testConvert(player, args[1], Integer.parseInt(args[2]));
|
||||
} else if (args[0].equals("play")) {
|
||||
testPlay(player, args[1], Integer.parseInt(args[2]));
|
||||
} else if (args[0].equals("pause")) {
|
||||
opusFilePlayer.pause();
|
||||
} else if (args[0].equals("unpause")) {
|
||||
opusFilePlayer.unpause();
|
||||
switch (action) {
|
||||
case "download" -> testDownload(player, args[1]);
|
||||
case "convert" -> testConvert(player, args[1], Integer.parseInt(args[2]));
|
||||
case "play" -> testPlay(player, args[1], Integer.parseInt(args[2]));
|
||||
case "pause" -> opusFilePlayer.pause();
|
||||
case "unpause" -> opusFilePlayer.unpause();
|
||||
case "stop" -> opusFilePlayer.stop();
|
||||
case null, default -> sender.sendMessage("download | convert | play | pause | unpause | stop");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -41,18 +41,20 @@ public class TestCommand implements CommandExecutor {
|
|||
private void testDownload(Player player, String url) {
|
||||
player.sendMessage("Downloading " + url);
|
||||
MusicPlugin.getDownloader().download(URI.create(url)).handle((hash, ex) -> {
|
||||
if (ex != null)
|
||||
player.sendMessage("ERROR downloading: " + ex.getMessage());
|
||||
else
|
||||
if (ex != null) {
|
||||
ex.printStackTrace();
|
||||
player.sendMessage("Error downloading. See console for details.");
|
||||
} else {
|
||||
player.spigot().sendMessage(
|
||||
new ComponentBuilder("Hash: " + hash)
|
||||
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to copy")))
|
||||
new ComponentBuilder("Downloaded. Hash: " + hash.substring(0, 7) + "...")
|
||||
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to copy hash")))
|
||||
.event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, hash))
|
||||
.append("\nClick here to convert to 48 kbps")
|
||||
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to do that")))
|
||||
.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/convert " + hash + " 48000"))
|
||||
.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/test convert " + hash + " 48000"))
|
||||
.create()
|
||||
);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
@ -60,15 +62,17 @@ public class TestCommand implements CommandExecutor {
|
|||
private void testConvert(Player player, String hash, int bitrate) {
|
||||
try {
|
||||
MusicPlugin.getStorage().convert(MusicPlugin.getConverter(), hash, bitrate).handle((f, ex) -> {
|
||||
if (ex != null)
|
||||
player.sendMessage("ERROR converting: " + ex.getMessage());
|
||||
else
|
||||
if (ex != null) {
|
||||
ex.printStackTrace();
|
||||
player.sendMessage("Error converting. See console for details.");
|
||||
} else {
|
||||
player.spigot().sendMessage(
|
||||
new ComponentBuilder("Converted " + hash.substring(0, 7) + "... to " + bitrate + "bps! Click to play")
|
||||
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to play")))
|
||||
.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/test play " + hash + " " + bitrate))
|
||||
.create()
|
||||
);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
} catch (IOException e) {
|
||||
|
@ -87,13 +91,11 @@ public class TestCommand implements CommandExecutor {
|
|||
try {
|
||||
opusFilePlayer = new OpusFilePlayer(new OpusFile(MusicPlugin.getStorage().get(hash, bitrate).toFile()), channel);
|
||||
opusFilePlayer.play();
|
||||
opusFilePlayer.setOnTrackEvent(e -> {
|
||||
player.sendMessage("Event: " + e);
|
||||
});
|
||||
opusFilePlayer.setOnTrackEvent(e -> player.sendMessage("Event: " + e));
|
||||
player.sendMessage("Now playing");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
player.sendMessage("Error");
|
||||
player.sendMessage("Error. See console for details.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import net.bramp.ffmpeg.FFmpeg;
|
|||
import net.bramp.ffmpeg.FFmpegExecutor;
|
||||
import net.bramp.ffmpeg.builder.FFmpegBuilder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
|
@ -44,7 +43,7 @@ public class Converter {
|
|||
private CompletableFuture<Path> convert(String input, Path output, int bitrate) throws IOException {
|
||||
var builder = new FFmpegBuilder()
|
||||
.setInput(input)
|
||||
.addOutput(output.toAbsolutePath().toUri())
|
||||
.addOutput(output.toAbsolutePath().toString())
|
||||
.setAudioChannels(1)
|
||||
.setAudioSampleRate(48_000)
|
||||
.setAudioBitRate(bitrate)
|
||||
|
|
|
@ -3,14 +3,17 @@ package eu.m724.music_plugin.audio;
|
|||
import de.maxhenkel.voicechat.api.opus.OpusEncoder;
|
||||
import eu.m724.music_plugin.DebugLogger;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.gagravarr.opus.OpusAudioData;
|
||||
import org.gagravarr.opus.OpusFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class OpusFileEncoder implements OpusEncoder {
|
||||
private final OpusFile opusFile;
|
||||
private boolean closed = false;
|
||||
private boolean closeLock = false;
|
||||
|
||||
private OpusAudioData nextPacket;
|
||||
|
||||
public OpusFileEncoder(OpusFile file) {
|
||||
this.opusFile = file;
|
||||
|
@ -18,14 +21,10 @@ public class OpusFileEncoder implements OpusEncoder {
|
|||
|
||||
@Override
|
||||
public byte[] encode(short[] rawAudio) {
|
||||
try {
|
||||
var packet = opusFile.getNextAudioPacket();
|
||||
if (packet != null)
|
||||
return packet.getData();
|
||||
else
|
||||
return new byte[0];
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Reading next packet", e);
|
||||
if (nextPacket != null) {
|
||||
return nextPacket.getData();
|
||||
} else {
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,6 +42,12 @@ public class OpusFileEncoder implements OpusEncoder {
|
|||
@Override
|
||||
public void close() {
|
||||
DebugLogger.fine("opus file just closed");
|
||||
if (closeLock) {
|
||||
DebugLogger.fine("but locked");
|
||||
closeLock = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
|
||||
|
@ -53,11 +58,21 @@ public class OpusFileEncoder implements OpusEncoder {
|
|||
}
|
||||
}
|
||||
|
||||
public void lock() {
|
||||
closeLock = true;
|
||||
}
|
||||
|
||||
/* SUPPLIER */
|
||||
|
||||
private final short[] array = new short[960];
|
||||
|
||||
public short[] supplier() {
|
||||
return !closed ? array : null;
|
||||
try {
|
||||
nextPacket = opusFile.getNextAudioPacket();
|
||||
} catch (IOException e) {
|
||||
// TODO catch this?
|
||||
}
|
||||
|
||||
return nextPacket != null ? array : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,10 +102,10 @@ public class OpusFilePlayer {
|
|||
if (!playing) return;
|
||||
|
||||
playing = false;
|
||||
audioPlayer.stopPlaying();
|
||||
if (!reset)
|
||||
encoder.lock();
|
||||
|
||||
if (reset)
|
||||
encoder.resetState();
|
||||
audioPlayer.stopPlaying();
|
||||
|
||||
DebugLogger.fine("Playback stopped (reset: %s)", reset);
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ import java.net.URI;
|
|||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HexFormat;
|
||||
|
@ -32,14 +32,19 @@ public class Downloader {
|
|||
* @return the downloaded file's SHA-256 hash (in a future)
|
||||
*/
|
||||
public CompletableFuture<String> download(URI uri) {
|
||||
DebugLogger.fine("About to download from " + uri);
|
||||
DebugLogger.fine("About to download from %s", uri);
|
||||
|
||||
try (
|
||||
var client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build();
|
||||
) {
|
||||
var request = HttpRequest.newBuilder(uri).GET().build();
|
||||
return client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()).thenApply(this::futureFunction);
|
||||
}
|
||||
// TODO fix this. Yes this is the only way. sendAsync doesn't block for some reason. I don't know why.
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (
|
||||
var client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build()
|
||||
) {
|
||||
var request = HttpRequest.newBuilder(uri).GET().build();
|
||||
return client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()).thenApply(this::futureFunction).join();
|
||||
} catch (Exception e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String futureFunction(HttpResponse<InputStream> response) {
|
||||
|
@ -51,7 +56,7 @@ public class Downloader {
|
|||
try {
|
||||
digest = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
|
||||
try (
|
||||
|
@ -65,17 +70,15 @@ public class Downloader {
|
|||
digest.update(dataBuffer, 0, bytesRead);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Exception downloading from " + response.uri(), e);
|
||||
throw new CompletionException("Exception downloading from " + response.uri(), e);
|
||||
}
|
||||
|
||||
String hash = HexFormat.of().formatHex(digest.digest());
|
||||
DebugLogger.fine("Downloaded, hash: " + hash);
|
||||
|
||||
try {
|
||||
Files.createDirectory(basePath.resolve(hash));
|
||||
Files.move(temp, basePath.resolve(hash).resolve("0.original"));
|
||||
} catch (FileAlreadyExistsException e) {
|
||||
DebugLogger.fine("Already exists!");
|
||||
Files.createDirectories(basePath.resolve(hash));
|
||||
Files.move(temp, basePath.resolve(hash).resolve("0.original"), StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
throw new CompletionException("Exception saving file", e);
|
||||
}
|
||||
|
|
|
@ -10,22 +10,6 @@ libraries:
|
|||
- org.gagravarr:vorbis-java-core:0.8
|
||||
|
||||
commands:
|
||||
download:
|
||||
description: Downloads a file from URL. Run /convert next
|
||||
usage: /<command> <url>
|
||||
convert:
|
||||
description: Converts original downloaded file to some bitrate (in bps)
|
||||
usage: /<command> <hash> <bitrate>
|
||||
play:
|
||||
description: Plays the audio file of specified bitrate. Run /convert first
|
||||
usage: /<command> <hash> <bitrate>
|
||||
stop:
|
||||
description: Stops playback
|
||||
usage: /<command>
|
||||
pause:
|
||||
description: Pauses playback
|
||||
usage: /<command>
|
||||
resume:
|
||||
description: Resumes playback
|
||||
usage: /<command>
|
||||
pmp:
|
||||
pmp:
|
||||
|
||||
test:
|
Loading…
Add table
Add a link
Reference in a new issue