Skip to content

Add option to delete images from clips not used in x days - #1168

Open
eddiekk wants to merge 1 commit into
sabrogden:masterfrom
eddiekk:expire-images
Open

eddiekk wants to merge 1 commit into
sabrogden:masterfrom
eddiekk:expire-images

Conversation

@eddiekk

@eddiekk eddiekk commented Oct 14, 2026 •

Copy link
Copy Markdown

Summary

Adds an advanced option to automatically delete image data (CF_DIB, CF_DIBV5, PNG) from clips that haven't been used in a set number of days, while keeping everything else.

Related: #897 (auto delete images), #875 (30 GB database).

Motivation

Images are by far the largest thing in the database. With a long retention time, old screenshots end up taking almost all of the space. On my machine (keep 365 days / 10,000 copies), removing old image data shrank the database from 9 GB to 300 MB. I still wanted to keep a long history of text clips. Today the only options are to shorten retention for every clip, or to delete images by hand in Delete Clip Data.

What it does

New option in Options → Advanced:

Delete images (CF_DIB, PNG) from clips not used in x days (0 = never): default 0 (disabled, so existing behaviour is unchanged)

When set, RemoveOldEntries now also calls a new RemoveExpiredImages:

  • It finds clips whose lastPasteDate is older than the setting and that have a CF_DIB, CF_DIBV5 or PNG format.
  • It skips the same clips the existing expire after x days option skips: groups, clips in groups, clips with shortcuts, sticky clips, and clips marked never auto delete.
  • It deletes only the image rows from Data. A clip that had other formats (e.g. Excel/Word copies with text + HTML + image) keeps them.
    • That clip's CRC is set to 0, so copying the same content again creates a new clip with the image instead of moving the image-less clip to the top.
    • The clip is refreshed in the list (UPDATE_CLIP_DESCRIPTION) so the cached thumbnail goes away.
  • A clip that only had image data is deleted through CClipIDs::DeleteIDs, same as the other expire paths.

To avoid locking the db while deleting large blobs, it uses the same throttling as the MainDeletes cleanup:

  • It only runs when the computer has been idle for IdleSecondsBeforeDelete.
  • It processes at most MainDeletesDeleteCount clips per run.
  • Compact and Repair (checkIdleTime = false) skips the idle check.

New databases use auto_vacuum, so the space is returned to the file system automatically. Older databases may need Compact and Repair once to shrink the file.

Files

  • Options.h/.cpp: Get/SetExpiredImageDays (ExpiredImageDays, default 0)
  • AdvGeneral.cpp: new property in the advanced grid
  • DatabaseUtilities.cpp: RemoveExpiredImages, called from RemoveOldEntries

Testing

  • Builds with the repo's MSBuild workflow (Release x64).
  • I ran the SQL against the Ditto schema (Main/Data/MainDeletes + triggers) in SQLite with these clips: old image-only, old mixed text+image, old CF_DIBV5, recent image, old text-only, and old clips with a shortcut / never auto delete / in a group / sticky.
    • Only the old unprotected clips were affected.
    • Image-only clips were deleted.
    • The mixed clip kept its text and HTML formats.
    • The query plan uses the existing Data_ParentId_Format index.
  • Ran a local Release x64 build (portable mode) against a copy of my real database: 365 days / 10,000 clips, 290 MB, 128 clips with CF_DIB. The option was set to 30 days, and IdleSecondsBeforeDelete=0 so the timer didn't wait for idle.
    • The 15-minute timer handled 100 clips on the first run and the remaining 23 on the second, as limited by MainDeletesDeleteCount.
    • All 123 old clips were text, HTML or file copies that also carried a CF_DIB, mostly text copied from a browser or terminal. They all kept their other formats, so no clips disappeared from the list, and their CRC was set to 0.
    • The 5 image clips used in the last 30 days were untouched.
    • The database file shrank from 290 MB to 124.5 MB right away because of auto_vacuum.
    • Changing the value in the Advanced options grid saves and reloads correctly.

🤖 Generated with Claude Code

Images (CF_DIB, CF_DIBV5, PNG) are by far the largest items in the
database. With a long retention time the database can grow to many GB,
mostly from old screenshots.

New advanced option "Delete images (CF_DIB, PNG) from clips not used in
x days (0 = never)", default 0 (disabled). When set, RemoveOldEntries
removes the image formats from clips whose lastPasteDate is older than
the set number of days. Other formats of the clip (text, html, rtf) are
kept; clips that only contained image data are deleted. Clips with
shortcuts, in groups, sticky or set to never auto delete are skipped,
the same rules as the existing expire option.

Like the MainDeletes cleanup, this only runs when the computer has been
idle and processes MainDeletesDeleteCount clips per run so deleting the
large blobs doesn't lock the db.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant