switch to rabbitpipe
Marco Andronaco andronacomarco@gmail.com
Sat, 23 Nov 2024 01:43:05 +0100
6 files changed,
56 insertions(+),
139 deletions(-)
M
go.mod
→
go.mod
@@ -5,20 +5,14 @@
require ( github.com/FoxeiZ/dca v0.0.0-20240420115706-e4859a963796 github.com/birabittoh/myks v0.0.2 + github.com/birabittoh/rabbitpipe v0.0.2 github.com/bwmarrin/discordgo v0.28.1 - github.com/kkdai/youtube/v2 v2.10.1 golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f ) require ( - github.com/bitly/go-simplejson v0.5.1 // indirect - github.com/dlclark/regexp2 v1.11.4 // indirect - github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd // indirect - github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect - github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/jonas747/ogg v0.0.0-20161220051205-b4f6f4cf3757 // indirect golang.org/x/crypto v0.29.0 // indirect golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.20.0 // indirect )
M
go.sum
→
go.sum
@@ -1,51 +1,25 @@
github.com/FoxeiZ/dca v0.0.0-20240420115706-e4859a963796 h1:H8+3uNS2PWDRABx7QixGgJY+sa+GeUpwSN9QNFKiK/Y= github.com/FoxeiZ/dca v0.0.0-20240420115706-e4859a963796/go.mod h1:+hXF1jadw9rDuEoyhAIMWH3acfzvPcqVQG6kKGqNrFY= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/birabittoh/myks v0.0.2 h1:EBukMUsAflwiqdNo4LE7o2WQdEvawty5ewCZWY+IXSU= github.com/birabittoh/myks v0.0.2/go.mod h1:klNWaeUWm7TmhnBHBMt9vALwCHW11/Xw1BpCNkCx7hs= -github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow= -github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q= +github.com/birabittoh/rabbitpipe v0.0.2 h1:4ptBS4Ai9NJH9gv3uG5TZBp1H5gfgEabEw6XldSjUx0= +github.com/birabittoh/rabbitpipe v0.0.2/go.mod h1:6cEDb0WpwrRm2vt5IO3s2gPjzhZZLP7gYx+l9e3gx1k= github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4= github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= -github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd h1:QMSNEh9uQkDjyPwu/J541GgSH+4hw+0skJDIj9HJ3mE= -github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= -github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= -github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs= -github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jonas747/ogg v0.0.0-20161220051205-b4f6f4cf3757 h1:Kyv+zTfWIGRNaz/4+lS+CxvuKVZSKFz/6G8E3BKKBRs= github.com/jonas747/ogg v0.0.0-20161220051205-b4f6f4cf3757/go.mod h1:cZnNmdLiLpihzgIVqiaQppi9Ts3D4qF/M45//yW35nI= -github.com/kkdai/youtube/v2 v2.10.1 h1:jdPho4R7VxWoRi9Wx4ULMq4+hlzSVOXxh4Zh83f2F9M= -github.com/kkdai/youtube/v2 v2.10.1/go.mod h1:qL8JZv7Q1IoDs4nnaL51o/hmITXEIvyCIXopB0oqgVM= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
M
src/globals/utils.go
→
src/globals/utils.go
@@ -11,8 +11,8 @@ "strings"
"time" "github.com/birabittoh/myks" + "github.com/birabittoh/rabbitpipe" "github.com/bwmarrin/discordgo" - "github.com/kkdai/youtube/v2" ) var (@@ -58,8 +58,9 @@ func FormatCommand(command, guildID string) string {
return fmt.Sprintf("`%s%s`", GetPrefix(guildID), command) } -func FormatVideo(v *youtube.Video) string { - return fmt.Sprintf("**%s** (`%s`)", v.Title, v.Duration.String()) +func FormatVideo(v *rabbitpipe.Video) string { + duration := time.Duration(v.LengthSeconds) * time.Second + return fmt.Sprintf("**%s** (`%s`)", v.Title, duration.String()) } func ParseUserMessage(messageContent, guildID string) (command string, args []string, ok bool) {
M
src/music/commands.go
→
src/music/commands.go
@@ -4,8 +4,8 @@ import (
"fmt" gl "github.com/birabittoh/disgord/src/globals" + "github.com/birabittoh/rabbitpipe" "github.com/bwmarrin/discordgo" - "github.com/kkdai/youtube/v2" ) const (@@ -21,7 +21,7 @@ MsgLeft = "Left."
MsgQueueLine = "%d. %s\n" ) -var yt = youtube.Client{} +var yt = rabbitpipe.New() func HandlePlay(args []string, s *discordgo.Session, m *discordgo.MessageCreate) string { r, _, vc := gl.GetVoiceChannelID(s, m)
M
src/music/queue.go
→
src/music/queue.go
@@ -4,15 +4,15 @@ import (
"os" "github.com/birabittoh/disgord/src/mylog" + "github.com/birabittoh/rabbitpipe" "github.com/bwmarrin/discordgo" - "github.com/kkdai/youtube/v2" ) var logger = mylog.NewLogger(os.Stdin, "music", mylog.DEBUG) type Queue struct { - nowPlaying *youtube.Video - items []*youtube.Video + nowPlaying *rabbitpipe.Video + items []*rabbitpipe.Video audioStream *Audio vc *discordgo.VoiceConnection }@@ -45,7 +45,7 @@ return nil
} // AddVideo adds a new video to the queue -func (q *Queue) AddVideo(video *youtube.Video) { +func (q *Queue) AddVideo(video *rabbitpipe.Video) { q.items = append(q.items, video) if q.nowPlaying == nil { q.PlayNext()@@ -53,7 +53,7 @@ }
} // AddVideos adds a list of videos to the queue -func (q *Queue) AddVideos(videos []*youtube.Video) { +func (q *Queue) AddVideos(videos []*rabbitpipe.Video) { q.items = append(q.items, videos...) if q.nowPlaying == nil { err := q.PlayNext()@@ -79,7 +79,7 @@ q.items = q.items[1:]
format := getFormat(*q.nowPlaying) if format == nil { - logger.Debug("no formats with audio channels available for video " + q.nowPlaying.ID) + logger.Debug("no formats with audio channels available for video " + q.nowPlaying.VideoID) return q.PlayNext() }@@ -120,13 +120,13 @@ }
// Clear clears the video queue func (q *Queue) Clear() { - q.items = []*youtube.Video{} + q.items = []*rabbitpipe.Video{} } // Videos returns all videos in the queue including the now playing one -func (q *Queue) Videos() []*youtube.Video { +func (q *Queue) Videos() []*rabbitpipe.Video { if q.nowPlaying != nil { - return append([]*youtube.Video{q.nowPlaying}, q.items...) + return append([]*rabbitpipe.Video{q.nowPlaying}, q.items...) } return q.items }@@ -143,6 +143,6 @@ func (q *Queue) VoiceConnection() *discordgo.VoiceConnection {
return q.vc } -func (q *Queue) NowPlaying() *youtube.Video { +func (q *Queue) NowPlaying() *rabbitpipe.Video { return q.nowPlaying }
M
src/music/video.go
→
src/music/video.go
@@ -2,16 +2,12 @@ package music
import ( "errors" - "fmt" - "net/http" "regexp" - "strconv" "strings" "time" gl "github.com/birabittoh/disgord/src/globals" - "github.com/birabittoh/myks" - "github.com/kkdai/youtube/v2" + "github.com/birabittoh/rabbitpipe" ) const (@@ -19,107 +15,64 @@ defaultCacheDuration = 6 * time.Hour
) var ( - expireRegex = regexp.MustCompile(`(?i)expire=(\d+)`) - ks = myks.New[youtube.Video](time.Hour) + expireRegex = regexp.MustCompile(`(?i)expire=(\d+)`) + videoRegexpList = []*regexp.Regexp{ // from github.com/kkdai/youtube + regexp.MustCompile(`(?:v|embed|shorts|watch\?v)(?:=|/)([^"&?/=%]{11})`), + regexp.MustCompile(`(?:=|/)([^"&?/=%]{11})`), + regexp.MustCompile(`([^"&?/=%]{11})`), + } ) -func getFormat(video youtube.Video) *youtube.Format { - formats := video.Formats.Type("audio") - for i, format := range formats { - if format.URL != "" { - return &formats[i] +func extractVideoID(videoID string) (string, error) { + if strings.Contains(videoID, "youtu") || strings.ContainsAny(videoID, "\"?&/<%=") { + for _, re := range videoRegexpList { + if isMatch := re.MatchString(videoID); isMatch { + subs := re.FindStringSubmatch(videoID) + videoID = subs[1] + } } } - return nil -} - -func parseExpiration(url string) time.Duration { - expireString := expireRegex.FindStringSubmatch(url) - if len(expireString) < 2 { - return defaultCacheDuration + if strings.ContainsAny(videoID, "?&/<%=") { + return "", errors.New("invalid characters in videoID") } - expireTimestamp, err := strconv.ParseInt(expireString[1], 10, 64) - if err != nil { - return defaultCacheDuration + if len(videoID) < 10 { + return "", errors.New("videoID is too short") } - return time.Until(time.Unix(expireTimestamp, 0)) + return videoID, nil } -func getFromYT(videoID string) (video *youtube.Video, err error) { - url := "https://youtu.be/" + videoID - - const maxRetries = 5 - const maxBytesToCheck = 1024 - var duration time.Duration - - for i := 0; i < maxRetries; i++ { - logger.Info("Requesting video", url, "attempt", i+1) - video, err = yt.GetVideo(url) - if err != nil || video == nil { - logger.Error("Error fetching video info:", err) - continue - } - - format := getFormat(*video) - if format == nil { - logger.Errorf("no audio formats available for video %s", videoID) - continue - } - - duration = parseExpiration(format.URL) - - resp, err := http.Get(format.URL) - if err != nil { - logger.Error("Error fetching video URL:", err) - continue - } - defer resp.Body.Close() - - if resp.ContentLength <= 0 { - logger.Error("Invalid video link, no content length...") - continue - } - - buffer := make([]byte, maxBytesToCheck) - n, err := resp.Body.Read(buffer) - if err != nil { - logger.Error("Error reading video content:", err) - continue - } - - if n > 0 { - logger.Info("Valid video link found.") - ks.Set(videoID, *video, duration) - return video, nil +func getFormat(video rabbitpipe.Video) *rabbitpipe.AdaptiveFormat { + formats := video.AdaptiveFormats + for i, format := range formats { + if format.URL != "" && format.AudioChannels > 0 { + return &formats[i] } - - logger.Error("Invalid video link, content is empty...") - time.Sleep(1 * time.Second) } - err = fmt.Errorf("failed to fetch valid video after %d attempts", maxRetries) - return nil, err + return nil } -func getFromCache(videoID string) (video *youtube.Video, err error) { - video, err = ks.Get(videoID) - if err != nil { - return +func getFromYT(videoID string) (video *rabbitpipe.Video, err error) { + video, err = yt.GetVideo(videoID) + if err != nil || video == nil { + logger.Error("Error fetching video info:", err) + return nil, errors.New("error fetching video info") } - if video == nil { - err = errors.New("video should not be nil") - return + format := getFormat(*video) + if format == nil { + logger.Errorf("no audio formats available for video %s", videoID) + return nil, errors.New("no audio formats available") } return } -func getVideo(args []string) (*youtube.Video, error) { - videoID, err := youtube.ExtractVideoID(args[0]) +func getVideo(args []string) (*rabbitpipe.Video, error) { + videoID, err := extractVideoID(args[0]) if err != nil { searchQuery := strings.Join(args, " ") videoID, err = gl.Search(searchQuery)@@ -128,10 +81,5 @@ return nil, err
} } - video, err := getFromCache(videoID) - if err != nil { - return getFromYT(videoID) - } - - return video, nil + return getFromYT(videoID) }