Compare commits

..

No commits in common. "b5b6e99946b17cbe35255724b68ce9e288951f2b" and "4cbbb36159d642a6e88d1a79cc045ac44e8a244a" have entirely different histories.

8 changed files with 4 additions and 112 deletions

View file

@ -126,7 +126,6 @@
| M12 | 🟢 **LOW** | `m!play` игнорирует неудачу подключения — «Добавлено в очередь», а звука нет | `MusicBotPlayHandler.java:36` |
| M13 | 🟢 **LOW** | `destroy()` глотает `InterruptedException` без восстановления флага | `GuildMusicManager.java:64-66` |
| M14 | 🟢 **LOW** | `r!volume` рапортует успех при отсутствии радио-сессии | `RadioSubcommands.java:108-113` |
| M15 | 🟡 **MEDIUM** | Spotify-плейлисты не грузятся: Spotify (политика 27.11.2024) требует user-auth для `/v1/playlists/*` — client credentials получают `401 Valid user authentication required`. Одиночные треки работают (метаданные по client credentials + плейбек через scsearch-мирроринг). Варианты: OAuth refresh token юзера с scope `playlist-read-private`, либо revival browser-tokener | `lavalink/application.yml` (lavasrc) |
#### Core / database

View file

@ -1,19 +1,8 @@
services:
spotify-tokener:
build:
context: ./lavalink
dockerfile: Dockerfile.tokener
container_name: spotify-tokener
restart: unless-stopped
environment:
- SPOTIFY_TOKENER_CHROME_PATH=/usr/bin/chromium
lavalink:
image: ghcr.io/lavalink-devs/lavalink:4-alpine
container_name: loki-lavalink
restart: unless-stopped
depends_on:
- spotify-tokener
ports:
- "127.0.0.1:2333:2333"
volumes:
@ -24,7 +13,6 @@ services:
JAVA_OPTS: "-Xmx1G -Xms256M"
SPOTIFY_CLIENT_ID: ${SPOTIFY_CLIENT_ID}
SPOTIFY_CLIENT_SECRET: ${SPOTIFY_CLIENT_SECRET}
YOUTUBE_OAUTH_REFRESH_TOKEN: ${YOUTUBE_OAUTH_REFRESH_TOKEN}
healthcheck:
test: ["CMD-SHELL", "wget -q -O /dev/null --header=Authorization:Hch4HiVCxkKs9JPiSnrt4Q http://localhost:2333/v4/info || exit 1"]
interval: 30s
@ -32,16 +20,6 @@ services:
retries: 3
start_period: 20s
lavasrc-patcher:
build:
context: ./lavalink
dockerfile: Dockerfile.config-patcher
container_name: lavasrc-patcher
depends_on:
lavalink:
condition: service_healthy
restart: "no"
bot:
build: .
container_name: loki-bot

View file

@ -1,5 +0,0 @@
FROM alpine:3.20
RUN apk add --no-cache curl
COPY apply-config.sh /apply-config.sh
RUN chmod +x /apply-config.sh
CMD ["/apply-config.sh"]

View file

