add: add main CLI entrypoint

This commit is contained in:
Esa Kataja
2026-04-04 18:07:38 +03:00
parent 0f11c62c4d
commit b54cf700aa
+174
View File
@@ -0,0 +1,174 @@
package main
import (
"crypto/rand"
"encoding/hex"
"flag"
"fmt"
"os"
"os/signal"
"path/filepath"
"syscall"
"videnc-vibe/internal/config"
"videnc-vibe/internal/encoder"
"videnc-vibe/internal/logger"
"videnc-vibe/internal/metadata"
"videnc-vibe/internal/mover"
"videnc-vibe/internal/watcher"
"videnc-vibe/pkg/types"
)
var (
deleteOrigin bool
configPath string
)
func init() {
flag.BoolVar(&deleteOrigin, "d", false, "Delete original after successful encode")
flag.StringVar(&configPath, "c", "", "Config file path (default: ~/.config/videnc-vibe/config.yaml)")
}
func main() {
flag.Parse()
cfg, err := config.Load(configPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load config: %v\n", err)
os.Exit(1)
}
if err := config.EnsureDirs(cfg); err != nil {
fmt.Fprintf(os.Stderr, "Failed to create directories: %v\n", err)
os.Exit(1)
}
log, err := logger.New(".")
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to initialize logger: %v\n", err)
os.Exit(1)
}
defer log.Close()
enc := encoder.New()
if err := enc.CheckDeps(); err != nil {
log.Error("Dependency check failed", err.Error())
fmt.Fprintf(os.Stderr, "Dependency check failed: %v\n", err)
os.Exit(1)
}
metaClient := metadata.NewClient(cfg.OMDBAPIKey)
done := make(chan struct{})
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
close(done)
}()
w := watcher.New(cfg.Paths.Input, 15)
w.Start(func(inputPath string) error {
return processFile(inputPath, cfg, enc, metaClient, log)
}, done)
log.Info("videnc-vibe started")
}
func processFile(inputPath string, cfg *types.Config, enc *encoder.Encoder, metaClient *metadata.Client, log *logger.Logger) error {
log.Info(fmt.Sprintf("Processing: %s", inputPath))
filename := filepath.Base(inputPath)
isSeries, imdbID, tvmazeID, season, episode := metadata.ParseFilename(filename)
width, height, err := enc.GetMediaInfo(inputPath)
if err != nil {
log.ErrorFile(inputPath, "Getting media info", err.Error())
mover.MoveToFailed(inputPath, cfg.Paths.Failed)
return err
}
mediaType := watcher.DetectMediaType(width, height)
crf := cfg.Encoding.DVD.CRF
preset := cfg.Encoding.DVD.Preset
if mediaType == types.MediaTypeBluRay {
crf = cfg.Encoding.Bluray.CRF
preset = cfg.Encoding.Bluray.Preset
}
var meta *types.Metadata
if isSeries && tvmazeID != "" && season != "" && episode != "" {
meta, err = metaClient.FetchSeriesMetadata(tvmazeID, season, episode)
if err != nil {
log.ErrorFile(inputPath, "Fetching series metadata", err.Error())
}
} else if imdbID != "" {
meta, err = metaClient.FetchMovieMetadata(imdbID)
if err != nil {
log.ErrorFile(inputPath, "Fetching movie metadata", err.Error())
}
}
if meta == nil {
meta = &types.Metadata{
Title: "Unknown",
DateReleased: "",
IMDBID: "",
OriginalMedia: mediaType,
}
}
meta.OriginalMedia = mediaType
job := &types.Job{
InputPath: inputPath,
MediaType: mediaType,
CRF: crf,
Preset: preset,
DeleteOrigin: deleteOrigin,
}
if err := enc.Transcode(inputPath, job, meta); err != nil {
log.ErrorFile(inputPath, "Transcoding", err.Error())
mover.MoveToFailed(inputPath, cfg.Paths.Failed)
outputPath := filepath.Join(filepath.Dir(inputPath), "output.mkv")
mover.MoveToFailed(outputPath, cfg.Paths.Failed)
return err
}
outputDir := filepath.Dir(inputPath)
outputPath := filepath.Join(outputDir, "output.mkv")
var outFilename string
if meta.IMDBID == "" || meta.Title == "Unknown" {
outFilename = fmt.Sprintf("%s.nometadata.mkv", generateRandomString(8))
} else if isSeries {
outFilename = fmt.Sprintf("%s.S%sE%s.mkv", meta.Collection, season, episode)
} else {
outFilename = fmt.Sprintf("%s.%s.mkv", meta.Title, meta.IMDBID)
}
finalOutput := filepath.Join(cfg.Paths.Output, outFilename)
if err := mover.Rename(outputPath, finalOutput); err != nil {
log.ErrorFile(inputPath, "Moving output", err.Error())
mover.MoveToFailed(outputPath, cfg.Paths.Failed)
return err
}
if deleteOrigin {
mover.Delete(inputPath)
log.Info(fmt.Sprintf("Deleted original: %s", inputPath))
} else {
mover.MoveToOriginals(inputPath, cfg.Paths.Originals)
log.Info(fmt.Sprintf("Moved original to originals: %s", inputPath))
}
log.Info(fmt.Sprintf("Completed: %s -> %s", inputPath, finalOutput))
return nil
}
func generateRandomString(length int) string {
bytes := make([]byte, length/2+1)
rand.Read(bytes)
return hex.EncodeToString(bytes)[:length]
}