diff --git a/example.yaml b/example.yaml index eb09d4d..966ad63 100644 --- a/example.yaml +++ b/example.yaml @@ -2,8 +2,8 @@ dest: /path/to/store/backup/files # The programs will store database and backup enable_pcre2: false # Optional. Default value: false. Try to use PCRE2 first. PCRE2 may be a little slower than internal regex library. remove_old_files: true # Optional. Default value: true. Remove unneeded backup files which already deleted in source tree when backuping files. ignore_hidden_files: true # Optional. Default value: true. Whether to ignore files which its name starts with ".". Only effect folder which type is "path". -compress_method: "bzip2" # Optional. Default value: null. Supported value: "bzip2", "gzip", "lzma" -compress_level: 6 # Optional. Default value: null. bzip2 support 1-9 (Default: 9). gzip support 0-9 (Default: 9). lzma support 0-9 (Default: 6). +compress_method: "bzip2" # Optional. Default value: null. Supported value: "bzip2", "gzip", "lzma", "lzip" +compress_level: 6 # Optional. Default value: null. bzip2 support 1-9 (Default: 9). gzip support 0-9 (Default: 9). lzma or lzip support 0-9 (Default: 6). programs: - name: Your program name # This name is used to identify different application. base: /path/to/save/path # Must be absoulte path. diff --git a/game_backuper/compress.py b/game_backuper/compress.py index 974fb70..8455ffb 100644 --- a/game_backuper/compress.py +++ b/game_backuper/compress.py @@ -13,6 +13,14 @@ try: have_lzma = True except ImportError: have_lzma = False +try: + from lzip import ( + FileEncoder as LZIPFileEncoder, + decompress_file_iter as LZIP_decompress_file_iter, + ) + have_lzip = True +except ImportError: + have_lzip = False from enum import IntEnum, unique try: from functools import cached_property @@ -27,6 +35,7 @@ class CompressMethod(IntEnum): BZIP2 = 0 GZIP = 1 LZMA = 2 + LZIP = 3 @staticmethod def from_str(v: str) -> IntEnum: @@ -38,6 +47,8 @@ class CompressMethod(IntEnum): return CompressMethod.GZIP elif t == "lzma": return CompressMethod.LZMA + elif t == "lzip": + return CompressMethod.LZIP else: raise TypeError('Must be str.') @@ -80,6 +91,17 @@ class CompressConfig: else: raise ValueError('lzma: compress_level should be 0-9.') self._ext = ".xz" + elif self._method == CompressMethod.LZIP: + if not have_lzip: + raise NotImplementedError("lzip not supported.") + if level is None: + self._level = 6 + else: + if isinstance(level, int) and level >= 0 and level <= 9: + self._level = level + else: + raise ValueError('lzip: compress_level should be 0-9.') + self._ext = ".lz" self._chunk_size = 1048576 def __repr__(self): @@ -110,6 +132,8 @@ if have_gzip: supported_exts.append('.gz') if have_lzma: supported_exts.append('.xz') +if have_lzip: + supported_exts.append('.lz') def sizeof_fmt(num, suffix='B'): @@ -155,6 +179,14 @@ def compress(src: str, dest: str, c: CompressConfig, name: str, prog: str): f.write(a) a = t.read(cs) del a + elif c.method == CompressMethod.LZIP: + with open(src, 'rb') as t: + with LZIPFileEncoder(fn, c.level) as f: + a = t.read(cs) + while a != b'': + f.compress(a) + a = t.read(cs) + del a i = compress_info(getsize(src), getsize(fn)) print(f'{prog}: Compressed {src}({name}) -> {fn} ({i})') del i @@ -194,5 +226,11 @@ def decompress(src: str, dest: str, c: CompressConfig, name: str, prog: str): t.write(a) a = f.read(cs) del a + elif c.method == CompressMethod.LZIP: + with open(dest, 'wb') as t: + f = LZIP_decompress_file_iter(fn, chunk_size=cs) + for a in f: + t.write(a) + del a i = compress_info(getsize(dest), getsize(fn)) print(f'{prog}: Decompressed {fn}({name}) -> {dest} ({i})') diff --git a/requirements.txt b/requirements.txt index 498b0a0..42b3e5d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pyyaml plyvel[leveldb] +lzip[lzip] diff --git a/setup.py b/setup.py index 15baa1b..b364a51 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,8 @@ else: 'console_scripts': ['game-backuper = game_backuper:start'] }, "extras_require": { - "leveldb": "plyvel" + "leveldb": "plyvel", + "lzip": "lzip", }, "python_requires": ">=3.6" }