Skip to content

Instantly share code, notes, and snippets.

@legraphista
Last active June 3, 2020 14:38
Show Gist options
  • Save legraphista/433cddcba96b13a315644d7110075a11 to your computer and use it in GitHub Desktop.
Save legraphista/433cddcba96b13a315644d7110075a11 to your computer and use it in GitHub Desktop.
Backup files when they change! (I'm looking at you, Minecraft Dungeons....)

Requirements

Save file location

Microsoft Store

C:\Users\{USER}\AppData\Local\Packages\Microsoft.Lovika_8wekyb3d8bbwe\LocalCache\Local\Dungeons\{ID}

Standalone

C:\Users\{USER}\AppData\Local\Dungeons\{ID}

How to use

node <Save file location> <Existing backup dir>

The script will backup all folder contents everytime something chanes, but a trottle of one minute.
The throttle has a rising edge and a falling edge trigger, to it will backup the first change and last change.
MC Dungeons likes to save to the save file 10 times in a row, lol

// C:\Users\Stefan\AppData\Local\Packages\Microsoft.Lovika_8wekyb3d8bbwe\LocalCache\Local\Dungeons
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const [toWatch, toBackup] = process.argv.slice(2);
assert(fs.existsSync(toWatch), `cannot find dir ${toWatch}`);
assert(fs.existsSync(toBackup), `cannot find backup dir ${toBackup}`);
console.log('\n\n\n\n');
console.log('stating watcher for', toWatch);
console.log('will backup to', toBackup);
const watcher = fs.watch(toWatch, {recursive: true});
watcher.on('change', (event, path) => event === 'change' && backup());
const BACKUP_THROTTLE_TIME = 1 * 60 * 1000;
let lastBackup = 0;
let postPoneTimer = null;
function backup() {
const now = new Date();
console.log('\n');
console.log('~~~~~', now.toString(), '~~~~~');
if(lastBackup + BACKUP_THROTTLE_TIME > now.getTime()){
console.log('backup postponed, last backup was too close');
if(!postPoneTimer){
postPoneTimer = setTimeout(() => {
console.log('\n\n\n\n ~~~~ retrying postponed backup... ~~~~~');
postPoneTimer = null;
backup();
}, BACKUP_THROTTLE_TIME + 1000);
}
return;
}
lastBackup = now.getTime();
const dirToBackupTo = path.join(toBackup, `${now.getFullYear()}-${pad2(now.getMonth())}-${pad2(now.getDate())}-${pad2(now.getHours())}-${pad2(now.getMinutes())}-${pad2(now.getSeconds())}-${Date.now()}`);
console.log('backing up:', toWatch, 'to:', dirToBackupTo);
fs.mkdirSync(dirToBackupTo);
copyFolderRecursiveSync(toWatch, dirToBackupTo);
console.log('\n');
}
backup();
function copyFileSync(source, target) {
let targetFile = target;
//if target is a directory a new file with the same name will be created
if (fs.existsSync(target)) {
if (fs.lstatSync(target).isDirectory() ) {
targetFile = path.join(target, path.basename(source));
}
}
fs.copyFileSync(source, targetFile);
}
function copyFolderRecursiveSync(source, target) {
console.group();
//check if folder needs to be created or integrated
const targetFolder = path.join(target, path.basename(source));
if (!fs.existsSync(targetFolder)) {
//console.log('mkdir', targetFolder);
fs.mkdirSync(targetFolder);
}
//copy
if (fs.lstatSync(source).isDirectory()) {
const files = fs.readdirSync(source);
files.forEach( file => {
const curSource = path.join(source, file);
if (fs.lstatSync(curSource).isDirectory()) {
copyFolderRecursiveSync(curSource, targetFolder);
} else {
//console.log('copy', curSource, '->', targetFolder);
copyFileSync(curSource, targetFolder);
}
});
}
console.groupEnd();
}
function pad2(x) {
return x > 9 ? x : `0${x}`;
}
node ".\Minecraft Dungeons Backuper.js" "C:\Users\Stefan\AppData\Local\Packages\Microsoft.Lovika_8wekyb3d8bbwe\LocalCache\Local\Dungeons\2533274963056686" "C:\Users\Stefan\Desktop\MCD Backups\Backups"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment