Last active
June 16, 2025 00:57
-
-
Save ntippie/3f5523a99d7713e5e237c3b8be909fd2 to your computer and use it in GitHub Desktop.
Mac python script to sync Balatro profiles between Steam and iCloud
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/python3 | |
| # adapted from https://github.com/TomJinW/BalatroSave_AppleArcadeMac | |
| import plistlib | |
| from os.path import expanduser, getmtime, join, exists, basename, dirname, isfile | |
| from datetime import datetime | |
| from os import utime, makedirs, listdir | |
| ICLOUD_PATH = f"~/Library/Containers/com.playstack.balatroarcade/Data/Library/Preferences/com.playstack.balatroarcade.plist" | |
| STEAM_PATH = f"~/Library/Application Support/Balatro/" | |
| PROFILE_MAX = 3 | |
| file_names = ["meta", "profile"] | |
| def get_mod_time(path): | |
| try: | |
| return getmtime(expanduser(path)) | |
| except OSError: | |
| return 0 | |
| def get_sync_actions(steam_files, icloud_file): | |
| icloud_mod_time = get_mod_time(icloud_file) | |
| steam_to_icloud = [] | |
| icloud_to_steam = [] | |
| latest_steam_mod_time = 0 | |
| for file in steam_files: | |
| steam_mod_time = get_mod_time(file) | |
| latest_steam_mod_time = max(latest_steam_mod_time, steam_mod_time) | |
| if steam_mod_time > icloud_mod_time: | |
| steam_to_icloud.append(file) | |
| elif icloud_mod_time > steam_mod_time: | |
| icloud_to_steam.append(file) | |
| return steam_to_icloud, icloud_to_steam, latest_steam_mod_time, icloud_mod_time | |
| if __name__ == "__main__": | |
| profile_list = [f"{profile}" for profile in range(1, PROFILE_MAX + 1)] | |
| possible_steam_files = [ | |
| join(STEAM_PATH, profile, f"{file_name}.jkr") | |
| for profile in profile_list | |
| for file_name in file_names | |
| ] | |
| steam_files = [f for f in possible_steam_files if isfile(expanduser(f))] | |
| steam_to_icloud, icloud_to_steam, steam_mod_time, icloud_mod_time = ( | |
| get_sync_actions(steam_files, ICLOUD_PATH) | |
| ) | |
| if steam_to_icloud: | |
| print("π Sync from Steam β iCloud") | |
| with open(expanduser(ICLOUD_PATH), "rb") as file: | |
| plist_data = plistlib.load(file) | |
| for file_name in steam_to_icloud: | |
| base_file_name = basename(file_name) | |
| file_dir = dirname(file_name) | |
| profile = basename(file_dir) | |
| # sync modified times for files in the profile | |
| dir_files = [ | |
| join(file_dir, f) for f in file_names if isfile(join(file_dir, f)) | |
| ] | |
| for dir_file in dir_files: | |
| utime(expanduser(dir_file), (steam_mod_time, steam_mod_time)) | |
| plist_key = f"{profile}__{base_file_name}.data" | |
| with open(expanduser(file_name), "rb") as file: | |
| file_data = file.read() | |
| plist_data[f"{profile}__{base_file_name}.data"] = file_data | |
| with open(expanduser(ICLOUD_PATH), "wb") as file: | |
| plistlib.dump(plist_data, file) | |
| utime(expanduser(ICLOUD_PATH), (steam_mod_time, steam_mod_time)) | |
| if icloud_to_steam: | |
| print("π Sync from iCloud β Steam") | |
| with open(expanduser(ICLOUD_PATH), "rb") as file: | |
| plist_data = plistlib.load(file) | |
| for file_name in icloud_to_steam: | |
| base_file_name = basename(file_name) | |
| profile = basename(dirname(file_name)) | |
| plist_key = f"{profile}__{base_file_name}.data" | |
| if plist_key not in plist_data: | |
| icloud_to_steam.remove(file_name) | |
| continue | |
| key_data = plist_data[plist_key] | |
| profile_path = dirname(file_name) | |
| if not exists(expanduser(profile_path)): | |
| makedirs(expanduser(profile_path)) | |
| with open(expanduser(file_name), "wb") as file: | |
| print(f"Writing {file_name}") | |
| file.write(key_data) | |
| file.close() | |
| utime(expanduser(file_name), (icloud_mod_time, icloud_mod_time)) | |
| if not steam_to_icloud and not icloud_to_steam: | |
| print("β All files are already in sync.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment