Created
December 5, 2022 05:53
-
-
Save Ricket/a6b5a83240a448e27126de2db37052e5 to your computer and use it in GitHub Desktop.
Minecraft backups: prune the backups to just one file per play session (i.e. find groups of consecutive backups and delete all but the newest)
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
from datetime import datetime, timedelta | |
from dateutil import tz | |
from functools import reduce | |
import os | |
from pprint import pprint | |
import re | |
import sys | |
if sys.version_info < (3,7): | |
sys.exit('Python 3.7 guarantees ordered dicts, which this script relies on') | |
dir = r'Z:\srv\minecraft\backups\Enigmatica6Expert\backups' | |
min_time_between_backups = timedelta(hours=3) | |
min_time_ago_to_cull = timedelta(days=14) | |
#date_regex = re.compile(r'^Backup--world--(?P<year>[0-9]+)-(?P<month>[0-9]+)-(?P<day>[0-9]+)--(?P<hour>[0-9]+)-(?P<minute>[0-9]+).zip$') | |
date_regex = re.compile(r'^(?P<year>[0-9]+)-(?P<month>[0-9]+)-(?P<day>[0-9]+)-(?P<hour>[0-9]+)-(?P<minute>[0-9]+)-(?P<second>[0-9]+).zip$') | |
names = os.listdir(dir) | |
names.sort() # it seems to already be sorted, but just in case... | |
def parse_date(filename): | |
match = date_regex.match(filename) | |
if match is None: | |
raise ValueError(f'Invalid filename: {filename}') | |
return datetime(int(match['year']), int(match['month']), int(match['day']), int(match['hour']), int(match['minute']), tzinfo=tz.tzutc()) | |
dates = {name: parse_date(name) for name in names} | |
def sufficiently_spaced(datetime1, datetime2): | |
delta = abs(datetime1 - datetime2) | |
return delta >= min_time_between_backups | |
now = datetime.now(tz=tz.tzlocal()) | |
def add_preserve_entry(map, entry): | |
name, date = entry | |
# if first record, default to preserving it | |
if len(map) == 0: | |
map[name] = True | |
return map | |
# get the previous entry | |
prevname = list(map.keys())[-1] | |
prevdate = dates[prevname] | |
# preserve the current entry | |
map[name] = True | |
# only delete it if it's more than min_time_ago | |
old_enough = (now - prevdate) > min_time_ago_to_cull | |
# if the previous one was within a short delta, don't keep it | |
if old_enough and not sufficiently_spaced(prevdate, date): | |
map[prevname] = False | |
return map | |
preserve = reduce(add_preserve_entry, dates.items(), {}) | |
#pprint(preserve) | |
#exit(0) | |
files_to_delete = [key for key, value in preserve.items() if value is False] | |
#pprint(files_to_delete) | |
files_to_keep = [key for key, value in preserve.items() if value is True] | |
#print("Keeping files:") | |
#pprint(files_to_keep) | |
sizes = {file: os.path.getsize(f'{dir}\{file}') for file in files_to_delete} | |
total_bytes = sum(sizes.values()) | |
bytes_so_far = 0 | |
for file in files_to_delete: | |
os.remove(f'{dir}\{file}') | |
bytes_so_far += sizes[file] | |
progress = bytes_so_far / total_bytes | |
print(f'{file} {progress:.0%}') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment