189 lines
3.8 KiB
Go
189 lines
3.8 KiB
Go
package metadata
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"git.meatbag.se/varl/subsyt/internal/model"
|
|
)
|
|
|
|
type ShowAsset struct {
|
|
Show model.Show
|
|
InfoPath string
|
|
Dir string
|
|
Images []string
|
|
}
|
|
|
|
type EpisodeAsset struct {
|
|
Episode model.Episode
|
|
InfoPath string
|
|
Dir string
|
|
MediaPath string
|
|
Sidecars []string
|
|
}
|
|
|
|
func findFiles(root, ext string) ([]string, error) {
|
|
if root == "" {
|
|
return nil, fmt.Errorf("find files: empty root")
|
|
}
|
|
|
|
if _, err := os.Stat(root); errors.Is(err, os.ErrNotExist) {
|
|
return nil, nil
|
|
} else if err != nil {
|
|
return nil, fmt.Errorf("find files: %w", err)
|
|
}
|
|
|
|
var result []string
|
|
walkErr := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if d.IsDir() {
|
|
return nil
|
|
}
|
|
if strings.HasSuffix(path, ext) {
|
|
result = append(result, path)
|
|
}
|
|
return nil
|
|
})
|
|
if walkErr != nil {
|
|
return nil, fmt.Errorf("walk %s: %w", root, walkErr)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func trimInfoSuffix(path string) string {
|
|
return strings.TrimSuffix(path, ".info.json")
|
|
}
|
|
|
|
func globSidecars(base string) ([]string, error) {
|
|
matches, err := filepath.Glob(base + ".*")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("glob %s.*: %w", base, err)
|
|
}
|
|
|
|
var sidecars []string
|
|
for _, match := range matches {
|
|
if strings.HasSuffix(match, ".info.json") {
|
|
continue
|
|
}
|
|
sidecars = append(sidecars, match)
|
|
}
|
|
return sidecars, nil
|
|
}
|
|
|
|
func directoryImages(dir string) ([]string, error) {
|
|
entries, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return nil, nil
|
|
}
|
|
return nil, fmt.Errorf("readdir %s: %w", dir, err)
|
|
}
|
|
|
|
var images []string
|
|
for _, entry := range entries {
|
|
if entry.IsDir() {
|
|
continue
|
|
}
|
|
name := entry.Name()
|
|
switch strings.ToLower(filepath.Ext(name)) {
|
|
case ".jpg", ".jpeg", ".png", ".webp":
|
|
images = append(images, filepath.Join(dir, name))
|
|
}
|
|
}
|
|
return images, nil
|
|
}
|
|
|
|
func resolveMediaPath(base string, episode model.Episode, sidecars []string) (string, []string) {
|
|
if episode.Ext != "" {
|
|
candidate := base + "." + episode.Ext
|
|
if _, err := os.Stat(candidate); err == nil {
|
|
var rest []string
|
|
for _, path := range sidecars {
|
|
if path != candidate {
|
|
rest = append(rest, path)
|
|
}
|
|
}
|
|
return candidate, rest
|
|
}
|
|
}
|
|
|
|
for _, path := range sidecars {
|
|
ext := strings.ToLower(filepath.Ext(path))
|
|
switch ext {
|
|
case ".mp4", ".mkv", ".webm", ".m4v", ".mov", ".avi":
|
|
var rest []string
|
|
for _, other := range sidecars {
|
|
if other != path {
|
|
rest = append(rest, other)
|
|
}
|
|
}
|
|
return path, rest
|
|
}
|
|
}
|
|
|
|
return "", sidecars
|
|
}
|
|
|
|
func ScanShows(root string) ([]ShowAsset, error) {
|
|
infoFiles, err := findFiles(root, ".info.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var assets []ShowAsset
|
|
for _, infoPath := range infoFiles {
|
|
show := model.LoadShow(infoPath)
|
|
dir := filepath.Dir(infoPath)
|
|
|
|
imgs, imgErr := directoryImages(dir)
|
|
if imgErr != nil {
|
|
log.Printf("scan shows: %v\n", imgErr)
|
|
}
|
|
|
|
assets = append(assets, ShowAsset{
|
|
Show: show,
|
|
InfoPath: infoPath,
|
|
Dir: dir,
|
|
Images: imgs,
|
|
})
|
|
}
|
|
|
|
return assets, nil
|
|
}
|
|
|
|
func ScanEpisodes(root string) ([]EpisodeAsset, error) {
|
|
infoFiles, err := findFiles(root, ".info.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var assets []EpisodeAsset
|
|
for _, infoPath := range infoFiles {
|
|
episode := model.LoadEpisode(infoPath)
|
|
base := trimInfoSuffix(infoPath)
|
|
dir := filepath.Dir(infoPath)
|
|
|
|
sidecars, sidecarErr := globSidecars(base)
|
|
if sidecarErr != nil {
|
|
return nil, sidecarErr
|
|
}
|
|
|
|
mediaPath, remaining := resolveMediaPath(base, episode, sidecars)
|
|
assets = append(assets, EpisodeAsset{
|
|
Episode: episode,
|
|
InfoPath: infoPath,
|
|
Dir: dir,
|
|
MediaPath: mediaPath,
|
|
Sidecars: remaining,
|
|
})
|
|
}
|
|
|
|
return assets, nil
|
|
}
|