Files
game-backuper/game_backuper/backuper.py

155 lines
5.9 KiB
Python

from game_backuper.db import Db
from game_backuper.config import (
Config,
Program,
ConfigNormalFile,
ConfigLeveldb,
)
from game_backuper.cml import Opts, OptAction
from threading import Thread
from os.path import exists, join, isdir
from os import mkdir, remove
from game_backuper.file import new_file, copy_file, File, mkdir_for_file
from game_backuper.filetype import FileType
from game_backuper.restorer import RestoreTask
class BackupTask(Thread):
def __init__(self, prog: Program, db: Db, cfg: Config):
Thread.__init__(self, name=f"Backup_{prog.name}")
self.cfg = cfg
self.prog = prog
self.db = db
def run(self):
self.prog.clear_cache()
prog = self.prog.name
bp = join(self.cfg.dest, prog)
if not exists(bp):
mkdir(bp)
fl = self.db.get_file_list(prog)
for f in self.prog.files:
if isinstance(f, ConfigNormalFile):
if exists(f[1]):
if f.name in fl:
fl.remove(f.name)
ori = self.db.get_file(prog, f[0])
nf = new_file(f[1], f[0], prog)
if nf is None:
continue
de = join(bp, f[0])
if ori is not None:
if ori.size == nf.size and ori.hash == nf.hash:
print(f'{prog}: Skip {f[0]}.')
continue
copy_file(f[1], de, f[0], prog)
self.db.set_file(ori.id, nf.size, nf.hash)
else:
copy_file(f[1], de, f[0], prog)
self.db.add_file(nf)
elif isinstance(f, ConfigLeveldb):
from game_backuper.leveldb import have_leveldb
if not have_leveldb:
raise NotImplementedError('Leveldb is not supported.')
if not exists(f.full_path):
continue
if f.name in fl:
fl.remove(f.name)
from game_backuper.leveldb import (
list_leveldb_entries,
leveldb_stats,
leveldb_to_sqlite,
)
ent = list_leveldb_entries(f.full_path, f.domains)
stats = leveldb_stats(f.full_path, ent)
ori = self.db.get_file(prog, f.name)
de = join(bp, f.name + ".db")
if ori is not None:
if ori.size == stats.size and ori.hash == stats.hash:
print(f'{prog}: Skip {f[0]}')
continue
if ori.type is None or ori.type != FileType.LEVELDB:
pp = join(bp, ori.file)
if exists(pp):
remove(pp)
self.db.remove_file(ori)
ori = None
if exists(de):
remove(de)
mkdir_for_file(de)
leveldb_to_sqlite(f.full_path, de, ent)
print(f'{prog}: Covert leveldb done. {f.full_path}({f.name}) -> {de}') # noqa: E501
if ori is None:
nf = File(None, f.name, stats.size, prog, stats.hash,
FileType.LEVELDB)
self.db.add_file(nf)
else:
self.db.set_file(ori.id, stats.size, stats.hash)
for fn in fl:
f = self.db.get_file(prog, fn)
if f.type is None:
de = join(bp, fn)
if exists(de):
remove(de)
print(f'{prog}: Remove {de}({fn})')
self.db.remove_file(f)
if f.type == FileType.LEVELDB:
de = join(bp, fn + '.db')
if exists(de):
remove(de)
print(f'{prog}: Remove {de}({fn})')
self.db.remove_file(f)
class Backuper:
def __init__(self, db: Db, config: Config, opts: Opts):
self.db = db
self.conf = config
self.opts = opts
self.tasks = []
def deal_prog(self, prog: Program):
if self.opts.action == OptAction.BACKUP:
t = BackupTask(prog, self.db, self.conf)
self.tasks.append(t)
t.start()
elif self.opts.action == OptAction.LIST:
print(prog.name)
elif self.opts.action == OptAction.RESTORE:
t = RestoreTask(prog, self.db, self.conf)
self.tasks.append(t)
t.start()
def run(self):
if self.opts.action == OptAction.LIST_LEVELDB_KEY:
from game_backuper.leveldb import have_leveldb
if not have_leveldb:
raise NotImplementedError('Leveldb is not supported.')
from game_backuper.leveldb import list_leveldb_entries
for db in self.opts.programs_list:
if exists(db):
if isdir(db):
print(f'Keys in "{db}":')
for i in list_leveldb_entries(db):
print(i)
else:
raise FileExistsError(f'"{db}" should be a directory.')
else:
raise FileNotFoundError(f'Can not find "{db}"')
elif self.opts.programs_list is None:
for prog in self.conf.progs:
self.deal_prog(prog)
else:
for n in self.opts.programs_list:
if n not in self.conf.progs_name:
raise ValueError(f'Can not find "{n}" in config file.')
for n in self.opts.programs_list:
prog = self.conf.progs[self.conf.progs_name.index(n)]
self.deal_prog(prog)
self.wait()
return 0
def wait(self):
for task in self.tasks:
task.join()