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>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/MANIFEST.MF</exclude>
|
||||
<exclude>META-INF/maven/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</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.PmpCommand;
|
||||
import eu.m724.music_plugin.item.speaker.BlockChecker;
|
||||
import eu.m724.music_plugin.library.LibraryStorage;
|
||||
import net.bramp.ffmpeg.FFmpeg;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
@ -23,6 +24,7 @@ public final class MusicPlugin extends JavaPlugin {
|
|||
private static AudioFileStorage AUDIO_FILE_STORAGE;
|
||||
private static Converter CONVERTER;
|
||||
private static Downloader DOWNLOADER;
|
||||
private static LibraryStorage LIBRARY_STORAGE;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
|
@ -41,14 +43,19 @@ public final class MusicPlugin extends JavaPlugin {
|
|||
service.registerPlugin(new MyVoicechatPlugin(api -> VOICECHAT_API = api));
|
||||
|
||||
var storagePath = getDataFolder().toPath().resolve("storage");
|
||||
var audioStoragePath = storagePath.resolve("audio");
|
||||
var libraryStoragePath = storagePath.resolve("library");
|
||||
|
||||
try {
|
||||
Files.createDirectories(storagePath);
|
||||
Files.createDirectories(audioStoragePath);
|
||||
Files.createDirectories(libraryStoragePath);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
AUDIO_FILE_STORAGE = new AudioFileStorage(storagePath);
|
||||
DOWNLOADER = new Downloader(storagePath);
|
||||
AUDIO_FILE_STORAGE = new AudioFileStorage(audioStoragePath);
|
||||
DOWNLOADER = new Downloader(audioStoragePath);
|
||||
LIBRARY_STORAGE = new LibraryStorage(libraryStoragePath);
|
||||
|
||||
try {
|
||||
CONVERTER = new Converter(new FFmpeg());
|
||||
|
@ -91,6 +98,10 @@ public final class MusicPlugin extends JavaPlugin {
|
|||
return DOWNLOADER;
|
||||
}
|
||||
|
||||
public static LibraryStorage getLibraryStorage() {
|
||||
return LIBRARY_STORAGE;
|
||||
}
|
||||
|
||||
public static NamespacedKey getNamespacedKey(String key) {
|
||||
return new NamespacedKey(INSTANCE, key);
|
||||
}
|
||||
|
|
|
@ -60,26 +60,21 @@ public class TestCommand implements CommandExecutor {
|
|||
}
|
||||
|
||||
private void testConvert(Player player, String hash, int bitrate) {
|
||||
try {
|
||||
player.sendMessage("Converting...");
|
||||
MusicPlugin.getStorage().convert(MusicPlugin.getConverter(), hash, bitrate).handle((f, ex) -> {
|
||||
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) {
|
||||
e.printStackTrace();
|
||||
player.sendMessage("Error");
|
||||
}
|
||||
player.sendMessage("Converting...");
|
||||
MusicPlugin.getStorage().convert(MusicPlugin.getConverter(), hash, bitrate).handle((f, ex) -> {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
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 bitrate the bitrate in bps like 32000
|
||||
|
@ -77,13 +80,18 @@ public class AudioFileStorage {
|
|||
* @param bitrate the target bitrate in bps
|
||||
* @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);
|
||||
if (Files.exists(file)) return CompletableFuture.completedFuture(file);
|
||||
|
||||
var og = getOriginal(hash);
|
||||
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) {
|
||||
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(() -> {
|
||||
try (
|
||||
var client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build()
|
||||
|
|
|
@ -20,7 +20,7 @@ public class ItemEvents implements Listener {
|
|||
|
||||
if (event.getItem() == null) return;
|
||||
|
||||
var player = PortableMediaPlayers.get(event.getItem());
|
||||
var player = PortableMediaPlayers.fromItemStack(event.getItem());
|
||||
if (player == null) return;
|
||||
|
||||
System.out.println("A player used");
|
||||
|
@ -31,7 +31,7 @@ public class ItemEvents implements Listener {
|
|||
player.next();
|
||||
} else {
|
||||
System.out.println("no shift + lmb: rpevous song");
|
||||
player.prev();
|
||||
player.previous();
|
||||
}
|
||||
} else {
|
||||
if (right) {
|
||||
|
@ -65,7 +65,7 @@ public class ItemEvents implements Listener {
|
|||
|
||||
@EventHandler
|
||||
public void onDrop(PlayerDropItemEvent event) {
|
||||
var player = PortableMediaPlayers.get(event.getItemDrop().getItemStack());
|
||||
var player = PortableMediaPlayers.fromItemStack(event.getItemDrop().getItemStack());
|
||||
if (player == null) return;
|
||||
|
||||
System.out.println("A playe rdropped");
|
||||
|
@ -75,7 +75,7 @@ public class ItemEvents implements Listener {
|
|||
|
||||
@EventHandler
|
||||
public void onPickup(EntityPickupItemEvent event) {
|
||||
var player = PortableMediaPlayers.get(event.getItem().getItemStack());
|
||||
var player = PortableMediaPlayers.fromItemStack(event.getItem().getItemStack());
|
||||
if (player == null) return;
|
||||
|
||||
System.out.println("A player puckuperd");
|
||||
|
@ -86,7 +86,7 @@ public class ItemEvents implements Listener {
|
|||
@EventHandler
|
||||
public void onMove(InventoryMoveItemEvent event) {
|
||||
if (event.getDestination().getType() == InventoryType.PLAYER) {
|
||||
var player = PortableMediaPlayers.get(event.getItem());
|
||||
var player = PortableMediaPlayers.fromItemStack(event.getItem());
|
||||
if (player == null) return;
|
||||
|
||||
System.out.println("A player storaged :((");
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
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.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class PmpCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
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;
|
||||
|
||||
|
@ -22,18 +21,7 @@ public class PmpCommand implements CommandExecutor {
|
|||
if (pmp != null) {
|
||||
sender.sendMessage("ID: " + pmp.id);
|
||||
sender.sendMessage("Premium: " + pmp.premium);
|
||||
|
||||
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("Capacity: " + secondsToHms(pmp.storageSeconds));
|
||||
sender.sendMessage("Bitrate: %d Kbps".formatted(pmp.audioBitrate / 1000));
|
||||
}
|
||||
}
|
||||
|
@ -41,23 +29,57 @@ public class PmpCommand implements CommandExecutor {
|
|||
if ("create".equals(action)) {
|
||||
pmp = PortableMediaPlayer.create(args[1].equals("yes"), String.join(" ", Arrays.asList(args).subList(2, args.length)));
|
||||
player.getInventory().addItem(pmp.getItemStack());
|
||||
} else if ("play".equals(action)) {
|
||||
} else if ("add".equals(action)) {
|
||||
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]));
|
||||
|
||||
try {
|
||||
pmp.play(path.toFile());
|
||||
} catch (IOException 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 {
|
||||
player.sendMessage("You must hold a Portable Music Player");
|
||||
}
|
||||
} else {
|
||||
player.sendMessage("create | play");
|
||||
player.sendMessage("create | add | play | skip");
|
||||
}
|
||||
|
||||
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.TrackEvent;
|
||||
import eu.m724.music_plugin.item.speaker.Speaker;
|
||||
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 eu.m724.music_plugin.library.Library;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class PortableMediaPlayer {
|
||||
static final NamespacedKey idKey = MusicPlugin.getNamespacedKey("player_id");
|
||||
static final NamespacedKey dataKey = MusicPlugin.getNamespacedKey("player_data");
|
||||
|
||||
// TODO getters for all that
|
||||
public final int id;
|
||||
|
||||
// TODO configurable
|
||||
// TODO make those two configurable
|
||||
public final int storageSeconds;
|
||||
public final int audioBitrate;
|
||||
|
||||
public final boolean premium;
|
||||
public final String engraving;
|
||||
|
||||
private final Library library;
|
||||
private OpusFilePlayer player; // TODO rename?
|
||||
private Speaker<?> speaker;
|
||||
private Speaker<?> linkedSpeaker;
|
||||
|
||||
PortableMediaPlayer(int id, int storageSeconds, int audioBitrate, boolean premium, String engraving) {
|
||||
this.id = id;
|
||||
|
@ -43,55 +34,45 @@ public class PortableMediaPlayer {
|
|||
this.audioBitrate = audioBitrate;
|
||||
this.premium = premium;
|
||||
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) {
|
||||
return new PortableMediaPlayer(ThreadLocalRandom.current().nextInt(), 600, 32000, premium, engraving);
|
||||
}
|
||||
|
||||
public void setSpeaker(Speaker<?> speaker) {
|
||||
if (speaker.equals(this.speaker))
|
||||
return;
|
||||
|
||||
if (this.speaker != null) {
|
||||
// no need to pause if no need to pause
|
||||
this.speaker.setDestroyCallback(c -> {});
|
||||
this.speaker.destroy();
|
||||
}
|
||||
|
||||
speaker.setDestroyCallback(v -> {
|
||||
System.out.println("spekar rip");
|
||||
if (player != null)
|
||||
player.pause();
|
||||
this.speaker = null;
|
||||
public void play() {
|
||||
DebugLogger.fine("play() called");
|
||||
library.getPlaying().getPath(audioBitrate).thenAccept(p -> {
|
||||
DebugLogger.finer("accepted");
|
||||
try {
|
||||
play(p.toAbsolutePath().toFile());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
});
|
||||
|
||||
this.speaker = speaker;
|
||||
if (player != null)
|
||||
player.setChannel(speaker.getChannel().getAudioChannel());
|
||||
}
|
||||
|
||||
public void play(File file) throws IOException {
|
||||
DebugLogger.finer("pmp play");
|
||||
if (speaker == null) return;
|
||||
private void play(File file) throws IOException {
|
||||
DebugLogger.finer("playing a file: %s", file.toString());
|
||||
if (linkedSpeaker == null) return;
|
||||
|
||||
//played = new Track(audioFile);
|
||||
if (player != null)
|
||||
player.stop();
|
||||
|
||||
this.player = new OpusFilePlayer(file, speaker.getChannel().getAudioChannel());
|
||||
this.player = new OpusFilePlayer(file, linkedSpeaker.getChannel().getAudioChannel());
|
||||
|
||||
player.setOnTrackEvent(event -> {
|
||||
if (event == TrackEvent.START) {
|
||||
DebugLogger.finer("I detected track START");
|
||||
//played.unpause();
|
||||
} else if (event == TrackEvent.STOP) {
|
||||
DebugLogger.finer("I detected track STOP");
|
||||
//played.pause();
|
||||
//played.hint(played.file.getEncoder().getFrame());
|
||||
} else if (event == TrackEvent.END) {
|
||||
DebugLogger.finer("I detected track END");
|
||||
//next();
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -119,58 +100,46 @@ public class PortableMediaPlayer {
|
|||
player.stop();
|
||||
}
|
||||
|
||||
// TODO
|
||||
void prev() {
|
||||
DebugLogger.fine("pmp previous (does nothing)");
|
||||
void previous() {
|
||||
// TODO seek to beginning
|
||||
if (library.previous() != null)
|
||||
play();
|
||||
}
|
||||
|
||||
// TODO
|
||||
void next() {
|
||||
System.out.println("pmp next (does nothing)");
|
||||
if (library.next() != null)
|
||||
play();
|
||||
}
|
||||
|
||||
/* Item functions */
|
||||
/* Getters / Setters */
|
||||
|
||||
public ItemStack getItemStack() {
|
||||
var is = new ItemStack(premium ? Material.GOLD_INGOT : Material.IRON_INGOT);
|
||||
var meta = is.getItemMeta();
|
||||
public void setSpeaker(Speaker<?> speaker) {
|
||||
if (speaker.equals(this.linkedSpeaker))
|
||||
return;
|
||||
|
||||
if (premium) {
|
||||
var hue = (id & 0xFFFFFF) / (float) 0xFFFFFF;
|
||||
var saturation = (id >> 24) & 0xF;
|
||||
var color = Color.getHSBColor(hue, 0.84f + saturation / 15.0f, 1.0f);
|
||||
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)));
|
||||
if (this.linkedSpeaker != null) {
|
||||
// no need to pause if no need to pause
|
||||
this.linkedSpeaker.setDestroyCallback(c -> {});
|
||||
this.linkedSpeaker.destroy();
|
||||
}
|
||||
|
||||
meta.addEnchant(Enchantment.UNBREAKING, 1, false);
|
||||
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
|
||||
meta.getPersistentDataContainer().set(idKey, PersistentDataType.INTEGER, id);
|
||||
meta.getPersistentDataContainer().set(dataKey, PersistentDataType.BYTE_ARRAY, getData());
|
||||
speaker.setDestroyCallback(v -> {
|
||||
DebugLogger.finer("linked speaker destroyed");
|
||||
if (player != null)
|
||||
player.pause();
|
||||
this.linkedSpeaker = null;
|
||||
});
|
||||
|
||||
is.setItemMeta(meta);
|
||||
|
||||
return is;
|
||||
this.linkedSpeaker = speaker;
|
||||
if (player != null)
|
||||
player.setChannel(speaker.getChannel().getAudioChannel());
|
||||
}
|
||||
|
||||
private byte[] getData() {
|
||||
var buffer = ByteBuffer.allocate(11 + engraving.length());
|
||||
buffer.put((byte) 0); // data format version
|
||||
buffer.put((byte) (premium ? 1 : 0));
|
||||
public ItemStack getItemStack() {
|
||||
return PortableMediaPlayers.getItemStack(this);
|
||||
}
|
||||
|
||||
buffer.putShort((short) storageSeconds); // make int if 18 hours is not enough. or store as minutes
|
||||
buffer.put((byte) (audioBitrate / 1000));
|
||||
|
||||
buffer.put((byte) engraving.length());
|
||||
buffer.put(engraving.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
return buffer.array();
|
||||
public Library getLibrary() {
|
||||
return this.library;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,36 @@
|
|||
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.persistence.PersistentDataType;
|
||||
|
||||
import java.awt.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
public static PortableMediaPlayer get(ItemStack itemStack) {
|
||||
public static PortableMediaPlayer fromItemStack(ItemStack itemStack) {
|
||||
var meta = itemStack.getItemMeta();
|
||||
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;
|
||||
|
||||
// check cache
|
||||
|
@ -30,7 +40,7 @@ public class PortableMediaPlayers {
|
|||
|
||||
// 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;
|
||||
|
||||
player = fromData(id, data);
|
||||
|
@ -57,4 +67,47 @@ public class PortableMediaPlayers {
|
|||
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Library {
|
||||
private List<Track> tracks = new ArrayList<>();
|
||||
private int playingTrack;
|
||||
private final int id;
|
||||
|
||||
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) {
|
||||
tracks.add(track);
|
||||
|
@ -21,14 +29,27 @@ public class Library {
|
|||
return tracks.get(playingTrack);
|
||||
}
|
||||
|
||||
public static Library load(InputStream inputStream) throws IOException {
|
||||
var tracks = new ArrayList<Track>();
|
||||
|
||||
byte[] bytes = new byte[32];
|
||||
while (inputStream.read(bytes) == 32) {
|
||||
tracks.add(new Track(bytes));
|
||||
/**
|
||||
* Previous Track
|
||||
* @return the previous Track or null if this was the first
|
||||
*/
|
||||
public Track previous() {
|
||||
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;
|
||||
|
||||
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.concurrent.CompletableFuture;
|
||||
|
||||
public class Track {
|
||||
/** SHA-256 hash as 32 bytes */
|
||||
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) {
|
||||
assert hash.length == 32;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Track from an SHA-256 hex encoded hash
|
||||
* @param hash the hex encoded hash
|
||||
*/
|
||||
public Track(String hash) {
|
||||
this(HexFormat.of().parseHex(hash));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the SHA-256 hash as 32 bytes
|
||||
*/
|
||||
public byte[] getHash() {
|
||||
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