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