--- a/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
@@ -20,14 +20,61 @@
 import net.minecraft.world.phys.Vec3;
 import net.minecraft.world.ticks.ContainerSingleItem;
 
+// CraftBukkit start
+import java.util.Collections;
+import java.util.List;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.entity.CraftHumanEntity;
+import org.bukkit.entity.HumanEntity;
+// CraftBukkit end
+
 public class JukeboxBlockEntity extends BlockEntity implements Clearable, ContainerSingleItem.BlockContainerSingleItem {
+
     public static final String SONG_ITEM_TAG_ID = "RecordItem";
     public static final String TICKS_SINCE_SONG_STARTED_TAG_ID = "ticks_since_song_started";
-    private ItemStack item = ItemStack.EMPTY;
-    private final JukeboxSongPlayer jukeboxSongPlayer = new JukeboxSongPlayer(this::onSongChanged, this.getBlockPos());
+    private ItemStack item;
+    private final JukeboxSongPlayer jukeboxSongPlayer;
+    // CraftBukkit start - add fields and methods
+    public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
+    private int maxStack = MAX_STACK;
+    public boolean opened;
+
+    @Override
+    public List<ItemStack> getContents() {
+        return Collections.singletonList(this.item);
+    }
+
+    @Override
+    public void onOpen(CraftHumanEntity who) {
+        this.transaction.add(who);
+    }
+
+    @Override
+    public void onClose(CraftHumanEntity who) {
+        this.transaction.remove(who);
+    }
+
+    @Override
+    public List<HumanEntity> getViewers() {
+        return this.transaction;
+    }
+
+    @Override
+    public void setMaxStackSize(int size) {
+        this.maxStack = size;
+    }
+
+    @Override
+    public Location getLocation() {
+        if (this.level == null) return null;
+        return new org.bukkit.Location(this.level.getWorld(), this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ());
+    }
+    // CraftBukkit end
 
     public JukeboxBlockEntity(BlockPos pos, BlockState state) {
         super(BlockEntityType.JUKEBOX, pos, state);
+        this.item = ItemStack.EMPTY;
+        this.jukeboxSongPlayer = new JukeboxSongPlayer(this::onSongChanged, this.getBlockPos());
     }
 
     public JukeboxSongPlayer getSongPlayer() {
@@ -41,22 +88,24 @@
 
     private void notifyItemChangedInJukebox(boolean hasRecord) {
         if (this.level != null && this.level.getBlockState(this.getBlockPos()) == this.getBlockState()) {
-            this.level.setBlock(this.getBlockPos(), this.getBlockState().setValue(JukeboxBlock.HAS_RECORD, Boolean.valueOf(hasRecord)), 2);
-            this.level.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(this.getBlockState()));
+            this.level.setBlock(this.getBlockPos(), (BlockState) this.getBlockState().setValue(JukeboxBlock.HAS_RECORD, hasRecord), 2);
+            this.level.gameEvent((Holder) GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(this.getBlockState()));
         }
     }
 
     public void popOutTheItem() {
         if (this.level != null && !this.level.isClientSide) {
-            BlockPos blockPos = this.getBlockPos();
-            ItemStack itemStack = this.getTheItem();
-            if (!itemStack.isEmpty()) {
+            BlockPos blockposition = this.getBlockPos();
+            ItemStack itemstack = this.getTheItem();
+
+            if (!itemstack.isEmpty()) {
                 this.removeTheItem();
-                Vec3 vec3 = Vec3.atLowerCornerWithOffset(blockPos, 0.5, 1.01, 0.5).offsetRandom(this.level.random, 0.7F);
-                ItemStack itemStack2 = itemStack.copy();
-                ItemEntity itemEntity = new ItemEntity(this.level, vec3.x(), vec3.y(), vec3.z(), itemStack2);
-                itemEntity.setDefaultPickUpDelay();
-                this.level.addFreshEntity(itemEntity);
+                Vec3 vec3d = Vec3.atLowerCornerWithOffset(blockposition, 0.5D, 1.01D, 0.5D).offsetRandom(this.level.random, 0.7F);
+                ItemStack itemstack1 = itemstack.copy();
+                ItemEntity entityitem = new ItemEntity(this.level, vec3d.x(), vec3d.y(), vec3d.z(), itemstack1);
+
+                entityitem.setDefaultPickUpDelay();
+                this.level.addFreshEntity(entityitem);
             }
         }
     }
@@ -66,22 +115,24 @@
     }
 
     public int getComparatorOutput() {
-        return JukeboxSong.fromStack(this.level.registryAccess(), this.item).map(Holder::value).map(JukeboxSong::comparatorOutput).orElse(0);
+        return (Integer) JukeboxSong.fromStack(this.level.registryAccess(), this.item).map(Holder::value).map(JukeboxSong::comparatorOutput).orElse(0);
     }
 
     @Override
     protected void loadAdditional(CompoundTag nbt, HolderLookup.Provider registryLookup) {
         super.loadAdditional(nbt, registryLookup);
         if (nbt.contains("RecordItem", 10)) {
-            this.item = ItemStack.parse(registryLookup, nbt.getCompound("RecordItem")).orElse(ItemStack.EMPTY);
+            this.item = (ItemStack) ItemStack.parse(registryLookup, nbt.getCompound("RecordItem")).orElse(ItemStack.EMPTY);
         } else {
             this.item = ItemStack.EMPTY;
         }
 
         if (nbt.contains("ticks_since_song_started", 4)) {
-            JukeboxSong.fromStack(registryLookup, this.item)
-                .ifPresent(song -> this.jukeboxSongPlayer.setSongWithoutPlaying((Holder<JukeboxSong>)song, nbt.getLong("ticks_since_song_started")));
+            JukeboxSong.fromStack(registryLookup, this.item).ifPresent((holder) -> {
+                this.jukeboxSongPlayer.setSongWithoutPlaying(holder, nbt.getLong("ticks_since_song_started"));
+            });
         }
+
     }
 
     @Override
@@ -94,6 +145,7 @@
         if (this.jukeboxSongPlayer.getSong() != null) {
             nbt.putLong("ticks_since_song_started", this.jukeboxSongPlayer.getTicksSinceSongStarted());
         }
+
     }
 
     @Override
