umu-commander 1.4.0__py3-none-any.whl → 1.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of umu-commander might be problematic. Click here for more details.
- umu_commander/classes.py +47 -9
- umu_commander/configuration.py +69 -19
- umu_commander/database.py +43 -0
- umu_commander/main.py +22 -9
- umu_commander/proton.py +34 -18
- umu_commander/tracking.py +26 -32
- umu_commander/umu_config.py +42 -50
- umu_commander/util.py +4 -5
- {umu_commander-1.4.0.dist-info → umu_commander-1.5.2.dist-info}/METADATA +10 -2
- umu_commander-1.5.2.dist-info/RECORD +14 -0
- umu_commander/db.py +0 -66
- umu_commander-1.4.0.dist-info/RECORD +0 -14
- {umu_commander-1.4.0.dist-info → umu_commander-1.5.2.dist-info}/WHEEL +0 -0
- {umu_commander-1.4.0.dist-info → umu_commander-1.5.2.dist-info}/entry_points.txt +0 -0
- {umu_commander-1.4.0.dist-info → umu_commander-1.5.2.dist-info}/licenses/LICENSE.txt +0 -0
umu_commander/classes.py
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
+
from enum import Enum
|
|
2
3
|
|
|
3
4
|
|
|
4
|
-
# analogous to Proton version
|
|
5
5
|
@dataclass
|
|
6
6
|
class Element:
|
|
7
7
|
group_id: str = ""
|
|
8
8
|
value: str = ""
|
|
9
9
|
info: str = ""
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
def as_proton_ver(self) -> "ProtonVer":
|
|
12
|
+
return ProtonVer(self.group_id, self.value, self.info)
|
|
13
|
+
|
|
14
|
+
def as_dll_override(self) -> "DLLOverride":
|
|
15
|
+
return DLLOverride(self.info, self.value)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class ProtonVer(Element):
|
|
20
|
+
def __init__(self, dir: str = "", version_num: str = "", user_count: str = ""):
|
|
21
|
+
super().__init__(group_id=dir, value=version_num, info=user_count)
|
|
22
|
+
|
|
12
23
|
@property
|
|
13
24
|
def dir(self):
|
|
14
25
|
return self.group_id
|
|
@@ -21,23 +32,50 @@ class Element:
|
|
|
21
32
|
def user_count(self):
|
|
22
33
|
return self.info
|
|
23
34
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class DLLOverride(Element):
|
|
38
|
+
def __init__(self, label: str = "", override_str: str = ""):
|
|
39
|
+
super().__init__(group_id="", value=override_str, info=label)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def override_str(self):
|
|
43
|
+
return self.value
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def label(self):
|
|
47
|
+
return self.info
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class Value(Element):
|
|
52
|
+
def __init__(self, value: str):
|
|
53
|
+
super().__init__(value=value)
|
|
27
54
|
|
|
28
55
|
|
|
29
|
-
# analogous to Proton directory
|
|
30
56
|
@dataclass
|
|
31
57
|
class Group:
|
|
32
58
|
identity: str = ""
|
|
33
59
|
label: str = ""
|
|
34
60
|
elements: list[Element] = list
|
|
35
61
|
|
|
36
|
-
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class ProtonDir(Group):
|
|
37
65
|
@property
|
|
38
66
|
def path(self):
|
|
39
67
|
return self.identity
|
|
40
68
|
|
|
41
69
|
@property
|
|
42
|
-
def versions(self):
|
|
43
|
-
return self.elements
|
|
70
|
+
def versions(self) -> list[ProtonVer]:
|
|
71
|
+
return [e.as_proton_ver() for e in self.elements]
|
|
72
|
+
|
|
73
|
+
@versions.setter
|
|
74
|
+
def versions(self, value):
|
|
75
|
+
self.elements = value
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ExitCode(Enum):
|
|
79
|
+
SUCCESS = 0
|
|
80
|
+
READING_ERROR = 1
|
|
81
|
+
INVALID_SELECTION = 2
|
umu_commander/configuration.py
CHANGED
|
@@ -1,22 +1,72 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import tomllib
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
5
|
+
import tomli_w
|
|
6
|
+
|
|
7
|
+
from umu_commander.classes import DLLOverride
|
|
8
|
+
|
|
9
|
+
CONFIG_DIR: str = os.path.join(Path.home(), ".config")
|
|
10
|
+
CONFIG_NAME: str = "umu-commander.toml"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Configuration:
|
|
14
|
+
PROTON_PATHS: tuple[str, ...] = (
|
|
15
|
+
os.path.join(Path.home(), ".local/share/Steam/compatibilitytools.d/"),
|
|
16
|
+
os.path.join(Path.home(), ".local/share/umu/compatibilitytools"),
|
|
17
|
+
)
|
|
18
|
+
UMU_PROTON_PATH: str = os.path.join(
|
|
19
|
+
Path.home(), ".local/share/Steam/compatibilitytools.d/"
|
|
20
|
+
)
|
|
21
|
+
DB_NAME: str = "tracking.json"
|
|
22
|
+
DB_DIR: str = os.path.join(Path.home(), ".local/share/umu/compatibilitytools")
|
|
23
|
+
UMU_CONFIG_NAME: str = "umu-config.toml"
|
|
24
|
+
DEFAULT_PREFIX_DIR: str = os.path.join(Path.home(), ".local/share/wineprefixes/")
|
|
25
|
+
DLL_OVERRIDES_OPTIONS: tuple[DLLOverride, ...] = (
|
|
26
|
+
DLLOverride("winhttp for BepInEx", "winhttp.dll=n;"),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def load():
|
|
31
|
+
try:
|
|
32
|
+
with open(os.path.join(CONFIG_DIR, CONFIG_NAME), "rb") as conf_file:
|
|
33
|
+
toml_conf = tomllib.load(conf_file)
|
|
34
|
+
toml_conf["DLL_OVERRIDES_OPTIONS"] = tuple(
|
|
35
|
+
[
|
|
36
|
+
DLLOverride(label, override_str)
|
|
37
|
+
for label, override_str in toml_conf[
|
|
38
|
+
"DLL_OVERRIDES_OPTIONS"
|
|
39
|
+
].items()
|
|
40
|
+
]
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
for key, value in toml_conf.items():
|
|
44
|
+
setattr(Configuration, key, value)
|
|
45
|
+
|
|
46
|
+
except FileNotFoundError:
|
|
47
|
+
Configuration.dump()
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def dump():
|
|
51
|
+
if not os.path.exists(CONFIG_DIR):
|
|
52
|
+
os.mkdir(CONFIG_DIR)
|
|
53
|
+
|
|
54
|
+
with open(os.path.join(CONFIG_DIR, CONFIG_NAME), "wb") as conf_file:
|
|
55
|
+
toml_conf = Configuration._get_attributes()
|
|
56
|
+
toml_conf["DLL_OVERRIDES_OPTIONS"] = dict(
|
|
57
|
+
[
|
|
58
|
+
(override.info, override.value)
|
|
59
|
+
for override in Configuration.DLL_OVERRIDES_OPTIONS
|
|
60
|
+
]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
tomli_w.dump(toml_conf, conf_file)
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def _get_attributes():
|
|
67
|
+
attributes = {}
|
|
68
|
+
for key, value in vars(Configuration).items():
|
|
69
|
+
if not key.startswith("__") and not callable(getattr(Configuration, key)):
|
|
70
|
+
attributes[key] = value
|
|
71
|
+
|
|
72
|
+
return attributes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
|
|
5
|
+
from umu_commander.configuration import Configuration as config
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Database:
|
|
9
|
+
_db: defaultdict[str, defaultdict[str, list[str]]]
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def load():
|
|
13
|
+
if not os.path.exists(config.DB_DIR):
|
|
14
|
+
os.mkdir(config.DB_DIR)
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
with open(os.path.join(config.DB_DIR, config.DB_NAME), "rt") as db_file:
|
|
18
|
+
Database._db = defaultdict(lambda: defaultdict(list))
|
|
19
|
+
Database._db.update(json.load(db_file))
|
|
20
|
+
|
|
21
|
+
except FileNotFoundError:
|
|
22
|
+
Database._db = defaultdict(lambda: defaultdict(list))
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def dump():
|
|
26
|
+
with open(os.path.join(config.DB_DIR, config.DB_NAME), "wt") as db_file:
|
|
27
|
+
# noinspection PyTypeChecker
|
|
28
|
+
json.dump(Database._db, db_file, indent="\t")
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def get(
|
|
32
|
+
proton_dir: str = None, proton_ver: str = None
|
|
33
|
+
) -> dict[str, dict[str, list[str]]] | dict[str, list[str]] | list[str]:
|
|
34
|
+
if proton_dir is None and proton_ver is None:
|
|
35
|
+
return Database._db
|
|
36
|
+
|
|
37
|
+
if proton_ver is None:
|
|
38
|
+
return Database._db[proton_dir]
|
|
39
|
+
|
|
40
|
+
if proton_ver not in Database._db[proton_dir]:
|
|
41
|
+
Database._db[proton_dir][proton_ver] = []
|
|
42
|
+
|
|
43
|
+
return Database._db[proton_dir][proton_ver]
|
umu_commander/main.py
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/python3
|
|
2
|
+
import os
|
|
2
3
|
import sys
|
|
3
4
|
from json import JSONDecodeError
|
|
4
5
|
|
|
5
|
-
from umu_commander import
|
|
6
|
-
from umu_commander.
|
|
6
|
+
from umu_commander import tracking, umu_config
|
|
7
|
+
from umu_commander.classes import ExitCode
|
|
8
|
+
from umu_commander.configuration import CONFIG_DIR, CONFIG_NAME
|
|
9
|
+
from umu_commander.configuration import Configuration as config
|
|
10
|
+
from umu_commander.database import Database as db
|
|
7
11
|
|
|
8
12
|
|
|
9
13
|
def print_help():
|
|
@@ -15,16 +19,24 @@ def print_help():
|
|
|
15
19
|
)
|
|
16
20
|
|
|
17
21
|
|
|
18
|
-
def main() ->
|
|
22
|
+
def main() -> ExitCode:
|
|
23
|
+
try:
|
|
24
|
+
config.load()
|
|
25
|
+
except (JSONDecodeError, KeyError):
|
|
26
|
+
config_path: str = os.path.join(CONFIG_DIR, CONFIG_NAME)
|
|
27
|
+
print(f"Config file at {config_path} could not be read.")
|
|
28
|
+
os.rename(config_path, os.path.join(CONFIG_DIR, CONFIG_NAME + ".old"))
|
|
29
|
+
|
|
19
30
|
try:
|
|
20
31
|
db.load()
|
|
21
32
|
except JSONDecodeError:
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
db_path: str = os.path.join(config.DB_DIR, config.DB_NAME)
|
|
34
|
+
print(f"Tracking file at {db_path} could not be read.")
|
|
35
|
+
os.rename(db_path, os.path.join(config.DB_DIR, config.DB_NAME + ".old"))
|
|
24
36
|
|
|
25
37
|
if len(sys.argv) == 1:
|
|
26
38
|
print_help()
|
|
27
|
-
return
|
|
39
|
+
return ExitCode.SUCCESS
|
|
28
40
|
|
|
29
41
|
verb: str = sys.argv[1]
|
|
30
42
|
match verb:
|
|
@@ -43,13 +55,14 @@ def main() -> int:
|
|
|
43
55
|
case _:
|
|
44
56
|
print("Invalid verb.")
|
|
45
57
|
print_help()
|
|
46
|
-
return
|
|
58
|
+
return ExitCode.INVALID_SELECTION
|
|
47
59
|
|
|
48
60
|
tracking.untrack_unlinked()
|
|
49
61
|
db.dump()
|
|
62
|
+
config.dump()
|
|
50
63
|
|
|
51
|
-
return
|
|
64
|
+
return ExitCode.SUCCESS
|
|
52
65
|
|
|
53
66
|
|
|
54
67
|
if __name__ == "__main__":
|
|
55
|
-
exit(main())
|
|
68
|
+
exit(main().value)
|
umu_commander/proton.py
CHANGED
|
@@ -2,12 +2,13 @@ import os
|
|
|
2
2
|
import re
|
|
3
3
|
import subprocess
|
|
4
4
|
|
|
5
|
-
from umu_commander.classes import
|
|
6
|
-
from umu_commander.configuration import
|
|
5
|
+
from umu_commander.classes import ProtonDir, ProtonVer
|
|
6
|
+
from umu_commander.configuration import Configuration as config
|
|
7
|
+
from umu_commander.database import Database as db
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
def _natural_sort_proton_ver_key(
|
|
10
|
-
s: str =
|
|
10
|
+
def _natural_sort_proton_ver_key(p: ProtonVer, _nsre=re.compile(r"(\d+)")):
|
|
11
|
+
s: str = p.version_num
|
|
11
12
|
return [int(text) if text.isdigit() else text for text in _nsre.split(s)]
|
|
12
13
|
|
|
13
14
|
|
|
@@ -31,33 +32,48 @@ def refresh_proton_versions():
|
|
|
31
32
|
break
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
def _sort_proton_versions(versions: list[
|
|
35
|
+
def _sort_proton_versions(versions: list[ProtonVer]) -> list[ProtonVer]:
|
|
35
36
|
return sorted(versions, key=_natural_sort_proton_ver_key, reverse=True)
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
def collect_proton_versions(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
def collect_proton_versions(
|
|
40
|
+
sort: bool = False, user_count: bool = False
|
|
41
|
+
) -> list[ProtonDir]:
|
|
42
|
+
def get_user_count(proton_dir: str, proton_ver) -> str:
|
|
43
|
+
return (
|
|
44
|
+
"(" + str(len(db.get(proton_dir, proton_ver))) + ")"
|
|
45
|
+
if proton_ver in db.get(proton_dir)
|
|
46
|
+
else "(-)"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
proton_dirs: list[ProtonDir] = []
|
|
50
|
+
for proton_dir in config.PROTON_PATHS:
|
|
51
|
+
versions: list[ProtonVer] = [
|
|
52
|
+
ProtonVer(
|
|
53
|
+
proton_dir,
|
|
54
|
+
version,
|
|
55
|
+
get_user_count(proton_dir, version) if user_count else "",
|
|
56
|
+
)
|
|
43
57
|
for version in os.listdir(proton_dir)
|
|
44
|
-
if version
|
|
58
|
+
if os.path.isdir(os.path.join(proton_dir, version))
|
|
45
59
|
]
|
|
60
|
+
|
|
46
61
|
if sort:
|
|
47
62
|
versions = sorted(versions, key=_natural_sort_proton_ver_key, reverse=True)
|
|
48
63
|
|
|
49
|
-
|
|
50
|
-
|
|
64
|
+
proton_dirs.append(
|
|
65
|
+
ProtonDir(proton_dir, f"Proton versions in {proton_dir}:", versions)
|
|
51
66
|
)
|
|
52
67
|
|
|
53
|
-
return
|
|
68
|
+
return proton_dirs
|
|
54
69
|
|
|
55
70
|
|
|
56
71
|
def get_latest_umu_proton():
|
|
57
|
-
umu_proton_versions: list[
|
|
58
|
-
|
|
59
|
-
for version in os.listdir(
|
|
60
|
-
if "UMU" in version
|
|
72
|
+
umu_proton_versions: list[ProtonVer] = [
|
|
73
|
+
ProtonVer(config.UMU_PROTON_PATH, version)
|
|
74
|
+
for version in os.listdir(config.UMU_PROTON_PATH)
|
|
75
|
+
if "UMU" in version
|
|
76
|
+
and os.path.isdir(os.path.join(config.UMU_PROTON_PATH, version))
|
|
61
77
|
]
|
|
62
78
|
umu_proton_versions = sorted(
|
|
63
79
|
umu_proton_versions, key=_natural_sort_proton_ver_key, reverse=True
|
umu_commander/tracking.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import shutil
|
|
2
3
|
|
|
3
|
-
from umu_commander import
|
|
4
|
-
from umu_commander.
|
|
5
|
-
from umu_commander.configuration import *
|
|
4
|
+
from umu_commander.classes import ProtonDir, ProtonVer
|
|
5
|
+
from umu_commander.database import Database as db
|
|
6
6
|
from umu_commander.proton import (
|
|
7
7
|
collect_proton_versions,
|
|
8
8
|
get_latest_umu_proton,
|
|
@@ -15,55 +15,49 @@ from umu_commander.util import (
|
|
|
15
15
|
|
|
16
16
|
def untrack(quiet: bool = False):
|
|
17
17
|
current_dir: str = os.getcwd()
|
|
18
|
-
for proton_dir in db.
|
|
18
|
+
for proton_dir in db.get().keys():
|
|
19
19
|
for proton_ver in db.get(proton_dir):
|
|
20
|
-
db.
|
|
20
|
+
if current_dir in db.get(proton_dir, proton_ver):
|
|
21
|
+
db.get(proton_dir, proton_ver).remove(current_dir)
|
|
21
22
|
|
|
22
23
|
if not quiet:
|
|
23
24
|
print("Directory removed from all user lists.")
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
def track(
|
|
27
|
+
def track(
|
|
28
|
+
proton_ver: ProtonVer = None, refresh_versions: bool = True, quiet: bool = False
|
|
29
|
+
):
|
|
27
30
|
if refresh_versions:
|
|
28
31
|
refresh_proton_versions()
|
|
29
32
|
|
|
30
|
-
if
|
|
31
|
-
|
|
33
|
+
if proton_ver is None:
|
|
34
|
+
proton_ver: ProtonVer = get_selection(
|
|
32
35
|
"Select Proton version to add directory as user:",
|
|
33
36
|
None,
|
|
34
37
|
collect_proton_versions(sort=True),
|
|
35
|
-
)
|
|
38
|
+
).as_proton_ver()
|
|
36
39
|
|
|
37
40
|
untrack(quiet=True)
|
|
38
41
|
current_directory: str = os.getcwd()
|
|
39
|
-
db.
|
|
42
|
+
db.get(proton_ver.dir, proton_ver.version_num).append(current_directory)
|
|
40
43
|
|
|
41
44
|
if not quiet:
|
|
42
45
|
print(
|
|
43
|
-
f"Directory {current_directory} added to Proton version's {
|
|
46
|
+
f"Directory {current_directory} added to Proton version's {proton_ver.version_num} in {proton_ver.dir} user list."
|
|
44
47
|
)
|
|
45
48
|
|
|
46
49
|
|
|
47
50
|
def users():
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
for proton_dir in proton_version_dirs:
|
|
51
|
-
for proton in proton_dir.versions:
|
|
52
|
-
if proton.version_num in db.get(proton.dir):
|
|
53
|
-
proton.user_count = (
|
|
54
|
-
"(" + str(len(db.get(proton.dir, proton.version_num))) + ")"
|
|
55
|
-
)
|
|
56
|
-
else:
|
|
57
|
-
proton.user_count = "(-)"
|
|
51
|
+
proton_dirs: list[ProtonDir] = collect_proton_versions(sort=True, user_count=True)
|
|
58
52
|
|
|
59
|
-
|
|
60
|
-
"Select Proton version to view user list:", None,
|
|
61
|
-
)
|
|
53
|
+
proton_ver: ProtonVer = get_selection(
|
|
54
|
+
"Select Proton version to view user list:", None, proton_dirs
|
|
55
|
+
).as_proton_ver()
|
|
62
56
|
|
|
63
|
-
if
|
|
64
|
-
version_users: list[str] = db.get(
|
|
57
|
+
if proton_ver.dir in db.get() and proton_ver.version_num in db.get(proton_ver.dir):
|
|
58
|
+
version_users: list[str] = db.get(proton_ver.dir, proton_ver.version_num)
|
|
65
59
|
if len(version_users) > 0:
|
|
66
|
-
print(f"Directories using {
|
|
60
|
+
print(f"Directories using {proton_ver.version_num} of {proton_ver.dir}:")
|
|
67
61
|
print(*version_users, sep="\n")
|
|
68
62
|
else:
|
|
69
63
|
print("No directories currently use this version.")
|
|
@@ -72,7 +66,7 @@ def users():
|
|
|
72
66
|
|
|
73
67
|
|
|
74
68
|
def delete():
|
|
75
|
-
for proton_dir in db.
|
|
69
|
+
for proton_dir in db.get().keys():
|
|
76
70
|
for proton_ver, version_users in db.get(proton_dir).copy().items():
|
|
77
71
|
if proton_ver == get_latest_umu_proton():
|
|
78
72
|
continue
|
|
@@ -86,12 +80,12 @@ def delete():
|
|
|
86
80
|
shutil.rmtree(os.path.join(proton_dir, proton_ver))
|
|
87
81
|
except FileNotFoundError:
|
|
88
82
|
pass
|
|
89
|
-
db.
|
|
83
|
+
del db.get(proton_dir)[proton_ver]
|
|
90
84
|
|
|
91
85
|
|
|
92
86
|
def untrack_unlinked():
|
|
93
|
-
for proton_dir in db.
|
|
94
|
-
for proton_ver, version_users in db.get(proton_dir
|
|
87
|
+
for proton_dir in db.get().keys():
|
|
88
|
+
for proton_ver, version_users in db.get()[proton_dir].items():
|
|
95
89
|
for user in version_users:
|
|
96
90
|
if not os.path.exists(user):
|
|
97
|
-
db.
|
|
91
|
+
db.get(proton_dir, proton_ver).remove(user)
|
umu_commander/umu_config.py
CHANGED
|
@@ -1,67 +1,56 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import subprocess
|
|
2
3
|
import tomllib
|
|
3
|
-
from collections.abc import Mapping
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
+
import tomli_w
|
|
7
|
+
|
|
6
8
|
from umu_commander import tracking
|
|
7
|
-
from umu_commander.
|
|
9
|
+
from umu_commander.classes import DLLOverride, ProtonVer
|
|
10
|
+
from umu_commander.configuration import Configuration as config
|
|
8
11
|
from umu_commander.proton import collect_proton_versions, refresh_proton_versions
|
|
9
12
|
from umu_commander.util import (
|
|
10
13
|
get_selection,
|
|
11
|
-
|
|
14
|
+
strings_to_values,
|
|
12
15
|
)
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
def _write(params: Mapping[str, Any]):
|
|
16
|
-
config: str = "[umu]\n"
|
|
17
|
-
for key in set(params.keys()) - {"env", "launch_args"}:
|
|
18
|
-
config += f'{key} = "{params[key]}"\n'
|
|
19
|
-
|
|
20
|
-
config += f"launch_args = {params["launch_args"]}\n"
|
|
21
|
-
|
|
22
|
-
config += "\n[env]\n"
|
|
23
|
-
for key, value in params["env"].items():
|
|
24
|
-
config += f'{key} = "{value}"\n'
|
|
25
|
-
|
|
26
|
-
config_file = open(CONFIG_NAME, "wt")
|
|
27
|
-
config_file.write(config)
|
|
28
|
-
config_file.close()
|
|
29
|
-
|
|
30
|
-
|
|
31
18
|
def create():
|
|
32
19
|
refresh_proton_versions()
|
|
33
20
|
|
|
34
|
-
params: dict[str, Any] = {"env": {}}
|
|
21
|
+
params: dict[str, Any] = {"umu": {}, "env": {}}
|
|
35
22
|
|
|
36
23
|
# Prefix selection
|
|
37
24
|
selection: str = get_selection(
|
|
38
25
|
"Select wine prefix:",
|
|
39
|
-
|
|
26
|
+
strings_to_values(
|
|
27
|
+
[*os.listdir(config.DEFAULT_PREFIX_DIR), "Current directory"]
|
|
28
|
+
),
|
|
40
29
|
None,
|
|
41
30
|
).value
|
|
42
31
|
|
|
43
32
|
if selection == "Current directory":
|
|
44
|
-
params["prefix"] = os.path.join(os.getcwd(), "prefix")
|
|
33
|
+
params["umu"]["prefix"] = os.path.join(os.getcwd(), "prefix")
|
|
45
34
|
else:
|
|
46
|
-
params["prefix"] = os.path.join(
|
|
35
|
+
params["umu"]["prefix"] = os.path.join(config.DEFAULT_PREFIX_DIR, selection)
|
|
47
36
|
|
|
48
37
|
# Proton selection
|
|
49
38
|
selected_umu_latest: bool = False
|
|
50
|
-
|
|
39
|
+
proton_ver: ProtonVer = get_selection(
|
|
51
40
|
"Select Proton version:",
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
)
|
|
55
|
-
if
|
|
41
|
+
strings_to_values(["Always latest UMU Proton"]),
|
|
42
|
+
collect_proton_versions(sort=True),
|
|
43
|
+
).as_proton_ver()
|
|
44
|
+
if proton_ver.version_num == "Always latest UMU Proton":
|
|
56
45
|
selected_umu_latest = True
|
|
57
46
|
else:
|
|
58
|
-
params["proton"] = os.path.join(
|
|
47
|
+
params["umu"]["proton"] = os.path.join(proton_ver.dir, proton_ver.version_num)
|
|
59
48
|
|
|
60
49
|
# Select DLL overrides
|
|
61
|
-
possible_overrides: list[
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
*DLL_OVERRIDES_OPTIONS,
|
|
50
|
+
possible_overrides: list[DLLOverride] = [
|
|
51
|
+
DLLOverride(label="Reset"),
|
|
52
|
+
DLLOverride(label="Done"),
|
|
53
|
+
*config.DLL_OVERRIDES_OPTIONS,
|
|
65
54
|
]
|
|
66
55
|
selected: set[int] = set()
|
|
67
56
|
while True:
|
|
@@ -69,7 +58,7 @@ def create():
|
|
|
69
58
|
for idx, override in enumerate(possible_overrides):
|
|
70
59
|
if idx in selected:
|
|
71
60
|
idx = "Y"
|
|
72
|
-
print(f"{idx}) {override.
|
|
61
|
+
print(f"{idx}) {override.label}")
|
|
73
62
|
|
|
74
63
|
try:
|
|
75
64
|
index: int = int(input("? "))
|
|
@@ -92,12 +81,14 @@ def create():
|
|
|
92
81
|
params["env"]["WINEDLLOVERRIDES"] = ""
|
|
93
82
|
for selection in selected:
|
|
94
83
|
# noinspection PyTypeChecker
|
|
95
|
-
params["env"]["WINEDLLOVERRIDES"] += possible_overrides[
|
|
84
|
+
params["env"]["WINEDLLOVERRIDES"] += possible_overrides[
|
|
85
|
+
selection
|
|
86
|
+
].override_str
|
|
96
87
|
|
|
97
88
|
# Set language locale
|
|
98
89
|
match get_selection(
|
|
99
90
|
"Select locale:",
|
|
100
|
-
|
|
91
|
+
strings_to_values(["Default", "Japanese"]),
|
|
101
92
|
None,
|
|
102
93
|
).value:
|
|
103
94
|
case "Default":
|
|
@@ -109,7 +100,7 @@ def create():
|
|
|
109
100
|
launch_args: list[str] = input(
|
|
110
101
|
"Enter executable options, separated by spaces:\n? "
|
|
111
102
|
).split()
|
|
112
|
-
params["launch_args"] = launch_args
|
|
103
|
+
params["umu"]["launch_args"] = launch_args
|
|
113
104
|
|
|
114
105
|
# Select executable name
|
|
115
106
|
files: list[str] = [
|
|
@@ -118,30 +109,31 @@ def create():
|
|
|
118
109
|
if os.path.isfile(os.path.join(os.getcwd(), file))
|
|
119
110
|
]
|
|
120
111
|
executable_name: str = get_selection(
|
|
121
|
-
"Select game executable:",
|
|
112
|
+
"Select game executable:", strings_to_values(files), None
|
|
122
113
|
).value
|
|
123
|
-
params["exe"] = executable_name
|
|
114
|
+
params["umu"]["exe"] = executable_name
|
|
124
115
|
|
|
125
116
|
try:
|
|
126
|
-
|
|
127
|
-
|
|
117
|
+
with open(config.UMU_CONFIG_NAME, "wb") as file:
|
|
118
|
+
tomli_w.dump(params, file)
|
|
119
|
+
|
|
120
|
+
print(f"Configuration file {config.UMU_CONFIG_NAME} created at {os.getcwd()}.")
|
|
128
121
|
print(f"Use by running umu-commander run.")
|
|
129
122
|
if not selected_umu_latest:
|
|
130
|
-
tracking.track(
|
|
123
|
+
tracking.track(proton_ver, False)
|
|
131
124
|
except:
|
|
132
|
-
print("Could not create
|
|
125
|
+
print("Could not create configiguration file.")
|
|
133
126
|
|
|
134
127
|
|
|
135
128
|
def run():
|
|
136
|
-
with open(
|
|
137
|
-
|
|
138
|
-
config = tomllib.load(toml_file)
|
|
129
|
+
with open(config.UMU_CONFIG_NAME, "rb") as toml_file:
|
|
130
|
+
toml_conf = tomllib.load(toml_file)
|
|
139
131
|
|
|
140
|
-
if not os.path.exists(
|
|
141
|
-
os.mkdir(
|
|
132
|
+
if not os.path.exists(toml_conf["umu"]["prefix"]):
|
|
133
|
+
os.mkdir(toml_conf["umu"]["prefix"])
|
|
142
134
|
|
|
143
|
-
os.environ.update(
|
|
135
|
+
os.environ.update(toml_conf.get("env", {}))
|
|
144
136
|
subprocess.run(
|
|
145
|
-
args=["umu-run", "--config",
|
|
137
|
+
args=["umu-run", "--config", config.UMU_CONFIG_NAME],
|
|
146
138
|
env=os.environ,
|
|
147
139
|
)
|
umu_commander/util.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
from umu_commander.classes import Group
|
|
2
|
-
from umu_commander.configuration import *
|
|
1
|
+
from umu_commander.classes import Element, ExitCode, Group, Value
|
|
3
2
|
|
|
4
3
|
|
|
5
|
-
def
|
|
6
|
-
return [
|
|
4
|
+
def strings_to_values(values: list[str]) -> list[Value]:
|
|
5
|
+
return [Value(value) for value in values]
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
def _selection_set_valid(
|
|
@@ -59,7 +58,7 @@ def get_selection(
|
|
|
59
58
|
) -> Element:
|
|
60
59
|
if not _selection_set_valid(selection_elements, selection_groups):
|
|
61
60
|
print("Nothing to select from.")
|
|
62
|
-
exit(
|
|
61
|
+
exit(ExitCode.INVALID_SELECTION)
|
|
63
62
|
|
|
64
63
|
if selection_groups is None:
|
|
65
64
|
selection_groups = []
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: umu-commander
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.2
|
|
4
4
|
Summary: umu-commander is an interactive CLI tool to help you manage umu.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Mpaxlamitsounas/umu-commander
|
|
6
6
|
Project-URL: Issues, https://github.com/Mpaxlamitsounas/umu-commander/issues
|
|
@@ -21,7 +21,15 @@ What directories each Proton version is being used by is tracked within `trackin
|
|
|
21
21
|
|
|
22
22
|
Vanilla umu configuration files currently do not support setting environmental variables, this tool adds such functionality by adding an extra TOML table in the umu config itself. An example config is available under the name `example_config.toml`.
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
### Config
|
|
25
|
+
The configuration file lives at `~/.config/umu-commander.toml`. Within it, you can edit the following:
|
|
26
|
+
* PROTON_PATHS: List of directories umu-commander will search for Proton versions.
|
|
27
|
+
* UMU_PROTON_PATH: Directory where umu itself downloads its umu Proton versions.
|
|
28
|
+
* DB_NAME: Tracking DB filename.
|
|
29
|
+
* DB_DIR: Directory where the Tracking DB is stored.
|
|
30
|
+
* UMU_CONFIG_NAME: Name of the umu config created using umu-commander run.
|
|
31
|
+
* DEFAULT_PREFIX_DIR: Directory where WINE prefixes are scanned.
|
|
32
|
+
* [DLL_OVERRIDES_OPTIONS]: Category to list possible DLL overrides in "Label" = "Override string" format.
|
|
25
33
|
|
|
26
34
|
### Verbs
|
|
27
35
|
umu-commander needs one of the following verbs specified after the executable name:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
umu_commander/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
umu_commander/classes.py,sha256=SW8k4TrP4-W4DdaQC_P88wBIgeJ3TIaV7lyt32IfVD8,1654
|
|
3
|
+
umu_commander/configuration.py,sha256=tzVFoQx7M1RGGtP_c_k3IUVFNcLwsmjbQplTeKlFUnM,2398
|
|
4
|
+
umu_commander/database.py,sha256=rkjEEy-Em9ghZOypsWyKTlNjDNMU4XXWuthnpfa6yz4,1343
|
|
5
|
+
umu_commander/main.py,sha256=LWLLzMO2xm-45USc0Uw1-oTdPW14tOd0-WAbnLvFTQg,1966
|
|
6
|
+
umu_commander/proton.py,sha256=QWqU5N2v-QqP_d-kFGXxXiCufLFQywnMNrbuLXaINZk,2572
|
|
7
|
+
umu_commander/tracking.py,sha256=MVj-GfbROyqhKa-m6557RGCzavTxgphNBVPJNtSTe3k,3058
|
|
8
|
+
umu_commander/umu_config.py,sha256=3b5XeTZppmLpxUGBHi4JSBc7zmKxNKjboldwGfLE0rc,4091
|
|
9
|
+
umu_commander/util.py,sha256=mBRdLs-dnrTE_23qSb9wZ7tw4WmcNU3AS9oJFrVLgH4,2642
|
|
10
|
+
umu_commander-1.5.2.dist-info/METADATA,sha256=E-8ommUma55wG-liyjohc6eaIh1IE3B6NxpMpx1Lzsg,3180
|
|
11
|
+
umu_commander-1.5.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
12
|
+
umu_commander-1.5.2.dist-info/entry_points.txt,sha256=7RGP35zAHeEojZ-sv7JIITuMeH_VVNuG2g2_SUrUnbM,58
|
|
13
|
+
umu_commander-1.5.2.dist-info/licenses/LICENSE.txt,sha256=yipFXBRmVZ2Q44x1q18HccPUAECBQLXAOAr21aS57uY,1071
|
|
14
|
+
umu_commander-1.5.2.dist-info/RECORD,,
|
umu_commander/db.py
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from collections import defaultdict
|
|
3
|
-
from json import JSONDecodeError
|
|
4
|
-
|
|
5
|
-
from umu_commander.configuration import *
|
|
6
|
-
|
|
7
|
-
_db: defaultdict[str, defaultdict[str, list[str]]]
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def load():
|
|
11
|
-
global _db
|
|
12
|
-
|
|
13
|
-
if not os.path.exists(DB_DIR):
|
|
14
|
-
os.mkdir(DB_DIR)
|
|
15
|
-
|
|
16
|
-
try:
|
|
17
|
-
with open(os.path.join(DB_DIR, DB_NAME), "rt") as db_file:
|
|
18
|
-
_db = defaultdict(lambda: defaultdict(list))
|
|
19
|
-
_db.update(json.load(db_file))
|
|
20
|
-
|
|
21
|
-
except JSONDecodeError:
|
|
22
|
-
print(f"Could not decode DB file, is it valid JSON?")
|
|
23
|
-
raise JSONDecodeError("", "", 0)
|
|
24
|
-
|
|
25
|
-
except FileNotFoundError:
|
|
26
|
-
_db = defaultdict(lambda: defaultdict(list))
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def dump():
|
|
30
|
-
with open(os.path.join(DB_DIR, DB_NAME), "wt") as db_file:
|
|
31
|
-
# noinspection PyTypeChecker
|
|
32
|
-
json.dump(_db, db_file, indent="\t")
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def copy():
|
|
36
|
-
return _db.copy()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def get(proton_dir: str, proton_ver: str = None) -> dict[str, list[str]] | list[str]:
|
|
40
|
-
if proton_ver is None:
|
|
41
|
-
return _db[proton_dir]
|
|
42
|
-
|
|
43
|
-
return _db[proton_dir][proton_ver]
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# add user_dir to proton_ver's of proton_dir list of users
|
|
47
|
-
def add_to(proton_dir: str, proton_ver: str, user_dir: str):
|
|
48
|
-
global _db
|
|
49
|
-
|
|
50
|
-
if proton_ver not in _db[proton_dir]:
|
|
51
|
-
_db[proton_dir][proton_ver] = []
|
|
52
|
-
|
|
53
|
-
_db[proton_dir][proton_ver].append(user_dir)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def remove_from(proton_dir: str, proton_ver: str, user_dir: str):
|
|
57
|
-
global _db
|
|
58
|
-
|
|
59
|
-
if user_dir in _db[proton_dir][proton_ver]:
|
|
60
|
-
_db[proton_dir][proton_ver].remove(user_dir)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def delete(proton_dir: str, proton_ver: str):
|
|
64
|
-
global _db
|
|
65
|
-
|
|
66
|
-
del _db[proton_dir][proton_ver]
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
umu_commander/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
umu_commander/classes.py,sha256=-_5vgcWIwQ3btE8yVc1v8sbBKH9iAR_qqO0XogtqhK8,784
|
|
3
|
-
umu_commander/configuration.py,sha256=EOlXyB5xs2befhNdmUzlgWzZqUVAuOhfAEp3P7DCcPc,798
|
|
4
|
-
umu_commander/db.py,sha256=T_Rw2WbPi3Ig4kS1xUhfeLeH0IXm7ZIFwx_KOsrvij8,1596
|
|
5
|
-
umu_commander/main.py,sha256=CW8GopWCOLzWMS8_43RihRmjm_4yu0IXVGiMC7yap2k,1306
|
|
6
|
-
umu_commander/proton.py,sha256=KrkrBcNlRNpfwtAloUQQbThw6ioWiDgtQCqqlHYEQRg,2031
|
|
7
|
-
umu_commander/tracking.py,sha256=NdRarb_r65pDZXZer9Ls4uWqYhEd48qODp9hF68xpEQ,3219
|
|
8
|
-
umu_commander/umu_config.py,sha256=jBpIvf55QmolgHX43XM5Lmg_xUbL13zCLZMjqdAGalE,4189
|
|
9
|
-
umu_commander/util.py,sha256=f5YaYvyug4Tv8ydkhFy-AwEgWtVycV9-576A7h1D-dg,2644
|
|
10
|
-
umu_commander-1.4.0.dist-info/METADATA,sha256=r7MWhMvSQwoXcoB_2ukpOtZ5uF3gHljvJ88dc7xNF-c,2791
|
|
11
|
-
umu_commander-1.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
12
|
-
umu_commander-1.4.0.dist-info/entry_points.txt,sha256=7RGP35zAHeEojZ-sv7JIITuMeH_VVNuG2g2_SUrUnbM,58
|
|
13
|
-
umu_commander-1.4.0.dist-info/licenses/LICENSE.txt,sha256=yipFXBRmVZ2Q44x1q18HccPUAECBQLXAOAr21aS57uY,1071
|
|
14
|
-
umu_commander-1.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|