2024-02-24 00:22:19 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2024-03-27 14:20:45 +00:00
|
|
|
"io"
|
2024-02-24 00:22:19 +00:00
|
|
|
"os"
|
2024-03-27 14:20:45 +00:00
|
|
|
"sync"
|
2024-02-24 00:22:19 +00:00
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
"git.luzifer.io/luzifer/publish-vod/pkg/config"
|
|
|
|
"git.luzifer.io/luzifer/publish-vod/pkg/uploader"
|
2024-02-24 00:22:19 +00:00
|
|
|
"github.com/cheggaaa/pb/v3"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
"github.com/Luzifer/go_helpers/v2/str"
|
2024-02-24 00:22:19 +00:00
|
|
|
"github.com/Luzifer/rconfig/v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
cfg = struct {
|
2024-04-01 11:02:07 +00:00
|
|
|
Config string `flag:"config,c" default:"config.yaml" description:"Configuration for the uploaders"`
|
|
|
|
Limit []string `flag:"limit,l" description:"Limit to certain uploader IDs"`
|
|
|
|
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
|
|
|
|
ValidateOnly bool `flag:"validate-only" default:"false" description:"Only execute validation and prepare functions, do not upload"`
|
|
|
|
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
|
2024-02-24 00:22:19 +00:00
|
|
|
}{}
|
|
|
|
|
|
|
|
version = "dev"
|
|
|
|
)
|
|
|
|
|
|
|
|
func initApp() error {
|
|
|
|
rconfig.AutoEnv(true)
|
|
|
|
if err := rconfig.ParseAndValidate(&cfg); err != nil {
|
|
|
|
return errors.Wrap(err, "parsing cli options")
|
|
|
|
}
|
|
|
|
|
2024-04-01 11:08:23 +00:00
|
|
|
if len(cfg.Limit) == 1 && cfg.Limit[0] == "" || len(cfg.Limit) == 0 {
|
2024-04-01 11:02:07 +00:00
|
|
|
cfg.Limit = nil
|
|
|
|
}
|
|
|
|
|
2024-02-24 00:22:19 +00:00
|
|
|
l, err := logrus.ParseLevel(cfg.LogLevel)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "parsing log-level")
|
|
|
|
}
|
|
|
|
logrus.SetLevel(l)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
var err error
|
|
|
|
if err = initApp(); err != nil {
|
|
|
|
logrus.WithError(err).Fatal("initializing app")
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.VersionAndExit {
|
2024-03-27 14:20:45 +00:00
|
|
|
logrus.WithField("version", version).Info("publish-vod")
|
2024-02-24 00:22:19 +00:00
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
configFile, err := config.Load(cfg.Config)
|
|
|
|
if err != nil {
|
|
|
|
logrus.WithError(err).Fatal("loading config")
|
2024-02-24 00:22:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2024-03-27 17:26:13 +00:00
|
|
|
ctx = context.Background()
|
2024-04-01 11:02:07 +00:00
|
|
|
wg = new(sync.WaitGroup)
|
2024-02-24 00:22:19 +00:00
|
|
|
)
|
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
var (
|
|
|
|
barPool = pb.NewPool()
|
|
|
|
longestPrefix int
|
|
|
|
)
|
2024-02-24 00:22:19 +00:00
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
if longestPrefix, err = preflight(ctx, configFile); err != nil {
|
|
|
|
logrus.WithError(err).Fatal("executing preflight checks")
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.ValidateOnly {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(rconfig.Args()) < 2 { //nolint:gomnd
|
|
|
|
logrus.Fatal("Usage: publish-vod <filename>")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = startUploads(ctx, rconfig.Args()[1], configFile, wg, barPool, longestPrefix); err != nil {
|
|
|
|
logrus.WithError(err).Fatal("starting uploads")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = barPool.Start(); err != nil {
|
|
|
|
logrus.WithError(err).Fatal("starting progressbars")
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
if err = barPool.Stop(); err != nil {
|
|
|
|
logrus.WithError(err).Fatal("stopping progressbars")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func preflight(ctx context.Context, configFile config.File) (longestPrefix int, err error) {
|
|
|
|
for id, c := range configFile.Uploaders {
|
|
|
|
if cfg.Limit != nil && !str.StringInSlice(id, cfg.Limit) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
logrus.WithField("id", id).Info("validating config...")
|
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
u := uploaderByName(c.Type)
|
|
|
|
if u == nil {
|
2024-04-01 11:02:07 +00:00
|
|
|
return longestPrefix, fmt.Errorf("unknown uploader %q", c.Type)
|
2024-03-27 14:20:45 +00:00
|
|
|
}
|
2024-03-27 17:26:13 +00:00
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
if err = u.ValidateConfig(c.Settings); err != nil {
|
2024-04-01 11:02:07 +00:00
|
|
|
return longestPrefix, fmt.Errorf("validating config entry %q: %w", c.Name, err)
|
2024-03-27 14:20:45 +00:00
|
|
|
}
|
2024-02-24 00:22:19 +00:00
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
if err = u.Prepare(ctx, uploader.Opts{
|
2024-03-27 17:26:13 +00:00
|
|
|
Name: c.Name,
|
|
|
|
Config: c.Settings,
|
|
|
|
}); err != nil {
|
2024-04-01 11:02:07 +00:00
|
|
|
return longestPrefix, fmt.Errorf("preparing uploader %q: %w", c.Name, err)
|
2024-03-27 17:26:13 +00:00
|
|
|
}
|
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
if l := len(c.Name); l > longestPrefix {
|
|
|
|
longestPrefix = l
|
|
|
|
}
|
2024-02-24 00:22:19 +00:00
|
|
|
}
|
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
return longestPrefix, nil
|
|
|
|
}
|
2024-03-27 17:26:13 +00:00
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
func startUploads(
|
|
|
|
ctx context.Context,
|
|
|
|
fileName string,
|
|
|
|
configFile config.File,
|
|
|
|
wg *sync.WaitGroup,
|
|
|
|
barPool *pb.Pool,
|
|
|
|
longestPrefix int,
|
|
|
|
) (err error) {
|
|
|
|
f, err := os.Open(fileName) //#nosec G304 // Intended to open and upload arbitrary file
|
2024-03-27 17:26:13 +00:00
|
|
|
if err != nil {
|
2024-04-01 11:02:07 +00:00
|
|
|
return fmt.Errorf("opening VoD: %w", err)
|
2024-03-27 17:26:13 +00:00
|
|
|
}
|
|
|
|
defer f.Close() //nolint:errcheck // File is closed by process exit
|
|
|
|
|
|
|
|
stat, err := f.Stat()
|
|
|
|
if err != nil {
|
2024-04-01 11:02:07 +00:00
|
|
|
return fmt.Errorf("getting VoD stat: %w", err)
|
2024-03-27 17:26:13 +00:00
|
|
|
}
|
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
for id := range configFile.Uploaders {
|
|
|
|
if cfg.Limit != nil && !str.StringInSlice(id, cfg.Limit) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
logrus.WithField("id", id).Info("starting upload...")
|
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
wg.Add(1)
|
2024-02-24 00:22:19 +00:00
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
bar := pb.New64(stat.Size())
|
|
|
|
bar.SetTemplate(pb.Full)
|
|
|
|
barPool.Add(bar)
|
2024-02-24 00:22:19 +00:00
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
go func(c config.UploaderConfig, bar *pb.ProgressBar) {
|
|
|
|
defer wg.Done()
|
2024-02-24 00:22:19 +00:00
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
bar.Set("prefix", fmt.Sprintf(fmt.Sprintf("%%-%ds", longestPrefix), c.Name))
|
2024-02-24 00:22:19 +00:00
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
if err = uploaderByName(c.Type).UploadFile(ctx, uploader.Opts{
|
2024-03-27 14:20:45 +00:00
|
|
|
Name: c.Name,
|
|
|
|
Config: c.Settings,
|
2024-02-24 00:22:19 +00:00
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
Filename: fileName,
|
|
|
|
Content: io.NewSectionReader(f, 0, stat.Size()),
|
|
|
|
Size: stat.Size(),
|
2024-02-24 00:22:19 +00:00
|
|
|
|
2024-03-27 14:20:45 +00:00
|
|
|
ProgressBar: bar,
|
|
|
|
FinalMessage: func(format string, opts ...any) {
|
|
|
|
bar.SetTemplate(pb.ProgressBarTemplate(fmt.Sprintf(`{{ string . "prefix" }} %s`, fmt.Sprintf(format, opts...))))
|
|
|
|
},
|
|
|
|
}); err != nil {
|
|
|
|
bar.SetTemplate(pb.ProgressBarTemplate(fmt.Sprintf(`{{ string . "prefix" }} ERR: %s`, err)))
|
|
|
|
}
|
2024-04-01 11:02:07 +00:00
|
|
|
}(configFile.Uploaders[id], bar)
|
2024-02-24 00:22:19 +00:00
|
|
|
}
|
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
return nil
|
2024-02-24 00:22:19 +00:00
|
|
|
}
|