Skip to content

Commit

Permalink
fix live to cache switch
Browse files Browse the repository at this point in the history
  • Loading branch information
keshon committed Nov 30, 2024
1 parent ea34405 commit c772225
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 80 deletions.
94 changes: 21 additions & 73 deletions player/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ PLAYBACK_LOOP:
options.EncodingLineLog = true

streamPath := p.Song.StreamURL

cacheReady := make(chan bool)
if !isCached {
go func() {
Expand All @@ -124,16 +125,21 @@ PLAYBACK_LOOP:
return
}

path, err := ytdlp.New().GetStream(p.Song.PublicLink)
output, path, err := ytdlp.New().GetStream(p.Song.PublicLink)
if err != nil {
fmt.Printf("Error caching: %v\n", err)
return
}

p.Song.StreamURL = path
isCached = true // Mark as cached
cacheReady <- true
fmt.Println("DOING SWITCH")
fmt.Println(output)

if output.ExitCode == 0 {
time.Sleep(5 * time.Second)
p.Song.StreamURL = path
cacheReady <- true
}

fmt.Println("Caching is done, switching to playback from cache")
}()
}

Expand Down Expand Up @@ -220,18 +226,23 @@ PLAYBACK_LOOP:
encoding.Stop()
encoding.Cleanup()
p.Song = nil
isCached = false
continue PLAYBACK_LOOP
}
// finished
fmt.Printf("Finished playback of \"%v\"", p.Song.Title)
return nil
case <-cacheReady: // Cache is ready
fmt.Println("Switching to cached playback")
encoding.Stop()
_, position, err := p.getPlaybackDuration(encoding, streaming, p.Song)
if err != nil {
return err
}
isCached = true
//encoding.Stop()
encoding.Cleanup()
//streamPath = filename
startAt = position
continue PLAYBACK_LOOP

case signal := <-p.ActionSignals:
switch signal {
case ActionSkip:
Expand All @@ -240,6 +251,7 @@ PLAYBACK_LOOP:
encoding.Stop()
encoding.Cleanup()
p.Song = nil
isCached = false
continue PLAYBACK_LOOP
}
p.ActionSignals <- ActionStop
Expand All @@ -262,70 +274,6 @@ PLAYBACK_LOOP:
}
}
}
func (s *Player) streamToFile(stream io.Reader) (string, error) {
cacheDir := "./cache"
if err := os.MkdirAll(cacheDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create cache directory: %v", err)
}

tempFile, err := os.CreateTemp(cacheDir, "*.mp4")
if err != nil {
return "", fmt.Errorf("failed to create temp file: %v", err)
}
defer tempFile.Close()

_, err = io.Copy(tempFile, stream)
if err != nil {
return "", fmt.Errorf("failed to write stream to temp file: %v", err)
}

return tempFile.Name(), nil
}

func (p *Player) streamToFileWithRetry(streamProvider func() (io.Reader, error), retries int, delay time.Duration) (string, error) {
cacheDir := "./cache"
if err := os.MkdirAll(cacheDir, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create cache directory: %v", err)
}

var tempFilePath string
var err error

for attempt := 1; attempt <= retries; attempt++ {
fmt.Printf("Attempt %d/%d to write stream to file...\n", attempt, retries)

// Create a new stream for each attempt
stream, err := streamProvider()
if err != nil {
fmt.Printf("Error retrieving stream on attempt %d: %v\n", attempt, err)
time.Sleep(delay)
continue
}

// Create a temporary file
tempFile, err := os.CreateTemp(cacheDir, "*.mp4")
if err != nil {
return "", fmt.Errorf("failed to create temp file: %v", err)
}
tempFilePath = tempFile.Name()
defer tempFile.Close()

// Write the stream to the file
written, err := io.Copy(tempFile, stream)
if err == nil && written > 0 {
fmt.Printf("Successfully wrote %d bytes to %s\n", written, tempFilePath)
return tempFilePath, nil
}

// Clean up and retry if writing failed
fmt.Printf("Error writing stream on attempt %d: %v\n", attempt, err)
os.Remove(tempFilePath) // Clean up the failed file
time.Sleep(delay)
delay *= 2 // Exponential backoff
}

return "", fmt.Errorf("failed to write stream to file after %d attempts: %v", retries, err)
}

func (p *Player) joinVoiceChannel(session *discordgo.Session, guildID, channelID string) (*discordgo.VoiceConnection, error) {
var voiceConnection *discordgo.VoiceConnection
Expand Down Expand Up @@ -375,7 +323,7 @@ func (p *Player) getPlaybackDuration(encoding *dca.EncodeSession, streaming *dca
return 0, 0, fmt.Errorf("unknown source: %v", song.Source)
}

playbackPos := encodingStartTime + streamingPos + streamingDelay.Abs() // the delay is wrong here, but I'm out of ideas how to fix the precision
playbackPos := encodingStartTime + streamingPos

fmt.Printf("Playback stopped at:\t%s,\tTotal Song duration:\t%s\n", playbackPos, songDuration)
fmt.Printf("Encoding started at:\t%s,\tStreaming delay:\t%s\n", encodingStartTime, streamingDelay)
Expand Down
20 changes: 13 additions & 7 deletions ytdlp/ytdlp.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,30 +68,36 @@ func (y *YtdlpWrapper) GetMetaInfo(url string) (Meta, error) {
return meta, nil
}

func (y *YtdlpWrapper) GetStream(url string) (string, error) {
func (y *YtdlpWrapper) GetStream(url string) (*ytdlp.Result, string, error) {
cacheDir := "./cache"

if err := os.MkdirAll(cacheDir, 0755); err != nil {
return "", fmt.Errorf("failed to create cache directory: %w", err)
return nil, "", fmt.Errorf("failed to create cache directory: %w", err)
}

timestamp := time.Now().Format("20060102_150405")
outputFile := filepath.Join(cacheDir, timestamp+".webm")

dl := ytdlp.New().Output(outputFile)
dl := ytdlp.New().
FormatSort("res,ext:webm:webm").
NoPart().
NoPlaylist().
NoOverwrites().
Output(outputFile)

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()

if _, err := dl.Run(ctx, url); err != nil {
result, err := dl.Run(ctx, url)
if err != nil {
_ = os.Remove(outputFile)
return "", fmt.Errorf("failed to download stream from URL %q: %w", url, err)
return nil, "", fmt.Errorf("failed to download stream from URL %q: %w", url, err)
}

absPath, err := filepath.Abs(outputFile)
if err != nil {
return "", fmt.Errorf("failed to resolve absolute path for %q: %w", outputFile, err)
return nil, "", fmt.Errorf("failed to resolve absolute path for %q: %w", outputFile, err)
}

return absPath, nil
return result, absPath, nil
}

0 comments on commit c772225

Please sign in to comment.