diff --git a/config/config.go b/config/config.go index a0fe6ee..28e0378 100644 --- a/config/config.go +++ b/config/config.go @@ -33,7 +33,7 @@ var defaultConfig = Config{ }, } -const CONFIG_FILENAME = "config.toml" +var CONFIG_FILENAME string = "config.toml" func ReadConfig(filename string) (*Config, error) { cfgBytes, err := os.ReadFile(filename) diff --git a/main.go b/main.go index 7c27390..45845f3 100644 --- a/main.go +++ b/main.go @@ -28,19 +28,29 @@ var helpText string const segmentExtension = "mkv" func showHelp() { - execSplits := strings.Split(os.Args[0], "/") - execName := execSplits[len(execSplits) - 1] - fmt.Printf(helpText, execName) + fmt.Println(helpText) + os.Exit(0) } func main() { // config + userConfigDir, err := os.UserConfigDir() + if err != nil { + log.Fatalf("Could not determine user configuration directory: %v", err) + os.Exit(1) + } + config.CONFIG_FILENAME = path.Join(userConfigDir, "vodular", "config.toml") cfg, err := config.ReadConfig(config.CONFIG_FILENAME) if err != nil { log.Fatalf("Failed to read config: %v", err) os.Exit(1) } if cfg == nil { + err = os.MkdirAll(path.Dir(config.CONFIG_FILENAME), 0750) + if err != nil { + log.Fatalf("Failed to create config directory: %v", err) + os.Exit(1) + } err = config.GenerateConfig(config.CONFIG_FILENAME) if err != nil { log.Fatalf("Failed to generate config: %v", err) @@ -57,7 +67,6 @@ func main() { // arguments if len(os.Args) < 2 || os.Args[1] == "--help" || os.Args[1] == "-h" { showHelp() - os.Exit(0) } var verbose bool = false @@ -76,7 +85,6 @@ func main() { fallthrough case "--help": showHelp() - os.Exit(0) case "-v": fallthrough @@ -228,19 +236,34 @@ func main() { os.Exit(1) } fmt.Printf( - "\n================================\n" + - "TITLE:\n%s\n\n" + - "DESCRIPTION:\n%s\n" + + "\n================================\n\n" + + "< TITLE >\n%s\n\n" + + "< DESCRIPTION >\n%s\n" + "\n================================\n", title, description, ) } // concatenate VOD segments into full VOD - video.SizeBytes, err = vid.ConcatVideo(video, vodFiles, verbose) - if err != nil { - log.Fatalf("Failed to concatenate VOD segments: %v", err) - os.Exit(1) + fullVodExists := func () bool { + // check if full VOD already exists with expected duration + if fullVodProbe, err := scanner.ProbeSegment(video.Filename); err != nil { + var totalLength float64 = 0 + for _, filename := range vodFiles { + probe, err := scanner.ProbeSegment(filename) + if err != nil { continue } + totalLength += probe.Format.Duration + } + return fullVodProbe.Format.Duration == totalLength + } + return false + }() + if !fullVodExists { + video.SizeBytes, err = vid.ConcatVideo(video, vodFiles, verbose) + if err != nil { + log.Fatalf("Failed to concatenate VOD segments: %v", err) + os.Exit(1) + } } // youtube oauth flow diff --git a/scanner/scanner.go b/scanner/scanner.go index 86ea815..5cb3f14 100644 --- a/scanner/scanner.go +++ b/scanner/scanner.go @@ -1,12 +1,14 @@ package scanner import ( + "encoding/json" "os" "path" "strings" "time" "github.com/pelletier/go-toml/v2" + ffmpeg_go "github.com/u2takey/ffmpeg-go" ) type ( @@ -25,6 +27,15 @@ type ( Uploaded bool `toml:"uploaded"` Category *Category `toml:"category" comment:"(Optional) Category details, for additional credits."` } + + ffprobeFormat struct { + Duration float64 `json:"duration"` + Size int64 `json:"size"` + } + + ffprobeOutput struct { + Format ffprobeFormat `json:"format"` + } ) const METADATA_FILENAME = "metadata.toml" @@ -47,6 +58,17 @@ func ScanSegments(directory string, extension string) ([]string, error) { return files, nil } +func ProbeSegment(filename string) (*ffprobeOutput, error) { + out, err := ffmpeg_go.Probe(filename) + if err != nil { return nil, err } + + probe := ffprobeOutput{} + err = json.Unmarshal([]byte(out), probe) + if err != nil { return nil, err } + + return &probe, nil +} + func ReadMetadata(directory string) (*Metadata, error) { metadata := &Metadata{} file, err := os.OpenFile(