Add clean_library command

This adds a clean_library command to that will scan for deleted
files and offer to remove them from the database, and then offer to remove
tracks with no associated files as well as empty albums and empty artists.

Fixes #362.
Signed-off-by: Toke Høiland-Jørgensen's avatarToke Høiland-Jørgensen <>
parent 14d8224d
Pipeline #1943 failed with stages
in 21 seconds
import glob
import os
from django.conf import settings
from django.core.files import File
from import BaseCommand, CommandError
from django.db.models import Count
from import models, tasks
from funkwhale_api.users.models import User
class Command(BaseCommand):
help = "Clean library of missing files and empty albums/artists"
def add_arguments(self, parser):
help="Don't ask for confirmation before deleting data",
def confirm(self, message):
if self.confirm_enabled:
message = message + "\n\nType 'yes' to continue, or 'no' to cancel: "
return input(message) == "yes"
return True
def handle(self, *args, **options):
self.stdout.write("Cleaning library...")
self.confirm_enabled = options["confirm"]
files = self.find_missing_files()
self.stdout.write("- Found {} files that no longer exist.".format(
if files and self.confirm("Remove files from database?"):
i = 0
for tf in files:
i += 1
self.stdout.write(" Removed {} files from database".format(i))
tracks = self.find_empty_tracks()
"- Found {} tracks with no associated files.".format(len(tracks)))
if tracks and self.confirm("Remove tracks?"):
i = 0
for t in tracks:
i += 1
self.stdout.write(" Removed {} tracks from database".format(i))
albums = self.find_empty_albums()
"- Found {} empty albums.".format(len(albums)))
if albums and self.confirm("Remove albums?"):
i = 0
for a in albums:
i += 1
self.stdout.write(" Removed {} albums from database".format(i))
artists = self.find_empty_artists()
"- Found {} empty artists.".format(len(artists)))
if artists and self.confirm("Remove artists?"):
i = 0
for a in artists:
i += 1
self.stdout.write(" Removed {} artists from database".format(i))
def find_missing_files(self):
missing_files = []
for tf in models.TrackFile.objects.filter(source__startswith="file://"):
fname = tf.source.replace("file://", "")
if not os.path.exists(fname):
return missing_files
def find_empty(self, model, field):
return model.objects.annotate(num_obj=Count(field)).filter(num_obj=0)
def find_empty_tracks(self):
return self.find_empty(models.Track, 'files')
def find_empty_albums(self):
return self.find_empty(models.Album, 'tracks')
def find_empty_artists(self):
return models.Artist.objects.annotate(
Command line action to clean library of missing files and empty albums/artists
The new clean_library command to will look through all the local files
stored in the database and offer to remove those that no longer exists. It will
also look for tracks with no associated files and empty albums and artists, and
offer to remove those.
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment