feat(music): let the dashboard add songs, starting a session if needed
New AddToQueue appends to an already-active session without touching voice (the dashboard can't join a channel on its own), and add_song's dispatch case now branches: a voice_channel_id in the payload means "start fresh", so it goes through EnqueueAndPlay (join + play) instead. Query normalization (plain text -> YouTube search, stripping autoplay/radio params off pasted YouTube URLs) moves out of /play's handler into a shared NormalizeQuery, both entry points need it, not just one. Starting playback from the dashboard also posts the now-playing embed into the voice channel's own built-in text chat, matching what /play already does, instead of leaving it with nowhere to show up.
This commit is contained in:
@@ -2,8 +2,6 @@ package public
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"velox-bot/internal/commands/music/shared"
|
||||
"velox-bot/internal/music"
|
||||
|
||||
@@ -49,13 +47,6 @@ func PlayHandler(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
return
|
||||
}
|
||||
|
||||
// If user provided plain text, turn it into a YouTube search
|
||||
if !strings.HasPrefix(query, "http://") && !strings.HasPrefix(query, "https://") && !strings.HasPrefix(query, "ytsearch:") {
|
||||
query = "ytsearch:" + query
|
||||
} else {
|
||||
query = normalizeYouTubeRadioURL(query)
|
||||
}
|
||||
|
||||
vs, err := findUserVoiceState(s, i.GuildID, i.Member.User.ID)
|
||||
if err != nil {
|
||||
_, _ = s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
|
||||
@@ -90,37 +81,6 @@ func PlayHandler(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
|
||||
func ptr[T any](v T) *T { return new(v) }
|
||||
|
||||
// normalizeYouTubeRadioURL strips auto-generated "radio/mix" params like:
|
||||
// https://www.youtube.com/watch?v=ID&list=RDID&start_radio=1 -> https://www.youtube.com/watch?v=ID
|
||||
// It intentionally does NOT strip normal playlist URLs.
|
||||
func normalizeYouTubeRadioURL(raw string) string {
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil || u == nil {
|
||||
return raw
|
||||
}
|
||||
|
||||
host := strings.ToLower(u.Host)
|
||||
if !strings.Contains(host, "youtube.com") || u.Path != "/watch" {
|
||||
return raw
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
v := q.Get("v")
|
||||
if v == "" {
|
||||
return raw
|
||||
}
|
||||
|
||||
list := q.Get("list")
|
||||
_, hasStartRadio := q["start_radio"]
|
||||
if !hasStartRadio && !strings.HasPrefix(list, "RD") {
|
||||
return raw
|
||||
}
|
||||
|
||||
u.RawQuery = url.Values{"v": []string{v}}.Encode()
|
||||
u.Fragment = ""
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func findUserVoiceState(s *discordgo.Session, guildID, userID string) (*discordgo.VoiceState, error) {
|
||||
g, err := s.State.Guild(guildID)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user