示例#1
0
 def loadInstalled(self) -> None:
     for path in self.modspath.iterdir():
         if path.joinpath('.w3mm').is_file():
             mod = Mod.from_json(path.joinpath('.w3mm').read_bytes())
             mod.enabled = not path.name.startswith('~')
             mod.filename = re.sub(r'^(~)', r'', path.name)
             if mod.enabled:
                 enabled = getSettingsValue(
                     mod.filename, 'Enabled',
                     self.configpath.joinpath('mods.settings'))
                 if enabled == '0':
                     mod.enabled = False
             self._modList[(mod.filename, mod.target)] = mod
         else:
             try:
                 for mod in Mod.fromDirectory(path, recursive=False):
                     mod.installdate = datetime.fromtimestamp(
                         path.stat().st_ctime, tz=timezone.utc)
                     mod.target = 'mods'
                     mod.datatype = 'mod'
                     mod.enabled = not path.name.startswith('~')
                     if mod.enabled:
                         enabled = getSettingsValue(
                             mod.filename, 'Enabled',
                             self.configpath.joinpath('mods.settings'))
                         if enabled == '0':
                             mod.enabled = False
                     self._modList[(mod.filename, mod.target)] = mod
                     asyncio.create_task(self.update(mod))
             except InvalidPathError:
                 logger.bind(path=path).debug('Invalid MOD')
     for path in self.dlcspath.iterdir():
         if path.joinpath('.w3mm').is_file():
             mod = Mod.from_json(path.joinpath('.w3mm').read_bytes())
             mod.enabled = not all(
                 file.name.endswith('.disabled')
                 for file in path.glob('**/*')
                 if file.is_file() and not file.name == '.w3mm')
             mod.filename = path.name
             self._modList[(mod.filename, mod.target)] = mod
         else:
             try:
                 for mod in Mod.fromDirectory(path, recursive=False):
                     mod.installdate = datetime.fromtimestamp(
                         path.stat().st_ctime, tz=timezone.utc)
                     mod.target = 'dlc'
                     mod.datatype = 'dlc'
                     mod.enabled = not all(
                         file.name.endswith('.disabled')
                         for file in path.glob('**/*')
                         if file.is_file() and not file.name == '.w3mm')
                     self._modList[(mod.filename, mod.target)] = mod
                     asyncio.create_task(self.update(mod))
             except InvalidPathError:
                 logger.bind(path=path).debug('Invalid DLC')
示例#2
0
 async def update(self, mod: Mod) -> None:
     # serialize and store mod structure
     target = self.getModPath(mod, True)
     try:
         with target.joinpath('.w3mm').open(
                 'w', encoding='utf-8') as modInfoFile:
             modSerialized = mod.to_json()
             modInfoFile.write(modSerialized)
     except Exception as e:
         logger.exception(f'Could not update mod: {e}')
示例#3
0
 async def add(self, mod: Mod) -> None:
     # TODO: incomplete: always override compilation trigger mod
     if self.modspath in [mod.source, *mod.source.parents]:
         raise InvalidSourcePath(
             mod.source,
             'Invalid mod source: Mods cannot be installed from the mods directory'
         )
     async with self.updateLock:
         if (mod.filename, mod.target) in self._modList:
             raise ModExistsError(mod.filename, mod.target)
         target = self.getModPath(mod)
         if target.exists():
             # TODO: incomplete: make sure the mod is tracked by the model
             raise ModExistsError(mod.filename, mod.target)
         settings = 0
         inputs = 0
         try:
             target.mkdir(parents=True)
             # copy mod files
             for _file in mod.files:
                 sourceFile = mod.source.joinpath(_file.source)
                 targetFile = target.joinpath(_file.source)
                 targetFile.parent.mkdir(parents=True, exist_ok=True)
                 copyfile(sourceFile, targetFile)
             for _content in mod.contents:
                 sourceFile = mod.source.joinpath(_content.source)
                 targetFile = target.joinpath(_content.source)
                 targetFile.parent.mkdir(parents=True, exist_ok=True)
                 copyfile(sourceFile, targetFile)
             mod.installed = True
             settings = addSettings(
                 mod.settings, self.configpath.joinpath('user.settings'))
             inputs = addSettings(
                 mod.inputs, self.configpath.joinpath('input.settings'))
             setSettingsValue(mod.filename, 'Enabled', '1',
                              self.configpath.joinpath('mods.settings'))
             await self.update(mod)
         except Exception as e:
             removeDirectory(target)
             if settings:
                 removeSettings(mod.settings,
                                self.configpath.joinpath('user.settings'))
             if inputs:
                 removeSettings(mod.inputs,
                                self.configpath.joinpath('input.settings'))
             removeSettingsSection(
                 mod.filename, self.configpath.joinpath('mods.settings'))
             raise e
         self._modList[(mod.filename, mod.target)] = mod
     self.setLastUpdateTime(datetime.now(tz=timezone.utc))
示例#4
0
 async def updateModDetails(self, mod: Mod) -> bool:
     logger.bind(name=mod.filename,
                 dots=True).debug('Requesting details for mod')
     if not mod.md5hash:
         logger.bind(name=mod.filename).warning(
             'Could not get details for mod not installed from archive')
         return False
     try:
         details = await getModInformation(mod.md5hash)
     except Exception as e:
         logger.bind(name=mod.filename).warning(f'{e}')
         return False
     try:
         package = str(details[0]['mod']['name'])
         summary = str(details[0]['mod']['summary'])
         modid = int(details[0]['mod']['mod_id'])
         category = int(details[0]['mod']['category_id'])
         version = str(details[0]['file_details']['version'])
         fileid = int(details[0]['file_details']['file_id'])
         uploadname = str(details[0]['file_details']['name'])
         uploadtime = str(details[0]['file_details']['uploaded_time'])
         mod.package = package
         mod.summary = summary
         mod.modid = modid
         mod.category = getCategoryName(category)
         mod.version = version
         mod.fileid = fileid
         mod.uploadname = uploadname
         uploaddate = dateparser.parse(uploadtime)
         if uploaddate:
             mod.uploaddate = uploaddate.astimezone(tz=timezone.utc)
         else:
             logger.bind(name=mod.filename).debug(
                 f'Could not parse date {uploadtime} in mod information response'
             )
     except KeyError as e:
         logger.bind(name=mod.filename).exception(
             f'Could not find key "{str(e)}" in mod information response')
         return False
     try:
         await self.modmodel.update(mod)
     except Exception as e:
         logger.bind(
             name=mod.filename).exception(f'Could not update mod: {e}')
         return False
     return True
示例#5
0
 async def loadInstalledMod(self, path: Path) -> None:
     if path.joinpath('.w3mm').is_file():
         mod = Mod.from_json(path.joinpath('.w3mm').read_bytes())
         mod.enabled = not path.name.startswith('~')
         mod.filename = re.sub(r'^(~)', r'', path.name)
         if mod.enabled:
             enabled = getSettingsValue(
                 mod.filename, 'Enabled',
                 self.configpath.joinpath('mods.settings'))
             if enabled == '0':
                 mod.enabled = False
         if (mod.filename, mod.target) in self._modList:
             logger.bind(path=path).error('Ignoring duplicate MOD')
             if not self._modList[(mod.filename, mod.target)].enabled:
                 self._modList[(mod.filename, mod.target)] = mod
         else:
             self._modList[(mod.filename, mod.target)] = mod
     else:
         try:
             for mod in await Mod.fromDirectory(path, recursive=False):
                 mod.installdate = datetime.fromtimestamp(
                     path.stat().st_ctime, tz=timezone.utc)
                 mod.target = 'mods'
                 mod.datatype = 'mod'
                 mod.enabled = not path.name.startswith('~')
                 mod.filename = re.sub(r'^(~)', r'', path.name)
                 if mod.enabled:
                     enabled = getSettingsValue(
                         mod.filename, 'Enabled',
                         self.configpath.joinpath('mods.settings'))
                     if enabled == '0':
                         mod.enabled = False
                 if (mod.filename, mod.target) in self._modList:
                     logger.bind(path=path).error('Ignoring duplicate MOD')
                     if not self._modList[(mod.filename,
                                           mod.target)].enabled:
                         self._modList[(mod.filename, mod.target)] = mod
                 else:
                     self._modList[(mod.filename, mod.target)] = mod
                     await self.update(mod)
         except InvalidPathError:
             logger.bind(path=path).debug('Invalid MOD')
示例#6
0
 async def loadInstalledDlc(self, path: Path) -> None:
     if path.joinpath('.w3mm').is_file():
         mod = Mod.from_json(path.joinpath('.w3mm').read_bytes())
         mod.enabled = not all(
             file.name.endswith('.disabled') for file in path.glob('**/*')
             if file.is_file() and not file.name == '.w3mm')
         mod.filename = path.name
         self._modList[(mod.filename, mod.target)] = mod
     else:
         try:
             for mod in await Mod.fromDirectory(path, recursive=False):
                 mod.installdate = datetime.fromtimestamp(
                     path.stat().st_ctime, tz=timezone.utc)
                 mod.target = 'dlc'
                 mod.datatype = 'dlc'
                 mod.enabled = not all(
                     file.name.endswith('.disabled')
                     for file in path.glob('**/*')
                     if file.is_file() and not file.name == '.w3mm')
                 mod.filename = path.name
                 self._modList[(mod.filename, mod.target)] = mod
                 await self.update(mod)
         except InvalidPathError:
             logger.bind(path=path).debug('Invalid DLC')
示例#7
0
    async def installFromFile(self, path: Path, installtime: Optional[datetime] = None) -> Tuple[int, int]:
        originalpath = path
        installed = 0
        errors = 0
        archive = path.is_file()
        source = None
        md5hash = ''
        details = None
        detailsrequest: Optional[asyncio.Task] = None

        if not installtime:
            installtime = datetime.now(tz=timezone.utc)
        try:
            if archive:
                # unpack archive, set source and request details
                md5hash = getMD5Hash(path)
                source = path
                settings = QSettings()
                if settings.value('nexusGetInfo', 'False') == 'True':
                    logger.bind(path=str(path), dots=True).debug('Requesting details for archive')
                    detailsrequest = asyncio.create_task(getModInformation(md5hash))
                logger.bind(path=str(path), dots=True).debug('Unpacking archive')
                path = await extractMod(source)

            # validate and read mod
            valid, exhausted = containsValidMod(path, searchlimit=8)
            if not valid:
                if not exhausted and self.showContinueSearchDialog(searchlimit=8):
                    if not containsValidMod(path):
                        raise InvalidPathError(path, 'Invalid mod')
                elif not exhausted:
                    raise InvalidPathError(path, 'Stopped searching for mod')
                else:
                    raise InvalidPathError(path, 'Invalid mod')
            mods = Mod.fromDirectory(path, searchCommonRoot=not archive)

            installedMods = []
            # update mod details and add mods to the model
            for mod in mods:
                mod.md5hash = md5hash
                try:
                    # TODO: incomplete: check if mod is installed, ask if replace
                    await self.modmodel.add(mod)
                    installedMods.append(mod)
                    installed += 1
                except ModExistsError:
                    logger.bind(path=source if source else mod.source, name=mod.filename).error(f'Mod exists')
                    errors += 1
                    continue

            # wait for details response if requested
            if detailsrequest:
                try:
                    details = await detailsrequest
                except (RequestError, ResponseError, Exception) as e:
                    logger.warning(f'Could not get information for {source.name if source else path.name}: {e}')

            # update mod with additional information
            if source or details:
                for mod in installedMods:
                    if source:
                        # set source if it differs from the scan directory, e.g. an archive
                        mod.source = source
                    if details:
                        # set additional details if requested and available
                        try:
                            package = str(details[0]['mod']['name'])
                            summary = str(details[0]['mod']['summary'])
                            modid = int(details[0]['mod']['mod_id'])
                            category = int(details[0]['mod']['category_id'])
                            version = str(details[0]['file_details']['version'])
                            fileid = int(details[0]['file_details']['file_id'])
                            uploadname = str(details[0]['file_details']['name'])
                            uploadtime = str(details[0]['file_details']['uploaded_time'])
                            mod.package = package
                            mod.summary = summary
                            mod.modid = modid
                            mod.category = getCategoryName(category)
                            mod.version = version
                            mod.fileid = fileid
                            mod.uploadname = uploadname
                            uploaddate = dateparser.parse(uploadtime)
                            if uploaddate:
                                mod.uploaddate = uploaddate.astimezone(tz=timezone.utc)
                            else:
                                logger.bind(name=mod.filename).debug(
                                    f'Could not parse date {uploadtime} in mod information response')
                        except KeyError as e:
                            logger.bind(name=mod.filename).exception(
                                f'Could not find key "{str(e)}" in mod information response')
                    try:
                        await self.modmodel.update(mod)
                    except Exception:
                        logger.bind(name=mod.filename).warning('Could not update mod details')

        except ModelError as e:
            logger.bind(path=e.path).error(e.message)
            errors += 1
        except InvalidPathError as e:
            # TODO: enhancement: better install error message
            logger.bind(path=e.path).error(e.message)
            errors += 1
        except FileNotFoundError as e:
            logger.bind(path=e.filename).error(e.strerror if e.strerror else str(e))
            errors += 1
        except OSError as e:
            logger.bind(path=e.filename).error(e.strerror if e.strerror else str(e))
            errors += 1
        except Exception as e:
            logger.exception(str(e))
            errors += 1
        finally:
            if detailsrequest and not detailsrequest.done():
                detailsrequest.cancel()
            if archive and not path == originalpath:
                try:
                    util.removeDirectory(path)
                except Exception:
                    logger.bind(path=path).warning('Could not remove temporary directory')
            self.modmodel.setLastUpdateTime(installtime)
            self.repaint()
        return installed, errors
