package main import ( "fmt" "strconv" "codeberg.org/maxwelljensen/weimar/internal/config" "codeberg.org/maxwelljensen/weimar/internal/db" "codeberg.org/maxwelljensen/weimar/internal/storage" "github.com/spf13/cobra" ) var imageCmd = &cobra.Command{ Use: "image", Short: "Manage images", } var imageDeleteCmd = &cobra.Command{ Use: "delete ", Short: "Delete an image by ID", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { id, err := strconv.ParseInt(args[0], 10, 64) if err != nil { return fmt.Errorf("invalid image ID: %w", err) } cfg := config.FromViper() database, err := db.Open(cfg.Database.Path) if err != nil { return fmt.Errorf("opening database: %w", err) } defer database.Close() img, err := database.GetImageByID(cmd.Context(), id) if err != nil { return fmt.Errorf("looking up image: %w", err) } st, err := storage.New(cfg.Storage.Path) if err != nil { return fmt.Errorf("opening storage: %w", err) } // Delete thumbnail first if it exists. if img.ThumbnailPath != nil { if err := st.Delete(*img.ThumbnailPath); err != nil { return fmt.Errorf("deleting thumbnail: %w", err) } } // Delete original file. if err := st.Delete(img.StoragePath); err != nil { return fmt.Errorf("deleting file: %w", err) } // Delete database record. if err := database.DeleteImage(cmd.Context(), id); err != nil { return fmt.Errorf("deleting image record: %w", err) } fmt.Fprintf(cmd.OutOrStdout(), "deleted image %d: %s\n", img.ID, img.Filename) return nil }, } var imageRescanInnateCmd = &cobra.Command{ Use: "rescan-innate", Short: "Recompute innate tags for all images missing them", Long: `Scans every image in the database and computes innate tags (image, video, gif, lowres, highres, >10 sec, <10 sec) for any record that has no innate tags yet. This is useful after upgrading from an older version that did not support innate tags.`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { cfg := config.FromViper() database, err := db.Open(cfg.Database.Path) if err != nil { return fmt.Errorf("opening database: %w", err) } defer database.Close() st, err := storage.New(cfg.Storage.Path) if err != nil { return fmt.Errorf("opening storage: %w", err) } images, err := database.ListAllImages(cmd.Context()) if err != nil { return fmt.Errorf("listing images: %w", err) } var updated int for _, img := range images { if len(img.InnateTags) > 0 { continue } videoPath := "" if storage.IsVideo(img.MimeType) { videoPath = st.AbsPath(img.StoragePath) } tags := storage.ComputeInnateTags(img.MimeType, img.Width, img.Height, videoPath) if err := database.UpdateImageInnateTags(cmd.Context(), img.ID, tags); err != nil { fmt.Fprintf(cmd.ErrOrStderr(), "error updating image %d: %v\n", img.ID, err) continue } updated++ fmt.Fprintf(cmd.OutOrStdout(), " image %d (%s): %v\n", img.ID, img.Filename, tags) } fmt.Fprintf(cmd.OutOrStdout(), "done — updated %d image(s)\n", updated) return nil }, } func init() { rootCmd.AddCommand(imageCmd) imageCmd.AddCommand(imageDeleteCmd) imageCmd.AddCommand(imageRescanInnateCmd) }