2# +==== BEGIN CatFeeder =================+
5# ...............)..(.')
7# ...............\(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
13# FILE: archive_to_archive.py
14# CREATION DATE: 15-01-2026
15# LAST Modified: 1:33:19 17-01-2026
17# This is the backend server in charge of making the actual website work.
19# COPYRIGHT: (c) Cat Feeder
20# PURPOSE: The file containing the code for converting archives from one format to another.
22# +==== END CatFeeder =================+
25from typing
import Optional
26from pathlib
import Path
30from display_tty
import Disp, initialise_logger
32from .
import converters_constants
as CONV_CONST
33from .aliases
import ARCHIVE_FORMAT_ALIASES
35from ..http_constants
import DataTypes, MEDIA_TYPES
38from ...core
import FinalClass
39from ...utils
import CONST
43 """Class used to convert archives from one format to another using patoolib."""
45 disp: Disp = initialise_logger(__qualname__, CONST.DEBUG)
47 _instance: Optional[
"ArchiveToArchive"] =
None
55 self.
disp.log_debug(
"Initialising...")
56 self.
disp.log_debug(
"Initialised.")
58 def __call__(self, data: bytes, source_format: DataTypes) -> CONV_CONST.ConversionResult:
63 """Check if a file is a valid archive."""
65 return patoolib.is_archive(str(path))
72 Handles multi-part extensions like .tar.gz
75 path: Path object to split
78 Tuple of (base_name, extension)
81 parts = name.split(
".")
84 return path.stem, path.suffix.lstrip(
".")
87 return parts[0],
".".join(parts[1:])
91 Get the file extension for a given archive DataType.
94 data_type: The DataType to get extension for
97 Extension string or None
100 return ARCHIVE_FORMAT_ALIASES.get(data_type)
105 source_format: DataTypes
106 ) -> tuple[Optional[DataTypes], Optional[str], Optional[str]]:
108 Validate conversion parameters and get destination format and extensions.
111 data: The archive data to validate
112 source_format: The source archive format
115 Tuple of (destination_format, source_ext, dest_ext)
116 Returns (None, None, None) if validation fails
119 destination_format = MEDIA_TYPES.get_conversion_target(
121 except (AttributeError, NameError):
122 destination_format =
None
124 if destination_format
is None:
125 self.
disp.log_debug(f
"No conversion target for {source_format}")
126 return None,
None,
None
128 if source_format == destination_format:
130 f
"Source and destination formats are the same: {source_format}"
132 return destination_format,
None,
None
137 if not source_ext
or not dest_ext:
138 self.
disp.log_warning(
139 f
"Unknown archive extension for {source_format} -> {destination_format}"
141 return destination_format,
None,
None
143 return destination_format, source_ext, dest_ext
150 ) -> tuple[Path, Path]:
152 Create temporary files for archive conversion.
155 data: The source archive data
156 source_ext: The source file extension
157 dest_ext: The destination file extension
160 Tuple of (source_path, destination_path)
162 with tempfile.NamedTemporaryFile(
163 suffix=f
".{source_ext}",
167 src_path = Path(src_file.name)
169 with tempfile.NamedTemporaryFile(
170 suffix=f
".{dest_ext}",
173 dst_path = Path(dst_file.name)
175 return src_path, dst_path
181 source_format: DataTypes,
182 destination_format: DataTypes
183 ) -> Optional[bytes]:
185 Perform the actual archive conversion using patoolib.
188 src_path: Path to source archive file
189 dst_path: Path to destination archive file
190 source_format: Source archive format
191 destination_format: Destination archive format
194 Converted archive data as bytes, or None if conversion failed
197 self.
disp.log_warning(
"Input data is not a valid archive")
201 f
"Converting archive: {source_format} -> {destination_format}"
204 patoolib.repack_archive(
210 with open(dst_path,
'rb')
as f:
211 converted_data = f.read()
213 self.
disp.log_debug(
"Archive conversion successful")
214 return converted_data
219 Clean up temporary files.
222 src_path: Source file path to delete
223 dst_path: Destination file path to delete
225 if src_path.exists():
227 if dst_path.exists():
233 source_format: DataTypes,
234 destination_format: DataTypes,
235 result: Optional[bytes] =
None
236 ) -> CONV_CONST.ConversionResult:
238 Create a failed conversion result.
241 data: Original archive data
242 source_format: Source archive format
243 destination_format: Destination archive format
244 result: Optional result data
247 ConversionResult indicating failure
249 return CONV_CONST.ConversionResult(
252 from_type=source_format,
253 to_type=destination_format,
260 source_format: DataTypes,
261 destination_format: DataTypes,
262 converted_data: bytes
263 ) -> CONV_CONST.ConversionResult:
265 Create a successful conversion result.
268 data: Original archive data
269 source_format: Source archive format
270 destination_format: Destination archive format
271 converted_data: Converted archive data
274 ConversionResult indicating success
276 return CONV_CONST.ConversionResult(
279 from_type=source_format,
280 to_type=destination_format,
281 result=converted_data
287 source_format: DataTypes,
288 destination_format: Optional[DataTypes],
289 source_ext: Optional[str],
290 dest_ext: Optional[str]
291 ) -> Optional[CONV_CONST.ConversionResult]:
293 Handle validation failures and return appropriate result.
296 data: Original archive data
297 source_format: Source archive format
298 destination_format: Destination format (may be None)
299 source_ext: Source extension (may be None)
300 dest_ext: Destination extension (may be None)
303 ConversionResult if validation failed, None if validation passed
305 if destination_format
is None:
307 data, source_format, source_format,
None
310 if source_ext
is None or dest_ext
is None:
311 result_data = data
if source_format == destination_format
else data
313 data, source_format, destination_format, result_data
321 source_format: DataTypes,
322 destination_format: DataTypes,
325 ) -> CONV_CONST.ConversionResult:
327 Perform conversion using temporary files.
330 data: Original archive data
331 source_format: Source archive format
332 destination_format: Destination archive format
333 source_ext: Source file extension
334 dest_ext: Destination file extension
337 ConversionResult with conversion outcome
339 src_path: Optional[Path] =
None
340 dst_path: Optional[Path] =
None
344 data, source_ext, dest_ext
348 src_path, dst_path, source_format, destination_format
351 if converted_data
is None:
353 data, source_format, destination_format,
None
357 data, source_format, destination_format, converted_data
360 except Exception
as e:
361 self.
disp.log_error(f
"Archive conversion error: {e}")
363 data, source_format, destination_format,
None
367 if src_path
is not None and dst_path
is not None:
370 def archive_to_archive(self, data: bytes, source_format: DataTypes) -> CONV_CONST.ConversionResult:
372 Convert archive data from one format to another using patoolib.
375 data (bytes): The archive data to convert.
376 source_format (DataTypes): The original archive format.
379 ConversionResult: The converted archive data in a contained dataclass.
386 data, source_format, destination_format, source_ext, dest_ext
388 if validation_result
is not None:
389 return validation_result
392 if destination_format
is None or source_ext
is None or dest_ext
is None:
395 data, source_format, source_format,
None
399 data, source_format, destination_format, source_ext, dest_ext
bool is_archive(Path path)
None _cleanup_temp_files(Path src_path, Path dst_path)
CONV_CONST.ConversionResult _create_success_result(self, bytes data, DataTypes source_format, DataTypes destination_format, bytes converted_data)
tuple[Optional[DataTypes], Optional[str], Optional[str]] _validate_conversion_params(self, bytes data, DataTypes source_format)
"ArchiveToArchive" __new__(cls)
CONV_CONST.ConversionResult _convert_with_temp_files(self, bytes data, DataTypes source_format, DataTypes destination_format, str source_ext, str dest_ext)
Optional[CONV_CONST.ConversionResult] _handle_validation_failure(self, bytes data, DataTypes source_format, Optional[DataTypes] destination_format, Optional[str] source_ext, Optional[str] dest_ext)
CONV_CONST.ConversionResult __call__(self, bytes data, DataTypes source_format)
Optional[bytes] _perform_conversion(self, Path src_path, Path dst_path, DataTypes source_format, DataTypes destination_format)
tuple[str, str] split_name_and_ext(Path path)
Optional[str] get_archive_extension(self, DataTypes data_type)
tuple[Path, Path] _create_temp_files(self, bytes data, str source_ext, str dest_ext)
CONV_CONST.ConversionResult archive_to_archive(self, bytes data, DataTypes source_format)
CONV_CONST.ConversionResult _create_failed_result(self, bytes data, DataTypes source_format, DataTypes destination_format, Optional[bytes] result=None)