From 0e534c06fe0f914eb541b75e2a702f26d79e33da Mon Sep 17 00:00:00 2001 From: Yonah Date: Thu, 19 Mar 2026 17:49:31 +0900 Subject: [PATCH] streamdata: optionally rename from alternate This is useful for migration, or external downloads. Signed-off-by: Yonah --- streamdata.go | 24 ++++++++++++++++++++++-- streamdata_test.go | 24 +++++++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/streamdata.go b/streamdata.go index c63eed1..82b98a6 100644 --- a/streamdata.go +++ b/streamdata.go @@ -143,6 +143,14 @@ func (c *ChannelMismatchError) Error() string { strconv.FormatUint(c.Want, 10) } +// RenameFrom is a pathname returned by a function passed to [Channel.Add] to +// rename from this pathname instead of the managed transaction file. +type RenameFrom string + +func (pathname RenameFrom) Error() string { + return "requesting rename from " + strconv.Quote(string(pathname)) +} + // Add adds a [VOD] and its corresponding asset to the on-disk representation. func (c *Channel) Add(ident *Ident, f func(v *VOD, w io.Writer) error) error { if ident == nil || f == nil { @@ -167,8 +175,20 @@ func (c *Channel) Add(ident *Ident, f func(v *VOD, w io.Writer) error) error { err = closeErr } if err != nil { - _ = c.root.Remove(channelPathPending) - return err + var rf RenameFrom + if !errors.As(err, &rf) { + _ = c.root.Remove(channelPathPending) + return err + } + if err = c.root.Remove(channelPathPending); err != nil { + return err + } + if err = os.Rename(string(rf), path.Join( + c.root.Name(), + channelPathPending, + )); err != nil { + return err + } } if err = c.root.Chmod(channelPathPending, 0444); err != nil { diff --git a/streamdata_test.go b/streamdata_test.go index 1b9dd9e..18e467c 100644 --- a/streamdata_test.go +++ b/streamdata_test.go @@ -194,7 +194,7 @@ func TestChannelBadMetadata(t *testing.T) { }) } -func TestChannelAdd(t *testing.T) { +func TestChannel(t *testing.T) { t.Parallel() d := t.TempDir() @@ -334,6 +334,28 @@ func TestChannelAdd(t *testing.T) { t.Errorf("Perm: %#o", fi.Mode().Perm()) } + rf := streamdata.RenameFrom(path.Join(d, "alternate")) + if err := os.WriteFile(string(rf), []byte{0}, 0); err != nil { + t.Fatal(err) + } + if err := c.Add(&streamdata.Ident{Channel: 0xcafe}, func(*streamdata.VOD, io.Writer) error { + return rf + }); err != nil { + t.Fatalf("Add: error = %v", err) + } + if _, err := os.Stat(string(rf)); !errors.Is(err, os.ErrNotExist) { + t.Fatalf("Stat: error = %v", err) + } + if data, err := os.ReadFile(path.Join( + d, + "vod", + (&streamdata.Ident{Channel: 0xcafe}).String()+".mp4", + )); err != nil { + t.Fatal(err) + } else if string(data) != "\x00" { + t.Errorf("(rf) Add: %#v", data) + } + if err := os.Chmod(path.Join(d, "vod", wantIdent), 0); err != nil { t.Fatal(err) }