add: add main CLI entrypoint
This commit is contained in:
@@ -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]
|
||||
}
|
||||
Reference in New Issue
Block a user