library
This commit is contained in:
parent
6c19cfe128
commit
0c45358fa6
12 changed files with 292 additions and 151 deletions
9
pom.xml
9
pom.xml
|
|
@ -90,6 +90,15 @@
|
||||||
<include>de.themoep:inventorygui</include>
|
<include>de.themoep:inventorygui</include>
|
||||||
</includes>
|
</includes>
|
||||||
</artifactSet>
|
</artifactSet>
|
||||||
|
<filters>
|
||||||
|
<filter>
|
||||||
|
<artifact>*</artifact>
|
||||||
|
<excludes>
|
||||||
|
<exclude>META-INF/MANIFEST.MF</exclude>
|
||||||
|
<exclude>META-INF/maven/**</exclude>
|
||||||
|
</excludes>
|
||||||
|
</filter>
|
||||||
|
</filters>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import eu.m724.music_plugin.audio.storage.Downloader;
|
||||||
import eu.m724.music_plugin.item.ItemEvents;
|
import eu.m724.music_plugin.item.ItemEvents;
|
||||||
import eu.m724.music_plugin.item.PmpCommand;
|
import eu.m724.music_plugin.item.PmpCommand;
|
||||||
import eu.m724.music_plugin.item.speaker.BlockChecker;
|
import eu.m724.music_plugin.item.speaker.BlockChecker;
|
||||||
|
import eu.m724.music_plugin.library.LibraryStorage;
|
||||||
import net.bramp.ffmpeg.FFmpeg;
|
import net.bramp.ffmpeg.FFmpeg;
|
||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
@ -23,6 +24,7 @@ public final class MusicPlugin extends JavaPlugin {
|
||||||
private static AudioFileStorage AUDIO_FILE_STORAGE;
|
private static AudioFileStorage AUDIO_FILE_STORAGE;
|
||||||
private static Converter CONVERTER;
|
private static Converter CONVERTER;
|
||||||
private static Downloader DOWNLOADER;
|
private static Downloader DOWNLOADER;
|
||||||
|
private static LibraryStorage LIBRARY_STORAGE;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
|
@ -41,14 +43,19 @@ public final class MusicPlugin extends JavaPlugin {
|
||||||
service.registerPlugin(new MyVoicechatPlugin(api -> VOICECHAT_API = api));
|
service.registerPlugin(new MyVoicechatPlugin(api -> VOICECHAT_API = api));
|
||||||
|
|
||||||
var storagePath = getDataFolder().toPath().resolve("storage");
|
var storagePath = getDataFolder().toPath().resolve("storage");
|
||||||
|
var audioStoragePath = storagePath.resolve("audio");
|
||||||
|
var libraryStoragePath = storagePath.resolve("library");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(storagePath);
|
Files.createDirectories(audioStoragePath);
|
||||||
|
Files.createDirectories(libraryStoragePath);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
AUDIO_FILE_STORAGE = new AudioFileStorage(storagePath);
|
AUDIO_FILE_STORAGE = new AudioFileStorage(audioStoragePath);
|
||||||
DOWNLOADER = new Downloader(storagePath);
|
DOWNLOADER = new Downloader(audioStoragePath);
|
||||||
|
LIBRARY_STORAGE = new LibraryStorage(libraryStoragePath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
CONVERTER = new Converter(new FFmpeg());
|
CONVERTER = new Converter(new FFmpeg());
|
||||||
|
|
@ -91,6 +98,10 @@ public final class MusicPlugin extends JavaPlugin {
|
||||||
return DOWNLOADER;
|
return DOWNLOADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LibraryStorage getLibraryStorage() {
|
||||||
|
return LIBRARY_STORAGE;
|
||||||
|
}
|
||||||
|
|
||||||
public static NamespacedKey getNamespacedKey(String key) {
|
public static NamespacedKey getNamespacedKey(String key) {
|
||||||
return new NamespacedKey(INSTANCE, key);
|
return new NamespacedKey(INSTANCE, key);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,26 +60,21 @@ public class TestCommand implements CommandExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testConvert(Player player, String hash, int bitrate) {
|
private void testConvert(Player player, String hash, int bitrate) {
|
||||||
try {
|
player.sendMessage("Converting...");
|
||||||
player.sendMessage("Converting...");
|
MusicPlugin.getStorage().convert(MusicPlugin.getConverter(), hash, bitrate).handle((f, ex) -> {
|
||||||
MusicPlugin.getStorage().convert(MusicPlugin.getConverter(), hash, bitrate).handle((f, ex) -> {
|
if (ex != null) {
|
||||||
if (ex != null) {
|
ex.printStackTrace();
|
||||||
ex.printStackTrace();
|
player.sendMessage("Error converting. See console for details.");
|
||||||
player.sendMessage("Error converting. See console for details.");
|
} else {
|
||||||
} else {
|
player.spigot().sendMessage(
|
||||||
player.spigot().sendMessage(
|
new ComponentBuilder("Converted " + hash.substring(0, 7) + "... to " + bitrate + "bps! Click to play")
|
||||||
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 HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to play")))
|
.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/test play " + hash + " " + bitrate))
|
||||||
.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/test play " + hash + " " + bitrate))
|
.create()
|
||||||
.create()
|
);
|
||||||
);
|
}
|
||||||
}
|
return null;
|
||||||
return null;
|
});
|
||||||
});
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
player.sendMessage("Error");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testPlay(Player player, String hash, int bitrate) {
|
private void testPlay(Player player, String hash, int bitrate) {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,10 @@ public class AudioFileStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an audio file of given original hash and bitrate
|
* Get an audio file of given original hash and bitrate<br>
|
||||||
|
* <br>
|
||||||
|
* <strong>WARNING</strong> Whether the file exists is not checked.<br>
|
||||||
|
* Do it yourself - simply check if the file exists.
|
||||||
*
|
*
|
||||||
* @param hash the hex sha256 hash of the original
|
* @param hash the hex sha256 hash of the original
|
||||||
* @param bitrate the bitrate in bps like 32000
|
* @param bitrate the bitrate in bps like 32000
|
||||||
|
|
@ -77,13 +80,18 @@ public class AudioFileStorage {
|
||||||
* @param bitrate the target bitrate in bps
|
* @param bitrate the target bitrate in bps
|
||||||
* @return the future with the new file
|
* @return the future with the new file
|
||||||
*/
|
*/
|
||||||
public CompletableFuture<Path> convert(Converter converter, String hash, int bitrate) throws IOException {
|
public CompletableFuture<Path> convert(Converter converter, String hash, int bitrate) {
|
||||||
var file = get(hash, bitrate);
|
var file = get(hash, bitrate);
|
||||||
if (Files.exists(file)) return CompletableFuture.completedFuture(file);
|
if (Files.exists(file)) return CompletableFuture.completedFuture(file);
|
||||||
|
|
||||||
var og = getOriginal(hash);
|
var og = getOriginal(hash);
|
||||||
if (!Files.exists(og)) return null;
|
if (!Files.exists(og)) return null;
|
||||||
|
|
||||||
return converter.convert(og, file, bitrate).thenApply(v -> file);
|
// TODO maybe do that in Converter?
|
||||||
|
try {
|
||||||
|
return converter.convert(og, file, bitrate);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return CompletableFuture.failedFuture(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ public class Downloader {
|
||||||
public CompletableFuture<String> download(URI uri) {
|
public CompletableFuture<String> download(URI uri) {
|
||||||
DebugLogger.fine("About to download from %s", uri);
|
DebugLogger.fine("About to download from %s", uri);
|
||||||
|
|
||||||
// TODO fix this. Yes this is the only way. sendAsync doesn't block for some reason. I don't know why.
|
// TODO sendAsync doesn't block for some reason
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
try (
|
try (
|
||||||
var client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build()
|
var client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build()
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public class ItemEvents implements Listener {
|
||||||
|
|
||||||
if (event.getItem() == null) return;
|
if (event.getItem() == null) return;
|
||||||
|
|
||||||
var player = PortableMediaPlayers.get(event.getItem());
|
var player = PortableMediaPlayers.fromItemStack(event.getItem());
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
|
|
||||||
System.out.println("A player used");
|
System.out.println("A player used");
|
||||||
|
|
@ -31,7 +31,7 @@ public class ItemEvents implements Listener {
|
||||||
player.next();
|
player.next();
|
||||||
} else {
|
} else {
|
||||||
System.out.println("no shift + lmb: rpevous song");
|
System.out.println("no shift + lmb: rpevous song");
|
||||||
player.prev();
|
player.previous();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (right) {
|
if (right) {
|
||||||
|
|
@ -65,7 +65,7 @@ public class ItemEvents implements Listener {
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onDrop(PlayerDropItemEvent event) {
|
public void onDrop(PlayerDropItemEvent event) {
|
||||||
var player = PortableMediaPlayers.get(event.getItemDrop().getItemStack());
|
var player = PortableMediaPlayers.fromItemStack(event.getItemDrop().getItemStack());
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
|
|
||||||
System.out.println("A playe rdropped");
|
System.out.println("A playe rdropped");
|
||||||
|
|
@ -75,7 +75,7 @@ public class ItemEvents implements Listener {
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPickup(EntityPickupItemEvent event) {
|
public void onPickup(EntityPickupItemEvent event) {
|
||||||
var player = PortableMediaPlayers.get(event.getItem().getItemStack());
|
var player = PortableMediaPlayers.fromItemStack(event.getItem().getItemStack());
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
|
|
||||||
System.out.println("A player puckuperd");
|
System.out.println("A player puckuperd");
|
||||||
|
|
@ -86,7 +86,7 @@ public class ItemEvents implements Listener {
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onMove(InventoryMoveItemEvent event) {
|
public void onMove(InventoryMoveItemEvent event) {
|
||||||
if (event.getDestination().getType() == InventoryType.PLAYER) {
|
if (event.getDestination().getType() == InventoryType.PLAYER) {
|
||||||
var player = PortableMediaPlayers.get(event.getItem());
|
var player = PortableMediaPlayers.fromItemStack(event.getItem());
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
|
|
||||||
System.out.println("A player storaged :((");
|
System.out.println("A player storaged :((");
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
package eu.m724.music_plugin.item;
|
package eu.m724.music_plugin.item;
|
||||||
|
|
||||||
import eu.m724.music_plugin.MusicPlugin;
|
import eu.m724.music_plugin.library.Track;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandExecutor;
|
import org.bukkit.command.CommandExecutor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class PmpCommand implements CommandExecutor {
|
public class PmpCommand implements CommandExecutor {
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||||
var player = (Player) sender;
|
var player = (Player) sender;
|
||||||
var pmp = PortableMediaPlayers.get(player.getInventory().getItemInMainHand());
|
var pmp = PortableMediaPlayers.fromItemStack(player.getInventory().getItemInMainHand());
|
||||||
|
|
||||||
var action = args.length > 0 ? args[0] : null;
|
var action = args.length > 0 ? args[0] : null;
|
||||||
|
|
||||||
|
|
@ -22,18 +21,7 @@ public class PmpCommand implements CommandExecutor {
|
||||||
if (pmp != null) {
|
if (pmp != null) {
|
||||||
sender.sendMessage("ID: " + pmp.id);
|
sender.sendMessage("ID: " + pmp.id);
|
||||||
sender.sendMessage("Premium: " + pmp.premium);
|
sender.sendMessage("Premium: " + pmp.premium);
|
||||||
|
sender.sendMessage("Capacity: " + secondsToHms(pmp.storageSeconds));
|
||||||
var capacityStr = "";
|
|
||||||
var h = pmp.storageSeconds % 3600;
|
|
||||||
var m = (pmp.storageSeconds - (h * 3600)) % 60;
|
|
||||||
var s = pmp.storageSeconds - (h * 3600) - (m * 60);
|
|
||||||
if (h > 0) {
|
|
||||||
capacityStr += h + "h ";
|
|
||||||
}
|
|
||||||
capacityStr += m + "m " + s + "s ";
|
|
||||||
|
|
||||||
sender.sendMessage("Capacity: " + capacityStr);
|
|
||||||
|
|
||||||
sender.sendMessage("Bitrate: %d Kbps".formatted(pmp.audioBitrate / 1000));
|
sender.sendMessage("Bitrate: %d Kbps".formatted(pmp.audioBitrate / 1000));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -41,23 +29,57 @@ public class PmpCommand implements CommandExecutor {
|
||||||
if ("create".equals(action)) {
|
if ("create".equals(action)) {
|
||||||
pmp = PortableMediaPlayer.create(args[1].equals("yes"), String.join(" ", Arrays.asList(args).subList(2, args.length)));
|
pmp = PortableMediaPlayer.create(args[1].equals("yes"), String.join(" ", Arrays.asList(args).subList(2, args.length)));
|
||||||
player.getInventory().addItem(pmp.getItemStack());
|
player.getInventory().addItem(pmp.getItemStack());
|
||||||
} else if ("play".equals(action)) {
|
} else if ("add".equals(action)) {
|
||||||
if (pmp != null) {
|
if (pmp != null) {
|
||||||
var storage = MusicPlugin.getStorage();
|
var library = pmp.getLibrary();
|
||||||
|
library.addTrack(new Track(args[1]));
|
||||||
|
|
||||||
|
player.sendMessage("Added track");
|
||||||
|
|
||||||
|
/*var storage = MusicPlugin.getStorage();
|
||||||
var path = storage.get(args[1], Integer.parseInt(args[2]));
|
var path = storage.get(args[1], Integer.parseInt(args[2]));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pmp.play(path.toFile());
|
pmp.play(path.toFile());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}*/
|
||||||
|
} else {
|
||||||
|
player.sendMessage("You must hold a Portable Music Player");
|
||||||
|
}
|
||||||
|
} else if ("play".equals(action)) {
|
||||||
|
if (pmp != null) {
|
||||||
|
pmp.play();
|
||||||
|
player.sendMessage("Started");
|
||||||
|
} else {
|
||||||
|
player.sendMessage("You must hold a Portable Music Player");
|
||||||
|
}
|
||||||
|
} else if ("skip".equals(action)) {
|
||||||
|
if (pmp != null) {
|
||||||
|
pmp.next();
|
||||||
|
player.sendMessage("Skipped");
|
||||||
} else {
|
} else {
|
||||||
player.sendMessage("You must hold a Portable Music Player");
|
player.sendMessage("You must hold a Portable Music Player");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
player.sendMessage("create | play");
|
player.sendMessage("create | add | play | skip");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String secondsToHms(int seconds) {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
var hours = seconds / 3600;
|
||||||
|
if (hours > 0) {
|
||||||
|
sb.append(hours).append("h ");
|
||||||
|
seconds = seconds % 3600;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(seconds / 60).append("m ");
|
||||||
|
sb.append(seconds % 60).append("s");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,37 +5,28 @@ import eu.m724.music_plugin.MusicPlugin;
|
||||||
import eu.m724.music_plugin.audio.player.OpusFilePlayer;
|
import eu.m724.music_plugin.audio.player.OpusFilePlayer;
|
||||||
import eu.m724.music_plugin.audio.player.TrackEvent;
|
import eu.m724.music_plugin.audio.player.TrackEvent;
|
||||||
import eu.m724.music_plugin.item.speaker.Speaker;
|
import eu.m724.music_plugin.item.speaker.Speaker;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import eu.m724.music_plugin.library.Library;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.NamespacedKey;
|
|
||||||
import org.bukkit.enchantments.Enchantment;
|
|
||||||
import org.bukkit.inventory.ItemFlag;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.persistence.PersistentDataType;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.util.concurrent.CompletionException;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
public class PortableMediaPlayer {
|
public class PortableMediaPlayer {
|
||||||
static final NamespacedKey idKey = MusicPlugin.getNamespacedKey("player_id");
|
// TODO getters for all that
|
||||||
static final NamespacedKey dataKey = MusicPlugin.getNamespacedKey("player_data");
|
|
||||||
|
|
||||||
public final int id;
|
public final int id;
|
||||||
|
|
||||||
// TODO configurable
|
// TODO make those two configurable
|
||||||
public final int storageSeconds;
|
public final int storageSeconds;
|
||||||
public final int audioBitrate;
|
public final int audioBitrate;
|
||||||
|
|
||||||
public final boolean premium;
|
public final boolean premium;
|
||||||
public final String engraving;
|
public final String engraving;
|
||||||
|
|
||||||
|
private final Library library;
|
||||||
private OpusFilePlayer player; // TODO rename?
|
private OpusFilePlayer player; // TODO rename?
|
||||||
private Speaker<?> speaker;
|
private Speaker<?> linkedSpeaker;
|
||||||
|
|
||||||
PortableMediaPlayer(int id, int storageSeconds, int audioBitrate, boolean premium, String engraving) {
|
PortableMediaPlayer(int id, int storageSeconds, int audioBitrate, boolean premium, String engraving) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|
@ -43,55 +34,45 @@ public class PortableMediaPlayer {
|
||||||
this.audioBitrate = audioBitrate;
|
this.audioBitrate = audioBitrate;
|
||||||
this.premium = premium;
|
this.premium = premium;
|
||||||
this.engraving = engraving;
|
this.engraving = engraving;
|
||||||
|
|
||||||
|
// TODO there should be a better way. Like load if needed?
|
||||||
|
this.library = MusicPlugin.getLibraryStorage().load(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PortableMediaPlayer create(boolean premium, String engraving) {
|
public static PortableMediaPlayer create(boolean premium, String engraving) {
|
||||||
return new PortableMediaPlayer(ThreadLocalRandom.current().nextInt(), 600, 32000, premium, engraving);
|
return new PortableMediaPlayer(ThreadLocalRandom.current().nextInt(), 600, 32000, premium, engraving);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSpeaker(Speaker<?> speaker) {
|
public void play() {
|
||||||
if (speaker.equals(this.speaker))
|
DebugLogger.fine("play() called");
|
||||||
return;
|
library.getPlaying().getPath(audioBitrate).thenAccept(p -> {
|
||||||
|
DebugLogger.finer("accepted");
|
||||||
if (this.speaker != null) {
|
try {
|
||||||
// no need to pause if no need to pause
|
play(p.toAbsolutePath().toFile());
|
||||||
this.speaker.setDestroyCallback(c -> {});
|
} catch (IOException e) {
|
||||||
this.speaker.destroy();
|
e.printStackTrace();
|
||||||
}
|
throw new CompletionException(e);
|
||||||
|
}
|
||||||
speaker.setDestroyCallback(v -> {
|
|
||||||
System.out.println("spekar rip");
|
|
||||||
if (player != null)
|
|
||||||
player.pause();
|
|
||||||
this.speaker = null;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.speaker = speaker;
|
|
||||||
if (player != null)
|
|
||||||
player.setChannel(speaker.getChannel().getAudioChannel());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void play(File file) throws IOException {
|
private void play(File file) throws IOException {
|
||||||
DebugLogger.finer("pmp play");
|
DebugLogger.finer("playing a file: %s", file.toString());
|
||||||
if (speaker == null) return;
|
if (linkedSpeaker == null) return;
|
||||||
|
|
||||||
//played = new Track(audioFile);
|
|
||||||
if (player != null)
|
if (player != null)
|
||||||
player.stop();
|
player.stop();
|
||||||
|
|
||||||
this.player = new OpusFilePlayer(file, speaker.getChannel().getAudioChannel());
|
this.player = new OpusFilePlayer(file, linkedSpeaker.getChannel().getAudioChannel());
|
||||||
|
|
||||||
player.setOnTrackEvent(event -> {
|
player.setOnTrackEvent(event -> {
|
||||||
if (event == TrackEvent.START) {
|
if (event == TrackEvent.START) {
|
||||||
DebugLogger.finer("I detected track START");
|
DebugLogger.finer("I detected track START");
|
||||||
//played.unpause();
|
|
||||||
} else if (event == TrackEvent.STOP) {
|
} else if (event == TrackEvent.STOP) {
|
||||||
DebugLogger.finer("I detected track STOP");
|
DebugLogger.finer("I detected track STOP");
|
||||||
//played.pause();
|
|
||||||
//played.hint(played.file.getEncoder().getFrame());
|
|
||||||
} else if (event == TrackEvent.END) {
|
} else if (event == TrackEvent.END) {
|
||||||
DebugLogger.finer("I detected track END");
|
DebugLogger.finer("I detected track END");
|
||||||
//next();
|
next();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -119,58 +100,46 @@ public class PortableMediaPlayer {
|
||||||
player.stop();
|
player.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
void previous() {
|
||||||
void prev() {
|
// TODO seek to beginning
|
||||||
DebugLogger.fine("pmp previous (does nothing)");
|
if (library.previous() != null)
|
||||||
|
play();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
|
||||||
void next() {
|
void next() {
|
||||||
System.out.println("pmp next (does nothing)");
|
if (library.next() != null)
|
||||||
|
play();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Item functions */
|
/* Getters / Setters */
|
||||||
|
|
||||||
public ItemStack getItemStack() {
|
public void setSpeaker(Speaker<?> speaker) {
|
||||||
var is = new ItemStack(premium ? Material.GOLD_INGOT : Material.IRON_INGOT);
|
if (speaker.equals(this.linkedSpeaker))
|
||||||
var meta = is.getItemMeta();
|
return;
|
||||||
|
|
||||||
if (premium) {
|
if (this.linkedSpeaker != null) {
|
||||||
var hue = (id & 0xFFFFFF) / (float) 0xFFFFFF;
|
// no need to pause if no need to pause
|
||||||
var saturation = (id >> 24) & 0xF;
|
this.linkedSpeaker.setDestroyCallback(c -> {});
|
||||||
var color = Color.getHSBColor(hue, 0.84f + saturation / 15.0f, 1.0f);
|
this.linkedSpeaker.destroy();
|
||||||
meta.setItemName(ChatColor.of(color) + "Portable Music Player");
|
|
||||||
|
|
||||||
if (engraving != null)
|
|
||||||
meta.setLore(List.of(ChatColor.of(color.darker()) + engraving));
|
|
||||||
} else {
|
|
||||||
meta.setItemName("Portable Music Player");
|
|
||||||
|
|
||||||
if (engraving != null) // custom colors only in premium
|
|
||||||
meta.setLore(List.of(ChatColor.GRAY + ChatColor.stripColor(engraving)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.addEnchant(Enchantment.UNBREAKING, 1, false);
|
speaker.setDestroyCallback(v -> {
|
||||||
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
|
DebugLogger.finer("linked speaker destroyed");
|
||||||
meta.getPersistentDataContainer().set(idKey, PersistentDataType.INTEGER, id);
|
if (player != null)
|
||||||
meta.getPersistentDataContainer().set(dataKey, PersistentDataType.BYTE_ARRAY, getData());
|
player.pause();
|
||||||
|
this.linkedSpeaker = null;
|
||||||
|
});
|
||||||
|
|
||||||
is.setItemMeta(meta);
|
this.linkedSpeaker = speaker;
|
||||||
|
if (player != null)
|
||||||
return is;
|
player.setChannel(speaker.getChannel().getAudioChannel());
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getData() {
|
public ItemStack getItemStack() {
|
||||||
var buffer = ByteBuffer.allocate(11 + engraving.length());
|
return PortableMediaPlayers.getItemStack(this);
|
||||||
buffer.put((byte) 0); // data format version
|
}
|
||||||
buffer.put((byte) (premium ? 1 : 0));
|
|
||||||
|
|
||||||
buffer.putShort((short) storageSeconds); // make int if 18 hours is not enough. or store as minutes
|
public Library getLibrary() {
|
||||||
buffer.put((byte) (audioBitrate / 1000));
|
return this.library;
|
||||||
|
|
||||||
buffer.put((byte) engraving.length());
|
|
||||||
buffer.put(engraving.getBytes(StandardCharsets.UTF_8));
|
|
||||||
|
|
||||||
return buffer.array();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,36 @@
|
||||||
package eu.m724.music_plugin.item;
|
package eu.m724.music_plugin.item;
|
||||||
|
|
||||||
|
import eu.m724.music_plugin.MusicPlugin;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
|
import org.bukkit.enchantments.Enchantment;
|
||||||
|
import org.bukkit.inventory.ItemFlag;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.persistence.PersistentDataType;
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
// This class stores PortableMediaPlayer instances so that there's no hassle of working with a new one every time
|
|
||||||
public class PortableMediaPlayers {
|
public class PortableMediaPlayers {
|
||||||
private static Map<Integer, PortableMediaPlayer> players = new HashMap<>();
|
static final NamespacedKey idKey = MusicPlugin.getNamespacedKey("player_id");
|
||||||
|
static final NamespacedKey dataKey = MusicPlugin.getNamespacedKey("player_data");
|
||||||
|
|
||||||
public static PortableMediaPlayer get(Integer id) {
|
private static final Map<Integer, PortableMediaPlayer> players = new HashMap<>();
|
||||||
|
|
||||||
|
public static PortableMediaPlayer fromId(Integer id) {
|
||||||
return players.get(id);
|
return players.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PortableMediaPlayer get(ItemStack itemStack) {
|
public static PortableMediaPlayer fromItemStack(ItemStack itemStack) {
|
||||||
var meta = itemStack.getItemMeta();
|
var meta = itemStack.getItemMeta();
|
||||||
if (meta == null) return null;
|
if (meta == null) return null;
|
||||||
|
|
||||||
var id = meta.getPersistentDataContainer().get(PortableMediaPlayer.idKey, PersistentDataType.INTEGER);
|
var id = meta.getPersistentDataContainer().get(idKey, PersistentDataType.INTEGER);
|
||||||
if (id == null) return null;
|
if (id == null) return null;
|
||||||
|
|
||||||
// check cache
|
// check cache
|
||||||
|
|
@ -30,7 +40,7 @@ public class PortableMediaPlayers {
|
||||||
|
|
||||||
// if not cached
|
// if not cached
|
||||||
|
|
||||||
var data = meta.getPersistentDataContainer().get(PortableMediaPlayer.dataKey, PersistentDataType.BYTE_ARRAY);
|
var data = meta.getPersistentDataContainer().get(dataKey, PersistentDataType.BYTE_ARRAY);
|
||||||
if (data == null) return null;
|
if (data == null) return null;
|
||||||
|
|
||||||
player = fromData(id, data);
|
player = fromData(id, data);
|
||||||
|
|
@ -57,4 +67,47 @@ public class PortableMediaPlayers {
|
||||||
|
|
||||||
return new PortableMediaPlayer(id, storageSeconds, audioBitrate, premium, engraving);
|
return new PortableMediaPlayer(id, storageSeconds, audioBitrate, premium, engraving);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static byte[] getData(PortableMediaPlayer player) {
|
||||||
|
var buffer = ByteBuffer.allocate(11 + player.engraving.length());
|
||||||
|
buffer.put((byte) 0); // data format version
|
||||||
|
buffer.put((byte) (player.premium ? 1 : 0));
|
||||||
|
|
||||||
|
buffer.putShort((short) player.storageSeconds); // make int if 18 hours is not enough. or store as minutes
|
||||||
|
buffer.put((byte) (player.audioBitrate / 1000));
|
||||||
|
|
||||||
|
buffer.put((byte) player.engraving.length());
|
||||||
|
buffer.put(player.engraving.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
return buffer.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack getItemStack(PortableMediaPlayer player) {
|
||||||
|
var is = new ItemStack(player.premium ? Material.GOLD_INGOT : Material.IRON_INGOT);
|
||||||
|
var meta = is.getItemMeta();
|
||||||
|
|
||||||
|
if (player.premium) {
|
||||||
|
var hue = (player.id & 0xFFFFFF) / (float) 0xFFFFFF;
|
||||||
|
var saturation = (player.id >> 24) & 0xF;
|
||||||
|
var color = Color.getHSBColor(hue, 0.84f + saturation / 15.0f, 1.0f);
|
||||||
|
meta.setItemName(ChatColor.of(color) + "Portable Music Player");
|
||||||
|
|
||||||
|
if (player.engraving != null)
|
||||||
|
meta.setLore(java.util.List.of(ChatColor.of(color.darker()) + player.engraving));
|
||||||
|
} else {
|
||||||
|
meta.setItemName("Portable Music Player");
|
||||||
|
|
||||||
|
if (player.engraving != null) // custom colors only in premium
|
||||||
|
meta.setLore(List.of(ChatColor.GRAY + ChatColor.stripColor(player.engraving)));
|
||||||
|
}
|
||||||
|
|
||||||
|
meta.addEnchant(Enchantment.UNBREAKING, 1, false);
|
||||||
|
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
|
||||||
|
meta.getPersistentDataContainer().set(idKey, PersistentDataType.INTEGER, player.id);
|
||||||
|
meta.getPersistentDataContainer().set(dataKey, PersistentDataType.BYTE_ARRAY, getData(player));
|
||||||
|
|
||||||
|
is.setItemMeta(meta);
|
||||||
|
|
||||||
|
return is;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,21 @@
|
||||||
package eu.m724.music_plugin.library;
|
package eu.m724.music_plugin.library;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Library {
|
public class Library {
|
||||||
private List<Track> tracks = new ArrayList<>();
|
private final int id;
|
||||||
private int playingTrack;
|
|
||||||
|
private final List<Track> tracks = new ArrayList<>();
|
||||||
|
private int playingTrack = 0;
|
||||||
|
|
||||||
|
public Library(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
public void addTrack(Track track) {
|
public void addTrack(Track track) {
|
||||||
tracks.add(track);
|
tracks.add(track);
|
||||||
|
|
@ -21,14 +29,27 @@ public class Library {
|
||||||
return tracks.get(playingTrack);
|
return tracks.get(playingTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Library load(InputStream inputStream) throws IOException {
|
/**
|
||||||
var tracks = new ArrayList<Track>();
|
* Previous Track
|
||||||
|
* @return the previous Track or null if this was the first
|
||||||
byte[] bytes = new byte[32];
|
*/
|
||||||
while (inputStream.read(bytes) == 32) {
|
public Track previous() {
|
||||||
tracks.add(new Track(bytes));
|
if (playingTrack > 0) {
|
||||||
|
return tracks.get(--playingTrack);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new Library()
|
/**
|
||||||
|
* Next Track
|
||||||
|
* @return the next Track or null if this was the last
|
||||||
|
*/
|
||||||
|
public Track next() {
|
||||||
|
if (playingTrack + 1 < tracks.size()) {
|
||||||
|
return tracks.get(++playingTrack);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package eu.m724.music_plugin.library;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import eu.m724.music_plugin.DebugLogger;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HexFormat;
|
||||||
|
|
||||||
|
public class LibraryStorage {
|
||||||
|
private final Path libraryStoragePath;
|
||||||
|
|
||||||
|
public LibraryStorage(Path libraryStoragePath) {
|
||||||
|
this.libraryStoragePath = libraryStoragePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Library load(int id) {
|
||||||
|
DebugLogger.fine("Now \"loading\" library %s", HexFormat.of().formatHex(Ints.toByteArray(id)));
|
||||||
|
// TODO
|
||||||
|
return new Library(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,52 @@
|
||||||
package eu.m724.music_plugin.library;
|
package eu.m724.music_plugin.library;
|
||||||
|
|
||||||
|
import eu.m724.music_plugin.DebugLogger;
|
||||||
|
import eu.m724.music_plugin.MusicPlugin;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.HexFormat;
|
import java.util.HexFormat;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class Track {
|
public class Track {
|
||||||
|
/** SHA-256 hash as 32 bytes */
|
||||||
private final byte[] hash;
|
private final byte[] hash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Track from an SHA-256 hash bytes
|
||||||
|
* @param hash the 32 bytes of the hash
|
||||||
|
*/
|
||||||
public Track(byte[] hash) {
|
public Track(byte[] hash) {
|
||||||
assert hash.length == 32;
|
assert hash.length == 32;
|
||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Track from an SHA-256 hex encoded hash
|
||||||
|
* @param hash the hex encoded hash
|
||||||
|
*/
|
||||||
public Track(String hash) {
|
public Track(String hash) {
|
||||||
this(HexFormat.of().parseHex(hash));
|
this(HexFormat.of().parseHex(hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the SHA-256 hash as 32 bytes
|
||||||
|
*/
|
||||||
public byte[] getHash() {
|
public byte[] getHash() {
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getHashHex() {
|
||||||
|
return HexFormat.of().formatHex(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Path> getPath(int bitrate) {
|
||||||
|
var path = MusicPlugin.getStorage().get(getHashHex(), bitrate);
|
||||||
|
if (Files.isRegularFile(path))
|
||||||
|
return CompletableFuture.completedFuture(path);
|
||||||
|
|
||||||
|
DebugLogger.fine("Need to convert to %d", bitrate);
|
||||||
|
|
||||||
|
return MusicPlugin.getStorage().convert(MusicPlugin.getConverter(), getHashHex(), bitrate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue