Updated script to sync and archive playlist files from a remote system

This commit is contained in:
Brian Lee 2025-02-08 09:17:09 -08:00
parent faf42af274
commit 362603d160
3 changed files with 237 additions and 0 deletions

35
scripts/NOTES.md Normal file
View File

@ -0,0 +1,35 @@
# Beat Saber Playlist Scripts
Tools for managing Beat Saber playlists and custom maps across Windows and other systems.
## Map Installation
To copy a custom map to Beat Saber:
```powershell
Copy-Item -Path "map_folder" -Destination "C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\CustomLevels\" -Recurse
```
## Playlist Sync Script
`sabersync.py` is a utility script that:
- Syncs `.bplist` files between systems
- Archives old/deleted playlists
- Optionally syncs Beat Saber video recordings
Usage:
```bash
./sabersync.py [--video] [--help]
```
Options:
- `--video`: Sync video recordings from the remote system
- `--help`: Show help message
The script expects:
- SSH access to a Windows machine with alias `winroar`
- Beat Saber installed at standard Steam paths
- Playlists in `BSManager/BSInstances/1.39.1/Playlists/`
When run with `--video`, it will:
1. Copy videos from the remote system
2. Wait for confirmation
3. Clean up remote video files after transfer

118
scripts/sabersync.py Executable file
View File

@ -0,0 +1,118 @@
#!/usr/bin/env python
# Helper script to backup playlists and copy new ones over
import argparse
import subprocess
import os
import shutil
from pathlib import Path
import sys
import shlex
def usage():
print("Usage: {} [--video]".format(sys.argv[0]))
sys.exit(1)
def run_command(command, capture_output=False, text=True):
try:
result = subprocess.run(command, shell=True, check=True, capture_output=capture_output, text=text)
return result.stdout if capture_output else None
except subprocess.CalledProcessError as e:
print(f"Command '{command}' failed with exit code {e.returncode}")
sys.exit(e.returncode)
def main():
parser = argparse.ArgumentParser(description="Synchronize Beat Saber playlists and optionally videos.", add_help=False)
parser.add_argument('-v', '--video', action='store_true', help='Enable video synchronization')
parser.add_argument('-h', '--help', action='store_true', help='Show help message and exit')
args = parser.parse_args()
if args.help:
usage()
COPY_VIDEOS = args.video
# Define paths
REMOTE_PLAYLISTS = "BSManager/BSInstances/1.39.1/Playlists/"
REMOTE_PLAYLISTS_WIN = "BSManager\\BSInstances\\1.39.1\\Playlists" # For Windows SSH commands
LOCAL_ARCHIVE_DIR = Path.home() / "archive" / "beatsaber-playlists"
LOCAL_ARCHIVE_SUBDIR = LOCAL_ARCHIVE_DIR / "archive"
LOCAL_BPLIST_DIR = Path.cwd()
# Ensure archive subdirectory exists
LOCAL_ARCHIVE_SUBDIR.mkdir(parents=True, exist_ok=True)
# Check for local *.bplist files and sync to remote using scp
bplist_files = list(LOCAL_BPLIST_DIR.glob("*.bplist"))
if bplist_files:
print("Starting synchronization of .bplist files to remote using scp...")
# Build file string with proper quoting using shlex.quote, so wildcard expansion isn't needed
files_str = " ".join(shlex.quote(str(file)) for file in bplist_files)
scp_sync_upload = f"scp {files_str} winroar:\"{REMOTE_PLAYLISTS}\""
run_command(scp_sync_upload)
# Remove local *.bplist files after successful transfer
for f in bplist_files:
f.unlink()
print(f"Removed local file: {f.name}")
# Fetch list of remote *.bplist files
print("Fetching list of remote .bplist files...")
ssh_command = f'ssh winroar "cd {REMOTE_PLAYLISTS_WIN} && dir /b *.bplist"'
remote_bplist = run_command(ssh_command, capture_output=True)
remote_bplist_files = set(remote_bplist.strip().split('\n')) if remote_bplist else set()
# Fetch list of local archive *.bplist files
print("Fetching list of local archived .bplist files...")
local_archive_files = set(f.name for f in LOCAL_ARCHIVE_DIR.glob("*.bplist"))
# Determine deleted files (present locally but not remotely)
deleted_files = local_archive_files - remote_bplist_files
if deleted_files:
print("Identifying deleted playlists...")
for file in deleted_files:
local_file = LOCAL_ARCHIVE_DIR / file
destination_file = LOCAL_ARCHIVE_SUBDIR / file
if local_file.exists():
if destination_file.exists():
destination_file.unlink() # Remove existing file to allow overwrite
print(f"Overwriting existing archived file: {file}")
shutil.move(str(local_file), str(destination_file))
print(f"Archived deleted playlist: {file}")
else:
print("No deleted playlists found.")
# Sync remote *.bplist to local archive directory
print("Downloading current remote .bplist files to local archive...")
scp_sync_download = (
f"scp winroar:\"{REMOTE_PLAYLISTS}*.bplist\" \"{LOCAL_ARCHIVE_DIR}/\""
)
run_command(scp_sync_download)
print("Download complete.")
# Optional video synchronization
if COPY_VIDEOS:
print("Starting video synchronization with scp...")
# Define remote and local video paths
REMOTE_VIDEOS = "/Users/pleb/Videos/Beat Saber/"
LOCAL_DOWNLOADS = Path.home() / "Downloads" / "Beat Saber"
# Ensure local downloads directory exists
LOCAL_DOWNLOADS.mkdir(parents=True, exist_ok=True)
# Construct scp command to copy videos from remote to local Downloads folder
scp_sync_videos = (
f"scp -r winroar:\"{REMOTE_VIDEOS}\" \"{LOCAL_DOWNLOADS}\""
)
run_command(scp_sync_videos)
print("Video files synchronized successfully.")
input("Press [Enter] to delete videos on winroar.")
# Delete videos on remote after confirmation
ssh_delete_command = f'ssh winroar \'rm -rf "{REMOTE_VIDEOS}"\''
run_command(ssh_delete_command)
print("Remote video directory deleted.")
if __name__ == "__main__":
main()

