Repository navigation
Expand file tree
/
Copy pathzip-mtgo-logs.py
More file actions
144 lines (129 loc) · 5.71 KB
/
Copy pathzip-mtgo-logs.py
File metadata and controls
144 lines (129 loc) · 5.71 KB
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
import argparse
import os
import sys
import zipfile
from typing import List, Dict, Tuple
def get_logtype_from_filename(filename: str) -> str:
"""
Classify a file by name as 'GameLog', 'DraftLog', or 'NA'.
Mirrors the original heuristic while being explicit and side-effect free.
"""
# GameLog example: contains 'Match_GameLog_' and ends with .dat
if ('Match_GameLog_' in filename) and filename.lower().endswith('.dat') and len(filename) >= 30:
return 'GameLog'
# DraftLog heuristic from original script
if (filename.count('.') != 3) or (filename.count('-') != 4) or (not filename.lower().endswith('.txt')):
return 'NA'
split_dash = filename.split('-')
try:
# Year part (from date YYYY.MM.DD) must be 4 digits; next segment is numeric ID (e.g. 10082) allow 4-6 digits
year_part = split_dash[1].split('.')[0]
id_part = split_dash[2]
if len(year_part) != 4 or not (4 <= len(id_part) <= 6 and id_part.isdigit()):
return 'NA'
except Exception:
return 'NA'
return 'DraftLog'
EXCLUDE_TOP_LEVEL_DIRS = {
'Windows',
'Program Files',
'Program Files (x86)',
'ProgramData',
'$Recycle.Bin',
'System Volume Information',
'Recovery',
'PerfLogs',
'MSOCache',
'Documents and Settings',
}
def is_drive_root(path: str) -> bool:
p = os.path.abspath(path)
# Typical Windows drive root is like 'C:\\'
return len(p) == 3 and p[1] == ':' and p.endswith('\\')
def iter_log_files(root_path: str):
"""
Walk root_path and yield absolute file paths that look like MTGO logs.
- Skips known system directories when scanning the drive root (e.g., C:\\).
- Does not follow symlinks.
"""
def _ignore_error(err: BaseException):
# Silently ignore inaccessible directories/files during walk
return None
for current_root, dirnames, filenames in os.walk(
root_path, topdown=True, onerror=_ignore_error, followlinks=False
):
if is_drive_root(current_root):
# prune noisy/system directories at the top level only
dirnames[:] = [d for d in dirnames if d not in EXCLUDE_TOP_LEVEL_DIRS]
for name in filenames:
kind = get_logtype_from_filename(name)
if kind in {'GameLog', 'DraftLog'}:
yield os.path.join(current_root, name)
def create_zip_of_logs(root_path: str, output_zip_path: str) -> Tuple[int, Dict[str, int]]:
"""
Find all MTGO log files under root_path and write them to output_zip_path.
- Preserves file modification times inside the ZIP (zipfile.write uses file mtime)
- Stores only filenames in the ZIP (no folder structure).
- When the same filename appears in multiple folders, keeps only the file with the oldest creation time.
Returns the number of files added.
"""
# First pass: for each basename, keep only the path with the oldest creation time
chosen: Dict[str, Tuple[str, str]] = {} # basename -> (full_path, kind)
for full_path in iter_log_files(root_path):
try:
base = os.path.basename(full_path)
kind = get_logtype_from_filename(base)
ctime = os.path.getctime(full_path)
if base not in chosen or ctime < os.path.getctime(chosen[base][0]):
chosen[base] = (full_path, kind)
except Exception as exc:
print(f"Warning: failed to read {full_path}: {exc}", file=sys.stderr)
files_added = 0
type_counts: Dict[str, int] = {"GameLog": 0, "DraftLog": 0}
with zipfile.ZipFile(output_zip_path, mode='w', compression=zipfile.ZIP_DEFLATED) as zf:
for base, (full_path, kind) in chosen.items():
try:
zf.write(full_path, arcname=base)
files_added += 1
type_counts[kind] += 1
except Exception as exc:
print(f"Warning: failed to add {full_path}: {exc}", file=sys.stderr)
return files_added, type_counts
def main(argv: List[str]) -> int:
parser = argparse.ArgumentParser(
description=(
'Zip MTGO GameLog and DraftLog files while preserving original timestamps. '
'Scans a chosen root folder (default: your user profile) and writes MTGO-Log-Files.zip.'
)
)
default_root = os.environ.get('USERPROFILE') or os.getcwd()
parser.add_argument('--root', type=str, default=default_root, help='Root folder to scan (default: %%USERPROFILE%%)')
parser.add_argument('--output', type=str, default=os.path.join(os.getcwd(), 'MTGO-Log-Files.zip'), help='Output ZIP path')
args = parser.parse_args(argv)
root_path = os.path.abspath(args.root)
output_zip_path = os.path.abspath(args.output)
if not os.path.exists(root_path) or not os.path.isdir(root_path):
print(f"Error: root path does not exist or is not a directory: {root_path}", file=sys.stderr)
return 2
print(f"Scanning for MTGO logs under: {root_path}")
print(f"Writing ZIP to: {output_zip_path}")
count, type_counts = create_zip_of_logs(root_path, output_zip_path)
if count == 0:
print("No MTGO GameLog or DraftLog files found.")
exit_code = 1
else:
print(f"Done. Added {count} file(s) to {output_zip_path}")
print("Summary:")
print(f" GameLogs: {type_counts.get('GameLog', 0)}")
print(f" DraftLogs: {type_counts.get('DraftLog', 0)}")
print(f" Total: {count}")
exit_code = 0
# If packaged as an executable (e.g., PyInstaller), pause before exit so users can read output
if getattr(sys, 'frozen', False):
try:
input("\nPress Enter to exit...")
except EOFError:
pass
return exit_code
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))