From 0c45358fa65b7fcef84e5624cdd333ec924a0ea3 Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Sat, 1 Feb 2025 13:02:21 +0100 Subject: [PATCH] library --- pom.xml | 9 ++ .../eu/m724/music_plugin/MusicPlugin.java | 17 ++- .../eu/m724/music_plugin/TestCommand.java | 35 ++--- .../audio/storage/AudioFileStorage.java | 14 +- .../audio/storage/Downloader.java | 2 +- .../eu/m724/music_plugin/item/ItemEvents.java | 10 +- .../eu/m724/music_plugin/item/PmpCommand.java | 60 +++++--- .../item/PortableMediaPlayer.java | 135 +++++++----------- .../item/PortableMediaPlayers.java | 65 ++++++++- .../eu/m724/music_plugin/library/Library.java | 43 ++++-- .../music_plugin/library/LibraryStorage.java | 21 +++ .../eu/m724/music_plugin/library/Track.java | 32 +++++ 12 files changed, 292 insertions(+), 151 deletions(-) create mode 100644 src/main/java/eu/m724/music_plugin/library/LibraryStorage.java diff --git a/pom.xml b/pom.xml index 804adc0..edd23ce 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,15 @@ de.themoep:inventorygui + + + * + + META-INF/MANIFEST.MF + META-INF/maven/** + + + diff --git a/src/main/java/eu/m724/music_plugin/MusicPlugin.java b/src/main/java/eu/m724/music_plugin/MusicPlugin.java index 9a234aa..d61b8bd 100644 --- a/src/main/java/eu/m724/music_plugin/MusicPlugin.java +++ b/src/main/java/eu/m724/music_plugin/MusicPlugin.java @@ -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); } diff --git a/src/main/java/eu/m724/music_plugin/TestCommand.java b/src/main/java/eu/m724/music_plugin/TestCommand.java index 31cdc23..eda2054 100644 --- a/src/main/java/eu/m724/music_plugin/TestCommand.java +++ b/src/main/java/eu/m724/music_plugin/TestCommand.java @@ -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) { diff --git a/src/main/java/eu/m724/music_plugin/audio/storage/AudioFileStorage.java b/src/main/java/eu/m724/music_plugin/audio/storage/AudioFileStorage.java index f90433a..9109f5c 100644 --- a/src/main/java/eu/m724/music_plugin/audio/storage/AudioFileStorage.java +++ b/src/main/java/eu/m724/music_plugin/audio/storage/AudioFileStorage.java @@ -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
+ *
+ * WARNING Whether the file exists is not checked.
+ * 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 convert(Converter converter, String hash, int bitrate) throws IOException { + public CompletableFuture 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); + } } } diff --git a/src/main/java/eu/m724/music_plugin/audio/storage/Downloader.java b/src/main/java/eu/m724/music_plugin/audio/storage/Downloader.java index 5887699..1dbd43d 100644 --- a/src/main/java/eu/m724/music_plugin/audio/storage/Downloader.java +++ b/src/main/java/eu/m724/music_plugin/audio/storage/Downloader.java @@ -34,7 +34,7 @@ public class Downloader { public CompletableFuture 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() diff --git a/src/main/java/eu/m724/music_plugin/item/ItemEvents.java b/src/main/java/eu/m724/music_plugin/item/ItemEvents.java index 7e62569..24e6415 100644 --- a/src/main/java/eu/m724/music_plugin/item/ItemEvents.java +++ b/src/main/java/eu/m724/music_plugin/item/ItemEvents.java @@ -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 :(("); diff --git a/src/main/java/eu/m724/music_plugin/item/PmpCommand.java b/src/main/java/eu/m724/music_plugin/item/PmpCommand.java index 7e1254b..83125c4 100644 --- a/src/main/java/eu/m724/music_plugin/item/PmpCommand.java +++ b/src/main/java/eu/m724/music_plugin/item/PmpCommand.java @@ -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(); + } } diff --git a/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayer.java b/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayer.java index 13a7279..c7e9601 100644 --- a/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayer.java +++ b/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayer.java @@ -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; } } diff --git a/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayers.java b/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayers.java index feab236..7782ad4 100644 --- a/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayers.java +++ b/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayers.java @@ -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 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 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; + } } diff --git a/src/main/java/eu/m724/music_plugin/library/Library.java b/src/main/java/eu/m724/music_plugin/library/Library.java index 643159d..bb59d93 100644 --- a/src/main/java/eu/m724/music_plugin/library/Library.java +++ b/src/main/java/eu/m724/music_plugin/library/Library.java @@ -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 tracks = new ArrayList<>(); - private int playingTrack; + private final int id; + + private final List 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(); - - 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; + } } } diff --git a/src/main/java/eu/m724/music_plugin/library/LibraryStorage.java b/src/main/java/eu/m724/music_plugin/library/LibraryStorage.java new file mode 100644 index 0000000..fc0dfd4 --- /dev/null +++ b/src/main/java/eu/m724/music_plugin/library/LibraryStorage.java @@ -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); + } +} diff --git a/src/main/java/eu/m724/music_plugin/library/Track.java b/src/main/java/eu/m724/music_plugin/library/Track.java index 66f317e..85b2bd2 100644 --- a/src/main/java/eu/m724/music_plugin/library/Track.java +++ b/src/main/java/eu/m724/music_plugin/library/Track.java @@ -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 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); + } }