#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2019 Étienne Loks # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # See the file COPYING for details. import os.path import sys from ishtar_common.utils import get_used_media, try_fix_file, get_broken_links from django.conf import settings from django.core.management.base import BaseCommand class Command(BaseCommand): args = '' help = 'Find missing files in media.' def add_arguments(self, parser): parser.add_argument( '--exclude', nargs='?', dest='exclude', default=None, help="Field to exclude separated with \",\". Example: " "ishtar_common.Import.imported_file," "ishtar_common.Import.imported_images" ) parser.add_argument( '--limit', nargs='?', dest='limit', default=None, help="Field to limit to. Separate field names with \",\"." ) parser.add_argument( '--try-fix', dest='try-fix', action='store_true', default=False, help='Try to find file with similar names and copy the file.') parser.add_argument( '--find-fix', dest='find-fix', action='store_true', default=False, help='Try to find file with similar names and print them.') parser.add_argument( '--hard', dest='hard', action='store_true', default=False, help='Search on the whole media dir.') parser.add_argument( '--set-empty-image', dest='set-empty-image', action='store_true', default=False, help='Set a link to empty-image for missing media.') parser.add_argument( '--set-alt-media-dir', dest='set-alt-media-dir', default=False, help='Try to set link from an alt directory for missing media.') parser.add_argument( '--no-media-dir-verification', dest='no-media-dir-verification', action='store_true', default=False, help="For set-alt-media-dir or set-empty-image do not verify media dir. WARNING: do only if you are aware " "of what you are doing this could led to security issue.") def handle(self, *args, **options): exclude = options['exclude'].split(',') if options['exclude'] else [] limit = options['limit'].split(',') if options['limit'] else [] try_fix = options['try-fix'] find_fix = options['find-fix'] hard = options['hard'] set_alt_media_dir = options["set-alt-media-dir"] set_empty_image = options["set-empty-image"] no_dir_verification = options["no-media-dir-verification"] if set_alt_media_dir and not os.path.isdir(set_alt_media_dir): self.stdout.write(f'set-alt-media-dir: "{set_alt_media_dir}" is not a valid path.\n') return if set_empty_image and set_alt_media_dir: self.stdout.write("set-empty-image and set-alt-media-dir options are not " "compatible.\n") return if try_fix and find_fix: self.stdout.write("try-fix and find-fix options are not " "compatible.\n") return if hard and not (try_fix or find_fix): self.stdout.write("hard option has no effect if try-fix or " "find-fix are not set.\n") if no_dir_verification and (not set_alt_media_dir and not set_empty_image): self.stdout.write("no-media-dir-verification option has no effect if set-empty-image or " "set-alt-media-dir are not set.\n") if try_fix: # first try to fix broken links out = None for l in get_broken_links(settings.MEDIA_ROOT): if not out: self.stdout.write("* fixes broken links:\n") out = True try_fix_file(l, hard=hard) missing = [] for media in get_used_media(exclude=exclude, limit=limit): if not os.path.isfile(media): missing.append(media) if try_fix or find_fix: if find_fix: self.stdout.write("* potential similar file:\n") else: self.stdout.write("* fixes files:\n") for item in missing[:]: source_file = try_fix_file(item, make_copy=try_fix, hard=hard) if source_file: missing.pop(missing.index(item)) sys.stdout.write( "{} <- {}\n".format(item.encode('utf-8'), source_file.encode('utf-8'))) if set_alt_media_dir: total = 0 for f in missing: f = os.path.abspath(f) if not no_dir_verification and not f.startswith(settings.MEDIA_ROOT): continue dest = os.path.join(set_alt_media_dir, f[len(settings.MEDIA_ROOT):]) if not os.path.isfile(dest): continue directory = f.split(os.path.sep)[:-1] directory = os.sep + os.path.join(*directory) if not os.path.isdir(directory): os.makedirs(directory) try: os.symlink(dest, f) except FileExistsError: continue total += 1 self.stdout.write(f"* {total} link to {set_alt_media_dir} created.\n") return if set_empty_image: empty_image = os.path.abspath( settings.LIB_BASE_PATH + "ishtar_common/static/media/images/empty-image" ) total = 0 for f in missing: f = os.path.abspath(f) if not no_dir_verification and not f.startswith(settings.MEDIA_ROOT): continue if f.lower().endswith(".jpg") or f.lower().endswith(".jpeg"): cempty_image = empty_image + ".jpg" elif f.lower().endswith(".png"): cempty_image = empty_image + ".png" else: continue directory = f.split(os.path.sep)[:-1] directory = os.sep + os.path.join(*directory) if not os.path.isdir(directory): os.makedirs(directory) os.symlink(cempty_image, f) total += 1 self.stdout.write(f"* {total} missing files set to empty image\n") elif missing: if find_fix or try_fix: self.stdout.write("* missing file with no similar file " "found:\n") for item in missing: sys.stdout.write(item + "\n") else: self.stdout.write("No missing files.\n")