diff --git a/README.md b/README.md index 017915f..2fa83d9 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,7 @@ mcd_root/ 需要备份的世界文件夹列表,原版服务端只会有一个世界,在默认值基础上填上世界文件夹的名字即可 对于非原版服务端如水桶、水龙头服务端,会有三个世界文件夹,此时可填写: + ``` "world_names": [ "world", @@ -200,17 +201,25 @@ mcd_root/ 备份的储存格式 -| 值 | 含义 | -|----------|-------------------------------------------------------------------| -| `plain` | 直接复制文件夹/文件来储存。默认值,这同时也是 v1.8 以前版本的 QBM 唯一支持的储存格式 | -| `tar` | 使用 tar 格式直接打包储存至 `backup.tar` 文件中。推荐使用,可有效减少文件的数量,但无法方便地访问备份里面的文件 | -| `tar_gz` | 使用 tar.gz 格式压缩打包储存至 `backup.tar.gz` 文件中。能减小备份体积,但是备份/回档的耗时将极大增加 | +| 值 | 含义 | +|----------|---------------------------------------------------------------------------| +| `plain` | 直接复制文件夹/文件来储存。默认值,这同时也是 v1.8 以前版本的 QBM 唯一支持的储存格式 | +| `tar` | 使用 tar 格式直接打包储存至 `backup.tar` 文件中。推荐使用,可有效减少文件的数量,但无法方便地访问备份里面的文件 | +| `tar_gz` | 使用 tar.gz 格式压缩打包储存至 `backup.tar.gz` 文件中。能减小备份体积,但是备份/回档的耗时将显著增加。支持自定义压缩等级 | +| `tar_xz` | 使用 tar.xz 格式压缩打包储存至 `backup.tar.xz` 文件中。能最大化地减小备份体积,但是备份/回档的耗时将极大增加 | 槽位的备份模式会储存在槽位的 `info.json` 中,并在回档时读取,因此的不同的槽位可以有着不同的储存格式。 若其值不存在,QBM 会假定这个槽位是由旧版 QBM 创建的,并使用默认值 `plain` 若配置文件中的 `backup_format` 非法,则会使用默认值 `plain` +### compress_level + +一个 1 ~ 9 的整数,代表在 `backup_format` 选项为 `tar_gz` 时,使用的压缩等级。 +等级越高,压缩率相对越高,耗时也越高 + +默认值:1 + ### minimum_permission_level 默认值: diff --git a/README_en.md b/README_en.md index cca7717..e01d20a 100644 --- a/README_en.md +++ b/README_en.md @@ -107,8 +107,6 @@ If turn off auto save when making backup or not ### ignored_files -If ignore file `session.lock` during backup, which can - Default: ``` @@ -117,7 +115,7 @@ Default: ] ``` -A list of file names to be ignored during backup. It contains `session.lock` by default to solve the back up failure problem caused by `session.lock` being occupied by the server +A list of file names to be ignored during backup. It contains `session.lock` by default to solve the backup failure problem caused by `session.lock` being occupied by the server If the name string starts with `*`, then it will ignore files with name ending with specific string, e.g. `*.test` makes all files ends with `.test` be ignored, like `a.test` @@ -166,7 +164,9 @@ Default: ] ``` -A list of world folder that you want to backup. For vanilla there should be only 1 folder. For not vanilla server like bukkit or paper, there are 3 folders. You can write like: +A list of world folder that you want to back up. For vanilla there should be only 1 folder. + +For not vanilla server like bukkit or paper, there are 3 folders. You can write like: ``` "world_names": [ @@ -201,19 +201,25 @@ Doing `!!qb back` will restore everything from world name symlink to the final a The format of the stored backup -| Value | Explanation | -|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `plain` | Store the backup directly via file / directory copy. The default value, the only supported format in QBM < v1.8 | -| `tar` | Pack the files into `backup.tar` in tar format. Recommend value. It can significantly reduce the file amount. Although you cannot access files inside the backup easily | -| `tar_gz` | Compress the files into `backup.tar.gz` in tar.gz format. The backup size will be smaller, but the time cost in backup / restore will increase quite a lot | - -槽位的备份模式会储存在槽位的 `info.json` 中,并在回档时读取。若其值不存在,则使用默认值 `plain`,对应着旧版 QBM 的表现 +| Value | Explanation | +|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `plain` | Store the backup directly via file / directory copy. The default value, the only supported format in QBM < v1.8 | +| `tar` | Pack the files into `backup.tar` in tar format. Recommend value. It can significantly reduce the file amount. Although you cannot access files inside the backup easily | +| `tar_gz` | Compress the files into `backup.tar.gz` in tar.gz format. The backup size will be smaller, but the time cost in backup / restore will increase | +| `tar_xz` | Compress the files into `backup.tar.xz` in tar.xz format. The backup size will be much smaller, but the time cost in backup / restore will greatly increase | The backup format of the slot will be stored inside the `info.json` of the slot, and will be read when restoring, so you can have different backup formats in your slots. If the backup format value doesn't exist, QBM will assume that it's a backup created from old QBM, and use the default `plain` format If the `backup_format` value is invalid in the config file, the default value `plain` will be used +### compress_level + +An integer in range 1 ~ 9, representing the compress level when config `backup_format` is set to `tar_gz`. +The higher the level is, the higher the compression rate will be, and the longer the time it will take. + +Default: 1 + ### minimum_permission_level Default: diff --git a/quick_backup_multi/__init__.py b/quick_backup_multi/__init__.py index 4befbe5..4fa1d51 100644 --- a/quick_backup_multi/__init__.py +++ b/quick_backup_multi/__init__.py @@ -7,7 +7,7 @@ import time from enum import Enum, auto from threading import Lock -from typing import Optional, Any, Callable, Tuple +from typing import Optional, Any, Callable, Tuple, NamedTuple from mcdreforged.api.all import * @@ -32,17 +32,28 @@ class CopyWorldIntent(Enum): class BackupFormat(Enum): - plain = auto() - tar = auto() - tar_gz = auto() + class Item(NamedTuple): + suffix: str + supports_compress_level: bool + + plain = Item('', False) + tar = Item('.tar', False) + tar_gz = Item('.tar.gz', True) + tar_xz = Item('.tar.xz', False) @classmethod def of(cls, mode: str) -> 'BackupFormat': try: return cls[mode] - except Exception: + except KeyError: return cls.plain + def get_file_name(self, base_name: str) -> str: + return base_name + self.value.suffix + + def supports_compress_level(self) -> bool: + return self.value.supports_compress_level + def get_backup_format() -> BackupFormat: return BackupFormat.of(config.backup_format) @@ -68,12 +79,7 @@ def command_run(message: Any, text: Any, command: str) -> RTextBase: def get_backup_file_name(backup_format: BackupFormat): if backup_format == BackupFormat.plain: raise ValueError('plain mode is not supported') - elif backup_format == BackupFormat.tar: - return 'backup.tar' - elif backup_format == BackupFormat.tar_gz: - return 'backup.tar.gz' - else: - raise ValueError('unknown backup mode {}'.format(backup_format)) + return backup_format.get_file_name('backup') def copy_worlds(src: str, dst: str, intent: CopyWorldIntent, *, backup_format: Optional[BackupFormat] = None): @@ -104,7 +110,7 @@ def copy_worlds(src: str, dst: str, intent: CopyWorldIntent, *, backup_format: O shutil.copy(src_path, dst_path) else: server_inst.logger.warning('{} does not exist while copying ({} -> {})'.format(src_path, src_path, dst_path)) - elif backup_format == BackupFormat.tar or backup_format == BackupFormat.tar_gz: + elif backup_format in [BackupFormat.tar, BackupFormat.tar_gz, BackupFormat.tar_xz]: if intent == CopyWorldIntent.restore: tar_path = os.path.join(src, get_backup_file_name(backup_format)) server_inst.logger.info('extracting {} -> {}'.format(tar_path, dst)) @@ -113,12 +119,17 @@ def copy_worlds(src: str, dst: str, intent: CopyWorldIntent, *, backup_format: O else: # backup if backup_format == BackupFormat.tar_gz: tar_mode = 'w:gz' + elif backup_format == BackupFormat.tar_xz: + tar_mode = 'w:xz' else: tar_mode = 'w' if not os.path.isdir(dst): os.makedirs(dst) tar_path = os.path.join(dst, get_backup_file_name(backup_format)) - with tarfile.open(tar_path, tar_mode) as backup_file: + kwargs = {} + if backup_format.supports_compress_level() and 1 <= config.compress_level <= 9: + kwargs['compresslevel'] = config.compress_level + with tarfile.open(tar_path, tar_mode, **kwargs) as backup_file: for world in config.world_names: src_path = os.path.join(src, world) server_inst.logger.info('storing {} -> {}'.format(src_path, tar_path)) @@ -130,6 +141,8 @@ def tar_filter(info: tarfile.TarInfo) -> Optional[tarfile.TarInfo]: backup_file.add(src_path, arcname=world, filter=tar_filter) else: server_inst.logger.warning('{} does not exist while storing'.format(src_path)) + else: + server_inst.logger.error('Unknown backup format {}'.format(backup_format.name)) def remove_worlds(folder: str): diff --git a/quick_backup_multi/config.py b/quick_backup_multi/config.py index ff22adc..933f9f0 100644 --- a/quick_backup_multi/config.py +++ b/quick_backup_multi/config.py @@ -1,4 +1,4 @@ -from typing import List, Dict +from typing import List, Dict, Optional from mcdreforged.api.utils.serializer import Serializable @@ -23,7 +23,8 @@ class Configuration(Serializable): world_names: List[str] = [ 'world' ] - backup_format: str = 'plain' # "plain", "tar", "tar_gz" + backup_format: str = 'plain' # "plain", "tar", "tar_gz", "tar_xz" + compress_level: int = 1 # in range [1, 9] # 0:guest 1:user 2:helper 3:admin 4:owner minimum_permission_level: Dict[str, int] = { 'make': 1,