def __install_spin(self, fid):
"""Install a spin-off from its zip archive with targeted folders.
Expected archive structure:
- sppas/... [expected]
- resources/... [optional]
Any other entry (file or folder) is ignored.
Behavior:
- All entries under "sppas/" are extracted into the spin-off
installation directory (paths.basedir or equivalent).
- All entries under "resources/" are extracted into the global
resources directory defined by paths.resources.
- If the archive contains only "sppas/", it is installed normally.
- No other top-level directories are supported.
:param fid: (str) Identifier of the spin-off archive to install.
:raises: sppasInstallationError
"""
self.__pmessage('Download, unzip and install spin-off {}'.format(fid))
url = self._features.spin_url(fid)
if url.endswith('/') is False:
url += '/'
_file_path = ''.join([self._features.spin(fid), '-', self._features.spin_version(fid), '.zip'])
err, tmp_file = Installer.download_resource(url, _file_path, paths.basedir)
if len(err) > 0:
raise sppasInstallationError(err)
try:
with zipfile.ZipFile(tmp_file, 'r') as zf:
for info in zf.infolist():
name = info.filename
if name.endswith('/'):
continue
if name.startswith('resources/'):
target_root = paths.resources
relative_path = name[len('resources/'):]
elif name.startswith('sppas/'):
target_root = paths.sppas
relative_path = name[len('sppas/'):]
else:
logging.warning(f"Spin-off {fid}. Ignored entry because outside 'sppas/' or 'resources/': {name}")
continue
if relative_path == '':
continue
target_path = os.path.join(target_root, relative_path)
os.makedirs(os.path.dirname(target_path), exist_ok=True)
with zf.open(info, 'r') as src, open(target_path, 'wb') as dst:
dst.write(src.read())
except sppasInstallationError:
raise
except Exception as e:
logging.debug(traceback.format_exc())
raise sppasInstallationError(str(e))
if os.path.exists(tmp_file) is True:
os.remove(tmp_file)