Git Repository

Mini Video Library

Mini Video Library scannt lokale Medienordner, verwaltet Metadaten und Vorschaubilder und streamt kompatible Videos direkt per HTTP an den Browser.

Projektseite ↗
HTTPShttps://zanvex.de/git/mini-video-library.git

internal/config/config.go

Zum Verzeichnis
package config

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

type Config struct {
	ListenAddress             string
	DatabasePath              string
	Libraries                 []string
	ThumbnailDir              string
	ThumbnailWidth            int
	ThumbnailCount            int
	ScanInterval              time.Duration
	ScanWorkers               int
	FFmpegPath                string
	FFprobePath               string
	Language                  string
	ActiveTheme               string
	PreferredAudioLanguage    string
	PreferredSubtitleLanguage string
}

func (c Config) Clone() Config { c.Libraries = append([]string(nil), c.Libraries...); return c }

func defaults() Config {
	return Config{ListenAddress: "localhost:8080", DatabasePath: "data/library.db", ThumbnailDir: "data/thumbnails", ThumbnailWidth: 480, ThumbnailCount: 3, ScanInterval: 30 * time.Minute, ScanWorkers: 4, FFmpegPath: "ffmpeg", FFprobePath: "ffprobe", Language: "de"}
}

func Load(path string) (Config, error) {
	c := defaults()
	f, err := os.Open(path)
	if errors.Is(err, os.ErrNotExist) {
		return c, Save(path, c)
	}
	if err != nil {
		return c, err
	}
	defer f.Close()
	s := bufio.NewScanner(f)
	for s.Scan() {
		line := strings.TrimSpace(strings.SplitN(s.Text(), "#", 2)[0])
		if line == "" || strings.HasPrefix(line, "[") {
			continue
		}
		p := strings.SplitN(line, "=", 2)
		if len(p) != 2 {
			return c, fmt.Errorf("invalid config line: %s", line)
		}
		key, val := strings.TrimSpace(p[0]), strings.TrimSpace(p[1])
		switch key {
		case "listen_address":
			c.ListenAddress = unquote(val)
		case "database_path":
			c.DatabasePath = unquote(val)
		case "thumbnail_dir":
			c.ThumbnailDir = unquote(val)
		case "ffmpeg_path":
			c.FFmpegPath = unquote(val)
		case "ffprobe_path":
			c.FFprobePath = unquote(val)
		case "language":
			c.Language = unquote(val)
		case "active_theme":
			c.ActiveTheme = unquote(val)
		case "preferred_audio_language":
			c.PreferredAudioLanguage = unquote(val)
		case "preferred_subtitle_language":
			c.PreferredSubtitleLanguage = unquote(val)
		case "thumbnail_width":
			c.ThumbnailWidth, err = strconv.Atoi(val)
		case "thumbnail_count":
			c.ThumbnailCount, err = strconv.Atoi(val)
		case "scan_workers":
			c.ScanWorkers, err = strconv.Atoi(val)
		case "scan_interval":
			c.ScanInterval, err = time.ParseDuration(unquote(val))
		case "libraries":
			c.Libraries = parseArray(val)
		}
		if err != nil {
			return c, fmt.Errorf("config %s: %w", key, err)
		}
	}
	return c, s.Err()
}

func Save(path string, c Config) error {
	if dir := filepath.Dir(path); dir != "." {
		if err := os.MkdirAll(dir, 0755); err != nil {
			return err
		}
	}
	q := func(v string) string { return strconv.Quote(v) }
	libs := make([]string, len(c.Libraries))
	for i, v := range c.Libraries {
		libs[i] = q(v)
	}
	data := fmt.Sprintf("# Mini Video Library\nlisten_address = %s\ndatabase_path = %s\nlibraries = [%s]\nthumbnail_dir = %s\nthumbnail_width = %d\nthumbnail_count = %d\nscan_interval = %s\nscan_workers = %d\nffmpeg_path = %s\nffprobe_path = %s\nlanguage = %s\nactive_theme = %s\npreferred_audio_language = %s\npreferred_subtitle_language = %s\n", q(c.ListenAddress), q(c.DatabasePath), strings.Join(libs, ", "), q(c.ThumbnailDir), c.ThumbnailWidth, c.ThumbnailCount, q(c.ScanInterval.String()), c.ScanWorkers, q(c.FFmpegPath), q(c.FFprobePath), q(c.Language), q(c.ActiveTheme), q(c.PreferredAudioLanguage), q(c.PreferredSubtitleLanguage))
	return os.WriteFile(path, []byte(data), 0644)
}
func unquote(v string) string {
	r, e := strconv.Unquote(strings.TrimSpace(v))
	if e == nil {
		return r
	}
	return strings.TrimSpace(v)
}
func parseArray(v string) []string {
	v = strings.Trim(strings.TrimSpace(v), "[]")
	if v == "" {
		return nil
	}
	parts := strings.Split(v, ",")
	out := make([]string, 0, len(parts))
	for _, p := range parts {
		if x := unquote(p); x != "" {
			out = append(out, x)
		}
	}
	return out
}