84
scripts/sabersync.sh Executable file
View File

@ -0,0 +1,84 @@
#!/usr/bin/env bash
set -e
# Function to display usage
usage() {
echo "Usage: $0 [--video]"
exit 1
}
# Parse command-line arguments
COPY_VIDEOS=false
while [[ "$#" -gt 0 ]]; do
case $1 in
-v|--video)
COPY_VIDEOS=true
shift
;;
-h|--help)
usage
;;
*)
echo "Unknown parameter passed: $1"
usage
;;
esac
done
if [ -n "$(find . -maxdepth 1 -type f -name "*.bplist" -print -quit)" ]; then
scp ./*.bplist winroar:BSManager/BSInstances/1.39.1/Playlists/
rm -v ./*.bplist
fi
##
## Playlist Archive
##
# Define remote and local directories for playlist synchronization
REMOTE_PLAYLISTS="BSManager/BSInstances/1.39.1/Playlists/"
LOCAL_ARCHIVE_DIR=~/archive/beatsaber-playlists/
LOCAL_ARCHIVE_SUBDIR=~/archive/beatsaber-playlists/archive/
PREVIOUS_LIST=~/archive/beatsaber-playlists/previous_bplist_files.txt
# Ensure archive subdirectory exists
mkdir -p "$LOCAL_ARCHIVE_SUBDIR"
# Fetch current remote *.bplist files
ssh winroar "cd \"${REMOTE_PLAYLISTS}\" && dir /B *.bplist" > current_bplist_files.txt
# Compare with previous list to find deleted files
if [[ -f "$PREVIOUS_LIST" ]]; then
deleted_files=$(comm -23 <(sort "$PREVIOUS_LIST") <(sort current_bplist_files.txt))
for file in $deleted_files; do
filename=$(basename "$file")
if [[ -f "${LOCAL_ARCHIVE_DIR}${filename}" ]]; then
mv "${LOCAL_ARCHIVE_DIR}${filename}" "$LOCAL_ARCHIVE_SUBDIR"
echo "Archived deleted playlist: $filename"
fi
done
fi
# Update the previous list
mv current_bplist_files.txt "$PREVIOUS_LIST"
# Sync playlist files from remote to local
scp -q winroar:"${REMOTE_PLAYLISTS}*.bplist" "$LOCAL_ARCHIVE_DIR"
# Optional video synchronization
if $COPY_VIDEOS; then
echo "Starting video synchronization..."
# Copy videos from remote to local Downloads folder
scp -r winroar:"Videos/Beat Saber/" "$HOME/Downloads/"
echo "Video files copied successfully."
echo "Press [Enter] to delete videos on winroar."
read -r
# Delete videos on remote after confirmation
ssh winroar 'rmdir Videos/Beat\ Saber /q /s'
echo "Remote video directory deleted."
fi