Version 246
This commit is contained in:
parent
9b9388f71c
commit
2ebedbc645
|
@ -8,6 +8,55 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 246</h3></li>
|
||||
<ul>
|
||||
<li>fixed a critical bug in serverside content deserialisating that meant servers were not processing most client-submitted data properly</li>
|
||||
<li>fixed a critical bug in 'all known tags' autocomplete regeneration--please run database->regen->a/c cache when it is convenient and let me know if your numbers are still off</li>
|
||||
<li>fixed the pre-v238 update problem for good by abandoning update attempts and advising users to try v238 first</li>
|
||||
<li>clientside invalid tags will now be collapsed like with the server last week. if a tag is invalid (typically something with an extra space, like "series: blah"), the update code will attempt to replace existing mappings with the collapsed valid version. some unusual cases may remain--they will be replaced with 'invalid namespace "series "' and similar. Please remove and replace these when convenient and contact me if there are way too many for you to deal with</li>
|
||||
<li>duplicates pages now have a file domain for the filtering section, and they remember this domain through session loads</li>
|
||||
<li>this file domain is accurate--counting potential duplicates and fetching pairs for 'show some pairs' only from those domains. the issue of remote files appearing should be gone!</li>
|
||||
<li>there is now only one 'idle' entry in the duplicates page cog menu--it combines the three previous into one</li>
|
||||
<li>fixed numerous irregularities across the wildcard code. all search input now has an implicit '*' on the end unless you put a '*' anywhere else, in which case it acts exactly as you enter it, with a non-* beginning matching beginning of string, whitespace, or colon, and non-* end matching end of string or whitespace</li>
|
||||
<li>autocomplete now searches namespace, so entering 'char' will load up all the 'character:' tags along with 'series:di gi charat'. this can lag to hell and back, so it may either need some work or be optional in the future. feedback would be appreciated</li>
|
||||
<li>typing 'namespace:' will include all the series tags below the special optimised 'namespace:*anything*' tag</li>
|
||||
<li>autocomplete searches recognise an explicit '*' no matter where it is in the entry text. typing 'a*' will load up all the a tags and present a 'a*' wildcard option</li>
|
||||
<li>quickly entering a wildcard entry will now submit the correct wildcard predicate ('rather than a literal 'hel*' or whatever tag)</li>
|
||||
<li>review services panel now reports total mappings info on tag services</li>
|
||||
<li>review services panel now reports total files info on file services</li>
|
||||
<li>manage services's listctrl is now type | name | deletable and initially sorts by type. the strings used for hydrus service types are also improved</li>
|
||||
<li>manage serverside services (called by server admins to manage their services) have fixed setnondupe port and name on edit service events</li>
|
||||
<li>new popup messages will now also appear if there were previously no popup messages to display if the current focus is on a child on_top frame, such as review services (you'll now see the processing popup appear when you click 'process now' on review services)</li>
|
||||
<li>the popup message manager now initialises its display window with a single message that is quickly dismissed. this helps set up some variables in a safe environment so they don't have to be generated later when the gui might be minimised or otherwise unusual</li>
|
||||
<li>hid hydrus update files from 'all local files' searches</li>
|
||||
<li>added 'media_view' entries for hydrus update files, just in case they are still visible in some unusual contexts (and they may be again in a future update anyway)</li>
|
||||
<li>fixed 'recent tags' being returned from the database out of order</li>
|
||||
<li>by default, 'recent tags' is now on for new users</li>
|
||||
<li>'get tags even if file already in db' now defaults to False</li>
|
||||
<li>file import status now allows a 'delete' action below the 'skip' action</li>
|
||||
<li>file import status right-click event processing is more sane</li>
|
||||
<li>fixed the new raw width/height sort choices, which were accidentally swapped</li>
|
||||
<li>cleaned the media sort code generally</li>
|
||||
<li>cleared out some redundant rows that are in some users' client_files_locations</li>
|
||||
<li>namespaced predicates are no longer count-merged with their namespaceless versions in 'write' autocomplete dropdowns</li>
|
||||
<li>'unknown' accounts should now properly resync after clientside service change</li>
|
||||
<li>improved how registration keys are checked serverside when fetching access keys</li>
|
||||
<li>fixed a v244 update problem when unexected additional tag parent/sibling petitions rows exist</li>
|
||||
<li>improved my free space test code and applied it to the old v243->v244 free space test (it'll now test free space on your temporary path and report problems appropriately)</li>
|
||||
<li>to improve log privacy and cleanliness, and to make it easier to report profiles, db/pubsub profiles now write to a separate log file named appropriately and labelled with the process's start time</li>
|
||||
<li>profiles are more concise and formatted a little neater</li>
|
||||
<li>across the program, three-period ... ellipses are now replaced with their single character unicode … counterpart (except to the console, where any instance of the unicode ellipsis will now be converted back to ...)</li>
|
||||
<li>cleaned up some log printing code</li>
|
||||
<li>cleaned up some experimental static serialisation code, still thinking if I like it or not</li>
|
||||
<li>started on some proper unit tests for hydrus serialisable objects</li>
|
||||
<li>fixed and otherwise updated a heap of unit test code to account for the v245 changes</li>
|
||||
<li>cleaned up a bunch of old database table join code</li>
|
||||
<li>started some databse-query-tuple-stripping code cleaning</li>
|
||||
<li>deleted more old unused code</li>
|
||||
<li>misc timing improvements</li>
|
||||
<li>misc code cleanup experimentation</li>
|
||||
<li>misc cleanup</li>
|
||||
</ul>
|
||||
<li><h3>version 245</h3></li>
|
||||
<ul>
|
||||
<li>fixed a v244 update problem for clients updating from <v238</li>
|
||||
|
|
|
@ -1987,7 +1987,10 @@ class ServicesManager( object ):
|
|||
|
||||
self._keys_to_services = { service.GetServiceKey() : service for service in services }
|
||||
|
||||
compare_function = lambda a, b: cmp( a.GetName(), b.GetName() )
|
||||
def compare_function( a, b ):
|
||||
|
||||
return cmp( a.GetName(), b.GetName() )
|
||||
|
||||
|
||||
self._services_sorted = list( services )
|
||||
self._services_sorted.sort( cmp = compare_function )
|
||||
|
@ -1997,7 +2000,12 @@ class ServicesManager( object ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
filtered_service_keys = [ service_key for service_key in service_keys if self._keys_to_services[ service_key ].GetServiceType() in desired_types ]
|
||||
def func( service_key ):
|
||||
|
||||
return self._keys_to_services[ service_key ].GetServiceType() in desired_types
|
||||
|
||||
|
||||
filtered_service_keys = filter( func, service_keys )
|
||||
|
||||
return filtered_service_keys
|
||||
|
||||
|
@ -2007,7 +2015,12 @@ class ServicesManager( object ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
filtered_service_keys = [ service_key for service_key in service_keys if service_key in self._keys_to_services ]
|
||||
def func( service_key ):
|
||||
|
||||
return service_key in self._keys_to_services
|
||||
|
||||
|
||||
filtered_service_keys = filter( func, service_keys )
|
||||
|
||||
return filtered_service_keys
|
||||
|
||||
|
@ -2045,7 +2058,12 @@ class ServicesManager( object ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
services = [ service for service in self._services_sorted if service.GetServiceType() in desired_types ]
|
||||
def func( service ):
|
||||
|
||||
return service.GetServiceType() in desired_types
|
||||
|
||||
|
||||
services = filter( func, self._services_sorted )
|
||||
|
||||
if randomised:
|
||||
|
||||
|
@ -2448,11 +2466,11 @@ class TagSiblingsManager( object ):
|
|||
|
||||
else:
|
||||
|
||||
matching_keys = ClientSearch.FilterTagsBySearchEntry( service_key, search_text, siblings.keys(), search_siblings = False )
|
||||
matching_keys = ClientSearch.FilterTagsBySearchText( service_key, search_text, siblings.keys(), search_siblings = False )
|
||||
|
||||
key_based_matching_values = { siblings[ key ] for key in matching_keys }
|
||||
|
||||
value_based_matching_values = ClientSearch.FilterTagsBySearchEntry( service_key, search_text, siblings.values(), search_siblings = False )
|
||||
value_based_matching_values = ClientSearch.FilterTagsBySearchText( service_key, search_text, siblings.values(), search_siblings = False )
|
||||
|
||||
|
||||
matching_values = key_based_matching_values.union( value_based_matching_values )
|
||||
|
|
|
@ -173,7 +173,8 @@ MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL_PAUSED = 1
|
|||
MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED = 2
|
||||
MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED_PAUSED = 3
|
||||
MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON = 4
|
||||
MEDIA_VIEWER_ACTION_DO_NOT_SHOW = 5
|
||||
MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY = 5
|
||||
MEDIA_VIEWER_ACTION_DO_NOT_SHOW = 6
|
||||
|
||||
media_viewer_action_string_lookup = {}
|
||||
|
||||
|
@ -182,11 +183,12 @@ media_viewer_action_string_lookup[ MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL_PAUSED ] =
|
|||
media_viewer_action_string_lookup[ MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED ] = 'show, but initially behind an embed button'
|
||||
media_viewer_action_string_lookup[ MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED_PAUSED ] = 'show, but initially behind an embed button, and start paused'
|
||||
media_viewer_action_string_lookup[ MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON ] = 'show an \'open externally\' button'
|
||||
media_viewer_action_string_lookup[ MEDIA_VIEWER_ACTION_DO_NOT_SHOW ] = 'do not show in the media viewer. on thumbnail activation, open externally'
|
||||
media_viewer_action_string_lookup[ MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY ] = 'do not show in the media viewer. on thumbnail activation, open externally'
|
||||
media_viewer_action_string_lookup[ MEDIA_VIEWER_ACTION_DO_NOT_SHOW ] = 'do not show at all'
|
||||
|
||||
static_full_support = [ MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED, MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, MEDIA_VIEWER_ACTION_DO_NOT_SHOW ]
|
||||
animated_full_support = [ MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL_PAUSED, MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED, MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED_PAUSED, MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, MEDIA_VIEWER_ACTION_DO_NOT_SHOW ]
|
||||
no_support = [ MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, MEDIA_VIEWER_ACTION_DO_NOT_SHOW ]
|
||||
static_full_support = [ MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED, MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY ]
|
||||
animated_full_support = [ MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL_PAUSED, MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED, MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED_PAUSED, MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY ]
|
||||
no_support = [ MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY, MEDIA_VIEWER_ACTION_DO_NOT_SHOW ]
|
||||
|
||||
media_viewer_capabilities = {}
|
||||
|
||||
|
@ -196,7 +198,7 @@ media_viewer_capabilities[ HC.IMAGE_GIF ] = animated_full_support
|
|||
|
||||
if HC.PLATFORM_WINDOWS:
|
||||
|
||||
media_viewer_capabilities[ HC.APPLICATION_FLASH ] = [ MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED, MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, MEDIA_VIEWER_ACTION_DO_NOT_SHOW ]
|
||||
media_viewer_capabilities[ HC.APPLICATION_FLASH ] = [ MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, MEDIA_VIEWER_ACTION_SHOW_BEHIND_EMBED, MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY, MEDIA_VIEWER_ACTION_DO_NOT_SHOW ]
|
||||
|
||||
else:
|
||||
|
||||
|
@ -215,7 +217,9 @@ media_viewer_capabilities[ HC.VIDEO_WMV ] = animated_full_support
|
|||
media_viewer_capabilities[ HC.AUDIO_MP3 ] = no_support
|
||||
media_viewer_capabilities[ HC.AUDIO_OGG ] = no_support
|
||||
media_viewer_capabilities[ HC.AUDIO_FLAC ] = no_support
|
||||
media_viewer_capabilities[ HC.AUDIO_WMA] = no_support
|
||||
media_viewer_capabilities[ HC.AUDIO_WMA ] = no_support
|
||||
media_viewer_capabilities[ HC.APPLICATION_HYDRUS_UPDATE_CONTENT ] = no_support
|
||||
media_viewer_capabilities[ HC.APPLICATION_HYDRUS_UPDATE_DEFINITIONS ] = no_support
|
||||
|
||||
MEDIA_VIEWER_SCALE_100 = 0
|
||||
MEDIA_VIEWER_SCALE_MAX_REGULAR = 1
|
||||
|
|
|
@ -45,6 +45,8 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
HydrusController.HydrusController.__init__( self, db_dir, no_daemons, no_wal )
|
||||
|
||||
self._name = 'client'
|
||||
|
||||
HydrusGlobals.client_controller = self
|
||||
|
||||
# just to set up some defaults, in case some db update expects something for an odd yaml-loading reason
|
||||
|
@ -518,7 +520,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
def InitModel( self ):
|
||||
|
||||
self.pub( 'splash_set_title_text', 'booting db...' )
|
||||
self.pub( 'splash_set_title_text', u'booting db\u2026' )
|
||||
|
||||
self._http = ClientNetworking.HTTPConnectionManager()
|
||||
|
||||
|
@ -601,7 +603,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
self.CallBlockingToWx( wx_code_password )
|
||||
|
||||
|
||||
self.pub( 'splash_set_title_text', 'booting gui...' )
|
||||
self.pub( 'splash_set_title_text', u'booting gui\u2026' )
|
||||
|
||||
def wx_code_gui():
|
||||
|
||||
|
@ -627,7 +629,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
if not self._no_daemons:
|
||||
|
||||
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'CheckMouseIdle', ClientDaemons.DAEMONCheckMouseIdle, period = 10 ) )
|
||||
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'SynchroniseAccounts', ClientDaemons.DAEMONSynchroniseAccounts, ( 'permissions_are_stale', ) ) )
|
||||
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'SynchroniseAccounts', ClientDaemons.DAEMONSynchroniseAccounts, ( 'notify_unknown_accounts', ) ) )
|
||||
self._daemons.append( HydrusThreading.DAEMONWorker( self, 'SaveDirtyObjects', ClientDaemons.DAEMONSaveDirtyObjects, ( 'important_dirt_to_clean', ), period = 30 ) )
|
||||
|
||||
self._daemons.append( HydrusThreading.DAEMONForegroundWorker( self, 'DownloadFiles', ClientDaemons.DAEMONDownloadFiles, ( 'notify_new_downloads', 'notify_new_permissions' ) ) )
|
||||
|
@ -688,7 +690,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
loaded_into_disk_cache = HydrusGlobals.client_controller.Read( 'load_into_disk_cache', stop_time = disk_cache_stop_time, caller_limit = disk_cache_maintenance_mb * 1024 * 1024 )
|
||||
|
||||
|
||||
if self._new_options.GetBoolean( 'maintain_similar_files_phashes_during_idle' ):
|
||||
if self._new_options.GetBoolean( 'maintain_similar_files_duplicate_pairs_during_idle' ):
|
||||
|
||||
phashes_stop_time = stop_time
|
||||
|
||||
|
@ -699,9 +701,6 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
self.WriteInterruptable( 'maintain_similar_files_phashes', stop_time = phashes_stop_time )
|
||||
|
||||
|
||||
if self._new_options.GetBoolean( 'maintain_similar_files_tree_during_idle' ):
|
||||
|
||||
tree_stop_time = stop_time
|
||||
|
||||
if tree_stop_time is None:
|
||||
|
@ -711,9 +710,6 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
self.WriteInterruptable( 'maintain_similar_files_tree', stop_time = tree_stop_time, abandon_if_other_work_to_do = True )
|
||||
|
||||
|
||||
if self._new_options.GetBoolean( 'maintain_similar_files_duplicate_pairs_during_idle' ):
|
||||
|
||||
search_distance = self._new_options.GetInteger( 'similar_files_duplicate_pairs_search_distance' )
|
||||
|
||||
search_stop_time = stop_time
|
||||
|
@ -956,7 +952,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
# I have had this as 'suppress' before
|
||||
self._app.SetAssertMode( wx.PYAPP_ASSERT_EXCEPTION )
|
||||
|
||||
HydrusData.Print( 'booting controller...' )
|
||||
HydrusData.Print( u'booting controller\u2026' )
|
||||
|
||||
self.CreateSplash()
|
||||
|
||||
|
@ -966,7 +962,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
self._app.MainLoop()
|
||||
|
||||
HydrusData.Print( 'shutting down controller...' )
|
||||
HydrusData.Print( u'shutting down controller\u2026' )
|
||||
|
||||
|
||||
def SaveDirtyObjects( self ):
|
||||
|
@ -1144,11 +1140,11 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
try:
|
||||
|
||||
self.pub( 'splash_set_title_text', 'shutting down gui...' )
|
||||
self.pub( 'splash_set_title_text', u'shutting down gui\u2026' )
|
||||
|
||||
self.ShutdownView()
|
||||
|
||||
self.pub( 'splash_set_title_text', 'shutting down db...' )
|
||||
self.pub( 'splash_set_title_text', u'shutting down db\u2026' )
|
||||
|
||||
self.ShutdownModel()
|
||||
|
||||
|
|
1816
include/ClientDB.py
1816
include/ClientDB.py
File diff suppressed because it is too large
Load Diff
|
@ -353,7 +353,6 @@ def MergePredicates( predicates, add_namespaceless = False ):
|
|||
|
||||
|
||||
|
||||
|
||||
return master_predicate_dict.values()
|
||||
|
||||
def ShowExceptionClient( e ):
|
||||
|
@ -567,8 +566,6 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
self._dictionary[ 'booleans' ][ 'use_system_ffmpeg' ] = False
|
||||
|
||||
self._dictionary[ 'booleans' ][ 'maintain_similar_files_phashes_during_idle' ] = False
|
||||
self._dictionary[ 'booleans' ][ 'maintain_similar_files_tree_during_idle' ] = False
|
||||
self._dictionary[ 'booleans' ][ 'maintain_similar_files_duplicate_pairs_during_idle' ] = False
|
||||
|
||||
#
|
||||
|
@ -600,7 +597,7 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
self._dictionary[ 'noneable_integers' ][ 'disk_cache_maintenance_mb' ] = 256
|
||||
self._dictionary[ 'noneable_integers' ][ 'disk_cache_init_period' ] = 4
|
||||
|
||||
self._dictionary[ 'noneable_integers' ][ 'num_recent_tags' ] = None
|
||||
self._dictionary[ 'noneable_integers' ][ 'num_recent_tags' ] = 20
|
||||
|
||||
self._dictionary[ 'noneable_integers' ][ 'maintenance_vacuum_period_days' ] = 30
|
||||
|
||||
|
@ -668,6 +665,9 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
self._dictionary[ 'media_view' ][ HC.APPLICATION_PDF ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, null_zoom_info )
|
||||
self._dictionary[ 'media_view' ][ HC.APPLICATION_HYDRUS_UPDATE_CONTENT ] = ( CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW, CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW, null_zoom_info )
|
||||
self._dictionary[ 'media_view' ][ HC.APPLICATION_HYDRUS_UPDATE_DEFINITIONS ] = ( CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW, CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW, null_zoom_info )
|
||||
|
||||
self._dictionary[ 'media_view' ][ HC.VIDEO_AVI ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, video_zoom_info )
|
||||
self._dictionary[ 'media_view' ][ HC.VIDEO_FLV ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, video_zoom_info )
|
||||
self._dictionary[ 'media_view' ][ HC.VIDEO_MOV ] = ( CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, CC.MEDIA_VIEWER_ACTION_SHOW_AS_NORMAL, video_zoom_info )
|
||||
|
|
|
@ -51,7 +51,7 @@ def GetClientDefaultOptions():
|
|||
|
||||
regex_favourites = []
|
||||
|
||||
regex_favourites.append( ( r'[1-9]+\d*(?=.{4}$)', r'...0074.jpg -> 74 - [1-9]+\d*(?=.{4}$)' ) )
|
||||
regex_favourites.append( ( r'[1-9]+\d*(?=.{4}$)', u'\u2026' + r'0074.jpg -> 74 - [1-9]+\d*(?=.{4}$)' ) )
|
||||
regex_favourites.append( ( r'[^' + os.path.sep.encode( 'string_escape' ) + ']+*(?=\s-)', r'E:\my collection\author name - v4c1p0074.jpg -> author name - [^' + os.path.sep.encode( 'string_escape' ) + ']+(?=\s-)' ) )
|
||||
|
||||
options[ 'regex_favourites' ] = regex_favourites
|
||||
|
|
|
@ -784,14 +784,20 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
for ( k, v ) in count.items():
|
||||
|
||||
if v > 100: print ( k, v )
|
||||
if v > 100:
|
||||
|
||||
HydrusData.Print( ( k, v ) )
|
||||
|
||||
|
||||
|
||||
HydrusData.Print( 'gc classes:' )
|
||||
|
||||
for ( k, v ) in class_count.items():
|
||||
|
||||
if v > 100: print ( k, v )
|
||||
if v > 100:
|
||||
|
||||
HydrusData.Print( ( k, v ) )
|
||||
|
||||
|
||||
|
||||
HydrusData.Print( 'uncollectable garbage: ' + HydrusData.ToUnicode( gc.garbage ) )
|
||||
|
@ -3281,39 +3287,6 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
self._controller.CallToThread( self._THREADSyncToTagArchive, hta_path, tag_service_key, file_service_key, adding, namespaces, hashes )
|
||||
|
||||
|
||||
'''
|
||||
class FrameComposeMessage( ClientGUITopLevelWindows.Frame ):
|
||||
|
||||
def __init__( self, empty_draft_message ):
|
||||
|
||||
ClientGUITopLevelWindows.Frame.__init__( self, None, HC.app.PrepStringForDisplay( 'Compose Message' ) )
|
||||
|
||||
self.SetInitialSize( ( 920, 600 ) )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
self._draft_panel = ClientGUIMessages.DraftPanel( self, empty_draft_message )
|
||||
|
||||
vbox.AddF( self._draft_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
self.Show( True )
|
||||
|
||||
HC.pubsub.sub( self, 'DeleteConversation', 'delete_conversation_gui' )
|
||||
HC.pubsub.sub( self, 'DeleteDraft', 'delete_draft_gui' )
|
||||
|
||||
|
||||
def DeleteConversation( self, conversation_key ):
|
||||
|
||||
if self._draft_panel.GetConversationKey() == conversation_key: self.Close()
|
||||
|
||||
|
||||
def DeleteDraft( self, draft_key ):
|
||||
|
||||
if draft_key == self._draft_panel.GetDraftKey(): self.Close()
|
||||
|
||||
'''
|
||||
class FrameSplash( wx.Frame ):
|
||||
|
||||
WIDTH = 420
|
||||
|
|
|
@ -102,7 +102,7 @@ class AutoCompleteDropdown( wx.Panel ):
|
|||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
self._cache_text = ''
|
||||
self._cache_text = None
|
||||
self._cached_results = []
|
||||
|
||||
self._initial_matches_fetched = False
|
||||
|
@ -462,8 +462,7 @@ class AutoCompleteDropdown( wx.Panel ):
|
|||
|
||||
def RefreshList( self ):
|
||||
|
||||
self._cache_text = ''
|
||||
self._current_namespace = ''
|
||||
self._cache_text = None
|
||||
|
||||
self._UpdateList()
|
||||
|
||||
|
@ -524,7 +523,6 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
AutoCompleteDropdown.__init__( self, parent )
|
||||
|
||||
self._current_namespace = ''
|
||||
self._current_matches = []
|
||||
|
||||
file_service = HydrusGlobals.client_controller.GetServicesManager().GetService( self._file_service_key )
|
||||
|
@ -532,7 +530,6 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
tag_service = HydrusGlobals.client_controller.GetServicesManager().GetService( self._tag_service_key )
|
||||
|
||||
|
||||
self._file_repo_button = ClientGUICommon.BetterButton( self._dropdown_window, file_service.GetName(), self.FileButtonHit )
|
||||
self._file_repo_button.SetMinSize( ( 20, -1 ) )
|
||||
|
||||
|
@ -577,8 +574,7 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
self._tag_repo_button.SetLabelText( name )
|
||||
|
||||
self._cache_text = ''
|
||||
self._current_namespace = ''
|
||||
self._cache_text = None
|
||||
|
||||
wx.CallAfter( self.RefreshList )
|
||||
|
||||
|
@ -741,7 +737,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
def _BroadcastCurrentText( self ):
|
||||
|
||||
( inclusive, search_text, entry_predicate ) = self._ParseSearchText()
|
||||
( inclusive, search_text, explicit_wildcard, cache_text, entry_predicate ) = self._ParseSearchText()
|
||||
|
||||
try:
|
||||
|
||||
|
@ -790,33 +786,46 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
inclusive = False
|
||||
|
||||
tag = raw_entry[1:]
|
||||
entry_text = raw_entry[1:]
|
||||
|
||||
else:
|
||||
|
||||
inclusive = True
|
||||
|
||||
tag = raw_entry
|
||||
entry_text = raw_entry
|
||||
|
||||
|
||||
tag = HydrusTags.CleanTag( tag )
|
||||
tag = HydrusTags.CleanTag( entry_text )
|
||||
|
||||
search_text = ClientSearch.ConvertTagToSearchable( tag )
|
||||
explicit_wildcard = '*' in entry_text
|
||||
|
||||
siblings_manager = HydrusGlobals.client_controller.GetManager( 'tag_siblings' )
|
||||
search_text = ClientSearch.ConvertEntryTextToSearchText( entry_text )
|
||||
|
||||
sibling = siblings_manager.GetSibling( self._tag_service_key, tag )
|
||||
|
||||
if sibling is None:
|
||||
if explicit_wildcard:
|
||||
|
||||
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive )
|
||||
cache_text = None
|
||||
|
||||
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_WILDCARD, search_text, inclusive )
|
||||
|
||||
else:
|
||||
|
||||
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, sibling, inclusive )
|
||||
cache_text = search_text[:-1] # take off the trailing '*' for the cache text
|
||||
|
||||
siblings_manager = HydrusGlobals.client_controller.GetManager( 'tag_siblings' )
|
||||
|
||||
sibling = siblings_manager.GetSibling( self._tag_service_key, tag )
|
||||
|
||||
if sibling is None:
|
||||
|
||||
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive )
|
||||
|
||||
else:
|
||||
|
||||
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, sibling, inclusive )
|
||||
|
||||
|
||||
|
||||
return ( inclusive, search_text, entry_predicate )
|
||||
return ( inclusive, search_text, explicit_wildcard, cache_text, entry_predicate )
|
||||
|
||||
|
||||
def _GenerateMatches( self ):
|
||||
|
@ -825,21 +834,26 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
num_autocomplete_chars = HC.options[ 'num_autocomplete_chars' ]
|
||||
|
||||
( inclusive, search_text, entry_predicate ) = self._ParseSearchText()
|
||||
( inclusive, search_text, explicit_wildcard, cache_text, entry_predicate ) = self._ParseSearchText()
|
||||
|
||||
if search_text in ( '', ':' ):
|
||||
if search_text in ( '', ':', '*' ):
|
||||
|
||||
input_just_changed = self._cache_text != ''
|
||||
input_just_changed = self._cache_text is not None
|
||||
|
||||
db_not_going_to_hang_if_we_hit_it = not HydrusGlobals.client_controller.DBCurrentlyDoingJob()
|
||||
|
||||
if input_just_changed or db_not_going_to_hang_if_we_hit_it or not self._initial_matches_fetched:
|
||||
|
||||
self._cache_text = ''
|
||||
self._current_namespace = ''
|
||||
self._cache_text = None
|
||||
|
||||
if self._file_service_key == CC.COMBINED_FILE_SERVICE_KEY: search_service_key = self._tag_service_key
|
||||
else: search_service_key = self._file_service_key
|
||||
if self._file_service_key == CC.COMBINED_FILE_SERVICE_KEY:
|
||||
|
||||
search_service_key = self._tag_service_key
|
||||
|
||||
else:
|
||||
|
||||
search_service_key = self._file_service_key
|
||||
|
||||
|
||||
self._cached_results = HydrusGlobals.client_controller.Read( 'file_system_predicates', search_service_key )
|
||||
|
||||
|
@ -848,41 +862,13 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
else:
|
||||
|
||||
must_do_a_search = False
|
||||
|
||||
if '*' in search_text:
|
||||
|
||||
must_do_a_search = True
|
||||
|
||||
|
||||
( namespace, half_complete_subtag ) = HydrusTags.SplitTag( search_text )
|
||||
|
||||
if namespace != '':
|
||||
|
||||
if namespace != self._current_namespace:
|
||||
|
||||
self._current_namespace = namespace # do a new search, no matter what half_complete tag is
|
||||
|
||||
if half_complete_subtag != '': must_do_a_search = True
|
||||
|
||||
else:
|
||||
|
||||
if self._cache_text == self._current_namespace + ':' and half_complete_subtag != '':
|
||||
|
||||
must_do_a_search = True
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
self._current_namespace = namespace
|
||||
|
||||
|
||||
siblings_manager = HydrusGlobals.client_controller.GetManager( 'tag_siblings' )
|
||||
|
||||
if half_complete_subtag == '':
|
||||
if False and half_complete_subtag == '':
|
||||
|
||||
self._cache_text = self._current_namespace + ':'
|
||||
self._cache_text = None
|
||||
|
||||
matches = [] # a query like 'namespace:'
|
||||
|
||||
|
@ -904,20 +890,28 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
if fetch_from_db:
|
||||
|
||||
# if user searches 'blah', then we include 'blah (23)' for 'series:blah (10)', 'blah (13)'
|
||||
# if they search for 'series:blah', then we don't!
|
||||
add_namespaceless = ':' not in namespace
|
||||
|
||||
include_current = self._file_search_context.IncludeCurrentTags()
|
||||
include_pending = self._file_search_context.IncludePendingTags()
|
||||
|
||||
if len( half_complete_subtag ) < num_autocomplete_chars and '*' not in search_text:
|
||||
small_and_specific_search = cache_text is not None and len( cache_text ) < num_autocomplete_chars
|
||||
|
||||
if small_and_specific_search:
|
||||
|
||||
predicates = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, exact_match = True, inclusive = inclusive, include_current = include_current, include_pending = include_pending, add_namespaceless = True, collapse_siblings = True )
|
||||
predicates = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, exact_match = True, inclusive = inclusive, include_current = include_current, include_pending = include_pending, add_namespaceless = add_namespaceless, collapse_siblings = True )
|
||||
|
||||
else:
|
||||
|
||||
if must_do_a_search or self._cache_text == '' or not search_text.startswith( self._cache_text ):
|
||||
cache_invalid_for_this_search = cache_text is None or self._cache_text is None or not cache_text.startswith( self._cache_text )
|
||||
|
||||
if cache_invalid_for_this_search:
|
||||
|
||||
self._cache_text = search_text
|
||||
self._cache_text = cache_text
|
||||
|
||||
self._cached_results = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, inclusive = inclusive, include_current = include_current, include_pending = include_pending, add_namespaceless = True, collapse_siblings = True )
|
||||
self._cached_results = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, inclusive = inclusive, include_current = include_current, include_pending = include_pending, add_namespaceless = add_namespaceless, collapse_siblings = True )
|
||||
|
||||
|
||||
predicates = self._cached_results
|
||||
|
@ -949,7 +943,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
current_tags_flat_iterable = itertools.chain.from_iterable( lists_of_current_tags )
|
||||
|
||||
current_tags_flat = ClientSearch.FilterTagsBySearchEntry( self._tag_service_key, search_text, current_tags_flat_iterable )
|
||||
current_tags_flat = ClientSearch.FilterTagsBySearchText( self._tag_service_key, search_text, current_tags_flat_iterable )
|
||||
|
||||
current_tags_to_count.update( current_tags_flat )
|
||||
|
||||
|
@ -962,7 +956,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
pending_tags_flat_iterable = itertools.chain.from_iterable( lists_of_pending_tags )
|
||||
|
||||
pending_tags_flat = ClientSearch.FilterTagsBySearchEntry( self._tag_service_key, search_text, pending_tags_flat_iterable )
|
||||
pending_tags_flat = ClientSearch.FilterTagsBySearchText( self._tag_service_key, search_text, pending_tags_flat_iterable )
|
||||
|
||||
pending_tags_to_count.update( pending_tags_flat )
|
||||
|
||||
|
@ -976,7 +970,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
predicates = siblings_manager.CollapsePredicates( self._tag_service_key, predicates )
|
||||
|
||||
|
||||
if self._current_namespace == '':
|
||||
if namespace == '':
|
||||
|
||||
predicates = ClientData.MergePredicates( predicates, add_namespaceless = True )
|
||||
|
||||
|
@ -984,32 +978,24 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
self._next_updatelist_is_probably_fast = True
|
||||
|
||||
|
||||
matches = ClientSearch.FilterPredicatesBySearchEntry( self._tag_service_key, search_text, predicates )
|
||||
matches = ClientSearch.FilterPredicatesBySearchText( self._tag_service_key, search_text, predicates )
|
||||
|
||||
matches = ClientSearch.SortPredicates( matches )
|
||||
|
||||
|
||||
if self._include_unusual_predicate_types:
|
||||
|
||||
if self._current_namespace != '':
|
||||
|
||||
if '*' not in self._current_namespace and half_complete_subtag == '':
|
||||
|
||||
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, self._current_namespace, inclusive ) )
|
||||
|
||||
|
||||
if half_complete_subtag != '':
|
||||
|
||||
if '*' in self._current_namespace or ( '*' in half_complete_subtag and half_complete_subtag != '*' ):
|
||||
|
||||
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_WILDCARD, search_text, inclusive ) )
|
||||
|
||||
|
||||
|
||||
elif '*' in search_text:
|
||||
if explicit_wildcard:
|
||||
|
||||
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_WILDCARD, search_text, inclusive ) )
|
||||
|
||||
else:
|
||||
|
||||
if namespace != '' and half_complete_subtag in ( '', '*' ):
|
||||
|
||||
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, namespace, inclusive ) )
|
||||
|
||||
|
||||
|
||||
|
||||
for match in matches:
|
||||
|
@ -1131,7 +1117,16 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
|
|||
|
||||
tag = HydrusTags.CleanTag( raw_entry )
|
||||
|
||||
search_text = ClientSearch.ConvertTagToSearchable( tag )
|
||||
search_text = ClientSearch.ConvertEntryTextToSearchText( raw_entry )
|
||||
|
||||
if ClientSearch.IsComplexWildcard( search_text ):
|
||||
|
||||
cache_text = None
|
||||
|
||||
else:
|
||||
|
||||
cache_text = search_text[:-1] # take off the trailing '*' for the cache text
|
||||
|
||||
|
||||
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag )
|
||||
|
||||
|
@ -1148,12 +1143,12 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
|
|||
sibling_predicate = None
|
||||
|
||||
|
||||
return ( search_text, entry_predicate, sibling_predicate )
|
||||
return ( search_text, cache_text, entry_predicate, sibling_predicate )
|
||||
|
||||
|
||||
def _BroadcastCurrentText( self ):
|
||||
|
||||
( search_text, entry_predicate, sibling_predicate ) = self._ParseSearchText()
|
||||
( search_text, cache_text, entry_predicate, sibling_predicate ) = self._ParseSearchText()
|
||||
|
||||
try:
|
||||
|
||||
|
@ -1173,12 +1168,11 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
|
|||
|
||||
num_autocomplete_chars = HC.options[ 'num_autocomplete_chars' ]
|
||||
|
||||
( search_text, entry_predicate, sibling_predicate ) = self._ParseSearchText()
|
||||
( search_text, cache_text, entry_predicate, sibling_predicate ) = self._ParseSearchText()
|
||||
|
||||
if search_text in ( '', ':' ):
|
||||
if search_text in ( '', ':', '*' ):
|
||||
|
||||
self._cache_text = ''
|
||||
self._current_namespace = ''
|
||||
self._cache_text = None
|
||||
|
||||
matches = []
|
||||
|
||||
|
@ -1186,31 +1180,19 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
|
|||
|
||||
must_do_a_search = False
|
||||
|
||||
( namespace, half_complete_subtag ) = HydrusTags.SplitTag( search_text )
|
||||
small_and_specific_search = cache_text is not None and len( cache_text ) < num_autocomplete_chars
|
||||
|
||||
if namespace != '':
|
||||
|
||||
if half_complete_subtag != '' and namespace != self._current_namespace:
|
||||
|
||||
self._current_namespace = namespace # do a new search, no matter what half_complete tag is
|
||||
|
||||
must_do_a_search = True
|
||||
|
||||
|
||||
else:
|
||||
|
||||
self._current_namespace = namespace
|
||||
|
||||
|
||||
if len( half_complete_subtag ) < num_autocomplete_chars and '*' not in search_text:
|
||||
if small_and_specific_search:
|
||||
|
||||
predicates = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, exact_match = True, add_namespaceless = False, collapse_siblings = False )
|
||||
|
||||
else:
|
||||
|
||||
if must_do_a_search or self._cache_text == '' or not half_complete_subtag.startswith( self._cache_text ):
|
||||
cache_invalid_for_this_search = cache_text is None or self._cache_text is None or not cache_text.startswith( self._cache_text )
|
||||
|
||||
if must_do_a_search or cache_invalid_for_this_search:
|
||||
|
||||
self._cache_text = half_complete_subtag
|
||||
self._cache_text = cache_text
|
||||
|
||||
self._cached_results = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, add_namespaceless = False, collapse_siblings = False )
|
||||
|
||||
|
@ -1220,7 +1202,7 @@ class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
|
|||
self._next_updatelist_is_probably_fast = True
|
||||
|
||||
|
||||
matches = ClientSearch.FilterPredicatesBySearchEntry( self._tag_service_key, half_complete_subtag, predicates )
|
||||
matches = ClientSearch.FilterPredicatesBySearchText( self._tag_service_key, search_text, predicates )
|
||||
|
||||
matches = ClientSearch.SortPredicates( matches )
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ def CalculateCanvasZooms( canvas, media, show_action ):
|
|||
return ( 1.0, 1.0 )
|
||||
|
||||
|
||||
if show_action in ( CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW, CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON ):
|
||||
if show_action in ( CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY, CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW ):
|
||||
|
||||
return ( 1.0, 1.0 )
|
||||
|
||||
|
@ -176,7 +176,7 @@ def CalculateCanvasZooms( canvas, media, show_action ):
|
|||
|
||||
def CalculateMediaContainerSize( media, zoom, action ):
|
||||
|
||||
if action == CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW:
|
||||
if action in ( CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY, CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW ):
|
||||
|
||||
raise Exception( 'This media should not be shown in the media viewer!' )
|
||||
|
||||
|
@ -1165,7 +1165,7 @@ class Canvas( wx.Window ):
|
|||
|
||||
def _IsZoomable( self ):
|
||||
|
||||
return self._GetShowAction( self._current_display_media ) not in ( CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW )
|
||||
return self._GetShowAction( self._current_display_media ) not in ( CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON, CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY, CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW )
|
||||
|
||||
|
||||
def _ManageRatings( self ):
|
||||
|
@ -1546,7 +1546,7 @@ class Canvas( wx.Window ):
|
|||
|
||||
media = None
|
||||
|
||||
elif self._GetShowAction( media.GetDisplayMedia() ) == CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW:
|
||||
elif self._GetShowAction( media.GetDisplayMedia() ) in ( CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY, CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW ):
|
||||
|
||||
media = None
|
||||
|
||||
|
@ -1874,7 +1874,6 @@ class CanvasPanel( Canvas ):
|
|||
elif command == 'copy_bmp': self._CopyBMPToClipboard()
|
||||
elif command == 'copy_files': self._CopyFileToClipboard()
|
||||
elif command == 'copy_hash': self._CopyHashToClipboard( data )
|
||||
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
|
||||
elif command == 'copy_path': self._CopyPathToClipboard()
|
||||
elif command == 'delete': self._Delete( data )
|
||||
elif command == 'inbox': self._Inbox()
|
||||
|
@ -2969,7 +2968,6 @@ class CanvasMediaListBrowser( CanvasMediaListNavigable ):
|
|||
elif command == 'copy_bmp': self._CopyBMPToClipboard()
|
||||
elif command == 'copy_files': self._CopyFileToClipboard()
|
||||
elif command == 'copy_hash': self._CopyHashToClipboard( data )
|
||||
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
|
||||
elif command == 'copy_path': self._CopyPathToClipboard()
|
||||
elif command == 'delete': self._Delete( data )
|
||||
elif command == 'fullscreen_switch': self.GetParent().FullscreenSwitch()
|
||||
|
@ -3410,7 +3408,6 @@ class CanvasMediaListCustomFilter( CanvasMediaListNavigable ):
|
|||
elif command == 'copy_bmp': self._CopyBMPToClipboard()
|
||||
elif command == 'copy_files': self._CopyFileToClipboard()
|
||||
elif command == 'copy_hash': self._CopyHashToClipboard( data )
|
||||
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
|
||||
elif command == 'copy_path': self._CopyPathToClipboard()
|
||||
elif command == 'delete': self._Delete( data )
|
||||
elif command == 'fullscreen_switch': self.GetParent().FullscreenSwitch()
|
||||
|
@ -3673,7 +3670,7 @@ class MediaContainer( wx.Window ):
|
|||
self._embed_button.Hide()
|
||||
|
||||
|
||||
if self._show_action == CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW:
|
||||
if self._show_action in ( CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY, CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW ):
|
||||
|
||||
raise Exception( 'This media should not be shown in the media viewer!' )
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import ClientData
|
|||
import ClientConstants as CC
|
||||
import ClientGUIMenus
|
||||
import ClientRatings
|
||||
import ClientThreading
|
||||
import itertools
|
||||
import os
|
||||
import random
|
||||
|
@ -641,8 +642,8 @@ class ExportPatternButton( wx.Button ):
|
|||
if id == self.ID_HASH: phrase = '{hash}'
|
||||
if id == self.ID_TAGS: phrase = '{tags}'
|
||||
if id == self.ID_NN_TAGS: phrase = '{nn tags}'
|
||||
if id == self.ID_NAMESPACE: phrase = '[...]'
|
||||
if id == self.ID_TAG: phrase = '(...)'
|
||||
if id == self.ID_NAMESPACE: phrase = u'[\u2026]'
|
||||
if id == self.ID_TAG: phrase = u'(\u2026)'
|
||||
else: event.Skip()
|
||||
|
||||
if phrase is not None: HydrusGlobals.client_controller.pub( 'clipboard', 'text', phrase )
|
||||
|
@ -662,11 +663,11 @@ class ExportPatternButton( wx.Button ):
|
|||
|
||||
menu.AppendSeparator()
|
||||
|
||||
menu.Append( self.ID_NAMESPACE, 'all instances of a particular namespace - [...]' )
|
||||
menu.Append( self.ID_NAMESPACE, u'all instances of a particular namespace - [\u2026]' )
|
||||
|
||||
menu.AppendSeparator()
|
||||
|
||||
menu.Append( self.ID_TAG, 'a particular tag, if the file has it - (...)' )
|
||||
menu.Append( self.ID_TAG, u'a particular tag, if the file has it - (\u2026)' )
|
||||
|
||||
HydrusGlobals.client_controller.PopupMenu( self, menu )
|
||||
|
||||
|
@ -1976,6 +1977,18 @@ class PopupMessageManager( wx.Frame ):
|
|||
|
||||
self._timer.Start( 500, wx.TIMER_CONTINUOUS )
|
||||
|
||||
job_key = ClientThreading.JobKey()
|
||||
|
||||
job_key.SetVariable( 'popup_text_1', u'initialising popup message manager\u2026' )
|
||||
|
||||
wx.CallAfter( self.AddMessage, job_key )
|
||||
|
||||
wx.CallAfter( self._Update )
|
||||
|
||||
wx.CallAfter( job_key.Delete )
|
||||
|
||||
wx.CallAfter( self._Update )
|
||||
|
||||
|
||||
def _CheckPending( self ):
|
||||
|
||||
|
@ -2066,11 +2079,23 @@ class PopupMessageManager( wx.Frame ):
|
|||
|
||||
current_focus_tlp = wx.GetTopLevelParent( wx.Window.FindFocus() )
|
||||
|
||||
gui_is_active = current_focus_tlp in ( self, parent )
|
||||
main_gui_is_active = current_focus_tlp in ( self, parent )
|
||||
|
||||
on_top_frame_is_active = False
|
||||
|
||||
if not main_gui_is_active:
|
||||
|
||||
c_f_tlp_is_child_frame_of_main_gui = isinstance( current_focus_tlp, wx.Frame ) and current_focus_tlp.GetParent() == parent
|
||||
|
||||
if c_f_tlp_is_child_frame_of_main_gui and current_focus_tlp.GetWindowStyle() & wx.FRAME_FLOAT_ON_PARENT == wx.FRAME_FLOAT_ON_PARENT:
|
||||
|
||||
on_top_frame_is_active = True
|
||||
|
||||
|
||||
|
||||
if new_options.GetBoolean( 'hide_message_manager_on_gui_deactive' ):
|
||||
|
||||
if gui_is_active:
|
||||
if main_gui_is_active:
|
||||
|
||||
# gui can have focus even while minimised to the taskbar--let's not show in this case
|
||||
if not self.IsShown() and parent.IsIconized():
|
||||
|
@ -2118,7 +2143,7 @@ class PopupMessageManager( wx.Frame ):
|
|||
|
||||
|
||||
# Unhiding tends to raise the main gui tlp, which is annoying if a media viewer window has focus
|
||||
show_is_not_annoying = gui_is_active or self._DisplayingError()
|
||||
show_is_not_annoying = main_gui_is_active or on_top_frame_is_active or self._DisplayingError()
|
||||
|
||||
ok_to_show = show_is_not_annoying and not going_to_bug_out_at_hide_or_show
|
||||
|
||||
|
@ -2160,6 +2185,38 @@ class PopupMessageManager( wx.Frame ):
|
|||
|
||||
|
||||
|
||||
def _Update( self ):
|
||||
|
||||
if HydrusGlobals.view_shutdown:
|
||||
|
||||
self._timer.Stop()
|
||||
|
||||
self.Destroy()
|
||||
|
||||
return
|
||||
|
||||
|
||||
sizer_items = self._message_vbox.GetChildren()
|
||||
|
||||
for sizer_item in sizer_items:
|
||||
|
||||
message_window = sizer_item.GetWindow()
|
||||
|
||||
if message_window.IsDeleted():
|
||||
|
||||
message_window.TryToDismiss()
|
||||
|
||||
break
|
||||
|
||||
else:
|
||||
|
||||
message_window.Update()
|
||||
|
||||
|
||||
|
||||
self._SizeAndPositionAndShow()
|
||||
|
||||
|
||||
def AddMessage( self, job_key ):
|
||||
|
||||
try:
|
||||
|
@ -2247,34 +2304,7 @@ class PopupMessageManager( wx.Frame ):
|
|||
|
||||
try:
|
||||
|
||||
if HydrusGlobals.view_shutdown:
|
||||
|
||||
self._timer.Stop()
|
||||
|
||||
self.Destroy()
|
||||
|
||||
return
|
||||
|
||||
|
||||
sizer_items = self._message_vbox.GetChildren()
|
||||
|
||||
for sizer_item in sizer_items:
|
||||
|
||||
message_window = sizer_item.GetWindow()
|
||||
|
||||
if message_window.IsDeleted():
|
||||
|
||||
message_window.TryToDismiss()
|
||||
|
||||
break
|
||||
|
||||
else:
|
||||
|
||||
message_window.Update()
|
||||
|
||||
|
||||
|
||||
self._SizeAndPositionAndShow()
|
||||
self._Update()
|
||||
|
||||
except wx.PyDeadObjectError:
|
||||
|
||||
|
@ -2850,8 +2880,8 @@ class RegexButton( wx.Button ):
|
|||
submenu.Append( self.ID_REGEX_BACKSPACE, r'backspace character - \\' )
|
||||
submenu.Append( self.ID_REGEX_BEGINNING, r'beginning of line - ^' )
|
||||
submenu.Append( self.ID_REGEX_END, r'end of line - $' )
|
||||
submenu.Append( self.ID_REGEX_SET, r'any of these - [...]' )
|
||||
submenu.Append( self.ID_REGEX_NOT_SET, r'anything other than these - [^...]' )
|
||||
submenu.Append( self.ID_REGEX_SET, u'any of these - [\u2026]' )
|
||||
submenu.Append( self.ID_REGEX_NOT_SET, u'anything other than these - [^\u2026]' )
|
||||
|
||||
submenu.AppendSeparator()
|
||||
|
||||
|
@ -2867,10 +2897,10 @@ class RegexButton( wx.Button ):
|
|||
|
||||
submenu.AppendSeparator()
|
||||
|
||||
submenu.Append( self.ID_REGEX_LOOKAHEAD, r'the next characters are: (non-consuming) - (?=...)' )
|
||||
submenu.Append( self.ID_REGEX_NEGATIVE_LOOKAHEAD, r'the next characters are not: (non-consuming) - (?!...)' )
|
||||
submenu.Append( self.ID_REGEX_LOOKBEHIND, r'the previous characters are: (non-consuming) - (?<=...)' )
|
||||
submenu.Append( self.ID_REGEX_NEGATIVE_LOOKBEHIND, r'the previous characters are not: (non-consuming) - (?<!...)' )
|
||||
submenu.Append( self.ID_REGEX_LOOKAHEAD, u'the next characters are: (non-consuming) - (?=\u2026)' )
|
||||
submenu.Append( self.ID_REGEX_NEGATIVE_LOOKAHEAD, u'the next characters are not: (non-consuming) - (?!\u2026)' )
|
||||
submenu.Append( self.ID_REGEX_LOOKBEHIND, u'the previous characters are: (non-consuming) - (?<=\u2026)' )
|
||||
submenu.Append( self.ID_REGEX_NEGATIVE_LOOKBEHIND, u'the previous characters are not: (non-consuming) - (?<!\u2026)' )
|
||||
|
||||
submenu.AppendSeparator()
|
||||
|
||||
|
@ -2910,8 +2940,8 @@ class RegexButton( wx.Button ):
|
|||
elif id == self.ID_REGEX_BACKSPACE: phrase = r'\\'
|
||||
elif id == self.ID_REGEX_BEGINNING: phrase = r'^'
|
||||
elif id == self.ID_REGEX_END: phrase = r'$'
|
||||
elif id == self.ID_REGEX_SET: phrase = r'[...]'
|
||||
elif id == self.ID_REGEX_NOT_SET: phrase = r'[^...]'
|
||||
elif id == self.ID_REGEX_SET: phrase = u'[\u2026]'
|
||||
elif id == self.ID_REGEX_NOT_SET: phrase = u'[^\u2026]'
|
||||
elif id == self.ID_REGEX_0_OR_MORE_GREEDY: phrase = r'*'
|
||||
elif id == self.ID_REGEX_1_OR_MORE_GREEDY: phrase = r'+'
|
||||
elif id == self.ID_REGEX_0_OR_1_GREEDY: phrase = r'?'
|
||||
|
@ -2921,10 +2951,10 @@ class RegexButton( wx.Button ):
|
|||
elif id == self.ID_REGEX_EXACTLY_M: phrase = r'{m}'
|
||||
elif id == self.ID_REGEX_M_TO_N_GREEDY: phrase = r'{m,n}'
|
||||
elif id == self.ID_REGEX_M_TO_N_MINIMAL: phrase = r'{m,n}?'
|
||||
elif id == self.ID_REGEX_LOOKAHEAD: phrase = r'(?=...)'
|
||||
elif id == self.ID_REGEX_NEGATIVE_LOOKAHEAD: phrase = r'(?!...)'
|
||||
elif id == self.ID_REGEX_LOOKBEHIND: phrase = r'(?<=...)'
|
||||
elif id == self.ID_REGEX_NEGATIVE_LOOKBEHIND: phrase = r'(?<!...)'
|
||||
elif id == self.ID_REGEX_LOOKAHEAD: phrase = u'(?=\u2026)'
|
||||
elif id == self.ID_REGEX_NEGATIVE_LOOKAHEAD: phrase = u'(?!\u2026)'
|
||||
elif id == self.ID_REGEX_LOOKBEHIND: phrase = u'(?<=\u2026)'
|
||||
elif id == self.ID_REGEX_NEGATIVE_LOOKBEHIND: phrase = u'(?<!\u2026)'
|
||||
elif id == self.ID_REGEX_NUMBER_WITHOUT_ZEROES: phrase = r'[1-9]+\d*'
|
||||
elif id == self.ID_REGEX_FILENAME: phrase = '(?<=' + os.path.sep.encode( 'string_escape' ) + r')[^' + os.path.sep.encode( 'string_escape' ) + ']*?(?=\..*$)'
|
||||
elif id == self.ID_REGEX_MANAGE_FAVOURITES:
|
||||
|
@ -3378,161 +3408,6 @@ class SaneListCtrlForSingleObject( SaneListCtrl ):
|
|||
self._objects_to_data_indices[ obj ] = data_index
|
||||
|
||||
|
||||
class SeedCacheControl( SaneListCtrlForSingleObject ):
|
||||
|
||||
def __init__( self, parent, seed_cache ):
|
||||
|
||||
height = 300
|
||||
columns = [ ( 'source', -1 ), ( 'status', 90 ), ( 'added', 150 ), ( 'last modified', 150 ), ( 'note', 200 ) ]
|
||||
|
||||
SaneListCtrlForSingleObject.__init__( self, parent, height, columns )
|
||||
|
||||
self._seed_cache = seed_cache
|
||||
|
||||
for seed in self._seed_cache.GetSeeds():
|
||||
|
||||
self._AddSeed( seed )
|
||||
|
||||
|
||||
self.Bind( wx.EVT_MENU, self.EventMenu )
|
||||
self.Bind( wx.EVT_RIGHT_DOWN, self.EventShowMenu )
|
||||
|
||||
HydrusGlobals.client_controller.sub( self, 'NotifySeedUpdated', 'seed_cache_seed_updated' )
|
||||
|
||||
|
||||
def _AddSeed( self, seed ):
|
||||
|
||||
sort_tuple = self._seed_cache.GetSeedInfo( seed )
|
||||
|
||||
( display_tuple, sort_tuple ) = self._GetListCtrlTuples( seed )
|
||||
|
||||
self.Append( display_tuple, sort_tuple, seed )
|
||||
|
||||
|
||||
def _GetListCtrlTuples( self, seed ):
|
||||
|
||||
sort_tuple = self._seed_cache.GetSeedInfo( seed )
|
||||
|
||||
( seed, status, added_timestamp, last_modified_timestamp, note ) = sort_tuple
|
||||
|
||||
pretty_seed = HydrusData.ToUnicode( seed )
|
||||
pretty_status = CC.status_string_lookup[ status ]
|
||||
pretty_added = HydrusData.ConvertTimestampToPrettyAgo( added_timestamp )
|
||||
pretty_modified = HydrusData.ConvertTimestampToPrettyAgo( last_modified_timestamp )
|
||||
pretty_note = note.split( os.linesep )[0]
|
||||
|
||||
display_tuple = ( pretty_seed, pretty_status, pretty_added, pretty_modified, pretty_note )
|
||||
|
||||
return ( display_tuple, sort_tuple )
|
||||
|
||||
|
||||
def _CopySelectedNotes( self ):
|
||||
|
||||
notes = []
|
||||
|
||||
for seed in self.GetObjects( only_selected = True ):
|
||||
|
||||
( seed, status, added_timestamp, last_modified_timestamp, note ) = self._seed_cache.GetSeedInfo( seed )
|
||||
|
||||
if note != '':
|
||||
|
||||
notes.append( note )
|
||||
|
||||
|
||||
|
||||
if len( notes ) > 0:
|
||||
|
||||
separator = os.linesep * 2
|
||||
|
||||
text = separator.join( notes )
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'clipboard', 'text', text )
|
||||
|
||||
|
||||
|
||||
def _CopySelectedSeeds( self ):
|
||||
|
||||
seeds = self.GetObjects( only_selected = True )
|
||||
|
||||
if len( seeds ) > 0:
|
||||
|
||||
separator = os.linesep * 2
|
||||
|
||||
text = separator.join( seeds )
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'clipboard', 'text', text )
|
||||
|
||||
|
||||
|
||||
def _SetSelected( self, status_to_set ):
|
||||
|
||||
seeds_to_reset = self.GetObjects( only_selected = True )
|
||||
|
||||
for seed in seeds_to_reset:
|
||||
|
||||
self._seed_cache.UpdateSeedStatus( seed, status_to_set )
|
||||
|
||||
|
||||
|
||||
def EventMenu( self, event ):
|
||||
|
||||
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
|
||||
|
||||
if action is not None:
|
||||
|
||||
( command, data ) = action
|
||||
|
||||
if command == 'copy_seed_notes': self._CopySelectedNotes()
|
||||
elif command == 'copy_seeds': self._CopySelectedSeeds()
|
||||
elif command == 'set_seed_unknown': self._SetSelected( CC.STATUS_UNKNOWN )
|
||||
elif command == 'set_seed_skipped': self._SetSelected( CC.STATUS_SKIPPED )
|
||||
else: event.Skip()
|
||||
|
||||
|
||||
|
||||
def EventShowMenu( self, event ):
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_seeds' ), 'copy sources' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_seed_notes' ), 'copy notes' )
|
||||
|
||||
menu.AppendSeparator()
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'set_seed_skipped' ), 'skip' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'set_seed_unknown' ), 'try again' )
|
||||
|
||||
HydrusGlobals.client_controller.PopupMenu( self, menu )
|
||||
|
||||
|
||||
def NotifySeedUpdated( self, seed ):
|
||||
|
||||
if self._seed_cache.HasSeed( seed ):
|
||||
|
||||
if self.HasObject( seed ):
|
||||
|
||||
index = self.GetIndexFromObject( seed )
|
||||
|
||||
( display_tuple, sort_tuple ) = self._GetListCtrlTuples( seed )
|
||||
|
||||
self.UpdateRow( index, display_tuple, sort_tuple, seed )
|
||||
|
||||
else:
|
||||
|
||||
self._AddSeed( seed )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if self.HasObject( seed ):
|
||||
|
||||
index = self.GetIndexFromObject( seed )
|
||||
|
||||
self.DeleteItem( index )
|
||||
|
||||
|
||||
|
||||
|
||||
class Shortcut( wx.TextCtrl ):
|
||||
|
||||
def __init__( self, parent, modifier = wx.ACCEL_NORMAL, key = wx.WXK_F7 ):
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
import ClientCaches
|
||||
import ClientConstants as CC
|
||||
import ClientGUICommon
|
||||
import ClientGUIDialogs
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUITopLevelWindows
|
||||
import HydrusConstants as HC
|
||||
import HydrusData
|
||||
import HydrusGlobals
|
||||
import HydrusNetworking
|
||||
import os
|
||||
import wx
|
||||
|
||||
class BandwidthRulesCtrl( ClientGUICommon.StaticBox ):
|
||||
|
@ -219,3 +223,192 @@ class BandwidthRulesCtrl( ClientGUICommon.StaticBox ):
|
|||
return ( bandwidth_type, time_delta, max_allowed )
|
||||
|
||||
|
||||
|
||||
class SeedCacheControl( ClientGUICommon.SaneListCtrlForSingleObject ):
|
||||
|
||||
def __init__( self, parent, seed_cache ):
|
||||
|
||||
height = 300
|
||||
columns = [ ( 'source', -1 ), ( 'status', 90 ), ( 'added', 150 ), ( 'last modified', 150 ), ( 'note', 200 ) ]
|
||||
|
||||
ClientGUICommon.SaneListCtrlForSingleObject.__init__( self, parent, height, columns )
|
||||
|
||||
self._seed_cache = seed_cache
|
||||
|
||||
for seed in self._seed_cache.GetSeeds():
|
||||
|
||||
self._AddSeed( seed )
|
||||
|
||||
|
||||
self.Bind( wx.EVT_MENU, self.EventMenu )
|
||||
self.Bind( wx.EVT_RIGHT_DOWN, self.EventShowMenu )
|
||||
|
||||
HydrusGlobals.client_controller.sub( self, 'NotifySeedUpdated', 'seed_cache_seed_updated' )
|
||||
|
||||
|
||||
def _AddSeed( self, seed ):
|
||||
|
||||
sort_tuple = self._seed_cache.GetSeedInfo( seed )
|
||||
|
||||
( display_tuple, sort_tuple ) = self._GetListCtrlTuples( seed )
|
||||
|
||||
self.Append( display_tuple, sort_tuple, seed )
|
||||
|
||||
|
||||
def _GetListCtrlTuples( self, seed ):
|
||||
|
||||
sort_tuple = self._seed_cache.GetSeedInfo( seed )
|
||||
|
||||
( seed, status, added_timestamp, last_modified_timestamp, note ) = sort_tuple
|
||||
|
||||
pretty_seed = HydrusData.ToUnicode( seed )
|
||||
pretty_status = CC.status_string_lookup[ status ]
|
||||
pretty_added = HydrusData.ConvertTimestampToPrettyAgo( added_timestamp )
|
||||
pretty_modified = HydrusData.ConvertTimestampToPrettyAgo( last_modified_timestamp )
|
||||
pretty_note = note.split( os.linesep )[0]
|
||||
|
||||
display_tuple = ( pretty_seed, pretty_status, pretty_added, pretty_modified, pretty_note )
|
||||
|
||||
return ( display_tuple, sort_tuple )
|
||||
|
||||
|
||||
def _CopySelectedNotes( self ):
|
||||
|
||||
notes = []
|
||||
|
||||
for seed in self.GetObjects( only_selected = True ):
|
||||
|
||||
( seed, status, added_timestamp, last_modified_timestamp, note ) = self._seed_cache.GetSeedInfo( seed )
|
||||
|
||||
if note != '':
|
||||
|
||||
notes.append( note )
|
||||
|
||||
|
||||
|
||||
if len( notes ) > 0:
|
||||
|
||||
separator = os.linesep * 2
|
||||
|
||||
text = separator.join( notes )
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'clipboard', 'text', text )
|
||||
|
||||
|
||||
|
||||
def _CopySelectedSeeds( self ):
|
||||
|
||||
seeds = self.GetObjects( only_selected = True )
|
||||
|
||||
if len( seeds ) > 0:
|
||||
|
||||
separator = os.linesep * 2
|
||||
|
||||
text = separator.join( seeds )
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'clipboard', 'text', text )
|
||||
|
||||
|
||||
|
||||
def _DeleteSelected( self ):
|
||||
|
||||
seeds_to_delete = self.GetObjects( only_selected = True )
|
||||
|
||||
for seed in seeds_to_delete:
|
||||
|
||||
self._seed_cache.RemoveSeed( seed )
|
||||
|
||||
|
||||
|
||||
def _SetSelected( self, status_to_set ):
|
||||
|
||||
seeds_to_reset = self.GetObjects( only_selected = True )
|
||||
|
||||
for seed in seeds_to_reset:
|
||||
|
||||
self._seed_cache.UpdateSeedStatus( seed, status_to_set )
|
||||
|
||||
|
||||
|
||||
def EventMenu( self, event ):
|
||||
|
||||
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
|
||||
|
||||
if action is not None:
|
||||
|
||||
( command, data ) = action
|
||||
|
||||
if command == 'copy_seed_notes': self._CopySelectedNotes()
|
||||
elif command == 'copy_seeds': self._CopySelectedSeeds()
|
||||
elif command == 'delete_seeds':
|
||||
|
||||
message = 'Are you sure you want to delete all the selected entries?'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
self._DeleteSelected()
|
||||
|
||||
|
||||
|
||||
elif command == 'set_seed_unknown': self._SetSelected( CC.STATUS_UNKNOWN )
|
||||
elif command == 'set_seed_skipped': self._SetSelected( CC.STATUS_SKIPPED )
|
||||
else: event.Skip()
|
||||
|
||||
|
||||
|
||||
def _ShowMenuIfNeeded( self ):
|
||||
|
||||
if self.GetSelectedItemCount() > 0:
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_seeds' ), 'copy sources' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_seed_notes' ), 'copy notes' )
|
||||
|
||||
menu.AppendSeparator()
|
||||
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'set_seed_unknown' ), 'try again' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'set_seed_skipped' ), 'skip' )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'delete_seeds' ), 'delete' )
|
||||
|
||||
HydrusGlobals.client_controller.PopupMenu( self, menu )
|
||||
|
||||
|
||||
|
||||
def EventShowMenu( self, event ):
|
||||
|
||||
wx.CallAfter( self._ShowMenuIfNeeded )
|
||||
|
||||
event.Skip() # let the right click event go through before doing menu, in case selection should happen
|
||||
|
||||
|
||||
def NotifySeedUpdated( self, seed ):
|
||||
|
||||
if self._seed_cache.HasSeed( seed ):
|
||||
|
||||
if self.HasObject( seed ):
|
||||
|
||||
index = self.GetIndexFromObject( seed )
|
||||
|
||||
( display_tuple, sort_tuple ) = self._GetListCtrlTuples( seed )
|
||||
|
||||
self.UpdateRow( index, display_tuple, sort_tuple, seed )
|
||||
|
||||
else:
|
||||
|
||||
self._AddSeed( seed )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if self.HasObject( seed ):
|
||||
|
||||
index = self.GetIndexFromObject( seed )
|
||||
|
||||
self.DeleteItem( index )
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4318,8 +4318,6 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
self._tag_siblings.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventItemSelected )
|
||||
self._tag_siblings.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventItemSelected )
|
||||
|
||||
removed_callable = lambda tags: 1
|
||||
|
||||
self._old_siblings = ClientGUIListBoxes.ListBoxTagsStringsAddRemove( self, self._service_key, show_sibling_text = False )
|
||||
self._new_sibling = wx.StaticText( self )
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import ClientGUICommon
|
|||
import ClientGUIDialogs
|
||||
import ClientGUIListBoxes
|
||||
import ClientGUIMedia
|
||||
import ClientGUIMenus
|
||||
import ClientGUIScrolledPanelsEdit
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientImporting
|
||||
|
@ -77,6 +78,8 @@ def CreateManagementControllerDuplicateFilter():
|
|||
|
||||
management_controller = CreateManagementController( MANAGEMENT_TYPE_DUPLICATE_FILTER )
|
||||
|
||||
management_controller.SetKey( 'duplicate_filter_file_domain', CC.LOCAL_FILE_SERVICE_KEY )
|
||||
|
||||
return management_controller
|
||||
|
||||
def CreateManagementControllerImportGallery( gallery_identifier ):
|
||||
|
@ -537,11 +540,21 @@ class ManagementController( HydrusSerialisable.SerialisableBase ):
|
|||
return ( self._management_type, serialisable_keys, serialisable_simples, serialisable_serialisables )
|
||||
|
||||
|
||||
def _InitialiseDefaults( self ):
|
||||
|
||||
if self._management_type == MANAGEMENT_TYPE_DUPLICATE_FILTER:
|
||||
|
||||
self._keys[ 'duplicate_filter_file_domain' ] = CC.LOCAL_FILE_SERVICE_KEY
|
||||
|
||||
|
||||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
( self._management_type, serialisable_keys, serialisable_simples, serialisables ) = serialisable_info
|
||||
|
||||
self._keys = { name : key.decode( 'hex' ) for ( name, key ) in serialisable_keys.items() }
|
||||
self._InitialiseDefaults()
|
||||
|
||||
self._keys.update( { name : key.decode( 'hex' ) for ( name, key ) in serialisable_keys.items() } )
|
||||
|
||||
if 'file_service' in self._keys:
|
||||
|
||||
|
@ -551,9 +564,9 @@ class ManagementController( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
self._simples = dict( serialisable_simples )
|
||||
self._simples.update( dict( serialisable_simples ) )
|
||||
|
||||
self._serialisables = { name : HydrusSerialisable.CreateFromSerialisableTuple( value ) for ( name, value ) in serialisables.items() }
|
||||
self._serialisables.update( { name : HydrusSerialisable.CreateFromSerialisableTuple( value ) for ( name, value ) in serialisables.items() } )
|
||||
|
||||
|
||||
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
|
||||
|
@ -627,6 +640,8 @@ class ManagementController( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
self._management_type = management_type
|
||||
|
||||
self._InitialiseDefaults()
|
||||
|
||||
|
||||
def SetVariable( self, name, value ):
|
||||
|
||||
|
@ -715,9 +730,7 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
menu_items.append( ( 'normal', 'refresh', 'This panel does not update itself when files are added or deleted elsewhere in the client. Hitting this will refresh the numbers from the database.', self._RefreshAndUpdateStatus ) )
|
||||
menu_items.append( ( 'normal', 'reset potential duplicates', 'This will delete all the potential duplicate pairs found so far and reset their files\' search status.', self._ResetUnknown ) )
|
||||
menu_items.append( ( 'separator', 0, 0, 0 ) )
|
||||
menu_items.append( ( 'check', 'regenerate file information in normal db maintenance', 'Tell the client to include file phash regeneration in its normal db maintenance cycles, whether you have that set to idle or shutdown time.', 'maintain_similar_files_phashes_during_idle' ) )
|
||||
menu_items.append( ( 'check', 'rebalance tree in normal db maintenance', 'Tell the client to balance the tree in its normal db maintenance cycles, whether you have that set to idle or shutdown time. It will not occur whille there are phashes still to regenerate.', 'maintain_similar_files_tree_during_idle' ) )
|
||||
menu_items.append( ( 'check', 'find duplicate pairs at the current distance in normal db maintenance', 'Tell the client to find duplicate pairs in its normal db maintenance cycles, whether you have that set to idle or shutdown time. It will not occur whille there are phashes still to regenerate or if the tree still needs rebalancing.', 'maintain_similar_files_duplicate_pairs_during_idle' ) )
|
||||
menu_items.append( ( 'check', 'search for duplicate pairs at the current distance during normal db maintenance', 'Tell the client to find duplicate pairs in its normal db maintenance cycles, whether you have that set to idle or shutdown time.', 'maintain_similar_files_duplicate_pairs_during_idle' ) )
|
||||
|
||||
self._cog_button = ClientGUICommon.MenuBitmapButton( self, CC.GlobalBMPs.cog, menu_items )
|
||||
|
||||
|
@ -755,6 +768,7 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
self._filtering_panel = ClientGUICommon.StaticBox( self, 'filtering' )
|
||||
|
||||
self._file_domain_button = ClientGUICommon.BetterButton( self._filtering_panel, 'file domain', self._FileDomainButtonHit )
|
||||
self._num_unknown_duplicates = wx.StaticText( self._filtering_panel )
|
||||
self._num_same_file_duplicates = wx.StaticText( self._filtering_panel )
|
||||
self._num_alternate_duplicates = wx.StaticText( self._filtering_panel )
|
||||
|
@ -766,6 +780,10 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
self._search_distance_spinctrl.SetValue( new_options.GetInteger( 'similar_files_duplicate_pairs_search_distance' ) )
|
||||
|
||||
duplicate_filter_file_domain = management_controller.GetKey( 'duplicate_filter_file_domain' )
|
||||
|
||||
self._SetFileDomain( duplicate_filter_file_domain ) # this spawns a refreshandupdatestatus
|
||||
|
||||
#
|
||||
|
||||
gridbox_1 = wx.FlexGridSizer( 0, 3 )
|
||||
|
@ -801,6 +819,7 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
#
|
||||
|
||||
self._filtering_panel.AddF( self._file_domain_button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._filtering_panel.AddF( self._num_unknown_duplicates, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._filtering_panel.AddF( self._num_same_file_duplicates, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._filtering_panel.AddF( self._num_alternate_duplicates, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
@ -820,9 +839,27 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
self.Bind( wx.EVT_TIMER, self.TIMEREventUpdateDBJob, id = ID_TIMER_UPDATE )
|
||||
self._update_db_job_timer = wx.Timer( self, id = ID_TIMER_UPDATE )
|
||||
|
||||
#
|
||||
|
||||
def _FileDomainButtonHit( self ):
|
||||
|
||||
self._RefreshAndUpdateStatus()
|
||||
services_manager = HydrusGlobals.client_controller.GetServicesManager()
|
||||
|
||||
services = []
|
||||
|
||||
services.append( services_manager.GetService( CC.LOCAL_FILE_SERVICE_KEY ) )
|
||||
services.append( services_manager.GetService( CC.TRASH_SERVICE_KEY ) )
|
||||
services.append( services_manager.GetService( CC.COMBINED_LOCAL_FILE_SERVICE_KEY ) )
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
for service in services:
|
||||
|
||||
call = HydrusData.Call( self._SetFileDomain, service.GetServiceKey() )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( self, menu, service.GetName(), 'Set the filtering file domain.', call )
|
||||
|
||||
|
||||
HydrusGlobals.client_controller.PopupMenu( self._file_domain_button, menu )
|
||||
|
||||
|
||||
def _RebalanceTree( self ):
|
||||
|
@ -862,6 +899,19 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
self._StartStopDBJob()
|
||||
|
||||
|
||||
def _SetFileDomain( self, service_key ):
|
||||
|
||||
self._management_controller.SetKey( 'duplicate_filter_file_domain', service_key )
|
||||
|
||||
services_manager = HydrusGlobals.client_controller.GetServicesManager()
|
||||
|
||||
service = services_manager.GetService( service_key )
|
||||
|
||||
self._file_domain_button.SetLabelText( service.GetName() )
|
||||
|
||||
self._RefreshAndUpdateStatus()
|
||||
|
||||
|
||||
def _SetSearchDistance( self, value ):
|
||||
|
||||
self._search_distance_spinctrl.SetValue( value )
|
||||
|
@ -871,7 +921,9 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
def _ShowSomeDupes( self ):
|
||||
|
||||
hashes = self._controller.Read( 'some_dupes' )
|
||||
duplicate_filter_file_domain = self._management_controller.GetKey( 'duplicate_filter_file_domain' )
|
||||
|
||||
hashes = self._controller.Read( 'some_dupes', duplicate_filter_file_domain )
|
||||
|
||||
media_results = self._controller.Read( 'media_results', hashes )
|
||||
|
||||
|
@ -926,6 +978,15 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
|
||||
|
||||
def _RefreshAndUpdateStatus( self ):
|
||||
|
||||
duplicate_filter_file_domain = self._management_controller.GetKey( 'duplicate_filter_file_domain' )
|
||||
|
||||
self._similar_files_maintenance_status = self._controller.Read( 'similar_files_maintenance_status', duplicate_filter_file_domain )
|
||||
|
||||
self._UpdateStatus()
|
||||
|
||||
|
||||
def _UpdateJob( self ):
|
||||
|
||||
if self._job_key.TimeRunning() > 30:
|
||||
|
@ -982,16 +1043,9 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
|
||||
|
||||
def _RefreshAndUpdateStatus( self ):
|
||||
|
||||
self._similar_files_maintenance_status = self._controller.Read( 'similar_files_maintenance_status' )
|
||||
|
||||
self._UpdateStatus()
|
||||
|
||||
|
||||
def _UpdateStatus( self ):
|
||||
|
||||
( searched_distances_to_count, duplicate_types_to_count, num_phashes_to_regen, num_branches_to_regen ) = self._similar_files_maintenance_status
|
||||
( num_phashes_to_regen, num_branches_to_regen, searched_distances_to_count, duplicate_types_to_count ) = self._similar_files_maintenance_status
|
||||
|
||||
self._cog_button.Enable()
|
||||
|
||||
|
|
|
@ -489,7 +489,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
media_show_action = new_options.GetMediaShowAction( display_media.GetMime() )
|
||||
|
||||
if media_show_action == CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW:
|
||||
if media_show_action == CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY:
|
||||
|
||||
hash = display_media.GetHash()
|
||||
mime = display_media.GetMime()
|
||||
|
@ -502,6 +502,10 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
return
|
||||
|
||||
elif media_show_action == CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
media_results = self.GenerateMediaResults( discriminant = CC.DISCRIMINANT_LOCAL, for_media_viewer = True )
|
||||
|
@ -2129,7 +2133,6 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
elif command == 'copy_hash': self._CopyHashToClipboard( data )
|
||||
elif command == 'copy_hashes': self._CopyHashesToClipboard( data )
|
||||
elif command == 'copy_known_urls': self._CopyKnownURLsToClipboard()
|
||||
elif command == 'copy_local_url': self._CopyLocalUrlToClipboard()
|
||||
elif command == 'copy_hashes': self._CopyHashesToClipboard( data )
|
||||
elif command == 'copy_service_filename': self._CopyServiceFilenameToClipboard( data )
|
||||
elif command == 'copy_service_filenames': self._CopyServiceFilenamesToClipboard( data )
|
||||
|
|
|
@ -65,9 +65,9 @@ def AppendMenuLabel( menu, label, description = None ):
|
|||
|
||||
def BindMenuItem( event_handler, menu, menu_item, callable, *args, **kwargs ):
|
||||
|
||||
l_callable = GetLambdaCallable( callable, *args, **kwargs )
|
||||
event_callable = GetEventCallable( callable, *args, **kwargs )
|
||||
|
||||
event_handler.Bind( wx.EVT_MENU, l_callable, source = menu_item )
|
||||
event_handler.Bind( wx.EVT_MENU, event_callable, source = menu_item )
|
||||
|
||||
menus_to_menu_item_data[ menu ].add( ( menu_item, event_handler ) )
|
||||
|
||||
|
@ -101,11 +101,14 @@ def DestroyMenu( menu ):
|
|||
|
||||
menu.Destroy()
|
||||
|
||||
def GetLambdaCallable( callable, *args, **kwargs ):
|
||||
def GetEventCallable( callable, *args, **kwargs ):
|
||||
|
||||
l_callable = lambda event: callable( *args, **kwargs )
|
||||
def event_callable( event ):
|
||||
|
||||
callable( *args, **kwargs )
|
||||
|
||||
|
||||
return l_callable
|
||||
return event_callable
|
||||
|
||||
def SanitiseLabel( label ):
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,12 @@
|
|||
import ClientConstants as CC
|
||||
import ClientGUICommon
|
||||
import ClientGUIDialogs
|
||||
import ClientThreading
|
||||
import HydrusConstants as HC
|
||||
import HydrusData
|
||||
import HydrusExceptions
|
||||
import HydrusGlobals
|
||||
import HydrusNATPunch
|
||||
import HydrusNetwork
|
||||
import HydrusPaths
|
||||
import os
|
||||
|
@ -87,36 +89,7 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
service_info = self._controller.Read( 'service_info', self._service_key )
|
||||
|
||||
if service_type in HC.FILE_SERVICES:
|
||||
|
||||
num_files = service_info[ HC.SERVICE_INFO_NUM_FILES ]
|
||||
total_size = service_info[ HC.SERVICE_INFO_TOTAL_SIZE ]
|
||||
|
||||
self._files_text.SetLabelText( HydrusData.ConvertIntToPrettyString( num_files ) + ' files, totalling ' + HydrusData.ConvertIntToBytes( total_size ) )
|
||||
|
||||
if service_type in ( HC.COMBINED_LOCAL_FILE, HC.FILE_REPOSITORY ):
|
||||
|
||||
num_deleted_files = service_info[ HC.SERVICE_INFO_NUM_DELETED_FILES ]
|
||||
|
||||
self._deleted_files_text.SetLabelText( HydrusData.ConvertIntToPrettyString( num_deleted_files ) + ' deleted files' )
|
||||
|
||||
|
||||
elif service_type in HC.TAG_SERVICES:
|
||||
|
||||
num_files = service_info[ HC.SERVICE_INFO_NUM_FILES ]
|
||||
num_tags = service_info[ HC.SERVICE_INFO_NUM_TAGS ]
|
||||
num_mappings = service_info[ HC.SERVICE_INFO_NUM_MAPPINGS ]
|
||||
|
||||
self._tags_text.SetLabelText( HydrusData.ConvertIntToPrettyString( num_files ) + ' hashes, ' + HydrusData.ConvertIntToPrettyString( num_tags ) + ' tags, totalling ' + HydrusData.ConvertIntToPrettyString( num_mappings ) + ' mappings' )
|
||||
|
||||
if service_type == HC.TAG_REPOSITORY:
|
||||
|
||||
num_deleted_mappings = service_info[ HC.SERVICE_INFO_NUM_DELETED_MAPPINGS ]
|
||||
|
||||
self._deleted_tags_text.SetLabelText( HydrusData.ConvertIntToPrettyString( num_deleted_mappings ) + ' deleted mappings' )
|
||||
|
||||
|
||||
elif service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ):
|
||||
if service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ):
|
||||
|
||||
num_ratings = service_info[ HC.SERVICE_INFO_NUM_FILES ]
|
||||
|
||||
|
@ -510,7 +483,7 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
self._my_updater = ClientGUICommon.ThreadToGUIUpdater( self, self._Refresh )
|
||||
|
||||
self._name_and_type = wx.StaticText( self )
|
||||
self._file_info_st = wx.StaticText( self )
|
||||
|
||||
#
|
||||
|
||||
|
@ -518,16 +491,14 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
#
|
||||
|
||||
self.AddF( self._name_and_type, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self.AddF( self._file_info_st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
HydrusGlobals.client_controller.sub( self, 'Update', 'service_updated' )
|
||||
|
||||
|
||||
def _Refresh( self ):
|
||||
|
||||
# put this fetch on a thread, since it'll have to go to the db
|
||||
|
||||
self._name_and_type.SetLabelText( 'This service has files. This box will regain its old information in a later version.' )
|
||||
HydrusGlobals.client_controller.CallToThread( self.THREADUpdateTagInfo )
|
||||
|
||||
|
||||
def Update( self, service ):
|
||||
|
@ -540,6 +511,25 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
|
||||
|
||||
def THREADUpdateTagInfo( self ):
|
||||
|
||||
service_info = HydrusGlobals.client_controller.Read( 'service_info', self._service.GetServiceKey() )
|
||||
|
||||
num_files = service_info[ HC.SERVICE_INFO_NUM_FILES ]
|
||||
total_size = service_info[ HC.SERVICE_INFO_TOTAL_SIZE ]
|
||||
|
||||
text = HydrusData.ConvertIntToPrettyString( num_files ) + ' files, totalling ' + HydrusData.ConvertIntToBytes( total_size )
|
||||
|
||||
if self._service.GetServiceType() in ( HC.COMBINED_LOCAL_FILE, HC.FILE_REPOSITORY ):
|
||||
|
||||
num_deleted_files = service_info[ HC.SERVICE_INFO_NUM_DELETED_FILES ]
|
||||
|
||||
text += ' - ' + HydrusData.ConvertIntToPrettyString( num_deleted_files ) + ' deleted files'
|
||||
|
||||
|
||||
wx.CallAfter( self._file_info_st.SetLabelText, text )
|
||||
|
||||
|
||||
|
||||
class _ServiceRemotePanel( ClientGUICommon.StaticBox ):
|
||||
|
||||
|
@ -777,7 +767,7 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
|
||||
self._refresh_account_button.Disable()
|
||||
self._refresh_account_button.SetLabelText( 'fetching...' )
|
||||
self._refresh_account_button.SetLabelText( u'fetching\u2026' )
|
||||
|
||||
HydrusGlobals.client_controller.CallToThread( do_it )
|
||||
|
||||
|
@ -805,10 +795,12 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
self._content_panel = wx.Panel( self )
|
||||
|
||||
self._metadata_st = wx.StaticText( self )
|
||||
|
||||
self._download_progress = ClientGUICommon.TextAndGauge( self )
|
||||
self._processing_progress = ClientGUICommon.TextAndGauge( self )
|
||||
|
||||
self._sync_now_button = ClientGUICommon.BetterButton( self, 'sync now', self._SyncNow )
|
||||
self._sync_now_button = ClientGUICommon.BetterButton( self, 'process now', self._SyncNow )
|
||||
self._pause_play_button = ClientGUICommon.BetterButton( self, 'pause', self._PausePlay )
|
||||
self._export_updates_button = ClientGUICommon.BetterButton( self, 'export updates', self._ExportUpdates )
|
||||
|
||||
|
@ -824,6 +816,7 @@ class ReviewServicePanel( wx.Panel ):
|
|||
hbox.AddF( self._pause_play_button, CC.FLAGS_LONE_BUTTON )
|
||||
hbox.AddF( self._export_updates_button, CC.FLAGS_LONE_BUTTON )
|
||||
|
||||
self.AddF( self._metadata_st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self.AddF( self._download_progress, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self.AddF( self._processing_progress, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self.AddF( hbox, CC.FLAGS_BUTTON_SIZER )
|
||||
|
@ -909,7 +902,7 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
path = HydrusData.ToUnicode( dlg.GetPath() )
|
||||
|
||||
self._export_updates_button.SetLabelText( 'exporting...' )
|
||||
self._export_updates_button.SetLabelText( u'exporting\u2026' )
|
||||
self._export_updates_button.Disable()
|
||||
|
||||
HydrusGlobals.client_controller.CallToThread( do_it, path )
|
||||
|
@ -948,6 +941,8 @@ class ReviewServicePanel( wx.Panel ):
|
|||
self._pause_play_button.SetLabelText( 'pause' )
|
||||
|
||||
|
||||
self._metadata_st.SetLabelText( self._service.GetNextUpdateDueString() )
|
||||
|
||||
HydrusGlobals.client_controller.CallToThread( self.THREADFetchUpdateProgress )
|
||||
|
||||
|
||||
|
@ -1118,7 +1113,7 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
self._my_updater = ClientGUICommon.ThreadToGUIUpdater( self, self._Refresh )
|
||||
|
||||
self._name_and_type = wx.StaticText( self )
|
||||
self._tag_info_st = wx.StaticText( self )
|
||||
|
||||
#
|
||||
|
||||
|
@ -1126,16 +1121,14 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
#
|
||||
|
||||
self.AddF( self._name_and_type, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self.AddF( self._tag_info_st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
HydrusGlobals.client_controller.sub( self, 'Update', 'service_updated' )
|
||||
|
||||
|
||||
def _Refresh( self ):
|
||||
|
||||
# put this fetch on a thread, since it'll have to go to the db
|
||||
|
||||
self._name_and_type.SetLabelText( 'This service has tags. This box will regain its old information in a later version.' )
|
||||
HydrusGlobals.client_controller.CallToThread( self.THREADUpdateTagInfo )
|
||||
|
||||
|
||||
def Update( self, service ):
|
||||
|
@ -1148,4 +1141,24 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
|
||||
|
||||
def THREADUpdateTagInfo( self ):
|
||||
|
||||
service_info = HydrusGlobals.client_controller.Read( 'service_info', self._service.GetServiceKey() )
|
||||
|
||||
num_files = service_info[ HC.SERVICE_INFO_NUM_FILES ]
|
||||
num_tags = service_info[ HC.SERVICE_INFO_NUM_TAGS ]
|
||||
num_mappings = service_info[ HC.SERVICE_INFO_NUM_MAPPINGS ]
|
||||
|
||||
text = HydrusData.ConvertIntToPrettyString( num_mappings ) + ' total mappings involving ' + HydrusData.ConvertIntToPrettyString( num_tags ) + ' different tags on ' + HydrusData.ConvertIntToPrettyString( num_files ) + ' different files'
|
||||
|
||||
if self._service.GetServiceType() == HC.TAG_REPOSITORY:
|
||||
|
||||
num_deleted_mappings = service_info[ HC.SERVICE_INFO_NUM_DELETED_MAPPINGS ]
|
||||
|
||||
text += ' - ' + HydrusData.ConvertIntToPrettyString( num_deleted_mappings ) + ' deleted mappings'
|
||||
|
||||
|
||||
wx.CallAfter( self._tag_info_st.SetLabelText, text )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -194,7 +194,10 @@ class PanelPredicateSystemHash( PanelPredicateSystem ):
|
|||
|
||||
def GetInfo( self ):
|
||||
|
||||
hex_filter = lambda c: c in string.hexdigits
|
||||
def hex_filter( c ):
|
||||
|
||||
return c in string.hexdigits
|
||||
|
||||
|
||||
hash = filter( hex_filter, self._hash.GetValue().lower() )
|
||||
|
||||
|
@ -685,7 +688,10 @@ class PanelPredicateSystemSimilarTo( PanelPredicateSystem ):
|
|||
|
||||
def GetInfo( self ):
|
||||
|
||||
hex_filter = lambda c: c in string.hexdigits
|
||||
def hex_filter( c ):
|
||||
|
||||
return c in string.hexdigits
|
||||
|
||||
|
||||
hash = filter( hex_filter, self._hash.GetValue().lower() )
|
||||
|
||||
|
|
|
@ -246,7 +246,7 @@ class EditMediaViewOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._media_show_action.Append( CC.media_viewer_action_string_lookup[ action ], action )
|
||||
|
||||
if action != CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW:
|
||||
if action != CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY:
|
||||
|
||||
self._preview_show_action.Append( CC.media_viewer_action_string_lookup[ action ], action )
|
||||
|
||||
|
@ -420,7 +420,7 @@ class EditSeedCachePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._seed_cache = seed_cache
|
||||
|
||||
self._text = wx.StaticText( self, label = 'initialising' )
|
||||
self._seed_cache_control = ClientGUICommon.SeedCacheControl( self, self._seed_cache )
|
||||
self._seed_cache_control = ClientGUIControls.SeedCacheControl( self, self._seed_cache )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import HydrusNetwork
|
|||
import HydrusNetworking
|
||||
import HydrusPaths
|
||||
import HydrusSerialisable
|
||||
import HydrusTagArchive
|
||||
import HydrusTags
|
||||
import itertools
|
||||
import os
|
||||
|
@ -233,7 +234,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
ClientGUIScrolledPanels.ManagePanel.__init__( self, parent )
|
||||
|
||||
self._listctrl = ClientGUICommon.SaneListCtrlForSingleObject( self, 400, [ ( 'name', -1 ), ( 'type', 220 ), ( 'deletable', 120 ) ], delete_key_callback = self._Delete, activation_callback = self._Edit )
|
||||
self._listctrl = ClientGUICommon.SaneListCtrlForSingleObject( self, 400, [ ( 'type', 220 ), ( 'name', -1 ), ( 'deletable', 120 ) ], delete_key_callback = self._Delete, activation_callback = self._Edit )
|
||||
|
||||
menu_items = []
|
||||
|
||||
|
@ -279,8 +280,8 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
def _ConvertServiceToTuples( self, service ):
|
||||
|
||||
name = service.GetName()
|
||||
service_type = service.GetServiceType()
|
||||
name = service.GetName()
|
||||
deletable = service_type in HC.ADDREMOVABLE_SERVICES
|
||||
|
||||
pretty_service_type = HC.service_string_lookup[ service_type ]
|
||||
|
@ -294,7 +295,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
pretty_deletable = ''
|
||||
|
||||
|
||||
return ( ( name, pretty_service_type, pretty_deletable ), ( name, pretty_service_type, deletable ) )
|
||||
return ( ( pretty_service_type, name, pretty_deletable ), ( pretty_service_type, name, deletable ) )
|
||||
|
||||
|
||||
def _Add( self, service_type ):
|
||||
|
@ -562,53 +563,6 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
|
||||
|
||||
def EventCheckService( self, event ):
|
||||
|
||||
service = self.GetValue()
|
||||
|
||||
try:
|
||||
|
||||
root = service.Request( HC.GET, '' )
|
||||
|
||||
except HydrusExceptions.WrongServiceTypeException:
|
||||
|
||||
wx.MessageBox( 'Connection was made, but the service was not a ' + HC.service_string_lookup[ self._service_type ] + '.' )
|
||||
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.ShowException( e )
|
||||
|
||||
wx.MessageBox( 'Could not connect!' )
|
||||
|
||||
return
|
||||
|
||||
|
||||
if service_type in HC.RESTRICTED_SERVICES:
|
||||
|
||||
credentials = service.GetCredentials()
|
||||
|
||||
if not credentials.HasAccessKey():
|
||||
|
||||
wx.MessageBox( 'No access key!' )
|
||||
|
||||
return
|
||||
|
||||
|
||||
response = service.Request( HC.GET, 'access_key_verification' )
|
||||
|
||||
if not response[ 'verified' ]:
|
||||
|
||||
wx.MessageBox( 'That access key was not recognised!' )
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
wx.MessageBox( 'Everything looks ok!' )
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
||||
name = self._service_panel.GetValue()
|
||||
|
@ -895,7 +849,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
service.SetCredentials( credentials )
|
||||
|
||||
self._register.Disable()
|
||||
self._register.SetLabel( 'fetching...' )
|
||||
self._register.SetLabel( u'fetching\u2026' )
|
||||
|
||||
HydrusGlobals.client_controller.CallToThread( do_it, service, registration_key )
|
||||
|
||||
|
@ -980,9 +934,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
account = HydrusNetwork.Account.GenerateUnknownAccount()
|
||||
|
||||
dictionary_part[ 'account' ] = account.ToSerialisableTuple()
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'permissions_are_stale' )
|
||||
dictionary_part[ 'account' ] = HydrusNetwork.Account.GenerateSerialisableTupleFromAccount( account )
|
||||
|
||||
session_manager = HydrusGlobals.client_controller.GetClientSessionManager()
|
||||
|
||||
|
@ -2608,7 +2560,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
self._media_zooms.SetValue( ','.join( ( str( media_zoom ) for media_zoom in media_zooms ) ) )
|
||||
|
||||
mimes_in_correct_order = ( HC.IMAGE_JPEG, HC.IMAGE_PNG, HC.IMAGE_GIF, HC.APPLICATION_FLASH, HC.APPLICATION_PDF, HC.VIDEO_AVI, HC.VIDEO_FLV, HC.VIDEO_MOV, HC.VIDEO_MP4, HC.VIDEO_MKV, HC.VIDEO_MPEG, HC.VIDEO_WEBM, HC.VIDEO_WMV, HC.AUDIO_MP3, HC.AUDIO_OGG, HC.AUDIO_FLAC, HC.AUDIO_WMA )
|
||||
mimes_in_correct_order = ( HC.IMAGE_JPEG, HC.IMAGE_PNG, HC.IMAGE_GIF, HC.APPLICATION_FLASH, HC.APPLICATION_PDF, HC.APPLICATION_HYDRUS_UPDATE_CONTENT, HC.APPLICATION_HYDRUS_UPDATE_DEFINITIONS, HC.VIDEO_AVI, HC.VIDEO_FLV, HC.VIDEO_MOV, HC.VIDEO_MP4, HC.VIDEO_MKV, HC.VIDEO_MPEG, HC.VIDEO_WEBM, HC.VIDEO_WMV, HC.AUDIO_MP3, HC.AUDIO_OGG, HC.AUDIO_FLAC, HC.AUDIO_WMA )
|
||||
|
||||
for mime in mimes_in_correct_order:
|
||||
|
||||
|
@ -2658,8 +2610,6 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
pretty_media_show_action = CC.media_viewer_action_string_lookup[ media_show_action ]
|
||||
pretty_preview_show_action = CC.media_viewer_action_string_lookup[ preview_show_action ]
|
||||
|
||||
no_show_actions = ( CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW, CC.MEDIA_VIEWER_ACTION_SHOW_OPEN_EXTERNALLY_BUTTON )
|
||||
|
||||
no_show = media_show_action in CC.no_support and preview_show_action in CC.no_support
|
||||
|
||||
if no_show:
|
||||
|
@ -3761,6 +3711,8 @@ class ManageServerServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
service = self._services_listctrl.GetObject( index )
|
||||
|
||||
original_name = service.GetName()
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit serverside service' ) as dlg_edit:
|
||||
|
||||
panel = ClientGUIScrolledPanelsEdit.EditServersideService( dlg_edit, service )
|
||||
|
@ -3773,7 +3725,10 @@ class ManageServerServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
edited_service = panel.GetValue()
|
||||
|
||||
self._services_listctrl.SetNonDupeName( edited_service )
|
||||
if edited_service.GetName() != original_name:
|
||||
|
||||
self._services_listctrl.SetNonDupeName( edited_service )
|
||||
|
||||
|
||||
self._SetNonDupePort( edited_service )
|
||||
|
||||
|
@ -3807,7 +3762,7 @@ class ManageServerServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
def _SetNonDupePort( self, new_service ):
|
||||
|
||||
existing_ports = [ service.GetPort() for service in self._services_listctrl.GetObjects() ]
|
||||
existing_ports = [ service.GetPort() for service in self._services_listctrl.GetObjects() if service.GetServiceKey() != new_service.GetServiceKey() ]
|
||||
|
||||
new_port = new_service.GetPort()
|
||||
|
||||
|
|
|
@ -168,9 +168,12 @@ def GenerateShapePerceptualHashes( path ):
|
|||
dct_88_boolean = dct_88 > median
|
||||
|
||||
# convert TTTFTFTF to 11101010 by repeatedly shifting answer and adding 0 or 1
|
||||
# you can even go ( a << 1 ) + b and leave out the initial param on the latel reduce call as bools act like ints for this
|
||||
# you can even go ( a << 1 ) + b and leave out the initial param on the reduce call as bools act like ints for this
|
||||
# but let's not go crazy for another two nanoseconds
|
||||
collapse_bools_to_binary_uint = lambda a, b: ( a << 1 ) + int( b )
|
||||
def collapse_bools_to_binary_uint( a, b ):
|
||||
|
||||
return ( a << 1 ) + int( b )
|
||||
|
||||
|
||||
bytes = []
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
self._pending_queries = []
|
||||
|
||||
self._get_tags_if_redundant = True
|
||||
self._get_tags_if_redundant = False
|
||||
self._file_limit = HC.options[ 'gallery_file_limit' ]
|
||||
self._gallery_paused = False
|
||||
self._files_paused = False
|
||||
|
|
|
@ -438,11 +438,17 @@ class MediaList( object ):
|
|||
|
||||
if sort_by_data == CC.SORT_BY_RANDOM:
|
||||
|
||||
sort_function = lambda x: random.random()
|
||||
def sort_key( x ):
|
||||
|
||||
return random.random()
|
||||
|
||||
|
||||
elif sort_by_data in ( CC.SORT_BY_SMALLEST, CC.SORT_BY_LARGEST ):
|
||||
|
||||
sort_function = lambda x: deal_with_none( x.GetSize() )
|
||||
def sort_key( x ):
|
||||
|
||||
return deal_with_none( x.GetSize() )
|
||||
|
||||
|
||||
if sort_by_data == CC.SORT_BY_LARGEST:
|
||||
|
||||
|
@ -451,7 +457,10 @@ class MediaList( object ):
|
|||
|
||||
elif sort_by_data in ( CC.SORT_BY_SHORTEST, CC.SORT_BY_LONGEST ):
|
||||
|
||||
sort_function = lambda x: deal_with_none( x.GetDuration() )
|
||||
def sort_key( x ):
|
||||
|
||||
return deal_with_none( x.GetDuration() )
|
||||
|
||||
|
||||
if sort_by_data == CC.SORT_BY_LONGEST:
|
||||
|
||||
|
@ -473,7 +482,10 @@ class MediaList( object ):
|
|||
file_service_key = self._file_service_key
|
||||
|
||||
|
||||
sort_function = lambda x: deal_with_none( x.GetTimestamp( file_service_key ) )
|
||||
def sort_key( x ):
|
||||
|
||||
return deal_with_none( x.GetTimestamp( file_service_key ) )
|
||||
|
||||
|
||||
if sort_by_data == CC.SORT_BY_NEWEST:
|
||||
|
||||
|
@ -482,7 +494,10 @@ class MediaList( object ):
|
|||
|
||||
elif sort_by_data in ( CC.SORT_BY_HEIGHT_ASC, CC.SORT_BY_HEIGHT_DESC ):
|
||||
|
||||
sort_function = lambda x: deal_with_none( x.GetResolution()[0] )
|
||||
def sort_key( x ):
|
||||
|
||||
return deal_with_none( x.GetResolution()[1] )
|
||||
|
||||
|
||||
if sort_by_data == CC.SORT_BY_HEIGHT_DESC:
|
||||
|
||||
|
@ -491,7 +506,10 @@ class MediaList( object ):
|
|||
|
||||
elif sort_by_data in ( CC.SORT_BY_WIDTH_ASC, CC.SORT_BY_WIDTH_DESC ):
|
||||
|
||||
sort_function = lambda x: deal_with_none( x.GetResolution()[1] )
|
||||
def sort_key( x ):
|
||||
|
||||
return deal_with_none( x.GetResolution()[0] )
|
||||
|
||||
|
||||
if sort_by_data == CC.SORT_BY_WIDTH_DESC:
|
||||
|
||||
|
@ -500,7 +518,7 @@ class MediaList( object ):
|
|||
|
||||
elif sort_by_data in ( CC.SORT_BY_RATIO_ASC, CC.SORT_BY_RATIO_DESC ):
|
||||
|
||||
def sort_function( x ):
|
||||
def sort_key( x ):
|
||||
|
||||
( width, height ) = x.GetResolution()
|
||||
|
||||
|
@ -521,7 +539,7 @@ class MediaList( object ):
|
|||
|
||||
elif sort_by_data in ( CC.SORT_BY_NUM_PIXELS_ASC, CC.SORT_BY_NUM_PIXELS_DESC ):
|
||||
|
||||
def sort_function( x ):
|
||||
def sort_key( x ):
|
||||
|
||||
( width, height ) = x.GetResolution()
|
||||
|
||||
|
@ -542,25 +560,28 @@ class MediaList( object ):
|
|||
|
||||
elif sort_by_data == CC.SORT_BY_MIME:
|
||||
|
||||
sort_function = lambda x: x.GetMime()
|
||||
def sort_key( x ):
|
||||
|
||||
return x.GetMime()
|
||||
|
||||
|
||||
|
||||
elif sort_by_type == 'namespaces':
|
||||
|
||||
def namespace_sort_function( namespaces, x ):
|
||||
namespaces = sort_by_data
|
||||
|
||||
def sort_key( x ):
|
||||
|
||||
x_tags_manager = x.GetTagsManager()
|
||||
|
||||
return [ x_tags_manager.GetComparableNamespaceSlice( ( namespace, ) ) for namespace in namespaces ]
|
||||
|
||||
|
||||
sort_function = lambda x: namespace_sort_function( sort_by_data, x )
|
||||
|
||||
elif sort_by_type in ( 'rating_descend', 'rating_ascend' ):
|
||||
|
||||
service_key = sort_by_data
|
||||
|
||||
def ratings_sort_function( service_key, x ):
|
||||
def sort_key( x ):
|
||||
|
||||
x_ratings_manager = x.GetRatingsManager()
|
||||
|
||||
|
@ -569,15 +590,13 @@ class MediaList( object ):
|
|||
return rating
|
||||
|
||||
|
||||
sort_function = lambda x: ratings_sort_function( service_key, x )
|
||||
|
||||
if sort_by_type == 'rating_descend':
|
||||
|
||||
reverse = True
|
||||
|
||||
|
||||
|
||||
return ( sort_function, reverse )
|
||||
return ( sort_key, reverse )
|
||||
|
||||
|
||||
def _RecalcHashes( self ):
|
||||
|
@ -824,7 +843,7 @@ class MediaList( object ):
|
|||
|
||||
media_show_action = new_options.GetMediaShowAction( media.GetMime() )
|
||||
|
||||
if media_show_action == CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW:
|
||||
if media_show_action in ( CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW_ON_ACTIVATION_OPEN_EXTERNALLY, CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW ):
|
||||
|
||||
continue
|
||||
|
||||
|
@ -979,15 +998,15 @@ class MediaList( object ):
|
|||
sort_by_fallback = sort_choices[ 0 ]
|
||||
|
||||
|
||||
( sort_function, reverse ) = self._GetSortFunction( sort_by_fallback )
|
||||
( sort_key, reverse ) = self._GetSortFunction( sort_by_fallback )
|
||||
|
||||
self._sorted_media.sort( sort_function, reverse = reverse )
|
||||
self._sorted_media.sort( sort_key, reverse = reverse )
|
||||
|
||||
# this is a stable sort, so the fallback order above will remain for equal items
|
||||
|
||||
( sort_function, reverse ) = self._GetSortFunction( self._sort_by )
|
||||
( sort_key, reverse ) = self._GetSortFunction( self._sort_by )
|
||||
|
||||
self._sorted_media.sort( sort_function = sort_function, reverse = reverse )
|
||||
self._sorted_media.sort( sort_key = sort_key, reverse = reverse )
|
||||
|
||||
|
||||
class ListeningMediaList( MediaList ):
|
||||
|
@ -1682,9 +1701,12 @@ class SortedList( object ):
|
|||
|
||||
def __init__( self, initial_items = None ):
|
||||
|
||||
if initial_items is None: initial_items = []
|
||||
if initial_items is None:
|
||||
|
||||
initial_items = []
|
||||
|
||||
|
||||
self._sort_function = lambda x: x
|
||||
self._sort_key = None
|
||||
self._sort_reverse = False
|
||||
|
||||
self._sorted_list = list( initial_items )
|
||||
|
@ -1779,20 +1801,20 @@ class SortedList( object ):
|
|||
self._DirtyIndices()
|
||||
|
||||
|
||||
def sort( self, sort_function = None, reverse = False ):
|
||||
def sort( self, sort_key = None, reverse = False ):
|
||||
|
||||
if sort_function is None:
|
||||
if sort_key is None:
|
||||
|
||||
sort_function = self._sort_function
|
||||
sort_key = self._sort_key
|
||||
reverse = self._sort_reverse
|
||||
|
||||
else:
|
||||
|
||||
self._sort_function = sort_function
|
||||
self._sort_key = sort_key
|
||||
self._sort_reverse = reverse
|
||||
|
||||
|
||||
self._sorted_list.sort( key = sort_function, reverse = reverse )
|
||||
self._sorted_list.sort( key = sort_key, reverse = reverse )
|
||||
|
||||
self._DirtyIndices()
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@ IGNORED_TAG_SEARCH_CHARACTERS_UNICODE_TRANSLATE = { ord( char ) : None for char
|
|||
|
||||
def ConvertTagToSearchable( tag ):
|
||||
|
||||
while tag.endswith( '*' ):
|
||||
if tag == '':
|
||||
|
||||
tag = tag[:-1]
|
||||
return ''
|
||||
|
||||
|
||||
if not isinstance( tag, unicode ):
|
||||
|
@ -23,9 +23,29 @@ def ConvertTagToSearchable( tag ):
|
|||
tag = HydrusData.ToUnicode( tag )
|
||||
|
||||
|
||||
return tag.translate( IGNORED_TAG_SEARCH_CHARACTERS_UNICODE_TRANSLATE )
|
||||
tag = tag.translate( IGNORED_TAG_SEARCH_CHARACTERS_UNICODE_TRANSLATE )
|
||||
|
||||
def FilterPredicatesBySearchEntry( service_key, search_entry, predicates ):
|
||||
while '**' in tag:
|
||||
|
||||
tag = tag.replace( '**', '*' )
|
||||
|
||||
|
||||
return tag
|
||||
|
||||
def ConvertEntryTextToSearchText( entry_text ):
|
||||
|
||||
entry_text = HydrusTags.CleanTag( entry_text )
|
||||
|
||||
entry_text = ConvertTagToSearchable( entry_text )
|
||||
|
||||
if not IsComplexWildcard( entry_text ) and not entry_text.endswith( '*' ):
|
||||
|
||||
entry_text = entry_text + u'*'
|
||||
|
||||
|
||||
return entry_text
|
||||
|
||||
def FilterPredicatesBySearchText( service_key, search_text, predicates ):
|
||||
|
||||
tags_to_predicates = {}
|
||||
|
||||
|
@ -39,59 +59,48 @@ def FilterPredicatesBySearchEntry( service_key, search_entry, predicates ):
|
|||
|
||||
|
||||
|
||||
matching_tags = FilterTagsBySearchEntry( service_key, search_entry, tags_to_predicates.keys() )
|
||||
matching_tags = FilterTagsBySearchText( service_key, search_text, tags_to_predicates.keys() )
|
||||
|
||||
matches = [ tags_to_predicates[ tag ] for tag in matching_tags ]
|
||||
|
||||
return matches
|
||||
|
||||
def FilterTagsBySearchEntry( service_key, search_entry, tags, search_siblings = True ):
|
||||
def FilterTagsBySearchText( service_key, search_text, tags, search_siblings = True ):
|
||||
|
||||
def compile_re( s ):
|
||||
|
||||
num_stars = s.count( '*' )
|
||||
|
||||
is_wildcard_search = ( num_stars == 1 and not s.endswith( '*' ) ) or num_stars > 1
|
||||
|
||||
regular_parts_of_s = s.split( '*' )
|
||||
|
||||
escaped_parts_of_s = [ re.escape( part ) for part in regular_parts_of_s ]
|
||||
escaped_parts_of_s = map( re.escape, regular_parts_of_s )
|
||||
|
||||
s = '.*'.join( escaped_parts_of_s )
|
||||
|
||||
if is_wildcard_search:
|
||||
# \A is start of string
|
||||
# \Z is end of string
|
||||
# \s is whitespace
|
||||
|
||||
if s.startswith( '.*' ):
|
||||
|
||||
return re.compile( s, flags = re.UNICODE )
|
||||
beginning = '(\\A|:)'
|
||||
|
||||
else:
|
||||
|
||||
return re.compile( '(\\A|\\s)' + s + '(\\s|\\Z)', flags = re.UNICODE )
|
||||
beginning = '(\\A|:|\\s)'
|
||||
|
||||
|
||||
|
||||
search_entry = ConvertTagToSearchable( search_entry )
|
||||
|
||||
( namespace, half_complete_subtag ) = HydrusTags.SplitTag( search_entry )
|
||||
|
||||
if namespace != '':
|
||||
if s.endswith( '.*' ):
|
||||
|
||||
end = '\\Z' # end of string
|
||||
|
||||
else:
|
||||
|
||||
end = '(\\s|\\Z)' # whitespace or end of string
|
||||
|
||||
|
||||
search_namespace = True
|
||||
|
||||
namespace_re_predicate = compile_re( ConvertTagToSearchable( namespace ) )
|
||||
|
||||
else:
|
||||
|
||||
search_namespace = False
|
||||
|
||||
namespace_re_predicate = None
|
||||
return re.compile( beginning + s + end, flags = re.UNICODE )
|
||||
|
||||
|
||||
if '*' not in half_complete_subtag:
|
||||
|
||||
half_complete_subtag += '*'
|
||||
|
||||
|
||||
half_complete_subtag_re_predicate = compile_re( half_complete_subtag )
|
||||
re_predicate = compile_re( search_text )
|
||||
|
||||
sibling_manager = HydrusGlobals.client_controller.GetManager( 'tag_siblings' )
|
||||
|
||||
|
@ -108,30 +117,11 @@ def FilterTagsBySearchEntry( service_key, search_entry, tags, search_siblings =
|
|||
possible_tags = [ tag ]
|
||||
|
||||
|
||||
possible_tags = map( ConvertTagToSearchable, possible_tags )
|
||||
|
||||
for possible_tag in possible_tags:
|
||||
|
||||
( possible_namespace, possible_subtag ) = HydrusTags.SplitTag( possible_tag )
|
||||
|
||||
if possible_namespace != '':
|
||||
|
||||
possible_namespace = ConvertTagToSearchable( possible_namespace )
|
||||
|
||||
if search_namespace and re.search( namespace_re_predicate, possible_namespace ) is None:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if search_namespace:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
|
||||
possible_subtag = ConvertTagToSearchable( possible_subtag )
|
||||
|
||||
if re.search( half_complete_subtag_re_predicate, possible_subtag ) is not None:
|
||||
if re.search( re_predicate, possible_tag ) is not None:
|
||||
|
||||
result.append( tag )
|
||||
|
||||
|
@ -142,6 +132,22 @@ def FilterTagsBySearchEntry( service_key, search_entry, tags, search_siblings =
|
|||
|
||||
return result
|
||||
|
||||
def IsComplexWildcard( search_text ):
|
||||
|
||||
num_stars = search_text.count( '*' )
|
||||
|
||||
if num_stars > 1:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if num_stars == 1 and not search_text.endswith( '*' ):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
def SortPredicates( predicates ):
|
||||
|
||||
def cmp_func( x, y ): return cmp( x.GetCount(), y.GetCount() )
|
||||
|
|
|
@ -33,7 +33,7 @@ def GenerateDefaultServiceDictionary( service_type ):
|
|||
|
||||
if service_type in HC.RESTRICTED_SERVICES:
|
||||
|
||||
dictionary[ 'account' ] = HydrusNetwork.Account.GenerateUnknownAccount().ToSerialisableTuple()
|
||||
dictionary[ 'account' ] = HydrusNetwork.Account.GenerateSerialisableTupleFromAccount( HydrusNetwork.Account.GenerateUnknownAccount() )
|
||||
dictionary[ 'next_account_sync' ] = 0
|
||||
|
||||
if service_type in HC.REPOSITORIES:
|
||||
|
@ -596,7 +596,7 @@ class ServiceRestricted( ServiceRemote ):
|
|||
|
||||
dictionary = ServiceRemote._GetSerialisableDictionary( self )
|
||||
|
||||
dictionary[ 'account' ] = self._account.ToSerialisableTuple()
|
||||
dictionary[ 'account' ] = HydrusNetwork.Account.GenerateSerialisableTupleFromAccount( self._account )
|
||||
dictionary[ 'next_account_sync' ] = self._next_account_sync
|
||||
|
||||
return dictionary
|
||||
|
@ -940,6 +940,14 @@ class ServiceRepository( ServiceRestricted ):
|
|||
return processing_value < range
|
||||
|
||||
|
||||
def GetNextUpdateDueString( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._metadata.GetNextUpdateDueString( from_client = True )
|
||||
|
||||
|
||||
|
||||
def GetTagArchiveSync( self ):
|
||||
|
||||
with self._lock:
|
||||
|
|
|
@ -49,7 +49,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 18
|
||||
SOFTWARE_VERSION = 245
|
||||
SOFTWARE_VERSION = 246
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
@ -261,9 +261,9 @@ service_string_lookup = {}
|
|||
|
||||
service_string_lookup[ TAG_REPOSITORY ] = 'hydrus tag repository'
|
||||
service_string_lookup[ FILE_REPOSITORY ] = 'hydrus file repository'
|
||||
service_string_lookup[ LOCAL_FILE_DOMAIN ] = 'hydrus local file domain'
|
||||
service_string_lookup[ LOCAL_FILE_TRASH_DOMAIN ] = 'hydrus trash domain'
|
||||
service_string_lookup[ COMBINED_LOCAL_FILE ] = 'hydrus local file service'
|
||||
service_string_lookup[ LOCAL_FILE_DOMAIN ] = 'local file domain'
|
||||
service_string_lookup[ LOCAL_FILE_TRASH_DOMAIN ] = 'local trash file domain'
|
||||
service_string_lookup[ COMBINED_LOCAL_FILE ] = 'virtual combined local file service'
|
||||
service_string_lookup[ MESSAGE_DEPOT ] = 'hydrus message depot'
|
||||
service_string_lookup[ LOCAL_TAG ] = 'local tag service'
|
||||
service_string_lookup[ LOCAL_RATING_NUMERICAL ] = 'local numerical rating service'
|
||||
|
@ -334,6 +334,7 @@ SERVICE_INFO_NUM_PETITIONED_TAG_SIBLINGS = 18
|
|||
SERVICE_INFO_NUM_PENDING_TAG_PARENTS = 19
|
||||
SERVICE_INFO_NUM_PETITIONED_TAG_PARENTS = 20
|
||||
SERVICE_INFO_NUM_SHARES = 21
|
||||
SERVICE_INFO_NUM_VIEWABLE_FILES = 22
|
||||
|
||||
SERVICE_UPDATE_DELETE_PENDING = 0
|
||||
SERVICE_UPDATE_RESET = 1
|
||||
|
|
|
@ -23,6 +23,8 @@ class HydrusController( object ):
|
|||
|
||||
HydrusGlobals.controller = self
|
||||
|
||||
self._name = 'hydrus'
|
||||
|
||||
self._db_dir = db_dir
|
||||
self._no_daemons = no_daemons
|
||||
self._no_wal = no_wal
|
||||
|
@ -249,6 +251,24 @@ class HydrusController( object ):
|
|||
raise NotImplementedError()
|
||||
|
||||
|
||||
def PrintProfile( self, summary, profile_text ):
|
||||
|
||||
boot_pretty_timestamp = time.strftime( '%Y-%m-%d %H-%M-%S', time.localtime( self._timestamps[ 'boot' ] ) )
|
||||
|
||||
profile_log_filename = self._name + ' profile - ' + boot_pretty_timestamp + '.log'
|
||||
|
||||
profile_log_path = os.path.join( self._db_dir, profile_log_filename )
|
||||
|
||||
with open( profile_log_path, 'a' ) as f:
|
||||
|
||||
prefix = time.strftime( '%Y/%m/%d %H:%M:%S: ', time.localtime() )
|
||||
|
||||
f.write( prefix + summary )
|
||||
f.write( os.linesep * 2 )
|
||||
f.write( profile_text )
|
||||
|
||||
|
||||
|
||||
def ProcessPubSub( self ):
|
||||
|
||||
self._currently_doing_pubsub = True
|
||||
|
|
|
@ -47,37 +47,11 @@ def CanVacuum( db_path, stop_time = None ):
|
|||
|
||||
|
||||
|
||||
temp_dir = tempfile.gettempdir()
|
||||
( db_dir, db_filename ) = os.path.split( db_path )
|
||||
|
||||
temp_disk_free_space = HydrusPaths.GetFreeSpace( temp_dir )
|
||||
( has_space, reason ) = HydrusPaths.HasSpaceForDBTransaction( db_dir, db_size )
|
||||
|
||||
a = HydrusPaths.GetDevice( temp_dir )
|
||||
b = HydrusPaths.GetDevice( db_dir )
|
||||
|
||||
if HydrusPaths.GetDevice( temp_dir ) == HydrusPaths.GetDevice( db_dir ):
|
||||
|
||||
if temp_disk_free_space < db_size * 2.2:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if temp_disk_free_space < db_size * 1.1:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
db_disk_free_space = HydrusPaths.GetFreeSpace( db_dir )
|
||||
|
||||
if db_disk_free_space < db_size * 1.1:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
return True
|
||||
return has_space
|
||||
|
||||
except Exception as e:
|
||||
|
||||
|
@ -206,7 +180,15 @@ class HydrusDB( object ):
|
|||
|
||||
( version, ) = self._c.execute( 'SELECT version FROM version;' ).fetchone()
|
||||
|
||||
if version < HC.SOFTWARE_VERSION - 50: raise Exception( 'Your current version of hydrus ' + str( version ) + ' is too old for this version ' + str( HC.SOFTWARE_VERSION ) + ' to update. Please try updating with version ' + str( version + 45 ) + ' or earlier first.' )
|
||||
if version < HC.SOFTWARE_VERSION - 50:
|
||||
|
||||
raise Exception( 'Your current database version of hydrus ' + str( version ) + ' is too old for this software version ' + str( HC.SOFTWARE_VERSION ) + ' to update. Please try updating with version ' + str( version + 45 ) + ' or earlier first.' )
|
||||
|
||||
|
||||
if version < 238:
|
||||
|
||||
raise Exception( 'Unfortunately, this software cannot update your database. Please try installing version 238 first.' )
|
||||
|
||||
|
||||
while version < HC.SOFTWARE_VERSION:
|
||||
|
||||
|
@ -585,6 +567,20 @@ class HydrusDB( object ):
|
|||
return [ row for row in self._SelectFromList( select_statement, xs ) ]
|
||||
|
||||
|
||||
def _STL( self, iterable_cursor ):
|
||||
|
||||
# strip singleton tuples to a list
|
||||
|
||||
return [ item for ( item, ) in iterable_cursor ]
|
||||
|
||||
|
||||
def _STS( self, iterable_cursor ):
|
||||
|
||||
# strip singleton tuples to a set
|
||||
|
||||
return { item for ( item, ) in iterable_cursor }
|
||||
|
||||
|
||||
def _UpdateDB( self, version ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
@ -662,9 +658,11 @@ class HydrusDB( object ):
|
|||
|
||||
if HydrusGlobals.db_profile_mode:
|
||||
|
||||
HydrusData.ShowText( 'Profiling ' + job.ToString() )
|
||||
summary = 'Profiling ' + job.ToString()
|
||||
|
||||
HydrusData.Profile( 'self._ProcessJob( job )', globals(), locals() )
|
||||
HydrusData.ShowText( summary )
|
||||
|
||||
HydrusData.Profile( summary, 'self._ProcessJob( job )', globals(), locals() )
|
||||
|
||||
else:
|
||||
|
||||
|
|
|
@ -709,7 +709,12 @@ def IntelligentMassIntersect( sets_to_reduce ):
|
|||
|
||||
sets_to_reduce = list( sets_to_reduce )
|
||||
|
||||
sets_to_reduce.sort( cmp = lambda x, y: cmp( len( x ), len( y ) ) )
|
||||
def get_len( item ):
|
||||
|
||||
return len( item )
|
||||
|
||||
|
||||
sets_to_reduce.sort( key = get_len )
|
||||
|
||||
for set_to_reduce in sets_to_reduce:
|
||||
|
||||
|
@ -831,7 +836,7 @@ def MergeKeyToListDicts( key_to_list_dicts ):
|
|||
|
||||
def Print( text ):
|
||||
|
||||
print( ToByteString( text ) )
|
||||
print( ToUnicode( text ) )
|
||||
|
||||
ShowText = Print
|
||||
|
||||
|
@ -864,7 +869,7 @@ def PrintException( e, do_wait = True ):
|
|||
|
||||
ShowException = PrintException
|
||||
|
||||
def Profile( code, g, l ):
|
||||
def Profile( summary, code, g, l ):
|
||||
|
||||
profile = cProfile.Profile()
|
||||
|
||||
|
@ -878,38 +883,19 @@ def Profile( code, g, l ):
|
|||
|
||||
stats.sort_stats( 'tottime' )
|
||||
|
||||
output.seek( 0 )
|
||||
|
||||
output.write( 'Stats' )
|
||||
output.write( os.linesep )
|
||||
output.write( os.linesep * 2 )
|
||||
|
||||
stats.print_stats()
|
||||
|
||||
output.seek( 0 )
|
||||
|
||||
DebugPrint( output.read() )
|
||||
|
||||
output.seek( 0 )
|
||||
|
||||
output.write( 'Callers' )
|
||||
output.write( os.linesep )
|
||||
output.write( os.linesep * 2 )
|
||||
|
||||
stats.print_callers()
|
||||
|
||||
output.seek( 0 )
|
||||
|
||||
DebugPrint( output.read() )
|
||||
|
||||
output.seek( 0 )
|
||||
|
||||
output.write( 'Callees' )
|
||||
output.write( os.linesep )
|
||||
|
||||
stats.print_callees()
|
||||
|
||||
output.seek( 0 )
|
||||
|
||||
DebugPrint( output.read() )
|
||||
HydrusGlobals.controller.PrintProfile( summary, output.read() )
|
||||
|
||||
def RandomPop( population ):
|
||||
|
||||
|
|
|
@ -163,7 +163,10 @@ def GetFileInfo( path ):
|
|||
|
||||
mime = GetMime( path )
|
||||
|
||||
if mime not in HC.ALLOWED_MIMES: raise HydrusExceptions.MimeException( 'Filetype is not permitted!' )
|
||||
if mime not in HC.ALLOWED_MIMES:
|
||||
|
||||
raise HydrusExceptions.MimeException( 'Filetype is not permitted!' )
|
||||
|
||||
|
||||
width = None
|
||||
height = None
|
||||
|
|
|
@ -113,13 +113,15 @@ class HydrusLogger( object ):
|
|||
prefix = time.strftime( '%Y/%m/%d %H:%M:%S: ', time.localtime() )
|
||||
|
||||
|
||||
message = HydrusData.ToByteString( prefix + value )
|
||||
message = prefix + value
|
||||
|
||||
if not self._problem_with_previous_stdout:
|
||||
|
||||
stdout_message = HydrusData.ToByteString( message.replace( u'\u2026', '...' ) )
|
||||
|
||||
try:
|
||||
|
||||
self._previous_sys_stdout.write( message )
|
||||
self._previous_sys_stdout.write( stdout_message )
|
||||
|
||||
except IOError:
|
||||
|
||||
|
@ -127,7 +129,9 @@ class HydrusLogger( object ):
|
|||
|
||||
|
||||
|
||||
self._log_file.write( message )
|
||||
log_message = HydrusData.ToByteString( message )
|
||||
|
||||
self._log_file.write( log_message )
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ def ConvertContentsToClientToServerContentUpdatePackage( action, contents, reaso
|
|||
hash_ids_to_hashes = {}
|
||||
hash_i = 0
|
||||
|
||||
content_data_dict = GetEmptyDataDict()
|
||||
content_data_dict = HydrusData.GetEmptyDataDict()
|
||||
|
||||
for content in contents:
|
||||
|
||||
|
@ -211,12 +211,12 @@ def DumpToBodyString( args ):
|
|||
|
||||
if 'account' in args:
|
||||
|
||||
args[ 'account' ] = args[ 'account' ].ToSerialisableTuple()
|
||||
args[ 'account' ] = Account.GenerateSerialisableTupleFromAccount( args[ 'account' ] )
|
||||
|
||||
|
||||
if 'accounts' in args:
|
||||
|
||||
args[ 'accounts' ] = [ account.ToSerialisableTuple() for account in args[ 'accounts' ] ]
|
||||
args[ 'accounts' ] = map( Account.GenerateSerialisableTupleFromAccount, args[ 'accounts' ] )
|
||||
|
||||
|
||||
if 'account_types' in args:
|
||||
|
@ -399,7 +399,17 @@ def ParseGETArgs( requests_args ):
|
|||
|
||||
class Account( object ):
|
||||
|
||||
def __init__( self, account_key, account_type, created, expires, dictionary ):
|
||||
def __init__( self, account_key, account_type, created, expires, banned_info = None, bandwidth_tracker = None ):
|
||||
|
||||
if banned_info is None:
|
||||
|
||||
banned_info = None # stupid, but keep it in case we change this
|
||||
|
||||
|
||||
if bandwidth_tracker is None:
|
||||
|
||||
bandwidth_tracker = HydrusNetworking.BandwidthTracker()
|
||||
|
||||
|
||||
HydrusSerialisable.SerialisableBase.__init__( self )
|
||||
|
||||
|
@ -409,8 +419,8 @@ class Account( object ):
|
|||
self._account_type = account_type
|
||||
self._created = created
|
||||
self._expires = expires
|
||||
|
||||
self._LoadFromDictionary( dictionary )
|
||||
self._banned_info = banned_info
|
||||
self._bandwidth_tracker = bandwidth_tracker
|
||||
|
||||
self._dirty = False
|
||||
|
||||
|
@ -425,24 +435,6 @@ class Account( object ):
|
|||
return self.__repr__()
|
||||
|
||||
|
||||
def _GetSerialisableDictionary( self ):
|
||||
|
||||
dictionary = HydrusSerialisable.SerialisableDictionary()
|
||||
|
||||
dictionary[ 'banned_info' ] = self._banned_info
|
||||
|
||||
dictionary[ 'bandwidth_tracker' ] = self._bandwidth_tracker
|
||||
|
||||
return dictionary
|
||||
|
||||
|
||||
def _LoadFromDictionary( self, dictionary ):
|
||||
|
||||
self._banned_info = dictionary[ 'banned_info' ]
|
||||
|
||||
self._bandwidth_tracker = dictionary[ 'bandwidth_tracker' ]
|
||||
|
||||
|
||||
def _GetBannedString( self ):
|
||||
|
||||
if self._banned_info is None:
|
||||
|
@ -706,25 +698,11 @@ class Account( object ):
|
|||
|
||||
|
||||
|
||||
def ToSerialisableTuple( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
dictionary = self._GetSerialisableDictionary()
|
||||
|
||||
dictionary_string = dictionary.DumpToString()
|
||||
|
||||
return ( self._account_key.encode( 'hex' ), self._account_type.ToSerialisableTuple(), self._created, self._expires, dictionary_string )
|
||||
|
||||
|
||||
|
||||
def ToTuple( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
dictionary = self._GetSerialisableDictionary()
|
||||
|
||||
return ( self._account_key, self._account_type, self._created, self._expires, dictionary )
|
||||
return ( self._account_key, self._account_type, self._created, self._expires, self._banned_info, self._bandwidth_tracker )
|
||||
|
||||
|
||||
|
||||
|
@ -745,18 +723,44 @@ class Account( object ):
|
|||
account_type = AccountType.GenerateAccountTypeFromSerialisableTuple( account_type_serialisable_tuple )
|
||||
dictionary = HydrusSerialisable.CreateFromString( dictionary_string )
|
||||
|
||||
return Account( account_key, account_type, created, expires, dictionary )
|
||||
return Account.GenerateAccountFromTuple( ( account_key, account_type, created, expires, dictionary ) )
|
||||
|
||||
|
||||
@staticmethod
|
||||
def GenerateNewAccount( account_key, account_type, created, expires ):
|
||||
def GenerateAccountFromTuple( ( account_key, account_type, created, expires, dictionary ) ):
|
||||
|
||||
banned_info = dictionary[ 'banned_info' ]
|
||||
bandwidth_tracker = dictionary[ 'bandwidth_tracker' ]
|
||||
|
||||
return Account( account_key, account_type, created, expires, banned_info, bandwidth_tracker )
|
||||
|
||||
|
||||
@staticmethod
|
||||
def GenerateSerialisableTupleFromAccount( account ):
|
||||
|
||||
( account_key, account_type, created, expires, dictionary ) = Account.GenerateTupleFromAccount( account )
|
||||
|
||||
account_key_encoded = account_key.encode( 'hex' )
|
||||
|
||||
serialisable_account_type = account_type.ToSerialisableTuple()
|
||||
|
||||
dictionary_string = dictionary.DumpToString()
|
||||
|
||||
return ( account_key_encoded, serialisable_account_type, created, expires, dictionary_string )
|
||||
|
||||
|
||||
@staticmethod
|
||||
def GenerateTupleFromAccount( account ):
|
||||
|
||||
( account_key, account_type, created, expires, banned_info, bandwidth_tracker ) = account.ToTuple()
|
||||
|
||||
dictionary = HydrusSerialisable.SerialisableDictionary()
|
||||
|
||||
dictionary[ 'banned_info' ] = None
|
||||
dictionary[ 'bandwidth_tracker' ] = HydrusNetworking.BandwidthTracker()
|
||||
dictionary[ 'banned_info' ] = banned_info
|
||||
|
||||
return Account( account_key, account_type, created, expires, dictionary )
|
||||
dictionary[ 'bandwidth_tracker' ] = bandwidth_tracker
|
||||
|
||||
return ( account_key, account_type, created, expires, dictionary )
|
||||
|
||||
|
||||
@staticmethod
|
||||
|
@ -766,7 +770,7 @@ class Account( object ):
|
|||
created = 0
|
||||
expires = None
|
||||
|
||||
unknown_account = Account.GenerateNewAccount( account_key, account_type, created, expires )
|
||||
unknown_account = Account( account_key, account_type, created, expires )
|
||||
|
||||
return unknown_account
|
||||
|
||||
|
@ -998,7 +1002,7 @@ class ClientToServerUpdate( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
for ( action, serialisable_contents_and_reasons ) in self._actions_to_contents_and_reasons.items():
|
||||
for ( action, serialisable_contents_and_reasons ) in serialisable_info:
|
||||
|
||||
contents_and_reasons = [ ( HydrusSerialisable.CreateFromSerialisableTuple( serialisable_content ), reason ) for ( serialisable_content, reason ) in serialisable_contents_and_reasons ]
|
||||
|
||||
|
@ -1655,6 +1659,18 @@ class Metadata( HydrusSerialisable.SerialisableBase ):
|
|||
self._update_hashes = set()
|
||||
|
||||
|
||||
def _GetNextUpdateDueTime( self, from_client = False ):
|
||||
|
||||
delay = 0
|
||||
|
||||
if from_client:
|
||||
|
||||
delay = self.CLIENT_DELAY
|
||||
|
||||
|
||||
return self._next_update_due + delay
|
||||
|
||||
|
||||
def _GetSerialisableInfo( self ):
|
||||
|
||||
serialisable_metadata = [ ( update_index, [ update_hash.encode( 'hex' ) for update_hash in update_hashes ], begin, end ) for ( update_index, ( update_hashes, begin, end ) ) in self._metadata.items() ]
|
||||
|
@ -1729,18 +1745,20 @@ class Metadata( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
def GetNextUpdateDueTime( self, from_client = False ):
|
||||
def GetNextUpdateDueString( self, from_client = False ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
delay = 0
|
||||
|
||||
if from_client:
|
||||
if self._next_update_due == 0:
|
||||
|
||||
delay = self.CLIENT_DELAY
|
||||
return 'have not yet synced metadata'
|
||||
|
||||
else:
|
||||
|
||||
update_due = self._GetNextUpdateDueTime( from_client )
|
||||
|
||||
return 'next update due ' + HydrusData.ConvertTimestampToPrettyPending( update_due )
|
||||
|
||||
|
||||
return self._next_update_due + delay
|
||||
|
||||
|
||||
|
||||
|
@ -1851,14 +1869,9 @@ class Metadata( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
delay = 0
|
||||
next_update_due_time = self._GetNextUpdateDueTime( from_client )
|
||||
|
||||
if from_client:
|
||||
|
||||
delay = self.CLIENT_DELAY
|
||||
|
||||
|
||||
return HydrusData.TimeHasPassed( self._next_update_due + delay )
|
||||
return HydrusData.TimeHasPassed( next_update_due_time )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -268,6 +268,43 @@ def GetTempPath( suffix = '' ):
|
|||
|
||||
return tempfile.mkstemp( suffix = suffix, prefix = 'hydrus' )
|
||||
|
||||
def HasSpaceForDBTransaction( db_dir, num_bytes ):
|
||||
|
||||
temp_dir = tempfile.gettempdir()
|
||||
|
||||
temp_disk_free_space = GetFreeSpace( temp_dir )
|
||||
|
||||
a = GetDevice( temp_dir )
|
||||
b = GetDevice( db_dir )
|
||||
|
||||
if GetDevice( temp_dir ) == GetDevice( db_dir ):
|
||||
|
||||
space_needed = int( num_bytes * 2.2 )
|
||||
|
||||
if temp_disk_free_space < space_needed:
|
||||
|
||||
return ( False, 'I believe you need about ' + HydrusData.ConvertIntToBytes( space_needed ) + ' on your db\'s partition, which I think also holds your temporary path, but you only seem to have ' + HydrusData.ConvertIntToBytes( temp_disk_free_space ) + '.' )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
space_needed = int( num_bytes * 1.1 )
|
||||
|
||||
if temp_disk_free_space < space_needed:
|
||||
|
||||
return ( False, 'I believe you need about ' + HydrusData.ConvertIntToBytes( space_needed ) + ' on your temporary path\'s partition, which I think is ' + temp_dir + ', but you only seem to have ' + HydrusData.ConvertIntToBytes( temp_disk_free_space ) + '.' )
|
||||
|
||||
|
||||
db_disk_free_space = GetFreeSpace( db_dir )
|
||||
|
||||
if db_disk_free_space < space_needed:
|
||||
|
||||
return ( False, 'I believe you need about ' + HydrusData.ConvertIntToBytes( space_needed ) + ' on your db\'s partition, but you only seem to have ' + HydrusData.ConvertIntToBytes( db_disk_free_space ) + '.' )
|
||||
|
||||
|
||||
|
||||
return ( True, 'You seem to have enough space!' )
|
||||
|
||||
def LaunchDirectory( path ):
|
||||
|
||||
def do_it():
|
||||
|
|
|
@ -107,18 +107,18 @@ class HydrusPubSub( object ):
|
|||
|
||||
if HydrusGlobals.pubsub_profile_mode:
|
||||
|
||||
text = 'Profiling ' + topic + ': ' + repr( callable )
|
||||
summary = 'Profiling ' + topic + ': ' + repr( callable )
|
||||
|
||||
if topic == 'message':
|
||||
|
||||
HydrusData.Print( text )
|
||||
HydrusData.Print( summary )
|
||||
|
||||
else:
|
||||
|
||||
HydrusData.ShowText( text )
|
||||
HydrusData.ShowText( summary )
|
||||
|
||||
|
||||
HydrusData.Profile( 'callable( *args, **kwargs )', globals(), locals() )
|
||||
HydrusData.Profile( summary, 'callable( *args, **kwargs )', globals(), locals() )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -165,4 +165,4 @@ class HydrusPubSub( object ):
|
|||
self._topics_to_method_names[ topic ].add( method_name )
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -231,7 +231,7 @@ class HydrusTagArchive( object ):
|
|||
try: hash_id = self._GetHashId( hash, read_only = True )
|
||||
except: return []
|
||||
|
||||
result = { tag for ( tag, ) in self._c.execute( 'SELECT tag FROM mappings, tags USING ( tag_id ) WHERE hash_id = ?;', ( hash_id, ) ) }
|
||||
result = { tag for ( tag, ) in self._c.execute( 'SELECT tag FROM mappings NATURAL JOIN tags WHERE hash_id = ?;', ( hash_id, ) ) }
|
||||
|
||||
return result
|
||||
|
||||
|
|
|
@ -137,8 +137,6 @@ def SortNumericTags( tags ):
|
|||
|
||||
def CheckTagNotEmpty( tag ):
|
||||
|
||||
empty_tag = False
|
||||
|
||||
( namespace, subtag ) = SplitTag( tag )
|
||||
|
||||
if subtag == '':
|
||||
|
|
|
@ -112,7 +112,7 @@ def ShutdownSiblingInstance( db_dir ):
|
|||
|
||||
port_found = True
|
||||
|
||||
HydrusData.Print( 'Sending shut down instruction...' )
|
||||
HydrusData.Print( u'Sending shut down instruction\u2026' )
|
||||
|
||||
connection.request( 'POST', '/shutdown' )
|
||||
|
||||
|
@ -160,6 +160,8 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
HydrusController.HydrusController.__init__( self, db_dir, no_daemons, no_wal )
|
||||
|
||||
self._name = 'server'
|
||||
|
||||
HydrusGlobals.server_controller = self
|
||||
|
||||
|
||||
|
@ -260,11 +262,11 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
def Exit( self ):
|
||||
|
||||
HydrusData.Print( 'Shutting down daemons and services...' )
|
||||
HydrusData.Print( u'Shutting down daemons and services\u2026' )
|
||||
|
||||
self.ShutdownView()
|
||||
|
||||
HydrusData.Print( 'Shutting down db...' )
|
||||
HydrusData.Print( u'Shutting down db\u2026' )
|
||||
|
||||
self.ShutdownModel()
|
||||
|
||||
|
@ -360,11 +362,11 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
HydrusData.RecordRunningStart( self._db_dir, 'server' )
|
||||
|
||||
HydrusData.Print( 'Initialising db...' )
|
||||
HydrusData.Print( u'Initialising db\u2026' )
|
||||
|
||||
self.InitModel()
|
||||
|
||||
HydrusData.Print( 'Initialising daemons and services...' )
|
||||
HydrusData.Print( u'Initialising daemons and services\u2026' )
|
||||
|
||||
self.InitView()
|
||||
|
||||
|
@ -384,7 +386,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
interrupt_received = True
|
||||
|
||||
HydrusData.Print( 'Received a keyboard interrupt...' )
|
||||
HydrusData.Print( u'Received a keyboard interrupt\u2026' )
|
||||
|
||||
def do_it():
|
||||
|
||||
|
@ -396,7 +398,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
|
||||
|
||||
HydrusData.Print( 'Shutting down controller...' )
|
||||
HydrusData.Print( u'Shutting down controller\u2026' )
|
||||
|
||||
|
||||
def SaveDirtyObjects( self ):
|
||||
|
@ -463,7 +465,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
def ShutdownFromServer( self ):
|
||||
|
||||
HydrusData.Print( 'Received a server shut down request...' )
|
||||
HydrusData.Print( u'Received a server shut down request\u2026' )
|
||||
|
||||
def do_it():
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
[ registration_key ] = self._GenerateRegistrationKeys( service_id, 1, service_admin_account_type_id, None, force_registration_key )
|
||||
|
||||
access_key = self._GetAccessKey( registration_key )
|
||||
access_key = self._GetAccessKey( service_key, registration_key )
|
||||
|
||||
if service_type in HC.REPOSITORIES:
|
||||
|
||||
|
@ -471,14 +471,16 @@ class DB( HydrusDB.HydrusDB ):
|
|||
return [ registration_key for ( registration_key, account_key, access_key ) in keys ]
|
||||
|
||||
|
||||
def _GetAccessKey( self, registration_key ):
|
||||
def _GetAccessKey( self, service_key, registration_key ):
|
||||
|
||||
service_id = self._GetServiceId( service_key )
|
||||
|
||||
# we generate a new access_key every time this is requested so that no one with access to the registration key can peek at the access_key before the legit user fetches it for real
|
||||
# the reg_key is deleted when the last-requested access_key is used to create a session, which calls getaccountkeyfromaccesskey
|
||||
|
||||
registration_key_sha256 = hashlib.sha256( registration_key ).digest()
|
||||
|
||||
result = self._c.execute( 'SELECT 1 FROM registration_keys WHERE registration_key = ?;', ( sqlite3.Binary( registration_key_sha256 ), ) ).fetchone()
|
||||
result = self._c.execute( 'SELECT 1 FROM registration_keys WHERE service_id = ? AND registration_key = ?;', ( service_id, sqlite3.Binary( registration_key_sha256 ) ) ).fetchone()
|
||||
|
||||
if result is None:
|
||||
|
||||
|
@ -487,7 +489,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
new_access_key = os.urandom( HC.HYDRUS_KEY_LENGTH )
|
||||
|
||||
self._c.execute( 'UPDATE registration_keys SET access_key = ? WHERE registration_key = ?;', ( sqlite3.Binary( new_access_key ), sqlite3.Binary( registration_key_sha256 ) ) )
|
||||
self._c.execute( 'UPDATE registration_keys SET access_key = ? WHERE service_id = ? AND registration_key = ?;', ( sqlite3.Binary( new_access_key ), service_id, sqlite3.Binary( registration_key_sha256 ) ) )
|
||||
|
||||
return new_access_key
|
||||
|
||||
|
@ -500,7 +502,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
dictionary = HydrusSerialisable.CreateFromString( dictionary_string )
|
||||
|
||||
return HydrusNetwork.Account( account_key, account_type, created, expires, dictionary )
|
||||
return HydrusNetwork.Account.GenerateAccountFromTuple( ( account_key, account_type, created, expires, dictionary ) )
|
||||
|
||||
|
||||
def _GetAccountFromAccountKey( self, service_key, account_key ):
|
||||
|
@ -543,9 +545,9 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
created = HydrusData.GetNow()
|
||||
|
||||
account = HydrusNetwork.Account.GenerateNewAccount( account_key, account_type, created, expires )
|
||||
account = HydrusNetwork.Account( account_key, account_type, created, expires )
|
||||
|
||||
( account_key, account_type, created, expires, dictionary ) = account.ToTuple()
|
||||
( account_key, account_type, created, expires, dictionary ) = HydrusNetwork.Account.GenerateTupleFromAccount( account )
|
||||
|
||||
dictionary_string = dictionary.DumpToString()
|
||||
|
||||
|
@ -628,7 +630,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
account_info = {}
|
||||
|
||||
|
||||
account_info[ 'account' ] = subject_account.ToSerialisableTuple()
|
||||
account_info[ 'account' ] = HydrusNetwork.Account.GenerateSerialisableTupleFromAccount( subject_account )
|
||||
|
||||
return account_info
|
||||
|
||||
|
@ -1283,7 +1285,9 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
else:
|
||||
|
||||
deleted_service_hash_ids = ( service_hash_id for ( service_hash_id, ) in self._c.execute( 'SELECT service_hash_id FROM ' + deleted_mappings_table_name + ' WHERE service_tag_id = ? AND service_hash_id = ?;', ( ( service_tag_id, service_hash_id ) for service_hash_id in service_hash_ids ) ) )
|
||||
select_statement = 'SELECT service_hash_id FROM ' + deleted_mappings_table_name + ' WHERE service_tag_id = ' + str( service_tag_id ) + ' AND service_hash_id IN %s;'
|
||||
|
||||
deleted_service_hash_ids = ( service_hash_id for ( service_hash_id, ) in self._SelectFromList( select_statement, service_hash_ids ) )
|
||||
|
||||
service_hash_ids = set( service_hash_ids ).difference( deleted_service_hash_ids )
|
||||
|
||||
|
@ -1588,7 +1592,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
now = HydrusData.GetNow()
|
||||
|
||||
self._c.execute( 'INSERT OR IGNORE INTO ' + deleted_tag_siblings_table_name + ' ( bad_tag_id, good_tag_id, account_id, sibling_timestamp ) VALUES ( ?, ?, ?, ? );', ( bad_service_tag_id, good_service_tag_id, account_id, now ) )
|
||||
self._c.execute( 'INSERT OR IGNORE INTO ' + deleted_tag_siblings_table_name + ' ( bad_service_tag_id, good_service_tag_id, account_id, sibling_timestamp ) VALUES ( ?, ?, ?, ? );', ( bad_service_tag_id, good_service_tag_id, account_id, now ) )
|
||||
|
||||
|
||||
def _RepositoryDenyFilePetition( self, service_id, service_hash_ids ):
|
||||
|
@ -1962,7 +1966,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateRepositoryMappingsTableNames( service_id )
|
||||
|
||||
master_hash_ids = [ master_hash_id for ( master_hash_id, ) in self._c.execute( 'SELECT master_hash_id FROM ' + hash_id_map_table_name + ' NATURAL JOIN ' + current_mappings_table_name + ' WHERE service_tag_id = ?;', ( service_id, service_tag_id ) ) ]
|
||||
master_hash_ids = [ master_hash_id for ( master_hash_id, ) in self._c.execute( 'SELECT master_hash_id FROM ' + hash_id_map_table_name + ' NATURAL JOIN ' + current_mappings_table_name + ' WHERE service_tag_id = ?;', ( service_tag_id, ) ) ]
|
||||
|
||||
return master_hash_ids
|
||||
|
||||
|
@ -3068,7 +3072,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
for account in accounts:
|
||||
|
||||
( account_key, account_type, created, expires, dictionary ) = account.ToTuple()
|
||||
( account_key, account_type, created, expires, dictionary ) = HydrusNetwork.Account.GenerateTupleFromAccount( account )
|
||||
|
||||
account_type_key = account_type.GetAccountTypeKey()
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class HydrusResourceAccessKey( HydrusServerResources.HydrusResource ):
|
|||
|
||||
registration_key = request.hydrus_args[ 'registration_key' ]
|
||||
|
||||
access_key = HydrusGlobals.server_controller.Read( 'access_key', registration_key )
|
||||
access_key = HydrusGlobals.server_controller.Read( 'access_key', self._service_key, registration_key )
|
||||
|
||||
body = HydrusNetwork.DumpToBodyString( { 'access_key' : access_key } )
|
||||
|
||||
|
|
|
@ -55,12 +55,11 @@ class TestManagers( unittest.TestCase ):
|
|||
|
||||
def test_services( self ):
|
||||
|
||||
def test_service( service, key, service_type, name, info ):
|
||||
def test_service( service, key, service_type, name ):
|
||||
|
||||
self.assertEqual( service.GetServiceKey(), key )
|
||||
self.assertEqual( service.GetServiceType(), service_type )
|
||||
self.assertEqual( service.GetName(), name )
|
||||
self.assertEqual( service.GetInfo(), info )
|
||||
|
||||
|
||||
repo_key = HydrusData.GenerateKey()
|
||||
|
@ -86,7 +85,7 @@ class TestManagers( unittest.TestCase ):
|
|||
|
||||
service = services_manager.GetService( repo_key )
|
||||
|
||||
test_service( service, repo_key, repo_type, repo_name, repo_info )
|
||||
test_service( service, repo_key, repo_type, repo_name )
|
||||
|
||||
service = services_manager.GetService( other_key )
|
||||
|
||||
|
|
|
@ -86,11 +86,11 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
self._clear_db()
|
||||
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'c' )
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'c*' )
|
||||
|
||||
self.assertEqual( result, [] )
|
||||
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'series:' )
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'series:*' )
|
||||
|
||||
self.assertEqual( result, [] )
|
||||
|
||||
|
@ -118,7 +118,7 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
# cars
|
||||
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'c', add_namespaceless = True )
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'c*', add_namespaceless = True )
|
||||
|
||||
preds = set()
|
||||
|
||||
|
@ -131,7 +131,7 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
# cars
|
||||
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'c', add_namespaceless = False )
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'c*', add_namespaceless = False )
|
||||
|
||||
preds = set()
|
||||
|
||||
|
@ -144,13 +144,17 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'ser' )
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'ser*' )
|
||||
|
||||
self.assertEqual( result, [] )
|
||||
preds = set()
|
||||
|
||||
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', min_current_count = 1 ) )
|
||||
|
||||
self.assertEqual( set( result ), preds )
|
||||
|
||||
#
|
||||
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'series:c' )
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'series:c*' )
|
||||
|
||||
pred = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', min_current_count = 1 )
|
||||
|
||||
|
@ -895,7 +899,11 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
result = self._read( 'pending', service_key )
|
||||
|
||||
self.assertIsInstance( result, HydrusNetwork.ClientToServerContentUpdatePackage )
|
||||
self.assertIsInstance( result, HydrusNetwork.ClientToServerUpdate )
|
||||
|
||||
self.assertTrue( result.HasContent() )
|
||||
|
||||
self.assertEqual( set( result.GetHashes() ), set( hashes ) )
|
||||
|
||||
#
|
||||
|
||||
|
@ -965,7 +973,7 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
result_service_keys = { service.GetServiceKey() for service in result }
|
||||
|
||||
self.assertItemsEqual( { CC.TRASH_SERVICE_KEY, CC.LOCAL_FILE_SERVICE_KEY, CC.COMBINED_LOCAL_FILE_SERVICE_KEY, CC.LOCAL_TAG_SERVICE_KEY }, result_service_keys )
|
||||
self.assertItemsEqual( { CC.TRASH_SERVICE_KEY, CC.LOCAL_FILE_SERVICE_KEY, CC.LOCAL_UPDATE_SERVICE_KEY, CC.COMBINED_LOCAL_FILE_SERVICE_KEY, CC.LOCAL_TAG_SERVICE_KEY }, result_service_keys )
|
||||
|
||||
#
|
||||
|
||||
|
@ -1234,8 +1242,8 @@ class TestServerDB( unittest.TestCase ):
|
|||
|
||||
r_key = r_keys[0]
|
||||
|
||||
access_key = self._read( 'access_key', r_key )
|
||||
access_key_2 = self._read( 'access_key', r_key )
|
||||
access_key = self._read( 'access_key', self._tag_service_key, r_key )
|
||||
access_key_2 = self._read( 'access_key', self._tag_service_key, r_key )
|
||||
|
||||
self.assertNotEqual( access_key, access_key_2 )
|
||||
|
||||
|
@ -1257,7 +1265,7 @@ class TestServerDB( unittest.TestCase ):
|
|||
|
||||
def _test_init_server_admin( self ):
|
||||
|
||||
result = self._read( 'init' ) # an access key
|
||||
result = self._read( 'access_key', HC.SERVER_ADMIN_KEY, 'init' )
|
||||
|
||||
self.assertEqual( type( result ), str )
|
||||
self.assertEqual( len( result ), 32 )
|
||||
|
|
|
@ -166,10 +166,10 @@ class TestNonDBDialogs( unittest.TestCase ):
|
|||
|
||||
list_of_tuples = [ ( 'a', 123 ), ( 'b', 456 ), ( 'c', 789 ) ]
|
||||
|
||||
with ClientGUIDialogs.DialogSelectFromList( None, 'select from a list of strings', [ 'a', 'b', 'c' ] ) as dlg:
|
||||
with ClientGUIDialogs.DialogSelectFromList( None, 'select from a list of strings', list_of_tuples ) as dlg:
|
||||
|
||||
wx.CallLater( 500, dlg._list.Select, 1 )
|
||||
wx.CallLater( 1000, PressKey, dlg._strings, wx.WXK_RETURN )
|
||||
wx.CallLater( 1000, PressKey, dlg._list, wx.WXK_RETURN )
|
||||
|
||||
result = dlg.ShowModal()
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
import ClientConstants as CC
|
||||
import ClientSearch
|
||||
import HydrusConstants as HC
|
||||
import HydrusNetwork
|
||||
import HydrusSerialisable
|
||||
import unittest
|
||||
|
||||
class TestServer( unittest.TestCase ):
|
||||
|
||||
def _dump_and_load_and_test( self, obj, test_func ):
|
||||
|
||||
serialisable_tuple = obj.GetSerialisableTuple()
|
||||
|
||||
self.assertIsInstance( serialisable_tuple, tuple )
|
||||
|
||||
if isinstance( obj, HydrusSerialisable.SerialisableBaseNamed ):
|
||||
|
||||
( serialisable_type, name, version, serialisable_info ) = serialisable_tuple
|
||||
|
||||
elif isinstance( obj, HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
( serialisable_type, version, serialisable_info ) = serialisable_tuple
|
||||
|
||||
|
||||
self.assertEqual( serialisable_type, obj.SERIALISABLE_TYPE )
|
||||
self.assertEqual( version, obj.SERIALISABLE_VERSION )
|
||||
|
||||
dupe_obj = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_tuple )
|
||||
|
||||
self.assertIsNot( obj, dupe_obj )
|
||||
|
||||
test_func( obj, dupe_obj )
|
||||
|
||||
#
|
||||
|
||||
json_string = obj.DumpToString()
|
||||
|
||||
self.assertIsInstance( json_string, str )
|
||||
|
||||
dupe_obj = HydrusSerialisable.CreateFromString( json_string )
|
||||
|
||||
self.assertIsNot( obj, dupe_obj )
|
||||
|
||||
test_func( obj, dupe_obj )
|
||||
|
||||
#
|
||||
|
||||
network_string = obj.DumpToNetworkString()
|
||||
|
||||
self.assertIsInstance( network_string, str )
|
||||
|
||||
dupe_obj = HydrusSerialisable.CreateFromNetworkString( network_string )
|
||||
|
||||
self.assertIsNot( obj, dupe_obj )
|
||||
|
||||
test_func( obj, dupe_obj )
|
||||
|
||||
|
||||
def test_basics( self ):
|
||||
|
||||
d = HydrusSerialisable.SerialisableDictionary()
|
||||
|
||||
d[ 1 ] = 2
|
||||
d[ 3 ] = 'test1'
|
||||
|
||||
d[ 'test2' ] = 4
|
||||
d[ 'test3' ] = 5
|
||||
|
||||
d[ 6 ] = HydrusSerialisable.SerialisableDictionary( { i : 'test' + str( i ) for i in range( 20 ) } )
|
||||
d[ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'test pred 1' ) ] = 56
|
||||
|
||||
d[ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'test pred 2' ) ] = HydrusSerialisable.SerialisableList( [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'test' + str( i ) ) for i in range( 10 ) ] )
|
||||
|
||||
def test( obj, dupe_obj ):
|
||||
|
||||
self.assertEqual( len( obj.items() ), len( dupe_obj.items() ) )
|
||||
|
||||
for ( key, value ) in obj.items():
|
||||
|
||||
self.assertEqual( value, dupe_obj[ key ] )
|
||||
|
||||
|
||||
|
||||
self._dump_and_load_and_test( d, test )
|
||||
|
||||
|
|
@ -3,6 +3,7 @@ import ClientData
|
|||
import ClientFiles
|
||||
import ClientLocalServer
|
||||
import ClientMedia
|
||||
import ClientServices
|
||||
import hashlib
|
||||
import httplib
|
||||
import HydrusConstants as HC
|
||||
|
@ -45,9 +46,11 @@ class TestServer( unittest.TestCase ):
|
|||
|
||||
services = []
|
||||
|
||||
self._file_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.FILE_REPOSITORY, 'file repo' )
|
||||
self._tag_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.TAG_REPOSITORY, 'tag repo' )
|
||||
self._admin_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.SERVER_ADMIN, 'server admin' )
|
||||
self._file_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.FILE_REPOSITORY, 'file repo', HC.DEFAULT_SERVICE_PORT + 1 )
|
||||
self._tag_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.TAG_REPOSITORY, 'tag repo', HC.DEFAULT_SERVICE_PORT )
|
||||
self._admin_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.SERVER_ADMIN, 'server admin', HC.DEFAULT_SERVER_ADMIN_PORT )
|
||||
|
||||
self._local_booru = ClientServices.GenerateService( HydrusData.GenerateKey(), HC.LOCAL_BOORU, 'local booru' )
|
||||
|
||||
services_manager = HydrusGlobals.test_controller.GetServicesManager()
|
||||
|
||||
|
@ -82,12 +85,11 @@ class TestServer( unittest.TestCase ):
|
|||
|
||||
context_factory = twisted.internet.ssl.DefaultOpenSSLContextFactory( self._ssl_key_path, self._ssl_cert_path )
|
||||
|
||||
reactor.listenSSL( HC.DEFAULT_SERVER_ADMIN_PORT, ServerServer.HydrusServiceAdmin( self._admin_service.GetServiceKey(), HC.SERVER_ADMIN, 'hello' ), context_factory )
|
||||
reactor.listenSSL( HC.DEFAULT_SERVICE_PORT, ServerServer.HydrusServiceRepositoryFile( self._file_service.GetServiceKey(), HC.FILE_REPOSITORY, 'hello' ), context_factory )
|
||||
reactor.listenSSL( HC.DEFAULT_SERVICE_PORT + 1, ServerServer.HydrusServiceRepositoryTag( self._tag_service.GetServiceKey(), HC.TAG_REPOSITORY, 'hello' ), context_factory )
|
||||
reactor.listenSSL( HC.DEFAULT_SERVER_ADMIN_PORT, ServerServer.HydrusServiceAdmin( self._admin_service ), context_factory )
|
||||
reactor.listenSSL( HC.DEFAULT_SERVICE_PORT, ServerServer.HydrusServiceRepositoryFile( self._file_service ), context_factory )
|
||||
reactor.listenSSL( HC.DEFAULT_SERVICE_PORT + 1, ServerServer.HydrusServiceRepositoryTag( self._tag_service ), context_factory )
|
||||
|
||||
reactor.listenTCP( HC.DEFAULT_LOCAL_FILE_PORT, ClientLocalServer.HydrusServiceLocal( CC.COMBINED_LOCAL_FILE_SERVICE_KEY, HC.COMBINED_LOCAL_FILE, 'hello' ) )
|
||||
reactor.listenTCP( HC.DEFAULT_LOCAL_BOORU_PORT, ClientLocalServer.HydrusServiceBooru( CC.LOCAL_BOORU_SERVICE_KEY, HC.LOCAL_BOORU, 'hello' ) )
|
||||
reactor.listenTCP( HC.DEFAULT_LOCAL_BOORU_PORT, ClientLocalServer.HydrusServiceBooru( self._local_booru ) )
|
||||
|
||||
|
||||
reactor.callFromThread( TWISTEDSetup )
|
||||
|
@ -136,51 +138,6 @@ class TestServer( unittest.TestCase ):
|
|||
self.assertEqual( data, favicon )
|
||||
|
||||
|
||||
def _test_local_file( self, host, port ):
|
||||
|
||||
connection = httplib.HTTPConnection( host, port, timeout = 10 )
|
||||
|
||||
#
|
||||
|
||||
client_files_default = os.path.join( TestConstants.DB_DIR, 'client_files' )
|
||||
|
||||
hash_encoded = self._file_hash.encode( 'hex' )
|
||||
|
||||
prefix = hash_encoded[:2]
|
||||
|
||||
path = os.path.join( client_files_default, 'f' + prefix, hash_encoded + '.jpg' )
|
||||
|
||||
with open( path, 'wb' ) as f: f.write( EXAMPLE_FILE )
|
||||
|
||||
connection.request( 'GET', '/file?hash=' + self._file_hash.encode( 'hex' ) )
|
||||
|
||||
response = connection.getresponse()
|
||||
|
||||
data = response.read()
|
||||
|
||||
self.assertEqual( data, EXAMPLE_FILE )
|
||||
|
||||
try: os.remove( path )
|
||||
except: pass
|
||||
|
||||
#
|
||||
|
||||
path = os.path.join( client_files_default, 't' + prefix, hash_encoded + '.thumbnail' )
|
||||
|
||||
with open( path, 'wb' ) as f: f.write( EXAMPLE_THUMBNAIL )
|
||||
|
||||
connection.request( 'GET', '/thumbnail?hash=' + self._file_hash.encode( 'hex' ) )
|
||||
|
||||
response = connection.getresponse()
|
||||
|
||||
data = response.read()
|
||||
|
||||
self.assertEqual( data, EXAMPLE_THUMBNAIL )
|
||||
|
||||
try: os.remove( path )
|
||||
except: pass
|
||||
|
||||
|
||||
def _test_file_repo( self, service, host, port ):
|
||||
|
||||
info = service.GetInfo()
|
||||
|
@ -577,15 +534,6 @@ class TestServer( unittest.TestCase ):
|
|||
pass
|
||||
|
||||
|
||||
def test_local_service( self ):
|
||||
|
||||
host = '127.0.0.1'
|
||||
port = HC.DEFAULT_LOCAL_FILE_PORT
|
||||
|
||||
self._test_basics( host, port, https = False )
|
||||
self._test_local_file( host, port )
|
||||
|
||||
|
||||
def test_repository_file( self ):
|
||||
|
||||
host = '127.0.0.1'
|
||||
|
|
|
@ -710,15 +710,15 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
|
||||
def test_autocomplete( self ):
|
||||
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'ishy' ) ), set( [ 'ishygddt', 'i sure hope you guys don\'t do that' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'i su' ) ), set( [ 'ishygddt', 'i sure hope you guys don\'t do that' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'ishy*' ) ), set( [ 'ishygddt', 'i sure hope you guys don\'t do that' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'i su*' ) ), set( [ 'ishygddt', 'i sure hope you guys don\'t do that' ] ) )
|
||||
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'ayan' ) ), set( [ 'character:rei ayanami', 'character:ayanami rei' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'rei' ) ), set( [ 'character:rei ayanami', 'character:ayanami rei' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'character:ayan' ) ), set( [ 'character:rei ayanami', 'character:ayanami rei' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'character:rei' ) ), set( [ 'character:rei ayanami', 'character:ayanami rei' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'ayan*' ) ), set( [ 'character:rei ayanami', 'character:ayanami rei' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'rei*' ) ), set( [ 'character:rei ayanami', 'character:ayanami rei' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'character:ayan*' ) ), set( [ 'character:rei ayanami', 'character:ayanami rei' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'character:rei*' ) ), set( [ 'character:rei ayanami', 'character:ayanami rei' ] ) )
|
||||
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._second_key, 'ishy' ) ), set() )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._second_key, 'ishy*' ) ), set() )
|
||||
|
||||
|
||||
def test_collapse_predicates( self ):
|
||||
|
@ -747,7 +747,7 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
|
||||
def test_chain( self ):
|
||||
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'chai' ) ), set( [ 'chain_a', 'chain_b', 'chain_c' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'chai*' ) ), set( [ 'chain_a', 'chain_b', 'chain_c' ] ) )
|
||||
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'chain_a' ), 'chain_c' )
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'chain_b' ), 'chain_c' )
|
||||
|
@ -763,7 +763,7 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
|
||||
def test_current( self ):
|
||||
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._second_key, 'curr' ) ), set( [ 'current_a', 'current_b' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._second_key, 'curr*' ) ), set( [ 'current_a', 'current_b' ] ) )
|
||||
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'current_a' ), 'current_b' )
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'current_b' ), None )
|
||||
|
@ -780,7 +780,7 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
|
||||
def test_deleted( self ):
|
||||
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'dele' ) ), set() )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'dele*' ) ), set() )
|
||||
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'deleted_a' ), None )
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'deleted_b' ), None )
|
||||
|
@ -795,7 +795,7 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
|
||||
def test_no_loop( self ):
|
||||
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'loop' ) ), set( [ 'loop_a', 'loop_b', 'loop_c' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'loop*' ) ), set( [ 'loop_a', 'loop_b', 'loop_c' ] ) )
|
||||
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'closed_loop' ), None )
|
||||
|
||||
|
@ -808,7 +808,7 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
|
||||
def test_not_exist( self ):
|
||||
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._second_key, 'not_' ) ), set() )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._second_key, 'not_*' ) ), set() )
|
||||
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'not_exist' ), None )
|
||||
|
||||
|
@ -821,7 +821,7 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
|
||||
def test_pending_overwrite( self ):
|
||||
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._second_key, 'pend' ) ), set( [ 'pending_a', 'pending_b' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._second_key, 'pend*' ) ), set( [ 'pending_a', 'pending_b' ] ) )
|
||||
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'pending_a' ), 'pending_b' )
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'pending_b' ), None )
|
||||
|
@ -838,7 +838,7 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
|
||||
def test_petitioned_no_overwrite( self ):
|
||||
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._second_key, 'petitioned_a' ) ), set( [ 'petitioned_a', 'petitioned_b' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._second_key, 'petitioned_a*' ) ), set( [ 'petitioned_a', 'petitioned_b' ] ) )
|
||||
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'petitioned_a' ), 'petitioned_b' )
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._second_key, 'petitioned_b' ), None )
|
||||
|
@ -855,7 +855,7 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
|
||||
def test_tree( self ):
|
||||
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'tree' ) ), set( [ 'tree_1', 'tree_2', 'tree_3', 'tree_4', 'tree_5', 'tree_6' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.GetAutocompleteSiblings( self._first_key, 'tree*' ) ), set( [ 'tree_1', 'tree_2', 'tree_3', 'tree_4', 'tree_5', 'tree_6' ] ) )
|
||||
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'tree_1' ), 'tree_6' )
|
||||
self.assertEqual( self._tag_siblings_manager.GetSibling( self._first_key, 'tree_2' ), 'tree_6' )
|
||||
|
|
|
@ -83,7 +83,7 @@ try:
|
|||
|
||||
if action in ( 'start', 'restart' ):
|
||||
|
||||
HydrusData.Print( 'Initialising controller...' )
|
||||
HydrusData.Print( u'Initialising controller\u2026' )
|
||||
|
||||
threading.Thread( target = reactor.run, kwargs = { 'installSignalHandlers' : 0 } ).start()
|
||||
|
||||
|
|
12
test.py
12
test.py
|
@ -24,6 +24,7 @@ from include import TestDB
|
|||
from include import TestFunctions
|
||||
from include import TestClientImageHandling
|
||||
from include import TestHydrusNATPunch
|
||||
from include import TestHydrusSerialisable
|
||||
from include import TestHydrusServer
|
||||
from include import TestHydrusSessions
|
||||
from include import TestHydrusTags
|
||||
|
@ -248,7 +249,15 @@ class Controller( object ):
|
|||
return HydrusGlobals.model_shutdown
|
||||
|
||||
|
||||
def Read( self, name, *args, **kwargs ): return self._reads[ name ]
|
||||
def Read( self, name, *args, **kwargs ):
|
||||
|
||||
return self._reads[ name ]
|
||||
|
||||
|
||||
def RequestMade( self, num_bytes ):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def ResetIdleTimer( self ): pass
|
||||
|
||||
|
@ -269,6 +278,7 @@ class Controller( object ):
|
|||
if run_all or only_run == 'functions': suites.append( unittest.TestLoader().loadTestsFromModule( TestFunctions ) )
|
||||
if run_all or only_run == 'image': suites.append( unittest.TestLoader().loadTestsFromModule( TestClientImageHandling ) )
|
||||
if run_all or only_run == 'nat': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusNATPunch ) )
|
||||
if run_all or only_run == 'serialisable': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusSerialisable ) )
|
||||
if run_all or only_run == 'server': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusServer ) )
|
||||
if run_all or only_run == 'sessions': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusSessions ) )
|
||||
if run_all or only_run == 'tags': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusTags ) )
|
||||
|
|
Loading…
Reference in New Issue