示例#8
0
 async def add(self, mod: Mod) -> None:
     # TODO: incomplete: always override compilation trigger mod
     if self.modspath in [mod.source, *mod.source.parents]:
         raise InvalidSourcePath(
             mod.source,
             'Invalid mod source: Mods cannot be installed from the mods directory'
         )
     async with self.updateLock:
         if (mod.filename, mod.target) in self._modList:
             raise ModExistsError(mod.filename, mod.target)
         target = self.getModPath(mod)
         if target.exists():
             # TODO: incomplete: make sure the mod is tracked by the model
             raise ModExistsError(mod.filename, mod.target)
         settings = 0
         inputs = 0
         try:
             event_loop = asyncio.get_running_loop()
             target.mkdir(parents=True)
             # copy mod files
             copies = []
             logger.bind(name=mod.filename,
                         path=target).debug('Copying binary files')
             for _file in mod.files:
                 sourceFile = mod.source.joinpath(_file.source)
                 targetFile = target.joinpath(_file.source)
                 targetFile.parent.mkdir(parents=True, exist_ok=True)
                 copies.append((sourceFile, targetFile))
             await asyncio.gather(*[
                 event_loop.run_in_executor(
                     None, partial(copyfile, _copy[0], _copy[1]))
                 for _copy in copies
             ])
             copies = []
             logger.bind(name=mod.filename,
                         path=target).debug('Copying content files')
             for _content in mod.contents:
                 sourceFile = mod.source.joinpath(_content.source)
                 targetFile = target.joinpath(_content.source)
                 targetFile.parent.mkdir(parents=True, exist_ok=True)
                 copies.append((sourceFile, targetFile))
             await asyncio.gather(*[
                 event_loop.run_in_executor(
                     None, partial(copyfile, _copy[0], _copy[1]))
                 for _copy in copies
             ])
             mod.installed = True
             # update settings
             logger.bind(name=mod.filename,
                         path=target).debug('Updating settings')
             settings = addSettings(
                 mod.settings, self.configpath.joinpath('user.settings'))
             inputs = addSettings(
                 mod.inputs, self.configpath.joinpath('input.settings'))
             setSettingsValue(mod.filename, 'Enabled', '1',
                              self.configpath.joinpath('mods.settings'))
             await self.update(mod)
         except Exception as e:
             removeDirectory(target)
             if settings:
                 removeSettings(mod.settings,
                                self.configpath.joinpath('user.settings'))
             if inputs:
                 removeSettings(mod.inputs,
                                self.configpath.joinpath('input.settings'))
             removeSettingsSection(
                 mod.filename, self.configpath.joinpath('mods.settings'))
             raise e
         self._modList[(mod.filename, mod.target)] = mod
     self.updateBundledContentsConflicts()
     self.setLastUpdateTime(datetime.now(tz=timezone.utc))