diff --git a/streamdata.go b/streamdata.go index 562a08d..bda67d2 100644 --- a/streamdata.go +++ b/streamdata.go @@ -6,11 +6,15 @@ import ( "encoding/json" "errors" "io" + "io/fs" + "iter" "os" "path" "strconv" + "strings" "syscall" "time" + "unsafe" ) // Channel represents a Twitch channel. @@ -198,6 +202,38 @@ func (c *Channel) Add(ident *Ident, f func(v *VOD, w io.Writer) error) error { return nil } +// All returns an iterator over all known [Ident] in the on-disk representation. +// Iteration stops when encountering the first non-nil error, and its value is +// saved to the value pointed to by errP. +func (c *Channel) All(errP *error) iter.Seq[*Ident] { + return func(yield func(*Ident) bool) { + dents, err := c.root.FS().(fs.ReadDirFS).ReadDir(channelPathVOD) + if err != nil { + *errP = err + return + } + + var ident Ident + for _, dent := range dents { + name := dent.Name() + if strings.HasSuffix(name, ChannelVODSuffix) { + continue + } + + if err = ident.UnmarshalText( + unsafe.Slice(unsafe.StringData(name), len(name)), + ); err != nil { + *errP = err + return + } + + if !yield(&ident) { + return + } + } + } +} + // Load loads the metadata of a [VOD] by [Ident] and returns its address. func (c *Channel) Load(ident *Ident) (*VOD, error) { var v VOD diff --git a/streamdata_test.go b/streamdata_test.go index e158648..1b9dd9e 100644 --- a/streamdata_test.go +++ b/streamdata_test.go @@ -7,6 +7,7 @@ import ( "os" "path" "reflect" + "slices" "syscall" "testing" "time" @@ -301,6 +302,15 @@ func TestChannelAdd(t *testing.T) { t.Errorf("Add: %#v, want %#v", got, wantVOD) } + var iterErr error + idents := slices.Collect(c.All(&iterErr)) + if iterErr != nil { + t.Fatalf("All: error = %#v", iterErr) + } + if len(idents) != 1 || idents[0].String() != wantIdent { + t.Errorf("All: %#v", idents) + } + if gotVOD, err := c.Load(&ident); err != nil { t.Fatalf("Load: error = %v", err) } else if *gotVOD != wantVOD {