cmd/weimar/image.go (view raw)
1package main
2
3import (
4 "fmt"
5 "strconv"
6
7 "codeberg.org/maxwelljensen/weimar/internal/config"
8 "codeberg.org/maxwelljensen/weimar/internal/db"
9 "codeberg.org/maxwelljensen/weimar/internal/storage"
10 "github.com/spf13/cobra"
11)
12
13var imageCmd = &cobra.Command{
14 Use: "image",
15 Short: "Manage images",
16}
17
18var imageDeleteCmd = &cobra.Command{
19 Use: "delete <id>",
20 Short: "Delete an image by ID",
21 Args: cobra.ExactArgs(1),
22 RunE: func(cmd *cobra.Command, args []string) error {
23 id, err := strconv.ParseInt(args[0], 10, 64)
24 if err != nil {
25 return fmt.Errorf("invalid image ID: %w", err)
26 }
27
28 cfg := config.FromViper()
29 database, err := db.Open(cfg.Database.Path)
30 if err != nil {
31 return fmt.Errorf("opening database: %w", err)
32 }
33 defer database.Close()
34
35 img, err := database.GetImageByID(cmd.Context(), id)
36 if err != nil {
37 return fmt.Errorf("looking up image: %w", err)
38 }
39
40 st, err := storage.New(cfg.Storage.Path)
41 if err != nil {
42 return fmt.Errorf("opening storage: %w", err)
43 }
44
45 // Delete thumbnail first if it exists.
46 if img.ThumbnailPath != nil {
47 if err := st.Delete(*img.ThumbnailPath); err != nil {
48 return fmt.Errorf("deleting thumbnail: %w", err)
49 }
50 }
51
52 // Delete original file.
53 if err := st.Delete(img.StoragePath); err != nil {
54 return fmt.Errorf("deleting file: %w", err)
55 }
56
57 // Delete database record.
58 if err := database.DeleteImage(cmd.Context(), id); err != nil {
59 return fmt.Errorf("deleting image record: %w", err)
60 }
61
62 fmt.Fprintf(cmd.OutOrStdout(), "deleted image %d: %s\n", img.ID, img.Filename)
63 return nil
64 },
65}
66
67var imageRescanInnateCmd = &cobra.Command{
68 Use: "rescan-innate",
69 Short: "Recompute innate tags for all images missing them",
70 Long: `Scans every image in the database and computes innate tags
71(image, video, gif, lowres, highres, >10 sec, <10 sec) for any
72record that has no innate tags yet. This is useful after upgrading
73from an older version that did not support innate tags.`,
74 Args: cobra.NoArgs,
75 RunE: func(cmd *cobra.Command, args []string) error {
76 cfg := config.FromViper()
77
78 database, err := db.Open(cfg.Database.Path)
79 if err != nil {
80 return fmt.Errorf("opening database: %w", err)
81 }
82 defer database.Close()
83
84 st, err := storage.New(cfg.Storage.Path)
85 if err != nil {
86 return fmt.Errorf("opening storage: %w", err)
87 }
88
89 images, err := database.ListAllImages(cmd.Context())
90 if err != nil {
91 return fmt.Errorf("listing images: %w", err)
92 }
93
94 var updated int
95 for _, img := range images {
96 if len(img.InnateTags) > 0 {
97 continue
98 }
99
100 videoPath := ""
101 if storage.IsVideo(img.MimeType) {
102 videoPath = st.AbsPath(img.StoragePath)
103 }
104 tags := storage.ComputeInnateTags(img.MimeType, img.Width, img.Height, videoPath)
105
106 if err := database.UpdateImageInnateTags(cmd.Context(), img.ID, tags); err != nil {
107 fmt.Fprintf(cmd.ErrOrStderr(), "error updating image %d: %v\n", img.ID, err)
108 continue
109 }
110 updated++
111 fmt.Fprintf(cmd.OutOrStdout(), " image %d (%s): %v\n", img.ID, img.Filename, tags)
112 }
113
114 fmt.Fprintf(cmd.OutOrStdout(), "done — updated %d image(s)\n", updated)
115 return nil
116 },
117}
118
119func init() {
120 rootCmd.AddCommand(imageCmd)
121 imageCmd.AddCommand(imageDeleteCmd)
122 imageCmd.AddCommand(imageRescanInnateCmd)
123}