Completed
Push — master ( cb1112...92ac7a )
by Andreas
15s queued 11s
created

CMS.proc_thread()   A

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nop 0
1
"""Car Music Sorter."""
2
import gettext
3
import os
4
import re
5
import shutil
6
import w_settings
7
8
from threading import Thread
9
from typing import Union
10
from sys import exit
11
from tkinter import Tk, PhotoImage, Menu, LabelFrame
12
from tkinter import Toplevel, messagebox
13
from tkinter.ttk import Button, Label, Progressbar
14
from pathlib import Path
15
from f_getconfig import getconfig
16
from f_logging import writelog
17
18
import tkinter.filedialog as fd
19
input_dir: str = ''
20
output_dir: str = ''
21
source_file = []
22
23
24
# BEGIN FUNCTIONS #
25
# FIXME: Вынести по возможности в отдельные файлы
26
# Определение исходной и целевой директорий
27
def workdirs(param: str) -> None:
28
    """Открывает диалог выбора директории."""
29
    if param == 'indir':
30
        global input_dir
31
        input_dir = fd.askdirectory(title = _('Open source directory'))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable _ does not seem to be defined.
Loading history...
32
        if input_dir != '':
33
            source_label.config(text = f'...{path_short(input_dir, 2)}')
34
            writelog(_('Input DIR set to: ') + input_dir)
35
        else:
36
            input_dir = ''
37
    elif param == 'outdir':
38
        global output_dir
39
        output_dir = fd.askdirectory(title = _('Set destination directory'))
40
        if output_dir != '':
41
            dest_label.config(text = f'...{path_short(output_dir, 2)}')
42
            writelog(_('Output DIR set to: ') + output_dir)
43
        else:
44
            output_dir = ''
45
    elif param == 'clear':
46
        source_label.config(text = _('Input DIR not defined'))
47
        dest_label.config(text = _('Output DIR not defined'))
48
        main_progressbar['value'] = 0
49
        input_dir = output_dir = ''
50
51
52
def path_short(path_string: str, len: int) -> Union[str, Path]:
53
    """Сокращает путь для корректного отображения в лейбле."""
54
    return Path(*Path(path_string).parts[-len:])
55
56
57
# Вызов "О программе"
58
def popup_about(vers: str) -> None:
59
    """Открывает окно 'О программе'."""
60
# Центровка окна
61
    main_width = 400
62
    main_height = 150
63
    center_x_pos = int(window.winfo_screenwidth() / 2) - main_width
64
    center_y_pos = int(window.winfo_screenheight() / 2) - main_height
65
66
    popup = Toplevel()
67
    popup.geometry(f'{main_width}x{main_height}+{center_x_pos}+{center_y_pos}')
68
    popup.title(_('About'))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable _ does not seem to be defined.
Loading history...
69
    imagepath = 'data/imgs/main.png'
70
    img = PhotoImage(file = imagepath)
71
    poplabel1 = Label(popup, image = img)
72
    poplabel1.grid(sticky = 'W', column = 0, row = 0, rowspan = 2)
73
74
    name_vers_str = 'Car Music Sorter\n\n' + _('Version: ') + vers
75
    author_github = 'https://github.com/intervisionlord'
76
    prog_author = _('\nAuthor: ') + 'Intervision\nGithub: ' + author_github
77
    poplabel_maindesc = Label(popup,
78
                              text = name_vers_str + prog_author,
79
                              justify = 'left')
80
    poplabel_maindesc.grid(sticky = 'W', column = 1, row = 0)
81
# Автор иконок
82
    icons_author = _('Icons: ') + 'icon king1 ' + _('on') + ' freeicons.io'
83
    poplabel_icons = Label(popup, text = icons_author, justify = 'left')
84
    poplabel_icons.grid(sticky = 'W', column = 1, row = 1)
85
86
    popup.grab_set()
87
    popup.focus_set()
88
    popup.wait_window()
89
90
91
# Основные операции
92
def check_paths() -> None:
93
    """Проверяет, что все пути заданы корректно и запускает копирование."""
94
    if input_dir == '' or output_dir == '':
95
        writelog(_('Input DIR or Output DIR are not defined!'))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable _ does not seem to be defined.
Loading history...
96
    elif input_dir == output_dir:
97
        writelog(_('Input DIR and Output DIR must be different!'))
98
        return
99
    else:
100
        for path, subdirs, files in os.walk(input_dir):
101
            for file in files:
102
                # Перегоняем MP3 без лайвов и ремиксов в целевую директорию.
103
                filtered = re.search(r'^(?!(.*[Rr]emix.*|.*[Ll]ive.*)).*mp3',
104
                                     file)
105
                if filtered is not None:
106
                    source_file.append(f'{path}/{filtered.group(0)}')
107
    main_progressbar['maximum'] = len(source_file)
108
    for files in source_file:
109
        maincopy(files, Path(output_dir))
110
    source_file.clear()
111
112
113
def proc_thread():
114
    """Запускает процесс в отдельном потоке."""
115
    forked_thread = Thread(target = processing)
116
    forked_thread.start()
117
118
119
def processing() -> None:
120
    """Удаляет ремиксы и лайвы."""
121
    check_paths()
122
# Удаление ремиксов и лайвов
123
# TODO: Depricated
124
    liveregexp = r'.*\(.*[Rr]emix.*\).*|.*\(.*[Ll]ive.*\).*'
125
    for files in os.walk(output_dir):
126
        for file in files[2]:
127
            try:
128
                source_file.append(re.search(liveregexp, file).group(0))
129
            except Exception:
130
                pass
131
    for file in source_file:
132
        writelog(_('Removing Remix: ') + file)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable _ does not seem to be defined.
Loading history...
133
        os.remove(f'{output_dir}/{file}')
134
        main_progressbar['value'] = main_progressbar['value'] + 1
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable main_progressbar does not seem to be defined.
Loading history...
135
        window.update_idletasks()
136
    source_file.clear()  # Очищаем список
137
    polish_filenames()
138
139
140
def polish_filenames() -> None:
141
    """Удаляет из имен треков мусор."""
142
# Готовим список свежепринесенных файлов с вычищенными ремиксами и лайвами
143
    for files in os.walk(output_dir):
144
        for file in files[2]:
145
            try:
146
                source_file.append(file)
147
            except Exception:
148
                pass
149
150
# Убираем из имен файлов мусор (номера треков в различном формате)
151
    main_progressbar['maximum'] = (main_progressbar['maximum'] +
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable main_progressbar does not seem to be defined.
Loading history...
152
                                   len(source_file))
153
    trashregexp = r'^[\d{1,2}\s\-\.]*'
154
    for file in source_file:
155
        new_file = re.sub(trashregexp, '', file)
156
        shutil.move(f'{output_dir}/{file}', f'{output_dir}/{new_file}')
157
        main_progressbar['value'] = main_progressbar['value'] + 1
158
        window.update_idletasks()
159
    source_file.clear()
160
    writelog(_('Completed!'))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable _ does not seem to be defined.
Loading history...
161
    messagebox.showinfo(_('Information'),
162
                        _('Completed!'))
163
164
165
# Копируем файлы
166
def maincopy(files: list[str], output_dir: Path) -> None:
167
    """Копирует файлы."""
168
    writelog(f'{files}')
169
    filename = str.split(str(files), '/')
170
    writelog(filename[-1])
171
    shutil.copyfile(f'{files}', f'{output_dir}/{filename[-1]}')
172
    main_progressbar['value'] = main_progressbar['value'] + 1
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable main_progressbar does not seem to be defined.
Loading history...
173
    window.update_idletasks()
174
# END FUNCTIONS #
175
176
177
# Вводим основные переменные
178
vers = getconfig()['core']['version']
179
langcode = getconfig()['settings']['locale']
180
# Локализация
181
gettext.translation('CarMusicSorter', localedir='l10n',
182
                    languages=[langcode]).install()
183
writelog('init')
184
# Рисуем окно
185
window = Tk()
186
window.iconphoto(True, PhotoImage(file = 'data/imgs/main.png'))
187
window.geometry('370x270')
188
window.eval('tk::PlaceWindow . center')
189
window.title('Car Music Sorter')
190
window.resizable(False, False)
191
192
# Пути к оформлению
193
sourceicon = PhotoImage(file = 'data/imgs/20source.png')
194
desticon = PhotoImage(file = 'data/imgs/20dest.png')
195
launchicon = PhotoImage(file = 'data/imgs/20ok.png')
196
clearicon = PhotoImage(file = 'data/imgs/20clear.png')
197
198
# Основное меню
199
menu = Menu(window)
200
menu_about = Menu(menu, tearoff = 0)
201
menu_file = Menu(menu, tearoff = 0)
202
menu.add_cascade(label = _('File'), menu = menu_file)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable _ does not seem to be defined.
Loading history...
203
menu.add_cascade(label = _('Info'), menu = menu_about)
204
205
# Элементы меню
206
menu_about.add_command(label = _('About'),
207
                       command = lambda: popup_about(vers),
208
                       accelerator = 'F1')
209
menu_file.add_command(label = _('Input Dir'),
210
                      command = lambda: workdirs('indir'),
211
                      accelerator = 'CTRL+O')
212
menu_file.add_command(label = _('Output Dir'),
213
                      command = lambda: workdirs('outdirs'),
214
                      accelerator = 'CTRL+D')
215
menu_file.add_command(label = _('Clear'),
216
                      command = lambda: workdirs('clear'),
217
                      accelerator = 'CTRL+R')
218
menu_file.add_separator()
219
menu_file.add_command(label = _('Settings'),
220
                      command = w_settings.popup_settings)
221
menu_file.add_separator()
222
menu_file.add_command(label = _('Exit'),
223
                      command = exit,
224
                      accelerator = 'CTRL+E')
225
226
# Биндим хоткеи к функциям
227
menu_file.bind_all('<Command-o>', lambda event: workdirs('indir'))
228
menu_file.bind_all('<Command-d>', lambda event: workdirs('outdir'))
229
menu_file.bind_all('<Command-r>', lambda event: workdirs('clear'))
230
menu_file.bind_all('<Command-e>', exit)
231
232
menu_about.bind_all('<F1>', lambda event: popup_about(vers))
233
window.config(menu = menu)
234
235
# Строим элеметны основного окна и группы
236
first_group = LabelFrame(window, text = _('IO Directories'))
237
238
first_group.grid(sticky = 'WE', column = 0, row = 0, padx = 5, pady = 10,
239
                 ipadx = 2, ipady = 4)
240
241
operation_group = LabelFrame(window, text = _('Operations'))
242
operation_group.grid(sticky = 'WE', column = 0, row = 3, padx = 5, pady = 5,
243
                     ipadx = 5, ipady = 5)
244
245
progress_group = LabelFrame(window, text = _('Progress'))
246
progress_group.grid(sticky = 'WE', column = 0, row = 1, padx = 5, pady = 5,
247
                    ipadx = 0, ipady = 2, rowspan = 2)
248
249
# Прогрессбар
250
main_progressbar = Progressbar(progress_group, length = 350, value = 0,
251
                               orient = 'horizontal', mode = 'determinate')
252
main_progressbar.grid(pady = 4, column = 0, row = 1)
253
254
# Поясняющие лейблы
255
source_label_text = _('Input DIR not defined')
256
dest_label_text = _('Output DIR not defined')
257
source_label = Label(first_group, text = source_label_text, justify = 'left')
258
source_label.grid(column = 1, row = 0)
259
dest_label = Label(first_group, text = dest_label_text, justify = 'left')
260
dest_label.grid(column = 1, row = 1)
261
262
# Кнопки
263
source_button = Button(first_group, text = _('Input Dir'),
264
                       command = lambda: workdirs('indir'), image = sourceicon,
265
                       width = 20, compound = 'left')
266
source_button.grid(row = 0, ipadx = 2, ipady = 2, padx = 4)
267
268
dest_button = Button(first_group, text = _('Output Dir'),
269
                     command = lambda: workdirs('outdir'), image = desticon,
270
                     width = 20, compound = 'left')
271
dest_button.grid(row = 1, ipadx = 2, ipady = 2, padx = 4)
272
273
launch_button = Button(operation_group, text = _('Process'),
274
                       command = proc_thread, image = launchicon,
275
                       width = 20, compound = 'left')
276
launch_button.grid(column = 0, row = 2, ipadx = 2, ipady = 2, padx = 12)
277
278
clear_button = Button(operation_group, text = _('Clear'),
279
                      command = lambda: workdirs('clear'), image = clearicon,
280
                      width = 20, compound = 'left')
281
282
clear_button.grid(column = 1, row = 2, ipadx = 2, ipady = 2, padx = 0)
283
284
if __name__ == '__main__':
285
    window.mainloop()
286