fix: flatten nested single-child directories after extraction
Archives like Debian_13_VMG.7z contain nested single-child dirs: Debian_13_VMG_LinuxVMImages.COM/Debian_13_VMG_LinuxVMImages.COM/ _flatten_nested() collapses these chains until files sit directly in the parent, eliminating the double-nesting issue.
This commit is contained in:
parent
a28e2af674
commit
9dcde5d022
1 changed files with 31 additions and 11 deletions
|
|
@ -110,18 +110,38 @@ def extract_if_needed(filename: str) -> Path:
|
||||||
# If the archive had a single top-level directory matching the stem, use it
|
# If the archive had a single top-level directory matching the stem, use it
|
||||||
expected_dir = STAGING_IN / stem
|
expected_dir = STAGING_IN / stem
|
||||||
if expected_dir in new_items and expected_dir.is_dir():
|
if expected_dir in new_items and expected_dir.is_dir():
|
||||||
return expected_dir
|
result = expected_dir
|
||||||
|
else:
|
||||||
# Otherwise, collect scattered files into a subdirectory
|
# Collect scattered files into a subdirectory
|
||||||
out_dir = STAGING_IN / stem
|
out_dir = STAGING_IN / stem
|
||||||
out_dir.mkdir(parents=True, exist_ok=True)
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
for item in new_items:
|
for item in new_items:
|
||||||
dest = out_dir / item.name
|
shutil.move(str(item), str(out_dir / item.name))
|
||||||
if item.is_dir():
|
result = out_dir
|
||||||
shutil.move(str(item), str(dest))
|
|
||||||
else:
|
# Flatten nested single-child directories (archive quirk)
|
||||||
shutil.move(str(item), str(dest))
|
result = _flatten_nested(result)
|
||||||
return out_dir
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _flatten_nested(base: Path) -> Path:
|
||||||
|
"""If a directory has exactly one child that is also a directory,
|
||||||
|
move the child's contents up and remove the child. Repeat until stable.
|
||||||
|
Returns the (possibly changed) base path."""
|
||||||
|
changed = True
|
||||||
|
while changed:
|
||||||
|
changed = False
|
||||||
|
if not base.is_dir():
|
||||||
|
break
|
||||||
|
entries = [e for e in base.iterdir() if e.name not in ('.', '..')]
|
||||||
|
if len(entries) == 1 and entries[0].is_dir():
|
||||||
|
inner = entries[0]
|
||||||
|
# Move everything from inner into base
|
||||||
|
for item in list(inner.iterdir()):
|
||||||
|
shutil.move(str(item), str(base / item.name))
|
||||||
|
inner.rmdir()
|
||||||
|
changed = True
|
||||||
|
return base
|
||||||
|
|
||||||
|
|
||||||
def _detect_archive_ext(name: str) -> Optional[str]:
|
def _detect_archive_ext(name: str) -> Optional[str]:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue