Repository navigation
Expand file tree
/
Copy path_common.py
More file actions
160 lines (126 loc) · 3.47 KB
/
Copy path_common.py
File metadata and controls
160 lines (126 loc) · 3.47 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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import os
import re
import unicodedata
from mtattr import Attr
from mtfs import read_json_file
from mtlogger import logger
from _constants import DIR_BLACKLIST, GENERIC_EXCLUSION_FILE
def validate_dir_paths(
dir_paths: list[str],
):
for dir_path in dir_paths:
if not os.path.isdir(dir_path):
raise ValueError(f'Path "{dir_path}" is not a directory.')
def read_steam_wishlist_game_names(
wishlist_file: str,
):
wishlist_content = read_json_file(wishlist_file)
if wishlist_content is None:
raise ValueError(f'Could not read wishlist file "{wishlist_file}".')
return [
app_name
for app in wishlist_content
if not app.get('store_item', {}).get('is_coming_soon')
if (app_name := get_app_name_from_steam_wishlist_item(app))
]
def get_app_name_from_steam_wishlist_item(app: dict):
store_item = (app or {}).get('store_item')
if not store_item:
return None
app_name = store_item.get('name')
if not app_name:
return None
return app_name
def scan_dir_names(
dir_paths: list[str],
exclusions: list[str] | None = None,
):
if exclusions is None:
exclusions = [GENERIC_EXCLUSION_FILE]
return [
entry.name
for dir_path in dir_paths
for entry in os.scandir(dir_path)
if entry.is_dir() and not should_skip_dir(entry.path, exclusions)
]
def should_skip_dir(
dir_path: str,
exclusions: list[str],
):
if Attr.is_hidden(dir_path):
logger.trace(f' Skipping "{dir_path}". Directory is hidden.')
return True
if os.path.basename(dir_path) in DIR_BLACKLIST:
logger.trace(f' Skipping "{dir_path}". Directory is blacklisted.')
return True
if any(has_exclusion_file(dir_path, exclusion) for exclusion in exclusions):
logger.trace(f' Skipping "{dir_path}". Directory is excluded.')
return True
return False
def has_exclusion_file(
path: str,
exclusion: str,
):
return os.path.exists(os.path.join(path, exclusion))
def seconds_to_hours(
seconds: int,
):
return round(seconds / 3600) if seconds is not None else None
def format_dimmed(
msg: str,
):
return f'<span class="dim">{msg}</span>'
def simplify_game_name(
name: str,
):
parsed_name = name
parsed_name = re.sub(r'[™®]', '', parsed_name)
parsed_name = re.sub(
r'([:-]\s?)?(GOTY|Game of The Year|Director.s Cut)(\sEdition)?',
'',
parsed_name,
flags=re.IGNORECASE,
)
parsed_name = re.sub(
r'([:-]\s?)?(Definitive|Deluxe|Gold|Premium|Ultimate)\sEdition',
'',
parsed_name,
flags=re.IGNORECASE,
)
parsed_name = re.sub(
r'[:-]\s?(\w+)\sEdition', '', parsed_name, flags=re.IGNORECASE
)
return parsed_name.strip()
def normalize_dir_name(
name: str,
):
parsed_name = name.lower()
parsed_name = re.sub(r'[\'’:꞉—-]', '', parsed_name)
parsed_name = ''.join(
char
for char in unicodedata.normalize('NFKD', parsed_name)
if not unicodedata.combining(char)
)
return parsed_name
def flatten_game_name(
name: str,
):
parsed_name = name
parsed_name = re.sub(
r'\b(HD|Remake|Remaster(?:ed)?)\b', '', parsed_name, flags=re.IGNORECASE
)
parsed_name = re.sub(r'(\s+)', '', parsed_name)
return parsed_name
def get_comparable_dir_name(
name: str,
):
parsed_name = name
parsed_name = simplify_game_name(parsed_name)
parsed_name = normalize_dir_name(parsed_name)
parsed_name = flatten_game_name(parsed_name)
return parsed_name
def matches_loosely(
name_a: str,
name_b: str,
):
return get_comparable_dir_name(name_a) == get_comparable_dir_name(name_b)