fix(music): spotify playlists, auto-leave, stats logging

- Fix Spotify playlist loading (401 error) by enabling preferPartnerApi
  in lavasrc (uses Partner API instead of v1 /items endpoint)
- Remove noisy DEBUG stats logging (change to TRACE)
- Increase auto-leave timer from 2 to 3 minutes
- Add proper voice disconnect on auto-leave via JDA AudioManager
- Increase playlist/album load limit from 6 to 10 (1000 tracks)
This commit is contained in:
loki5512344 2026-07-17 23:23:52 +02:00
parent ff9d3462f6
commit ea9e77de01
Signed by: boba
GPG key ID: 253067914055423B
4 changed files with 36 additions and 5 deletions

View file

@ -22,8 +22,9 @@ plugins:
clientId: "${SPOTIFY_CLIENT_ID}" clientId: "${SPOTIFY_CLIENT_ID}"
clientSecret: "${SPOTIFY_CLIENT_SECRET}" clientSecret: "${SPOTIFY_CLIENT_SECRET}"
countryCode: "RU" countryCode: "RU"
playlistLoadLimit: 6 playlistLoadLimit: 10
albumLoadLimit: 6 albumLoadLimit: 10
preferPartnerApi: true
applemusic: applemusic:
countryCode: "RU" countryCode: "RU"
mediaType: "music" mediaType: "music"

View file

@ -66,6 +66,14 @@ public class BotInitializer {
mainJda.getGuilds().forEach(g -> memberCounterListener.startScheduler(g)); mainJda.getGuilds().forEach(g -> memberCounterListener.startScheduler(g));
healthHttpServer.start(); healthHttpServer.start();
lavalinkManager.setVoiceDisconnectHandler(
guildId -> {
var guild = mainJda.getGuildById(guildId);
if (guild != null) {
guild.getAudioManager().closeAudioConnection();
}
});
JDA musicJda = createMusicJDA(); JDA musicJda = createMusicJDA();
JDA radioJda = null; JDA radioJda = null;
if (config.getRadioToken() != null && !config.getRadioToken().isBlank()) { if (config.getRadioToken() != null && !config.getRadioToken().isBlank()) {

View file

@ -2,6 +2,7 @@ package creator.loki.feature.music.core;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.LongConsumer;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@ -29,6 +30,7 @@ public class LavalinkManager {
private final Map<Long, GuildMusicManager> musicManagers = new ConcurrentHashMap<>(); private final Map<Long, GuildMusicManager> musicManagers = new ConcurrentHashMap<>();
private final BotConfig config; private final BotConfig config;
private final QueueRepository queueRepository; private final QueueRepository queueRepository;
private volatile LongConsumer voiceDisconnectHandler;
@Inject @Inject
public LavalinkManager(BotConfig config, QueueRepository queueRepository) { public LavalinkManager(BotConfig config, QueueRepository queueRepository) {
@ -79,7 +81,7 @@ public class LavalinkManager {
.on(StatsEvent.class) .on(StatsEvent.class)
.subscribe( .subscribe(
event -> { event -> {
logger.debug( logger.trace(
"Нода '{}' статистика: игроков {}/{}", "Нода '{}' статистика: игроков {}/{}",
event.getNode().getName(), event.getNode().getName(),
event.getPlayingPlayers(), event.getPlayingPlayers(),
@ -116,9 +118,17 @@ public class LavalinkManager {
return client.getOrCreateLink(guildId); return client.getOrCreateLink(guildId);
} }
public void setVoiceDisconnectHandler(LongConsumer handler) {
this.voiceDisconnectHandler = handler;
}
public LongConsumer getVoiceDisconnectHandler() {
return voiceDisconnectHandler;
}
public GuildMusicManager getMusicManager(long guildId) { public GuildMusicManager getMusicManager(long guildId) {
return musicManagers.computeIfAbsent( return musicManagers.computeIfAbsent(
guildId, id -> new GuildMusicManager(id, this, queueRepository)); guildId, id -> new GuildMusicManager(id, this, queueRepository, voiceDisconnectHandler));
} }
public GuildMusicManager getMusicManagerIfCached(long guildId) { public GuildMusicManager getMusicManagerIfCached(long guildId) {

View file

@ -7,6 +7,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer;
import creator.loki.feature.music.core.LavalinkManager; import creator.loki.feature.music.core.LavalinkManager;
import creator.loki.feature.music.repository.QueueRepository; import creator.loki.feature.music.repository.QueueRepository;
@ -27,13 +28,21 @@ public class GuildMusicManager {
private final QueueRepository queueRepository; private final QueueRepository queueRepository;
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> autoLeaveFuture; private ScheduledFuture<?> autoLeaveFuture;
private final LongConsumer voiceDisconnectHandler;
@SuppressWarnings("this-escape") @SuppressWarnings("this-escape")
public GuildMusicManager( public GuildMusicManager(
long guildId, LavalinkManager lavalinkManager, QueueRepository queueRepository) { long guildId, LavalinkManager lavalinkManager, QueueRepository queueRepository) {
this(guildId, lavalinkManager, queueRepository, null);
}
@SuppressWarnings("this-escape")
public GuildMusicManager(
long guildId, LavalinkManager lavalinkManager, QueueRepository queueRepository, LongConsumer voiceDisconnectHandler) {
this.guildId = guildId; this.guildId = guildId;
this.lavalinkManager = lavalinkManager; this.lavalinkManager = lavalinkManager;
this.queueRepository = queueRepository; this.queueRepository = queueRepository;
this.voiceDisconnectHandler = voiceDisconnectHandler;
this.scheduler = new TrackScheduler(this); this.scheduler = new TrackScheduler(this);
loadQueueAsync(); loadQueueAsync();
} }
@ -63,7 +72,7 @@ public class GuildMusicManager {
public void startAutoLeaveTimer() { public void startAutoLeaveTimer() {
cancelAutoLeaveTimer(); cancelAutoLeaveTimer();
autoLeaveFuture = executor.schedule(this::autoLeave, 2, TimeUnit.MINUTES); autoLeaveFuture = executor.schedule(this::autoLeave, 3, TimeUnit.MINUTES);
} }
private void autoLeave() { private void autoLeave() {
@ -73,6 +82,9 @@ public class GuildMusicManager {
link -> link ->
link.destroy() link.destroy()
.subscribe(null, err -> logger.warn("Lavalink: {}", err.getMessage()))); .subscribe(null, err -> logger.warn("Lavalink: {}", err.getMessage())));
if (voiceDisconnectHandler != null) {
voiceDisconnectHandler.accept(guildId);
}
lavalinkManager.removeMusicManager(guildId); lavalinkManager.removeMusicManager(guildId);
} }