funkwhale/api/funkwhale_api/music/management/commands/prune_library.py

138 wiersze
4.7 KiB
Python

from argparse import RawTextHelpFormatter
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from funkwhale_api.music import models, tasks
class Command(BaseCommand):
help = """
Remove tracks, albums and artists that are not associated with any file from the instance library:
- Tracks without uploads are deleted, if the --tracks flag is passed
- Albums without tracks are deleted, if the --albums flag is passed
- Artists without albums are deleted, if the --artists flag is passed
Tracks with associated favorites, playlists or listening won't be deleted
by default, unless you pass the corresponding --ignore-* flags.
"""
def create_parser(self, *args, **kwargs):
parser = super().create_parser(*args, **kwargs)
parser.formatter_class = RawTextHelpFormatter
return parser
def add_arguments(self, parser):
parser.add_argument(
"--no-dry-run",
action="store_false",
dest="dry_run",
default=True,
help="Disable dry run mode and apply pruning for real on the database",
)
parser.add_argument(
"--artists",
action="store_true",
dest="prune_artists",
default=False,
help="Prune artists without albums/tracks",
)
parser.add_argument(
"--albums",
action="store_true",
dest="prune_albums",
default=False,
help="Prune albums without tracks",
)
parser.add_argument(
"--tracks",
action="store_true",
dest="prune_tracks",
default=False,
help="Prune tracks without uploads",
)
parser.add_argument(
"--ignore-favorites",
action="store_false",
dest="exclude_favorites",
default=True,
help="Allow favorited tracks to be pruned",
)
parser.add_argument(
"--ignore-playlists",
action="store_false",
dest="exclude_playlists",
default=True,
help="Allow tracks included in playlists to be pruned",
)
parser.add_argument(
"--ignore-listenings",
action="store_false",
dest="exclude_listenings",
default=True,
help="Allow tracks with listening history to be pruned",
)
@transaction.atomic
def handle(self, *args, **options):
if not any(
[options["prune_albums"], options["prune_tracks"], options["prune_artists"]]
):
raise CommandError(
"You need to provide at least one of the --tracks, --albums or --artists flags"
)
if options["dry_run"]:
self.stdout.write("Dry-run on, will not commit anything")
else:
self.stdout.write("Dry-run off, *pruning for real*")
self.stdout.write("")
if options["prune_tracks"]:
prunable = tasks.get_prunable_tracks(
exclude_favorites=options["exclude_favorites"],
exclude_playlists=options["exclude_playlists"],
exclude_listenings=options["exclude_listenings"],
)
pruned_total = prunable.count()
total = models.Track.objects.count()
if options["dry_run"]:
self.stdout.write(f"Would prune {pruned_total}/{total} tracks")
else:
self.stdout.write(f"Deleting {pruned_total}/{total} tracks…")
prunable.delete()
if options["prune_albums"]:
prunable = tasks.get_prunable_albums()
pruned_total = prunable.count()
total = models.Album.objects.count()
if options["dry_run"]:
self.stdout.write(f"Would prune {pruned_total}/{total} albums")
else:
self.stdout.write(f"Deleting {pruned_total}/{total} albums…")
prunable.delete()
if options["prune_artists"]:
prunable = tasks.get_prunable_artists()
pruned_total = prunable.count()
total = models.Artist.objects.count()
if options["dry_run"]:
self.stdout.write(f"Would prune {pruned_total}/{total} artists")
else:
self.stdout.write(f"Deleting {pruned_total}/{total} artists…")
prunable.delete()
self.stdout.write("")
if options["dry_run"]:
self.stdout.write(
"Nothing was pruned, rerun this command with --no-dry-run to apply the changes"
)
else:
self.stdout.write("Pruning completed!")
self.stdout.write("")