Skip to content

Commit

Permalink
Merge pull request #43 from jmillerv/23-web-radio-will-never-stop
Browse files Browse the repository at this point in the history
23 web radio will never stop
  • Loading branch information
jmillerv authored Dec 29, 2022
2 parents 193fe4a + 0e5cca2 commit 3d69382
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 9 deletions.
4 changes: 4 additions & 0 deletions content/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ var (
PodcastPlayerOrderOldest bool
)

func (p *Program) getMediaType() MediaType {
return p.Type
}

func (p *Program) GetMedia() Media {
media := p.mediaFactory()
return media
Expand Down
91 changes: 84 additions & 7 deletions content/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package content

import (
"fmt"
"github.com/araddon/dateparse"
"github.com/jmillerv/go-utilities/formatter"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"math/rand"
"os"
"os/signal"
"strconv"
"sync"
"syscall"
"time"
)
Expand All @@ -26,8 +29,8 @@ type Scheduler struct {
}

func (s *Scheduler) Run() error {
var wg sync.WaitGroup
log.Info("Starting Daemon")

// setup signal listeners
sigchnl := make(chan os.Signal, 1)
signal.Notify(sigchnl)
Expand All @@ -39,36 +42,62 @@ func (s *Scheduler) Run() error {
// run operation in loop
for programIndex <= totalPrograms {
// check content from scheduler and run through it
// for loop that can be forced to continue from a go routine
for _, p := range s.Content.Programs {
now := time.Now()
log.Debugf("program %v", formatter.StructToIndentedString(p))

if p.Timeslot.IsScheduledNow(now) {
// if content is scheduled, retrieve and play
scheduled := p.Timeslot.IsScheduledNow(now)
if scheduled {
log.Infof("getting media type: %v", p.Type)
content := p.GetMedia()
log.Debugf("media struct: %v", content)
err := content.Get() // retrieve contents from file
if err != nil {
return err
}

// setup channel for os.Exit signal
go func() {
for {
stop := <-sigchnl
s.Stop(stop, content)
}
}()
err = content.Play()
if err != nil {
return err
} // play will block until done
}

// if p.getMediaType is webRadioContent call start a timer and stop content from inside a go routine
// because the stream will block until done, it behaves differently from other content.
if p.getMediaType() == webRadioContent && scheduled {
go func() {
duration := getDurationToEndTime(p.Timeslot.End) // might cause an index out of range issue
stopCountDown(content, duration, &wg)
}()
go func() {
log.Info("playing web radio inside of a go routine")
wg.Add(1)
err = content.Play()
if err != nil {
log.WithError(err).Error("Run::content.Play")
} // play will block until done
}()
} else {
err = content.Play()
if err != nil {
return err
}
}
}
log.Info("paused while go routines are running")
wg.Wait() // pause
if !p.Timeslot.IsScheduledNow(now) {
log.WithField("IsScheduledNow", p.Timeslot.IsScheduledNow(now)).
WithField("current time", time.Now().
Format(time.Kitchen)).Infof("media not scheduled")
}
programIndex++ // increment index

// check programs for scheduled content at regular interval
if programIndex > totalPrograms {
programIndex = 0

Expand Down Expand Up @@ -155,6 +184,10 @@ func (s *Scheduler) Stop(signal os.Signal, media Media) {
}
}

func (s *Scheduler) getNextProgram(index int) *Program {
return s.Content.Programs[index]
}

func NewScheduler(file string) (*Scheduler, error) {
log.Info("Loading Config File from: ", file)
viper.SetConfigType("yaml")
Expand All @@ -180,3 +213,47 @@ func NewScheduler(file string) (*Scheduler, error) {
log.Info("config loaded", formatter.StructToIndentedString(scheduler))
return scheduler, nil
}

// stopCountDown takes in a Media and duration and starts a ticker to stop the playing content
func stopCountDown(content Media, period time.Duration, wg *sync.WaitGroup) {
log.Infof("remaining time playing this stream %v", period)
t := time.NewTicker(period)
defer t.Stop()
for {
select {
case <-t.C: // call content.Stop
err := content.Stop()
if err != nil {
log.WithError(err).Error("stopCountDown::error stopping content")
}
// only send a wg.Done() signal if the web radio has stopped playing.
if !content.(*WebRadio).Player.isPlaying {
wg.Done()
}
log.Info("content stopped")
return
}
}
}

// getDurationToEndTime determines how much time in seconds needs to pass before the next program starts.
// TODO look at this function and timeslot.go's IsScheduleNow() and attempt to refactor to remove duplicate code.
func getDurationToEndTime(currentProgramEnd string) time.Duration {
current := time.Now()
// get date info for string
date := time.Date(current.Year(), current.Month(), current.Day(), 0, 0, 0, 0, current.Location())
year, month, day := date.Date()

// convert ints to dateString
dateString := strconv.Itoa(year) + "-" + strconv.Itoa(int(month)) + "-" + strconv.Itoa(day)

// parse the date and the config time
// parsed times are returned in 2022-12-05 15:05:00 +0000 UTC format which doesn't appear to have a const in the time package
parsedProgramEnd, _ := dateparse.ParseAny(dateString + " " + currentProgramEnd)

// matched parse time to fixed zone time
currentProgramEndTime := time.Date(parsedProgramEnd.Year(), parsedProgramEnd.Month(), parsedProgramEnd.Day(), parsedProgramEnd.Hour(), parsedProgramEnd.Minute(), parsedProgramEnd.Second(), parsedProgramEnd.Nanosecond(), current.Location())

duration := currentProgramEndTime.Sub(current)
return duration
}
25 changes: 23 additions & 2 deletions content/schedule_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package content_test
package content

import (
. "github.com/jmillerv/go-dj/content"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"testing"
Expand Down Expand Up @@ -88,3 +87,25 @@ func TestNewScheduler(t *testing.T) {
})
}
}

func Test_getDurationBetweenPrograms(t *testing.T) {
type args struct {
endTime string
}
tests := []struct {
name string
args args
}{
{
name: "Success: Returns Correct Duration",
args: args{
endTime: "9:30PM",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
log.Info(getDurationToEndTime(tt.args.endTime))
})
}
}

0 comments on commit 3d69382

Please sign in to comment.