all repos — weimar @ ab494a4c3e2c99426dd9f18f02bcb3fb7ee678a3

Unnamed repository; edit this file 'description' to name the repository.

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}