···29 return err
30 }
3132+ if *streamerName == "" {
33+ return fmt.Errorf("streamer name is required")
34+ }
35+36 secpSigner, _ := secp256k1.PrivKeyFromBytes(keyBs)
37 if secpSigner == nil {
38 return fmt.Errorf("invalid key")
+5-5
pkg/cmd/streamplace.go
···83 return Sign(context.Background())
84 }
85000086 if len(os.Args) > 1 && os.Args[1] == "self-test" {
87 err := media.RunSelfTest(context.Background())
88 if err != nil {
···122 fs.StringVar(&cli.AppBundleID, "app-bundle-id", "", "bundle id of an app that we facilitate oauth login for")
123 fs.StringVar(&cli.StreamerName, "streamer-name", "", "name of the person streaming from this streamplace node")
124 fs.StringVar(&cli.FrontendProxy, "dev-frontend-proxy", "", "(FOR DEVELOPMENT ONLY) proxy frontend requests to this address instead of using the bundled frontend")
0125 cli.StringSliceFlag(fs, &cli.AllowedStreams, "allowed-streams", "", "if set, only allow these addresses or atproto DIDs to upload to this node")
126 cli.StringSliceFlag(fs, &cli.Peers, "peers", "", "other streamplace nodes to replicate to")
127 cli.DebugFlag(fs, &cli.Debug, "debug", "", "modified log verbosity for specific functions or files in form func=ToHLS:3,file=gstreamer.go:4")
128 fs.BoolVar(&cli.TestStream, "test-stream", false, "run a built-in test stream on boot")
129 fs.BoolVar(&cli.NoFirehose, "no-firehose", false, "disable the bluesky firehose")
130- doValidate := fs.Bool("validate", false, "validate media")
131 verbosity := fs.String("v", "3", "log verbosity level")
132 fs.StringVar(&cli.RelayHost, "relay-host", "wss://bsky.network", "websocket url for relay firehose")
133 fs.Bool("insecure", false, "DEPRECATED, does nothing.")
···166 "runtime.Version", runtime.Version())
167 if *version {
168 return nil
169- }
170-171- if *doValidate {
172- return media.ValidateMedia(ctx)
173 }
174175 aqhttp.UserAgent = fmt.Sprintf("streamplace/%s", build.Version)
···83 return Sign(context.Background())
84 }
8586+ if len(os.Args) > 1 && os.Args[1] == "whip" {
87+ return WHIP()
88+ }
89+90 if len(os.Args) > 1 && os.Args[1] == "self-test" {
91 err := media.RunSelfTest(context.Background())
92 if err != nil {
···126 fs.StringVar(&cli.AppBundleID, "app-bundle-id", "", "bundle id of an app that we facilitate oauth login for")
127 fs.StringVar(&cli.StreamerName, "streamer-name", "", "name of the person streaming from this streamplace node")
128 fs.StringVar(&cli.FrontendProxy, "dev-frontend-proxy", "", "(FOR DEVELOPMENT ONLY) proxy frontend requests to this address instead of using the bundled frontend")
129+ fs.BoolVar(&cli.WideOpen, "wide-open", false, "allow ALL streams to be uploaded to this node (not recommended for production)")
130 cli.StringSliceFlag(fs, &cli.AllowedStreams, "allowed-streams", "", "if set, only allow these addresses or atproto DIDs to upload to this node")
131 cli.StringSliceFlag(fs, &cli.Peers, "peers", "", "other streamplace nodes to replicate to")
132 cli.DebugFlag(fs, &cli.Debug, "debug", "", "modified log verbosity for specific functions or files in form func=ToHLS:3,file=gstreamer.go:4")
133 fs.BoolVar(&cli.TestStream, "test-stream", false, "run a built-in test stream on boot")
134 fs.BoolVar(&cli.NoFirehose, "no-firehose", false, "disable the bluesky firehose")
0135 verbosity := fs.String("v", "3", "log verbosity level")
136 fs.StringVar(&cli.RelayHost, "relay-host", "wss://bsky.network", "websocket url for relay firehose")
137 fs.Bool("insecure", false, "DEPRECATED, does nothing.")
···170 "runtime.Version", runtime.Version())
171 if *version {
172 return nil
0000173 }
174175 aqhttp.UserAgent = fmt.Sprintf("streamplace/%s", build.Version)
···75 RelayHost string
76 Debug map[string]map[string]int
77 AllowedStreams []string
078 Peers []string
79 TestStream bool
80 FrontendProxy string
···334}
335336func (cli *CLI) StreamIsAllowed(did string) error {
000337 // if the user set no test streams, anyone can stream
338 openServer := len(cli.AllowedStreams) == 0 || (cli.TestStream && len(cli.AllowedStreams) == 1)
339 // but only valid atproto accounts! did:key is only allowed for our local test stream
···75 RelayHost string
76 Debug map[string]map[string]int
77 AllowedStreams []string
78+ WideOpen bool
79 Peers []string
80 TestStream bool
81 FrontendProxy string
···335}
336337func (cli *CLI) StreamIsAllowed(did string) error {
338+ if cli.WideOpen {
339+ return nil
340+ }
341 // if the user set no test streams, anyone can stream
342 openServer := len(cli.AllowedStreams) == 0 || (cli.TestStream && len(cli.AllowedStreams) == 1)
343 // but only valid atproto accounts! did:key is only allowed for our local test stream
+4-1
pkg/media/concat.go
···1617type ConcatStreamer interface {
18 SubscribeSegment(ctx context.Context, user string) <-chan string
019}
2021// This function remains in scope for the duration of a single users' playback
···127 allFiles := make(chan string, 1024)
128 go func() {
129 for {
0130 select {
131 case <-ctx.Done():
132 log.Warn(ctx, "exiting segment reader")
0133 return
134- case file := <-streamer.SubscribeSegment(ctx, user):
135 log.Debug(ctx, "got segment", "file", file)
136 allFiles <- file
137 if file == "" {
···1617type ConcatStreamer interface {
18 SubscribeSegment(ctx context.Context, user string) <-chan string
19+ UnsubscribeSegment(ctx context.Context, user string, ch <-chan string)
20}
2122// This function remains in scope for the duration of a single users' playback
···128 allFiles := make(chan string, 1024)
129 go func() {
130 for {
131+ ch := streamer.SubscribeSegment(ctx, user)
132 select {
133 case <-ctx.Done():
134 log.Warn(ctx, "exiting segment reader")
135+ streamer.UnsubscribeSegment(ctx, user, ch)
136 return
137+ case file := <-ch:
138 log.Debug(ctx, "got segment", "file", file)
139 allFiles <- file
140 if file == "" {
···122 return c
123}
12400000000000125// subscribe to the latest segments from a given user for livestreaming purposes
126func (mm *MediaManager) PublishSegment(ctx context.Context, user, file string) {
127 mm.mp4subsmut.Lock()
···122 return c
123}
124125+func (mm *MediaManager) UnsubscribeSegment(ctx context.Context, user string, ch <-chan string) {
126+ mm.mp4subsmut.Lock()
127+ defer mm.mp4subsmut.Unlock()
128+ for i, c := range mm.mp4subs[user] {
129+ if c == ch {
130+ mm.mp4subs[user] = append(mm.mp4subs[user][:i], mm.mp4subs[user][i+1:]...)
131+ break
132+ }
133+ }
134+}
135+136// subscribe to the latest segments from a given user for livestreaming purposes
137func (mm *MediaManager) PublishSegment(ctx context.Context, user, file string) {
138 mm.mp4subsmut.Lock()
+4-1
pkg/media/media_signer_ext.go
···68 "--streamer", ms.streamer,
69 "--start-time", fmt.Sprintf("%d", start))
7000071 // Set up pipes for stdin and stdout
72 stdin, err := cmd.StdinPipe()
73 if err != nil {
···87 // Copy input to stdin
88 _, err = io.Copy(stdin, input)
89 if err != nil {
90- return nil, fmt.Errorf("failed to write to stdin: %w", err)
91 }
92 stdin.Close()
93
···68 "--streamer", ms.streamer,
69 "--start-time", fmt.Sprintf("%d", start))
7071+ // overwrite so that our subprocesses don't do their own leak checking
72+ cmd.Env = append(os.Environ(), "LD_PRELOAD=")
73+74 // Set up pipes for stdin and stdout
75 stdin, err := cmd.StdinPipe()
76 if err != nil {
···90 // Copy input to stdin
91 _, err = io.Copy(stdin, input)
92 if err != nil {
93+ return nil, fmt.Errorf("failed to write to stdin: %w stderr=%s", err, stderr.String())
94 }
95 stdin.Close()
96