https://github.com/matthieugrieger/mumbledj/issues/65: Add support for YouTube offsets
This commit is contained in:
parent
35f673447f
commit
34431c9fa5
25
commands.go
25
commands.go
|
@ -164,27 +164,32 @@ func add(user *gumble.User, username, url string) {
|
|||
dj.SendPrivateMessage(user, NO_ARGUMENT_MSG)
|
||||
} else {
|
||||
youtubePatterns := []string{
|
||||
`https?:\/\/www\.youtube\.com\/watch\?v=([\w-]+)`,
|
||||
`https?:\/\/youtube\.com\/watch\?v=([\w-]+)`,
|
||||
`https?:\/\/youtu.be\/([\w-]+)`,
|
||||
`https?:\/\/youtube.com\/v\/([\w-]+)`,
|
||||
`https?:\/\/www.youtube.com\/v\/([\w-]+)`,
|
||||
`https?:\/\/www\.youtube\.com\/watch\?v=([\w-]+)(\&t=\d*m?\d*s?)?`,
|
||||
`https?:\/\/youtube\.com\/watch\?v=([\w-]+)(\&t=\d*m?\d*s?)?`,
|
||||
`https?:\/\/youtu.be\/([\w-]+)(\?t=\d*m?\d*s?)?`,
|
||||
`https?:\/\/youtube.com\/v\/([\w-]+)(\?t=\d*m?\d*s?)?`,
|
||||
`https?:\/\/www.youtube.com\/v\/([\w-]+)(\?t=\d*m?\d*s?)?`,
|
||||
}
|
||||
matchFound := false
|
||||
shortUrl := ""
|
||||
shortURL := ""
|
||||
startOffset := ""
|
||||
|
||||
for _, pattern := range youtubePatterns {
|
||||
if re, err := regexp.Compile(pattern); err == nil {
|
||||
if re.MatchString(url) {
|
||||
matchFound = true
|
||||
shortUrl = re.FindStringSubmatch(url)[1]
|
||||
matches := re.FindAllStringSubmatch(url, -1)
|
||||
shortURL = matches[0][1]
|
||||
if len(matches[0]) == 3 {
|
||||
startOffset = matches[0][2]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if matchFound {
|
||||
if newSong, err := NewYouTubeSong(username, shortUrl, nil); err == nil {
|
||||
if newSong, err := NewYouTubeSong(username, shortURL, startOffset, nil); err == nil {
|
||||
dj.client.Self.Channel.Send(fmt.Sprintf(SONG_ADDED_HTML, username, newSong.title), false)
|
||||
if dj.queue.Len() == 1 && !dj.audioStream.IsPlaying() {
|
||||
if err := dj.queue.CurrentSong().Download(); err == nil {
|
||||
|
@ -206,9 +211,9 @@ func add(user *gumble.User, username, url string) {
|
|||
if re, err := regexp.Compile(youtubePlaylistPattern); err == nil {
|
||||
if re.MatchString(url) {
|
||||
if dj.HasPermission(username, dj.conf.Permissions.AdminAddPlaylists) {
|
||||
shortUrl = re.FindStringSubmatch(url)[1]
|
||||
shortURL = re.FindStringSubmatch(url)[1]
|
||||
oldLength := dj.queue.Len()
|
||||
if newPlaylist, err := NewYouTubePlaylist(username, shortUrl); err == nil {
|
||||
if newPlaylist, err := NewYouTubePlaylist(username, shortURL); err == nil {
|
||||
dj.client.Self.Channel.Send(fmt.Sprintf(PLAYLIST_ADDED_HTML, username, newPlaylist.title), false)
|
||||
if oldLength == 0 && dj.queue.Len() != 0 && !dj.audioStream.IsPlaying() {
|
||||
if err := dj.queue.CurrentSong().Download(); err == nil {
|
||||
|
|
8
main.go
8
main.go
|
@ -45,12 +45,8 @@ func (dj *mumbledj) OnConnect(e *gumble.ConnectEvent) {
|
|||
fmt.Println("Channel doesn't exist or one was not provided, staying in root channel...")
|
||||
}
|
||||
|
||||
if audioStream, err := gumble_ffmpeg.New(dj.client); err == nil {
|
||||
dj.audioStream = audioStream
|
||||
dj.audioStream.Volume = dj.conf.Volume.DefaultVolume
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
dj.audioStream = gumble_ffmpeg.New(dj.client)
|
||||
dj.audioStream.Volume = dj.conf.Volume.DefaultVolume
|
||||
|
||||
dj.client.AudioEncoder.SetApplication(gopus.Audio)
|
||||
|
||||
|
|
|
@ -17,8 +17,10 @@ import (
|
|||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/jsonq"
|
||||
"github.com/layeh/gumble/gumble_ffmpeg"
|
||||
)
|
||||
|
||||
// ------------
|
||||
|
@ -30,6 +32,7 @@ type YouTubeSong struct {
|
|||
submitter string
|
||||
title string
|
||||
id string
|
||||
offset int
|
||||
filename string
|
||||
duration string
|
||||
thumbnail string
|
||||
|
@ -40,7 +43,7 @@ type YouTubeSong struct {
|
|||
|
||||
// NewYouTubeSong gathers the metadata for a song extracted from a YouTube video, and returns
|
||||
// the song.
|
||||
func NewYouTubeSong(user, id string, playlist *YouTubePlaylist) (*YouTubeSong, error) {
|
||||
func NewYouTubeSong(user, id, offset string, playlist *YouTubePlaylist) (*YouTubeSong, error) {
|
||||
var apiResponse *jsonq.JsonQuery
|
||||
var err error
|
||||
url := fmt.Sprintf("https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=%s&key=%s",
|
||||
|
@ -49,6 +52,24 @@ func NewYouTubeSong(user, id string, playlist *YouTubePlaylist) (*YouTubeSong, e
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var offsetMinutes, offsetSeconds int64
|
||||
if offset != "" {
|
||||
if strings.Contains(offset, "m") {
|
||||
offsetMinutes, _ = strconv.ParseInt(offset[3:strings.Index(offset, "m")], 10, 32)
|
||||
if strings.Contains(offset, "s") {
|
||||
offsetSeconds, _ = strconv.ParseInt(offset[strings.Index(offset, "m")+1:strings.Index(offset, "s")], 10, 32)
|
||||
} else {
|
||||
offsetSeconds = 0
|
||||
}
|
||||
} else if strings.Contains(offset, "s") {
|
||||
offsetMinutes = 0
|
||||
offsetSeconds, _ = strconv.ParseInt(offset[3:strings.Index(offset, "s")], 10, 32)
|
||||
}
|
||||
} else {
|
||||
offsetMinutes = 0
|
||||
offsetSeconds = 0
|
||||
}
|
||||
|
||||
title, _ := apiResponse.String("items", "0", "snippet", "title")
|
||||
thumbnail, _ := apiResponse.String("items", "0", "snippet", "thumbnails", "high", "url")
|
||||
duration, _ := apiResponse.String("items", "0", "contentDetails", "duration")
|
||||
|
@ -64,14 +85,18 @@ func NewYouTubeSong(user, id string, playlist *YouTubePlaylist) (*YouTubeSong, e
|
|||
} else {
|
||||
seconds = 0
|
||||
}
|
||||
|
||||
combinedMinutes := minutes - offsetMinutes
|
||||
combinedSeconds := seconds - offsetSeconds
|
||||
totalSeconds := int((minutes * 60) + seconds)
|
||||
durationString := fmt.Sprintf("%d:%02d", minutes, seconds)
|
||||
durationString := fmt.Sprintf("%d:%02d", combinedMinutes, combinedSeconds)
|
||||
|
||||
if dj.conf.General.MaxSongDuration == 0 || totalSeconds <= dj.conf.General.MaxSongDuration {
|
||||
song := &YouTubeSong{
|
||||
submitter: user,
|
||||
title: title,
|
||||
id: id,
|
||||
offset: int((offsetMinutes * 60) + offsetSeconds),
|
||||
filename: id + ".m4a",
|
||||
duration: durationString,
|
||||
thumbnail: thumbnail,
|
||||
|
@ -104,7 +129,12 @@ func (s *YouTubeSong) Download() error {
|
|||
// Play plays the song. Once the song is playing, a notification is displayed in a text message that features the video
|
||||
// thumbnail, URL, title, duration, and submitter.
|
||||
func (s *YouTubeSong) Play() {
|
||||
if err := dj.audioStream.Play(fmt.Sprintf("%s/.mumbledj/songs/%s.m4a", dj.homeDir, s.ID()), dj.queue.OnSongFinished); err != nil {
|
||||
if s.offset != 0 {
|
||||
offsetDuration, _ := time.ParseDuration(fmt.Sprintf("%ds", s.offset))
|
||||
dj.audioStream.Offset = offsetDuration
|
||||
}
|
||||
dj.audioStream.Source = gumble_ffmpeg.SourceFile(fmt.Sprintf("%s/.mumbledj/songs/%s", dj.homeDir, s.Filename()))
|
||||
if err := dj.audioStream.Play(); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
if s.Playlist() == nil {
|
||||
|
@ -143,6 +173,10 @@ func (s *YouTubeSong) Play() {
|
|||
dj.client.Self.Channel.Send(fmt.Sprintf(message, s.Thumbnail(), s.ID(),
|
||||
s.Title(), s.Duration(), s.Submitter(), s.Playlist().Title()), false)
|
||||
}
|
||||
go func() {
|
||||
dj.audioStream.Wait()
|
||||
dj.queue.OnSongFinished()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
|
||||
package main
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SongQueue type declaration.
|
||||
type SongQueue struct {
|
||||
|
@ -75,6 +79,8 @@ func (q *SongQueue) Traverse(visit func(i int, s Song)) {
|
|||
|
||||
// OnSongFinished event. Deletes Song that just finished playing, then queues the next Song (if exists).
|
||||
func (q *SongQueue) OnSongFinished() {
|
||||
resetOffset, _ := time.ParseDuration(fmt.Sprintf("%ds", 0))
|
||||
dj.audioStream.Offset = resetOffset
|
||||
if q.Len() != 0 {
|
||||
if dj.queue.CurrentSong().DontSkip() == true {
|
||||
dj.queue.CurrentSong().SetDontSkip(false)
|
||||
|
|
Reference in a new issue