Version 71
This commit is contained in:
parent
f8117fe392
commit
bcfce756b7
|
@ -8,6 +8,28 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 71</h3></li>
|
||||
<ul>
|
||||
<li>collapsed the four mappings tables into two tables</li>
|
||||
<li>merged the two active_mappings tables into the mappings table</li>
|
||||
<li>made a _great_ number of changes to how mappings and active_mappings are stored and processed throughout</li>
|
||||
<li>'active' and 'null' nomenclature is now 'combined'; null service_ids are now just ints</li>
|
||||
<li>improved deletepending so it isn't so tough on the a/c cache</li>
|
||||
<li>tags regex dialog entries 'for all files' and 'just for this file' was all broke</li>
|
||||
<li>A/C read now bumps the exact match of the entry to the top of the list, if its count is non-zero</li>
|
||||
<li>fixed A/C for weird-character queries, like '['</li>
|
||||
<li>the dumper now makes success/error noises as appropriate</li>
|
||||
<li>you can turn these noises off in the new sound tab in file->options</li>
|
||||
<li>screwed around with garbage collection while checking mimetypes during pre-import</li>
|
||||
<li>adding a tag parent will spam-add the actual parent tags to every child instance for the appropriate service</li>
|
||||
<li>updated db diagrams</li>
|
||||
<li>revised my sibling chain collapsing algorithm</li>
|
||||
<li>locked db on init dialog message is improved a little</li>
|
||||
<li>system:limit added to combined file service searches</li>
|
||||
<li>num pending menu counts now split into with (pending/petitioned)</li>
|
||||
<li>corrected a server db index oversight</li>
|
||||
<li>newgrounds title tag fixed</li>
|
||||
</ul>
|
||||
<li><h3>version 70</h3></li>
|
||||
<ul>
|
||||
<li>tag parents db stuff</li>
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 193 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 134 KiB |
|
@ -316,14 +316,14 @@ def GetFilePath( hash, mime ):
|
|||
|
||||
return HC.CLIENT_FILES_DIR + os.path.sep + first_two_chars + os.path.sep + hash_encoded + HC.mime_ext_lookup[ mime ]
|
||||
|
||||
def GetMediasTagCount( pool, tag_service_identifier = HC.NULL_SERVICE_IDENTIFIER ):
|
||||
def GetMediasTagCount( pool, tag_service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER ):
|
||||
|
||||
all_tags = []
|
||||
tags_managers = []
|
||||
|
||||
for media in pool:
|
||||
|
||||
if media.IsCollection(): all_tags.extend( media.GetSingletonsTags() )
|
||||
else: all_tags.append( media.GetTags() )
|
||||
if media.IsCollection(): tags_managers.extend( media.GetSingletonsTagsManagers() )
|
||||
else: tags_managers.append( media.GetTagsManager() )
|
||||
|
||||
|
||||
current_tags_to_count = collections.Counter()
|
||||
|
@ -331,15 +331,16 @@ def GetMediasTagCount( pool, tag_service_identifier = HC.NULL_SERVICE_IDENTIFIER
|
|||
pending_tags_to_count = collections.Counter()
|
||||
petitioned_tags_to_count = collections.Counter()
|
||||
|
||||
for tags in all_tags:
|
||||
for tags_manager in tags_managers:
|
||||
|
||||
if tag_service_identifier == HC.NULL_SERVICE_IDENTIFIER: ( current, deleted, pending, petitioned ) = tags.GetUnionCDPP()
|
||||
else: ( current, deleted, pending, petitioned ) = tags.GetCDPP( tag_service_identifier )
|
||||
statuses_to_tags = tags_manager.GetStatusesToTags( tag_service_identifier )
|
||||
|
||||
current_tags_to_count.update( current )
|
||||
deleted_tags_to_count.update( pending )
|
||||
pending_tags_to_count.update( pending )
|
||||
petitioned_tags_to_count.update( petitioned )
|
||||
current_tags_to_count.update( statuses_to_tags[ HC.CURRENT ] )
|
||||
deleted_tags_to_count.update( statuses_to_tags[ HC.DELETED ] )
|
||||
deleted_tags_to_count.update( statuses_to_tags[ HC.DELETED_PENDING ] )
|
||||
pending_tags_to_count.update( statuses_to_tags[ HC.DELETED_PENDING ] )
|
||||
pending_tags_to_count.update( statuses_to_tags[ HC.PENDING ] )
|
||||
petitioned_tags_to_count.update( statuses_to_tags[ HC.PETITIONED ] )
|
||||
|
||||
|
||||
return ( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count )
|
||||
|
@ -380,17 +381,47 @@ def IterateAllThumbnailPaths():
|
|||
for path in next_paths: yield dir + os.path.sep + path
|
||||
|
||||
|
||||
def MediaIntersectCDPPTagServiceIdentifiers( media, service_identifier ):
|
||||
def IntersectTags( tags_managers, service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER ):
|
||||
|
||||
all_tag_cdpps = [ m.GetTags().GetCDPP( service_identifier ) for m in media ]
|
||||
|
||||
current = list( HC.IntelligentMassIntersect( ( cdpp[0] for cdpp in all_tag_cdpps ) ) )
|
||||
deleted = list( HC.IntelligentMassIntersect( ( cdpp[1] for cdpp in all_tag_cdpps ) ) )
|
||||
pending = list( HC.IntelligentMassIntersect( ( cdpp[2] for cdpp in all_tag_cdpps ) ) )
|
||||
petitioned = list( HC.IntelligentMassIntersect( ( cdpp[3] for cdpp in all_tag_cdpps ) ) )
|
||||
current = list( HC.IntelligentMassIntersect( ( tags_manager.GetCurrent( service_identifier ) for tags_manager in tags_managers ) ) )
|
||||
deleted = list( HC.IntelligentMassIntersect( ( tags_manager.GetDeleted( service_identifier ) for tags_manager in tags_managers ) ) )
|
||||
pending = list( HC.IntelligentMassIntersect( ( tags_manager.GetPending( service_identifier ) for tags_manager in tags_managers ) ) )
|
||||
petitioned = list( HC.IntelligentMassIntersect( ( tags_manager.GetPetitioned( service_identifier ) for tags_manager in tags_managers ) ) )
|
||||
|
||||
return ( current, deleted, pending, petitioned )
|
||||
|
||||
def MergeTags( tags_managers ):
|
||||
|
||||
# [[( s_i, s_t_t )]]
|
||||
s_i_s_t_t_tupled = ( tags_manager.GetServiceIdentifiersToStatusesToTags().items() for tags_manager in tags_managers )
|
||||
|
||||
# [(s_i, s_t_t)]
|
||||
flattened_s_i_s_t_t = itertools.chain.from_iterable( s_i_s_t_t_tupled )
|
||||
|
||||
# s_i : [s_t_t]
|
||||
s_i_s_t_t_dict = HC.BuildKeyToListDict( flattened_s_i_s_t_t )
|
||||
|
||||
# now let's merge so we have s_i : s_t_t
|
||||
|
||||
merged_tags = {}
|
||||
|
||||
for ( service_identifier, several_statuses_to_tags ) in s_i_s_t_t_dict.items():
|
||||
|
||||
# [[( status, tags )]]
|
||||
s_t_t_tupled = ( s_t_t.items() for s_t_t in several_statuses_to_tags )
|
||||
|
||||
# [( status, tags )]
|
||||
flattened_s_t_t = itertools.chain.from_iterable( s_t_t_tupled )
|
||||
|
||||
statuses_to_tags = collections.defaultdict( set )
|
||||
|
||||
for ( status, tags ) in flattened_s_t_t: statuses_to_tags[ status ].update( tags )
|
||||
|
||||
merged_tags[ service_identifier ] = statuses_to_tags
|
||||
|
||||
|
||||
return TagsManager( wx.GetApp().Read( 'tag_service_precedence' ), merged_tags )
|
||||
|
||||
def ParseImportablePaths( raw_paths, include_subdirs = True ):
|
||||
|
||||
file_paths = []
|
||||
|
@ -450,6 +481,8 @@ def ParseImportablePaths( raw_paths, include_subdirs = True ):
|
|||
|
||||
progress.Destroy()
|
||||
|
||||
gc.collect()
|
||||
|
||||
good_paths_info = []
|
||||
odd_paths = []
|
||||
|
||||
|
@ -459,6 +492,8 @@ def ParseImportablePaths( raw_paths, include_subdirs = True ):
|
|||
|
||||
for ( i, path ) in enumerate( file_paths ):
|
||||
|
||||
if i % 500 == 0: gc.collect()
|
||||
|
||||
( should_continue, skip ) = progress.Update( i, 'Done ' + str( i ) + '/' + str( num_file_paths ) )
|
||||
|
||||
if not should_continue: break
|
||||
|
@ -860,217 +895,6 @@ class CDPPFileServiceIdentifiers():
|
|||
self._petitioned.discard( service_identifier )
|
||||
|
||||
|
||||
class CDPPTagServiceIdentifiers():
|
||||
|
||||
def __init__( self, tag_service_precedence, service_identifiers_to_cdpp ):
|
||||
|
||||
self._tag_service_precedence = tag_service_precedence
|
||||
|
||||
self._service_identifiers_to_cdpp = service_identifiers_to_cdpp
|
||||
|
||||
self._Recalc()
|
||||
|
||||
|
||||
def _Recalc( self ):
|
||||
|
||||
self._current = set()
|
||||
self._deleted = set()
|
||||
self._pending = set()
|
||||
self._petitioned = set()
|
||||
|
||||
t_s_p = list( self._tag_service_precedence )
|
||||
|
||||
t_s_p.reverse()
|
||||
|
||||
for service_identifier in t_s_p:
|
||||
|
||||
if service_identifier in self._service_identifiers_to_cdpp:
|
||||
|
||||
( current, deleted, pending, petitioned ) = self._service_identifiers_to_cdpp[ service_identifier ]
|
||||
|
||||
# the difference_update stuff is making active_mappings from tag_service_precedence
|
||||
|
||||
self._current.update( current )
|
||||
self._current.difference_update( deleted )
|
||||
|
||||
self._deleted.update( deleted )
|
||||
self._deleted.difference_update( current )
|
||||
|
||||
self._pending.update( pending )
|
||||
self._pending.difference_update( deleted )
|
||||
|
||||
self._petitioned.update( petitioned )
|
||||
self._petitioned.difference_update( current )
|
||||
|
||||
|
||||
|
||||
self._creators = set()
|
||||
self._series = set()
|
||||
self._titles = set()
|
||||
self._volumes = set()
|
||||
self._chapters = set()
|
||||
self._pages = set()
|
||||
|
||||
for tag in self._current | self._pending:
|
||||
|
||||
if tag.startswith( 'creator:' ): self._creators.add( tag.split( 'creator:', 1 )[1] )
|
||||
elif tag.startswith( 'series:' ): self._series.add( tag.split( 'series:', 1 )[1] )
|
||||
elif tag.startswith( 'title:' ): self._titles.add( tag.split( 'title:', 1 )[1] )
|
||||
elif tag.startswith( 'volume:' ): self._volumes.add( int( tag.split( 'volume:', 1 )[1] ) )
|
||||
elif tag.startswith( 'chapter:' ): self._chapters.add( int( tag.split( 'chapter:', 1 )[1] ) )
|
||||
elif tag.startswith( 'page:' ): self._pages.add( int( tag.split( 'page:', 1 )[1] ) )
|
||||
|
||||
|
||||
|
||||
def GetCSTVCP( self ): return ( self._creators, self._series, self._titles, self._volumes, self._chapters, self._pages )
|
||||
|
||||
def DeletePending( self, service_identifier ):
|
||||
|
||||
if service_identifier in self._service_identifiers_to_cdpp:
|
||||
|
||||
( current, deleted, pending, petitioned ) = self._service_identifiers_to_cdpp[ service_identifier ]
|
||||
|
||||
if len( pending ) > 0 or len( petitioned ) > 0:
|
||||
|
||||
self._service_identifiers_to_cdpp[ service_identifier ] = ( current, deleted, set(), set() )
|
||||
|
||||
self._Recalc()
|
||||
|
||||
|
||||
|
||||
|
||||
def GetCDPP( self, service_identifier ):
|
||||
|
||||
if service_identifier in self._service_identifiers_to_cdpp: return self._service_identifiers_to_cdpp[ service_identifier ]
|
||||
else: return ( set(), set(), set(), set() )
|
||||
|
||||
|
||||
def GetNamespaceSlice( self, namespaces ): return frozenset( [ tag for tag in list( self._current ) + list( self._pending ) if True in ( tag.startswith( namespace + ':' ) for namespace in namespaces ) ] )
|
||||
|
||||
def GetNumTags( self, tag_service_identifier, include_current_tags = True, include_pending_tags = False ):
|
||||
|
||||
num_tags = 0
|
||||
|
||||
if tag_service_identifier == HC.NULL_SERVICE_IDENTIFIER:
|
||||
|
||||
if include_current_tags: num_tags += len( self._current )
|
||||
if include_pending_tags: num_tags += len( self._pending )
|
||||
|
||||
else:
|
||||
|
||||
( current, deleted, pending, petitioned ) = self.GetCDPP( tag_service_identifier )
|
||||
|
||||
if include_current_tags: num_tags += len( current )
|
||||
if include_pending_tags: num_tags += len( pending )
|
||||
|
||||
|
||||
return num_tags
|
||||
|
||||
|
||||
def GetServiceIdentifiersToCDPP( self ): return self._service_identifiers_to_cdpp
|
||||
|
||||
def GetUnionCDPP( self ): return ( self._current, self._deleted, self._pending, self._petitioned )
|
||||
|
||||
def HasTag( self, tag ): return tag in self._current or tag in self._pending
|
||||
|
||||
def ProcessContentUpdate( self, content_update ):
|
||||
|
||||
service_identifier = content_update.GetServiceIdentifier()
|
||||
|
||||
if service_identifier in self._service_identifiers_to_cdpp: ( current, deleted, pending, petitioned ) = self._service_identifiers_to_cdpp[ service_identifier ]
|
||||
else:
|
||||
|
||||
( current, deleted, pending, petitioned ) = ( set(), set(), set(), set() )
|
||||
|
||||
self._service_identifiers_to_cdpp[ service_identifier ] = ( current, deleted, pending, petitioned )
|
||||
|
||||
|
||||
action = content_update.GetAction()
|
||||
|
||||
if action == HC.CONTENT_UPDATE_ADD:
|
||||
|
||||
tag = content_update.GetInfo()
|
||||
|
||||
current.add( tag )
|
||||
|
||||
deleted.discard( tag )
|
||||
pending.discard( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_DELETE:
|
||||
|
||||
tag = content_update.GetInfo()
|
||||
|
||||
deleted.add( tag )
|
||||
|
||||
current.discard( tag )
|
||||
petitioned.discard( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_EDIT_LOG:
|
||||
|
||||
edit_log = content_update.GetInfo()
|
||||
|
||||
for ( action, info ) in edit_log:
|
||||
|
||||
if action == HC.CONTENT_UPDATE_ADD:
|
||||
|
||||
tag = info
|
||||
|
||||
current.add( tag )
|
||||
|
||||
deleted.discard( tag )
|
||||
pending.discard( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_DELETE:
|
||||
|
||||
tag = info
|
||||
|
||||
deleted.add( tag )
|
||||
|
||||
current.discard( tag )
|
||||
petitioned.discard( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_PENDING:
|
||||
|
||||
tag = info
|
||||
|
||||
if tag not in current: pending.add( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_RESCIND_PENDING:
|
||||
|
||||
tag = info
|
||||
|
||||
pending.discard( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_PETITION:
|
||||
|
||||
( tag, reason ) = info
|
||||
|
||||
if tag not in deleted: petitioned.add( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_RESCIND_PETITION:
|
||||
|
||||
tag = info
|
||||
|
||||
petitioned.discard( tag )
|
||||
|
||||
|
||||
|
||||
|
||||
self._Recalc()
|
||||
|
||||
|
||||
def ResetService( self, service_identifier ):
|
||||
|
||||
if service_identifier in self._service_identifiers_to_cdpp:
|
||||
|
||||
( current, deleted, pending, petitioned ) = self._service_identifiers_to_cdpp[ service_identifier ]
|
||||
|
||||
self._service_identifiers_to_cdpp[ service_identifier ] = ( set(), set(), pending, set() )
|
||||
|
||||
self._Recalc()
|
||||
|
||||
|
||||
|
||||
class LocalRatings():
|
||||
|
||||
# c for current; feel free to rename this stupid thing
|
||||
|
@ -1482,6 +1306,7 @@ class FileQueryResult():
|
|||
|
||||
HC.pubsub.sub( self, 'ProcessContentUpdates', 'content_updates_data' )
|
||||
HC.pubsub.sub( self, 'ProcessServiceUpdate', 'service_update_data' )
|
||||
HC.pubsub.sub( self, 'RecalcCombinedTags', 'new_tag_service_precedence' )
|
||||
|
||||
|
||||
def __iter__( self ):
|
||||
|
@ -1574,7 +1399,7 @@ class FileQueryResult():
|
|||
|
||||
class FileSearchContext():
|
||||
|
||||
def __init__( self, file_service_identifier = HC.LOCAL_FILE_SERVICE_IDENTIFIER, tag_service_identifier = HC.NULL_SERVICE_IDENTIFIER, include_current_tags = True, include_pending_tags = True, predicates = [] ):
|
||||
def __init__( self, file_service_identifier = HC.LOCAL_FILE_SERVICE_IDENTIFIER, tag_service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER, include_current_tags = True, include_pending_tags = True, predicates = [] ):
|
||||
|
||||
self._file_service_identifier = file_service_identifier
|
||||
self._tag_service_identifier = tag_service_identifier
|
||||
|
@ -2145,7 +1970,7 @@ class MediaResult():
|
|||
|
||||
def __init__( self, tuple ):
|
||||
|
||||
# hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tag_service_identifiers_cdpp, file_service_identifiers_cdpp, local_ratings, remote_ratings
|
||||
# hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, file_service_identifiers_cdpp, local_ratings, remote_ratings
|
||||
|
||||
self._tuple = tuple
|
||||
|
||||
|
@ -2154,9 +1979,9 @@ class MediaResult():
|
|||
|
||||
service_type = service_identifier.GetType()
|
||||
|
||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tag_service_identifiers_cdpp, file_service_identifiers_cdpp, local_ratings, remote_ratings ) = self._tuple
|
||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, file_service_identifiers_cdpp, local_ratings, remote_ratings ) = self._tuple
|
||||
|
||||
if service_type == HC.TAG_REPOSITORY: tag_service_identifiers_cdpp.DeletePending( service_identifier )
|
||||
if service_type == HC.TAG_REPOSITORY: tags_manager.DeletePending( service_identifier )
|
||||
elif service_type in ( HC.FILE_REPOSITORY, HC.LOCAL_FILE ): file_service_identifiers_cdpp.DeletePending( service_identifier )
|
||||
|
||||
|
||||
|
@ -2180,7 +2005,7 @@ class MediaResult():
|
|||
|
||||
def GetSize( self ): return self._tuple[2]
|
||||
|
||||
def GetTags( self ): return self._tuple[10]
|
||||
def GetTagsManager( self ): return self._tuple[10]
|
||||
|
||||
def GetTimestamp( self ): return self._tuple[4]
|
||||
|
||||
|
@ -2188,13 +2013,13 @@ class MediaResult():
|
|||
|
||||
def ProcessContentUpdate( self, content_update ):
|
||||
|
||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tag_service_identifiers_cdpp, file_service_identifiers_cdpp, local_ratings, remote_ratings ) = self._tuple
|
||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, file_service_identifiers_cdpp, local_ratings, remote_ratings ) = self._tuple
|
||||
|
||||
service_identifier = content_update.GetServiceIdentifier()
|
||||
|
||||
service_type = service_identifier.GetType()
|
||||
|
||||
if service_type in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ): tag_service_identifiers_cdpp.ProcessContentUpdate( content_update )
|
||||
if service_type in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ): tags_manager.ProcessContentUpdate( content_update )
|
||||
elif service_type in ( HC.FILE_REPOSITORY, HC.LOCAL_FILE ):
|
||||
|
||||
if service_type == HC.LOCAL_FILE:
|
||||
|
@ -2206,7 +2031,7 @@ class MediaResult():
|
|||
elif action == HC.CONTENT_UPDATE_INBOX: inbox = True
|
||||
elif action == HC.CONTENT_UPDATE_DELETE: inbox = False
|
||||
|
||||
self._tuple = ( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tag_service_identifiers_cdpp, file_service_identifiers_cdpp, local_ratings, remote_ratings )
|
||||
self._tuple = ( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, file_service_identifiers_cdpp, local_ratings, remote_ratings )
|
||||
|
||||
|
||||
file_service_identifiers_cdpp.ProcessContentUpdate( content_update )
|
||||
|
@ -2222,9 +2047,9 @@ class MediaResult():
|
|||
|
||||
service_type = service_identifier.GetType()
|
||||
|
||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tag_service_identifiers_cdpp, file_service_identifiers_cdpp, local_ratings, remote_ratings ) = self._tuple
|
||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, file_service_identifiers_cdpp, local_ratings, remote_ratings ) = self._tuple
|
||||
|
||||
if service_type == HC.TAG_REPOSITORY: tag_service_identifiers_cdpp.ResetService( service_identifier )
|
||||
if service_type in ( HC.TAG_REPOSITORY, HC.COMBINED_TAG ): tags_manager.ResetService( service_identifier )
|
||||
elif service_type == HC.FILE_REPOSITORY: file_service_identifiers_cdpp.ResetService( service_identifier )
|
||||
|
||||
|
||||
|
@ -2644,6 +2469,324 @@ class ServiceRemoteRestrictedDepotMessage( ServiceRemoteRestrictedDepot ):
|
|||
|
||||
def Decrypt( self, encrypted_message ): return HydrusMessageHandling.UnpackageDeliveredMessage( encrypted_message, self._private_key )
|
||||
|
||||
class TagsManager():
|
||||
|
||||
def __init__( self, tag_service_precedence, service_identifiers_to_statuses_to_tags ):
|
||||
|
||||
self._tag_service_precedence = tag_service_precedence
|
||||
|
||||
self._service_identifiers_to_statuses_to_tags = service_identifiers_to_statuses_to_tags
|
||||
|
||||
self._cstvcp_initialised = False
|
||||
|
||||
|
||||
def _RecalcCombined( self ):
|
||||
|
||||
t_s_p = list( self._tag_service_precedence )
|
||||
|
||||
t_s_p.reverse()
|
||||
|
||||
combined_current = set()
|
||||
combined_pending = set()
|
||||
|
||||
for service_identifier in t_s_p:
|
||||
|
||||
if service_identifier in self._service_identifiers_to_statuses_to_tags:
|
||||
|
||||
statuses_to_tags = self._service_identifiers_to_statuses_to_tags[ service_identifier ]
|
||||
|
||||
combined_current.update( statuses_to_tags[ HC.CURRENT ] )
|
||||
combined_current.difference_update( statuses_to_tags[ HC.DELETED ] )
|
||||
combined_current.difference_update( statuses_to_tags[ HC.DELETED_PENDING ] )
|
||||
|
||||
combined_pending.update( statuses_to_tags[ HC.DELETED_PENDING ] )
|
||||
combined_pending.update( statuses_to_tags[ HC.PENDING ] )
|
||||
|
||||
|
||||
|
||||
combined_statuses_to_tags = collections.defaultdict( set )
|
||||
|
||||
combined_statuses_to_tags[ HC.CURRENT ] = combined_current
|
||||
combined_statuses_to_tags[ HC.PENDING ] = combined_pending
|
||||
|
||||
self._service_identifiers_to_statuses_to_tags[ HC.COMBINED_TAG_SERVICE_IDENTIFIER ] = combined_statuses_to_tags
|
||||
|
||||
if self._cstvcp_initialised: self._RecalcCSTVCP()
|
||||
|
||||
|
||||
def _RecalcCSTVCP( self ):
|
||||
|
||||
self._creators = set()
|
||||
self._series = set()
|
||||
self._titles = set()
|
||||
self._volumes = set()
|
||||
self._chapters = set()
|
||||
self._pages = set()
|
||||
|
||||
if HC.COMBINED_TAG_SERVICE_IDENTIFIER in self._service_identifiers_to_statuses_to_tags:
|
||||
|
||||
combined_statuses_to_tags = self._service_identifiers_to_statuses_to_tags[ HC.COMBINED_TAG_SERVICE_IDENTIFIER ]
|
||||
|
||||
combined_current = combined_statuses_to_tags[ HC.CURRENT ]
|
||||
combined_pending = combined_statuses_to_tags[ HC.PENDING ]
|
||||
|
||||
for tag in combined_current.union( combined_pending ):
|
||||
|
||||
if ':' in tag:
|
||||
|
||||
( namespace, tag ) = tag.split( ':', 1 )
|
||||
|
||||
if namespace == 'creator': self._creators.add( tag )
|
||||
elif namespace == 'series': self._series.add( tag )
|
||||
elif namespace == 'title': self._titles.add( tag )
|
||||
elif namespace in ( 'volume', 'chapter', 'page' ):
|
||||
|
||||
try: tag = int( tag )
|
||||
except: pass
|
||||
|
||||
if namespace == 'volume': self._volumes.add( tag )
|
||||
elif namespace == 'chapter': self._chapters.add( tag )
|
||||
elif namespace == 'page': self._pages.add( tag )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
self._cstvcp_initialised = True
|
||||
|
||||
|
||||
def GetCSTVCP( self ):
|
||||
|
||||
if not self._cstvcp_initialised: self._RecalcCSTVCP()
|
||||
|
||||
return ( self._creators, self._series, self._titles, self._volumes, self._chapters, self._pages )
|
||||
|
||||
|
||||
def DeletePending( self, service_identifier ):
|
||||
|
||||
if service_identifier in self._service_identifiers_to_statuses_to_tags:
|
||||
|
||||
statuses_to_tags = self._service_identifiers_to_statuses_to_tags[ service_identifier ]
|
||||
|
||||
if len( statuses_to_tags[ HC.PENDING ] ) + len( statuses_to_tags[ HC.DELETED_PENDING ] ) + len( statuses_to_tags[ HC.PETITIONED ] ) > 0:
|
||||
|
||||
statuses_to_tags[ HC.DELETED ].update( statuses_to_tags[ HC.DELETED_PENDING ] )
|
||||
|
||||
statuses_to_tags[ HC.PENDING ] = set()
|
||||
statuses_to_tags[ HC.DELETED_PENDING ] = set()
|
||||
statuses_to_tags[ HC.PETITIONED ] = set()
|
||||
|
||||
self._RecalcCombined()
|
||||
|
||||
|
||||
|
||||
|
||||
def GetCurrent( self, service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER ):
|
||||
|
||||
if service_identifier in self._service_identifiers_to_statuses_to_tags:
|
||||
|
||||
statuses_to_tags = self._service_identifiers_to_statuses_to_tags[ service_identifier ]
|
||||
|
||||
return set( statuses_to_tags[ HC.CURRENT ] )
|
||||
|
||||
else: return set()
|
||||
|
||||
|
||||
def GetDeleted( self, service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER ):
|
||||
|
||||
if service_identifier in self._service_identifiers_to_statuses_to_tags:
|
||||
|
||||
statuses_to_tags = self._service_identifiers_to_statuses_to_tags[ service_identifier ]
|
||||
|
||||
return statuses_to_tags[ HC.DELETED ].union( statuses_to_tags[ HC.DELETED_PENDING ] )
|
||||
|
||||
else: return set()
|
||||
|
||||
|
||||
def GetNamespaceSlice( self, namespaces ):
|
||||
|
||||
if HC.COMBINED_TAG_SERVICE_IDENTIFIER in self._service_identifiers_to_statuses_to_tags:
|
||||
|
||||
combined_statuses_to_tags = self._service_identifiers_to_statuses_to_tags[ HC.COMBINED_TAG_SERVICE_IDENTIFIER ]
|
||||
|
||||
combined_current = combined_statuses_to_tags[ HC.CURRENT ]
|
||||
combined_pending = combined_statuses_to_tags[ HC.PENDING ]
|
||||
|
||||
return frozenset( [ tag for tag in list( combined_current ) + list( combined_pending ) if True in ( tag.startswith( namespace + ':' ) for namespace in namespaces ) ] )
|
||||
|
||||
else: return frozenset()
|
||||
|
||||
|
||||
def GetNumTags( self, tag_service_identifier, include_current_tags = True, include_pending_tags = False ):
|
||||
|
||||
num_tags = 0
|
||||
|
||||
statuses_to_tags = self.GetStatusesToTags( tag_service_identifier )
|
||||
|
||||
if include_current_tags: num_tags += len( statuses_to_tags[ HC.CURRENT ] )
|
||||
if include_pending_tags: num_tags += len( statuses_to_tags[ HC.DELETED_PENDING ] ) + len( statuses_to_tags[ HC.PENDING ] )
|
||||
|
||||
return num_tags
|
||||
|
||||
|
||||
def GetPending( self, service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER ):
|
||||
|
||||
if service_identifier in self._service_identifiers_to_statuses_to_tags:
|
||||
|
||||
statuses_to_tags = self._service_identifiers_to_statuses_to_tags[ service_identifier ]
|
||||
|
||||
return statuses_to_tags[ HC.DELETED_PENDING ].union( statuses_to_tags[ HC.PENDING ] )
|
||||
|
||||
else: return set()
|
||||
|
||||
|
||||
def GetPetitioned( self, service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER ):
|
||||
|
||||
if service_identifier in self._service_identifiers_to_statuses_to_tags:
|
||||
|
||||
statuses_to_tags = self._service_identifiers_to_statuses_to_tags[ service_identifier ]
|
||||
|
||||
return set( statuses_to_tags[ HC.PETITIONED ] )
|
||||
|
||||
else: return set()
|
||||
|
||||
|
||||
def GetServiceIdentifiersToStatusesToTags( self ): return self._service_identifiers_to_statuses_to_tags
|
||||
|
||||
def GetStatusesToTags( self, service_identifier ):
|
||||
|
||||
if service_identifier in self._service_identifiers_to_statuses_to_tags: return self._service_identifiers_to_statuses_to_tags[ service_identifier ]
|
||||
else: return collections.defaultdict( set )
|
||||
|
||||
|
||||
def HasTag( self, tag ):
|
||||
|
||||
if HC.COMBINED_TAG_SERVICE_IDENTIFIER in self._service_identifiers_to_statuses_to_tags:
|
||||
|
||||
combined_statuses_to_tags = self._service_identifiers_to_statuses_to_tags[ HC.COMBINED_TAG_SERVICE_IDENTIFIER ]
|
||||
|
||||
return tag in combined_statuses_to_tags[ HC.CURRENT ] or tag in combined_statuses_to_tags[ HC.PENDING ]
|
||||
|
||||
else: return False
|
||||
|
||||
|
||||
def ProcessContentUpdate( self, content_update ):
|
||||
|
||||
service_identifier = content_update.GetServiceIdentifier()
|
||||
|
||||
if service_identifier not in self._service_identifiers_to_statuses_to_tags: self._service_identifiers_to_statuses_to_tags[ service_identifier ] = collections.defaultdict( set )
|
||||
|
||||
statuses_to_tags = self._service_identifiers_to_statuses_to_tags[ service_identifier ]
|
||||
|
||||
action = content_update.GetAction()
|
||||
|
||||
if action == HC.CONTENT_UPDATE_ADD:
|
||||
|
||||
tag = content_update.GetInfo()
|
||||
|
||||
statuses_to_tags[ HC.CURRENT ].add( tag )
|
||||
|
||||
statuses_to_tags[ HC.DELETED ].discard( tag )
|
||||
statuses_to_tags[ HC.DELETED_PENDING ].discard( tag )
|
||||
statuses_to_tags[ HC.PENDING ].discard( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_DELETE:
|
||||
|
||||
tag = content_update.GetInfo()
|
||||
|
||||
if tag in statuses_to_tags[ HC.PENDING ]:
|
||||
|
||||
statuses_to_tags[ HC.DELETED_PENDING ].add( tag )
|
||||
statuses_to_tags[ HC.PENDING ].discard( tag )
|
||||
|
||||
else:
|
||||
|
||||
statuses_to_tags[ HC.DELETED ].add( tag )
|
||||
statuses_to_tags[ HC.CURRENT ].discard( tag )
|
||||
|
||||
|
||||
statuses_to_tags[ HC.PETITIONED ].discard( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_EDIT_LOG:
|
||||
|
||||
edit_log = content_update.GetInfo()
|
||||
|
||||
for ( action, info ) in edit_log:
|
||||
|
||||
if action == HC.CONTENT_UPDATE_ADD:
|
||||
|
||||
tag = info
|
||||
|
||||
statuses_to_tags[ HC.CURRENT ].add( tag )
|
||||
|
||||
statuses_to_tags[ HC.DELETED ].discard( tag )
|
||||
statuses_to_tags[ HC.DELETED_PENDING ].discard( tag )
|
||||
statuses_to_tags[ HC.PENDING ].discard( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_DELETE:
|
||||
|
||||
tag = info
|
||||
|
||||
if tag in statuses_to_tags[ HC.PENDING ]:
|
||||
|
||||
statuses_to_tags[ HC.DELETED_PENDING ].add( tag )
|
||||
statuses_to_tags[ HC.PENDING ].discard( tag )
|
||||
|
||||
else:
|
||||
|
||||
statuses_to_tags[ HC.DELETED ].add( tag )
|
||||
statuses_to_tags[ HC.CURRENT ].discard( tag )
|
||||
|
||||
|
||||
statuses_to_tags[ HC.PETITIONED ].discard( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_PENDING:
|
||||
|
||||
tag = info
|
||||
|
||||
if tag in statuses_to_tags[ HC.DELETED ]:
|
||||
|
||||
statuses_to_tags[ HC.DELETED_PENDING ].add( tag )
|
||||
statuses_to_tags[ HC.DELETED ].discard( tag )
|
||||
|
||||
else: statuses_to_tags[ HC.PENDING ].add( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_RESCIND_PENDING:
|
||||
|
||||
tag = info
|
||||
|
||||
if tag in statuses_to_tags[ HC.DELETED_PENDING ]:
|
||||
|
||||
statuses_to_tags[ HC.DELETED ].add( tag )
|
||||
statuses_to_tags[ HC.DELETED_PENDING ].discard( tag )
|
||||
|
||||
else: statuses_to_tags[ HC.PENDING ].discard( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_PETITION:
|
||||
|
||||
( tag, reason ) = info
|
||||
|
||||
statuses_to_tags[ HC.PETITIONED ].add( tag )
|
||||
|
||||
elif action == HC.CONTENT_UPDATE_RESCIND_PETITION:
|
||||
|
||||
tag = info
|
||||
|
||||
statuses_to_tags[ HC.PETITIONED ].discard( tag )
|
||||
|
||||
|
||||
|
||||
|
||||
self._RecalcCombined()
|
||||
|
||||
|
||||
def ResetService( self, service_identifier ):
|
||||
|
||||
self._service_identifiers_to_statuses_to_tags[ service_identifier ] = collections.defaultdict[ set ]
|
||||
|
||||
self._RecalcCombined()
|
||||
|
||||
|
||||
class TagParentsManager():
|
||||
|
||||
def __init__( self ):
|
||||
|
@ -2657,7 +2800,7 @@ class TagParentsManager():
|
|||
|
||||
def _GetParents( self, service_identifier, tag ):
|
||||
|
||||
if service_identifier == HC.NULL_SERVICE_IDENTIFIER: return []
|
||||
if service_identifier == HC.COMBINED_TAG_SERVICE_IDENTIFIER: return []
|
||||
|
||||
if tag in self._parents[ service_identifier ]: still_to_process = list( self._parents[ service_identifier ][ tag ] )
|
||||
else: still_to_process = []
|
||||
|
@ -2697,7 +2840,7 @@ class TagParentsManager():
|
|||
|
||||
def ExpandPredicates( self, service_identifier, predicates ):
|
||||
|
||||
if service_identifier == HC.NULL_SERVICE_IDENTIFIER: return predicates
|
||||
if service_identifier == HC.COMBINED_TAG_SERVICE_IDENTIFIER: return predicates
|
||||
|
||||
results = []
|
||||
|
||||
|
@ -2757,13 +2900,44 @@ class TagSiblingsManager():
|
|||
|
||||
t_s_p = list( self._tag_service_precedence )
|
||||
|
||||
t_s_p.reverse()
|
||||
|
||||
processed_siblings = {}
|
||||
|
||||
# first combine the services
|
||||
# go from high precedence to low, writing A -> B
|
||||
# if A map already exists, don't overwrite
|
||||
# if A -> B forms a loop, don't write it
|
||||
|
||||
for service_identifier in t_s_p:
|
||||
|
||||
if service_identifier in unprocessed_siblings: processed_siblings.update( unprocessed_siblings[ service_identifier ] )
|
||||
if service_identifier in unprocessed_siblings:
|
||||
|
||||
some_siblings = unprocessed_siblings[ service_identifier ]
|
||||
|
||||
for ( old, new ) in some_siblings:
|
||||
|
||||
if old not in processed_siblings:
|
||||
|
||||
next_new = new
|
||||
|
||||
we_have_a_loop = False
|
||||
|
||||
while next_new in processed_siblings:
|
||||
|
||||
next_new = processed_siblings[ next_new ]
|
||||
|
||||
if next_new == old:
|
||||
|
||||
we_have_a_loop = True
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
if not we_have_a_loop: processed_siblings[ old ] = new
|
||||
|
||||
|
||||
|
||||
processed_siblings.update( unprocessed_siblings[ service_identifier ] )
|
||||
|
||||
|
||||
# now to collapse chains
|
||||
|
@ -2775,22 +2949,17 @@ class TagSiblingsManager():
|
|||
|
||||
# adding A -> B
|
||||
|
||||
if new_tag in self._siblings: # B -> C already added, so add A -> C
|
||||
if new_tag in self._siblings:
|
||||
|
||||
new_tag = self._siblings[ new_tag ]
|
||||
# B -> F already calculated and added, so add A -> F
|
||||
|
||||
self._siblings[ old_tag ] = new_tag
|
||||
self._siblings[ old_tag ] = self._siblings[ new_tag ]
|
||||
|
||||
else:
|
||||
|
||||
while new_tag in processed_siblings: # while B -> C, C -> D, D -> E in preprocessed list, pursue endpoint F
|
||||
|
||||
new_tag = processed_siblings[ new_tag ]
|
||||
|
||||
if new_tag == old_tag: break # we have a loop!
|
||||
|
||||
while new_tag in processed_siblings: new_tag = processed_siblings[ new_tag ] # pursue endpoint F
|
||||
|
||||
if old_tag != new_tag: self._siblings[ old_tag ] = new_tag
|
||||
self._siblings[ old_tag ] = new_tag
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import HydrusSessions
|
|||
import ClientConstants as CC
|
||||
import ClientDB
|
||||
import ClientGUI
|
||||
import ClientGUIDialogs
|
||||
import os
|
||||
import sqlite3
|
||||
import threading
|
||||
|
@ -157,6 +158,8 @@ class Controller( wx.App ):
|
|||
|
||||
try:
|
||||
|
||||
self._options = {} # this is for the db locked dialog
|
||||
|
||||
self._splash = ClientGUI.FrameSplash()
|
||||
|
||||
self.SetSplashText( 'log' )
|
||||
|
@ -165,7 +168,31 @@ class Controller( wx.App ):
|
|||
|
||||
self.SetSplashText( 'db' )
|
||||
|
||||
self._db = ClientDB.DB()
|
||||
db_initialised = False
|
||||
|
||||
while not db_initialised:
|
||||
|
||||
try:
|
||||
|
||||
self._db = ClientDB.DB()
|
||||
|
||||
db_initialised = True
|
||||
|
||||
except HC.DBAccessException as e:
|
||||
|
||||
print( unicode( e ) )
|
||||
|
||||
message = 'This instance of the client had a problem connecting to the database, which probably means an old instance is still closing.'
|
||||
message += os.linesep + os.linesep
|
||||
message += 'If the old instance does not close for a very long time, you can usually safely force-close it from task manager.'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( None, message, yes_label = 'wait a bit, then try again', no_label = 'quit now' ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES: time.sleep( 3 )
|
||||
else: return False
|
||||
|
||||
|
||||
|
||||
|
||||
self._options = self._db.Read( 'options', HC.HIGH_PRIORITY )
|
||||
|
||||
|
@ -217,11 +244,13 @@ class Controller( wx.App ):
|
|||
self._maintenance_event_timer = wx.Timer( self, ID_MAINTENANCE_EVENT_TIMER )
|
||||
self._maintenance_event_timer.Start( 20 * 60000, wx.TIMER_CONTINUOUS )
|
||||
|
||||
except sqlite3.OperationalError:
|
||||
|
||||
message = 'This instance of the client had a problem connecting to the database, which probably means an old instance is still closing.'
|
||||
except sqlite3.OperationalError as e:
|
||||
print( traceback.format_exc() )
|
||||
message = 'Database error!'
|
||||
message += os.linesep + os.linesep
|
||||
message += 'This instance will now close. You can either wait a while and try again, or force-close the old instance through task manager and try again immediately.'
|
||||
message += unicode( e )
|
||||
|
||||
print message
|
||||
|
||||
wx.MessageBox( message )
|
||||
|
||||
|
@ -274,7 +303,7 @@ class Controller( wx.App ):
|
|||
|
||||
if HC.shutdown: raise Exception( 'Client shutting down!' )
|
||||
elif pubsubs_queue.qsize() == 0: return
|
||||
else: time.sleep( 0.04 )
|
||||
else: time.sleep( 0.0001 )
|
||||
|
||||
|
||||
|
||||
|
|
1548
include/ClientDB.py
1548
include/ClientDB.py
File diff suppressed because it is too large
Load Diff
|
@ -1255,8 +1255,6 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
server_admin_identifiers = [ service.GetServiceIdentifier() for service in servers_admin if service.GetAccount().HasPermission( HC.GENERAL_ADMIN ) ]
|
||||
|
||||
nums_pending = wx.GetApp().Read( 'nums_pending' )
|
||||
|
||||
menu = wx.MenuBar()
|
||||
|
||||
file = wx.Menu()
|
||||
|
@ -1307,30 +1305,43 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
menu.Append( database, p( '&Database' ) )
|
||||
|
||||
if len( nums_pending ) > 0:
|
||||
nums_pending = wx.GetApp().Read( 'nums_pending' )
|
||||
|
||||
pending = wx.Menu()
|
||||
|
||||
total_num_pending = 0
|
||||
|
||||
for ( service_identifier, info ) in nums_pending.items():
|
||||
|
||||
pending = wx.Menu()
|
||||
service_type = service_identifier.GetType()
|
||||
|
||||
for ( service_identifier, num_pending ) in nums_pending.items():
|
||||
if service_type == HC.TAG_REPOSITORY:
|
||||
|
||||
if num_pending > 0:
|
||||
|
||||
service_type = service_identifier.GetType()
|
||||
|
||||
submenu = wx.Menu()
|
||||
|
||||
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'upload_pending', service_identifier ), p( '&Upload' ), p( 'Upload ' + service_identifier.GetName() + '\'s Pending and Petitions.' ) )
|
||||
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete_pending', service_identifier ), p( '&Forget' ), p( 'Clear ' + service_identifier.GetName() + '\'s Pending and Petitions.' ) )
|
||||
|
||||
pending.AppendMenu( CC.ID_NULL, p( service_identifier.GetName() + ' Pending (' + HC.ConvertIntToPrettyString( num_pending ) + ')' ), submenu )
|
||||
|
||||
num_pending = info[ HC.SERVICE_INFO_NUM_PENDING_MAPPINGS ]
|
||||
num_petitioned = info[ HC.SERVICE_INFO_NUM_PETITIONED_MAPPINGS ]
|
||||
|
||||
elif service_type == HC.FILE_REPOSITORY:
|
||||
|
||||
num_pending = info[ HC.SERVICE_INFO_NUM_PENDING_FILES ]
|
||||
num_petitioned = info[ HC.SERVICE_INFO_NUM_PETITIONED_FILES ]
|
||||
|
||||
|
||||
num_pending_total = sum( nums_pending.values() )
|
||||
if num_pending + num_petitioned > 0:
|
||||
|
||||
submenu = wx.Menu()
|
||||
|
||||
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'upload_pending', service_identifier ), p( '&Upload' ), p( 'Upload ' + service_identifier.GetName() + '\'s Pending and Petitions.' ) )
|
||||
submenu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete_pending', service_identifier ), p( '&Forget' ), p( 'Clear ' + service_identifier.GetName() + '\'s Pending and Petitions.' ) )
|
||||
|
||||
pending.AppendMenu( CC.ID_NULL, p( service_identifier.GetName() + ' Pending (' + HC.ConvertIntToPrettyString( num_pending ) + '/' + HC.ConvertIntToPrettyString( num_petitioned ) + ')' ), submenu )
|
||||
|
||||
|
||||
menu.Append( pending, p( '&Pending (' + HC.ConvertIntToPrettyString( num_pending_total ) + ')' ) )
|
||||
total_num_pending += num_pending + num_petitioned
|
||||
|
||||
|
||||
if total_num_pending > 0: menu.Append( pending, p( '&Pending (' + HC.ConvertIntToPrettyString( total_num_pending ) + ')' ) )
|
||||
else: pending.Destroy()
|
||||
|
||||
services = wx.Menu()
|
||||
|
||||
submenu = wx.Menu()
|
||||
|
|
|
@ -100,11 +100,12 @@ class Canvas():
|
|||
|
||||
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
|
||||
|
||||
tags_cdpp = self._current_media.GetTags()
|
||||
tags_manager = self._current_media.GetTagsManager()
|
||||
|
||||
( current, deleted, pending, petitioned ) = tags_cdpp.GetUnionCDPP()
|
||||
current = tags_manager.GetCurrent()
|
||||
pending = tags_manager.GetPending()
|
||||
|
||||
tags_i_want_to_display = list( current.union( pending ).union( petitioned ) )
|
||||
tags_i_want_to_display = list( current.union( pending ) )
|
||||
|
||||
tags_i_want_to_display.sort()
|
||||
|
||||
|
@ -116,7 +117,6 @@ class Canvas():
|
|||
|
||||
if tag in current: display_string = tag
|
||||
elif tag in pending: display_string = '(+) ' + tag
|
||||
elif tag in petitioned: display_string = '(-) ' + tag
|
||||
|
||||
if ':' in tag:
|
||||
|
||||
|
@ -568,7 +568,7 @@ class CanvasFullscreenMediaList( ClientGUIMixins.ListeningMediaList, Canvas, Cli
|
|||
|
||||
collections_string = ''
|
||||
|
||||
( creators, series, titles, volumes, chapters, pages ) = self._current_media.GetTags().GetCSTVCP()
|
||||
( creators, series, titles, volumes, chapters, pages ) = self._current_media.GetTagsManager().GetCSTVCP()
|
||||
|
||||
if len( creators ) > 0:
|
||||
|
||||
|
@ -1340,9 +1340,11 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaList ):
|
|||
|
||||
if service_type in ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ):
|
||||
|
||||
tags = self._current_media.GetTags()
|
||||
tags_manager = self._current_media.GetTagsManager()
|
||||
|
||||
( current, deleted, pending, petitioned ) = tags.GetCDPP( service_identifier )
|
||||
current = tags_manager.GetCurrent()
|
||||
pending = tags_manager.GetPending()
|
||||
petitioned = tags_manager.GetPetitioned()
|
||||
|
||||
if service_type == HC.LOCAL_TAG:
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import collections
|
|||
import HydrusConstants as HC
|
||||
import ClientConstants as CC
|
||||
import ClientGUIMixins
|
||||
import itertools
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
|
@ -380,14 +381,12 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
self._file_service_identifier = file_service_identifier
|
||||
self._tag_service_identifier = tag_service_identifier
|
||||
|
||||
if self._file_service_identifier == HC.NULL_SERVICE_IDENTIFIER: name = 'all known files'
|
||||
else: name = self._file_service_identifier.GetName()
|
||||
name = self._file_service_identifier.GetName()
|
||||
|
||||
self._file_repo_button = wx.Button( self._dropdown_window, label = name )
|
||||
self._file_repo_button.Bind( wx.EVT_BUTTON, self.EventFileButton )
|
||||
|
||||
if self._tag_service_identifier == HC.NULL_SERVICE_IDENTIFIER: name = 'all known tags'
|
||||
else: name = self._tag_service_identifier.GetName()
|
||||
name = self._tag_service_identifier.GetName()
|
||||
|
||||
self._tag_repo_button = wx.Button( self._dropdown_window, label = name )
|
||||
self._tag_repo_button.Bind( wx.EVT_BUTTON, self.EventTagButton )
|
||||
|
@ -410,13 +409,13 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
def EventFileButton( self, event ):
|
||||
|
||||
service_identifiers = wx.GetApp().Read( 'service_identifiers', ( HC.FILE_REPOSITORY, ) )
|
||||
service_identifiers = []
|
||||
service_identifiers.append( HC.COMBINED_FILE_SERVICE_IDENTIFIER )
|
||||
service_identifiers.append( HC.LOCAL_FILE_SERVICE_IDENTIFIER )
|
||||
service_identifiers.extend( wx.GetApp().Read( 'service_identifiers', ( HC.FILE_REPOSITORY, ) ) )
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_file_repository', HC.NULL_SERVICE_IDENTIFIER ), 'all known files' )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_file_repository', HC.LOCAL_FILE_SERVICE_IDENTIFIER ), 'local files' )
|
||||
|
||||
for service_identifier in service_identifiers: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_file_repository', service_identifier ), service_identifier.GetName() )
|
||||
|
||||
self.PopupMenu( menu )
|
||||
|
@ -440,8 +439,7 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
self._file_service_identifier = service_identifier
|
||||
|
||||
if service_identifier == HC.NULL_SERVICE_IDENTIFIER: name = 'all known files'
|
||||
else: name = service_identifier.GetName()
|
||||
name = service_identifier.GetName()
|
||||
|
||||
self._file_repo_button.SetLabel( name )
|
||||
|
||||
|
@ -453,8 +451,7 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
self._tag_service_identifier = service_identifier
|
||||
|
||||
if service_identifier == HC.NULL_SERVICE_IDENTIFIER: name = 'all known tags'
|
||||
else: name = service_identifier.GetName()
|
||||
name = service_identifier.GetName()
|
||||
|
||||
self._tag_repo_button.SetLabel( name )
|
||||
|
||||
|
@ -482,13 +479,13 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
def EventTagButton( self, event ):
|
||||
|
||||
service_identifiers = wx.GetApp().Read( 'service_identifiers', ( HC.TAG_REPOSITORY, ) )
|
||||
service_identifiers = []
|
||||
service_identifiers.append( HC.COMBINED_TAG_SERVICE_IDENTIFIER )
|
||||
service_identifiers.append( HC.LOCAL_TAG_SERVICE_IDENTIFIER )
|
||||
service_identifiers.extend( wx.GetApp().Read( 'service_identifiers', ( HC.TAG_REPOSITORY, ) ) )
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_tag_repository', HC.NULL_SERVICE_IDENTIFIER ), 'all known tags' )
|
||||
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_tag_repository', HC.LOCAL_TAG_SERVICE_IDENTIFIER ), 'local tags' )
|
||||
|
||||
for service_identifier in service_identifiers: menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'change_tag_repository', service_identifier ), service_identifier.GetName() )
|
||||
|
||||
self.PopupMenu( menu )
|
||||
|
@ -569,7 +566,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
self._first_letters = ''
|
||||
self._current_namespace = ''
|
||||
|
||||
if self._file_service_identifier == HC.NULL_SERVICE_IDENTIFIER: s_i = self._tag_service_identifier
|
||||
if self._file_service_identifier == HC.COMBINED_FILE_SERVICE_IDENTIFIER: s_i = self._tag_service_identifier
|
||||
else: s_i = self._file_service_identifier
|
||||
|
||||
matches = wx.GetApp().Read( 'file_system_predicates', s_i )
|
||||
|
@ -607,32 +604,26 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
if media is None: self._cached_results = wx.GetApp().Read( 'autocomplete_tags', file_service_identifier = self._file_service_identifier, tag_service_identifier = self._tag_service_identifier, half_complete_tag = search_text, include_current = self._include_current, include_pending = self._include_pending )
|
||||
else:
|
||||
|
||||
all_tags = []
|
||||
tags_managers = []
|
||||
|
||||
for m in media:
|
||||
|
||||
if m.IsCollection(): all_tags.extend( m.GetSingletonsTags() )
|
||||
else: all_tags.append( m.GetTags() )
|
||||
if m.IsCollection(): tags_managers.extend( m.GetSingletonsTagsManagers() )
|
||||
else: tags_managers.append( m.GetTagsManager() )
|
||||
|
||||
|
||||
absolutely_all_tags = []
|
||||
lists_of_tags = []
|
||||
|
||||
if self._tag_service_identifier == HC.NULL_SERVICE_IDENTIFIER:
|
||||
|
||||
if self._include_current: absolutely_all_tags += [ list( current ) for ( current, deleted, pending, petitioned ) in [ tags.GetUnionCDPP() for tags in all_tags ] ]
|
||||
if self._include_pending: absolutely_all_tags += [ list( pending ) for ( current, deleted, pending, petitioned ) in [ tags.GetUnionCDPP() for tags in all_tags ] ]
|
||||
|
||||
else:
|
||||
|
||||
if self._include_current: absolutely_all_tags += [ list( current ) for ( current, deleted, pending, petitioned ) in [ tags.GetCDPP( self._tag_service_identifier ) for tags in all_tags ] ]
|
||||
if self._include_pending: absolutely_all_tags += [ list( pending ) for ( current, deleted, pending, petitioned ) in [ tags.GetCDPP( self._tag_service_identifier ) for tags in all_tags ] ]
|
||||
|
||||
if self._include_current: lists_of_tags += [ list( tags_manager.GetCurrent( self._tag_service_identifier ) ) for tags_manager in tags_managers ]
|
||||
if self._include_pending: lists_of_tags += [ list( tags_manager.GetPending( self._tag_service_identifier ) ) for tags_manager in tags_managers ]
|
||||
|
||||
absolutely_all_tags_flat = [ tag for tags in absolutely_all_tags for tag in tags if HC.SearchEntryMatchesTag( half_complete_tag, tag ) ]
|
||||
all_tags_flat_iterable = itertools.chain.from_iterable( lists_of_tags )
|
||||
|
||||
if self._current_namespace != '': absolutely_all_tags_flat = [ tag for tag in absolutely_all_tags_flat if tag.startswith( self._current_namespace + ':' ) ]
|
||||
all_tags_flat = [ tag for tag in all_tags_flat_iterable if HC.SearchEntryMatchesTag( half_complete_tag, tag ) ]
|
||||
|
||||
tags_to_count = collections.Counter( absolutely_all_tags_flat )
|
||||
if self._current_namespace != '': all_tags_flat = [ tag for tag in all_tags_flat if tag.startswith( self._current_namespace + ':' ) ]
|
||||
|
||||
tags_to_count = collections.Counter( all_tags_flat )
|
||||
|
||||
self._cached_results = CC.AutocompleteMatchesPredicates( self._tag_service_identifier, [ HC.Predicate( HC.PREDICATE_TYPE_TAG, ( operator, tag ), count ) for ( tag, count ) in tags_to_count.items() ] )
|
||||
|
||||
|
@ -644,6 +635,20 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
if self._current_namespace != '': matches.insert( 0, HC.Predicate( HC.PREDICATE_TYPE_NAMESPACE, ( operator, namespace ), None ) )
|
||||
|
||||
entry_predicate = HC.Predicate( HC.PREDICATE_TYPE_TAG, ( operator, search_text ), None )
|
||||
|
||||
try:
|
||||
|
||||
index = matches.index( entry_predicate )
|
||||
|
||||
predicate = matches[ index ]
|
||||
|
||||
del matches[ index ]
|
||||
|
||||
matches.insert( 0, predicate )
|
||||
|
||||
except: pass
|
||||
|
||||
|
||||
for match in matches:
|
||||
|
||||
|
@ -684,7 +689,7 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
|
|||
|
||||
self._options = wx.GetApp().Read( 'options' )
|
||||
|
||||
if self._options[ 'show_all_tags_in_autocomplete' ]: file_service_identifier = HC.NULL_SERVICE_IDENTIFIER
|
||||
if self._options[ 'show_all_tags_in_autocomplete' ]: file_service_identifier = HC.COMBINED_FILE_SERVICE_IDENTIFIER
|
||||
|
||||
AutoCompleteDropdownTags.__init__( self, parent, file_service_identifier, tag_service_identifier )
|
||||
|
||||
|
@ -2933,7 +2938,7 @@ class TagsBoxCPP( TagsBox ):
|
|||
|
||||
self._page_key = page_key
|
||||
|
||||
self._tag_service_identifier = HC.NULL_SERVICE_IDENTIFIER
|
||||
self._tag_service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER
|
||||
self._last_media = None
|
||||
|
||||
self._current_tags_to_count = {}
|
||||
|
@ -3084,7 +3089,7 @@ class TagsBoxFlat( TagsBox ):
|
|||
TagsBox.__init__( self, parent )
|
||||
|
||||
self._removed_callable = removed_callable
|
||||
self._tags = []
|
||||
self._tags = set()
|
||||
|
||||
|
||||
def _RecalcTags( self ):
|
||||
|
@ -3111,11 +3116,11 @@ class TagsBoxFlat( TagsBox ):
|
|||
self._TextsHaveChanged()
|
||||
|
||||
|
||||
def _Activate( self, s, term ):
|
||||
def _Activate( self, s, tag ):
|
||||
|
||||
if term in self._tags:
|
||||
if tag in self._tags:
|
||||
|
||||
self._tags.remove( term )
|
||||
self._tags.discard( tag )
|
||||
|
||||
self._RecalcTags()
|
||||
|
||||
|
@ -3123,9 +3128,15 @@ class TagsBoxFlat( TagsBox ):
|
|||
|
||||
|
||||
|
||||
def AddTag( self, tag ):
|
||||
def AddTag( self, tag, parents ):
|
||||
|
||||
self._tags.append( tag )
|
||||
if tag in self._tags: self._tags.discard( tag )
|
||||
else:
|
||||
|
||||
self._tags.add( tag )
|
||||
|
||||
self._tags.update( parents )
|
||||
|
||||
|
||||
self._RecalcTags()
|
||||
|
||||
|
|
|
@ -350,7 +350,7 @@ class DialogInputCustomFilterAction( Dialog ):
|
|||
|
||||
self._tag_service_identifiers = wx.Choice( self._tag_panel )
|
||||
self._tag_value = wx.TextCtrl( self._tag_panel, style = wx.TE_READONLY )
|
||||
self._tag_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self._tag_panel, self.SetTag, HC.LOCAL_FILE_SERVICE_IDENTIFIER, HC.NULL_SERVICE_IDENTIFIER )
|
||||
self._tag_input = ClientGUICommon.AutoCompleteDropdownTagsWrite( self._tag_panel, self.SetTag, HC.LOCAL_FILE_SERVICE_IDENTIFIER, HC.COMBINED_TAG_SERVICE_IDENTIFIER )
|
||||
|
||||
self._ok_tag = wx.Button( self._tag_panel, label = 'ok' )
|
||||
self._ok_tag.Bind( wx.EVT_BUTTON, self.EventOKTag )
|
||||
|
@ -4363,6 +4363,16 @@ class DialogManageOptionsLocal( Dialog ):
|
|||
|
||||
self._listbook.AddPage( self._gui_page, 'gui' )
|
||||
|
||||
# sound
|
||||
|
||||
self._sound_page = wx.Panel( self._listbook )
|
||||
self._sound_page.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
||||
|
||||
self._play_dumper_noises = wx.CheckBox( self._sound_page, label = 'play success/fail noises when dumping' )
|
||||
self._play_dumper_noises.SetValue( self._options[ 'play_dumper_noises' ] )
|
||||
|
||||
self._listbook.AddPage( self._sound_page, 'sound' )
|
||||
|
||||
# default file system predicates
|
||||
|
||||
system_predicates = self._options[ 'file_system_predicates' ]
|
||||
|
@ -4733,6 +4743,14 @@ class DialogManageOptionsLocal( Dialog ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._play_dumper_noises, FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self._sound_page.SetSizer( vbox )
|
||||
|
||||
#
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._namespace_colours, FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( self._new_namespace_colour, FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._edit_namespace_colour, FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
@ -4919,6 +4937,8 @@ class DialogManageOptionsLocal( Dialog ):
|
|||
|
||||
def EventOK( self, event ):
|
||||
|
||||
self._options[ 'play_dumper_noises' ] = self._play_dumper_noises.GetValue()
|
||||
|
||||
self._options[ 'confirm_client_exit' ] = self._confirm_client_exit.GetValue()
|
||||
self._options[ 'gui_capitalisation' ] = self._gui_capitalisation.GetValue()
|
||||
self._options[ 'show_all_tags_in_autocomplete' ] = self._gui_show_all_tags_in_autocomplete.GetValue()
|
||||
|
@ -7928,13 +7948,21 @@ class DialogManageTagServicePrecedence( Dialog ):
|
|||
|
||||
def EventOK( self, event ):
|
||||
|
||||
try:
|
||||
message = 'This operation may take several minutes to complete. Are you sure?'
|
||||
|
||||
with DialogYesNo( self, message ) as dlg:
|
||||
|
||||
service_identifiers = [ self._tag_services.GetClientData( i ) for i in range( self._tag_services.GetCount() ) ]
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
try:
|
||||
|
||||
service_identifiers = [ self._tag_services.GetClientData( i ) for i in range( self._tag_services.GetCount() ) ]
|
||||
|
||||
wx.GetApp().Write( 'set_tag_service_precedence', service_identifiers )
|
||||
|
||||
except Exception as e: wx.MessageBox( 'Something went wrong when trying to save tag service precedence to the database: ' + unicode( e ) )
|
||||
|
||||
|
||||
wx.GetApp().Write( 'set_tag_service_precedence', service_identifiers )
|
||||
|
||||
except Exception as e: wx.MessageBox( 'Something went wrong when trying to save tag service precedence to the database: ' + unicode( e ) )
|
||||
|
||||
self.EndModal( wx.ID_OK )
|
||||
|
||||
|
@ -8190,7 +8218,9 @@ class DialogManageTags( Dialog ):
|
|||
self._account = service.GetAccount()
|
||||
|
||||
|
||||
( self._current_tags, self._deleted_tags, self._pending_tags, self._petitioned_tags ) = CC.MediaIntersectCDPPTagServiceIdentifiers( media, tag_service_identifier )
|
||||
tags_managers = [ m.GetTagsManager() for m in media ]
|
||||
|
||||
( self._current_tags, self._deleted_tags, self._pending_tags, self._petitioned_tags ) = CC.IntersectTags( tags_managers, tag_service_identifier )
|
||||
|
||||
self._current_tags.sort()
|
||||
self._pending_tags.sort()
|
||||
|
@ -9057,11 +9087,11 @@ class DialogPathsToTagsRegex( Dialog ):
|
|||
|
||||
|
||||
|
||||
def AddTag( self, tag ):
|
||||
def AddTag( self, tag, parents = [] ):
|
||||
|
||||
if tag is not None:
|
||||
|
||||
self._tags.AddTag( tag )
|
||||
self._tags.AddTag( tag, parents )
|
||||
|
||||
self._tag_box.Clear()
|
||||
|
||||
|
@ -9069,11 +9099,11 @@ class DialogPathsToTagsRegex( Dialog ):
|
|||
|
||||
|
||||
|
||||
def AddTagSingle( self, tag ):
|
||||
def AddTagSingle( self, tag, parents = [] ):
|
||||
|
||||
if tag is not None:
|
||||
|
||||
self._single_tags.AddTag( tag )
|
||||
self._single_tags.AddTag( tag, parents )
|
||||
|
||||
self._single_tag_box.Clear()
|
||||
|
||||
|
@ -9085,6 +9115,11 @@ class DialogPathsToTagsRegex( Dialog ):
|
|||
|
||||
if tag not in self._paths_to_single_tags[ path ]: self._paths_to_single_tags[ path ].append( tag )
|
||||
|
||||
for parent in parents:
|
||||
|
||||
if parent not in self._paths_to_single_tags[ path ]: self._paths_to_single_tags[ path ].append( parent )
|
||||
|
||||
|
||||
|
||||
self._RefreshFileList() # make this more clever
|
||||
|
||||
|
@ -9165,10 +9200,6 @@ class DialogPathsToTagsRegex( Dialog ):
|
|||
|
||||
else: self._single_tag_box.Disable()
|
||||
|
||||
single_tags = list( single_tags )
|
||||
|
||||
single_tags.sort()
|
||||
|
||||
self._single_tags.SetTags( single_tags )
|
||||
|
||||
|
||||
|
@ -10214,12 +10245,12 @@ class DialogSetupExport( Dialog ):
|
|||
|
||||
for ( term_type, term ) in terms:
|
||||
|
||||
tags = media.GetTags()
|
||||
tags_manager = media.GetTagsManager()
|
||||
|
||||
if term_type == 'string': filename += term
|
||||
elif term_type == 'namespace':
|
||||
|
||||
tags = tags.GetNamespaceSlice( ( term, ) )
|
||||
tags = tags_manager.GetNamespaceSlice( ( term, ) )
|
||||
|
||||
filename += ', '.join( [ tag.split( ':' )[1] for tag in tags ] )
|
||||
|
||||
|
@ -10227,7 +10258,8 @@ class DialogSetupExport( Dialog ):
|
|||
|
||||
if term in ( 'tags', 'nn tags' ):
|
||||
|
||||
( current, deleted, pending, petitioned ) = tags.GetUnionCDPP()
|
||||
current = tags_manager.GetCurrent()
|
||||
pending = tags_manager.GetPending()
|
||||
|
||||
tags = list( current.union( pending ) )
|
||||
|
||||
|
@ -10249,7 +10281,7 @@ class DialogSetupExport( Dialog ):
|
|||
|
||||
if ':' in term: term = term.split( ':' )[1]
|
||||
|
||||
if tags.HasTag( term ): filename += term
|
||||
if tags_manager.HasTag( term ): filename += term
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import HydrusConstants as HC
|
||||
import HydrusAudioHandling
|
||||
import HydrusDownloading
|
||||
import HydrusImageHandling
|
||||
import ClientConstants as CC
|
||||
|
@ -352,6 +353,8 @@ class ManagementPanel( wx.lib.scrolledpanel.ScrolledPanel ):
|
|||
|
||||
wx.lib.scrolledpanel.ScrolledPanel.__init__( self, parent, style = wx.BORDER_NONE | wx.VSCROLL )
|
||||
|
||||
self._options = wx.GetApp().Read( 'options' )
|
||||
|
||||
self.SetupScrolling()
|
||||
|
||||
#self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
||||
|
@ -360,7 +363,7 @@ class ManagementPanel( wx.lib.scrolledpanel.ScrolledPanel ):
|
|||
self._page = page
|
||||
self._page_key = page_key
|
||||
self._file_service_identifier = file_service_identifier
|
||||
self._tag_service_identifier = HC.NULL_SERVICE_IDENTIFIER
|
||||
self._tag_service_identifier = HC.COMBINED_TAG_SERVICE_IDENTIFIER
|
||||
|
||||
HC.pubsub.sub( self, 'SetSearchFocus', 'set_search_focus' )
|
||||
|
||||
|
@ -628,7 +631,10 @@ class ManagementPanelDumper( ManagementPanel ):
|
|||
|
||||
for ( service_identifier, namespaces ) in advanced_tag_options.items():
|
||||
|
||||
( current, deleted, pending, petitioned ) = media.GetTags().GetCDPP( service_identifier )
|
||||
tags = media.GetTagsManager()
|
||||
|
||||
current = tags_manager.GetCurrent( service_identifier )
|
||||
pending = tags_manager.GetPending( service_identifier )
|
||||
|
||||
tags = current.union( pending )
|
||||
|
||||
|
@ -743,6 +749,12 @@ class ManagementPanelDumper( ManagementPanel ):
|
|||
|
||||
self._actually_dumping = False
|
||||
|
||||
if self._options[ 'play_dumper_noises' ]:
|
||||
|
||||
if status == 'success': HydrusAudioHandling.PlayNoise( 'success' )
|
||||
else: HydrusAudioHandling.PlayNoise( 'error' )
|
||||
|
||||
|
||||
if status == 'success':
|
||||
|
||||
dump_status_enum = CC.DUMPER_DUMPED_OK
|
||||
|
@ -2592,7 +2604,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
self._current_predicates_box = ClientGUICommon.TagsBoxPredicates( self._search_panel, self._page_key, initial_predicates )
|
||||
|
||||
self._searchbox = ClientGUICommon.AutoCompleteDropdownTagsRead( self._search_panel, self._page_key, self._file_service_identifier, HC.NULL_SERVICE_IDENTIFIER, self._page.GetMedia )
|
||||
self._searchbox = ClientGUICommon.AutoCompleteDropdownTagsRead( self._search_panel, self._page_key, self._file_service_identifier, HC.COMBINED_TAG_SERVICE_IDENTIFIER, self._page.GetMedia )
|
||||
|
||||
self._search_panel.AddF( self._current_predicates_box, FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._search_panel.AddF( self._searchbox, FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
|
|
@ -1145,7 +1145,7 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
if t is not None:
|
||||
|
||||
if t.GetFileServiceIdentifiersCDPP().HasLocal(): self._FullScreen( t )
|
||||
elif self._file_service_identifier != HC.NULL_SERVICE_IDENTIFIER: wx.GetApp().Write( 'add_downloads', self._file_service_identifier, t.GetHashes() )
|
||||
elif self._file_service_identifier != HC.COMBINED_FILE_SERVICE_IDENTIFIER: wx.GetApp().Write( 'add_downloads', self._file_service_identifier, t.GetHashes() )
|
||||
|
||||
|
||||
|
||||
|
@ -1751,7 +1751,7 @@ class Thumbnail( Selectable ):
|
|||
|
||||
local = self.GetFileServiceIdentifiersCDPP().HasLocal()
|
||||
|
||||
( creators, series, titles, volumes, chapters, pages ) = self.GetTags().GetCSTVCP()
|
||||
( creators, series, titles, volumes, chapters, pages ) = self.GetTagsManager().GetCSTVCP()
|
||||
|
||||
if self._hydrus_bmp is None: self._LoadFromDB()
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ class MediaList():
|
|||
|
||||
for media in self._singleton_media:
|
||||
|
||||
if len( namespaces_to_collect_by ) > 0: namespace_key = media.GetTags().GetNamespaceSlice( namespaces_to_collect_by )
|
||||
if len( namespaces_to_collect_by ) > 0: namespace_key = media.GetTagsManager().GetNamespaceSlice( namespaces_to_collect_by )
|
||||
else: namespace_key = None
|
||||
|
||||
if len( ratings_to_collect_by ) > 0:
|
||||
|
@ -304,13 +304,13 @@ class MediaList():
|
|||
|
||||
def namespace_compare( x, y ):
|
||||
|
||||
x_tags = x.GetTags()
|
||||
y_tags = y.GetTags()
|
||||
x_tags_manager = x.GetTagsManager()
|
||||
y_tags_manager = y.GetTagsManager()
|
||||
|
||||
for namespace in sort_by_data:
|
||||
|
||||
x_namespace_slice = x_tags.GetNamespaceSlice( ( namespace, ) )
|
||||
y_namespace_slice = y_tags.GetNamespaceSlice( ( namespace, ) )
|
||||
x_namespace_slice = x_tags_manager.GetNamespaceSlice( ( namespace, ) )
|
||||
y_namespace_slice = y_tags_manager.GetNamespaceSlice( ( namespace, ) )
|
||||
|
||||
if x_namespace_slice == y_namespace_slice: continue # this covers len == 0 for both, too
|
||||
else:
|
||||
|
@ -412,7 +412,7 @@ class MediaCollection( MediaList, Media ):
|
|||
self._duration = None
|
||||
self._num_frames = None
|
||||
self._num_words = None
|
||||
self._tags = None
|
||||
self._tags_manager = None
|
||||
self._file_service_identifiers = None
|
||||
|
||||
self._RecalcInternals()
|
||||
|
@ -438,37 +438,11 @@ class MediaCollection( MediaList, Media ):
|
|||
if duration_sum > 0: self._duration = duration_sum
|
||||
else: self._duration = None
|
||||
|
||||
# better-but-still-pretty-horrible code starts here
|
||||
tags_managers = [ m.GetTagsManager() for m in self._sorted_media ]
|
||||
|
||||
# remember: the only time a collection is asked for its tags is by thumbnail.getbmp(), to draw series and page info
|
||||
# until I make SVCP more complicated, it mostly needs only be a quick and ugly intersection
|
||||
self._tags_manager = CC.MergeTags( tags_managers )
|
||||
|
||||
all_tags_cdpp = [ m.GetTags().GetServiceIdentifiersToCDPP() for m in self._sorted_media ]
|
||||
|
||||
combined_tags = collections.defaultdict( list )
|
||||
|
||||
for tags_cdpp in all_tags_cdpp:
|
||||
|
||||
for ( service_identifier, cdpp ) in tags_cdpp.items(): combined_tags[ service_identifier ].append( cdpp )
|
||||
|
||||
|
||||
final_tags = {}
|
||||
|
||||
for ( service_identifier, cdpps ) in combined_tags.items():
|
||||
|
||||
current = list( HC.IntelligentMassIntersect( ( c for ( c, d, p, pet ) in cdpps ) ) )
|
||||
deleted = []
|
||||
pending = list( HC.IntelligentMassIntersect( ( p for ( c, d, p, pet ) in cdpps ) ) )
|
||||
petitioned = []
|
||||
|
||||
final_tags[ service_identifier ] = ( current, deleted, pending, petitioned )
|
||||
|
||||
|
||||
self._tags = CC.CDPPTagServiceIdentifiers( wx.GetApp().Read( 'tag_service_precedence' ), final_tags )
|
||||
|
||||
# END OF HORRIBLE CODE
|
||||
|
||||
# new horrible compromise
|
||||
# horrible compromise
|
||||
if len( self._sorted_media ) > 0: self._ratings = self._sorted_media[0].GetRatings()
|
||||
else: self._ratings = ( CC.LocalRatings( {} ), CC.CPRemoteRatingsServiceIdentifiers( {} ) )
|
||||
|
||||
|
@ -541,18 +515,18 @@ class MediaCollection( MediaList, Media ):
|
|||
|
||||
def GetResolution( self ): return ( self._width, self._height )
|
||||
|
||||
def GetSingletonsTags( self ):
|
||||
def GetSingletonsTagsManagers( self ):
|
||||
|
||||
all_tags = [ m.GetTags() for m in self._singleton_media ]
|
||||
tags_managers = [ m.GetTagsManager() for m in self._singleton_media ]
|
||||
|
||||
for m in self._collected_media: all_tags.extend( m.GetSingletonsTags() )
|
||||
for m in self._collected_media: tags_managers.extend( m.GetSingletonsTagsManagers() )
|
||||
|
||||
return all_tags
|
||||
return tags_managers
|
||||
|
||||
|
||||
def GetSize( self ): return self._size
|
||||
|
||||
def GetTags( self ): return self._tags
|
||||
def GetTagsManager( self ): return self._tags_manager
|
||||
|
||||
def GetTimestamp( self ): return self._timestamp
|
||||
|
||||
|
@ -641,7 +615,7 @@ class MediaSingleton( Media ):
|
|||
|
||||
def GetPrettyInfo( self ):
|
||||
|
||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags, file_service_identifiers, local_ratings, remote_ratings ) = self._media_result.GetInfo()
|
||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, file_service_identifiers, local_ratings, remote_ratings ) = self._media_result.GetInfo()
|
||||
|
||||
info_string = HC.ConvertIntToBytes( size ) + ' ' + HC.mime_string_lookup[ mime ]
|
||||
|
||||
|
@ -674,7 +648,7 @@ class MediaSingleton( Media ):
|
|||
else: return size
|
||||
|
||||
|
||||
def GetTags( self ): return self._media_result.GetTags()
|
||||
def GetTagsManager( self ): return self._media_result.GetTagsManager()
|
||||
|
||||
def HasArchive( self ): return not self._media_result.GetInbox()
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import HydrusConstants as HC
|
||||
import mp3play
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
import wx
|
||||
|
||||
parsed_noises = {}
|
||||
|
||||
def PlayNoise( name ):
|
||||
|
||||
if name not in parsed_noises:
|
||||
|
||||
if name == 'success': filename = 'success.mp3'
|
||||
elif name == 'error': filename = 'error.mp3'
|
||||
|
||||
path = HC.STATIC_DIR + os.path.sep + filename
|
||||
|
||||
noise = mp3play.load( path )
|
||||
|
||||
parsed_noises[ name ] = noise
|
||||
|
||||
|
||||
noise = parsed_noises[ name ]
|
||||
|
||||
noise.play()
|
||||
|
|
@ -30,7 +30,7 @@ TEMP_DIR = BASE_DIR + os.path.sep + 'temp'
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 9
|
||||
SOFTWARE_VERSION = 70
|
||||
SOFTWARE_VERSION = 71
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
@ -88,6 +88,8 @@ LOCAL_RATING_NUMERICAL = 6
|
|||
LOCAL_RATING_LIKE = 7
|
||||
RATING_NUMERICAL_REPOSITORY = 8
|
||||
RATING_LIKE_REPOSITORY = 9
|
||||
COMBINED_TAG = 10
|
||||
COMBINED_FILE = 11
|
||||
SERVER_ADMIN = 99
|
||||
NULL_SERVICE = 100
|
||||
|
||||
|
@ -120,6 +122,7 @@ CURRENT = 0
|
|||
PENDING = 1
|
||||
DELETED = 2
|
||||
PETITIONED = 3
|
||||
DELETED_PENDING = 4
|
||||
|
||||
HIGH_PRIORITY = 0
|
||||
LOW_PRIORITY = 2
|
||||
|
@ -141,6 +144,10 @@ SERVICE_INFO_NUM_PENDING = 11
|
|||
SERVICE_INFO_NUM_CONVERSATIONS = 12
|
||||
SERVICE_INFO_NUM_UNREAD = 13
|
||||
SERVICE_INFO_NUM_DRAFTS = 14
|
||||
SERVICE_INFO_NUM_PENDING_MAPPINGS = 15
|
||||
SERVICE_INFO_NUM_PETITIONED_MAPPINGS = 16
|
||||
SERVICE_INFO_NUM_PENDING_FILES = 15
|
||||
SERVICE_INFO_NUM_PETITIONED_FILES = 16
|
||||
|
||||
SERVICE_UPDATE_ACCOUNT = 0
|
||||
SERVICE_UPDATE_DELETE_PENDING = 1
|
||||
|
@ -477,6 +484,14 @@ def BuildKeyToListDict( pairs ):
|
|||
|
||||
return d
|
||||
|
||||
def BuildKeyToSetDict( pairs ):
|
||||
|
||||
d = collections.defaultdict( set )
|
||||
|
||||
for ( key, value ) in pairs: d[ key ].add( value )
|
||||
|
||||
return d
|
||||
|
||||
def CalculateScoreFromRating( count, rating ):
|
||||
|
||||
# http://www.evanmiller.org/how-not-to-sort-by-average-rating.html
|
||||
|
@ -1601,6 +1616,8 @@ class ClientServiceIdentifier( HydrusYAMLBase ):
|
|||
|
||||
def __ne__( self, other ): return self.__hash__() != other.__hash__()
|
||||
|
||||
def GetInfo( self ): return ( self._service_key, self._type, self._name )
|
||||
|
||||
def GetName( self ): return self._name
|
||||
|
||||
def GetServiceKey( self ): return self._service_key
|
||||
|
@ -1609,6 +1626,8 @@ class ClientServiceIdentifier( HydrusYAMLBase ):
|
|||
|
||||
LOCAL_FILE_SERVICE_IDENTIFIER = ClientServiceIdentifier( 'local files', LOCAL_FILE, 'local files' )
|
||||
LOCAL_TAG_SERVICE_IDENTIFIER = ClientServiceIdentifier( 'local tags', LOCAL_TAG, 'local tags' )
|
||||
COMBINED_FILE_SERVICE_IDENTIFIER = ClientServiceIdentifier( 'all known files', COMBINED_FILE, 'all known files' )
|
||||
COMBINED_TAG_SERVICE_IDENTIFIER = ClientServiceIdentifier( 'all known tags', COMBINED_TAG, 'all known tags' )
|
||||
NULL_SERVICE_IDENTIFIER = ClientServiceIdentifier( '', NULL_SERVICE, 'no service' )
|
||||
|
||||
class ContentUpdate():
|
||||
|
@ -2266,6 +2285,7 @@ sqlite3.register_adapter( HydrusUpdateTagRepository, yaml.safe_dump )
|
|||
|
||||
# Custom Exceptions
|
||||
|
||||
class DBAccessException( Exception ): pass
|
||||
class NetworkVersionException( Exception ): pass
|
||||
class NoContentException( Exception ): pass
|
||||
class NotFoundException( Exception ): pass
|
||||
|
|
|
@ -752,7 +752,7 @@ class DownloaderNewgrounds( Downloader ):
|
|||
|
||||
title = soup.find( 'title' )
|
||||
|
||||
tags.append( 'title:' + title )
|
||||
tags.append( 'title:' + title.string )
|
||||
|
||||
except: pass
|
||||
|
||||
|
|
|
@ -1542,6 +1542,8 @@ class DB( ServiceDB ):
|
|||
c.execute( 'CREATE INDEX mapping_petitions_service_id_tag_id_hash_id_index ON mapping_petitions ( service_id, tag_id, hash_id );' )
|
||||
|
||||
c.execute( 'CREATE TABLE mappings ( service_id INTEGER REFERENCES services ON DELETE CASCADE, tag_id INTEGER, hash_id INTEGER, account_id INTEGER, timestamp INTEGER, PRIMARY KEY( service_id, tag_id, hash_id ) );' )
|
||||
c.execute( 'CREATE INDEX mappings_account_id_index ON mappings ( account_id );' )
|
||||
c.execute( 'CREATE INDEX mappings_timestamp_index ON mappings ( timestamp );' )
|
||||
|
||||
c.execute( 'CREATE TABLE messages ( message_key BLOB_BYTES PRIMARY KEY, service_id INTEGER REFERENCES services ON DELETE CASCADE, account_id INTEGER, timestamp INTEGER );' )
|
||||
c.execute( 'CREATE INDEX messages_service_id_account_id_index ON messages ( service_id, account_id );' )
|
||||
|
@ -1565,6 +1567,22 @@ class DB( ServiceDB ):
|
|||
c.execute( 'CREATE TABLE tags ( tag_id INTEGER PRIMARY KEY, tag TEXT );' )
|
||||
c.execute( 'CREATE UNIQUE INDEX tags_tag_index ON tags ( tag );' )
|
||||
|
||||
c.execute( 'CREATE TABLE tag_siblings ( service_id INTEGER REFERENCES services ON DELETE CASCADE, old_tag_id INTEGER, new_tag_id INTEGER, account_id INTEGER, timestamp INTEGER, PRIMARY KEY ( service_id, old_tag_id ) );' )
|
||||
c.execute( 'CREATE INDEX tag_siblings_service_id_account_id_index ON tag_siblings ( service_id, account_id );' )
|
||||
c.execute( 'CREATE INDEX tag_siblings_service_id_timestamp_index ON tag_siblings ( service_id, timestamp );' )
|
||||
|
||||
c.execute( 'CREATE TABLE deleted_tag_siblings ( service_id INTEGER REFERENCES services ON DELETE CASCADE, old_tag_id INTEGER, new_tag_id INTEGER, reason_id INTEGER, account_id INTEGER, admin_account_id INTEGER, timestamp INTEGER, PRIMARY KEY ( service_id, old_tag_id ) );' )
|
||||
c.execute( 'CREATE INDEX deleted_tag_siblings_service_id_account_id_index ON deleted_tag_siblings ( service_id, account_id );' )
|
||||
c.execute( 'CREATE INDEX deleted_tag_siblings_service_id_timestamp_index ON deleted_tag_siblings ( service_id, timestamp );' )
|
||||
|
||||
c.execute( 'CREATE TABLE pending_tag_siblings ( service_id INTEGER REFERENCES services ON DELETE CASCADE, old_tag_id INTEGER, new_tag_id INTEGER, account_id INTEGER, timestamp INTEGER, PRIMARY KEY ( service_id, old_tag_id, account_id ) );' )
|
||||
c.execute( 'CREATE INDEX pending_tag_siblings_service_id_account_id_index ON tag_siblings ( service_id, account_id );' )
|
||||
c.execute( 'CREATE INDEX pending_tag_siblings_service_id_timestamp_index ON tag_siblings ( service_id, timestamp );' )
|
||||
|
||||
c.execute( 'CREATE TABLE tag_sibling_petitions ( service_id INTEGER REFERENCES services ON DELETE CASCADE, account_id INTEGER, old_tag_id INTEGER, new_tag_id INTEGER, reason_id INTEGER, PRIMARY KEY ( service_id, account_id, old_tag_id, reason_id ) );' )
|
||||
c.execute( 'CREATE INDEX tag_sibling_petitions_service_id_account_id_reason_id_tag_id_index ON tag_sibling_petitions ( service_id, account_id, reason_id );' )
|
||||
c.execute( 'CREATE INDEX tag_sibling_petitions_service_id_tag_id_hash_id_index ON tag_sibling_petitions ( service_id, old_tag_id );' )
|
||||
|
||||
c.execute( 'CREATE TABLE update_cache ( service_id INTEGER REFERENCES services ON DELETE CASCADE, begin INTEGER, end INTEGER, update_key TEXT, dirty INTEGER_BOOLEAN, PRIMARY KEY( service_id, begin ) );' )
|
||||
c.execute( 'CREATE UNIQUE INDEX update_cache_service_id_end_index ON update_cache ( service_id, end );' )
|
||||
c.execute( 'CREATE INDEX update_cache_service_id_dirty_index ON update_cache ( service_id, dirty );' )
|
||||
|
@ -1760,6 +1778,31 @@ class DB( ServiceDB ):
|
|||
|
||||
|
||||
|
||||
if version < 71:
|
||||
|
||||
try: c.execute( 'CREATE INDEX mappings_account_id_index ON mappings ( account_id );' )
|
||||
except: pass
|
||||
|
||||
try: c.execute( 'CREATE INDEX mappings_timestamp_index ON mappings ( timestamp );' )
|
||||
except: pass
|
||||
'''
|
||||
c.execute( 'CREATE TABLE tag_siblings ( service_id INTEGER REFERENCES services ON DELETE CASCADE, old_tag_id INTEGER, new_tag_id INTEGER, account_id INTEGER, timestamp INTEGER, PRIMARY KEY ( service_id, old_tag_id ) );' )
|
||||
c.execute( 'CREATE INDEX tag_siblings_service_id_account_id_index ON tag_siblings ( service_id, account_id );' )
|
||||
c.execute( 'CREATE INDEX tag_siblings_service_id_timestamp_index ON tag_siblings ( service_id, timestamp );' )
|
||||
|
||||
c.execute( 'CREATE TABLE pending_tag_siblings ( service_id INTEGER REFERENCES services ON DELETE CASCADE, old_tag_id INTEGER, new_tag_id INTEGER, account_id INTEGER, timestamp INTEGER, PRIMARY KEY ( service_id, old_tag_id, account_id ) );' )
|
||||
c.execute( 'CREATE INDEX pending_tag_siblings_service_id_account_id_index ON tag_siblings ( service_id, account_id );' )
|
||||
c.execute( 'CREATE INDEX pending_tag_siblings_service_id_timestamp_index ON tag_siblings ( service_id, timestamp );' )
|
||||
|
||||
c.execute( 'CREATE TABLE deleted_tag_siblings ( service_id INTEGER REFERENCES services ON DELETE CASCADE, old_tag_id INTEGER, new_tag_id INTEGER, reason_id INTEGER, account_id INTEGER, admin_account_id INTEGER, timestamp INTEGER, PRIMARY KEY ( service_id, old_tag_id ) );' )
|
||||
c.execute( 'CREATE INDEX deleted_tag_siblings_service_id_account_id_index ON deleted_tag_siblings ( service_id, account_id );' )
|
||||
c.execute( 'CREATE INDEX deleted_tag_siblings_service_id_timestamp_index ON deleted_tag_siblings ( service_id, timestamp );' )
|
||||
|
||||
c.execute( 'CREATE TABLE tag_sibling_petitions ( service_id INTEGER REFERENCES services ON DELETE CASCADE, account_id INTEGER, old_tag_id INTEGER, new_tag_id INTEGER, reason_id INTEGER, PRIMARY KEY ( service_id, account_id, old_tag_id, reason_id ) );' )
|
||||
c.execute( 'CREATE INDEX tag_sibling_petitions_service_id_account_id_reason_id_tag_id_index ON tag_sibling_petitions ( service_id, account_id, reason_id );' )
|
||||
c.execute( 'CREATE INDEX tag_sibling_petitions_service_id_tag_id_hash_id_index ON tag_sibling_petitions ( service_id, old_tag_id );' )
|
||||
'''
|
||||
|
||||
c.execute( 'UPDATE version SET version = ?;', ( HC.SOFTWARE_VERSION, ) )
|
||||
|
||||
c.execute( 'COMMIT' )
|
||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue