def SetStyleFromName(name): global CURRENT_STYLE_NAME if name == CURRENT_STYLE_NAME: return if name in GetAvailableStyles(): try: QW.QApplication.instance().setStyle(name) CURRENT_STYLE_NAME = name except Exception as e: raise HydrusExceptions.DataMissing( 'Style "{}" could not be generated/applied. If this is the default, perhaps a third-party custom style, you may have to restart the client to re-set it. Extra error info: {}' .format(name, e)) else: raise HydrusExceptions.DataMissing( 'Style "{}" does not exist! If this is the default, perhaps a third-party custom style, you may have to restart the client to re-set it.' .format(name))
def _GetListCtrlMenu( self ): selected_gallery_seeds = self._list_ctrl.GetData( only_selected = True ) if len( selected_gallery_seeds ) == 0: raise HydrusExceptions.DataMissing() menu = QW.QMenu() ClientGUIMenus.AppendMenuItem( menu, 'copy urls', 'Copy all the selected urls to clipboard.', self._CopySelectedGalleryURLs ) ClientGUIMenus.AppendMenuItem( menu, 'copy notes', 'Copy all the selected notes to clipboard.', self._CopySelectedNotes ) ClientGUIMenus.AppendSeparator( menu ) ClientGUIMenus.AppendMenuItem( menu, 'open urls', 'Open all the selected urls in your web browser.', self._OpenSelectedGalleryURLs ) ClientGUIMenus.AppendSeparator( menu ) if not self._read_only: ClientGUIMenus.AppendMenuItem( menu, 'try again (just this one page)', 'Schedule this url to occur again.', HydrusData.Call( self._TrySelectedAgain, False ) ) if self._can_generate_more_pages: ClientGUIMenus.AppendMenuItem( menu, 'try again (and allow search to continue)', 'Schedule this url to occur again and continue.', HydrusData.Call( self._TrySelectedAgain, True ) ) ClientGUIMenus.AppendMenuItem( menu, 'skip', 'Skip all the selected urls.', HydrusData.Call( self._SetSelected, CC.STATUS_SKIPPED ) ) return menu
def GetHashIdFromExtraHash(self, hash_type, hash): if hash_type == 'md5': result = self._Execute( 'SELECT hash_id FROM local_hashes WHERE md5 = ?;', (sqlite3.Binary(hash), )).fetchone() elif hash_type == 'sha1': result = self._Execute( 'SELECT hash_id FROM local_hashes WHERE sha1 = ?;', (sqlite3.Binary(hash), )).fetchone() elif hash_type == 'sha512': result = self._Execute( 'SELECT hash_id FROM local_hashes WHERE sha512 = ?;', (sqlite3.Binary(hash), )).fetchone() if result is None: raise HydrusExceptions.DataMissing( 'Hash Id not found for {} hash {}!'.format( hash_type, hash.hex())) (hash_id, ) = result return hash_id
def _GetListCtrlMenu( self ): selected_file_seeds = self._list_ctrl.GetData( only_selected = True ) if len( selected_file_seeds ) == 0: raise HydrusExceptions.DataMissing() menu = QW.QMenu() can_show_files_in_new_page = True in ( file_seed.HasHash() for file_seed in selected_file_seeds ) if can_show_files_in_new_page: ClientGUIMenus.AppendMenuItem( menu, 'open selected import files in a new page', 'Show all the known selected files in a new thumbnail page. This is complicated, so cannot always be guaranteed, even if the import says \'success\'.', self._ShowSelectionInNewPage ) ClientGUIMenus.AppendSeparator( menu ) ClientGUIMenus.AppendMenuItem( menu, 'copy sources', 'Copy all the selected sources to clipboard.', self._CopySelectedFileSeedData ) ClientGUIMenus.AppendMenuItem( menu, 'copy notes', 'Copy all the selected notes to clipboard.', self._CopySelectedNotes ) ClientGUIMenus.AppendSeparator( menu ) ClientGUIMenus.AppendMenuItem( menu, 'open sources', 'Open all the selected sources in your file explorer or web browser.', self._OpenSelectedFileSeedData ) ClientGUIMenus.AppendSeparator( menu ) ClientGUIMenus.AppendMenuItem( menu, 'try again', 'Reset the progress of all the selected imports.', HydrusData.Call( self._SetSelected, CC.STATUS_UNKNOWN ) ) ClientGUIMenus.AppendMenuItem( menu, 'skip', 'Skip all the selected imports.', HydrusData.Call( self._SetSelected, CC.STATUS_SKIPPED ) ) ClientGUIMenus.AppendMenuItem( menu, 'delete from list', 'Remove all the selected imports.', self._DeleteSelected ) return menu
def GetYAMLDump( self, dump_type, dump_name = None ): if dump_name is None: result = { dump_name : data for ( dump_name, data ) in self._c.execute( 'SELECT dump_name, dump FROM yaml_dumps WHERE dump_type = ?;', ( dump_type, ) ) } if dump_type == YAML_DUMP_ID_LOCAL_BOORU: result = { bytes.fromhex( dump_name ) : data for ( dump_name, data ) in list(result.items()) } else: if dump_type == YAML_DUMP_ID_LOCAL_BOORU: dump_name = dump_name.hex() result = self._c.execute( 'SELECT dump FROM yaml_dumps WHERE dump_type = ? AND dump_name = ?;', ( dump_type, dump_name ) ).fetchone() if result is None: if result is None: raise HydrusExceptions.DataMissing( dump_name + ' was not found!' ) else: ( result, ) = result return result
def GetICCProfileBytes(pil_image: PILImage.Image) -> bytes: if HasICCProfile(pil_image): return pil_image.info['icc_profile'] raise HydrusExceptions.DataMissing('This image has no ICC profile!')
def GetServiceId( self, service_key: bytes ) -> int: if service_key in self._service_keys_to_service_ids: return self._service_keys_to_service_ids[ service_key ] raise HydrusExceptions.DataMissing( 'Service id error in database: key "{}" does not exist!'.format( service_key.hex() ) )
def GetPageData(self, hash) -> "GUISessionPageData": if hash not in self._hashes_to_page_data: raise HydrusExceptions.DataMissing( 'The page hash "{}" was not found!'.format(hash.hex())) return self._hashes_to_page_data[hash]
def GetServiceType( self, service_id ) -> ClientServices.Service: if service_id in self._service_ids_to_services: return self._service_ids_to_services[ service_id ].GetServiceType() raise HydrusExceptions.DataMissing( 'Service id error in database: id "{}" does not exist!'.format( service_id ) )
def GetNote( self, name: str ): if name in self._names_to_notes: return self._names_to_notes[ name ] else: raise HydrusExceptions.DataMissing( 'Note "{}" does not exist!'.format( name ) )
def _GetService(self, service_key: bytes): try: return self._keys_to_services[service_key] except KeyError: raise HydrusExceptions.DataMissing('That service was not found!')
def GetPermissions( self, access_key ): with self._lock: if access_key not in self._access_keys_to_permissions: raise HydrusExceptions.DataMissing( 'Did not find an entry for that access key!' ) return self._access_keys_to_permissions[ access_key ]
def GetText(self, text_id): result = self._Execute('SELECT text FROM texts WHERE text_id = ?;', (text_id, )).fetchone() if result is None: raise HydrusExceptions.DataMissing('Text lookup error in database') (text, ) = result return text
def GetExtraHash( self, hash_type, hash_id ) -> bytes: result = self._c.execute( 'SELECT {} FROM local_hashes WHERE hash_id = ?;'.format( hash_type ), ( hash_id, ) ).fetchone() if result is None: raise HydrusExceptions.DataMissing( '{} not available for file {}!'.format( hash_type, hash_id ) ) ( hash, ) = result return hash
def GetServiceKeyFromName(self, allowed_types: typing.Iterable[int], service_name: str): with self._lock: for service in self._services_sorted: if service.GetServiceType( ) in allowed_types and service.GetName() == service_name: return service.GetServiceKey() raise HydrusExceptions.DataMissing()
def GetMime(self, hash_id: int) -> int: result = self._Execute( 'SELECT mime FROM files_info WHERE hash_id = ?;', (hash_id, )).fetchone() if result is None: raise HydrusExceptions.DataMissing( 'Did not have mime information for that file!') (mime, ) = result return mime
def GetCookie(cookies, search_domain, cookie_name_string_match): for cookie in cookies: if CookieDomainMatches( cookie, search_domain) and cookie_name_string_match.Matches( cookie.name): return cookie raise HydrusExceptions.DataMissing('Cookie "' + cookie_name_string_match.ToString() + '" not found for domain ' + search_domain + '!')
def SetStylesheetFromPath(filename): path = os.path.join(STYLESHEET_DIR, filename) if not os.path.exists(path): raise HydrusExceptions.DataMissing( 'Stylesheet "{}" does not exist!'.format(path)) with open(path, 'r', encoding='utf-8') as f: qss = f.read() SetStyleSheet(qss)
def GetAvailableStylesheets(): if not os.path.exists(STYLESHEET_DIR) or not os.path.isdir(STYLESHEET_DIR): raise HydrusExceptions.DataMissing( 'Stylesheet dir "{}" is missing or not a directory!'.format( STYLESHEET_DIR)) stylesheet_filenames = [] extensions = ['.qss', '.css'] for filename in os.listdir(STYLESHEET_DIR): if True in (filename.endswith(ext) for ext in extensions): stylesheet_filenames.append(filename) return stylesheet_filenames
def GetAccessKey( self, session_key ): with self._lock: if session_key not in self._session_keys_to_access_keys_and_expirys: raise HydrusExceptions.DataMissing( 'Did not find an entry for that session key!' ) ( access_key, session_expiry ) = self._session_keys_to_access_keys_and_expirys[ session_key ] if HydrusData.TimeHasPassed( session_expiry ): del self._session_keys_to_access_keys_and_expirys[ session_expiry ] raise HydrusExceptions.SessionException( 'That session key has expired!' ) self._session_keys_to_access_keys_and_expirys[ session_key ] = ( access_key, HydrusData.GetNow() + SESSION_EXPIRY ) return access_key
def GetJSONDumpNamed(self, dump_type, dump_name=None, timestamp=None): if dump_name is None: results = self._c.execute( 'SELECT dump_name, version, dump, timestamp FROM json_dumps_named WHERE dump_type = ?;', (dump_type, )).fetchall() objs = [] for (dump_name, version, dump, object_timestamp) in results: try: if isinstance(dump, bytes): dump = str(dump, 'utf-8') serialisable_info = json.loads(dump) objs.append( HydrusSerialisable.CreateFromSerialisableTuple( (dump_type, dump_name, version, serialisable_info))) except: self._c.execute( 'DELETE FROM json_dumps_named WHERE dump_type = ? AND dump_name = ? AND timestamp = ?;', (dump_type, dump_name, object_timestamp)) self._cursor_transaction_wrapper.CommitAndBegin() DealWithBrokenJSONDump( self._db_dir, dump, 'dump_type {} dump_name {} timestamp {}'.format( dump_type, dump_name[:10], timestamp)) return objs else: if timestamp is None: result = self._c.execute( 'SELECT version, dump, timestamp FROM json_dumps_named WHERE dump_type = ? AND dump_name = ? ORDER BY timestamp DESC;', (dump_type, dump_name)).fetchone() else: result = self._c.execute( 'SELECT version, dump, timestamp FROM json_dumps_named WHERE dump_type = ? AND dump_name = ? AND timestamp = ?;', (dump_type, dump_name, timestamp)).fetchone() if result is None: raise HydrusExceptions.DataMissing( 'Could not find the object of type "{}" and name "{}" and timestamp "{}".' .format(dump_type, dump_name, str(timestamp))) (version, dump, object_timestamp) = result try: if isinstance(dump, bytes): dump = str(dump, 'utf-8') serialisable_info = json.loads(dump) except: self._c.execute( 'DELETE FROM json_dumps_named WHERE dump_type = ? AND dump_name = ? AND timestamp = ?;', (dump_type, dump_name, object_timestamp)) self._cursor_transaction_wrapper.CommitAndBegin() DealWithBrokenJSONDump( self._db_dir, dump, 'dump_type {} dump_name {} timestamp {}'.format( dump_type, dump_name[:10], object_timestamp)) return HydrusSerialisable.CreateFromSerialisableTuple( (dump_type, dump_name, version, serialisable_info))
def _PopulateHashIdsToHashesCache(self, hash_ids, exception_on_error=False): if len(self._hash_ids_to_hashes_cache) > 100000: if not isinstance(hash_ids, set): hash_ids = set(hash_ids) self._hash_ids_to_hashes_cache = { hash_id: hash for (hash_id, hash) in self._hash_ids_to_hashes_cache.items() if hash_id in hash_ids } uncached_hash_ids = { hash_id for hash_id in hash_ids if hash_id not in self._hash_ids_to_hashes_cache } if len(uncached_hash_ids) > 0: pubbed_error = False if len(uncached_hash_ids) == 1: (uncached_hash_id, ) = uncached_hash_ids rows = self._Execute( 'SELECT hash_id, hash FROM hashes WHERE hash_id = ?;', (uncached_hash_id, )).fetchall() else: with self._MakeTemporaryIntegerTable( uncached_hash_ids, 'hash_id') as temp_table_name: # temp hash_ids to actual hashes rows = self._Execute( 'SELECT hash_id, hash FROM {} CROSS JOIN hashes USING ( hash_id );' .format(temp_table_name)).fetchall() uncached_hash_ids_to_hashes = dict(rows) if len(uncached_hash_ids_to_hashes) < len(uncached_hash_ids): for hash_id in uncached_hash_ids: if hash_id not in uncached_hash_ids_to_hashes: if exception_on_error: raise HydrusExceptions.DataMissing( 'Did not find all entries for those hash ids!') HydrusData.DebugPrint('Database hash error: hash_id ' + str(hash_id) + ' was missing!') HydrusData.PrintException( Exception('Missing file identifier stack trace.')) if not pubbed_error: HydrusData.ShowText( 'A file identifier was missing! This is a serious error that means your client database has an orphan file id! Think about contacting hydrus dev!' ) pubbed_error = True hash = bytes.fromhex('aaaaaaaaaaaaaaaa') + os.urandom( 16) uncached_hash_ids_to_hashes[hash_id] = hash self._hash_ids_to_hashes_cache.update(uncached_hash_ids_to_hashes)
def GetFFMPEGInfoLines( path, count_frames_manually = False, only_first_second = False ): # open the file in a pipe, provoke an error, read output cmd = [ FFMPEG_PATH, "-i", path ] if only_first_second: cmd.insert( 1, '-t' ) cmd.insert( 2, '1' ) if count_frames_manually: # added -an here to remove audio component, which was sometimes causing convert fails on single-frame music webms if HC.PLATFORM_WINDOWS: cmd += [ "-vf", "scale=-2:120", "-an", "-f", "null", "NUL" ] else: cmd += [ "-vf", "scale=-2:120", "-an", "-f", "null", "/dev/null" ] sbp_kwargs = HydrusData.GetSubprocessKWArgs() HydrusData.CheckProgramIsNotShuttingDown() try: process = subprocess.Popen( cmd, bufsize = 10**5, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, **sbp_kwargs ) except FileNotFoundError as e: global FFMPEG_MISSING_ERROR_PUBBED if not FFMPEG_MISSING_ERROR_PUBBED: message = 'FFMPEG, which hydrus uses to parse and render video, was not found! This may be due to it not being available on your system, or hydrus being unable to find it.' message += os.linesep * 2 if HC.PLATFORM_WINDOWS: message += 'You are on Windows, so there should be a copy of ffmpeg.exe in your install_dir/bin folder. If not, please check if your anti-virus has removed it and restore it through a new install.' else: message += 'If you are certain that FFMPEG is installed on your OS and accessible in your PATH, please let hydrus_dev know, as this problem is likely due to an environment problem. You may be able to solve this problem immediately by putting a static build of the ffmpeg executable in your install_dir/bin folder.' message += os.linesep * 2 message += 'You can check your current FFMPEG status through help->about.' HydrusData.ShowText( message ) FFMPEG_MISSING_ERROR_PUBBED = True raise FileNotFoundError( 'Cannot interact with video because FFMPEG not found--are you sure it is installed? Full error: ' + str( e ) ) ( stdout, stderr ) = HydrusThreading.SubprocessCommunicate( process ) data_bytes = stderr if len( data_bytes ) == 0: global FFMPEG_NO_CONTENT_ERROR_PUBBED if not FFMPEG_NO_CONTENT_ERROR_PUBBED: message = 'FFMPEG, which hydrus uses to parse and render video, did not return any data on a recent file metadata check! More debug info has been written to the log.' message += os.linesep * 2 message += 'You can check this info again through help->about.' HydrusData.ShowText( message ) message += os.linesep * 2 message += str( sbp_kwargs ) message += os.linesep * 2 message += str( os.environ ) message += os.linesep * 2 message += 'STDOUT Response: {}'.format( stdout ) message += os.linesep * 2 message += 'STDERR Response: {}'.format( stderr ) HydrusData.DebugPrint( message ) FFMPEG_NO_CONTENT_ERROR_PUBBED = True raise HydrusExceptions.DataMissing( 'Cannot interact with video because FFMPEG did not return any content.' ) del process ( text, encoding ) = HydrusText.NonFailingUnicodeDecode( data_bytes, 'utf-8' ) lines = text.splitlines() CheckFFMPEGError( lines ) return lines
def _GetListCtrlMenu(self): selected_file_seeds = self._list_ctrl.GetData(only_selected=True) if len(selected_file_seeds) == 0: raise HydrusExceptions.DataMissing() menu = QW.QMenu() can_show_files_in_new_page = True in ( file_seed.HasHash() for file_seed in selected_file_seeds) if can_show_files_in_new_page: ClientGUIMenus.AppendMenuItem( menu, 'open selected import files in a new page', 'Show all the known selected files in a new thumbnail page. This is complicated, so cannot always be guaranteed, even if the import says \'success\'.', self._ShowSelectionInNewPage) ClientGUIMenus.AppendSeparator(menu) ClientGUIMenus.AppendMenuItem( menu, 'copy sources', 'Copy all the selected sources to clipboard.', self._CopySelectedFileSeedData) ClientGUIMenus.AppendMenuItem( menu, 'copy notes', 'Copy all the selected notes to clipboard.', self._CopySelectedNotes) if len(selected_file_seeds) == 1: ClientGUIMenus.AppendSeparator(menu) (selected_file_seed, ) = selected_file_seeds hash_types_to_hashes = selected_file_seed.GetHashTypesToHashes() if len(hash_types_to_hashes) == 0: ClientGUIMenus.AppendMenuLabel(menu, 'no hashes yet') else: hash_submenu = QW.QMenu(menu) for hash_type in ('sha256', 'md5', 'sha1', 'sha512'): if hash_type in hash_types_to_hashes: h = hash_types_to_hashes[hash_type] ClientGUIMenus.AppendMenuLabel( hash_submenu, '{}:{}'.format(hash_type, h.hex())) ClientGUIMenus.AppendMenu(menu, hash_submenu, 'hashes') # if selected_file_seed.IsURLFileImport(): referral_url = selected_file_seed.GetReferralURL() primary_urls = sorted(selected_file_seed.GetPrimaryURLs()) source_urls = sorted(selected_file_seed.GetSourceURLs()) if referral_url is None and len(primary_urls) + len( source_urls) == 0: ClientGUIMenus.AppendMenuLabel(menu, 'no additional urls') else: url_submenu = QW.QMenu(menu) if referral_url is not None: ClientGUIMenus.AppendMenuLabel(url_submenu, 'referral url:') ClientGUIMenus.AppendMenuLabel(url_submenu, referral_url) if len(primary_urls) > 0: ClientGUIMenus.AppendSeparator(url_submenu) ClientGUIMenus.AppendMenuLabel(url_submenu, 'primary urls:') for url in primary_urls: ClientGUIMenus.AppendMenuLabel(url_submenu, url) if len(source_urls) > 0: ClientGUIMenus.AppendSeparator(url_submenu) ClientGUIMenus.AppendMenuLabel(url_submenu, 'source urls:') for url in source_urls: ClientGUIMenus.AppendMenuLabel(url_submenu, url) ClientGUIMenus.AppendMenu(menu, url_submenu, 'additional urls') # tags = list(selected_file_seed.GetExternalTags()) tag_sort = ClientTagSorting.TagSort( sort_type=ClientTagSorting.SORT_BY_HUMAN_TAG, sort_order=CC.SORT_ASC) ClientTagSorting.SortTags(tag_sort, tags) if len(tags) == 0: ClientGUIMenus.AppendMenuLabel(menu, 'no parsed tags') else: tag_submenu = QW.QMenu(menu) for tag in tags: ClientGUIMenus.AppendMenuLabel(tag_submenu, tag) ClientGUIMenus.AppendMenu(menu, tag_submenu, 'parsed tags') ClientGUIMenus.AppendSeparator(menu) ClientGUIMenus.AppendMenuItem( menu, 'open sources', 'Open all the selected sources in your file explorer or web browser.', self._OpenSelectedFileSeedData) ClientGUIMenus.AppendSeparator(menu) ClientGUIMenus.AppendMenuItem( menu, 'try again', 'Reset the progress of all the selected imports.', HydrusData.Call(self._SetSelected, CC.STATUS_UNKNOWN)) ClientGUIMenus.AppendMenuItem( menu, 'skip', 'Skip all the selected imports.', HydrusData.Call(self._SetSelected, CC.STATUS_SKIPPED)) ClientGUIMenus.AppendMenuItem(menu, 'delete from list', 'Remove all the selected imports.', HydrusData.Call(self._DeleteSelected)) return menu