Compare commits
No commits in common. "b5b6e99946b17cbe35255724b68ce9e288951f2b" and "4cbbb36159d642a6e88d1a79cc045ac44e8a244a" have entirely different histories.
b5b6e99946
...
4cbbb36159
8 changed files with 4 additions and 112 deletions
1
TODO.md
1
TODO.md
|
|
@ -126,7 +126,6 @@
|
||||||
| M12 | 🟢 **LOW** | `m!play` игнорирует неудачу подключения — «Добавлено в очередь», а звука нет | `MusicBotPlayHandler.java:36` |
|
| M12 | 🟢 **LOW** | `m!play` игнорирует неудачу подключения — «Добавлено в очередь», а звука нет | `MusicBotPlayHandler.java:36` |
|
||||||
| M13 | 🟢 **LOW** | `destroy()` глотает `InterruptedException` без восстановления флага | `GuildMusicManager.java:64-66` |
|
| M13 | 🟢 **LOW** | `destroy()` глотает `InterruptedException` без восстановления флага | `GuildMusicManager.java:64-66` |
|
||||||
| M14 | 🟢 **LOW** | `r!volume` рапортует успех при отсутствии радио-сессии | `RadioSubcommands.java:108-113` |
|
| 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
|
#### Core / database
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,8 @@
|
||||||
services:
|
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:
|
lavalink:
|
||||||
image: ghcr.io/lavalink-devs/lavalink:4-alpine
|
image: ghcr.io/lavalink-devs/lavalink:4-alpine
|
||||||
container_name: loki-lavalink
|
container_name: loki-lavalink
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
|
||||||
- spotify-tokener
|
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:2333:2333"
|
- "127.0.0.1:2333:2333"
|
||||||
volumes:
|
volumes:
|
||||||
|
|
@ -24,7 +13,6 @@ services:
|
||||||
JAVA_OPTS: "-Xmx1G -Xms256M"
|
JAVA_OPTS: "-Xmx1G -Xms256M"
|
||||||
SPOTIFY_CLIENT_ID: ${SPOTIFY_CLIENT_ID}
|
SPOTIFY_CLIENT_ID: ${SPOTIFY_CLIENT_ID}
|
||||||
SPOTIFY_CLIENT_SECRET: ${SPOTIFY_CLIENT_SECRET}
|
SPOTIFY_CLIENT_SECRET: ${SPOTIFY_CLIENT_SECRET}
|
||||||
YOUTUBE_OAUTH_REFRESH_TOKEN: ${YOUTUBE_OAUTH_REFRESH_TOKEN}
|
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "wget -q -O /dev/null --header=Authorization:Hch4HiVCxkKs9JPiSnrt4Q http://localhost:2333/v4/info || exit 1"]
|
test: ["CMD-SHELL", "wget -q -O /dev/null --header=Authorization:Hch4HiVCxkKs9JPiSnrt4Q http://localhost:2333/v4/info || exit 1"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
|
|
@ -32,16 +20,6 @@ services:
|
||||||
retries: 3
|
retries: 3
|
||||||
start_period: 20s
|
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:
|
bot:
|
||||||
build: .
|
build: .
|
||||||
container_name: loki-bot
|
container_name: loki-bot
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -6,7 +6,6 @@ plugins:
|
||||||
youtube:
|
youtube:
|
||||||
enabled: true
|
enabled: true
|
||||||
clients:
|
clients:
|
||||||
- TV
|
|
||||||
- MUSIC
|
- MUSIC
|
||||||
- ANDROID_VR
|
- ANDROID_VR
|
||||||
- WEB
|
- WEB
|
||||||
|
|
@ -14,9 +13,6 @@ plugins:
|
||||||
remoteCipher:
|
remoteCipher:
|
||||||
url: "https://cipher.kikkia.dev/"
|
url: "https://cipher.kikkia.dev/"
|
||||||
userAgent: "loki-bot"
|
userAgent: "loki-bot"
|
||||||
oauth:
|
|
||||||
enabled: true
|
|
||||||
refreshToken: "${YOUTUBE_OAUTH_REFRESH_TOKEN}"
|
|
||||||
lavasrc:
|
lavasrc:
|
||||||
providers:
|
providers:
|
||||||
- "scsearch:%ISRC%"
|
- "scsearch:%ISRC%"
|
||||||
|
|
@ -29,12 +25,9 @@ plugins:
|
||||||
spotify:
|
spotify:
|
||||||
clientId: "${SPOTIFY_CLIENT_ID}"
|
clientId: "${SPOTIFY_CLIENT_ID}"
|
||||||
clientSecret: "${SPOTIFY_CLIENT_SECRET}"
|
clientSecret: "${SPOTIFY_CLIENT_SECRET}"
|
||||||
spDc: "AQDVrLVOyLLT3ZuiLoY32uH8HotvY_Lw67ay1lejf55S7O3f_O9IefxZ8XDf3pYklpwl5Klei2NeYUPRZ-cbkpQg5S3RyMjuiTmBxdAuMlugA61_wAy-bXD979aYDK44Bid6UKxLHjh0kFQZ_tX71DAUGezA2XjeLcRi_-TLcKHWKZVKPhGniUOZZEhg2Ij9t6valbqSbDOQ1t0dtQ"
|
|
||||||
countryCode: "RU"
|
countryCode: "RU"
|
||||||
playlistLoadLimit: 10
|
playlistLoadLimit: 10
|
||||||
albumLoadLimit: 10
|
albumLoadLimit: 10
|
||||||
preferPartnerApi: true
|
|
||||||
customTokenEndpoint: "http://spotify-tokener:8080/api/token"
|
|
||||||
applemusic:
|
applemusic:
|
||||||
countryCode: "RU"
|
countryCode: "RU"
|
||||||
mediaType: "music"
|
mediaType: "music"
|
||||||
|
|
@ -61,4 +54,3 @@ logging:
|
||||||
level:
|
level:
|
||||||
root: INFO
|
root: INFO
|
||||||
lavalink: INFO
|
lavalink: INFO
|
||||||
com.github.topi314.lavasrc: INFO
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -9,7 +9,6 @@ import dev.arbjerg.lavalink.client.event.ReadyEvent;
|
||||||
import dev.arbjerg.lavalink.client.event.StatsEvent;
|
import dev.arbjerg.lavalink.client.event.StatsEvent;
|
||||||
import dev.arbjerg.lavalink.client.event.TrackEndEvent;
|
import dev.arbjerg.lavalink.client.event.TrackEndEvent;
|
||||||
import dev.arbjerg.lavalink.client.event.TrackStartEvent;
|
import dev.arbjerg.lavalink.client.event.TrackStartEvent;
|
||||||
import dev.arbjerg.lavalink.client.event.WebSocketClosedEvent;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
@ -66,18 +65,5 @@ class LavalinkEventRouter {
|
||||||
manager.getScheduler().onTrackEnd(event.getTrack(), event.getEndReason());
|
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()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,6 @@ 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.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.LongConsumer;
|
import java.util.function.LongConsumer;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
@ -30,13 +27,6 @@ public class LavalinkManager {
|
||||||
private final BotConfig config;
|
private final BotConfig config;
|
||||||
private final QueueRepository queueRepository;
|
private final QueueRepository queueRepository;
|
||||||
private volatile LongConsumer voiceDisconnectHandler;
|
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
|
@Inject
|
||||||
public LavalinkManager(BotConfig config, QueueRepository queueRepository) {
|
public LavalinkManager(BotConfig config, QueueRepository queueRepository) {
|
||||||
|
|
@ -56,17 +46,6 @@ public class LavalinkManager {
|
||||||
|
|
||||||
this.client = new LavalinkClient(Helpers.getUserIdFromToken(botToken));
|
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(
|
client.addNode(
|
||||||
new NodeOptions.Builder()
|
new NodeOptions.Builder()
|
||||||
.setName("main")
|
.setName("main")
|
||||||
|
|
@ -74,26 +53,11 @@ public class LavalinkManager {
|
||||||
.setPassword(lavalinkConfig.getPassword())
|
.setPassword(lavalinkConfig.getPassword())
|
||||||
.setHttpTimeout(30000L)
|
.setHttpTimeout(30000L)
|
||||||
.build());
|
.build());
|
||||||
}
|
|
||||||
|
|
||||||
private void checkNodeHealth() {
|
new LavalinkEventRouter(client, musicManagers).register();
|
||||||
try {
|
|
||||||
var nodes = client.getNodes();
|
LoggerDecorator.music(
|
||||||
if (nodes.isEmpty()) {
|
logger, "LavalinkManager инициализирован, нода: " + lavalinkConfig.getWsUri());
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LavalinkClient getClient() {
|
public LavalinkClient getClient() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue