Skip to content

Commit

Permalink
add tar_xz support, add compress_level in config (default 1)
Browse files Browse the repository at this point in the history
resolved #43
  • Loading branch information
Fallen-Breath committed May 4, 2023
1 parent a484e50 commit 7c37b99
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 31 deletions.
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ mcd_root/
需要备份的世界文件夹列表,原版服务端只会有一个世界,在默认值基础上填上世界文件夹的名字即可

对于非原版服务端如水桶、水龙头服务端,会有三个世界文件夹,此时可填写:

```
"world_names": [
"world",
Expand Down Expand Up @@ -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

默认值:
Expand Down
28 changes: 17 additions & 11 deletions README_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

```
Expand All @@ -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`

Expand Down Expand Up @@ -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": [
Expand Down Expand Up @@ -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:
Expand Down
39 changes: 26 additions & 13 deletions quick_backup_multi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 *

Expand All @@ -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)
Expand All @@ -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):
Expand Down Expand Up @@ -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))
Expand All @@ -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))
Expand All @@ -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):
Expand Down
5 changes: 3 additions & 2 deletions quick_backup_multi/config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Dict
from typing import List, Dict, Optional

from mcdreforged.api.utils.serializer import Serializable

Expand All @@ -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,
Expand Down

0 comments on commit 7c37b99

Please sign in to comment.