summaryrefslogtreecommitdiff
path: root/ishtar_common/management/commands/media_find_missing_files.py
blob: 61a7919e2082a883653cd00297c02cac17bc0bc5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2019  Étienne Loks  <etienne.loks_AT_peacefrogsDOTnet>

# 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 <http://www.gnu.org/licenses/>.

# 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_alt_media_dir):
            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")