From 140f2a3b0f6caa4faa6fe580b2f77e81d0c60f10 Mon Sep 17 00:00:00 2001 From: Yonah Date: Fri, 20 Mar 2026 02:12:30 +0900 Subject: [PATCH] streamdata: edit metadata This edits metadata in a robust way. Signed-off-by: Yonah --- streamdata.go | 40 ++++++++++++++++++++++++++++++++++++++++ streamdata_test.go | 22 ++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/streamdata.go b/streamdata.go index 3157887..b38971f 100644 --- a/streamdata.go +++ b/streamdata.go @@ -224,6 +224,46 @@ func (c *Channel) Add(ident *Ident, f func(v *VOD, w io.Writer) error) error { return nil } +// Edit loads a [VOD] by its [Ident], and writes the modified [VOD] back to the +// on-disk representation. +func (c *Channel) Edit(ident *Ident, f func(v *VOD) error) error { + v, err := c.Load(ident) + if err != nil { + return err + } + + err = f(v) + if err != nil { + return err + } + + pathname := path.Join( + channelPathVOD, + "."+ident.String(), + ) + + var w *os.File + if w, err = c.root.OpenFile( + pathname, + os.O_WRONLY|os.O_CREATE|os.O_EXCL, + 0444, + ); err != nil { + return err + } else if err = json.NewEncoder(w).Encode(v); err != nil { + _ = w.Close() + _ = c.root.Remove(pathname) + return err + } else if err = w.Close(); err != nil { + _ = c.root.Remove(pathname) + return err + } else { + return c.root.Rename(pathname, path.Join( + channelPathVOD, + ident.String(), + )) + } +} + // Path returns a pathname by [Ident]. func (c *Channel) Path(ident *Ident) string { return path.Join( diff --git a/streamdata_test.go b/streamdata_test.go index 18e467c..634ee2e 100644 --- a/streamdata_test.go +++ b/streamdata_test.go @@ -356,6 +356,28 @@ func TestChannel(t *testing.T) { t.Errorf("(rf) Add: %#v", data) } + wantVODEdit := streamdata.VOD{ + Title: "edit\x00", + Date: time.Unix(0xdeadbeef, 0).UTC(), + Category: "\t\x00", + Mirror: "https://satori.hifuu.internal/edit.mp4\x00", + } + if err := c.Edit(&streamdata.Ident{Channel: 0xcafe}, func(v *streamdata.VOD) error { + if *v != (streamdata.VOD{}) { + t.Errorf("Edit: v = %#v", v) + } + + *v = wantVODEdit + return nil + }); err != nil { + t.Fatalf("Edit: error = %v", err) + } + if gotVODEdit, err := c.Load(&streamdata.Ident{Channel: 0xcafe}); err != nil { + t.Fatalf("(edit) Load: error = %v", err) + } else if *gotVODEdit != wantVODEdit { + t.Errorf("Edit: %#v, want %#v", *gotVODEdit, wantVODEdit) + } + if err := os.Chmod(path.Join(d, "vod", wantIdent), 0); err != nil { t.Fatal(err) }