Add flag to preserve cached media on cleanup (#36200)

Co-authored-by: Daniel King <git@kin.gy>
pull/37666/head
Daniel King 4 weeks ago committed by GitHub
parent 0196c12e7f
commit d5d57ac25a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -214,6 +214,14 @@ class MediaAttachment < ApplicationRecord
scope :remote, -> { where.not(remote_url: '') }
scope :unattached, -> { where(status_id: nil, scheduled_status_id: nil) }
scope :updated_before, ->(value) { where(arel_table[:updated_at].lt(value)) }
scope :without_local_interaction, lambda {
where.not(Favourite.joins(:account).merge(Account.local).where(Favourite.arel_table[:status_id].eq(MediaAttachment.arel_table[:status_id])).select(1).arel.exists)
.where.not(Bookmark.where(Bookmark.arel_table[:status_id].eq(MediaAttachment.arel_table[:status_id])).select(1).arel.exists)
.where.not(Status.local.where(Status.arel_table[:in_reply_to_id].eq(MediaAttachment.arel_table[:status_id])).select(1).arel.exists)
.where.not(Status.local.where(Status.arel_table[:reblog_of_id].eq(MediaAttachment.arel_table[:status_id])).select(1).arel.exists)
.where.not(Quote.joins(:status).merge(Status.local).where(Quote.arel_table[:quoted_status_id].eq(MediaAttachment.arel_table[:status_id])).select(1).arel.exists)
.where.not(Quote.joins(:quoted_status).merge(Status.local).where(Quote.arel_table[:status_id].eq(MediaAttachment.arel_table[:status_id])).select(1).arel.exists)
}
attr_accessor :skip_download

@ -90,6 +90,7 @@ class Status < ApplicationRecord
has_many :local_favorited, -> { merge(Account.local) }, through: :favourites, source: :account
has_many :local_reblogged, -> { merge(Account.local) }, through: :reblogs, source: :account
has_many :local_bookmarked, -> { merge(Account.local) }, through: :bookmarks, source: :account
has_many :local_replied, -> { merge(Account.local) }, through: :replies, source: :account
has_and_belongs_to_many :tags # rubocop:disable Rails/HasAndBelongsToMany

@ -17,6 +17,7 @@ module Mastodon::CLI
option :concurrency, type: :numeric, default: 5, aliases: [:c]
option :verbose, type: :boolean, default: false, aliases: [:v]
option :dry_run, type: :boolean, default: false
option :keep_interacted, type: :boolean, default: false
desc 'remove', 'Remove remote media files, headers or avatars'
long_desc <<-DESC
Removes locally cached copies of media attachments (and optionally profile
@ -26,6 +27,9 @@ module Mastodon::CLI
they are removed. In case of avatars and headers, it specifies how old
the last webfinger request and update to the user has to be before they
are pruned. It defaults to 7 days.
If --keep-interacted is specified, any media attached to a status that
was favourited, bookmarked, quoted, replied to, or reblogged by a local
account will be preserved.
If --prune-profiles is specified, only avatars and headers are removed.
If --remove-headers is specified, only headers are removed.
If --include-follows is specified along with --prune-profiles or
@ -61,7 +65,11 @@ module Mastodon::CLI
end
unless options[:prune_profiles] || options[:remove_headers]
processed, aggregate = parallelize_with_progress(MediaAttachment.cached.remote.where(created_at: ..time_ago)) do |media_attachment|
attachment_scope = MediaAttachment.cached.remote.where(created_at: ..time_ago)
attachment_scope = attachment_scope.without_local_interaction if options[:keep_interacted]
processed, aggregate = parallelize_with_progress(attachment_scope) do |media_attachment|
next if media_attachment.file.blank?
size = (media_attachment.file_file_size || 0) + (media_attachment.thumbnail_file_size || 0)

@ -73,6 +73,66 @@ RSpec.describe Mastodon::CLI::Media do
expect(media_attachment.reload.thumbnail).to be_blank
end
end
context 'with --keep-interacted' do
let(:options) { { keep_interacted: true } }
let!(:favourited_media) { Fabricate(:media_attachment, created_at: 1.month.ago, remote_url: 'https://example.com/image.jpg') }
let!(:bookmarked_media) { Fabricate(:media_attachment, created_at: 1.month.ago, remote_url: 'https://example.com/image.jpg') }
let!(:replied_to_media) { Fabricate(:media_attachment, created_at: 1.month.ago, remote_url: 'https://example.com/image.jpg') }
let!(:reblogged_media) { Fabricate(:media_attachment, created_at: 1.month.ago, remote_url: 'https://example.com/image.jpg') }
let!(:remote_quoted_media) { Fabricate(:media_attachment, created_at: 1.month.ago, remote_url: 'https://example.com/image.jpg') }
let!(:remote_quoting_media) { Fabricate(:media_attachment, created_at: 1.month.ago, remote_url: 'https://example.com/image.jpg') }
before do
local_account = Fabricate(:account, username: 'alice')
remote_account = Fabricate(:account, username: 'bob', domain: 'example.com')
favourited_status = Fabricate(:status, account: remote_account)
bookmarked_status = Fabricate(:status, account: remote_account)
replied_to_status = Fabricate(:status, account: remote_account)
reblogged_status = Fabricate(:status, account: remote_account)
favourited_media.update!(status: favourited_status)
bookmarked_media.update!(status: bookmarked_status)
replied_to_media.update!(status: replied_to_status)
reblogged_media.update!(status: reblogged_status)
local_quoting_status = Fabricate(:status, account: local_account)
remote_quoted_status = Fabricate(:status, account: remote_account)
local_status_being_quoted = Fabricate(:status, account: local_account)
remote_quoting_status = Fabricate(:status, account: remote_account)
remote_quoted_media.update!(status: remote_quoted_status)
remote_quoting_media.update!(status: remote_quoting_status)
non_interacted_status = Fabricate(:status, account: remote_account)
media_attachment.update(status: non_interacted_status)
Fabricate(:favourite, account: local_account, status: favourited_status)
Fabricate(:bookmark, account: local_account, status: bookmarked_status)
Fabricate(:status, account: local_account, in_reply_to_id: replied_to_status.id)
Fabricate(:status, account: local_account, reblog: reblogged_status)
Fabricate(:quote, account: local_account, status: local_quoting_status, quoted_status: remote_quoted_status)
Fabricate(:quote, account: remote_account, status: remote_quoting_status, quoted_status: local_status_being_quoted)
end
it 'keeps media associated with statuses that have been favourited, bookmarked, replied to, or reblogged by a local account' do
expect { subject }
.to output_results('Removed 1')
expect(favourited_media.reload.file).to be_present
expect(bookmarked_media.reload.file).to be_present
expect(replied_to_media.reload.file).to be_present
expect(reblogged_media.reload.file).to be_present
expect(remote_quoted_media.reload.file).to be_present
expect(remote_quoting_media.reload.file).to be_present
expect(media_attachment.reload.file).to be_blank
expect(media_attachment.reload.thumbnail).to be_blank
end
end
end
end

Loading…
Cancel
Save