Add command to remove avatar and header images of inactive remote acc… (#1259)

This cherry-picks [this pull request
commit](https://github.com/mastodon/mastodon/pull/22149) into Hometown.
It will be coming in a future Mastodon release but we will get it early.
Basically it adds options to `tootctl media remove`:

> Removes locally cached copies of media attachments (and optionally
profile headers and
avatars) from other servers. By default, only media attachements are
removed. The --days option specifies how old media attachments have to
be before 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 --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 --remove-headers, all non-local profiles
will be pruned irrespective of follow status. By default, only accounts
  that are not followed by or following anyone locally are pruned.

Relates to but does not fully address #1209 because there needs to be a
web UI component, too.

Co-authored-by: Evan <35814742+evanphilip@users.noreply.github.com>
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
Darius Kazemi 2022-12-28 15:25:25 -08:00 committed by GitHub
parent d2e626dd1d
commit 5de8a60c1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 18 deletions

View File

@ -2,7 +2,7 @@ require:
- rubocop-rails - rubocop-rails
AllCops: AllCops:
TargetRubyVersion: 2.5 TargetRubyVersion: 2.7
NewCops: disable NewCops: disable
Exclude: Exclude:
- 'spec/**/*' - 'spec/**/*'

View File

@ -14,21 +14,63 @@ module Mastodon
end end
option :days, type: :numeric, default: 7, aliases: [:d] option :days, type: :numeric, default: 7, aliases: [:d]
option :prune_profiles, type: :boolean, default: false
option :remove_headers, type: :boolean, default: false
option :include_follows, type: :boolean, default: false
option :concurrency, type: :numeric, default: 5, aliases: [:c] option :concurrency, type: :numeric, default: 5, aliases: [:c]
option :verbose, type: :boolean, default: false, aliases: [:v]
option :dry_run, type: :boolean, default: false option :dry_run, type: :boolean, default: false
desc 'remove', 'Remove remote media files' desc 'remove', 'Remove remote media files, headers or avatars'
long_desc <<-DESC long_desc <<-DESC
Removes locally cached copies of media attachments from other servers. Removes locally cached copies of media attachments (and optionally profile
headers and avatars) from other servers. By default, only media attachements
are removed.
The --days option specifies how old media attachments have to be before The --days option specifies how old media attachments have to be before
they are removed. It defaults to 7 days. 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 --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
--remove-headers, all non-local profiles will be pruned irrespective of
follow status. By default, only accounts that are not followed by or
following anyone locally are pruned.
DESC DESC
# rubocop:disable Metrics/PerceivedComplexity
def remove def remove
if options[:prune_profiles] && options[:remove_headers]
say('--prune-profiles and --remove-headers should not be specified simultaneously', :red, true)
exit(1)
end
if options[:include_follows] && !(options[:prune_profiles] || options[:remove_headers])
say('--include-follows can only be used with --prune-profiles or --remove-headers', :red, true)
exit(1)
end
time_ago = options[:days].days.ago time_ago = options[:days].days.ago
dry_run = options[:dry_run] ? ' (DRY RUN)' : '' dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
processed, aggregate = parallelize_with_progress(MediaAttachment.cached.where.not(remote_url: '').where('created_at < ?', time_ago)) do |media_attachment| if options[:prune_profiles] || options[:remove_headers]
processed, aggregate = parallelize_with_progress(Account.remote.where({ last_webfingered_at: ..time_ago, updated_at: ..time_ago })) do |account|
next if !options[:include_follows] && Follow.where(account: account).or(Follow.where(target_account: account)).exists?
next if account.avatar.blank? && account.header.blank?
next if options[:remove_headers] && account.header.blank?
size = (account.header_file_size || 0)
size += (account.avatar_file_size || 0) if options[:prune_profiles]
unless options[:dry_run]
account.header.destroy
account.avatar.destroy if options[:prune_profiles]
account.save!
end
size
end
say("Visited #{processed} accounts and removed profile media totaling #{number_to_human_size(aggregate)}#{dry_run}", :green, true)
end
unless options[:prune_profiles] || options[:remove_headers]
processed, aggregate = parallelize_with_progress(MediaAttachment.cached.where.not(remote_url: '').where(created_at: ..time_ago)) do |media_attachment|
next if media_attachment.file.blank? next if media_attachment.file.blank?
size = (media_attachment.file_file_size || 0) + (media_attachment.thumbnail_file_size || 0) size = (media_attachment.file_file_size || 0) + (media_attachment.thumbnail_file_size || 0)
@ -44,6 +86,7 @@ module Mastodon
say("Removed #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run}", :green, true) say("Removed #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run}", :green, true)
end end
end
option :start_after option :start_after
option :prefix option :prefix
@ -183,6 +226,7 @@ module Mastodon
say("Removed #{removed} orphans (approx. #{number_to_human_size(reclaimed_bytes)})#{dry_run}", :green, true) say("Removed #{removed} orphans (approx. #{number_to_human_size(reclaimed_bytes)})#{dry_run}", :green, true)
end end
# rubocop:enable Metrics/PerceivedComplexity
option :account, type: :string option :account, type: :string
option :domain, type: :string option :domain, type: :string
@ -269,7 +313,7 @@ module Mastodon
def lookup(url) def lookup(url)
path = Addressable::URI.parse(url).path path = Addressable::URI.parse(url).path
path_segments = path.split('/')[2..-1] path_segments = path.split('/')[2..]
path_segments.delete('cache') path_segments.delete('cache')
unless [7, 10].include?(path_segments.size) unless [7, 10].include?(path_segments.size)