async def findIcon(self, ipfsop, ipfsPath, rscStat, mimeType, maxIconSize=kilobytes(512)): icon = None statInfo = StatInfo(rscStat) if statInfo.valid and mimeType.isImage: if statInfo.dataSmallerThan(maxIconSize): await ipfsop.sleep() data = await ipfsop.catObject(str(ipfsPath), timeout=5) if data: icon = getIconFromImageData(data) else: icon = getMimeIcon('image/x-generic') elif mimeType.isDir: favIcon = await getFavIconFromDir(ipfsop, ipfsPath) if favIcon: icon = favIcon else: icon = getMimeIcon('inode/directory') else: await ipfsop.sleep() icon = getIconFromMimeType(mimeType) if icon is None: icon = getIcon('unknown-file.png') return icon
async def registerHashmark(self, ipfsop, mark, button=None, maxIconSize=512 * 1024): """ Register an object in the toolbar with an associated hashmark """ await mark._fetch_all() ipfsPath = IPFSPath(mark.path) if not ipfsPath.valid: return result = await self.analyzer(ipfsPath) if result is None: return mimeType, rscStat = result mIcon = mark.icon.path if mark.icon else None icon = None if mIcon and IPFSPath(mIcon).valid: stat = await ipfsop.objStat(mIcon) statInfo = StatInfo(stat) # Check filesize from the stat on the object if statInfo.valid and statInfo.dataSmallerThan(maxIconSize): icon = await getIconFromIpfs(ipfsop, mIcon) if icon is None: icon = getIcon('unknown-file.png') else: if not await ipfsop.isPinned(mIcon): log.debug('Pinning icon {0}'.format(mIcon)) await ipfsop.ctx.pin(mIcon) elif mimeType: icon = await self.findIcon(ipfsop, ipfsPath, rscStat, mimeType) else: icon = getIcon('unknown-file.png') if not button: button = HashmarkToolButton(mark, parent=self, icon=icon) action = self.addWidget(button) button.setToolTip(iHashmarkInfoToolTipShort(mark)) button.clicked.connect(lambda: ensure( self.app.resourceOpener.open(str(ipfsPath), openingFrom='qa'))) button.deleteRequest.connect( lambda: ensure(self.onDelete(button, action))) else: if icon: button.setIcon(icon) button.setToolTip(iHashmarkInfoToolTipShort(mark))
async def open(self, ipfsop, pathRef, mimeType=None, openingFrom=None, pyramidOrigin=None, minWebProfile='ipfs', schemePreferred=None, tryDecrypt=False, fromEncrypted=False, editObject=False, pin=False, burnAfterReading=False, useWorkspace=None): """ Open the resource referenced by rscPath according to its MIME type :param pathRef: IPFS object's path (can be str or IPFSPath) :param openingFrom str: UI component making the open request :param minWebProfile str: Minimum Web profile to use :param tryDecrypt bool: Try to decrypt the object or not :param editObject bool: Set Text Editor in edit mode for text files :param MIMEType mimeType: MIME type """ ipfsPath = None statInfo = None if isinstance(pathRef, IPFSPath): ipfsPath = pathRef elif isinstance(pathRef, str): url = QUrl(pathRef) if isEnsUrl(url): return self.openEnsUrl(url, pin=pin) if isUrlSupported(url): return self.openUrl(url) ipfsPath = IPFSPath(pathRef, autoCidConv=True) else: raise ValueError(f'Invalid input value: {pathRef}') if not ipfsPath.valid: return False rscPath = ipfsPath.objPath if self.app.mainWindow.pinAllGlobalChecked: ensure( ipfsop.ctx.pinner.queue(rscPath, False, None, qname='default')) rscShortName = ipfsPath.shortRepr() if ipfsPath.isIpfs: # Try to reuse metadata from the multihash store rscMeta = await self.app.multihashDb.get(rscPath) if rscMeta: cachedMime = rscMeta.get('mimetype') cachedStat = rscMeta.get('stat') if cachedMime: mimeType = MIMEType(cachedMime) if cachedStat: statInfo = StatInfo(cachedStat) wsSwitch = True mtConfig = self.cObjectOpener.mimeTypes.default if mimeType is None: ltBefore = loopTime() mimeType = await detectMimeType(rscPath) spent = loopTime() - ltBefore if spent > mtConfig.slowObjectTimer: # We won't switch workspaces wsSwitch = False if mimeType and mimeType.valid: logUser.info('{path} ({type}): opening'.format(path=rscPath, type=str(mimeType))) else: logUser.info(iResourceCannotOpen(rscPath)) return hashmark = await hashmarksByPath(rscPath) if hashmark and not useWorkspace: await hashmark._fetch_all() useWorkspace = self.app.mainWindow.stack.wsHashmarkTagRulesRun( hashmark) if mimeType == mimeTypeDagUnknown: indexPath = ipfsPath.child('index.html') stat = await ipfsop.objStat(indexPath.objPath, timeout=8) if stat: # Browse the index return self.app.mainWindow.addBrowserTab( minProfile=minWebProfile, pinBrowsed=pin).browseFsPath( indexPath, schemePreferred=schemePreferred) # Otherwise view the DAG view = DAGViewer(rscPath, self.app.mainWindow) self.app.mainWindow.registerTab(view, iDagViewer(), current=True, icon=getIcon('ipld.png'), tooltip=rscPath) return if mimeType.type == 'application/octet-stream' and not fromEncrypted: # Try to decode it with our key if it's a small file if statInfo is None: statInfo = StatInfo(await ipfsop.objStat(rscPath, timeout=5)) profile = ipfsop.ctx.currentProfile if profile and statInfo.valid and \ (statInfo.dataSmallerThan(megabytes(8)) or tryDecrypt): data = await ipfsop.catObject(ipfsPath.objPath, timeout=30) if not data: # XXX return decrypted = await profile.rsaAgent.decrypt(data) if decrypted: # # "Good evening, 007" # # Create a short-lived IPFS offline file (not announced) # with the decrypted content and open it # logUser.info('{path}: RSA OK'.format(path=rscPath)) # This one won't be announced or pinned entry = await ipfsop.addBytes(decrypted, offline=True, pin=False) if not entry: logUser.info( '{path}: cannot import decrypted file'.format( path=rscPath)) return # Open the decrypted file return ensure( self.open(entry['Hash'], fromEncrypted=True, burnAfterReading=True)) else: logUser.debug( '{path}: decryption impossible'.format(path=rscPath)) if mimeType.isText or editObject: tab = TextEditorTab(parent=self.app.mainWindow, editing=editObject, pyramidOrigin=pyramidOrigin) tab.editor.display(ipfsPath) self.objectOpened.emit(ipfsPath) return self.app.mainWindow.registerTab( tab, rscShortName, icon=getMimeIcon('text/x-generic'), tooltip=rscPath, current=True, workspace=WS_EDIT) if mimeType.isImage or mimeType.isAnimation: tab = ImageViewerTab(self.app.mainWindow) ensure(tab.view.showImage(rscPath)) self.objectOpened.emit(ipfsPath) return self.app.mainWindow.registerTab( tab, rscShortName, icon=getMimeIcon('image/x-generic'), tooltip=rscPath, current=True) if mimeType.isVideo or mimeType.isAudio: tab = self.app.mainWindow.getMediaPlayer() if tab: tab.playFromPath(rscPath) return if mimeType == 'application/pdf' and 0: # not usable yet tab = WebTab(self.app.mainWindow) tab.attach(DWebView(page=PDFViewerPage(rscPath))) self.objectOpened.emit(ipfsPath) return self.app.mainWindow.registerTab( tab, rscShortName, icon=getMimeIcon('application/pdf'), tooltip=rscPath, current=True) if mimeType.isHtml: self.objectOpened.emit(ipfsPath) return self.app.mainWindow.addBrowserTab( minProfile=minWebProfile, pinBrowsed=pin, workspace=useWorkspace, wsSwitch=wsSwitch).browseFsPath( ipfsPath, schemePreferred=schemePreferred) if mimeType.isDir: indexPath = ipfsPath.child('index.html') stat = await ipfsop.objStat(indexPath.objPath, timeout=8) if stat: self.objectOpened.emit(ipfsPath) return self.app.mainWindow.addBrowserTab( minProfile=minWebProfile, pinBrowsed=pin, workspace=useWorkspace, wsSwitch=wsSwitch).browseFsPath( ipfsPath, schemePreferred=schemePreferred) else: return await self.app.mainWindow.exploreIpfsPath(ipfsPath) if mimeType.isBitTorrent: wStack = self.app.mainWindow.stack with wStack.workspaceCtx(WS_FILES, show=True) as ws: btClient = await ws.getTorrentClient() return await btClient.addTorrentFromIpfs(ipfsPath) if openingFrom in ['filemanager', 'qa', 'didlocal']: await self.needUserConfirm.emit(ipfsPath, mimeType, True) else: await self.needUserConfirm.emit(ipfsPath, mimeType, False)