aboutsummaryrefslogtreecommitdiff
path: root/internal/pvfm/recording
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2024-09-02 09:33:04 -0400
committerXe Iaso <me@xeiaso.net>2024-09-02 09:33:04 -0400
commit4628a6e4ba4920925bef6b5dbb6dfd15c7b08a73 (patch)
tree34aefa02776bec947c3cb6f38b7c1fd69f7a9d2f /internal/pvfm/recording
parenta2cf1050866bc902ad014ab21767531ff64a337a (diff)
downloadx-4628a6e4ba4920925bef6b5dbb6dfd15c7b08a73.tar.xz
x-4628a6e4ba4920925bef6b5dbb6dfd15c7b08a73.zip
import cmd/aerial
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'internal/pvfm/recording')
-rw-r--r--internal/pvfm/recording/doc.go4
-rw-r--r--internal/pvfm/recording/recording.go120
-rw-r--r--internal/pvfm/recording/recording_demo.go59
3 files changed, 183 insertions, 0 deletions
diff --git a/internal/pvfm/recording/doc.go b/internal/pvfm/recording/doc.go
new file mode 100644
index 0000000..95bd800
--- /dev/null
+++ b/internal/pvfm/recording/doc.go
@@ -0,0 +1,4 @@
+/*
+Package recording manages recording radio streams to files.
+*/
+package recording
diff --git a/internal/pvfm/recording/recording.go b/internal/pvfm/recording/recording.go
new file mode 100644
index 0000000..d3378ce
--- /dev/null
+++ b/internal/pvfm/recording/recording.go
@@ -0,0 +1,120 @@
+package recording
+
+import (
+ "context"
+ "errors"
+ "log"
+ "os"
+ "os/exec"
+ "time"
+)
+
+var (
+ ErrMismatchWrite = errors.New("recording: did not write the same number of bytes that were read")
+)
+
+// Recording ...
+type Recording struct {
+ ctx context.Context
+ url string
+ fname string
+ cancel context.CancelFunc
+ started time.Time
+ restarts int
+
+ Debug bool
+ Err error
+}
+
+// New creates a new Recording of the given URL to the given filename for output.
+func New(url, fname string) (*Recording, error) {
+ ctx, cancel := context.WithTimeout(context.Background(), 8*time.Hour)
+
+ r := &Recording{
+ ctx: ctx,
+ url: url,
+ fname: fname,
+ cancel: cancel,
+ started: time.Now(),
+ }
+
+ return r, nil
+}
+
+// Cancel stops the recording.
+func (r *Recording) Cancel() {
+ r.cancel()
+}
+
+// Done returns the done channel of the recording.
+func (r *Recording) Done() <-chan struct{} {
+ return r.ctx.Done()
+}
+
+// OutputFilename gets the output filename originally passed into New.
+func (r *Recording) OutputFilename() string {
+ return r.fname
+}
+
+// StartTime gets start time
+func (r *Recording) StartTime() time.Time {
+ return r.started
+}
+
+// Start blockingly starts the recording and returns the error if one is encountered while streaming.
+// This should be stopped in another goroutine.
+func (r *Recording) Start() error {
+ sr, err := exec.LookPath("streamripper")
+ if err != nil {
+ return err
+ }
+
+ cmd := exec.CommandContext(r.ctx, sr, r.url, "-A", "-a", r.fname)
+ cmd.Stderr = os.Stderr
+ cmd.Stdout = os.Stdout
+
+ log.Printf("%s: %v", cmd.Path, cmd.Args)
+
+ err = cmd.Start()
+ if err != nil {
+ return err
+ }
+
+ // Automatically kill recordings after four hours
+ go func() {
+ t := time.NewTicker(4 * time.Hour)
+ defer t.Stop()
+
+ log.Println("got here")
+
+ for {
+ select {
+ case <-r.ctx.Done():
+ return
+ case <-t.C:
+ log.Printf("Automatically killing recording after 4 hours...")
+ r.Cancel()
+ }
+ }
+ }()
+
+ go func() {
+ defer r.Cancel()
+ err := cmd.Wait()
+ if err != nil {
+ log.Println(err)
+ }
+ }()
+
+ defer r.cancel()
+
+ for {
+ time.Sleep(250 * time.Millisecond)
+
+ select {
+ case <-r.ctx.Done():
+ return nil
+ default:
+ }
+ }
+}
diff --git a/internal/pvfm/recording/recording_demo.go b/internal/pvfm/recording/recording_demo.go
new file mode 100644
index 0000000..3264841
--- /dev/null
+++ b/internal/pvfm/recording/recording_demo.go
@@ -0,0 +1,59 @@
+// +build ignore
+
+package main
+
+import (
+ "flag"
+ "log"
+ "os"
+ "os/signal"
+
+ "github.com/PonyvilleFM/aura/recording"
+)
+
+var (
+ url = flag.String("url", "", "url to record")
+ fname = flag.String("fname", "", "filename to record to")
+ debug = flag.Bool("debug", false, "debug mode")
+
+ askedToDie bool
+)
+
+func main() {
+ flag.Parse()
+
+ r, err := recording.New(*url, *fname)
+ if err != nil {
+ log.Printf("%s -> %s: %v", *url, *fname, err)
+ log.Fatal(err)
+ }
+
+ r.Debug = *debug
+
+ go func() {
+ log.Printf("Starting download of stream %s to %s", *url, *fname)
+ err := r.Start()
+ if err != nil {
+ log.Fatal(err)
+ }
+ }()
+
+ go func() {
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt)
+
+ for _ = range c {
+ if askedToDie {
+ os.Exit(2)
+ }
+
+ log.Println("Stopping recording... (^C again to kill now)")
+ r.Cancel()
+
+ askedToDie = true
+ }
+ }()
+
+ <-r.Done()
+ log.Printf("stream %s recorded to %s", *url, *fname)
+}