package scanner import ( "bytes" "encoding/json" "fmt" "os" "path" "strconv" "strings" "text/template" "time" "github.com/pelletier/go-toml/v2" ffmpeg_go "github.com/u2takey/ffmpeg-go" ) type ( Category struct { Name string `toml:"name" json:"name"` Type string `toml:"type" json:"type" comment:"Valid types: gaming, other (default: other)"` Url string `toml:"url" json:"url"` } Metadata struct { Title string `toml:"title" json:"title"` Part int `toml:"part" json:"part"` Date toml.LocalDate `toml:"date" json:"date"` Tags []string `toml:"tags" json:"tags"` FootageDir string `toml:"footage_dir" json:"footage_dir"` Uploaded bool `toml:"uploaded" json:"uploaded"` Category *Category `toml:"category" json:"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" func ScanSegments(directory string, extension string) ([]string, error) { entries, err := os.ReadDir(directory) if err != nil { return nil, err } files := []string{} for _, item := range entries { if item.IsDir() { continue } if strings.HasPrefix(item.Name(), ".") { continue } if !strings.HasSuffix(item.Name(), "." + extension) { continue } if strings.HasSuffix(item.Name(), "-fullvod." + extension) { continue } files = append(files, item.Name()) } return files, nil } func ProbeSegment(filename string) (*FFprobeOutput, error) { out, err := ffmpeg_go.Probe(filename) if err != nil { return nil, err } type ( RawFFprobeFormat struct { // these being strings upsets me immensely Duration string `json:"duration"` Size string `json:"size"` } RawFFprobeOutput struct { Format RawFFprobeFormat `json:"format"` } ) probe := RawFFprobeOutput{} err = json.Unmarshal([]byte(out), &probe) if err != nil { return nil, err } duration, err := strconv.ParseFloat(probe.Format.Duration, 64) if err != nil { return nil, err } size, err := strconv.ParseInt(probe.Format.Size, 10, 0) if err != nil { return nil, err } return &FFprobeOutput{ Format: FFprobeFormat{ Duration: duration, Size: size, }, }, nil } func ReadMetadata(directory string) (*Metadata, error) { metadata := &Metadata{} file, err := os.OpenFile( path.Join(directory, METADATA_FILENAME), os.O_RDONLY, os.ModePerm, ) if err != nil { return nil, err } err = toml.NewDecoder(file).Decode(metadata) if err != nil { return nil, err } return metadata, nil } func WriteMetadata(directory string, metadata *Metadata) (error) { data, err := toml.Marshal(metadata) if err != nil { return err } err = os.WriteFile(path.Join(directory, METADATA_FILENAME), data, 0644) return err } func DefaultMetadata() *Metadata { return &Metadata{ Title: "Untitled Stream", Date: toml.LocalDate{ Year: time.Now().Year(), Month: int(time.Now().Month()), Day: time.Now().Day(), }, Part: 0, Category: nil, } } func BuildTemplate(metadata *Metadata, tmpl *template.Template) (string, error) { out := &bytes.Buffer{} err := tmpl.Execute(out, metadata) return strings.TrimSpace(out.String()), err } func FileSizeString(size int64) string { denomination := "B" if size > 1000 { size /= 1000; denomination = "KB" } if size > 1000 { size /= 1000; denomination = "MB" } if size > 1000 { size /= 1000; denomination = "GB" } if size > 1000 { size /= 1000; denomination = "TB" } return fmt.Sprintf("%d%s", size, denomination) }