mirror of
https://github.com/glitch-soc/mastodon.git
synced 2026-03-29 03:00:33 +02:00
Add flag to preserve cached media on cleanup (#36200)
Co-authored-by: Daniel King <git@kin.gy>
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user