Add files via upload

This commit is contained in:
Yutaka Sawada
2023-03-20 14:09:49 +09:00
committed by GitHub
parent 328c9ff49b
commit 6d57e38739
8 changed files with 2653 additions and 0 deletions

428
alpha/tool/diff_folder.py Normal file
View File

@@ -0,0 +1,428 @@
import sys
import os
import glob
import re
import subprocess
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
# Set path of par2j
client_path = "../par2j64.exe"
gui_path = "../MultiPar.exe"
# Initialize global variables
list_dir = []
dir_index = 0;
current_dir = "./"
list_par = []
list_src = []
list_size = []
list_now = []
# Check a folder exists in list already
def check_dir_exist(one_path):
global list_dir
for list_item in list_dir:
if os.path.samefile(one_path, list_item):
return True
return False
# Search PAR2 sets in a folder
def search_par_set(one_path):
global list_par, list_src, list_size, list_now
if one_path == "":
return
# Clear lists at first
list_par.clear()
list_src.clear()
list_size.clear()
list_now.clear()
for item in treeview_list.get_children():
treeview_list.delete(item)
found_base = ""
button_check.config(state=tk.DISABLED)
button_open.config(state=tk.DISABLED)
treeview_list.heading('PAR', text="Items in PAR", anchor='center')
treeview_list.heading('Now', text="Directory content", anchor='center')
# Add found PAR sets
for par_path in glob.glob(glob.escape(one_path) + "/*.par2"):
file_name = os.path.basename(par_path)
# Remove extension ".par2"
one_name = os.path.splitext(file_name)[0]
# Compare filename in case insensitive
base_name = one_name.lower()
# Remove ".vol#-#", ".vol#+#", or ".vol_#" at the last
base_name = re.sub(r'[.]vol\d*[-+_]\d+$', "", base_name)
# Confirm only one PAR2 set in the directory.
if found_base != "":
if base_name != found_base:
list_par.clear()
label_status.config(text= "There are multiple PAR sets in \"" + one_path + "\".")
return
else:
found_base = base_name
#print(file_name)
list_par.append(file_name)
if len(list_par) == 0:
label_status.config(text= "PAR set isn't found in \"" + one_path + "\".")
return
label_status.config(text= "Directory = \"" + one_path + "\", PAR file = \"" + list_par[0] + "\"")
button_check.config(state=tk.NORMAL)
button_open.config(state=tk.NORMAL)
# Check automatically
root.after(100, button_check_clicked)
# Select a folder to search PAR files
def button_folder_clicked():
global current_dir, list_dir, dir_index
if os.path.exists(current_dir) == False:
current_dir = "./"
search_dir = filedialog.askdirectory(initialdir=current_dir)
if search_dir == "":
return
# This replacing seems to be worthless.
#search_dir = search_dir.replace('\\', '/')
# Insert the folder to list
dir_count = len(list_dir)
if dir_count == 0:
dir_index = 0
list_dir.append(search_dir)
dir_count += 1
else:
# Check the folder exists already
if check_dir_exist(search_dir):
dir_index = list_dir.index(search_dir)
else:
dir_index += 1
list_dir.insert(dir_index, search_dir)
dir_count += 1
if dir_count > 1:
root.title('PAR Diff ' + str(dir_index + 1) + '/' + str(dir_count))
current_dir = os.path.dirname(search_dir)
search_par_set(search_dir)
# Read text and get source file
def parse_text(output_text):
global list_src, list_size
multi_lines = output_text.split('\n')
# Search starting point of list
offset = multi_lines.index("Input File list\t:")
offset += 2
# Get file size and name
while offset < len(multi_lines):
single_line = multi_lines[offset]
if single_line == "":
break
# File size
single_line = single_line.lstrip()
fisrt_item = single_line.split()[0]
file_size = int(fisrt_item)
list_size.append(file_size)
#print(file_size)
# Compare filename in case insensitive
file_name = single_line.split("\"")[1]
file_name = file_name.lower()
list_src.append(file_name)
#print(file_name)
offset += 1
# Check PAR files and list source files
def button_check_clicked():
global list_dir, dir_index, list_par, list_src, list_size, list_now
if len(list_dir) == 0:
return
treeview_list.heading('PAR', text="? items in PAR", anchor='center')
treeview_list.heading('Now', text="Directory content", anchor='center')
search_dir = list_dir[dir_index]
if os.path.exists(search_dir) == False:
label_status.config(text= "\"" + search_dir + "\" doesn't exist.")
return
if len(list_par) == 0:
return
if os.path.exists(client_path) == False:
label_status.config(text= "Cannot call \"" + client_path + "\". Set path correctly.")
return
# Clear lists at first
list_src.clear()
list_size.clear()
list_now.clear()
for item in treeview_list.get_children():
treeview_list.delete(item)
list_lost = []
add_count = 0
diff_count = 0
# Read source files in PAR set
for par_path in list_par:
# Call par2j's list command
cmd = "\"" + client_path + "\" l /uo \"" + search_dir + "/" + par_path + "\""
res = subprocess.run(cmd, shell=True, capture_output=True, encoding='utf8')
#print("return code: {}".format(res.returncode))
#print("captured stdout: {}".format(res.stdout))
if res.returncode == 0:
#label_status.config(text= "Read \"" + par_path + "\" ok.")
parse_text(res.stdout)
break
if (len(list_src) == 0) or (len(list_src) != len(list_size)):
label_status.config(text= "Failed to read source files in the PAR set.")
return
# Get current directory-tree
for dirs, subdirs, files in os.walk(search_dir):
# Get sub-directory from base directory
sub_dir = dirs.lstrip(search_dir)
sub_dir = sub_dir.replace('\\', '/')
sub_dir = sub_dir.lstrip('/')
sub_dir = sub_dir.lower()
if sub_dir != "":
sub_dir += "/"
# Add folders
for dir_name in subdirs:
item_name = sub_dir + dir_name.lower() + "/"
list_now.append(item_name)
#print("folder", item_name)
# Add files
for file_name in files:
item_name = sub_dir + file_name
if (sub_dir == "") and (item_name in list_par):
continue
item_name = item_name.lower()
list_now.append(item_name)
#print("file:", item_name)
# Make list of missing items
for item_name in list_src:
#print(item_name)
if not item_name in list_now:
# The item doesn't exit now.
list_lost.append(item_name)
list_now.append(item_name)
# Compare lists to find additional items
list_now.sort()
for item_name in list_now:
#print(item_name)
if item_name.endswith("/"):
# This item is a folder.
if item_name in list_lost:
treeview_list.insert(parent='', index='end', values=(item_name, ""), tags='red')
elif item_name in list_src:
if not bool_diff.get():
treeview_list.insert(parent='', index='end', values=(item_name, item_name))
else:
find_flag = 0
for src_name in list_src:
if src_name.startswith(item_name):
# The folder exists as sub-directory.
find_flag = 1
break;
if find_flag == 0:
# The folder doesn't exit in PAR set.
treeview_list.insert(parent='', index='end', values=("", item_name), tags='blue')
add_count += 1;
else:
# This item is a file.
if item_name in list_lost:
treeview_list.insert(parent='', index='end', values=(item_name, ""), tags='red')
elif item_name in list_src:
file_path = search_dir + "/" + item_name
item_index = list_src.index(item_name)
file_size = os.path.getsize(file_path)
#print(item_index, list_size[item_index], file_size)
if file_size == list_size[item_index]:
if not bool_diff.get():
treeview_list.insert(parent='', index='end', values=(item_name, item_name))
else:
treeview_list.insert(parent='', index='end', values=(item_name, item_name), tags='yellow')
diff_count += 1
else:
# The file doesn't exit in PAR set.
treeview_list.insert(parent='', index='end', values=("", item_name), tags='blue')
add_count += 1;
# Number of missing or additional items
item_count = len(list_src)
lost_count = len(list_lost)
if lost_count == 0:
treeview_list.heading('PAR', text= str(item_count) + " items in PAR", anchor='center')
else:
treeview_list.heading('PAR', text= str(item_count) + " items in PAR ( " + str(lost_count) + " miss )", anchor='center')
if add_count + diff_count > 0:
if add_count == 0:
treeview_list.heading('Now', text="Directory content ( " + str(diff_count) + " diff )", anchor='center')
elif diff_count == 0:
treeview_list.heading('Now', text="Directory content ( " + str(add_count) + " add )", anchor='center')
else:
treeview_list.heading('Now', text="Directory content ( " + str(add_count) + " add, " + str(diff_count) + " diff )", anchor='center')
# If you want to see some summary, uncomment below section.
#status_text = "Directory = \"" + search_dir + "\", PAR file = \"" + par_path + "\"\nTotal items = " + str(item_count) + ". "
#if lost_count + add_count + diff_count == 0:
# status_text += "Directory and Par File structure match."
#else:
# if lost_count > 0:
# status_text += str(lost_count) + " items are missing. "
# if add_count > 0:
# status_text += str(add_count) + " items are additional. "
# if diff_count > 0:
# status_text += str(diff_count) + " items are different size."
#label_status.config(text=status_text)
# Open PAR set by MultiPar
def button_open_clicked():
global list_dir, dir_index, list_par
if len(list_dir) == 0:
return
search_dir = list_dir[dir_index]
if os.path.exists(search_dir) == False:
return
if len(list_par) == 0:
return
if os.path.exists(gui_path) == False:
label_status.config(text= "Cannot call \"" + gui_path + "\". Set path correctly.")
return
# Set command-line
# Cover path by " for possible space
par_path = search_dir + "/" + list_par[0]
cmd = "\"" + gui_path + "\" /verify \"" + par_path + "\""
# Open MultiPar GUI to see details
# Because this doesn't wait finish of MultiPar, you may open some at once.
subprocess.Popen(cmd)
# Move to next folder
def button_next_clicked():
global current_dir, list_dir, dir_index
dir_count = len(list_dir)
search_dir = ""
# Goto next directory
while dir_count > 1:
dir_index += 1
if dir_index >= dir_count:
dir_index = 0
search_dir = list_dir[dir_index]
# Check the directory exists
if os.path.exists(search_dir):
break
else:
list_dir.pop(dir_index)
dir_index -= 1
dir_count -= 1
search_dir = ""
if search_dir == "":
return
root.title('PAR Diff ' + str(dir_index + 1) + '/' + str(dir_count))
current_dir = os.path.dirname(search_dir)
search_par_set(search_dir)
# Window size and title
root = tk.Tk()
root.title('PAR Diff')
root.minsize(width=480, height=240)
# Centering window
init_width = 640
init_height = 480
init_left = (root.winfo_screenwidth() - init_width) // 2
init_top = (root.winfo_screenheight() - init_height) // 2
root.geometry('{}x{}+{}+{}'.format(init_width, init_height, init_left, init_top))
#root.geometry("640x480")
root.columnconfigure(0, weight=1)
root.rowconfigure(1, weight=1)
# Control panel
frame_top = ttk.Frame(root, padding=(3,4,3,2))
frame_top.grid(row=0, column=0, sticky=(tk.E,tk.W))
button_next = ttk.Button(frame_top, text="Next folder", width=12, command=button_next_clicked)
button_next.pack(side=tk.LEFT, padx=2)
button_folder = ttk.Button(frame_top, text="Select folder", width=12, command=button_folder_clicked)
button_folder.pack(side=tk.LEFT, padx=2)
button_check = ttk.Button(frame_top, text="Check again", width=12, command=button_check_clicked, state=tk.DISABLED)
button_check.pack(side=tk.LEFT, padx=2)
button_open = ttk.Button(frame_top, text="Open with MultiPar", width=19, command=button_open_clicked, state=tk.DISABLED)
button_open.pack(side=tk.LEFT, padx=2)
bool_diff = tk.BooleanVar()
bool_diff.set(False) # You may change initial state here.
check_diff = ttk.Checkbutton(frame_top, variable=bool_diff, text="Diff only")
check_diff.pack(side=tk.LEFT, padx=2)
# List
frame_middle = ttk.Frame(root, padding=(4,2,4,2))
frame_middle.grid(row=1, column=0, sticky=(tk.E,tk.W,tk.S,tk.N))
frame_middle.rowconfigure(0, weight=1)
frame_middle.columnconfigure(0, weight=1)
column = ('PAR', 'Now')
treeview_list = ttk.Treeview(frame_middle, columns=column, selectmode='none')
treeview_list.grid(row=0, column=0, sticky=(tk.E,tk.W,tk.S,tk.N))
treeview_list.column('#0',width=0, stretch='no')
treeview_list.heading('#0', text='')
treeview_list.heading('PAR', text="Items in PAR", anchor='center')
treeview_list.heading('Now', text="Directory content", anchor='center')
treeview_list.tag_configure('red', background='#FFC0C0')
treeview_list.tag_configure('blue', background='#80FFFF')
treeview_list.tag_configure('yellow', background='#FFFF80')
scrollbar_list = ttk.Scrollbar(frame_middle, orient=tk.VERTICAL, command=treeview_list.yview)
scrollbar_list.grid(row=0, column=1, sticky=(tk.N, tk.S))
treeview_list["yscrollcommand"] = scrollbar_list.set
xscrollbar_list = ttk.Scrollbar(frame_middle, orient=tk.HORIZONTAL, command=treeview_list.xview)
xscrollbar_list.grid(row=1, column=0, sticky=(tk.E, tk.W))
treeview_list["xscrollcommand"] = xscrollbar_list.set
# Status text
frame_bottom = ttk.Frame(root)
frame_bottom.grid(row=2, column=0, sticky=(tk.E,tk.W))
label_status = ttk.Label(frame_bottom, text='Select folder to check difference.')
label_status.pack(side=tk.LEFT, padx=2)
# When folders are specified in command-line
if len(sys.argv) > 1:
search_dir = ""
for one_argv in sys.argv[1:]:
if os.path.isdir(one_argv):
one_path = os.path.abspath(one_argv)
one_path = one_path.replace('\\', '/')
if check_dir_exist(one_path) == False:
#print("argv=", one_path)
list_dir.append(one_path)
if search_dir == "":
search_dir = one_path
dir_count = len(list_dir)
if dir_count > 1:
root.title('PAR Diff 1/' + str(dir_count))
if search_dir != "":
current_dir = os.path.dirname(search_dir)
search_par_set(search_dir)
# Show window
root.mainloop()