main
Raw Download raw file
  1// Package output manages the output directory structure.
  2package output
  3
  4import (
  5	"fmt"
  6	"os"
  7	"path/filepath"
  8
  9	"github.com/crash/upvs/internal/sanitize"
 10)
 11
 12// Layout manages paths within the output directory.
 13type Layout struct {
 14	root   string
 15	camera string
 16	date   string
 17}
 18
 19// NewLayout creates a Layout for the given output directory, camera, and date.
 20func NewLayout(outDir, camera, date string) *Layout {
 21	return &Layout{
 22		root:   outDir,
 23		camera: sanitize.Filename(camera),
 24		date:   date,
 25	}
 26}
 27
 28// EnsureDirs creates all required directories in the output structure.
 29func (l *Layout) EnsureDirs() error {
 30	dirs := []string{
 31		l.MetadataDir(),
 32		l.ClipsDir(),
 33		l.ManifestsDir(),
 34		l.TimelapseDir(),
 35		l.LogsDir(),
 36	}
 37	for _, dir := range dirs {
 38		err := os.MkdirAll(dir, 0755)
 39		if err != nil {
 40			return fmt.Errorf("creating directory %s: %w", dir, err)
 41		}
 42	}
 43	return nil
 44}
 45
 46// Root returns the root output directory.
 47func (l *Layout) Root() string {
 48	return l.root
 49}
 50
 51// MetadataDir returns the path to the metadata directory.
 52func (l *Layout) MetadataDir() string {
 53	return filepath.Join(l.root, "metadata")
 54}
 55
 56// CameraJSONPath returns the path to camera.json.
 57func (l *Layout) CameraJSONPath() string {
 58	return filepath.Join(l.MetadataDir(), "camera.json")
 59}
 60
 61// DayJSONPath returns the path to day.json.
 62func (l *Layout) DayJSONPath() string {
 63	return filepath.Join(l.MetadataDir(), "day.json")
 64}
 65
 66// ClipsDir returns the path to the clips directory for this camera/date.
 67func (l *Layout) ClipsDir() string {
 68	return filepath.Join(l.root, "clips", l.camera, l.date)
 69}
 70
 71// ClipPath returns the path for a specific clip file.
 72func (l *Layout) ClipPath(startMs, endMs int64, eventID string) string {
 73	filename := fmt.Sprintf("clip_%d_%d_%s.mp4", startMs, endMs, sanitize.Filename(eventID))
 74	return filepath.Join(l.ClipsDir(), filename)
 75}
 76
 77// ClipPartialPath returns the path for a partial (in-progress) clip download.
 78func (l *Layout) ClipPartialPath(startMs, endMs int64, eventID string) string {
 79	return l.ClipPath(startMs, endMs, eventID) + ".partial"
 80}
 81
 82// ManifestsDir returns the path to the manifests directory.
 83func (l *Layout) ManifestsDir() string {
 84	return filepath.Join(l.root, "manifests")
 85}
 86
 87// ClipIndexPath returns the path to clip_index.json.
 88func (l *Layout) ClipIndexPath() string {
 89	return filepath.Join(l.ManifestsDir(), "clip_index.json")
 90}
 91
 92// ConcatTxtPath returns the path to concat.txt for FFmpeg.
 93func (l *Layout) ConcatTxtPath() string {
 94	return filepath.Join(l.ManifestsDir(), "concat.txt")
 95}
 96
 97// TimelapseDir returns the path to the timelapse output directory.
 98func (l *Layout) TimelapseDir() string {
 99	return filepath.Join(l.root, "timelapse")
100}
101
102// TimelapsePath returns the path to the final timelapse video.
103func (l *Layout) TimelapsePath() string {
104	filename := fmt.Sprintf("%s_%s_timelapse.mp4", l.camera, l.date)
105	return filepath.Join(l.TimelapseDir(), filename)
106}
107
108// LogsDir returns the path to the logs directory.
109func (l *Layout) LogsDir() string {
110	return filepath.Join(l.root, "logs")
111}
112
113// RunLogPath returns the path to run.log.
114func (l *Layout) RunLogPath() string {
115	return filepath.Join(l.LogsDir(), "run.log")
116}