def subtag_lexicographic_key(tag): (namespace, subtag) = HydrusTags.SplitTag(tag) comparable_subtag = HydrusTags.ConvertTagToSortable(subtag) return comparable_subtag
def GetTagId( self, tag ) -> int: clean_tag = HydrusTags.CleanTag( tag ) try: HydrusTags.CheckTagNotEmpty( clean_tag ) except HydrusExceptions.TagSizeException: # update this to instead go 'hey, does the dirty tag exist?' if it does, run the fix invalid tags routine raise HydrusExceptions.TagSizeException( '"{}" tag seems not valid--when cleaned, it ends up with zero size!'.format( tag ) ) ( namespace, subtag ) = HydrusTags.SplitTag( clean_tag ) namespace_id = self.GetNamespaceId( namespace ) subtag_id = self.GetSubtagId( subtag ) result = self._c.execute( 'SELECT tag_id FROM tags WHERE namespace_id = ? AND subtag_id = ?;', ( namespace_id, subtag_id ) ).fetchone() if result is None: self._c.execute( 'INSERT INTO tags ( namespace_id, subtag_id ) VALUES ( ?, ? );', ( namespace_id, subtag_id ) ) tag_id = self._c.lastrowid else: ( tag_id, ) = result return tag_id
def GetComparableNamespaceSlice(self, namespaces, tag_display_type): with self._lock: service_keys_to_statuses_to_tags = self._GetServiceKeysToStatusesToTags( tag_display_type) combined_statuses_to_tags = service_keys_to_statuses_to_tags[ CC.COMBINED_TAG_SERVICE_KEY] combined_current = combined_statuses_to_tags[ HC.CONTENT_STATUS_CURRENT] combined_pending = combined_statuses_to_tags[ HC.CONTENT_STATUS_PENDING] combined = combined_current.union(combined_pending) pairs = [HydrusTags.SplitTag(tag) for tag in combined] slice = [] for desired_namespace in namespaces: subtags = [ HydrusTags.ConvertTagToSortable(subtag) for (namespace, subtag) in pairs if namespace == desired_namespace ] subtags.sort() slice.append(tuple(subtags)) return tuple(slice)
def GetTagId(self, tag) -> int: clean_tag = HydrusTags.CleanTag(tag) try: HydrusTags.CheckTagNotEmpty(clean_tag) except HydrusExceptions.TagSizeException: raise HydrusExceptions.TagSizeException( '"{}" tag seems not valid--when cleaned, it ends up with zero size!' .format(tag)) result = self._Execute( 'SELECT tag_id FROM local_tags_cache WHERE tag = ?;', (tag, )).fetchone() if result is None: return self.modules_tags.GetTagId(tag) else: (tag_id, ) = result return tag_id
def _PopulateTagIdsToTagsCache( self, tag_ids ): if len( self._tag_ids_to_tags_cache ) > 100000: if not isinstance( tag_ids, set ): tag_ids = set( tag_ids ) self._tag_ids_to_tags_cache = { tag_id : tag for ( tag_id, tag ) in self._tag_ids_to_tags_cache.items() if tag_id in tag_ids } uncached_tag_ids = { tag_id for tag_id in tag_ids if tag_id not in self._tag_ids_to_tags_cache } if len( uncached_tag_ids ) > 0: if len( uncached_tag_ids ) == 1: ( uncached_tag_id, ) = uncached_tag_ids rows = self._c.execute( 'SELECT tag_id, namespace, subtag FROM tags NATURAL JOIN namespaces NATURAL JOIN subtags WHERE tag_id = ?;', ( uncached_tag_id, ) ).fetchall() else: with HydrusDB.TemporaryIntegerTable( self._c, uncached_tag_ids, 'tag_id' ) as temp_table_name: # temp tag_ids to tags to subtags and namespaces rows = self._c.execute( 'SELECT tag_id, namespace, subtag FROM {} CROSS JOIN tags USING ( tag_id ) CROSS JOIN subtags USING ( subtag_id ) CROSS JOIN namespaces USING ( namespace_id );'.format( temp_table_name ) ).fetchall() uncached_tag_ids_to_tags = { tag_id : HydrusTags.CombineTag( namespace, subtag ) for ( tag_id, namespace, subtag ) in rows } if len( uncached_tag_ids_to_tags ) < len( uncached_tag_ids ): for tag_id in uncached_tag_ids: if tag_id not in uncached_tag_ids_to_tags: tag = 'unknown tag:' + HydrusData.GenerateKey().hex() ( namespace, subtag ) = HydrusTags.SplitTag( tag ) namespace_id = self.GetNamespaceId( namespace ) subtag_id = self.GetSubtagId( subtag ) self._c.execute( 'REPLACE INTO tags ( tag_id, namespace_id, subtag_id ) VALUES ( ?, ?, ? );', ( tag_id, namespace_id, subtag_id ) ) uncached_tag_ids_to_tags[ tag_id ] = tag self._tag_ids_to_tags_cache.update( uncached_tag_ids_to_tags )
def namespace_lexicographic_key(tag): # '{' is above 'z' in ascii, so this works for most situations (namespace, subtag) = HydrusTags.SplitTag(tag) if namespace == '': return ('{', HydrusTags.ConvertTagToSortable(subtag)) else: return (namespace, HydrusTags.ConvertTagToSortable(subtag))
def GetCopyableText(self, with_counts: bool = False) -> str: if self._namespace is None: return HydrusTags.ConvertTagSliceToString(':') elif self._namespace == '': return HydrusTags.ConvertTagSliceToString('') else: return HydrusTags.ConvertTagSliceToString('{}:'.format( self._namespace))
def lexicographic_key(tag): (namespace, subtag) = HydrusTags.SplitTag(tag) comparable_namespace = HydrusTags.ConvertTagToSortable(namespace) comparable_subtag = HydrusTags.ConvertTagToSortable(subtag) if namespace == '': return (comparable_subtag, comparable_subtag) else: return (comparable_namespace, comparable_subtag)
def TagExists( self, tag ): try: tag = HydrusTags.CleanTag( tag ) except: return False try: HydrusTags.CheckTagNotEmpty( tag ) except HydrusExceptions.TagSizeException: return False ( namespace, subtag ) = HydrusTags.SplitTag( tag ) if self.NamespaceExists( namespace ): namespace_id = self.GetNamespaceId( namespace ) else: return False if self.SubtagExists( subtag ): subtag_id = self.GetSubtagId( subtag ) result = self._c.execute( 'SELECT 1 FROM tags WHERE namespace_id = ? AND subtag_id = ?;', ( namespace_id, subtag_id ) ).fetchone() if result is None: return False else: return True else: return False
def RenderTag(tag, render_for_user): (namespace, subtag) = HydrusTags.SplitTag(tag) if namespace == '': return subtag else: if render_for_user: new_options = HG.client_controller.new_options if new_options.GetBoolean('show_namespaces'): connector = new_options.GetString('namespace_connector') else: return subtag else: connector = ':' return namespace + connector + subtag
def GetHashIdsFromTag( self, tag_display_type: int, location_context: ClientLocation.LocationContext, tag_search_context: ClientSearch.TagSearchContext, tag, hash_ids = None, hash_ids_table_name = None, allow_unnamespaced_to_fetch_namespaced = True, job_key = None ): ( file_service_keys, file_location_is_cross_referenced ) = location_context.GetCoveringCurrentFileServiceKeys() if not file_location_is_cross_referenced and hash_ids_table_name is not None: file_location_is_cross_referenced = True ( namespace, subtag ) = HydrusTags.SplitTag( tag ) subtag_id = self.modules_tags.GetSubtagId( subtag ) if not self.modules_tags.SubtagExists( subtag ): return set() tag_service_id = self.modules_services.GetServiceId( tag_search_context.service_key ) results = set() for file_service_key in file_service_keys: if namespace == '' and allow_unnamespaced_to_fetch_namespaced: file_service_id = self.modules_services.GetServiceId( file_service_key ) tag_ids = self.modules_tag_search.GetTagIdsFromSubtagIds( file_service_id, tag_service_id, ( subtag_id, ) ) else: if not self.modules_tags.TagExists( tag ): return set() tag_id = self.modules_tags.GetTagId( tag ) tag_ids = ( tag_id, ) some_results = self.GetHashIdsFromTagIds( tag_display_type, file_service_key, tag_search_context, tag_ids, hash_ids = hash_ids, hash_ids_table_name = hash_ids_table_name, job_key = job_key ) if len( results ) == 0: results = some_results else: results.update( some_results ) if not file_location_is_cross_referenced: results = self.modules_files_storage.FilterHashIds( location_context, results ) return results
def RenderTag(tag, render_for_user: bool): if render_for_user: new_options = HG.client_controller.new_options if new_options.GetBoolean('replace_tag_underscores_with_spaces'): tag = tag.replace('_', ' ') (namespace, subtag) = HydrusTags.SplitTag(tag) if namespace == '': return subtag else: if render_for_user: if new_options.GetBoolean('show_namespaces'): connector = new_options.GetString('namespace_connector') else: return subtag else: connector = ':' return namespace + connector + subtag
def GetRowsOfPresentationTextsWithNamespaces( self, render_for_user: bool, sibling_decoration_allowed: bool, child_rows_allowed: bool ) -> typing.List[typing.List[typing.Tuple[str, str]]]: # this should be with counts or whatever, but we need to think about this more lad (namespace, subtag) = HydrusTags.SplitTag(self._tag) tag_text = ClientTags.RenderTag(self._tag, render_for_user) first_row_of_texts_with_namespaces = [(tag_text, namespace)] if sibling_decoration_allowed and self._ideal_tag is not None: self._AppendIdealTagTextWithNamespace( first_row_of_texts_with_namespaces, render_for_user) rows_of_texts_with_namespaces = [first_row_of_texts_with_namespaces] if self._parent_tags is not None: if child_rows_allowed: self._AppendParentsTextWithNamespaces( rows_of_texts_with_namespaces, render_for_user) elif sibling_decoration_allowed: self._AppendParentSuffixTagTextWithNamespace( first_row_of_texts_with_namespaces) return rows_of_texts_with_namespaces
def __init__( self, name = 'new api permissions', access_key = None, basic_permissions = None, search_tag_filter = None ): if access_key is None: access_key = HydrusData.GenerateKey() if basic_permissions is None: basic_permissions = set() if search_tag_filter is None: search_tag_filter = HydrusTags.TagFilter() HydrusSerialisable.SerialisableBaseNamed.__init__( self, name ) self._access_key = access_key self._basic_permissions = set( basic_permissions ) self._search_tag_filter = search_tag_filter self._last_search_results = None self._search_results_timeout = 0 self._lock = threading.Lock()
def __init__( self, fetch_tags_even_if_url_recognised_and_file_already_in_db=False, fetch_tags_even_if_hash_recognised_and_file_already_in_db=False, tag_blacklist=None, tag_whitelist=None, service_keys_to_service_tag_import_options=None, is_default=False): HydrusSerialisable.SerialisableBase.__init__(self) if tag_blacklist is None: tag_blacklist = HydrusTags.TagFilter() if tag_whitelist is None: tag_whitelist = [] if service_keys_to_service_tag_import_options is None: service_keys_to_service_tag_import_options = {} self._fetch_tags_even_if_url_recognised_and_file_already_in_db = fetch_tags_even_if_url_recognised_and_file_already_in_db self._fetch_tags_even_if_hash_recognised_and_file_already_in_db = fetch_tags_even_if_hash_recognised_and_file_already_in_db self._tag_blacklist = tag_blacklist self._tag_whitelist = tag_whitelist self._service_keys_to_service_tag_import_options = service_keys_to_service_tag_import_options self._is_default = is_default
def GetServiceKeysToContentUpdates( self, status: int, media_result: ClientMediaResult.MediaResult, filterable_tags: typing.Iterable[str], external_filterable_tags=None, external_additional_service_keys_to_tags=None): if external_filterable_tags is None: external_filterable_tags = set() if external_additional_service_keys_to_tags is None: external_additional_service_keys_to_tags = ClientTags.ServiceKeysToTags( ) filterable_tags = HydrusTags.CleanTags(filterable_tags) service_keys_to_tags = ClientTags.ServiceKeysToTags() for service_key in HG.client_controller.services_manager.GetServiceKeys( HC.REAL_TAG_SERVICES): service_additional_tags = set() if service_key in external_additional_service_keys_to_tags: service_additional_tags.update( external_additional_service_keys_to_tags[service_key]) if service_key in self._service_keys_to_service_tag_import_options: service_tag_import_options = self._service_keys_to_service_tag_import_options[ service_key] service_filterable_tags = set(filterable_tags) service_filterable_tags.update(external_filterable_tags) service_tags = service_tag_import_options.GetTags( service_key, status, media_result, service_filterable_tags, service_additional_tags) else: service_tags = service_additional_tags if len(service_tags) > 0: service_keys_to_tags[service_key] = service_tags hash = media_result.GetHash() service_keys_to_content_updates = ClientData.ConvertServiceKeysToTagsToServiceKeysToContentUpdates( {hash}, service_keys_to_tags) return service_keys_to_content_updates
def namespace_key(tag): (namespace, subtag) = HydrusTags.SplitTag(tag) if namespace == '': namespace = '{' # '{' is above 'z' in ascii, so this works for most situations return namespace
def _AppendIdealTagTextWithNamespace(self, texts_with_namespaces, render_for_user): (namespace, subtag) = HydrusTags.SplitTag(self._ideal_tag) ideal_text = ' (displays as {})'.format( ClientTags.RenderTag(self._ideal_tag, render_for_user)) texts_with_namespaces.append((ideal_text, namespace))
def GetTags( self, service_key: bytes, status: int, media_result: ClientMediaResult.MediaResult, filterable_tags: typing.Collection[str], additional_tags: typing.Optional[typing.Collection[str]] = None): if additional_tags is None: additional_tags = set() tags = set() in_inbox = media_result.GetInbox() if ClientImportOptions.NewInboxArchiveMatch( self._to_new_files, self._to_already_in_inbox, self._to_already_in_archive, status, in_inbox): if self._get_tags: filtered_tags = self._get_tags_filter.Filter(filterable_tags) if not self._get_tags_overwrite_deleted: filtered_tags = ClientImportOptions.FilterDeletedTags( service_key, media_result, filtered_tags) tags.update(filtered_tags) additional_tags = set(additional_tags) additional_tags.update(self._additional_tags) additional_tags = HydrusTags.CleanTags(additional_tags) if not self._additional_tags_overwrite_deleted: additional_tags = ClientImportOptions.FilterDeletedTags( service_key, media_result, additional_tags) tags.update(additional_tags) if self._only_add_existing_tags: applicable_tags = self._only_add_existing_tags_filter.Filter( tags) tags.difference_update(applicable_tags) existing_applicable_tags = HG.client_controller.Read( 'filter_existing_tags', service_key, applicable_tags) tags.update(existing_applicable_tags) return tags
def _UpdateSerialisableInfo( self, version, old_serialisable_info ): if version == 1: ( serialisable_service_actions, delete_second_file ) = old_serialisable_info tag_service_actions = [] rating_service_actions = [] # As the client isn't booted when this is loaded in options, there isn't a good way to figure out tag from rating # So, let's just dupe and purge later on, in serialisation for ( service_key_encoded, action ) in serialisable_service_actions: service_key = bytes.fromhex( service_key_encoded ) tag_filter = HydrusTags.TagFilter() tag_service_actions.append( ( service_key, action, tag_filter ) ) rating_service_actions.append( ( service_key, action ) ) serialisable_tag_service_actions = [ ( service_key.hex(), action, tag_filter.GetSerialisableTuple() ) for ( service_key, action, tag_filter ) in tag_service_actions ] serialisable_rating_service_actions = [ ( service_key.hex(), action ) for ( service_key, action ) in rating_service_actions ] sync_archive = delete_second_file delete_both_files = False new_serialisable_info = ( serialisable_tag_service_actions, serialisable_rating_service_actions, delete_second_file, sync_archive, delete_both_files ) return ( 2, new_serialisable_info ) if version == 2: ( serialisable_tag_service_actions, serialisable_rating_service_actions, delete_second_file, sync_archive, delete_both_files ) = old_serialisable_info sync_urls_action = None new_serialisable_info = ( serialisable_tag_service_actions, serialisable_rating_service_actions, delete_second_file, sync_archive, delete_both_files, sync_urls_action ) return ( 3, new_serialisable_info ) if version == 3: ( serialisable_tag_service_actions, serialisable_rating_service_actions, delete_second_file, sync_archive, delete_both_files, sync_urls_action ) = old_serialisable_info new_serialisable_info = ( serialisable_tag_service_actions, serialisable_rating_service_actions, sync_archive, sync_urls_action ) return ( 4, new_serialisable_info )
def _AppendParentsTextWithNamespaces(self, rows_of_texts_with_namespaces, render_for_user): indent = ' ' for parent in self._parent_tags: (namespace, subtag) = HydrusTags.SplitTag(parent) tag_text = ClientTags.RenderTag(parent, render_for_user) texts_with_namespaces = [(indent + tag_text, namespace)] rows_of_texts_with_namespaces.append(texts_with_namespaces)
def CheckTagsVeto(self, tags: typing.Collection[str], sibling_tags: typing.Collection[str]): tags = set(tags) sibling_tags = set(sibling_tags) for test_tags in (tags, sibling_tags): ok_tags = self._tag_blacklist.Filter( test_tags, apply_unnamespaced_rules_to_namespaced_tags=True) if len(ok_tags) < len(test_tags): bad_tags = test_tags.difference(ok_tags) bad_tags = HydrusTags.SortNumericTags(bad_tags) raise HydrusExceptions.VetoException(', '.join(bad_tags) + ' is blacklisted!') if len(self._tag_whitelist) > 0: all_tags = tags.union(sibling_tags) for tag in list(all_tags): (namespace, subtag) = HydrusTags.SplitTag(tag) all_tags.add(subtag) intersecting_tags = all_tags.intersection(self._tag_whitelist) if len(intersecting_tags) == 0: raise HydrusExceptions.VetoException( 'did not pass the whitelist!')
def __init__(self, get_tags=False, get_tags_filter=None, additional_tags=None, to_new_files=True, to_already_in_inbox=True, to_already_in_archive=True, only_add_existing_tags=False, only_add_existing_tags_filter=None, get_tags_overwrite_deleted=False, additional_tags_overwrite_deleted=False): if get_tags_filter is None: get_tags_filter = HydrusTags.TagFilter() if additional_tags is None: additional_tags = [] if only_add_existing_tags_filter is None: only_add_existing_tags_filter = HydrusTags.TagFilter() HydrusSerialisable.SerialisableBase.__init__(self) self._get_tags = get_tags self._get_tags_filter = get_tags_filter self._additional_tags = additional_tags self._to_new_files = to_new_files self._to_already_in_inbox = to_already_in_inbox self._to_already_in_archive = to_already_in_archive self._only_add_existing_tags = only_add_existing_tags self._only_add_existing_tags_filter = only_add_existing_tags_filter self._get_tags_overwrite_deleted = get_tags_overwrite_deleted self._additional_tags_overwrite_deleted = additional_tags_overwrite_deleted
def GetRowsOfPresentationTextsWithNamespaces( self, render_for_user: bool, sibling_decoration_allowed: bool, child_rows_allowed: bool) -> typing.List[typing.Tuple[str, str]]: presentation_text = self.GetCopyableText() if self._tag_slice in ('', ':'): namespace = '' else: (namespace, subtag) = HydrusTags.SplitTag(self._tag_slice) return [[(presentation_text, namespace)]]
def GetRowsOfPresentationTextsWithNamespaces( self, render_for_user: bool, sibling_decoration_allowed: bool, child_rows_allowed: bool ) -> typing.List[typing.List[typing.Tuple[str, str]]]: rows_of_texts_and_namespaces = [] first_row_of_texts_and_namespaces = self._predicate.GetTextsAndNamespaces( render_for_user, or_under_construction=self._i_am_an_or_under_construction) if sibling_decoration_allowed and self._predicate.HasIdealSibling(): ideal_sibling = self._predicate.GetIdealSibling() (ideal_namespace, ideal_subtag) = HydrusTags.SplitTag(ideal_sibling) ideal_text = ' (displays as {})'.format( ClientTags.RenderTag(ideal_sibling, render_for_user)) first_row_of_texts_and_namespaces.append( (ideal_text, ideal_namespace)) rows_of_texts_and_namespaces.append(first_row_of_texts_and_namespaces) parent_preds = self._predicate.GetParentPredicates() if len(parent_preds) > 0: if child_rows_allowed: for parent_pred in self._predicate.GetParentPredicates(): rows_of_texts_and_namespaces.append( parent_pred.GetTextsAndNamespaces(render_for_user)) elif sibling_decoration_allowed: parents_text = ' ({} parents)'.format( HydrusData.ToHumanInt(len(parent_preds))) first_row_of_texts_and_namespaces.append((parents_text, '')) return rows_of_texts_and_namespaces
def GetCopyableText(self, with_counts: bool = False) -> str: if self._predicate.GetType() == ClientSearch.PREDICATE_TYPE_NAMESPACE: namespace = self._predicate.GetValue() # this is useful for workflow text = '{}:*'.format(namespace) elif self._predicate.GetType() == ClientSearch.PREDICATE_TYPE_PARENT: text = HydrusTags.CleanTag(self._predicate.GetValue()) else: text = self._predicate.ToString(with_count=with_counts) return text
def SubtagExists(self, subtag): try: HydrusTags.CheckTagNotEmpty(subtag) except HydrusExceptions.TagSizeException: return False result = self._Execute('SELECT 1 FROM subtags WHERE subtag = ?;', (subtag, )).fetchone() if result is None: return False else: return True
def _IterateTagSlices(self, tag, apply_unnamespaced_rules_to_namespaced_tags): # this guy gets called a lot, so we are making it an iterator yield tag (namespace, subtag) = HydrusTags.SplitTag(tag) if tag != subtag and apply_unnamespaced_rules_to_namespaced_tags: yield subtag if namespace != '': yield '{}:'.format(namespace) yield ':' else: yield ''
def _GetTagSlices(self, tag, apply_unnamespaced_rules_to_namespaced_tags): (namespace, subtag) = HydrusTags.SplitTag(tag) tag_slices = [] tag_slices.append(tag) if tag != subtag and apply_unnamespaced_rules_to_namespaced_tags: tag_slices.append(subtag) if namespace != '': tag_slices.append(namespace + ':') tag_slices.append(':') else: tag_slices.append('') return tag_slices
def GenerateExportFilename( destination_directory, media, terms, do_not_use_filenames = None ): def clean_tag_text( t ): if HC.PLATFORM_WINDOWS: t = re.sub( r'\\', '_', t ) else: t = re.sub( '/', '_', t ) return t if len( destination_directory ) > ( MAX_PATH_LENGTH - 10 ): raise Exception( 'The destination directory is too long!' ) filename = '' for ( term_type, term ) in terms: tags_manager = media.GetTagsManager() if term_type == 'string': filename += term elif term_type == 'namespace': tags = tags_manager.GetNamespaceSlice( ( term, ), ClientTags.TAG_DISPLAY_ACTUAL ) subtags = sorted( ( HydrusTags.SplitTag( tag )[1] for tag in tags ) ) filename += clean_tag_text( ', '.join( subtags ) ) elif term_type == 'predicate': if term in ( 'tags', 'nn tags' ): current = tags_manager.GetCurrent( CC.COMBINED_TAG_SERVICE_KEY, ClientTags.TAG_DISPLAY_ACTUAL ) pending = tags_manager.GetPending( CC.COMBINED_TAG_SERVICE_KEY, ClientTags.TAG_DISPLAY_ACTUAL ) tags = sorted( current.union( pending ) ) if term == 'nn tags': tags = [ tag for tag in tags if ':' not in tag ] else: tags = [ HydrusTags.SplitTag( tag )[1] for tag in tags ] filename += clean_tag_text( ', '.join( tags ) ) elif term == 'hash': hash = media.GetHash() filename += hash.hex() elif term == 'file_id': hash_id = media.GetHashId() filename += str( hash_id ) elif term_type == 'tag': tag = term ( namespace, subtag ) = HydrusTags.SplitTag( tag ) if tags_manager.HasTag( subtag, ClientTags.TAG_DISPLAY_ACTUAL ): filename += clean_tag_text( subtag ) while filename.startswith( os.path.sep ): filename = filename[1:] if HC.PLATFORM_WINDOWS: # replace many consecutive backspace with single backspace filename = re.sub( '\\\\+', '\\\\', filename ) # /, :, *, ?, ", <, >, | filename = re.sub( r'/|:|\*|\?|"|<|>|\|', '_', filename ) else: filename = re.sub( '/+', '/', filename ) # mime = media.GetMime() ext = HC.mime_ext_lookup[ mime ] if filename.endswith( ext ): filename = filename[ : - len( ext ) ] example_dest_path = os.path.join( destination_directory, filename + ext ) excess_chars = len( example_dest_path ) - MAX_PATH_LENGTH if excess_chars > 0: filename = filename[ : - excess_chars ] if do_not_use_filenames is not None: i = 1 possible_filename = '{}{}'.format( filename, ext ) while possible_filename in do_not_use_filenames: possible_filename = '{} ({}){}'.format( filename, i, ext ) i += 1 filename = possible_filename else: filename += ext return filename