-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathcdmf_paths.py
More file actions
278 lines (240 loc) · 10.2 KB
/
cdmf_paths.py
File metadata and controls
278 lines (240 loc) · 10.2 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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# C:\AceForge\cdmf_paths.py
from __future__ import annotations
from pathlib import Path
import sys
import json
import os
import platform
# ---------------------------------------------------------------------------
# Core paths and directories (shared across modules)
# ---------------------------------------------------------------------------
if getattr(sys, "frozen", False):
APP_DIR = Path(sys.executable).resolve().parent
else:
APP_DIR = Path(__file__).parent.resolve()
def get_user_preferences_dir() -> Path:
"""
Get the user preferences directory following platform conventions.
- macOS: ~/Library/Preferences/com.audiohacking.AceForge/
- Windows/Linux: APP_DIR (fallback to app directory)
"""
system = platform.system()
if system == "Darwin": # macOS
pref_dir = Path.home() / "Library" / "Preferences" / "com.audiohacking.AceForge"
pref_dir.mkdir(parents=True, exist_ok=True)
return pref_dir
# Fallback for Windows/Linux: use app directory
return APP_DIR
def get_user_data_dir() -> Path:
"""
Get the user data directory following platform conventions.
- macOS: ~/Library/Application Support/AceForge/
- Windows/Linux: APP_DIR (fallback to app directory)
"""
system = platform.system()
if system == "Darwin": # macOS
data_dir = Path.home() / "Library" / "Application Support" / "AceForge"
data_dir.mkdir(parents=True, exist_ok=True)
return data_dir
# Fallback for Windows/Linux: use app directory
return APP_DIR
# Configuration file for user settings
CONFIG_PATH = get_user_preferences_dir() / "aceforge_config.json"
def load_config() -> dict:
"""Load configuration from aceforge_config.json or return defaults."""
if CONFIG_PATH.exists():
try:
with CONFIG_PATH.open("r", encoding="utf-8") as f:
config = json.load(f)
return config
except Exception as e:
print(f"[AceForge] Warning: Failed to load config: {e}", flush=True)
return {}
def save_config(config: dict) -> None:
"""Save configuration to aceforge_config.json."""
try:
with CONFIG_PATH.open("w", encoding="utf-8") as f:
json.dump(config, f, indent=2)
except Exception as e:
print(f"[AceForge] Warning: Failed to save config: {e}", flush=True)
def get_models_folder() -> Path:
"""
Get the configured models folder path, or default based on platform:
- macOS: ~/Library/Application Support/AceForge/models/
- Windows/Linux: APP_DIR / ace_models
"""
config = load_config()
models_path = config.get("models_folder")
if models_path:
path = Path(models_path)
# Validate that the path exists or can be created
try:
path.mkdir(parents=True, exist_ok=True)
return path
except Exception as e:
print(f"[AceForge] Warning: Cannot use configured models folder {models_path}: {e}", flush=True)
print("[AceForge] Falling back to default models folder.", flush=True)
# Default path based on platform
system = platform.system()
if system == "Darwin": # macOS
default_path = get_user_data_dir() / "models"
else:
# Windows/Linux: use app directory
default_path = APP_DIR / "ace_models"
default_path.mkdir(parents=True, exist_ok=True)
return default_path
def set_models_folder(path: str) -> bool:
"""Set the models folder path in configuration."""
try:
path_obj = Path(path).resolve()
# Try to create the directory to validate the path
path_obj.mkdir(parents=True, exist_ok=True)
config = load_config()
config["models_folder"] = str(path_obj)
save_config(config)
# Update environment variable for HF_HOME
os.environ["HF_HOME"] = str(path_obj)
return True
except Exception as e:
print(f"[AceForge] Error setting models folder: {e}", flush=True)
return False
# Where finished tracks go
def _get_default_output_dir() -> Path:
"""Get default output directory based on platform."""
system = platform.system()
if system == "Darwin": # macOS
output_dir = get_user_data_dir() / "generated"
else:
output_dir = APP_DIR / "generated"
output_dir.mkdir(parents=True, exist_ok=True)
return output_dir
DEFAULT_OUT_DIR = str(_get_default_output_dir())
def get_output_dir() -> str:
"""Return configured global output directory, or default. Used when client does not send out_dir."""
config = load_config()
path = config.get("output_dir")
if path:
try:
p = Path(path).resolve()
p.mkdir(parents=True, exist_ok=True)
return str(p)
except Exception as e:
print(f"[AceForge] Invalid output_dir in config: {e}", flush=True)
return DEFAULT_OUT_DIR
def get_next_available_output_path(out_dir: Path | str, base_stem: str, ext: str = ".wav") -> Path:
"""
Return a path under out_dir for the given base name and extension that does not
yet exist. If the exact path exists, appends -1, -2, -3, etc. to avoid overwriting.
base_stem should not include the extension (e.g. "My Track" not "My Track.wav").
"""
out_dir = Path(out_dir)
out_dir.mkdir(parents=True, exist_ok=True)
if not ext.startswith("."):
ext = "." + ext
stem = (base_stem or "").strip()
if not stem:
stem = "output"
# Sanitize: remove path separators
stem = stem.replace("/", "_").replace("\\", "_").replace(":", "_")
candidate = out_dir / f"{stem}{ext}"
if not candidate.exists():
return candidate
idx = 1
while True:
candidate = out_dir / f"{stem}-{idx}{ext}"
if not candidate.exists():
return candidate
idx += 1
# Presets / tracks metadata / user presets
# Keep these in APP_DIR as they're bundled with the application
# User presets go in user data directory
PRESETS_PATH = APP_DIR / "presets.json"
TRACK_META_PATH = get_user_data_dir() / "tracks_meta.json" if platform.system() == "Darwin" else APP_DIR / "tracks_meta.json"
USER_PRESETS_PATH = get_user_data_dir() / "user_presets.json" if platform.system() == "Darwin" else APP_DIR / "user_presets.json"
# Shared location for ACE-Step base model weights used by the LoRA trainer.
# Use the same location as get_models_folder() for consistency
ACE_TRAINER_MODEL_ROOT = get_models_folder()
# Root for all ACE-Step training datasets (LoRA + MuFun).
def _get_training_data_root() -> Path:
"""Get training data root directory based on platform."""
system = platform.system()
if system == "Darwin": # macOS
training_dir = get_user_data_dir() / "training_datasets"
else:
training_dir = APP_DIR / "training_datasets"
training_dir.mkdir(parents=True, exist_ok=True)
return training_dir
TRAINING_DATA_ROOT = _get_training_data_root()
# Training configs (JSON files for LoRA hyperparameters)
# Keep these in APP_DIR as they're bundled configuration templates
TRAINING_CONFIG_ROOT = APP_DIR / "training_config"
TRAINING_CONFIG_ROOT.mkdir(parents=True, exist_ok=True)
DEFAULT_LORA_CONFIG = TRAINING_CONFIG_ROOT / "default_config.json"
# Where custom LoRA adapters live
def _get_custom_lora_root() -> Path:
"""Get custom LoRA adapters directory based on platform."""
system = platform.system()
if system == "Darwin": # macOS
lora_dir = get_user_data_dir() / "custom_lora"
else:
lora_dir = APP_DIR / "custom_lora"
lora_dir.mkdir(parents=True, exist_ok=True)
return lora_dir
CUSTOM_LORA_ROOT = _get_custom_lora_root()
# Seed vibes (these should match ACE_VIBE_TAGS in generate_ace.py)
SEED_VIBES = [
("any", "Any / Auto"),
("lofi_dreamy", "Lo-fi & Dreamy"),
("chiptunes_upbeat", "Chiptunes – Upbeat"),
("chiptunes_zelda", "Chiptunes – Legend of Zelda Fusion"),
("fantasy", "Fantasy / Orchestral"),
("cyberpunk", "Cyberpunk / Synthwave"),
("misc", "Misc / Other"),
]
# ---------------------------------------------------------------------------
# Version management
# ---------------------------------------------------------------------------
def get_app_version() -> str:
"""
Read the application version from VERSION file.
Falls back to 'dev' if file doesn't exist or can't be read.
The VERSION file is updated by GitHub Actions during release builds.
For frozen apps (PyInstaller), checks multiple locations:
1. sys._MEIPASS (PyInstaller temp extraction directory) - primary location
2. APP_DIR (executable directory) - fallback
3. Bundle root (for macOS app bundles) - fallback
"""
# Try multiple locations for frozen apps
candidates = []
if getattr(sys, "frozen", False):
# For frozen apps, check sys._MEIPASS first (PyInstaller extraction dir)
# This is where PyInstaller extracts bundled files during execution
if hasattr(sys, "_MEIPASS"):
candidates.append(Path(sys._MEIPASS) / "VERSION")
# Also check executable directory (MacOS folder)
candidates.append(APP_DIR / "VERSION")
# For macOS app bundles, also check bundle root (Contents/)
if sys.platform == "darwin":
# sys.executable is Contents/MacOS/AceForge_bin
# APP_DIR is Contents/MacOS/
# Bundle root (Contents/) is APP_DIR.parent
bundle_root = APP_DIR.parent
candidates.append(bundle_root / "VERSION")
else:
# For development, just check APP_DIR (project root)
candidates.append(APP_DIR / "VERSION")
# Try each candidate location
for version_file in candidates:
if version_file.exists():
try:
with version_file.open("r", encoding="utf-8") as f:
version = f.read().strip()
if version:
print(f"[AceForge] Loaded version '{version}' from {version_file}", flush=True)
return version
except Exception as e:
print(f"[AceForge] Warning: Failed to read VERSION file from {version_file}: {e}", flush=True)
# Default fallback for development builds
print(f"[AceForge] No VERSION file found, using default 'dev'", flush=True)
return "dev"
APP_VERSION = get_app_version()