diff --git a/README.md b/README.md index f208f1f..45fcb5a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ CLI tool for downloading subtitles from napiprojekt.pl, fork of [gabrys/napi.py](https://github.com/gabrys/napi.py) ## prerequisites -- Python 3.7 or newer +- Python 3.9 or newer ## installation - `pip install napi-py` for user-wide installation diff --git a/mypy.ini b/mypy.ini index 2c31f82..a62c0c4 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,5 +1,5 @@ [mypy] warn_unused_configs = True -[mypy-py7zlib] +[mypy-py7zr] ignore_missing_imports = True \ No newline at end of file diff --git a/napi/read_7z.py b/napi/read_7z.py index 0af49a7..7a4cf0e 100644 --- a/napi/read_7z.py +++ b/napi/read_7z.py @@ -1,14 +1,71 @@ from io import BytesIO -from typing import Optional -from py7zlib import Archive7z, ArchiveError +from typing import Optional, Dict +import threading +import py7zr NAPI_ARCHIVE_PASSWORD = "iBlm8NTigvru0Jr0" -def un7zip_api_response(content_7z: bytes) -> Optional[bytes]: +class InMemoryIO(py7zr.io.Py7zIO): + def __init__(self, fname: str): + self.fname = fname + self._buf = bytearray() + self._length = 0 + self._lock = threading.Lock() + + def write(self, data: bytes) -> None: + with self._lock: + self._buf.extend(data) + self._length += len(data) + + def read(self, size: Optional[int] = None) -> bytes: + return b"" + + def seek(self, offset: int, whence: int = 0) -> int: + return offset + + def flush(self) -> None: + pass + + def size(self) -> int: + return self._length + + def getvalue(self) -> bytes: + with self._lock: + return bytes(self._buf) + + +class InMemoryFactory(py7zr.io.WriterFactory): + def __init__(self, target_filename: Optional[str] = None): + self.products: Dict[str, InMemoryIO] = {} + self.target_filename = target_filename + + def create(self, filename: str) -> py7zr.io.Py7zIO: + if self.target_filename is not None and filename != self.target_filename: + product = InMemoryIO(filename) + else: + product = InMemoryIO(filename) + self.products[filename] = product + return product + + +def un7zip_api_response(content_7z: bytes, target_filename: Optional[str] = None) -> Optional[bytes]: try: buffer = BytesIO(content_7z) - archive = Archive7z(buffer, password=NAPI_ARCHIVE_PASSWORD) - return archive.getmember(0).read() - except ArchiveError: + with py7zr.SevenZipFile(buffer, mode="r", password=NAPI_ARCHIVE_PASSWORD) as archive: + factory = InMemoryFactory(target_filename=target_filename) + archive.extractall(factory=factory) + + if target_filename: + product = factory.products.get(target_filename) + return product.getvalue() if product else None + + if not factory.products: + return None + first_product = next(iter(factory.products.values())) + return first_product.getvalue() + + except (py7zr.exceptions.Bad7zFile, + py7zr.exceptions.PasswordRequired, + py7zr.exceptions.UnsupportedCompressionMethodError): return None diff --git a/pyproject.toml b/pyproject.toml index 1f62c46..8046635 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "napi-py" -version = "1.2.4" +version = "1.2.5" description = "CLI tool for downloading subtitles from napiprojekt.pl" authors = ["emkor93 "] license = "GPL-3.0 License" @@ -11,11 +11,11 @@ keywords = ["napiprojekt", "subs", "subtitles", "movie", "film"] packages = [{ include = "napi" }] [tool.poetry.dependencies] -python = "^3.7" -pylzma = "^0.5.0" +python = "^3.9" +py7zr = "^1.0.0" chardet = "^5.2.0" -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] mypy = "^1.4.0" pytest = "^7.4.4" black = "^22.3.0" @@ -29,4 +29,4 @@ build-backend = "poetry.core.masonry.api" [tool.black] line-length = 120 -target-version = ['py37', 'py38', 'py39', 'py310'] +target-version = ['py39', 'py310']