Coverage for src/lanraragi_api/enhanced/server_side.py: 0%
18 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-22 23:20 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-22 23:20 +0000
1# the same to server side's code
2import hashlib
3import os
4import re
7class ArchiveFileError(Exception):
8 """Raised when an archive file cannot be accessed on disk."""
11def compute_id(file_path: str) -> str:
12 """Compute the archive ID of a file the same way the server does.
14 The ID of an archive is determined only by the archive itself, so it can be
15 computed on the client side as well.
17 Args:
18 file_path: Path of the archive file.
20 Returns:
21 str: Hexadecimal SHA-1 digest of the first 512 KB of the file.
23 Raises:
24 ArchiveFileError: If ``file_path`` is not a file, or if the file cannot
25 be opened or read.
27 Note:
28 The algorithm matches the server side implementation in
29 ``LANraragi/lib/LANraragi/Utils/Database.pm``.
30 """
31 if not os.path.isfile(file_path):
32 raise ArchiveFileError(f"not a valid file path: {file_path}")
33 try:
34 # Read the first 512 KB of the file
35 with open(file_path, "rb") as file:
36 data = file.read(512000)
37 except OSError as e:
38 raise ArchiveFileError(f"Couldn't open {file_path}: {e}") from e
40 # Compute the SHA-1 hash of the data
41 sha1 = hashlib.sha1()
42 sha1.update(data)
43 digest = sha1.hexdigest()
45 return digest
48def is_archive(file_name: str):
49 """Check whether a file name has a supported archive extension.
51 Args:
52 file_name: File name to test.
54 Returns:
55 bool: True if the name ends with ``zip``, ``rar``, ``7z``, ``tar``,
56 ``tar.gz``, ``lzma``, ``xz``, ``cbz``, ``cbr``, ``cb7``, ``cbt``,
57 ``pdf`` or ``epub``, compared case-insensitively. False otherwise.
59 Note:
60 The list of extensions matches the server side implementation in
61 ``LANraragi/lib/LANraragi/Utils/Generic.pm``.
62 """
63 return (
64 re.match(
65 r"^.+\.(zip|rar|7z|tar|tar\.gz|lzma|xz|cbz|cbr|cb7|cbt|pdf|epub)$",
66 file_name,
67 re.IGNORECASE,
68 )
69 is not None
70 )