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:
parent
d2e626dd1d
commit
5de8a60c1a
|
@ -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/**/*'
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue