Skip to content

Instantly share code, notes, and snippets.

@iexa
Last active March 30, 2021 01:47
Show Gist options
  • Save iexa/cea72ff9ae698f832abd9da64f7ea413 to your computer and use it in GitHub Desktop.
Save iexa/cea72ff9ae698f832abd9da64f7ea413 to your computer and use it in GitHub Desktop.
unpack scene release files (double-packed .rar files) with python
"""
unpacks rar files that are double-packed
-usually scene-release files are packed this way
"""
__author__ = 'iexa'
from pathlib import Path
import rarfile as rf
from time import time
# import zipfile as zf
def unpack(file, todir):
"""unpacks a double-packed file; needs unrar on system path; returns a Path or False"""
def testpath(z):
"""test file exts for rar multipart start -input: Path.suffixes list"""
if len(z) == 1 and z[0] == '.rar': return True
elif len(z) >= 2 and z[-2] in ('.part1', '.part01'): return True
elif len(z) >= 2 and z[-1] == '.rar' and '.part' not in z[-2]: return True
return False
z = rf.RarFile(file, crc_check=False)
extraction_dir = Path(z.namelist()[0]).parent
print(' '*7, f"| {file.stat().st_size>>20}mb unpacking...", end='', flush=True)
t0 = time()
try:
z.extractall(path=todir)
except rf.Error:
print(' '*7, f'\nINFO: ==> CHECK <== ARCHIVE FILE ERROR ?CRC or PASSWORD?')
z.close()
return False
z.close()
inside = [x for x in (todir/extraction_dir).iterdir() if testpath(x.suffixes)]
if len(inside) != 1:
print(' '*7, f'\nINFO: ==> CHECK <== CANNOT UNPACK INSIDE FILE(s)')
return False
print(f'{round(time()-t0,1)}s, now the inner one...', end='', flush=True)
t0 = time()
inside = inside[0]
z = rf.RarFile(inside, crc_check=False)
final_file = z.namelist()[0]
if len(z.namelist()) > 1:
print(' '*7, f'\nINFO: ==> CHECK <== MORE THAN 1 INSIDE FILES')
return False
try:
z.extractall(path=(todir/extraction_dir))
except rf.Error:
print(' '*7, f'\nINFO: ==> CHECK <== INSIDE ARCHIVE FILE ERROR ?CRC or PASSWORD?')
z.close()
return False
z.close()
print(f'{round(time()-t0,1)}s done.', flush=True)
return todir / extraction_dir / final_file
""" MAIN """
startpath = Path('/home/brunhilda/downloads/packed-stuff/') # TODO: modify this line to where your files are
t0 = time()
allfiles = tuple(startpath.glob('*.rar'))
allfiles_cnt = len(allfiles)
print(f'>>> Unpacking from {startpath}')
for nr, p1 in enumerate(allfiles, start=1):
print(f'{str(nr).zfill(3)}/{str(allfiles_cnt).zfill(3)}: {p1.name}', flush=True)
gotit = unpack(p1, startpath)
if not gotit:
print('-'*7, '==> CHECK <== something went wrong with this one')
continue
# some filename massaging
rename_to = str(gotit.parent.relative_to(startpath))
rename_to = rename_to.translate(rename_to.maketrans('_.', ' '))
rename_to = rename_to.partition(' eShop')[0] + '.nsp'
gotit.rename(startpath / Path(rename_to))
# cleanup files
for x in gotit.parent.iterdir():
x.unlink()
gotit.parent.rmdir()
p1.unlink()
# if nr > 77:
# break
t1 = int(time()-t0)
print(f'>>> DONE in {t1//60}m {t1%60}s - [phew!]')
@iexa
Copy link
Author

iexa commented Nov 17, 2019

What is it used for?

If you have a bunch of .rar files in a folder which are .rar files that have inside of them even more rar files (usually multipart rar) and you want to unpack them automatically then use this. Doing it by hand is tedious and very error-prone. It can be modified to use zipfile instead of rarfile, the api is / should be the same.

  • The script has some built-in error handling ->if the archive file is corrupted or has multiple files inside so you need to check it by hand then it will print that out to the console and continues with the rest. You can check once it finishes.
  • Also if you have thousands of files it could take very long -- so you can uncomment lines 83-84 to finish after a number of iterations.
  • It checks if only one file is found in the inside archives - if there are more than 1 then it skips deletion and will tell you about it.
  • It removes the original files (both the first and the second unpack) after successfully unpacking.

Requirements

  • It was written using python 3.7, but should work with 3.6+ (it needs the pathlib module and uses f-strings)
  • Have rarfile installed with your python distro python3 -m pip install rarfile --user
  • Have unrar installed in your system path. Grab the proper one for your system from rar rarlab.com. Test if it runs in your shell.
  • Configure the path where your files are using the startpath variable on line 61.
    I could've used argparse, etc. but since it's a quick 'n' dirty solution you will use it once and then forget about it for months
    It needs enough free space to do its work on the drive where your files are.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment