tangled
alpha
login
or
join now
stream.place
/
streamplace
77
fork
atom
Live video on the AT Protocol
77
fork
atom
overview
issues
1
pulls
pipelines
move to your favourite cli library
Natalie B.
1 month ago
7b48c1eb
97ba8748
+946
-389
6 changed files
expand all
collapse all
unified
split
go.mod
go.sum
pkg
cmd
combine.go
streamplace.go
config
config.go
media
segment_roundtrip_test.go
+1
go.mod
···
475
475
github.com/ultraware/funlen v0.2.0 // indirect
476
476
github.com/ultraware/whitespace v0.2.0 // indirect
477
477
github.com/urfave/cli/v2 v2.27.7 // indirect
478
478
+
github.com/urfave/cli/v3 v3.6.2 // indirect
478
479
github.com/uudashr/gocognit v1.2.0 // indirect
479
480
github.com/uudashr/iface v1.3.1 // indirect
480
481
github.com/valyala/bytebufferpool v1.0.0 // indirect
+2
go.sum
···
1389
1389
github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
1390
1390
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
1391
1391
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
1392
1392
+
github.com/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=
1393
1393
+
github.com/urfave/cli/v3 v3.6.2/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
1392
1394
github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA=
1393
1395
github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU=
1394
1396
github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U=
+19
-10
pkg/cmd/combine.go
···
17
17
func Combine(ctx context.Context, build *config.BuildFlags, allArgs []string) error {
18
18
gstinit.InitGST()
19
19
cli := &config.CLI{Build: build}
20
20
-
fs := cli.NewFlagSet("streamplace combine")
21
21
-
debugDir := fs.String("debug-dir", "", "directory to write debug files to")
22
20
23
23
-
err := cli.Parse(fs, allArgs)
24
24
-
if err != nil {
25
25
-
return err
21
21
+
var debugDir string
22
22
+
// Simple flag parsing for debug-dir
23
23
+
args := allArgs
24
24
+
for i, arg := range allArgs {
25
25
+
if arg == "--debug-dir" && i+1 < len(allArgs) {
26
26
+
debugDir = allArgs[i+1]
27
27
+
// Remove the flag from args
28
28
+
args = append(allArgs[:i], allArgs[i+2:]...)
29
29
+
break
30
30
+
}
26
31
}
27
27
-
if *debugDir != "" {
28
28
-
err := os.MkdirAll(*debugDir, 0755)
32
32
+
33
33
+
if debugDir != "" {
34
34
+
err := os.MkdirAll(debugDir, 0755)
29
35
if err != nil {
30
36
return fmt.Errorf("failed to create debug directory: %w", err)
31
37
}
32
38
}
33
33
-
log.Debug(context.Background(), "combine command: starting", "args", fs.Args())
39
39
+
log.Debug(context.Background(), "combine command: starting", "args", args)
34
40
ctx = log.WithDebugValue(ctx, cli.Debug)
35
41
cryptoSigner, err := createSigner(ctx, cli)
36
42
if err != nil {
···
40
46
if err != nil {
41
47
return err
42
48
}
43
43
-
args := fs.Args()
49
49
+
50
50
+
if len(args) < 2 {
51
51
+
return fmt.Errorf("usage: streamplace combine [--debug-dir dir] <output> <input1> [input2...]")
52
52
+
}
44
53
outFile := args[0]
45
54
inputs := args[1:]
46
55
log.Log(ctx, "combining segments", "outFile", outFile, "inputs", inputs)
···
62
71
if err != nil {
63
72
return err
64
73
}
65
65
-
err = CheckCombined(ctx, cli, outFd, *debugDir)
74
74
+
err = CheckCombined(ctx, cli, outFd, debugDir)
66
75
if err != nil {
67
76
return err
68
77
}
+215
-141
pkg/cmd/streamplace.go
···
21
21
"github.com/bluesky-social/indigo/carstore"
22
22
"github.com/ethereum/go-ethereum/common/hexutil"
23
23
"github.com/livepeer/go-livepeer/cmd/livepeer/starter"
24
24
-
"github.com/peterbourgon/ff/v3"
25
24
"github.com/streamplace/oatproxy/pkg/oatproxy"
25
25
+
urfavecli "github.com/urfave/cli/v3"
26
26
"stream.place/streamplace/pkg/aqhttp"
27
27
"stream.place/streamplace/pkg/atproto"
28
28
"stream.place/streamplace/pkg/bus"
···
54
54
// parse the CLI and fire up an streamplace node!
55
55
func start(build *config.BuildFlags, platformJobs []jobFunc) error {
56
56
iroh_streamplace.InitLogging()
57
57
-
selfTest := len(os.Args) > 1 && os.Args[1] == "self-test"
58
58
-
err := media.RunSelfTest(context.Background())
59
59
-
if err != nil {
60
60
-
if selfTest {
61
61
-
fmt.Println(err.Error())
62
62
-
os.Exit(1)
63
63
-
} else {
64
64
-
retryCount, _ := strconv.Atoi(os.Getenv("STREAMPLACE_SELFTEST_RETRY"))
65
65
-
if retryCount >= 3 {
66
66
-
log.Error(context.Background(), "gstreamer self-test failed 3 times, giving up", "error", err)
67
67
-
return err
68
68
-
}
69
69
-
log.Log(context.Background(), "error in gstreamer self-test, attempting recovery", "error", err, "retry", retryCount+1)
70
70
-
os.Setenv("STREAMPLACE_SELFTEST_RETRY", strconv.Itoa(retryCount+1))
71
71
-
err := syscall.Exec(os.Args[0], os.Args[1:], os.Environ())
72
72
-
if err != nil {
73
73
-
log.Error(context.Background(), "error in gstreamer self-test, could not restart", "error", err)
74
74
-
return err
75
75
-
}
76
76
-
panic("invalid code path: exec succeeded but we're still here???")
77
77
-
}
78
78
-
}
79
79
-
if selfTest {
80
80
-
runtime.GC()
81
81
-
if err := pprof.Lookup("goroutine").WriteTo(os.Stderr, 2); err != nil {
82
82
-
log.Error(context.Background(), "error creating pprof", "error", err)
83
83
-
}
84
84
-
fmt.Println("self-test successful!")
85
85
-
os.Exit(0)
86
86
-
}
87
57
88
88
-
if len(os.Args) > 1 && os.Args[1] == "stream" {
89
89
-
if len(os.Args) != 3 {
90
90
-
fmt.Println("usage: streamplace stream [user]")
91
91
-
os.Exit(1)
92
92
-
}
93
93
-
return Stream(os.Args[2])
94
94
-
}
95
95
-
96
96
-
if len(os.Args) > 1 && os.Args[1] == "live" {
97
97
-
cli := config.CLI{Build: build}
98
98
-
fs := cli.NewFlagSet("streamplace live")
99
99
-
100
100
-
err := cli.Parse(fs, os.Args[2:])
101
101
-
if err != nil {
102
102
-
return err
103
103
-
}
104
104
-
105
105
-
args := fs.Args()
106
106
-
if len(args) != 1 {
107
107
-
fmt.Println("usage: streamplace live [flags] [stream-key]")
108
108
-
os.Exit(1)
109
109
-
}
110
110
-
111
111
-
return Live(args[0], cli.HTTPInternalAddr)
112
112
-
}
113
113
-
114
114
-
if len(os.Args) > 1 && os.Args[1] == "sign" {
115
115
-
return Sign(context.Background())
116
116
-
}
117
117
-
118
118
-
if len(os.Args) > 1 && os.Args[1] == "whep" {
119
119
-
return WHEP(os.Args[2:])
120
120
-
}
121
121
-
if len(os.Args) > 1 && os.Args[1] == "whip" {
122
122
-
return WHIP(os.Args[2:])
58
58
+
cli := config.CLI{Build: build}
59
59
+
app := cli.NewCommand("streamplace")
60
60
+
app.Usage = "decentralized live streaming platform"
61
61
+
app.Version = build.Version
62
62
+
app.Commands = []*urfavecli.Command{
63
63
+
makeSelfTestCommand(build),
64
64
+
makeStreamCommand(build),
65
65
+
makeLiveCommand(build),
66
66
+
makeSignCommand(build),
67
67
+
makeWhepCommand(build),
68
68
+
makeWhipCommand(build),
69
69
+
makeCombineCommand(build),
70
70
+
makeSplitCommand(build),
71
71
+
makeLivepeerCommand(build),
72
72
+
makeMigrateCommand(build),
123
73
}
124
124
-
125
125
-
if len(os.Args) > 1 && os.Args[1] == "combine" {
126
126
-
return Combine(context.Background(), build, os.Args[2:])
127
127
-
}
128
128
-
129
129
-
if len(os.Args) > 1 && os.Args[1] == "split" {
130
130
-
cli := config.CLI{Build: build}
131
131
-
fs := cli.NewFlagSet("streamplace split")
132
132
-
133
133
-
err := cli.Parse(fs, os.Args[2:])
74
74
+
// Add the verbosity flag
75
75
+
app.Flags = append(app.Flags, &urfavecli.StringFlag{
76
76
+
Name: "v",
77
77
+
Usage: "log verbosity level",
78
78
+
Value: "3",
79
79
+
})
80
80
+
app.Before = func(ctx context.Context, cmd *urfavecli.Command) (context.Context, error) {
81
81
+
// Run self-test before starting
82
82
+
selfTest := cmd.Name == "self-test"
83
83
+
err := media.RunSelfTest(ctx)
134
84
if err != nil {
135
135
-
return err
136
136
-
}
137
137
-
ctx := context.Background()
138
138
-
ctx = log.WithDebugValue(ctx, cli.Debug)
139
139
-
if len(fs.Args()) != 2 {
140
140
-
fmt.Println("usage: streamplace split [flags] [input file] [output directory]")
141
141
-
os.Exit(1)
85
85
+
if selfTest {
86
86
+
fmt.Println(err.Error())
87
87
+
os.Exit(1)
88
88
+
} else {
89
89
+
retryCount, _ := strconv.Atoi(os.Getenv("STREAMPLACE_SELFTEST_RETRY"))
90
90
+
if retryCount >= 3 {
91
91
+
log.Error(ctx, "gstreamer self-test failed 3 times, giving up", "error", err)
92
92
+
return ctx, err
93
93
+
}
94
94
+
log.Log(ctx, "error in gstreamer self-test, attempting recovery", "error", err, "retry", retryCount+1)
95
95
+
os.Setenv("STREAMPLACE_SELFTEST_RETRY", strconv.Itoa(retryCount+1))
96
96
+
err := syscall.Exec(os.Args[0], os.Args[1:], os.Environ())
97
97
+
if err != nil {
98
98
+
log.Error(ctx, "error in gstreamer self-test, could not restart", "error", err)
99
99
+
return ctx, err
100
100
+
}
101
101
+
panic("invalid code path: exec succeeded but we're still here???")
102
102
+
}
142
103
}
143
143
-
gstinit.InitGST()
144
144
-
return Split(ctx, fs.Args()[0], fs.Args()[1])
104
104
+
return ctx, nil
145
105
}
146
146
-
147
147
-
if len(os.Args) > 1 && os.Args[1] == "self-test" {
148
148
-
err := media.RunSelfTest(context.Background())
149
149
-
if err != nil {
150
150
-
fmt.Println(err.Error())
151
151
-
os.Exit(1)
152
152
-
}
153
153
-
fmt.Println("self-test successful!")
154
154
-
os.Exit(0)
106
106
+
app.Action = func(ctx context.Context, cmd *urfavecli.Command) error {
107
107
+
return runMain(ctx, build, platformJobs, cmd, &cli)
155
108
}
156
109
157
157
-
if len(os.Args) > 1 && os.Args[1] == "livepeer" {
158
158
-
lpfs := flag.NewFlagSet("livepeer", flag.ExitOnError)
159
159
-
_ = starter.NewLivepeerConfig(lpfs)
160
160
-
err = ff.Parse(lpfs, os.Args[2:],
161
161
-
ff.WithConfigFileFlag("config"),
162
162
-
ff.WithEnvVarPrefix("LP"),
163
163
-
)
164
164
-
if err != nil {
165
165
-
return err
166
166
-
}
167
167
-
err = GoLivepeer(context.Background(), lpfs)
168
168
-
if err != nil {
169
169
-
log.Error(context.Background(), "error in livepeer", "error", err)
170
170
-
os.Exit(1)
171
171
-
}
172
172
-
os.Exit(0)
173
173
-
}
110
110
+
return app.Run(context.Background(), os.Args)
111
111
+
}
174
112
113
113
+
func runMain(ctx context.Context, build *config.BuildFlags, platformJobs []jobFunc, cmd *urfavecli.Command, cli *config.CLI) error {
175
114
_ = flag.Set("logtostderr", "true")
176
115
vFlag := flag.Lookup("v")
177
177
-
cli := config.CLI{Build: build}
178
178
-
fs := cli.NewFlagSet("streamplace")
179
179
-
verbosity := fs.String("v", "3", "log verbosity level")
180
180
-
version := fs.Bool("version", false, "print version and exit")
181
116
182
182
-
err = cli.Parse(
183
183
-
fs, os.Args[1:],
184
184
-
)
117
117
+
err := cli.Validate(cmd)
185
118
if err != nil {
186
119
return err
187
120
}
···
190
123
if err != nil {
191
124
return err
192
125
}
193
193
-
_ = vFlag.Value.Set(*verbosity)
126
126
+
verbosity := cmd.String("v")
127
127
+
_ = vFlag.Value.Set(verbosity)
194
128
log.SetColorLogger(cli.Color)
195
195
-
ctx := context.Background()
196
129
ctx = log.WithDebugValue(ctx, cli.Debug)
197
130
198
131
log.Log(ctx,
···
203
136
"runtime.GOOS", runtime.GOOS,
204
137
"runtime.GOARCH", runtime.GOARCH,
205
138
"runtime.Version", runtime.Version())
206
206
-
if *version {
207
207
-
return nil
208
208
-
}
209
209
-
signer, err := createSigner(ctx, &cli)
139
139
+
140
140
+
signer, err := createSigner(ctx, cli)
210
141
if err != nil {
211
142
return err
212
143
}
213
144
214
145
if len(os.Args) > 1 && os.Args[1] == "migrate" {
215
215
-
return statedb.Migrate(&cli)
146
146
+
return statedb.Migrate(cli)
216
147
}
217
148
218
149
spmetrics.Version.WithLabelValues(build.Version).Inc()
···
262
193
if err != nil {
263
194
return err
264
195
}
265
265
-
state, err := statedb.MakeDB(ctx, &cli, noter, mod)
196
196
+
state, err := statedb.MakeDB(ctx, cli, noter, mod)
266
197
if err != nil {
267
198
return err
268
199
}
269
269
-
handle, err := atproto.MakeLexiconRepo(ctx, &cli, mod, state)
200
200
+
handle, err := atproto.MakeLexiconRepo(ctx, cli, mod, state)
270
201
if err != nil {
271
202
return err
272
203
}
···
286
217
287
218
b := bus.NewBus()
288
219
atsync := &atproto.ATProtoSynchronizer{
289
289
-
CLI: &cli,
220
220
+
CLI: cli,
290
221
Model: mod,
291
222
StatefulDB: state,
292
223
Noter: noter,
···
297
228
return fmt.Errorf("failed to migrate: %w", err)
298
229
}
299
230
300
300
-
mm, err := media.MakeMediaManager(ctx, &cli, signer, mod, b, atsync, ldb)
231
231
+
mm, err := media.MakeMediaManager(ctx, cli, signer, mod, b, atsync, ldb)
301
232
if err != nil {
302
233
return err
303
234
}
304
235
305
305
-
ms, err := media.MakeMediaSigner(ctx, &cli, cli.StreamerName, signer, mod)
236
236
+
ms, err := media.MakeMediaSigner(ctx, cli, cli.StreamerName, signer, mod)
306
237
if err != nil {
307
238
return err
308
239
}
···
365
296
return err
366
297
}
367
298
}
368
368
-
replicator, err = iroh_replicator.NewSwarm(ctx, &cli, secret, topic, mm, b, mod)
299
299
+
replicator, err = iroh_replicator.NewSwarm(ctx, cli, secret, topic, mm, b, mod)
369
300
if err != nil {
370
301
return err
371
302
}
···
387
318
Public: cli.PublicOAuth,
388
319
HTTPClient: &aqhttp.Client,
389
320
})
390
390
-
d := director.NewDirector(mm, mod, &cli, b, op, state, replicator, ldb)
391
391
-
a, err := api.MakeStreamplaceAPI(&cli, mod, state, noter, mm, ms, b, atsync, d, op, ldb)
321
321
+
d := director.NewDirector(mm, mod, cli, b, op, state, replicator, ldb)
322
322
+
a, err := api.MakeStreamplaceAPI(cli, mod, state, noter, mm, ms, b, atsync, d, op, ldb)
392
323
if err != nil {
393
324
return err
394
325
}
···
418
349
})
419
350
if cli.RTMPServerAddon != "" {
420
351
group.Go(func() error {
421
421
-
return rtmps.ServeRTMPSAddon(ctx, &cli)
352
352
+
return rtmps.ServeRTMPSAddon(ctx, cli)
422
353
})
423
354
}
424
355
group.Go(func() error {
425
425
-
return a.ServeRTMPS(ctx, &cli)
356
356
+
return a.ServeRTMPS(ctx, cli)
426
357
})
427
358
} else {
428
359
group.Go(func() error {
···
453
384
})
454
385
455
386
group.Go(func() error {
456
456
-
return storage.StartSegmentCleaner(ctx, ldb, &cli)
387
387
+
return storage.StartSegmentCleaner(ctx, ldb, cli)
457
388
})
458
389
459
390
group.Go(func() error {
···
461
392
})
462
393
463
394
group.Go(func() error {
464
464
-
return replicator.Start(ctx, &cli)
395
395
+
return replicator.Start(ctx, cli)
465
396
})
466
397
467
398
if cli.LivepeerGateway {
···
475
406
return err
476
407
}
477
408
group.Go(func() error {
478
478
-
err := GoLivepeer(ctx, fs)
409
409
+
lpfs := flag.NewFlagSet("livepeer", flag.ExitOnError)
410
410
+
_ = starter.NewLivepeerConfig(lpfs)
411
411
+
// Parse livepeer flags from mainCmd
412
412
+
err := lpfs.Parse([]string{})
413
413
+
if err != nil {
414
414
+
return err
415
415
+
}
416
416
+
err = GoLivepeer(ctx, lpfs)
479
417
if err != nil {
480
418
return err
481
419
}
···
497
435
return err
498
436
}
499
437
did := atkey.DIDKey()
500
500
-
testMediaSigner, err := media.MakeMediaSigner(ctx, &cli, did, signer, mod)
438
438
+
testMediaSigner, err := media.MakeMediaSigner(ctx, cli, did, signer, mod)
501
439
if err != nil {
502
440
return err
503
441
}
···
524
462
return err
525
463
}
526
464
did2 := atkey2.DIDKey()
527
527
-
intermittentMediaSigner, err := media.MakeMediaSigner(ctx, &cli, did2, signer, mod)
465
465
+
intermittentMediaSigner, err := media.MakeMediaSigner(ctx, cli, did2, signer, mod)
528
466
if err != nil {
529
467
return err
530
468
}
···
561
499
562
500
for _, job := range platformJobs {
563
501
group.Go(func() error {
564
564
-
return job(ctx, &cli)
502
502
+
return job(ctx, cli)
565
503
})
566
504
}
567
505
···
599
537
}
600
538
}
601
539
}
540
540
+
541
541
+
func makeSelfTestCommand(build *config.BuildFlags) *urfavecli.Command {
542
542
+
return &urfavecli.Command{
543
543
+
Name: "self-test",
544
544
+
Usage: "run gstreamer self-test",
545
545
+
Action: func(ctx context.Context, cmd *urfavecli.Command) error {
546
546
+
err := media.RunSelfTest(ctx)
547
547
+
if err != nil {
548
548
+
fmt.Println(err.Error())
549
549
+
os.Exit(1)
550
550
+
}
551
551
+
runtime.GC()
552
552
+
if err := pprof.Lookup("goroutine").WriteTo(os.Stderr, 2); err != nil {
553
553
+
log.Error(ctx, "error creating pprof", "error", err)
554
554
+
}
555
555
+
fmt.Println("self-test successful!")
556
556
+
return nil
557
557
+
},
558
558
+
}
559
559
+
}
560
560
+
561
561
+
func makeStreamCommand(build *config.BuildFlags) *urfavecli.Command {
562
562
+
return &urfavecli.Command{
563
563
+
Name: "stream",
564
564
+
Usage: "stream command",
565
565
+
ArgsUsage: "[user]",
566
566
+
Action: func(ctx context.Context, cmd *urfavecli.Command) error {
567
567
+
args := cmd.Args()
568
568
+
if args.Len() != 1 {
569
569
+
return fmt.Errorf("usage: streamplace stream [user]")
570
570
+
}
571
571
+
return Stream(args.First())
572
572
+
},
573
573
+
}
574
574
+
}
575
575
+
576
576
+
func makeLiveCommand(build *config.BuildFlags) *urfavecli.Command {
577
577
+
cli := config.CLI{Build: build}
578
578
+
liveCmd := cli.NewCommand("live")
579
579
+
liveCmd.Usage = "start live stream"
580
580
+
liveCmd.ArgsUsage = "[stream-key]"
581
581
+
liveCmd.Action = func(ctx context.Context, cmd *urfavecli.Command) error {
582
582
+
args := cmd.Args()
583
583
+
if args.Len() != 1 {
584
584
+
return fmt.Errorf("usage: streamplace live [flags] [stream-key]")
585
585
+
}
586
586
+
return Live(args.First(), cli.HTTPInternalAddr)
587
587
+
}
588
588
+
return liveCmd
589
589
+
}
590
590
+
591
591
+
func makeSignCommand(build *config.BuildFlags) *urfavecli.Command {
592
592
+
return &urfavecli.Command{
593
593
+
Name: "sign",
594
594
+
Usage: "sign command",
595
595
+
Action: func(ctx context.Context, cmd *urfavecli.Command) error {
596
596
+
return Sign(ctx)
597
597
+
},
598
598
+
}
599
599
+
}
600
600
+
601
601
+
func makeWhepCommand(build *config.BuildFlags) *urfavecli.Command {
602
602
+
return &urfavecli.Command{
603
603
+
Name: "whep",
604
604
+
Usage: "WHEP client",
605
605
+
Action: func(ctx context.Context, cmd *urfavecli.Command) error {
606
606
+
return WHEP(cmd.Args().Slice())
607
607
+
},
608
608
+
}
609
609
+
}
610
610
+
611
611
+
func makeWhipCommand(build *config.BuildFlags) *urfavecli.Command {
612
612
+
return &urfavecli.Command{
613
613
+
Name: "whip",
614
614
+
Usage: "WHIP client",
615
615
+
Action: func(ctx context.Context, cmd *urfavecli.Command) error {
616
616
+
return WHIP(cmd.Args().Slice())
617
617
+
},
618
618
+
}
619
619
+
}
620
620
+
621
621
+
func makeCombineCommand(build *config.BuildFlags) *urfavecli.Command {
622
622
+
return &urfavecli.Command{
623
623
+
Name: "combine",
624
624
+
Usage: "combine segments",
625
625
+
Action: func(ctx context.Context, cmd *urfavecli.Command) error {
626
626
+
return Combine(ctx, build, cmd.Args().Slice())
627
627
+
},
628
628
+
}
629
629
+
}
630
630
+
631
631
+
func makeSplitCommand(build *config.BuildFlags) *urfavecli.Command {
632
632
+
cli := config.CLI{Build: build}
633
633
+
splitCmd := cli.NewCommand("split")
634
634
+
splitCmd.Usage = "split video file"
635
635
+
splitCmd.ArgsUsage = "[input file] [output directory]"
636
636
+
splitCmd.Action = func(ctx context.Context, cmd *urfavecli.Command) error {
637
637
+
args := cmd.Args()
638
638
+
if args.Len() != 2 {
639
639
+
return fmt.Errorf("usage: streamplace split [flags] [input file] [output directory]")
640
640
+
}
641
641
+
ctx = log.WithDebugValue(ctx, cli.Debug)
642
642
+
gstinit.InitGST()
643
643
+
return Split(ctx, args.Get(0), args.Get(1))
644
644
+
}
645
645
+
return splitCmd
646
646
+
}
647
647
+
648
648
+
func makeLivepeerCommand(build *config.BuildFlags) *urfavecli.Command {
649
649
+
return &urfavecli.Command{
650
650
+
Name: "livepeer",
651
651
+
Usage: "run livepeer gateway",
652
652
+
SkipFlagParsing: true,
653
653
+
Action: func(ctx context.Context, cmd *urfavecli.Command) error {
654
654
+
args := cmd.Args().Slice()
655
655
+
lpfs := flag.NewFlagSet("livepeer", flag.ExitOnError)
656
656
+
_ = starter.NewLivepeerConfig(lpfs)
657
657
+
err := lpfs.Parse(args)
658
658
+
if err != nil {
659
659
+
return err
660
660
+
}
661
661
+
return GoLivepeer(ctx, lpfs)
662
662
+
},
663
663
+
}
664
664
+
}
665
665
+
666
666
+
func makeMigrateCommand(build *config.BuildFlags) *urfavecli.Command {
667
667
+
cli := config.CLI{Build: build}
668
668
+
return &urfavecli.Command{
669
669
+
Name: "migrate",
670
670
+
Usage: "run database migrations",
671
671
+
Action: func(ctx context.Context, cmd *urfavecli.Command) error {
672
672
+
return statedb.Migrate(&cli)
673
673
+
},
674
674
+
}
675
675
+
}
+704
-235
pkg/config/config.go
···
7
7
"encoding/json"
8
8
"encoding/pem"
9
9
"errors"
10
10
-
"flag"
11
10
"fmt"
12
11
"io"
13
12
"net"
14
13
"os"
15
14
"path/filepath"
16
15
"runtime"
16
16
+
"slices"
17
17
"strconv"
18
18
"strings"
19
19
"time"
···
21
21
"math/rand/v2"
22
22
23
23
"github.com/lestrrat-go/jwx/v2/jwk"
24
24
-
"github.com/livepeer/go-livepeer/cmd/livepeer/starter"
25
24
"github.com/lmittmann/tint"
26
25
slogGorm "github.com/orandin/slog-gorm"
27
27
-
"github.com/peterbourgon/ff/v3"
26
26
+
urfavecli "github.com/urfave/cli/v3"
28
27
"stream.place/streamplace/pkg/aqtime"
29
28
"stream.place/streamplace/pkg/constants"
30
29
"stream.place/streamplace/pkg/crypto/aqpub"
···
160
159
ReplicatorIroh string = "iroh"
161
160
)
162
161
163
163
-
func (cli *CLI) NewFlagSet(name string) *flag.FlagSet {
164
164
-
fs := flag.NewFlagSet("streamplace", flag.ExitOnError)
165
165
-
fs.StringVar(&cli.DataDir, "data-dir", DefaultDataDir(), "directory for keeping all streamplace data")
166
166
-
fs.StringVar(&cli.HTTPAddr, "http-addr", ":38080", "Public HTTP address")
167
167
-
fs.StringVar(&cli.HTTPInternalAddr, "http-internal-addr", "127.0.0.1:39090", "Private, admin-only HTTP address")
168
168
-
fs.StringVar(&cli.HTTPSAddr, "https-addr", ":38443", "Public HTTPS address")
169
169
-
fs.BoolVar(&cli.Secure, "secure", false, "Run with HTTPS. Required for WebRTC output")
170
170
-
cli.DataDirFlag(fs, &cli.TLSCertPath, "tls-cert", filepath.Join("tls", "tls.crt"), "Path to TLS certificate")
171
171
-
cli.DataDirFlag(fs, &cli.TLSKeyPath, "tls-key", filepath.Join("tls", "tls.key"), "Path to TLS key")
172
172
-
fs.StringVar(&cli.SigningKeyPath, "signing-key", "", "Path to signing key for pushing OTA updates to the app")
173
173
-
fs.StringVar(&cli.DBURL, "db-url", "sqlite://$SP_DATA_DIR/state.sqlite", "URL of the database to use for storing private streamplace state")
162
162
+
func (cli *CLI) NewCommand(name string) *urfavecli.Command {
163
163
+
cmd := &urfavecli.Command{
164
164
+
Name: name,
165
165
+
Usage: "streamplace server",
166
166
+
Flags: []urfavecli.Flag{
167
167
+
&urfavecli.StringFlag{
168
168
+
Name: "data-dir",
169
169
+
Usage: "directory for keeping all streamplace data",
170
170
+
Value: DefaultDataDir(),
171
171
+
Destination: &cli.DataDir,
172
172
+
Sources: urfavecli.EnvVars("SP_DATA_DIR"),
173
173
+
},
174
174
+
&urfavecli.StringFlag{
175
175
+
Name: "http-addr",
176
176
+
Usage: "Public HTTP address",
177
177
+
Value: ":38080",
178
178
+
Destination: &cli.HTTPAddr,
179
179
+
Sources: urfavecli.EnvVars("SP_HTTP_ADDR"),
180
180
+
},
181
181
+
&urfavecli.StringFlag{
182
182
+
Name: "http-internal-addr",
183
183
+
Usage: "Private, admin-only HTTP address",
184
184
+
Value: "127.0.0.1:39090",
185
185
+
Destination: &cli.HTTPInternalAddr,
186
186
+
Sources: urfavecli.EnvVars("SP_HTTP_INTERNAL_ADDR"),
187
187
+
},
188
188
+
&urfavecli.StringFlag{
189
189
+
Name: "https-addr",
190
190
+
Usage: "Public HTTPS address",
191
191
+
Value: ":38443",
192
192
+
Destination: &cli.HTTPSAddr,
193
193
+
Sources: urfavecli.EnvVars("SP_HTTPS_ADDR"),
194
194
+
},
195
195
+
&urfavecli.BoolFlag{
196
196
+
Name: "secure",
197
197
+
Usage: "Run with HTTPS. Required for WebRTC output",
198
198
+
Value: false,
199
199
+
Destination: &cli.Secure,
200
200
+
Sources: urfavecli.EnvVars("SP_SECURE"),
201
201
+
},
202
202
+
&urfavecli.StringFlag{
203
203
+
Name: "tls-cert",
204
204
+
Usage: fmt.Sprintf(`Path to TLS certificate (default: "%s")`, filepath.Join(SPDataDir, "tls", "tls.crt")),
205
205
+
Destination: &cli.TLSCertPath,
206
206
+
Value: filepath.Join(SPDataDir, "tls", "tls.crt"),
207
207
+
Sources: urfavecli.EnvVars("SP_TLS_CERT"),
208
208
+
},
209
209
+
&urfavecli.StringFlag{
210
210
+
Name: "tls-key",
211
211
+
Usage: fmt.Sprintf(`Path to TLS key (default: "%s")`, filepath.Join(SPDataDir, "tls", "tls.key")),
212
212
+
Destination: &cli.TLSKeyPath,
213
213
+
Value: filepath.Join(SPDataDir, "tls", "tls.key"),
214
214
+
Sources: urfavecli.EnvVars("SP_TLS_KEY"),
215
215
+
},
216
216
+
&urfavecli.StringFlag{
217
217
+
Name: "signing-key",
218
218
+
Usage: "Path to signing key for pushing OTA updates to the app",
219
219
+
Destination: &cli.SigningKeyPath,
220
220
+
Sources: urfavecli.EnvVars("SP_SIGNING_KEY"),
221
221
+
},
222
222
+
&urfavecli.StringFlag{
223
223
+
Name: "db-url",
224
224
+
Usage: "URL of the database to use for storing private streamplace state",
225
225
+
Value: "sqlite://$SP_DATA_DIR/state.sqlite",
226
226
+
Destination: &cli.DBURL,
227
227
+
Sources: urfavecli.EnvVars("SP_DB_URL"),
228
228
+
},
229
229
+
&urfavecli.StringFlag{
230
230
+
Name: "admin-account",
231
231
+
Usage: "ethereum account that administrates this streamplace node",
232
232
+
Destination: &cli.AdminAccount,
233
233
+
Sources: urfavecli.EnvVars("SP_ADMIN_ACCOUNT"),
234
234
+
},
235
235
+
&urfavecli.StringFlag{
236
236
+
Name: "firebase-service-account",
237
237
+
Usage: "Base64-encoded JSON string of a firebase service account key",
238
238
+
Destination: &cli.FirebaseServiceAccount,
239
239
+
Sources: urfavecli.EnvVars("SP_FIREBASE_SERVICE_ACCOUNT"),
240
240
+
},
241
241
+
&urfavecli.StringFlag{
242
242
+
Name: "firebase-service-account-file",
243
243
+
Usage: "Path to a JSON file containing a firebase service account key",
244
244
+
Destination: &cli.FirebaseServiceAccountFile,
245
245
+
Sources: urfavecli.EnvVars("SP_FIREBASE_SERVICE_ACCOUNT_FILE"),
246
246
+
},
247
247
+
&urfavecli.StringFlag{
248
248
+
Name: "gitlab-url",
249
249
+
Usage: "gitlab url for generating download links",
250
250
+
Value: "https://git.stream.place/api/v4/projects/1",
251
251
+
Destination: &cli.GitLabURL,
252
252
+
Sources: urfavecli.EnvVars("SP_GITLAB_URL"),
253
253
+
},
254
254
+
&urfavecli.StringFlag{
255
255
+
Name: "eth-keystore-path",
256
256
+
Usage: fmt.Sprintf(`path to ethereum keystore (default: "%s")`, filepath.Join(SPDataDir, "keystore")),
257
257
+
Destination: &cli.EthKeystorePath,
258
258
+
Value: filepath.Join(SPDataDir, "keystore"),
259
259
+
Sources: urfavecli.EnvVars("SP_ETH_KEYSTORE_PATH"),
260
260
+
},
261
261
+
&urfavecli.StringFlag{
262
262
+
Name: "eth-account-addr",
263
263
+
Usage: "ethereum account address to use (if keystore contains more than one)",
264
264
+
Destination: &cli.EthAccountAddr,
265
265
+
Sources: urfavecli.EnvVars("SP_ETH_ACCOUNT_ADDR"),
266
266
+
},
267
267
+
&urfavecli.StringFlag{
268
268
+
Name: "eth-password",
269
269
+
Usage: "password for encrypting keystore",
270
270
+
Destination: &cli.EthPassword,
271
271
+
Sources: urfavecli.EnvVars("SP_ETH_PASSWORD"),
272
272
+
},
273
273
+
&urfavecli.StringFlag{
274
274
+
Name: "ta-url",
275
275
+
Usage: "timestamp authority server for signing",
276
276
+
Value: "http://timestamp.digicert.com",
277
277
+
Destination: &cli.TAURL,
278
278
+
Sources: urfavecli.EnvVars("SP_TA_URL"),
279
279
+
},
280
280
+
&urfavecli.StringFlag{
281
281
+
Name: "pkcs11-module-path",
282
282
+
Usage: "path to a PKCS11 module for HSM signing, for example /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so",
283
283
+
Destination: &cli.PKCS11ModulePath,
284
284
+
Sources: urfavecli.EnvVars("SP_PKCS11_MODULE_PATH"),
285
285
+
},
286
286
+
&urfavecli.StringFlag{
287
287
+
Name: "pkcs11-pin",
288
288
+
Usage: "PIN for logging into PKCS11 token. if not provided, will be prompted interactively",
289
289
+
Destination: &cli.PKCS11Pin,
290
290
+
Sources: urfavecli.EnvVars("SP_PKCS11_PIN"),
291
291
+
},
292
292
+
&urfavecli.StringFlag{
293
293
+
Name: "pkcs11-token-slot",
294
294
+
Usage: "slot number of PKCS11 token (only use one of slot, label, or serial)",
295
295
+
Destination: &cli.PKCS11TokenSlot,
296
296
+
Sources: urfavecli.EnvVars("SP_PKCS11_TOKEN_SLOT"),
297
297
+
},
298
298
+
&urfavecli.StringFlag{
299
299
+
Name: "pkcs11-token-label",
300
300
+
Usage: "label of PKCS11 token (only use one of slot, label, or serial)",
301
301
+
Destination: &cli.PKCS11TokenLabel,
302
302
+
Sources: urfavecli.EnvVars("SP_PKCS11_TOKEN_LABEL"),
303
303
+
},
304
304
+
&urfavecli.StringFlag{
305
305
+
Name: "pkcs11-token-serial",
306
306
+
Usage: "serial number of PKCS11 token (only use one of slot, label, or serial)",
307
307
+
Destination: &cli.PKCS11TokenSerial,
308
308
+
Sources: urfavecli.EnvVars("SP_PKCS11_TOKEN_SERIAL"),
309
309
+
},
310
310
+
&urfavecli.StringFlag{
311
311
+
Name: "pkcs11-keypair-label",
312
312
+
Usage: "label of signing keypair on PKCS11 token",
313
313
+
Destination: &cli.PKCS11KeypairLabel,
314
314
+
Sources: urfavecli.EnvVars("SP_PKCS11_KEYPAIR_LABEL"),
315
315
+
},
316
316
+
&urfavecli.StringFlag{
317
317
+
Name: "pkcs11-keypair-id",
318
318
+
Usage: "id of signing keypair on PKCS11 token",
319
319
+
Destination: &cli.PKCS11KeypairID,
320
320
+
Sources: urfavecli.EnvVars("SP_PKCS11_KEYPAIR_ID"),
321
321
+
},
322
322
+
&urfavecli.StringFlag{
323
323
+
Name: "app-bundle-id",
324
324
+
Usage: "bundle id of an app that we facilitate oauth login for",
325
325
+
Destination: &cli.AppBundleID,
326
326
+
Sources: urfavecli.EnvVars("SP_APP_BUNDLE_ID"),
327
327
+
},
328
328
+
&urfavecli.StringFlag{
329
329
+
Name: "streamer-name",
330
330
+
Usage: "name of the person streaming from this streamplace node",
331
331
+
Destination: &cli.StreamerName,
332
332
+
Sources: urfavecli.EnvVars("SP_STREAMER_NAME"),
333
333
+
},
334
334
+
&urfavecli.StringFlag{
335
335
+
Name: "dev-frontend-proxy",
336
336
+
Usage: "(FOR DEVELOPMENT ONLY) proxy frontend requests to this address instead of using the bundled frontend",
337
337
+
Destination: &cli.FrontendProxy,
338
338
+
Sources: urfavecli.EnvVars("SP_DEV_FRONTEND_PROXY"),
339
339
+
},
340
340
+
&urfavecli.BoolFlag{
341
341
+
Name: "dev-public-oauth",
342
342
+
Usage: "(FOR DEVELOPMENT ONLY) enable public oauth login for http://127.0.0.1 development",
343
343
+
Value: false,
344
344
+
Destination: &cli.PublicOAuth,
345
345
+
Sources: urfavecli.EnvVars("SP_DEV_PUBLIC_OAUTH"),
346
346
+
},
347
347
+
&urfavecli.StringFlag{
348
348
+
Name: "livepeer-gateway-url",
349
349
+
Usage: "URL of the Livepeer Gateway to use for transcoding",
350
350
+
Destination: &cli.LivepeerGatewayURL,
351
351
+
Sources: urfavecli.EnvVars("SP_LIVEPEER_GATEWAY_URL"),
352
352
+
},
353
353
+
&urfavecli.BoolFlag{
354
354
+
Name: "livepeer-gateway",
355
355
+
Usage: "enable embedded Livepeer Gateway",
356
356
+
Value: false,
357
357
+
Destination: &cli.LivepeerGateway,
358
358
+
Sources: urfavecli.EnvVars("SP_LIVEPEER_GATEWAY"),
359
359
+
},
360
360
+
&urfavecli.BoolFlag{
361
361
+
Name: "wide-open",
362
362
+
Usage: "allow ALL streams to be uploaded to this node (not recommended for production)",
363
363
+
Value: false,
364
364
+
Destination: &cli.WideOpen,
365
365
+
Sources: urfavecli.EnvVars("SP_WIDE_OPEN"),
366
366
+
},
367
367
+
&urfavecli.StringFlag{
368
368
+
Name: "allowed-streams",
369
369
+
Usage: `if set, only allow these addresses or atproto DIDs to upload to this node (default: "")`,
370
370
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
371
371
+
if s == "" {
372
372
+
return nil
373
373
+
}
374
374
+
cli.AllowedStreams = strings.Split(s, ",")
375
375
+
return nil
376
376
+
},
377
377
+
Sources: urfavecli.EnvVars("SP_ALLOWED_STREAMS"),
378
378
+
},
379
379
+
&urfavecli.StringFlag{
380
380
+
Name: "peers",
381
381
+
Usage: `other streamplace nodes to replicate to (default: "")`,
382
382
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
383
383
+
if s == "" {
384
384
+
return nil
385
385
+
}
386
386
+
cli.Peers = strings.Split(s, ",")
387
387
+
return nil
388
388
+
},
389
389
+
Sources: urfavecli.EnvVars("SP_PEERS"),
390
390
+
},
391
391
+
&urfavecli.StringFlag{
392
392
+
Name: "redirects",
393
393
+
Usage: `http 302s /path/one:/path/two,/path/three:/path/four (default: "")`,
394
394
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
395
395
+
if s == "" {
396
396
+
return nil
397
397
+
}
398
398
+
cli.Redirects = strings.Split(s, ",")
399
399
+
return nil
400
400
+
},
401
401
+
Sources: urfavecli.EnvVars("SP_REDIRECTS"),
402
402
+
},
403
403
+
&urfavecli.StringFlag{
404
404
+
Name: "debug",
405
405
+
Usage: "modified log verbosity for specific functions or files in form func=ToHLS:3,file=gstreamer.go:4",
406
406
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
407
407
+
if s == "" {
408
408
+
return nil
409
409
+
}
410
410
+
cli.Debug = map[string]map[string]int{}
411
411
+
pairs := strings.SplitSeq(s, ",")
412
412
+
for pair := range pairs {
413
413
+
scoreSplit := strings.Split(pair, ":")
414
414
+
if len(scoreSplit) != 2 {
415
415
+
return fmt.Errorf("invalid debug flag: %s", pair)
416
416
+
}
417
417
+
score, err := strconv.Atoi(scoreSplit[1])
418
418
+
if err != nil {
419
419
+
return fmt.Errorf("invalid debug flag: %s", pair)
420
420
+
}
421
421
+
selectorSplit := strings.Split(scoreSplit[0], "=")
422
422
+
if len(selectorSplit) != 2 {
423
423
+
return fmt.Errorf("invalid debug flag: %s", pair)
424
424
+
}
425
425
+
_, ok := cli.Debug[selectorSplit[0]]
426
426
+
if !ok {
427
427
+
cli.Debug[selectorSplit[0]] = map[string]int{}
428
428
+
}
429
429
+
cli.Debug[selectorSplit[0]][selectorSplit[1]] = score
430
430
+
}
431
431
+
return nil
432
432
+
},
433
433
+
Sources: urfavecli.EnvVars("SP_DEBUG"),
434
434
+
},
435
435
+
&urfavecli.BoolFlag{
436
436
+
Name: "test-stream",
437
437
+
Usage: "run a built-in test stream on boot",
438
438
+
Value: false,
439
439
+
Destination: &cli.TestStream,
440
440
+
Sources: urfavecli.EnvVars("SP_TEST_STREAM"),
441
441
+
},
442
442
+
&urfavecli.BoolFlag{
443
443
+
Name: "no-firehose",
444
444
+
Usage: "disable the bluesky firehose",
445
445
+
Value: false,
446
446
+
Destination: &cli.NoFirehose,
447
447
+
Sources: urfavecli.EnvVars("SP_NO_FIREHOSE"),
448
448
+
},
449
449
+
&urfavecli.BoolFlag{
450
450
+
Name: "print-chat",
451
451
+
Usage: "print chat messages to stdout",
452
452
+
Value: false,
453
453
+
Destination: &cli.PrintChat,
454
454
+
Sources: urfavecli.EnvVars("SP_PRINT_CHAT"),
455
455
+
},
456
456
+
&urfavecli.StringFlag{
457
457
+
Name: "whip-test",
458
458
+
Usage: "run a WHIP self-test with the given parameters",
459
459
+
Destination: &cli.WHIPTest,
460
460
+
Sources: urfavecli.EnvVars("SP_WHIP_TEST"),
461
461
+
},
462
462
+
&urfavecli.StringFlag{
463
463
+
Name: "relay-host",
464
464
+
Usage: "websocket url for relay firehose",
465
465
+
Value: "wss://bsky.network",
466
466
+
Destination: &cli.RelayHost,
467
467
+
Sources: urfavecli.EnvVars("SP_RELAY_HOST"),
468
468
+
},
469
469
+
&urfavecli.StringFlag{
470
470
+
Name: "color",
471
471
+
Usage: "'true' to enable colorized logging, 'false' to disable",
472
472
+
Destination: &cli.Color,
473
473
+
Sources: urfavecli.EnvVars("SP_COLOR"),
474
474
+
},
475
475
+
&urfavecli.StringFlag{
476
476
+
Name: "broadcaster-host",
477
477
+
Usage: "public host for the broadcaster group that this node is a part of (excluding https:// e.g. stream.place)",
478
478
+
Destination: &cli.BroadcasterHost,
479
479
+
Sources: urfavecli.EnvVars("SP_BROADCASTER_HOST"),
480
480
+
},
481
481
+
&urfavecli.StringFlag{
482
482
+
Name: "public-host",
483
483
+
Usage: "deprecated, use broadcaster-host or server-host instead as appropriate",
484
484
+
Destination: &cli.XXDeprecatedPublicHost,
485
485
+
Sources: urfavecli.EnvVars("SP_PUBLIC_HOST"),
486
486
+
},
487
487
+
&urfavecli.StringFlag{
488
488
+
Name: "server-host",
489
489
+
Usage: "public host for this particular physical streamplace node. defaults to broadcaster-host and only must be set for multi-node broadcasters",
490
490
+
Destination: &cli.ServerHost,
491
491
+
Sources: urfavecli.EnvVars("SP_SERVER_HOST"),
492
492
+
},
493
493
+
&urfavecli.BoolFlag{
494
494
+
Name: "thumbnail",
495
495
+
Usage: "enable thumbnail generation",
496
496
+
Value: true,
497
497
+
Destination: &cli.Thumbnail,
498
498
+
Sources: urfavecli.EnvVars("SP_THUMBNAIL"),
499
499
+
},
500
500
+
&urfavecli.BoolFlag{
501
501
+
Name: "smear-audio",
502
502
+
Usage: "enable audio smearing to create 'perfect' segment timestamps",
503
503
+
Value: false,
504
504
+
Destination: &cli.SmearAudio,
505
505
+
Sources: urfavecli.EnvVars("SP_SMEAR_AUDIO"),
506
506
+
},
507
507
+
&urfavecli.StringFlag{
508
508
+
Name: "tracing-endpoint",
509
509
+
Usage: "gRPC endpoint to send traces to",
510
510
+
Destination: &cli.TracingEndpoint,
511
511
+
Sources: urfavecli.EnvVars("SP_TRACING_ENDPOINT"),
512
512
+
},
513
513
+
&urfavecli.IntFlag{
514
514
+
Name: "rate-limit-per-second",
515
515
+
Usage: "rate limit for requests per second per ip",
516
516
+
Value: 0,
517
517
+
Destination: &cli.RateLimitPerSecond,
518
518
+
Sources: urfavecli.EnvVars("SP_RATE_LIMIT_PER_SECOND"),
519
519
+
},
520
520
+
&urfavecli.IntFlag{
521
521
+
Name: "rate-limit-burst",
522
522
+
Usage: "rate limit burst for requests per ip",
523
523
+
Value: 0,
524
524
+
Destination: &cli.RateLimitBurst,
525
525
+
Sources: urfavecli.EnvVars("SP_RATE_LIMIT_BURST"),
526
526
+
},
527
527
+
&urfavecli.IntFlag{
528
528
+
Name: "rate-limit-websocket",
529
529
+
Usage: "number of concurrent websocket connections allowed per ip",
530
530
+
Value: 10,
531
531
+
Destination: &cli.RateLimitWebsocket,
532
532
+
Sources: urfavecli.EnvVars("SP_RATE_LIMIT_WEBSOCKET"),
533
533
+
},
534
534
+
&urfavecli.StringFlag{
535
535
+
Name: "rtmp-server-addon",
536
536
+
Usage: "address of external RTMP server to forward streams to",
537
537
+
Destination: &cli.RTMPServerAddon,
538
538
+
Sources: urfavecli.EnvVars("SP_RTMP_SERVER_ADDON"),
539
539
+
},
540
540
+
&urfavecli.StringFlag{
541
541
+
Name: "rtmps-addon-addr",
542
542
+
Usage: "address to listen for RTMPS on the addon server",
543
543
+
Value: ":1936",
544
544
+
Destination: &cli.RTMPSAddonAddr,
545
545
+
Sources: urfavecli.EnvVars("SP_RTMPS_ADDON_ADDR"),
546
546
+
},
547
547
+
&urfavecli.StringFlag{
548
548
+
Name: "rtmps-addr",
549
549
+
Usage: "address to listen for RTMPS connections (when --secure=true)",
550
550
+
Value: ":1935",
551
551
+
Destination: &cli.RTMPSAddr,
552
552
+
Sources: urfavecli.EnvVars("SP_RTMPS_ADDR"),
553
553
+
},
554
554
+
&urfavecli.StringFlag{
555
555
+
Name: "rtmp-addr",
556
556
+
Usage: "address to listen for RTMP connections (when --secure=false)",
557
557
+
Value: ":1935",
558
558
+
Destination: &cli.RTMPAddr,
559
559
+
Sources: urfavecli.EnvVars("SP_RTMP_ADDR"),
560
560
+
},
561
561
+
&urfavecli.StringFlag{
562
562
+
Name: "discord-webhooks",
563
563
+
Usage: `JSON array of Discord webhooks to send notifications to (default: "[]")`,
564
564
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
565
565
+
if s == "" {
566
566
+
return nil
567
567
+
}
568
568
+
return json.Unmarshal([]byte(s), &cli.DiscordWebhooks)
569
569
+
},
570
570
+
Sources: urfavecli.EnvVars("SP_DISCORD_WEBHOOKS"),
571
571
+
},
572
572
+
&urfavecli.BoolFlag{
573
573
+
Name: "new-webrtc-playback",
574
574
+
Usage: "enable new webrtc playback",
575
575
+
Value: true,
576
576
+
Destination: &cli.NewWebRTCPlayback,
577
577
+
Sources: urfavecli.EnvVars("SP_NEW_WEBRTC_PLAYBACK"),
578
578
+
},
579
579
+
&urfavecli.StringFlag{
580
580
+
Name: "apple-team-id",
581
581
+
Usage: "apple team id for deep linking",
582
582
+
Destination: &cli.AppleTeamID,
583
583
+
Sources: urfavecli.EnvVars("SP_APPLE_TEAM_ID"),
584
584
+
},
585
585
+
&urfavecli.StringFlag{
586
586
+
Name: "android-cert-fingerprint",
587
587
+
Usage: "android cert fingerprint for deep linking",
588
588
+
Destination: &cli.AndroidCertFingerprint,
589
589
+
Sources: urfavecli.EnvVars("SP_ANDROID_CERT_FINGERPRINT"),
590
590
+
},
591
591
+
&urfavecli.StringFlag{
592
592
+
Name: "labelers",
593
593
+
Usage: `did of labelers that this instance should subscribe to (default: "")`,
594
594
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
595
595
+
if s == "" {
596
596
+
return nil
597
597
+
}
598
598
+
cli.Labelers = strings.Split(s, ",")
599
599
+
return nil
600
600
+
},
601
601
+
Sources: urfavecli.EnvVars("SP_LABELERS"),
602
602
+
},
603
603
+
&urfavecli.StringFlag{
604
604
+
Name: "atproto-did",
605
605
+
Usage: "atproto did to respond to on /.well-known/atproto-did (default did:web:PUBLIC_HOST)",
606
606
+
Destination: &cli.AtprotoDID,
607
607
+
Sources: urfavecli.EnvVars("SP_ATPROTO_DID"),
608
608
+
},
609
609
+
&urfavecli.StringFlag{
610
610
+
Name: "content-filters",
611
611
+
Usage: `JSON content filtering rules (default: "{}")`,
612
612
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
613
613
+
if s == "" {
614
614
+
return nil
615
615
+
}
616
616
+
return json.Unmarshal([]byte(s), &cli.ContentFilters)
617
617
+
},
618
618
+
Sources: urfavecli.EnvVars("SP_CONTENT_FILTERS"),
619
619
+
},
620
620
+
&urfavecli.StringFlag{
621
621
+
Name: "default-recommended-streamers",
622
622
+
Usage: `comma-separated list of streamer DIDs to recommend by default when no other recommendations are available (default: "")`,
623
623
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
624
624
+
if s == "" {
625
625
+
return nil
626
626
+
}
627
627
+
cli.DefaultRecommendedStreamers = strings.Split(s, ",")
628
628
+
return nil
629
629
+
},
630
630
+
Sources: urfavecli.EnvVars("SP_DEFAULT_RECOMMENDED_STREAMERS"),
631
631
+
},
632
632
+
&urfavecli.BoolFlag{
633
633
+
Name: "livepeer-help",
634
634
+
Usage: "print help for livepeer flags and exit",
635
635
+
Value: false,
636
636
+
Destination: &cli.LivepeerHelp,
637
637
+
Sources: urfavecli.EnvVars("SP_LIVEPEER_HELP"),
638
638
+
},
639
639
+
&urfavecli.StringFlag{
640
640
+
Name: "plc-url",
641
641
+
Usage: "url of the plc directory",
642
642
+
Value: "https://plc.directory",
643
643
+
Destination: &cli.PLCURL,
644
644
+
Sources: urfavecli.EnvVars("SP_PLC_URL"),
645
645
+
},
646
646
+
&urfavecli.BoolFlag{
647
647
+
Name: "sql-logging",
648
648
+
Usage: "enable sql logging",
649
649
+
Value: false,
650
650
+
Destination: &cli.SQLLogging,
651
651
+
Sources: urfavecli.EnvVars("SP_SQL_LOGGING"),
652
652
+
},
653
653
+
&urfavecli.StringFlag{
654
654
+
Name: "sentry-dsn",
655
655
+
Usage: "sentry dsn for error reporting",
656
656
+
Destination: &cli.SentryDSN,
657
657
+
Sources: urfavecli.EnvVars("SP_SENTRY_DSN"),
658
658
+
},
659
659
+
&urfavecli.BoolFlag{
660
660
+
Name: "livepeer-debug",
661
661
+
Usage: "log livepeer segments to $SP_DATA_DIR/livepeer-debug",
662
662
+
Value: false,
663
663
+
Destination: &cli.LivepeerDebug,
664
664
+
Sources: urfavecli.EnvVars("SP_LIVEPEER_DEBUG"),
665
665
+
},
666
666
+
&urfavecli.StringFlag{
667
667
+
Name: "segment-debug-dir",
668
668
+
Usage: "directory to log segment validation to",
669
669
+
Destination: &cli.SegmentDebugDir,
670
670
+
Sources: urfavecli.EnvVars("SP_SEGMENT_DEBUG_DIR"),
671
671
+
},
672
672
+
&urfavecli.StringFlag{
673
673
+
Name: "tickets",
674
674
+
Usage: `tickets to join the swarm with (default: "")`,
675
675
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
676
676
+
if s == "" {
677
677
+
return nil
678
678
+
}
679
679
+
cli.Tickets = strings.Split(s, ",")
680
680
+
return nil
681
681
+
},
682
682
+
Sources: urfavecli.EnvVars("SP_TICKETS"),
683
683
+
},
684
684
+
&urfavecli.StringFlag{
685
685
+
Name: "iroh-topic",
686
686
+
Usage: "topic to use for the iroh swarm (must be 32 bytes in hex)",
687
687
+
Destination: &cli.IrohTopic,
688
688
+
Sources: urfavecli.EnvVars("SP_IROH_TOPIC"),
689
689
+
},
690
690
+
&urfavecli.BoolFlag{
691
691
+
Name: "disable-iroh-relay",
692
692
+
Usage: "disable the iroh relay",
693
693
+
Value: false,
694
694
+
Destination: &cli.DisableIrohRelay,
695
695
+
Sources: urfavecli.EnvVars("SP_DISABLE_IROH_RELAY"),
696
696
+
},
697
697
+
&urfavecli.StringFlag{
698
698
+
Name: "dev-account-creds",
699
699
+
Usage: `(FOR DEVELOPMENT ONLY) did=password pairs for logging into test accounts without oauth (default: "")`,
700
700
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
701
701
+
if s == "" {
702
702
+
return nil
703
703
+
}
704
704
+
cli.DevAccountCreds = map[string]string{}
705
705
+
pairs := strings.Split(s, ",")
706
706
+
for _, pair := range pairs {
707
707
+
parts := strings.Split(pair, "=")
708
708
+
if len(parts) != 2 {
709
709
+
return fmt.Errorf("invalid kv flag: %s", pair)
710
710
+
}
711
711
+
cli.DevAccountCreds[parts[0]] = parts[1]
712
712
+
}
713
713
+
return nil
714
714
+
},
715
715
+
Sources: urfavecli.EnvVars("SP_DEV_ACCOUNT_CREDS"),
716
716
+
},
717
717
+
&urfavecli.DurationFlag{
718
718
+
Name: "stream-session-timeout",
719
719
+
Usage: "how long to wait before considering a stream inactive on this node?",
720
720
+
Value: 60 * time.Second,
721
721
+
Destination: &cli.StreamSessionTimeout,
722
722
+
Sources: urfavecli.EnvVars("SP_STREAM_SESSION_TIMEOUT"),
723
723
+
},
724
724
+
&urfavecli.StringFlag{
725
725
+
Name: "replicators",
726
726
+
Usage: "comma-separated list of replication protocols to use (websocket, iroh)",
727
727
+
Value: ReplicatorWebsocket,
728
728
+
Sources: urfavecli.EnvVars("SP_REPLICATORS"),
729
729
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
730
730
+
if s != "" {
731
731
+
cli.Replicators = strings.Split(s, ",")
732
732
+
}
733
733
+
return nil
734
734
+
},
735
735
+
},
736
736
+
&urfavecli.StringFlag{
737
737
+
Name: "websocket-url",
738
738
+
Usage: "override the websocket (ws:// or wss://) url to use for replication (normally not necessary, used for testing)",
739
739
+
Destination: &cli.WebsocketURL,
740
740
+
Sources: urfavecli.EnvVars("SP_WEBSOCKET_URL"),
741
741
+
},
742
742
+
&urfavecli.BoolFlag{
743
743
+
Name: "behind-https-proxy",
744
744
+
Usage: "set to true if this node is behind an https proxy and we should report https URLs even though the node isn't serving HTTPS",
745
745
+
Value: false,
746
746
+
Destination: &cli.BehindHTTPSProxy,
747
747
+
Sources: urfavecli.EnvVars("SP_BEHIND_HTTPS_PROXY"),
748
748
+
},
749
749
+
&urfavecli.StringFlag{
750
750
+
Name: "admin-dids",
751
751
+
Usage: `comma-separated list of DIDs that are authorized to modify branding and other admin operations (default: "")`,
752
752
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
753
753
+
if s == "" {
754
754
+
return nil
755
755
+
}
756
756
+
cli.AdminDIDs = strings.Split(s, ",")
757
757
+
return nil
758
758
+
},
759
759
+
Sources: urfavecli.EnvVars("SP_ADMIN_DIDS"),
760
760
+
},
761
761
+
&urfavecli.StringFlag{
762
762
+
Name: "syndicate",
763
763
+
Usage: `list of DIDs that we should rebroadcast ('*' for everybody) (default: "")`,
764
764
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
765
765
+
if s == "" {
766
766
+
return nil
767
767
+
}
768
768
+
cli.Syndicate = strings.Split(s, ",")
769
769
+
return nil
770
770
+
},
771
771
+
Sources: urfavecli.EnvVars("SP_SYNDICATE"),
772
772
+
},
773
773
+
&urfavecli.BoolFlag{
774
774
+
Name: "player-telemetry",
775
775
+
Usage: "enable player telemetry",
776
776
+
Value: true,
777
777
+
Destination: &cli.PlayerTelemetry,
778
778
+
Sources: urfavecli.EnvVars("SP_PLAYER_TELEMETRY"),
779
779
+
},
780
780
+
&urfavecli.StringFlag{
781
781
+
Name: "local-db-url",
782
782
+
Usage: "URL of the local database to use for storing local data",
783
783
+
Value: "sqlite://$SP_DATA_DIR/localdb.sqlite",
784
784
+
Destination: &cli.LocalDBURL,
785
785
+
Sources: urfavecli.EnvVars("SP_LOCAL_DB_URL"),
786
786
+
},
787
787
+
&urfavecli.BoolFlag{
788
788
+
Name: "external-signing",
789
789
+
Usage: "DEPRECATED, does nothing.",
790
790
+
Value: true,
791
791
+
},
792
792
+
&urfavecli.BoolFlag{
793
793
+
Name: "insecure",
794
794
+
Usage: "DEPRECATED, does nothing.",
795
795
+
Value: false,
796
796
+
},
797
797
+
},
798
798
+
Before: func(ctx context.Context, cmd *urfavecli.Command) (context.Context, error) {
799
799
+
return ctx, cli.Validate(cmd)
800
800
+
},
801
801
+
}
802
802
+
803
803
+
// Add data dir flags
174
804
cli.dataDirFlags = append(cli.dataDirFlags, &cli.DBURL)
175
175
-
fs.StringVar(&cli.AdminAccount, "admin-account", "", "ethereum account that administrates this streamplace node")
176
176
-
fs.StringVar(&cli.FirebaseServiceAccount, "firebase-service-account", "", "Base64-encoded JSON string of a firebase service account key")
177
177
-
fs.StringVar(&cli.FirebaseServiceAccountFile, "firebase-service-account-file", "", "Path to a JSON file containing a firebase service account key")
178
178
-
fs.StringVar(&cli.GitLabURL, "gitlab-url", "https://git.stream.place/api/v4/projects/1", "gitlab url for generating download links")
179
179
-
cli.DataDirFlag(fs, &cli.EthKeystorePath, "eth-keystore-path", "keystore", "path to ethereum keystore")
180
180
-
fs.StringVar(&cli.EthAccountAddr, "eth-account-addr", "", "ethereum account address to use (if keystore contains more than one)")
181
181
-
fs.StringVar(&cli.EthPassword, "eth-password", "", "password for encrypting keystore")
182
182
-
fs.StringVar(&cli.TAURL, "ta-url", "http://timestamp.digicert.com", "timestamp authority server for signing")
183
183
-
fs.StringVar(&cli.PKCS11ModulePath, "pkcs11-module-path", "", "path to a PKCS11 module for HSM signing, for example /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so")
184
184
-
fs.StringVar(&cli.PKCS11Pin, "pkcs11-pin", "", "PIN for logging into PKCS11 token. if not provided, will be prompted interactively")
185
185
-
fs.StringVar(&cli.PKCS11TokenSlot, "pkcs11-token-slot", "", "slot number of PKCS11 token (only use one of slot, label, or serial)")
186
186
-
fs.StringVar(&cli.PKCS11TokenLabel, "pkcs11-token-label", "", "label of PKCS11 token (only use one of slot, label, or serial)")
187
187
-
fs.StringVar(&cli.PKCS11TokenSerial, "pkcs11-token-serial", "", "serial number of PKCS11 token (only use one of slot, label, or serial)")
188
188
-
fs.StringVar(&cli.PKCS11KeypairLabel, "pkcs11-keypair-label", "", "label of signing keypair on PKCS11 token")
189
189
-
fs.StringVar(&cli.PKCS11KeypairID, "pkcs11-keypair-id", "", "id of signing keypair on PKCS11 token")
190
190
-
fs.StringVar(&cli.AppBundleID, "app-bundle-id", "", "bundle id of an app that we facilitate oauth login for")
191
191
-
fs.StringVar(&cli.StreamerName, "streamer-name", "", "name of the person streaming from this streamplace node")
192
192
-
fs.StringVar(&cli.FrontendProxy, "dev-frontend-proxy", "", "(FOR DEVELOPMENT ONLY) proxy frontend requests to this address instead of using the bundled frontend")
193
193
-
fs.BoolVar(&cli.PublicOAuth, "dev-public-oauth", false, "(FOR DEVELOPMENT ONLY) enable public oauth login for http://127.0.0.1 development")
194
194
-
fs.StringVar(&cli.LivepeerGatewayURL, "livepeer-gateway-url", "", "URL of the Livepeer Gateway to use for transcoding")
195
195
-
fs.BoolVar(&cli.LivepeerGateway, "livepeer-gateway", false, "enable embedded Livepeer Gateway")
196
196
-
fs.BoolVar(&cli.WideOpen, "wide-open", false, "allow ALL streams to be uploaded to this node (not recommended for production)")
197
197
-
cli.StringSliceFlag(fs, &cli.AllowedStreams, "allowed-streams", []string{}, "if set, only allow these addresses or atproto DIDs to upload to this node")
198
198
-
cli.StringSliceFlag(fs, &cli.Peers, "peers", []string{}, "other streamplace nodes to replicate to")
199
199
-
cli.StringSliceFlag(fs, &cli.Redirects, "redirects", []string{}, "http 302s /path/one:/path/two,/path/three:/path/four")
200
200
-
cli.DebugFlag(fs, &cli.Debug, "debug", "", "modified log verbosity for specific functions or files in form func=ToHLS:3,file=gstreamer.go:4")
201
201
-
fs.BoolVar(&cli.TestStream, "test-stream", false, "run a built-in test stream on boot")
202
202
-
fs.BoolVar(&cli.NoFirehose, "no-firehose", false, "disable the bluesky firehose")
203
203
-
fs.BoolVar(&cli.PrintChat, "print-chat", false, "print chat messages to stdout")
204
204
-
fs.StringVar(&cli.WHIPTest, "whip-test", "", "run a WHIP self-test with the given parameters")
205
205
-
fs.StringVar(&cli.RelayHost, "relay-host", "wss://bsky.network", "websocket url for relay firehose")
206
206
-
fs.StringVar(&cli.Color, "color", "", "'true' to enable colorized logging, 'false' to disable")
207
207
-
fs.StringVar(&cli.BroadcasterHost, "broadcaster-host", "", "public host for the broadcaster group that this node is a part of (excluding https:// e.g. stream.place)")
208
208
-
fs.StringVar(&cli.XXDeprecatedPublicHost, "public-host", "", "deprecated, use broadcaster-host or server-host instead as appropriate")
209
209
-
fs.StringVar(&cli.ServerHost, "server-host", "", "public host for this particular physical streamplace node. defaults to broadcaster-host and only must be set for multi-node broadcasters")
210
210
-
fs.BoolVar(&cli.Thumbnail, "thumbnail", true, "enable thumbnail generation")
211
211
-
fs.BoolVar(&cli.SmearAudio, "smear-audio", false, "enable audio smearing to create 'perfect' segment timestamps")
212
212
-
213
213
-
fs.StringVar(&cli.TracingEndpoint, "tracing-endpoint", "", "gRPC endpoint to send traces to")
214
214
-
fs.IntVar(&cli.RateLimitPerSecond, "rate-limit-per-second", 0, "rate limit for requests per second per ip")
215
215
-
fs.IntVar(&cli.RateLimitBurst, "rate-limit-burst", 0, "rate limit burst for requests per ip")
216
216
-
fs.IntVar(&cli.RateLimitWebsocket, "rate-limit-websocket", 10, "number of concurrent websocket connections allowed per ip")
217
217
-
fs.StringVar(&cli.RTMPServerAddon, "rtmp-server-addon", "", "address of external RTMP server to forward streams to")
218
218
-
fs.StringVar(&cli.RTMPSAddonAddr, "rtmps-addon-addr", ":1936", "address to listen for RTMPS on the addon server")
219
219
-
fs.StringVar(&cli.RTMPSAddr, "rtmps-addr", ":1935", "address to listen for RTMPS connections (when --secure=true)")
220
220
-
fs.StringVar(&cli.RTMPAddr, "rtmp-addr", ":1935", "address to listen for RTMP connections (when --secure=false)")
221
221
-
cli.JSONFlag(fs, &cli.DiscordWebhooks, "discord-webhooks", "[]", "JSON array of Discord webhooks to send notifications to")
222
222
-
fs.BoolVar(&cli.NewWebRTCPlayback, "new-webrtc-playback", true, "enable new webrtc playback")
223
223
-
fs.StringVar(&cli.AppleTeamID, "apple-team-id", "", "apple team id for deep linking")
224
224
-
fs.StringVar(&cli.AndroidCertFingerprint, "android-cert-fingerprint", "", "android cert fingerprint for deep linking")
225
225
-
cli.StringSliceFlag(fs, &cli.Labelers, "labelers", []string{}, "did of labelers that this instance should subscribe to")
226
226
-
fs.StringVar(&cli.AtprotoDID, "atproto-did", "", "atproto did to respond to on /.well-known/atproto-did (default did:web:PUBLIC_HOST)")
227
227
-
cli.JSONFlag(fs, &cli.ContentFilters, "content-filters", "{}", "JSON content filtering rules")
228
228
-
cli.StringSliceFlag(fs, &cli.DefaultRecommendedStreamers, "default-recommended-streamers", []string{}, "comma-separated list of streamer DIDs to recommend by default when no other recommendations are available")
229
229
-
fs.BoolVar(&cli.LivepeerHelp, "livepeer-help", false, "print help for livepeer flags and exit")
230
230
-
fs.StringVar(&cli.PLCURL, "plc-url", "https://plc.directory", "url of the plc directory")
231
231
-
fs.BoolVar(&cli.SQLLogging, "sql-logging", false, "enable sql logging")
232
232
-
fs.StringVar(&cli.SentryDSN, "sentry-dsn", "", "sentry dsn for error reporting")
233
233
-
fs.BoolVar(&cli.LivepeerDebug, "livepeer-debug", false, "log livepeer segments to $SP_DATA_DIR/livepeer-debug")
234
234
-
fs.StringVar(&cli.SegmentDebugDir, "segment-debug-dir", "", "directory to log segment validation to")
235
235
-
cli.StringSliceFlag(fs, &cli.Tickets, "tickets", []string{}, "tickets to join the swarm with")
236
236
-
fs.StringVar(&cli.IrohTopic, "iroh-topic", "", "topic to use for the iroh swarm (must be 32 bytes in hex)")
237
237
-
fs.BoolVar(&cli.DisableIrohRelay, "disable-iroh-relay", false, "disable the iroh relay")
238
238
-
cli.KVSliceFlag(fs, &cli.DevAccountCreds, "dev-account-creds", "", "(FOR DEVELOPMENT ONLY) did=password pairs for logging into test accounts without oauth")
239
239
-
fs.DurationVar(&cli.StreamSessionTimeout, "stream-session-timeout", 60*time.Second, "how long to wait before considering a stream inactive on this node?")
240
240
-
cli.StringSliceFlag(fs, &cli.Replicators, "replicators", []string{ReplicatorWebsocket}, "list of replication protocols to use (http, iroh)")
241
241
-
fs.StringVar(&cli.WebsocketURL, "websocket-url", "", "override the websocket (ws:// or wss://) url to use for replication (normally not necessary, used for testing)")
242
242
-
fs.BoolVar(&cli.BehindHTTPSProxy, "behind-https-proxy", false, "set to true if this node is behind an https proxy and we should report https URLs even though the node isn't serving HTTPS")
243
243
-
cli.StringSliceFlag(fs, &cli.AdminDIDs, "admin-dids", []string{}, "comma-separated list of DIDs that are authorized to modify branding and other admin operations")
244
244
-
cli.StringSliceFlag(fs, &cli.Syndicate, "syndicate", []string{}, "list of DIDs that we should rebroadcast ('*' for everybody)")
245
245
-
fs.BoolVar(&cli.PlayerTelemetry, "player-telemetry", true, "enable player telemetry")
246
246
-
fs.StringVar(&cli.LocalDBURL, "local-db-url", "sqlite://$SP_DATA_DIR/localdb.sqlite", "URL of the local database to use for storing local data")
247
805
cli.dataDirFlags = append(cli.dataDirFlags, &cli.LocalDBURL)
248
248
-
249
249
-
fs.Bool("external-signing", true, "DEPRECATED, does nothing.")
250
250
-
fs.Bool("insecure", false, "DEPRECATED, does nothing.")
251
251
-
252
252
-
lpFlags := flag.NewFlagSet("livepeer", flag.ContinueOnError)
253
253
-
_ = starter.NewLivepeerConfig(lpFlags)
254
254
-
lpFlags.VisitAll(func(f *flag.Flag) {
255
255
-
adapted := LivepeerFlags.CamelToSnake[f.Name]
256
256
-
fs.Var(f.Value, fmt.Sprintf("livepeer.%s", adapted), f.Usage)
257
257
-
})
806
806
+
cli.dataDirFlags = append(cli.dataDirFlags, &cli.TLSCertPath)
807
807
+
cli.dataDirFlags = append(cli.dataDirFlags, &cli.TLSKeyPath)
808
808
+
cli.dataDirFlags = append(cli.dataDirFlags, &cli.EthKeystorePath)
258
809
259
810
if runtime.GOOS == "linux" {
260
260
-
fs.BoolVar(&cli.NoMist, "no-mist", true, "Disable MistServer")
261
261
-
fs.IntVar(&cli.MistAdminPort, "mist-admin-port", 14242, "MistServer admin port (internal use only)")
262
262
-
fs.IntVar(&cli.MistRTMPPort, "mist-rtmp-port", 11935, "MistServer RTMP port (internal use only)")
263
263
-
fs.IntVar(&cli.MistHTTPPort, "mist-http-port", 18080, "MistServer HTTP port (internal use only)")
811
811
+
cmd.Flags = append(cmd.Flags, &urfavecli.BoolFlag{
812
812
+
Name: "no-mist",
813
813
+
Usage: "Disable MistServer",
814
814
+
Value: true,
815
815
+
Destination: &cli.NoMist,
816
816
+
Sources: urfavecli.EnvVars("SP_NO_MIST"),
817
817
+
})
818
818
+
cmd.Flags = append(cmd.Flags, &urfavecli.IntFlag{
819
819
+
Name: "mist-admin-port",
820
820
+
Usage: "MistServer admin port (internal use only)",
821
821
+
Value: 14242,
822
822
+
Destination: &cli.MistAdminPort,
823
823
+
Sources: urfavecli.EnvVars("SP_MIST_ADMIN_PORT"),
824
824
+
})
825
825
+
cmd.Flags = append(cmd.Flags, &urfavecli.IntFlag{
826
826
+
Name: "mist-rtmp-port",
827
827
+
Usage: "MistServer RTMP port (internal use only)",
828
828
+
Value: 11935,
829
829
+
Destination: &cli.MistRTMPPort,
830
830
+
Sources: urfavecli.EnvVars("SP_MIST_RTMP_PORT"),
831
831
+
})
832
832
+
cmd.Flags = append(cmd.Flags, &urfavecli.IntFlag{
833
833
+
Name: "mist-http-port",
834
834
+
Usage: "MistServer HTTP port (internal use only)",
835
835
+
Value: 18080,
836
836
+
Destination: &cli.MistHTTPPort,
837
837
+
Sources: urfavecli.EnvVars("SP_MIST_HTTP_PORT"),
838
838
+
})
264
839
}
265
265
-
return fs
840
840
+
841
841
+
return cmd
266
842
}
267
843
268
844
var StreamplaceSchemePrefix = "streamplace://"
···
350
926
)
351
927
}
352
928
353
353
-
func (cli *CLI) Parse(fs *flag.FlagSet, args []string) error {
354
354
-
err := ff.Parse(
355
355
-
fs, args,
356
356
-
ff.WithEnvVarPrefix("SP"),
357
357
-
)
358
358
-
if err != nil {
359
359
-
return err
360
360
-
}
929
929
+
func (cli *CLI) Validate(cmd *urfavecli.Command) error {
361
930
if cli.DataDir == "" {
362
931
return fmt.Errorf("could not determine default data dir (no $HOME) and none provided, please set --data-dir")
363
932
}
···
366
935
}
367
936
if cli.LivepeerGateway {
368
937
log.MonkeypatchStderr()
369
369
-
gatewayPath := cli.DataFilePath([]string{"livepeer", "gateway"})
370
370
-
err = fs.Set("livepeer.rtmp-addr", "127.0.0.1:0")
371
371
-
if err != nil {
372
372
-
return err
373
373
-
}
374
374
-
err = fs.Set("livepeer.data-dir", gatewayPath)
375
375
-
if err != nil {
376
376
-
return err
377
377
-
}
378
378
-
err = fs.Set("livepeer.gateway", "true")
379
379
-
if err != nil {
380
380
-
return err
381
381
-
}
382
382
-
httpAddrFlag := fs.Lookup("livepeer.http-addr")
383
383
-
if httpAddrFlag == nil {
384
384
-
return fmt.Errorf("livepeer.http-addr not found")
385
385
-
}
386
386
-
httpAddr := httpAddrFlag.Value.String()
387
387
-
if httpAddr == "" {
388
388
-
httpAddr = "127.0.0.1:8935"
389
389
-
err = fs.Set("livepeer.http-addr", httpAddr)
390
390
-
if err != nil {
391
391
-
return err
392
392
-
}
393
393
-
}
394
394
-
cli.LivepeerGatewayURL = fmt.Sprintf("http://%s", httpAddr)
938
938
+
// Livepeer gateway configuration will be handled in the caller
939
939
+
cli.LivepeerGatewayURL = "http://127.0.0.1:8935"
395
940
}
396
941
for _, dest := range cli.dataDirFlags {
397
942
*dest = strings.Replace(*dest, SPDataDir, cli.DataDir, 1)
···
420
965
return err
421
966
}
422
967
cli.FirebaseServiceAccount = string(bs)
968
968
+
}
969
969
+
// Set default replicator if none specified
970
970
+
if len(cli.Replicators) == 0 {
971
971
+
cli.Replicators = []string{ReplicatorWebsocket}
423
972
}
424
973
return nil
425
974
}
···
529
1078
return nil
530
1079
}
531
1080
532
532
-
func (cli *CLI) DataDirFlag(fs *flag.FlagSet, dest *string, name, defaultValue, usage string) {
533
533
-
cli.dataDirFlags = append(cli.dataDirFlags, dest)
534
534
-
*dest = filepath.Join(SPDataDir, defaultValue)
535
535
-
usage = fmt.Sprintf(`%s (default: "%s")`, usage, *dest)
536
536
-
fs.Func(name, usage, func(s string) error {
537
537
-
*dest = s
538
538
-
return nil
539
539
-
})
540
540
-
}
541
541
-
542
1081
func (cli *CLI) HasMist() bool {
543
1082
return runtime.GOOS == "linux"
544
1083
}
545
1084
546
1085
// type for comma-separated ethereum addresses
547
547
-
func (cli *CLI) AddressSliceFlag(fs *flag.FlagSet, dest *[]aqpub.Pub, name, defaultValue, usage string) {
1086
1086
+
func (cli *CLI) AddressSliceFlag(name, defaultValue, usage string, dest *[]aqpub.Pub) urfavecli.Flag {
548
1087
*dest = []aqpub.Pub{}
549
549
-
usage = fmt.Sprintf(`%s (default: "%s")`, usage, *dest)
550
550
-
fs.Func(name, usage, func(s string) error {
551
551
-
if s == "" {
552
552
-
return nil
553
553
-
}
554
554
-
strs := strings.Split(s, ",")
555
555
-
for _, str := range strs {
556
556
-
pub, err := aqpub.FromHexString(str)
557
557
-
if err != nil {
558
558
-
return err
559
559
-
}
560
560
-
*dest = append(*dest, pub)
561
561
-
}
562
562
-
return nil
563
563
-
})
564
564
-
}
565
565
-
566
566
-
func (cli *CLI) StringSliceFlag(fs *flag.FlagSet, dest *[]string, name string, defaultValue []string, usage string) {
567
567
-
*dest = defaultValue
568
568
-
usage = fmt.Sprintf(`%s (default: "%s")`, usage, *dest)
569
569
-
fs.Func(name, usage, func(s string) error {
570
570
-
if s == "" {
571
571
-
return nil
572
572
-
}
573
573
-
strs := strings.Split(s, ",")
574
574
-
*dest = append([]string{}, strs...)
575
575
-
return nil
576
576
-
})
577
577
-
}
578
578
-
579
579
-
func (cli *CLI) KVSliceFlag(fs *flag.FlagSet, dest *map[string]string, name, defaultValue, usage string) {
580
580
-
*dest = map[string]string{}
581
581
-
usage = fmt.Sprintf(`%s (default: "%s")`, usage, *dest)
582
582
-
fs.Func(name, usage, func(s string) error {
583
583
-
if s == "" {
584
584
-
return nil
585
585
-
}
586
586
-
pairs := strings.Split(s, ",")
587
587
-
for _, pair := range pairs {
588
588
-
parts := strings.Split(pair, "=")
589
589
-
if len(parts) != 2 {
590
590
-
return fmt.Errorf("invalid kv flag: %s", pair)
591
591
-
}
592
592
-
(*dest)[parts[0]] = parts[1]
593
593
-
}
594
594
-
return nil
595
595
-
})
596
596
-
}
597
597
-
598
598
-
func (cli *CLI) JSONFlag(fs *flag.FlagSet, dest any, name, defaultValue, usage string) {
599
1088
usage = fmt.Sprintf(`%s (default: "%s")`, usage, defaultValue)
600
600
-
fs.Func(name, usage, func(s string) error {
601
601
-
if s == "" {
602
602
-
return nil
603
603
-
}
604
604
-
return json.Unmarshal([]byte(s), dest)
605
605
-
})
606
606
-
}
607
1089
608
608
-
// debug flag for turning func=ToHLS:3,file=gstreamer.go:4 into {"func": {"ToHLS": 3}, "file": {"gstreamer.go": 4}}
609
609
-
func (cli *CLI) DebugFlag(fs *flag.FlagSet, dest *map[string]map[string]int, name, defaultValue, usage string) {
610
610
-
*dest = map[string]map[string]int{}
611
611
-
fs.Func(name, usage, func(s string) error {
612
612
-
if s == "" {
613
613
-
return nil
614
614
-
}
615
615
-
pairs := strings.Split(s, ",")
616
616
-
for _, pair := range pairs {
617
617
-
scoreSplit := strings.Split(pair, ":")
618
618
-
if len(scoreSplit) != 2 {
619
619
-
return fmt.Errorf("invalid debug flag: %s", pair)
620
620
-
}
621
621
-
score, err := strconv.Atoi(scoreSplit[1])
622
622
-
if err != nil {
623
623
-
return fmt.Errorf("invalid debug flag: %s", pair)
624
624
-
}
625
625
-
selectorSplit := strings.Split(scoreSplit[0], "=")
626
626
-
if len(selectorSplit) != 2 {
627
627
-
return fmt.Errorf("invalid debug flag: %s", pair)
1090
1090
+
return &urfavecli.StringFlag{
1091
1091
+
Name: name,
1092
1092
+
Usage: usage,
1093
1093
+
Action: func(ctx context.Context, cmd *urfavecli.Command, s string) error {
1094
1094
+
if s == "" {
1095
1095
+
return nil
628
1096
}
629
629
-
_, ok := (*dest)[selectorSplit[0]]
630
630
-
if !ok {
631
631
-
(*dest)[selectorSplit[0]] = map[string]int{}
1097
1097
+
strs := strings.Split(s, ",")
1098
1098
+
for _, str := range strs {
1099
1099
+
pub, err := aqpub.FromHexString(str)
1100
1100
+
if err != nil {
1101
1101
+
return err
1102
1102
+
}
1103
1103
+
*dest = append(*dest, pub)
632
1104
}
633
633
-
(*dest)[selectorSplit[0]][selectorSplit[1]] = score
634
634
-
}
635
635
-
636
636
-
return nil
637
637
-
})
1105
1105
+
return nil
1106
1106
+
},
1107
1107
+
Sources: urfavecli.EnvVars(fmt.Sprintf("SP_%s", strings.ToUpper(strings.ReplaceAll(name, "-", "_")))),
1108
1108
+
}
638
1109
}
639
1110
640
1111
func (cli *CLI) StreamIsAllowed(did string) error {
···
648
1119
if openServer && !isDIDKey {
649
1120
return nil
650
1121
}
651
651
-
for _, a := range cli.AllowedStreams {
652
652
-
if a == did {
653
653
-
return nil
654
654
-
}
1122
1122
+
if slices.Contains(cli.AllowedStreams, did) {
1123
1123
+
return nil
655
1124
}
656
1125
return fmt.Errorf("user is not allowed to stream")
657
1126
}
+5
-3
pkg/media/segment_roundtrip_test.go
···
87
87
require.NoError(t, err)
88
88
89
89
signedSplitSegDir := makeTestSubdir(t, tempDir, "signed-split-segments")
90
90
-
cli := &config.CLI{}
91
91
-
fs := cli.NewFlagSet("rtcrec-test")
92
92
-
err = cli.Parse(fs, []string{})
90
90
+
cli := &config.CLI{
91
91
+
DataDir: tempDir, // Set data dir for test
92
92
+
}
93
93
+
cmd := cli.NewCommand("rtcrec-test")
94
94
+
err = cli.Validate(cmd)
93
95
require.NoError(t, err)
94
96
err = SplitSegments(context.Background(), cli, rws, func(fname string) ReadWriteSeekCloser {
95
97
fd, err := os.Create(filepath.Join(signedSplitSegDir, fname))