@@ -103,27 +155,30 @@
 
     @Override
     public ItemStack splitTheItem(int count) {
-        ItemStack itemStack = this.item;
+        ItemStack itemstack = this.item;
+
         this.setTheItem(ItemStack.EMPTY);
-        return itemStack;
+        return itemstack;
     }
 
     @Override
     public void setTheItem(ItemStack stack) {
         this.item = stack;
-        boolean bl = !this.item.isEmpty();
+        boolean flag = !this.item.isEmpty();
         Optional<Holder<JukeboxSong>> optional = JukeboxSong.fromStack(this.level.registryAccess(), this.item);
-        this.notifyItemChangedInJukebox(bl);
-        if (bl && optional.isPresent()) {
-            this.jukeboxSongPlayer.play(this.level, optional.get());
+
+        this.notifyItemChangedInJukebox(flag);
+        if (flag && optional.isPresent()) {
+            this.jukeboxSongPlayer.play(this.level, (Holder) optional.get());
         } else {
             this.jukeboxSongPlayer.stop(this.level, this.getBlockState());
         }
+
     }
 
     @Override
     public int getMaxStackSize() {
-        return 1;
+        return this.maxStack; // CraftBukkit
     }
 
     @Override
@@ -142,17 +197,24 @@
     }
 
     @VisibleForTesting
-    public void setSongItemWithoutPlaying(ItemStack stack) {
-        this.item = stack;
-        JukeboxSong.fromStack(this.level.registryAccess(), stack)
-            .ifPresent(song -> this.jukeboxSongPlayer.setSongWithoutPlaying((Holder<JukeboxSong>)song, 0L));
-        this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
+    public void setSongItemWithoutPlaying(ItemStack itemstack, long ticksSinceSongStarted) { // CraftBukkit - add argument
+        this.item = itemstack;
+        this.jukeboxSongPlayer.song = null; // CraftBukkit - reset
+        JukeboxSong.fromStack(this.level != null ? this.level.registryAccess() : org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry(), itemstack).ifPresent((holder) -> { // Paper - fallback to other RegistyrAccess if no level
+            this.jukeboxSongPlayer.setSongWithoutPlaying(holder, ticksSinceSongStarted); // CraftBukkit - add argument
+        });
+        // CraftBukkit start - add null check for level
+        if (this.level != null) {
+            this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
+        }
+        // CraftBukkit end
         this.setChanged();
     }
 
     @VisibleForTesting
     public void tryForcePlaySong() {
-        JukeboxSong.fromStack(this.level.registryAccess(), this.getTheItem())
-            .ifPresent(song -> this.jukeboxSongPlayer.play(this.level, (Holder<JukeboxSong>)song));
+        JukeboxSong.fromStack(this.level.registryAccess(), this.getTheItem()).ifPresent((holder) -> {
+            this.jukeboxSongPlayer.play(this.level, holder);
+        });
     }
 }
