From 7ff2b4feb1507fe51c64456db625e0a00a0703ec Mon Sep 17 00:00:00 2001 From: Yonah Date: Thu, 19 Mar 2026 00:37:46 +0900 Subject: [PATCH] cmd/streamdata: vod creation tool This prompts the user for metadata and downloads the asset. Signed-off-by: Yonah --- cmd/streamdata/main.go | 139 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 cmd/streamdata/main.go diff --git a/cmd/streamdata/main.go b/cmd/streamdata/main.go new file mode 100644 index 0000000..5ae23d4 --- /dev/null +++ b/cmd/streamdata/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "bufio" + "context" + "errors" + "io" + "log" + "net/http" + "os" + "os/signal" + "path" + "strings" + "syscall" + "time" + "unsafe" + + "git.gensokyo.uk/yonah/streamdata" + "hakurei.app/command" +) + +const programName = "streamdata" + +func main() { + log.SetFlags(0) + log.SetPrefix(programName + ": ") + + var channel *streamdata.Channel + ctx, stop := signal.NotifyContext(context.Background(), + syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) + defer stop() + + var ( + flagBase string + ) + c := command.New(os.Stderr, log.Printf, programName, func([]string) (err error) { + if flagBase != "" { + channel, err = streamdata.Open(flagBase) + } + return + }).Flag( + &flagBase, + "d", command.StringFlag(""), + "Channel directory to open", + ) + + c.NewCommand( + "add", + "Add a VOD to the channel", + func(args []string) error { + if channel == nil { + return errors.New("add requires a channel selected") + } + + if len(args) != 1 { + return errors.New("add requires 1 argument") + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, args[0], nil) + if err != nil { + return err + } + + name := path.Base(req.URL.Path) + if !strings.HasSuffix(name, streamdata.ChannelVODSuffix) { + return errors.New("file name missing expected suffix") + } + name = name[:len(name)-len(streamdata.ChannelVODSuffix)] + + var ident streamdata.Ident + if err = ident.UnmarshalText( + unsafe.Slice(unsafe.StringData(name), len(name)), + ); err != nil { + return err + } + + return channel.Add(&ident, func( + v *streamdata.VOD, + w io.Writer, + ) (err error) { + defer toErr(&err) + isattyStd() + br := bufio.NewReader(os.Stdin) + + v.Title = require(promptTrimNoEmpty(br, "Title: ")) + v.Category = require(prompt(br, "Category: ")) + + now := time.Now() + year := require(promptUintFallbackBounds( + br, "Year: ", + 1970, uint64(now.Year()), + uint64(now.Year()), + )) + + ubMonth := time.December + if year == uint64(now.Year()) { + ubMonth = now.Month() + } + month := require(promptUintFallbackBounds( + br, "Month: ", + uint64(time.January), uint64(ubMonth), + uint64(now.Month()), + )) + day := require(promptUintFallbackBounds( + br, "Day:", + 1, 31, + uint64(now.Day()), + )) + v.Date = time.Date( + int(year), time.Month(month), int(day), + 0, 0, 0, 0, + time.UTC, + ) + + var resp *http.Response + if resp, err = http.DefaultClient.Do(req); err != nil { + return + } else { + _, err = copyProgress(w, resp.Body, resp.ContentLength) + if closeErr := resp.Body.Close(); err == nil { + err = closeErr + } + } + return + }) + }, + ) + + c.MustParse(os.Args[1:], func(err error) { + if channel != nil { + if closeErr := channel.Close(); closeErr != nil { + log.Println(closeErr) + } + } + if err != nil { + log.Fatalln(err) + } + }) +}