From 756e9f10c46d4d714aafd3a2558566172097c0e5 Mon Sep 17 00:00:00 2001 From: ksz <22708888+ksz16@users.noreply.github.com> Date: Tue, 9 Dec 2025 00:19:53 +0100 Subject: [PATCH 1/2] use py7zr instead of pylzma --- README.md | 2 +- mypy.ini | 2 +- napi/read_7z.py | 22 +++++++++++++++++----- pyproject.toml | 10 +++++----- 4 files changed, 24 insertions(+), 12 deletions(-) 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..1399358 100644 --- a/napi/read_7z.py +++ b/napi/read_7z.py @@ -1,14 +1,26 @@ from io import BytesIO from typing import Optional -from py7zlib import Archive7z, ArchiveError +import py7zr +import tempfile +import os NAPI_ARCHIVE_PASSWORD = "iBlm8NTigvru0Jr0" - def un7zip_api_response(content_7z: bytes) -> Optional[bytes]: try: buffer = BytesIO(content_7z) - archive = Archive7z(buffer, password=NAPI_ARCHIVE_PASSWORD) - return archive.getmember(0).read() - except ArchiveError: + with tempfile.TemporaryDirectory() as tmpdir: + with py7zr.SevenZipFile(buffer, mode="r", password=NAPI_ARCHIVE_PASSWORD) as archive: + archive.extractall(path=tmpdir) + + files = os.listdir(tmpdir) + if not files: + return None + first_file = os.path.join(tmpdir, files[0]) + with open(first_file, "rb") as f: + return f.read() + + 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'] From aabe18c049d754ae0784209c98b64af27d5dd04f Mon Sep 17 00:00:00 2001 From: ksz <22708888+ksz16@users.noreply.github.com> Date: Tue, 9 Dec 2025 09:14:31 +0100 Subject: [PATCH 2/2] use WriterFactory --- napi/read_7z.py | 73 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/napi/read_7z.py b/napi/read_7z.py index 1399358..7a4cf0e 100644 --- a/napi/read_7z.py +++ b/napi/read_7z.py @@ -1,24 +1,69 @@ from io import BytesIO -from typing import Optional +from typing import Optional, Dict +import threading import py7zr -import tempfile -import os 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) - with tempfile.TemporaryDirectory() as tmpdir: - with py7zr.SevenZipFile(buffer, mode="r", password=NAPI_ARCHIVE_PASSWORD) as archive: - archive.extractall(path=tmpdir) - - files = os.listdir(tmpdir) - if not files: - return None - first_file = os.path.join(tmpdir, files[0]) - with open(first_file, "rb") as f: - return f.read() + 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,