import os from pathlib import Path from django.conf import settings from django.core.management.base import BaseCommand from apps.library.models import MediaLocation from apps.library.services import ALLOWED_EXTENSIONS, index_file class Command(BaseCommand): help = "Scan the watched folder and index media files into the library." def add_arguments(self, parser): parser.add_argument( "--prune", action="store_true", help="Remove locations whose files no longer exist on disk.", ) def handle(self, *args, **options): folder = Path(settings.WATCHED_FOLDER) if not folder.is_dir(): self.stderr.write( self.style.ERROR(f"Watched folder does not exist: {folder}") ) return self.stdout.write(f"Scanning {folder} ...") seen = set() new_items = 0 new_locations = 0 updated_locations = 0 skipped = 0 for root, dirs, files in os.walk(folder): dirs.sort() for name in sorted(files): if Path(name).suffix.lower() not in ALLOWED_EXTENSIONS: continue path = Path(root) / name try: _, created_item, created_location = index_file(path, folder) except OSError as exc: skipped += 1 self.stderr.write(f"Skipped {path}: {exc}") continue seen.add(str(path)) new_items += int(created_item) if created_location: new_locations += 1 else: updated_locations += 1 processed = len(seen) if processed % 200 == 0: self.stdout.write(f"Processed {processed} files ...") if options["prune"]: stale = MediaLocation.objects.exclude(path__in=seen) pruned = stale.count() stale.delete() self.stdout.write(f"Pruned {pruned} missing location(s).") self.stdout.write( self.style.SUCCESS( f"Done. {len(seen)} file(s) on disk, " f"{new_items} new item(s), " f"{new_locations} new location(s), " f"{updated_locations} refreshed, " f"{skipped} skipped." ) )