1
|
|
|
# -*- coding: utf-8 -*- |
2
|
1 |
|
"""Rename file name. |
3
|
|
|
""" |
4
|
1 |
|
import os |
5
|
1 |
|
import os.path |
6
|
1 |
|
import re |
7
|
|
|
|
8
|
1 |
|
from .core import Common |
9
|
1 |
|
from .exceptions import ( |
10
|
|
|
InvalidNumberParameterTypeError, |
11
|
|
|
ChangeFileNameOSError, |
12
|
|
|
InvalidImageParameterTypeError, |
13
|
|
|
) |
14
|
1 |
|
from .utils.logging import get_logger |
15
|
|
|
|
16
|
1 |
|
_logger = get_logger("change_filename") |
17
|
|
|
|
18
|
|
|
|
19
|
1 |
|
class ChangeFilename(Common): |
20
|
|
|
"""Change file name to only digit name. |
21
|
|
|
""" |
22
|
|
|
|
23
|
1 |
|
def __init__(self, directory_path, digits, extension): |
24
|
|
|
"""Constructor |
25
|
|
|
|
26
|
|
|
Args: |
27
|
|
|
directory_path (str): Target directory path. |
28
|
|
|
digits (str): Regex target digit. |
29
|
|
|
extension (str): Target file extension. |
30
|
|
|
""" |
31
|
1 |
|
super().__init__() |
32
|
1 |
|
self.__directory_path = directory_path |
33
|
1 |
|
self.__digits = digits |
34
|
1 |
|
self.__exist_files = [] |
35
|
1 |
|
self.__extension = self._convert_extension_with_dot(extension) |
36
|
1 |
|
self.__max_digit = self._check_digit_format(self.__digits) |
37
|
1 |
|
self.__regex_ext = re.compile(self.__extension) |
38
|
1 |
|
os.chdir(self.__directory_path) |
39
|
|
|
|
40
|
1 |
|
@staticmethod |
41
|
|
|
def _create_new_name(num, digit, extension): |
42
|
|
|
"""Create only digit file name. |
43
|
|
|
|
44
|
|
|
Args: |
45
|
|
|
num (Match): Match object |
46
|
|
|
digit (int): digit number |
47
|
|
|
extension (str): File Extension like ".jpg" |
48
|
|
|
|
49
|
|
|
Returns: |
50
|
|
|
str: Only digit file name. |
51
|
|
|
|
52
|
|
|
Raises: |
53
|
|
|
InvalidNumberParameterTypeError: If input digit is not Match object. |
54
|
|
|
|
55
|
|
|
""" |
56
|
1 |
|
try: |
57
|
1 |
|
return num.group().zfill(digit) + extension |
58
|
1 |
|
except AttributeError as attribute_error: |
59
|
1 |
|
_logger.exception(attribute_error) |
60
|
1 |
|
raise InvalidNumberParameterTypeError() from attribute_error |
61
|
|
|
|
62
|
1 |
|
def __check_exist_file(self, new_name, old_name, append_list): |
63
|
|
|
"""Check current directory and exists same name file, return true. |
64
|
|
|
Args: |
65
|
|
|
new_name (str): Target file name. |
66
|
|
|
old_name (str): before name |
67
|
|
|
append_list (bool): If true, make duplicate name list |
68
|
|
|
|
69
|
|
|
Returns: |
70
|
|
|
bool: If success, return true |
71
|
|
|
|
72
|
|
|
Raises: |
73
|
|
|
ChangeFileNameOSError: If input a bad filename or path. |
74
|
|
|
""" |
75
|
1 |
|
try: |
76
|
1 |
|
if os.path.isfile(new_name): |
77
|
1 |
|
_logger.info("File Exist: {filename}".format(filename=new_name)) |
78
|
1 |
|
if old_name != new_name and append_list: |
79
|
1 |
|
_logger.debug( |
80
|
|
|
"Append exist files list: {filename}".format(filename=old_name) |
81
|
|
|
) |
82
|
1 |
|
self.__exist_files.append(old_name) |
83
|
1 |
|
return True |
84
|
|
|
else: |
85
|
1 |
|
return False |
86
|
1 |
|
except OSError as os_error: |
87
|
1 |
|
_logger.exception(os_error) |
88
|
1 |
|
raise ChangeFileNameOSError() from os_error |
89
|
|
|
|
90
|
1 |
|
def __input_new_file_name(self, old_name, overwrite): |
91
|
|
|
"""Provide input command prompt. |
92
|
|
|
Args: |
93
|
|
|
old_name (str): Old name |
94
|
|
|
overwrite (bool): If true, ignore exist file name. |
95
|
|
|
|
96
|
|
|
Returns: |
97
|
|
|
str: new file name. |
98
|
|
|
""" |
99
|
1 |
|
new_name = input("Input new name? {old_name} =>".format(old_name=old_name)) |
100
|
1 |
|
while self.__check_exist_file(new_name, new_name, False) and not overwrite: |
101
|
1 |
|
_logger.warn("Already file exist: {new_name}") |
102
|
1 |
|
new_name = input( |
103
|
|
|
"Input Another file name {old_name} => ?".format(old_name=old_name) |
104
|
|
|
) |
105
|
1 |
|
return new_name |
106
|
|
|
|
107
|
1 |
|
def filename_to_digit_number(self): |
108
|
|
|
"""Change file name to only digit name. |
109
|
|
|
|
110
|
|
|
Change file name contains digit like "foo001bar.txt" to |
111
|
|
|
only digit file name like "001.txt". |
112
|
|
|
|
113
|
|
|
Returns: |
114
|
|
|
List[str]: Skipping files list by exists same name. |
115
|
|
|
""" |
116
|
1 |
|
count = 0 |
117
|
1 |
|
files = self._make_file_list(self.__directory_path) |
118
|
|
|
|
119
|
1 |
|
_logger.info( |
120
|
|
|
"Target directory: {directory_path}".format( |
121
|
|
|
directory_path=self.__directory_path |
122
|
|
|
) |
123
|
|
|
) |
124
|
1 |
|
_logger.info("Digit: {digits}".format(digits=self.__digits)) |
125
|
1 |
|
_logger.info("Extension: {extension}".format(extension=self.__extension)) |
126
|
1 |
|
_logger.info("-" * 55) |
127
|
|
|
|
128
|
1 |
|
for file in files: |
129
|
1 |
|
self._rename_digit_filename(file) |
130
|
1 |
|
count += 1 |
131
|
|
|
|
132
|
1 |
|
_logger.info("-" * 55) |
133
|
1 |
|
_logger.info("Finished! Rename file count: {count}".format(count=count)) |
134
|
1 |
|
return self.__exist_files |
135
|
|
|
|
136
|
1 |
|
def async_filename_to_digit_number(self): |
137
|
|
|
"""Change file name to only digit name on async. |
138
|
|
|
|
139
|
|
|
If use this function, a little bit speedy |
140
|
|
|
compare with filename_to_digit_number function. |
141
|
|
|
|
142
|
|
|
Returns: |
143
|
|
|
List[str]: Skipping files list by exists same name. |
144
|
|
|
""" |
145
|
1 |
|
files = self._make_file_list(self.__directory_path) |
146
|
|
|
|
147
|
1 |
|
_logger.info( |
148
|
|
|
"Target directory: {directory_path}".format( |
149
|
|
|
directory_path=self.__directory_path |
150
|
|
|
) |
151
|
|
|
) |
152
|
1 |
|
_logger.info("Digit: {digits}".format(digits=self.__digits)) |
153
|
1 |
|
_logger.info("Extension: {extension}".format(extension=self.__extension)) |
154
|
1 |
|
_logger.info("-" * 55) |
155
|
|
|
|
156
|
1 |
|
loop = self._get_eventloop() |
157
|
1 |
|
queue = self._create_task_queue(files) |
158
|
1 |
|
loop.run_until_complete( |
159
|
|
|
self._execute_queuing_tasks(queue, loop, None, self._rename_digit_filename) |
160
|
|
|
) |
161
|
1 |
|
_logger.info("-" * 55) |
162
|
1 |
|
_logger.info("Finished! Async rename") |
163
|
1 |
|
return self.__exist_files |
164
|
|
|
|
165
|
1 |
|
def _rename_digit_filename(self, file): |
166
|
1 |
|
num = self._check_serial_number(file, self.__digits) |
167
|
1 |
|
if not self._check_skip_file(file, self.__regex_ext, num): |
168
|
1 |
|
new_name = self._create_new_name(num, self.__max_digit, self.__extension) |
169
|
1 |
|
if not self.__check_exist_file(new_name, file, True): |
170
|
1 |
|
self._rename_file(file, new_name) |
171
|
1 |
|
return True |
172
|
|
|
|
173
|
1 |
|
def change_name_manually(self, overwrite=False): |
174
|
|
|
"""Change filename manually looking exist_file list. |
175
|
|
|
|
176
|
|
|
After changing file name for filename_to_digit_number() method, |
177
|
|
|
duplicate file name change manually. |
178
|
|
|
|
179
|
|
|
Args: |
180
|
|
|
overwrite (bool): If true, overwrite file name even if exist same name file. |
181
|
|
|
|
182
|
|
|
Returns: |
183
|
|
|
bool: If success, return True. |
184
|
|
|
|
185
|
|
|
""" |
186
|
|
|
|
187
|
1 |
|
def _flag_yes(): |
188
|
1 |
|
new_name_y = self.__input_new_file_name(file, overwrite) |
189
|
1 |
|
self._rename_file(file, new_name_y) |
190
|
1 |
|
_logger.info( |
191
|
|
|
"Rename: {old_name} => {new_name} \n".format( |
192
|
|
|
old_name=file, new_name=new_name_y |
193
|
|
|
) |
194
|
|
|
) |
195
|
|
|
|
196
|
1 |
|
def _flag_rename(): |
197
|
1 |
|
flag_delete = input( |
198
|
|
|
"Will be {file} deleted? " "OK? (y/n/c/r)".format(file=file) |
199
|
|
|
) # y="Yes" n="No" c="check" r="rename" |
200
|
1 |
|
if flag_delete == "c": |
201
|
1 |
|
self._check_image_file(file) |
202
|
1 |
|
elif flag_delete in ("Y", "y"): |
203
|
1 |
|
self._remove_file(file) |
204
|
1 |
|
elif flag_delete == "r": |
205
|
1 |
|
new_name_rename = self.__input_new_file_name(file, overwrite) |
206
|
1 |
|
self._rename_file(file, new_name_rename) |
207
|
|
|
else: |
208
|
1 |
|
_logger.info("Leave file: {file}\n".format(file=file)) |
209
|
|
|
|
210
|
1 |
|
_logger.info("-" * 55) |
211
|
1 |
|
_logger.info("Manually determine file names duplicated by the serial number\n") |
212
|
1 |
|
for file in self.__exist_files: |
213
|
1 |
|
_logger.info("File name: {file_name}") |
214
|
1 |
|
flag = input( |
215
|
|
|
"Does it rename? (y/n/r)".format(file_name=file) |
216
|
|
|
) # y="Yes" n="No" r="Remove" |
217
|
1 |
|
if flag in ("Y", "y"): |
218
|
1 |
|
_flag_yes() |
219
|
1 |
|
elif flag == "r": |
220
|
1 |
|
try: |
221
|
1 |
|
_flag_rename() |
222
|
1 |
|
except InvalidImageParameterTypeError as invalid_image_parameter_type: |
223
|
1 |
|
_logger.warn(invalid_image_parameter_type) |
224
|
1 |
|
_logger.info("Skip..") |
225
|
|
|
else: |
226
|
1 |
|
_logger.info("Leave file: {file}\n".format(file=file)) |
227
|
1 |
|
_logger.info("Finished.") |
228
|
1 |
|
return True |
229
|
|
|
|
230
|
1 |
|
def add_before_after_str(self, before, after): |
231
|
|
|
"""Add file name specify string. |
232
|
|
|
|
233
|
|
|
After changing file name for filename_to_digit_number() method, |
234
|
|
|
add specify string before or after file name. |
235
|
|
|
|
236
|
|
|
Args: |
237
|
|
|
before (str): String before file name |
238
|
|
|
after (str): String after file name |
239
|
|
|
|
240
|
|
|
Returns: |
241
|
|
|
bool: If success, return True. |
242
|
|
|
|
243
|
|
|
""" |
244
|
1 |
|
_logger.info("-" * 55) |
245
|
1 |
|
files = self._make_file_list(self.__directory_path) |
246
|
1 |
|
if before is not None: |
247
|
1 |
|
_logger.info("Add {before} before serial digit".format(before=before)) |
248
|
|
|
else: |
249
|
1 |
|
before = "" |
250
|
1 |
|
if after is not None: |
251
|
1 |
|
_logger.info("Add {after} before serial digit".format(after=after)) |
252
|
|
|
else: |
253
|
1 |
|
after = "" |
254
|
|
|
|
255
|
1 |
|
for file in files: |
256
|
1 |
|
num = self._check_serial_number(file, self.__digits) |
257
|
1 |
|
if not num: |
258
|
1 |
|
_logger.debug("Skip(No number): {filename}".format(filename=str(file))) |
259
|
|
|
else: |
260
|
1 |
|
if self.__regex_ext.search(file): |
261
|
1 |
|
_, center, _ = self._split_dir_root_ext(file) |
262
|
1 |
|
new_name = before + center + after + self.__extension |
263
|
1 |
|
if self.__check_exist_file(new_name, new_name, False): |
264
|
1 |
|
pass |
265
|
|
|
else: |
266
|
1 |
|
self._rename_file(file, new_name) |
267
|
|
|
return True |
268
|
|
|
|