summaryrefslogtreecommitdiff
path: root/internal/app/loader_gui.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/app/loader_gui.go')
-rw-r--r--internal/app/loader_gui.go132
1 files changed, 132 insertions, 0 deletions
diff --git a/internal/app/loader_gui.go b/internal/app/loader_gui.go
new file mode 100644
index 0000000..d964287
--- /dev/null
+++ b/internal/app/loader_gui.go
@@ -0,0 +1,132 @@
+package app
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "path/filepath"
+ "sort"
+
+ "codeberg.org/snonux/yoga/internal/tags"
+ "codeberg.org/snonux/yoga/internal/thumbnail"
+)
+
+type Loader struct {
+ root string
+ durationCache *durationCache
+ thumbnailCache *thumbnail.Cache
+ generator *thumbnail.Generator
+}
+
+func NewLoader(root string, durationCachePath string) *Loader {
+ durationCache, _ := loadDurationCache(durationCachePath)
+ return &Loader{
+ root: root,
+ durationCache: durationCache,
+ thumbnailCache: thumbnail.NewCache(root),
+ generator: thumbnail.NewGenerator(),
+ }
+}
+
+func (l *Loader) LoadVideos(ctx context.Context) ([]video, []string, []string, error) {
+ paths, err := collectVideoPathsForLoader(ctx, l.root)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ videos := make([]video, 0, len(paths))
+ durationPending := make([]string, 0)
+ thumbnailPending := make([]string, 0)
+ var tagErrors []string
+
+ for _, path := range paths {
+ info, statErr := os.Stat(path)
+ if statErr != nil {
+ videos = append(videos, video{
+ Name: filepath.Base(path),
+ Path: path,
+ Err: statErr,
+ Tags: []string{},
+ })
+ continue
+ }
+
+ dur := cachedDuration(l.durationCache, path, info)
+ if dur == 0 {
+ durationPending = append(durationPending, path)
+ }
+
+ thumbPath, hasThumb := l.checkThumbnail(path, info)
+ if !hasThumb {
+ thumbnailPending = append(thumbnailPending, path)
+ }
+
+ tagList, tagErr := tags.Load(path)
+ if tagErr != nil {
+ tagErrors = append(tagErrors, fmt.Sprintf("%s: %v", filepath.Base(path), tagErr))
+ }
+
+ videos = append(videos, video{
+ Name: filepath.Base(path),
+ Path: path,
+ Duration: dur,
+ ModTime: info.ModTime(),
+ Size: info.Size(),
+ Tags: tagList,
+ Thumbnail: thumbPath,
+ ThumbnailGenerated: hasThumb,
+ })
+ }
+
+ sort.Strings(durationPending)
+ sort.Strings(thumbnailPending)
+ sort.Strings(tagErrors)
+
+ return videos, durationPending, thumbnailPending, joinErrors(tagErrors)
+}
+
+func (l *Loader) checkThumbnail(videoPath string, info os.FileInfo) (string, bool) {
+ if l.thumbnailCache == nil {
+ return "", false
+ }
+
+ return l.thumbnailCache.Lookup(videoPath, info.ModTime())
+}
+
+func (l *Loader) GenerateThumbnail(ctx context.Context, videoPath string, modTime os.FileInfo) (string, error) {
+ if l.thumbnailCache == nil {
+ return "", fmt.Errorf("thumbnail cache not initialized")
+ }
+
+ thumbPath, err := l.generator.Generate(ctx, videoPath)
+ if err != nil {
+ return "", err
+ }
+
+ if err := l.thumbnailCache.Store(videoPath, modTime.ModTime(), thumbPath); err != nil {
+ return "", fmt.Errorf("store thumbnail cache: %w", err)
+ }
+
+ return thumbPath, nil
+}
+
+func collectVideoPathsForLoader(ctx context.Context, root string) ([]string, error) {
+ info, err := os.Stat(root)
+ if err != nil {
+ return nil, err
+ }
+ if !info.IsDir() {
+ if isVideo(root) {
+ return []string{root}, nil
+ }
+ return nil, nil
+ }
+
+ paths, err := collectVideoPaths(root)
+ if err != nil {
+ return nil, err
+ }
+
+ sort.Strings(paths)
+ return paths, nil
+}