@ -1,6 +0,0 @@
FROM ghcr.io/topi314/spotify-tokener:master
RUN apt-get update && \
apt-get install -y --no-install-recommends chromium && \
rm -rf /var/lib/apt/lists/*
ENV SPOTIFY_TOKENER_CHROME_PATH=/usr/bin/chromium
ENV CHROMIUM_FLAGS="--no-sandbox --disable-gpu --headless"

View file

@ -6,7 +6,6 @@ plugins:
youtube:
enabled: true
clients:
- TV
- MUSIC
- ANDROID_VR
- WEB
@ -14,9 +13,6 @@ plugins:
remoteCipher:
url: "https://cipher.kikkia.dev/"
userAgent: "loki-bot"
oauth:
enabled: true
refreshToken: "${YOUTUBE_OAUTH_REFRESH_TOKEN}"
lavasrc:
providers:
- "scsearch:%ISRC%"
@ -29,12 +25,9 @@ plugins:
spotify:
clientId: "${SPOTIFY_CLIENT_ID}"
clientSecret: "${SPOTIFY_CLIENT_SECRET}"
spDc: "AQDVrLVOyLLT3ZuiLoY32uH8HotvY_Lw67ay1lejf55S7O3f_O9IefxZ8XDf3pYklpwl5Klei2NeYUPRZ-cbkpQg5S3RyMjuiTmBxdAuMlugA61_wAy-bXD979aYDK44Bid6UKxLHjh0kFQZ_tX71DAUGezA2XjeLcRi_-TLcKHWKZVKPhGniUOZZEhg2Ij9t6valbqSbDOQ1t0dtQ"
countryCode: "RU"
playlistLoadLimit: 10
albumLoadLimit: 10
preferPartnerApi: true
customTokenEndpoint: "http://spotify-tokener:8080/api/token"
applemusic:
countryCode: "RU"
mediaType: "music"
@ -61,4 +54,3 @@ logging:
level:
root: INFO
lavalink: INFO
com.github.topi314.lavasrc: INFO

View file

@ -1,16 +0,0 @@
#!/bin/sh
# Apply LavaSrc runtime config after Lavalink starts
until curl -sf -H "Authorization: Hch4HiVCxkKs9JPiSnrt4Q" http://lavalink:2333/v4/info >/dev/null 2>&1; do
sleep 2
done
sleep 5
curl -sf -X PATCH \
-H "Authorization: Hch4HiVCxkKs9JPiSnrt4Q" \
-H "Content-Type: application/json" \
-d '{"spotify":{"preferPartnerApi":true,"customTokenEndpoint":"http://spotify-tokener:8080/api/token","spDc":"AQDVrLVOyLLT3ZuiLoY32uH8HotvY_Lw67ay1lejf55S7O3f_O9IefxZ8XDf3pYklpwl5Klei2NeYUPRZ-cbkpQg5S3RyMjuiTmBxdAuMlugA61_wAy-bXD979aYDK44Bid6UKxLHjh0kFQZ_tX71DAUGezA2XjeLcRi_-TLcKHWKZVKPhGniUOZZEhg2Ij9t6valbqSbDOQ1t0dtQ"}}' \
http://lavalink:2333/v4/lavasrc/config
echo "LavaSrc config applied"

View file

@ -9,7 +9,6 @@ import dev.arbjerg.lavalink.client.event.ReadyEvent;
import dev.arbjerg.lavalink.client.event.StatsEvent;
import dev.arbjerg.lavalink.client.event.TrackEndEvent;
import dev.arbjerg.lavalink.client.event.TrackStartEvent;
import dev.arbjerg.lavalink.client.event.WebSocketClosedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -66,18 +65,5 @@ class LavalinkEventRouter {
manager.getScheduler().onTrackEnd(event.getTrack(), event.getEndReason());
}
});
client
.on(WebSocketClosedEvent.class)
.subscribe(
event ->
LoggerDecorator.music(
logger,
"WebSocket закрыт: code="
+ event.getCode()
+ ", reason="
+ event.getReason()
+ ", byRemote="
+ event.getByRemote()));
}
}

View file

@ -2,9 +2,6 @@ package creator.loki.feature.music.core;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer;
import com.google.inject.Inject;
@ -30,13 +27,6 @@ public class LavalinkManager {
private final BotConfig config;
private final QueueRepository queueRepository;
private volatile LongConsumer voiceDisconnectHandler;
private final ScheduledExecutorService healthCheck =
Executors.newSingleThreadScheduledExecutor(
r -> {
Thread t = new Thread(r, "lavalink-health-check");
t.setDaemon(true);
return t;
});
@Inject
public LavalinkManager(BotConfig config, QueueRepository queueRepository) {
@ -56,17 +46,6 @@ public class LavalinkManager {
this.client = new LavalinkClient(Helpers.getUserIdFromToken(botToken));
addNode(lavalinkConfig);
new LavalinkEventRouter(client, musicManagers).register();
healthCheck.scheduleWithFixedDelay(this::checkNodeHealth, 30, 30, TimeUnit.SECONDS);
LoggerDecorator.music(
logger, "LavalinkManager инициализирован, нода: " + lavalinkConfig.getWsUri());
}
private void addNode(LavalinkConfig lavalinkConfig) {
client.addNode(
new NodeOptions.Builder()
.setName("main")
@ -74,26 +53,11 @@ public class LavalinkManager {
.setPassword(lavalinkConfig.getPassword())
.setHttpTimeout(30000L)
.build());
}
private void checkNodeHealth() {
try {
var nodes = client.getNodes();
if (nodes.isEmpty()) {
logger.warn("Lavalink: нет нод, пересоздаю");
addNode(config.getLavalinkConfig());
return;
}
var node = nodes.get(0);
if (!node.getAvailable()) {
logger.warn("Lavalink нода '{}' недоступна, переподключаю", node.getName());
client.removeNode(node);
addNode(config.getLavalinkConfig());
LoggerDecorator.music(logger, "Lavalink нода переподключена");
}
} catch (Exception e) {
logger.error("Ошибка health-check Lavalink", e);
}
new LavalinkEventRouter(client, musicManagers).register();
LoggerDecorator.music(
logger, "LavalinkManager инициализирован, нода: " + lavalinkConfig.getWsUri());
}
public LavalinkClient getClient() {