Version 353
This commit is contained in:
parent
93e8578e99
commit
4dcdc1e324
|
@ -8,6 +8,52 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 353</h3></li>
|
||||
<ul>
|
||||
<li>duplicate filter:</li>
|
||||
<li>duplicate action options no longer handle file deletion</li>
|
||||
<li>renamed 'not duplicates' across the program to 'not related' or 'false positive'</li>
|
||||
<li>'alternates' and 'not related/false positive' duplicate actions no longer have duplicate action options. no merge content update now occurs on these actions</li>
|
||||
<li>the duplicate filter hover panel now splits 'this is better' decisions into two buttons--whether to delete or keep the worse file</li>
|
||||
<li>when selecting 'custom action' in the duplicate filter hover panel, it now asks if you would like to delete the current file, the other file, or both</li>
|
||||
<li>the 'duplicate_filter_this_is_better' shortcut action will be auto-updated to 'duplicate_filter_this_is_better_and_delete_other'. an alternate 'duplicate_filter_this_is_better_but_keep_both' is now also available</li>
|
||||
<li>the 'duplicate_filter_not_dupes' shortcut action will be auto-updated to 'duplicate_filter_false_positive'</li>
|
||||
<li>separated the buttons on the duplicate filter hover panel to more carefully split 'yes, files are duplicates' vs other decisions</li>
|
||||
<li>in prep for the duplicate db overhaul, refactored all PHash search code and Duplicate management code apart</li>
|
||||
<li>misc other prep work for duplicate db overhaul</li>
|
||||
<li>.</li>
|
||||
<li>file maintenance:</li>
|
||||
<li>wrote a new unified manager to handle various long-term file maintenance tasks like regenerating file metadata and thumbnails</li>
|
||||
<li>options to govern how this manager can run are now in options->maintenance and processing. you can enable it for idle and shutdown maintenance time and give it a throttle to limit how fast it will work on files, defaulting to 200 per day</li>
|
||||
<li>unified the previous db-level attempts at file maintenance to the new system, which supports async job queueing, and moving regen code up to the new manager, out of the db lock</li>
|
||||
<li>unified a variety of file and thumbnail regen code to work through the new simpler and saner path</li>
|
||||
<li>the right-click->regen thumbnail commands now run through the new manager and no longer need a modal popup. you can keep browsing while they work. they will also not hang the ui as the old system could on big jobs</li>
|
||||
<li>when right-click->regenning on more than 50 thumbnails, you now get a dialog asking if you want to do the job now or put it off later</li>
|
||||
<li>file maintenance tasks can now run in shutdown time! you will get previews of the jobs with file counts and status progress reports on the shutdown splash</li>
|
||||
<li>cleaned up some file extension renaming and dupe-removing code</li>
|
||||
<li>in future, I will move the current file integrity check to this new system and have some ui to prompt and set up other big jobs, like fixing various historical misparsing issues</li>
|
||||
<li>thumbnail resizing during thumbnail fade that resizes down is now more efficient</li>
|
||||
<li>moved the ClientFilesManager to ClientFiles.py</li>
|
||||
<li>.</li>
|
||||
<li>the rest:</li>
|
||||
<li>the 'manage upnp' dialog now moves the duplicated external ip display from the column up to the status text at the top. it fetches the ip after the initial mappings fetch is done. this ip is no longer affected by the external host override option</li>
|
||||
<li>cleaned up options->connection page and removed the now defunct external host override option</li>
|
||||
<li>the manage services page for the local booru now has optional override for scheme, host, and port for the 'copy external url' function</li>
|
||||
<li>fixed an issue with the recent 'collect by' session saving where a restored session that needed a collect was not sorted</li>
|
||||
<li>fixed an issue with collections being sorted by approx bitrate</li>
|
||||
<li>added a new checkbox to options->sort/collect to set it so the default sort updates every time you choose a new sort anywhere</li>
|
||||
<li>fixed an issue with 'remove trashed files from view', which was incorrectly removing on 'all local files' pages</li>
|
||||
<li>the 'all local files' file domain, which is frequently confusing to new users, is now no longer an option for new file pages or the autocomplete file domain if the user is not in advanced mode</li>
|
||||
<li>the client now searches for versions of urls both with and without a final '/' character when looking up file url import status at the db level and in import lists. system:known_url is unfortunately still an inefficient mess</li>
|
||||
<li>improved how the server code deals with some connectionLost errors</li>
|
||||
<li>cleaned up and unified some older dialog button code</li>
|
||||
<li>fixed a problem in manage tag siblings when petitioning existing pairs and then cancelling when asked for a reason</li>
|
||||
<li>fixed a miscount issue when uploading pending tags while many new tags are coming in. progress would sometimes be -754/1,234, ha ha</li>
|
||||
<li>db maintenance, repository sync, and file maintenance processing will all now wake on a force idle mode call</li>
|
||||
<li>deleted some old code</li>
|
||||
<li>misc fixes and cleanup</li>
|
||||
<li>some misc gui layout fixes</li>
|
||||
</ul>
|
||||
<li><h3>version 352</h3></li>
|
||||
<ul>
|
||||
<li>the client now supports importing .ico files! (.cur should be supported too)</li>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -349,11 +349,11 @@ SHORTCUTS_RESERVED_NAMES = [ 'archive_delete_filter', 'duplicate_filter', 'media
|
|||
|
||||
# shortcut commands
|
||||
|
||||
SHORTCUTS_MEDIA_ACTIONS = [ 'manage_file_tags', 'manage_file_ratings', 'manage_file_urls', 'manage_file_notes', 'archive_file', 'inbox_file', 'delete_file', 'export_files', 'export_files_quick_auto_export', 'remove_file_from_view', 'open_file_in_external_program', 'open_selection_in_new_page', 'launch_the_archive_delete_filter', 'copy_bmp', 'copy_file', 'copy_path', 'copy_sha256_hash', 'get_similar_to_exact', 'get_similar_to_very_similar', 'get_similar_to_similar', 'get_similar_to_speculative', 'duplicate_media_remove_relationships', 'duplicate_media_reset_to_potential', 'duplicate_media_set_alternate', 'duplicate_media_set_alternate_collections', 'duplicate_media_set_custom', 'duplicate_media_set_focused_better', 'duplicate_media_set_not_duplicate', 'duplicate_media_set_same_quality', 'open_known_url' ]
|
||||
SHORTCUTS_MEDIA_ACTIONS = [ 'manage_file_tags', 'manage_file_ratings', 'manage_file_urls', 'manage_file_notes', 'archive_file', 'inbox_file', 'delete_file', 'export_files', 'export_files_quick_auto_export', 'remove_file_from_view', 'open_file_in_external_program', 'open_selection_in_new_page', 'launch_the_archive_delete_filter', 'copy_bmp', 'copy_file', 'copy_path', 'copy_sha256_hash', 'get_similar_to_exact', 'get_similar_to_very_similar', 'get_similar_to_similar', 'get_similar_to_speculative', 'duplicate_media_remove_relationships', 'duplicate_media_reset_to_potential', 'duplicate_media_set_alternate', 'duplicate_media_set_alternate_collections', 'duplicate_media_set_custom', 'duplicate_media_set_focused_better', 'duplicate_media_set_false_positive', 'duplicate_media_set_same_quality', 'open_known_url' ]
|
||||
SHORTCUTS_MEDIA_VIEWER_ACTIONS = [ 'move_animation_to_previous_frame', 'move_animation_to_next_frame', 'switch_between_fullscreen_borderless_and_regular_framed_window', 'pan_up', 'pan_down', 'pan_left', 'pan_right', 'zoom_in', 'zoom_out', 'switch_between_100_percent_and_canvas_zoom', 'flip_darkmode' ]
|
||||
SHORTCUTS_MEDIA_VIEWER_BROWSER_ACTIONS = [ 'view_next', 'view_first', 'view_last', 'view_previous' ]
|
||||
SHORTCUTS_MAIN_GUI_ACTIONS = [ 'refresh', 'new_page', 'new_page_of_pages', 'new_duplicate_filter_page', 'new_gallery_downloader_page', 'new_url_downloader_page', 'new_simple_downloader_page', 'new_watcher_downloader_page', 'synchronised_wait_switch', 'set_media_focus', 'show_hide_splitters', 'set_search_focus', 'unclose_page', 'close_page', 'redo', 'undo', 'flip_darkmode', 'check_all_import_folders', 'flip_debug_force_idle_mode_do_not_set_this' ]
|
||||
SHORTCUTS_DUPLICATE_FILTER_ACTIONS = [ 'duplicate_filter_this_is_better', 'duplicate_filter_exactly_the_same', 'duplicate_filter_alternates', 'duplicate_filter_not_dupes', 'duplicate_filter_custom_action', 'duplicate_filter_skip', 'duplicate_filter_back' ]
|
||||
SHORTCUTS_DUPLICATE_FILTER_ACTIONS = [ 'duplicate_filter_this_is_better_and_delete_other', 'duplicate_filter_this_is_better_but_keep_both', 'duplicate_filter_exactly_the_same', 'duplicate_filter_alternates', 'duplicate_filter_false_positive', 'duplicate_filter_custom_action', 'duplicate_filter_skip', 'duplicate_filter_back' ]
|
||||
SHORTCUTS_ARCHIVE_DELETE_FILTER_ACTIONS = [ 'archive_delete_filter_keep', 'archive_delete_filter_delete', 'archive_delete_filter_skip', 'archive_delete_filter_back' ]
|
||||
|
||||
simple_shortcut_name_to_action_lookup = {}
|
||||
|
|
|
@ -16,6 +16,7 @@ from . import ClientCaches
|
|||
from . import ClientData
|
||||
from . import ClientDaemons
|
||||
from . import ClientDefaults
|
||||
from . import ClientFiles
|
||||
from . import ClientGUICommon
|
||||
from . import ClientGUIMenus
|
||||
from . import ClientNetworking
|
||||
|
@ -428,6 +429,11 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
|
||||
|
||||
if self.new_options.GetBoolean( 'file_maintenance_on_shutdown' ):
|
||||
|
||||
self.files_maintenance_manager.DoMaintenance( only_when_idle = False, stop_time = stop_time )
|
||||
|
||||
|
||||
self.Write( 'last_shutdown_work_time', HydrusData.GetNow() )
|
||||
|
||||
|
||||
|
@ -565,6 +571,11 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
|
||||
|
||||
if self.new_options.GetBoolean( 'file_maintenance_on_shutdown' ):
|
||||
|
||||
work_to_do.extend( self.files_maintenance_manager.GetIdleShutdownWorkDue() )
|
||||
|
||||
|
||||
return work_to_do
|
||||
|
||||
|
||||
|
@ -585,7 +596,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
self.client_files_manager = ClientCaches.ClientFilesManager( self )
|
||||
self.client_files_manager = ClientFiles.ClientFilesManager( self )
|
||||
|
||||
missing_locations = self.client_files_manager.GetMissing()
|
||||
|
||||
|
@ -598,7 +609,9 @@ class Controller( HydrusController.HydrusController ):
|
|||
return missing_locations
|
||||
|
||||
|
||||
self.client_files_manager = ClientCaches.ClientFilesManager( self )
|
||||
self.client_files_manager = ClientFiles.ClientFilesManager( self )
|
||||
|
||||
self.files_maintenance_manager = ClientFiles.FilesMaintenanceManager( self )
|
||||
|
||||
missing_locations = self.client_files_manager.GetMissing()
|
||||
|
||||
|
@ -811,7 +824,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
self._daemons.append( HydrusThreading.DAEMONForegroundWorker( self, 'DownloadFiles', ClientDaemons.DAEMONDownloadFiles, ( 'notify_new_downloads', 'notify_new_permissions' ) ) )
|
||||
self._daemons.append( HydrusThreading.DAEMONForegroundWorker( self, 'SynchroniseSubscriptions', ClientDaemons.DAEMONSynchroniseSubscriptions, ( 'notify_restart_subs_sync_daemon', 'notify_new_subscriptions' ), period = 4 * 3600, init_wait = 60, pre_call_wait = 3 ) )
|
||||
self._daemons.append( HydrusThreading.DAEMONForegroundWorker( self, 'MaintainTrash', ClientDaemons.DAEMONMaintainTrash, init_wait = 120 ) )
|
||||
self._daemons.append( HydrusThreading.DAEMONForegroundWorker( self, 'SynchroniseRepositories', ClientDaemons.DAEMONSynchroniseRepositories, ( 'notify_restart_repo_sync_daemon', 'notify_new_permissions' ), period = 4 * 3600, pre_call_wait = 1 ) )
|
||||
self._daemons.append( HydrusThreading.DAEMONForegroundWorker( self, 'SynchroniseRepositories', ClientDaemons.DAEMONSynchroniseRepositories, ( 'notify_restart_repo_sync_daemon', 'notify_new_permissions', 'wake_idle_workers' ), period = 4 * 3600, pre_call_wait = 1 ) )
|
||||
|
||||
|
||||
job = self.CallRepeating( 5.0, 180.0, ClientDaemons.DAEMONCheckImportFolders )
|
||||
|
@ -820,6 +833,11 @@ class Controller( HydrusController.HydrusController ):
|
|||
job.ShouldDelayOnWakeup( True )
|
||||
self._daemon_jobs[ 'import_folders' ] = job
|
||||
|
||||
job = self.CallRepeating( 60.0, 300.0, self.files_maintenance_manager.DoMaintenance )
|
||||
job.ShouldDelayOnWakeup( True )
|
||||
job.WakeOnPubSub( 'wake_idle_workers' )
|
||||
self._daemon_jobs[ 'maintain_files' ] = job
|
||||
|
||||
job = self.CallRepeating( 5.0, 180.0, ClientDaemons.DAEMONCheckExportFolders )
|
||||
job.WakeOnPubSub( 'notify_restart_export_folders_daemon' )
|
||||
job.WakeOnPubSub( 'notify_new_export_folders' )
|
||||
|
@ -906,12 +924,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
search_stop_time = HydrusData.GetNow() + 60
|
||||
|
||||
|
||||
self.WriteSynchronous( 'maintain_similar_files_duplicate_pairs', search_distance, stop_time = search_stop_time, abandon_if_other_work_to_do = True )
|
||||
|
||||
|
||||
if stop_time is None or not HydrusData.TimeHasPassed( stop_time ):
|
||||
|
||||
self.WriteSynchronous( 'maintain_file_reparsing', stop_time = stop_time )
|
||||
self.WriteSynchronous( 'maintain_similar_files_search_for_potential_duplicates', search_distance, stop_time = search_stop_time, abandon_if_other_work_to_do = True )
|
||||
|
||||
|
||||
if stop_time is None or not HydrusData.TimeHasPassed( stop_time ):
|
||||
|
|
3454
include/ClientDB.py
3454
include/ClientDB.py
File diff suppressed because it is too large
Load Diff
|
@ -503,7 +503,7 @@ class ApplicationCommand( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_APPLICATION_COMMAND
|
||||
SERIALISABLE_NAME = 'Application Command'
|
||||
SERIALISABLE_VERSION = 1
|
||||
SERIALISABLE_VERSION = 2
|
||||
|
||||
def __init__( self, command_type = None, data = None ):
|
||||
|
||||
|
@ -560,6 +560,30 @@ class ApplicationCommand( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
|
||||
|
||||
if version == 1:
|
||||
|
||||
( command_type, serialisable_data ) = old_serialisable_info
|
||||
|
||||
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
|
||||
|
||||
if serialisable_data == 'duplicate_filter_this_is_better':
|
||||
|
||||
serialisable_data = 'duplicate_filter_this_is_better_and_delete_other'
|
||||
|
||||
elif serialisable_data == 'duplicate_filter_not_dupes':
|
||||
|
||||
serialisable_data = 'duplicate_filter_false_positive'
|
||||
|
||||
|
||||
|
||||
new_serialisable_info = ( command_type, serialisable_data )
|
||||
|
||||
return ( 2, new_serialisable_info )
|
||||
|
||||
|
||||
|
||||
def GetCommandType( self ):
|
||||
|
||||
return self._command_type
|
||||
|
|
|
@ -37,7 +37,6 @@ def GetClientDefaultOptions():
|
|||
options[ 'trash_max_size' ] = 2048
|
||||
options[ 'remove_trashed_files' ] = False
|
||||
options[ 'remove_filtered_files' ] = False
|
||||
options[ 'external_host' ] = None
|
||||
options[ 'gallery_file_limit' ] = 2000
|
||||
options[ 'always_embed_autocompletes' ] = HC.PLATFORM_LINUX or HC.PLATFORM_OSX
|
||||
options[ 'confirm_trash' ] = True
|
||||
|
@ -335,11 +334,11 @@ def GetDefaultShortcuts():
|
|||
|
||||
duplicate_filter = ClientGUIShortcuts.Shortcuts( 'duplicate_filter' )
|
||||
|
||||
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
|
||||
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better_and_delete_other' ) )
|
||||
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_RIGHT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_alternates' ) )
|
||||
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_MIDDLE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_back' ) )
|
||||
|
||||
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) )
|
||||
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_SPACE, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better_and_delete_other' ) )
|
||||
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
|
||||
duplicate_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD, wx.WXK_NUMPAD_UP, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ class DuplicateActionOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_DUPLICATE_ACTION_OPTIONS
|
||||
SERIALISABLE_NAME = 'Duplicate Action Options'
|
||||
SERIALISABLE_VERSION = 3
|
||||
SERIALISABLE_VERSION = 4
|
||||
|
||||
def __init__( self, tag_service_actions = None, rating_service_actions = None, delete_second_file = False, sync_archive = False, delete_both_files = False, sync_urls_action = None ):
|
||||
def __init__( self, tag_service_actions = None, rating_service_actions = None, sync_archive = False, sync_urls_action = None ):
|
||||
|
||||
if tag_service_actions is None:
|
||||
|
||||
|
@ -28,9 +28,7 @@ class DuplicateActionOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
self._tag_service_actions = tag_service_actions
|
||||
self._rating_service_actions = rating_service_actions
|
||||
self._delete_second_file = delete_second_file
|
||||
self._sync_archive = sync_archive
|
||||
self._delete_both_files = delete_both_files
|
||||
self._sync_urls_action = sync_urls_action
|
||||
|
||||
|
||||
|
@ -47,12 +45,12 @@ class DuplicateActionOptions( HydrusSerialisable.SerialisableBase ):
|
|||
serialisable_tag_service_actions = [ ( service_key.hex(), action, tag_filter.GetSerialisableTuple() ) for ( service_key, action, tag_filter ) in self._tag_service_actions ]
|
||||
serialisable_rating_service_actions = [ ( service_key.hex(), action ) for ( service_key, action ) in self._rating_service_actions ]
|
||||
|
||||
return ( serialisable_tag_service_actions, serialisable_rating_service_actions, self._delete_second_file, self._sync_archive, self._delete_both_files, self._sync_urls_action )
|
||||
return ( serialisable_tag_service_actions, serialisable_rating_service_actions, self._sync_archive, self._sync_urls_action )
|
||||
|
||||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
( serialisable_tag_service_actions, serialisable_rating_service_actions, self._delete_second_file, self._sync_archive, self._delete_both_files, self._sync_urls_action ) = serialisable_info
|
||||
( serialisable_tag_service_actions, serialisable_rating_service_actions, self._sync_archive, self._sync_urls_action ) = serialisable_info
|
||||
|
||||
self._tag_service_actions = [ ( bytes.fromhex( serialisable_service_key ), action, HydrusSerialisable.CreateFromSerialisableTuple( serialisable_tag_filter ) ) for ( serialisable_service_key, action, serialisable_tag_filter ) in serialisable_tag_service_actions ]
|
||||
self._rating_service_actions = [ ( bytes.fromhex( serialisable_service_key ), action ) for ( serialisable_service_key, action ) in serialisable_rating_service_actions ]
|
||||
|
@ -104,42 +102,30 @@ class DuplicateActionOptions( HydrusSerialisable.SerialisableBase ):
|
|||
return ( 3, new_serialisable_info )
|
||||
|
||||
|
||||
|
||||
def GetDeletedHashes( self, first_media, second_media ):
|
||||
|
||||
first_hashes = first_media.GetHashes()
|
||||
second_hashes = second_media.GetHashes()
|
||||
|
||||
if self._delete_second_file:
|
||||
if version == 3:
|
||||
|
||||
return second_hashes
|
||||
( serialisable_tag_service_actions, serialisable_rating_service_actions, delete_second_file, sync_archive, delete_both_files, sync_urls_action ) = old_serialisable_info
|
||||
|
||||
elif self._delete_both_files:
|
||||
new_serialisable_info = ( serialisable_tag_service_actions, serialisable_rating_service_actions, sync_archive, sync_urls_action )
|
||||
|
||||
return first_hashes.union( second_hashes )
|
||||
|
||||
else:
|
||||
|
||||
return set()
|
||||
return ( 4, new_serialisable_info )
|
||||
|
||||
|
||||
|
||||
def SetTuple( self, tag_service_actions, rating_service_actions, delete_second_file, sync_archive, delete_both_files, sync_urls_action ):
|
||||
def SetTuple( self, tag_service_actions, rating_service_actions, sync_archive, sync_urls_action ):
|
||||
|
||||
self._tag_service_actions = tag_service_actions
|
||||
self._rating_service_actions = rating_service_actions
|
||||
self._delete_second_file = delete_second_file
|
||||
self._sync_archive = sync_archive
|
||||
self._delete_both_files = delete_both_files
|
||||
self._sync_urls_action = sync_urls_action
|
||||
|
||||
|
||||
def ToTuple( self ):
|
||||
|
||||
return ( self._tag_service_actions, self._rating_service_actions, self._delete_second_file, self._sync_archive, self._delete_both_files, self._sync_urls_action )
|
||||
return ( self._tag_service_actions, self._rating_service_actions, self._sync_archive, self._sync_urls_action )
|
||||
|
||||
|
||||
def ProcessPairIntoContentUpdates( self, first_media, second_media, file_deletion_reason = None ):
|
||||
def ProcessPairIntoContentUpdates( self, first_media, second_media, delete_first = False, delete_second = False, delete_both = False, file_deletion_reason = None ):
|
||||
|
||||
if file_deletion_reason is None:
|
||||
|
||||
|
@ -341,20 +327,17 @@ class DuplicateActionOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
deletee_media = []
|
||||
|
||||
if self._delete_second_file or self._delete_both_files:
|
||||
if delete_first or delete_second or delete_both:
|
||||
|
||||
if self._delete_both_files:
|
||||
if delete_first or delete_both:
|
||||
|
||||
deletee_media.append( first_media )
|
||||
|
||||
file_deletion_reason += ': both files deleted'
|
||||
|
||||
else:
|
||||
|
||||
file_deletion_reason += ': \'worse\' file deleted'
|
||||
|
||||
|
||||
deletee_media.append( second_media )
|
||||
if delete_second or delete_both:
|
||||
|
||||
deletee_media.append( second_media )
|
||||
|
||||
|
||||
|
||||
for media in deletee_media:
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -100,10 +100,14 @@ def THREADUploadPending( service_key ):
|
|||
info = nums_pending[ service_key ]
|
||||
|
||||
remaining_num_pending = sum( info.values() )
|
||||
done_num_pending = initial_num_pending - remaining_num_pending
|
||||
|
||||
job_key.SetVariable( 'popup_text_1', 'uploading to ' + service_name + ': ' + HydrusData.ConvertValueRangeToPrettyString( done_num_pending, initial_num_pending ) )
|
||||
job_key.SetVariable( 'popup_gauge_1', ( done_num_pending, initial_num_pending ) )
|
||||
# sometimes more come in while we are pending, -754/1,234 ha ha
|
||||
num_to_do = max( initial_num_pending, remaining_num_pending )
|
||||
|
||||
done_num_pending = num_to_do - remaining_num_pending
|
||||
|
||||
job_key.SetVariable( 'popup_text_1', 'uploading to ' + service_name + ': ' + HydrusData.ConvertValueRangeToPrettyString( done_num_pending, num_to_do ) )
|
||||
job_key.SetVariable( 'popup_gauge_1', ( done_num_pending, num_to_do ) )
|
||||
|
||||
while job_key.IsPaused() or job_key.IsCancelled():
|
||||
|
||||
|
@ -4047,7 +4051,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
HG.force_idle_mode = not HG.force_idle_mode
|
||||
|
||||
self._controller.pub( 'wake_daemons' )
|
||||
self._controller.pub( 'wake_idle_workers' )
|
||||
self.SetStatusBarDirty()
|
||||
|
||||
elif name == 'no_page_limit_mode':
|
||||
|
|
|
@ -1155,7 +1155,12 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
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 ) )
|
||||
|
||||
if HG.client_controller.new_options.GetBoolean( 'advanced_mode' ):
|
||||
|
||||
services.append( services_manager.GetService( CC.COMBINED_LOCAL_FILE_SERVICE_KEY ) )
|
||||
|
||||
|
||||
services.extend( services_manager.GetServices( ( HC.FILE_REPOSITORY, ) ) )
|
||||
|
||||
advanced_mode = HG.client_controller.new_options.GetBoolean( 'advanced_mode' )
|
||||
|
|
|
@ -5,6 +5,7 @@ from . import HydrusGlobals as HG
|
|||
from . import ClientCaches
|
||||
from . import ClientConstants as CC
|
||||
from . import ClientData
|
||||
from . import ClientDuplicates
|
||||
from . import ClientGUICommon
|
||||
from . import ClientGUIDialogs
|
||||
from . import ClientGUIDialogsManage
|
||||
|
@ -3170,9 +3171,9 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
pair_info = []
|
||||
|
||||
for ( hash_pair, duplicate_type, first_media, second_media, duplicate_action_options, was_auto_skipped ) in self._processed_pairs:
|
||||
for ( hash_pair, duplicate_type, first_media, second_media, service_keys_to_content_updates, was_auto_skipped ) in self._processed_pairs:
|
||||
|
||||
if duplicate_type == HC.DUPLICATE_UNKNOWN:
|
||||
if duplicate_type == HC.DUPLICATE_UNKNOWN or was_auto_skipped:
|
||||
|
||||
continue # it was a 'skip' decision
|
||||
|
||||
|
@ -3180,19 +3181,6 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
first_hash = first_media.GetHash()
|
||||
second_hash = second_media.GetHash()
|
||||
|
||||
if duplicate_type in ( HC.DUPLICATE_BETTER, HC.DUPLICATE_WORSE, HC.DUPLICATE_LARGER_BETTER, HC.DUPLICATE_SMALLER_BETTER, HC.DUPLICATE_BETTER_OR_WORSE ):
|
||||
|
||||
file_deletion_reason = 'better/worse'
|
||||
|
||||
else:
|
||||
|
||||
file_deletion_reason = HC.duplicate_type_string_lookup[ duplicate_type ]
|
||||
|
||||
|
||||
file_deletion_reason = 'Deleted in Duplicate Filter ({}).'.format( file_deletion_reason )
|
||||
|
||||
service_keys_to_content_updates = duplicate_action_options.ProcessPairIntoContentUpdates( first_media, second_media, file_deletion_reason = file_deletion_reason )
|
||||
|
||||
pair_info.append( ( duplicate_type, first_hash, second_hash, service_keys_to_content_updates ) )
|
||||
|
||||
|
||||
|
@ -3205,9 +3193,9 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
self._hashes_due_to_be_deleted_in_this_batch = set()
|
||||
|
||||
|
||||
def _CurrentMediaIsBetter( self ):
|
||||
def _CurrentMediaIsBetter( self, delete_second = True ):
|
||||
|
||||
self._ProcessPair( HC.DUPLICATE_BETTER )
|
||||
self._ProcessPair( HC.DUPLICATE_BETTER, delete_second = delete_second )
|
||||
|
||||
|
||||
def _Delete( self, media = None, reason = None, file_service_key = None ):
|
||||
|
@ -3270,7 +3258,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
return
|
||||
|
||||
|
||||
duplicate_types = [ HC.DUPLICATE_BETTER, HC.DUPLICATE_SAME_QUALITY, HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_NOT_DUPLICATE ]
|
||||
duplicate_types = [ HC.DUPLICATE_BETTER, HC.DUPLICATE_SAME_QUALITY, HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_FALSE_POSITIVE ]
|
||||
|
||||
choice_tuples = [ ( HC.duplicate_type_string_lookup[ duplicate_type ], duplicate_type ) for duplicate_type in duplicate_types ]
|
||||
|
||||
|
@ -3285,22 +3273,74 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
new_options = HG.client_controller.new_options
|
||||
|
||||
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
|
||||
if duplicate_type in [ HC.DUPLICATE_BETTER, HC.DUPLICATE_SAME_QUALITY ]:
|
||||
|
||||
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit duplicate merge options' ) as dlg_2:
|
||||
|
||||
panel = ClientGUIScrolledPanelsEdit.EditDuplicateActionOptionsPanel( dlg_2, duplicate_type, duplicate_action_options, for_custom_action = True )
|
||||
|
||||
dlg_2.SetPanel( panel )
|
||||
|
||||
if dlg_2.ShowModal() == wx.ID_OK:
|
||||
|
||||
duplicate_action_options = panel.GetValue()
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
duplicate_action_options = None
|
||||
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit duplicate merge options' ) as dlg_2:
|
||||
text = 'Delete any of the files?'
|
||||
|
||||
yes_tuples = []
|
||||
|
||||
yes_tuples.append( ( 'delete this one', 'delete_first' ) )
|
||||
yes_tuples.append( ( 'delete the other', 'delete_second' ) )
|
||||
yes_tuples.append( ( 'delete both', 'delete_both' ) )
|
||||
|
||||
delete_first = False
|
||||
delete_second = False
|
||||
delete_both = False
|
||||
|
||||
with ClientGUIDialogs.DialogYesYesNo( self, text, yes_tuples = yes_tuples, no_label = 'forget it' ) as dlg:
|
||||
|
||||
panel = ClientGUIScrolledPanelsEdit.EditDuplicateActionOptionsPanel( dlg_2, duplicate_type, duplicate_action_options, for_custom_action = True )
|
||||
|
||||
dlg_2.SetPanel( panel )
|
||||
|
||||
if dlg_2.ShowModal() == wx.ID_OK:
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
duplicate_action_options = panel.GetValue()
|
||||
value = dlg.GetValue()
|
||||
|
||||
self._ProcessPair( duplicate_type, duplicate_action_options )
|
||||
if value == 'delete_first':
|
||||
|
||||
delete_first = True
|
||||
|
||||
elif value == 'delete_second':
|
||||
|
||||
delete_second = True
|
||||
|
||||
elif value == 'delete_both':
|
||||
|
||||
delete_both = True
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
self._ProcessPair( duplicate_type, delete_first = delete_first, delete_second = delete_second, delete_both = delete_both, duplicate_action_options = duplicate_action_options )
|
||||
|
||||
|
||||
def _DrawBackgroundDetails( self, dc ):
|
||||
|
||||
|
@ -3384,7 +3424,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
def _GetNumCommittableDecisions( self ):
|
||||
|
||||
return len( [ 1 for ( hash_pair, duplicate_type, first_media, second_media, duplicate_action_options, was_auto_skipped ) in self._processed_pairs if duplicate_type != HC.DUPLICATE_UNKNOWN ] )
|
||||
return len( [ 1 for ( hash_pair, duplicate_type, first_media, second_media, service_keys_to_content_updates, was_auto_skipped ) in self._processed_pairs if duplicate_type != HC.DUPLICATE_UNKNOWN ] )
|
||||
|
||||
|
||||
def _GoBack( self ):
|
||||
|
@ -3393,13 +3433,13 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
self._unprocessed_pairs.append( self._current_pair )
|
||||
|
||||
( hash_pair, duplicate_type, first_media, second_media, duplicate_action_options, was_auto_skipped ) = self._processed_pairs.pop()
|
||||
( hash_pair, duplicate_type, first_media, second_media, service_keys_to_content_updates, was_auto_skipped ) = self._processed_pairs.pop()
|
||||
|
||||
self._unprocessed_pairs.append( hash_pair )
|
||||
|
||||
while was_auto_skipped:
|
||||
|
||||
( hash_pair, duplicate_type, first_media, second_media, duplicate_action_options, was_auto_skipped ) = self._processed_pairs.pop()
|
||||
( hash_pair, duplicate_type, first_media, second_media, service_keys_to_content_updates, was_auto_skipped ) = self._processed_pairs.pop()
|
||||
|
||||
self._unprocessed_pairs.append( hash_pair )
|
||||
|
||||
|
@ -3415,9 +3455,9 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
self._ProcessPair( HC.DUPLICATE_ALTERNATE )
|
||||
|
||||
|
||||
def _MediaAreNotDupes( self ):
|
||||
def _MediaAreFalsePositive( self ):
|
||||
|
||||
self._ProcessPair( HC.DUPLICATE_NOT_DUPLICATE )
|
||||
self._ProcessPair( HC.DUPLICATE_FALSE_POSITIVE )
|
||||
|
||||
|
||||
def _MediaAreTheSame( self ):
|
||||
|
@ -3425,7 +3465,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
self._ProcessPair( HC.DUPLICATE_SAME_QUALITY )
|
||||
|
||||
|
||||
def _ProcessPair( self, duplicate_type, duplicate_action_options = None ):
|
||||
def _ProcessPair( self, duplicate_type, delete_first = False, delete_second = False, delete_both = False, duplicate_action_options = None ):
|
||||
|
||||
if self._current_media is None:
|
||||
|
||||
|
@ -3434,20 +3474,64 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
if duplicate_action_options is None:
|
||||
|
||||
new_options = HG.client_controller.new_options
|
||||
|
||||
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
|
||||
if duplicate_type in [ HC.DUPLICATE_BETTER, HC.DUPLICATE_SAME_QUALITY ]:
|
||||
|
||||
new_options = HG.client_controller.new_options
|
||||
|
||||
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
|
||||
|
||||
else:
|
||||
|
||||
duplicate_action_options = ClientDuplicates.DuplicateActionOptions( [], [] )
|
||||
|
||||
|
||||
|
||||
other_media = self._media_list.GetNext( self._current_media )
|
||||
|
||||
deleted_hashes = duplicate_action_options.GetDeletedHashes( self._current_media, other_media )
|
||||
|
||||
self._hashes_due_to_be_deleted_in_this_batch.update( deleted_hashes )
|
||||
first_media = self._current_media
|
||||
second_media = self._media_list.GetNext( first_media )
|
||||
|
||||
was_auto_skipped = False
|
||||
|
||||
self._processed_pairs.append( ( self._current_pair, duplicate_type, self._current_media, other_media, duplicate_action_options, was_auto_skipped ) )
|
||||
if delete_first or delete_second or delete_both:
|
||||
|
||||
if delete_first or delete_both:
|
||||
|
||||
self._hashes_due_to_be_deleted_in_this_batch.update( first_media.GetHashes() )
|
||||
|
||||
|
||||
if delete_second or delete_both:
|
||||
|
||||
self._hashes_due_to_be_deleted_in_this_batch.update( second_media.GetHashes() )
|
||||
|
||||
|
||||
if duplicate_type in ( HC.DUPLICATE_BETTER, HC.DUPLICATE_WORSE, HC.DUPLICATE_LARGER_BETTER, HC.DUPLICATE_SMALLER_BETTER, HC.DUPLICATE_BETTER_OR_WORSE ):
|
||||
|
||||
file_deletion_reason = 'better/worse'
|
||||
|
||||
if delete_second:
|
||||
|
||||
file_deletion_reason += ', worse file deleted'
|
||||
|
||||
|
||||
else:
|
||||
|
||||
file_deletion_reason = HC.duplicate_type_string_lookup[ duplicate_type ]
|
||||
|
||||
|
||||
if delete_both:
|
||||
|
||||
file_deletion_reason += ', both files deleted'
|
||||
|
||||
|
||||
file_deletion_reason = 'Deleted in Duplicate Filter ({}).'.format( file_deletion_reason )
|
||||
|
||||
else:
|
||||
|
||||
file_deletion_reason = None
|
||||
|
||||
|
||||
service_keys_to_content_updates = duplicate_action_options.ProcessPairIntoContentUpdates( first_media, second_media, delete_first = delete_first, delete_second = delete_second, delete_both = delete_both, file_deletion_reason = file_deletion_reason )
|
||||
|
||||
self._processed_pairs.append( ( self._current_pair, duplicate_type, first_media, second_media, service_keys_to_content_updates, was_auto_skipped ) )
|
||||
|
||||
self._ShowNewPair()
|
||||
|
||||
|
@ -3475,13 +3559,13 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
else:
|
||||
|
||||
( hash_pair, duplicate_type, first_media, second_media, duplicate_action_options, was_auto_skipped ) = self._processed_pairs.pop()
|
||||
( hash_pair, duplicate_type, first_media, second_media, service_keys_to_content_updates, was_auto_skipped ) = self._processed_pairs.pop()
|
||||
|
||||
self._unprocessed_pairs.append( hash_pair )
|
||||
|
||||
while was_auto_skipped:
|
||||
|
||||
( hash_pair, duplicate_type, first_media, second_media, duplicate_action_options, was_auto_skipped ) = self._processed_pairs.pop()
|
||||
( hash_pair, duplicate_type, first_media, second_media, service_keys_to_content_updates, was_auto_skipped ) = self._processed_pairs.pop()
|
||||
|
||||
self._unprocessed_pairs.append( hash_pair )
|
||||
|
||||
|
@ -3538,7 +3622,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
was_auto_skipped = True
|
||||
|
||||
self._processed_pairs.append( ( potential_pair, HC.DUPLICATE_UNKNOWN, None, None, None, was_auto_skipped ) )
|
||||
self._processed_pairs.append( ( potential_pair, HC.DUPLICATE_UNKNOWN, None, None, {}, was_auto_skipped ) )
|
||||
|
||||
if len( self._unprocessed_pairs ) == 0:
|
||||
|
||||
|
@ -3609,7 +3693,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
was_auto_skipped = False
|
||||
|
||||
self._processed_pairs.append( ( self._current_pair, HC.DUPLICATE_UNKNOWN, None, None, None, was_auto_skipped ) )
|
||||
self._processed_pairs.append( ( self._current_pair, HC.DUPLICATE_UNKNOWN, None, None, {}, was_auto_skipped ) )
|
||||
|
||||
self._ShowNewPair()
|
||||
|
||||
|
@ -3768,9 +3852,13 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
action = data
|
||||
|
||||
if action == 'duplicate_filter_this_is_better':
|
||||
if action == 'duplicate_filter_this_is_better_and_delete_other':
|
||||
|
||||
self._CurrentMediaIsBetter()
|
||||
self._CurrentMediaIsBetter( delete_second = True )
|
||||
|
||||
elif action == 'duplicate_filter_this_is_better_but_keep_both':
|
||||
|
||||
self._CurrentMediaIsBetter( delete_second = False )
|
||||
|
||||
elif action == 'duplicate_filter_exactly_the_same':
|
||||
|
||||
|
@ -3780,9 +3868,9 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
self._MediaAreAlternates()
|
||||
|
||||
elif action == 'duplicate_filter_not_dupes':
|
||||
elif action == 'duplicate_filter_false_positive':
|
||||
|
||||
self._MediaAreNotDupes()
|
||||
self._MediaAreFalsePositive()
|
||||
|
||||
elif action == 'duplicate_filter_custom_action':
|
||||
|
||||
|
|
|
@ -1531,6 +1531,16 @@ class ChoiceSort( wx.Panel ):
|
|||
|
||||
|
||||
|
||||
def _UserChoseASort( self ):
|
||||
|
||||
if HG.client_controller.new_options.GetBoolean( 'save_page_sort_on_change' ):
|
||||
|
||||
media_sort = self._GetCurrentSort()
|
||||
|
||||
HG.client_controller.new_options.SetDefaultSort( media_sort )
|
||||
|
||||
|
||||
|
||||
def ACollectHappened( self, page_key, collect_by ):
|
||||
|
||||
if self._management_controller is not None:
|
||||
|
@ -1556,11 +1566,15 @@ class ChoiceSort( wx.Panel ):
|
|||
|
||||
def EventSortAscChoice( self, event ):
|
||||
|
||||
self._UserChoseASort()
|
||||
|
||||
self._BroadcastSort()
|
||||
|
||||
|
||||
def EventSortTypeChoice( self, event ):
|
||||
|
||||
self._UserChoseASort()
|
||||
|
||||
self._UpdateAscLabels( set_default_asc = True )
|
||||
|
||||
self._BroadcastSort()
|
||||
|
|
|
@ -365,7 +365,7 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( transformations_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
@ -901,7 +901,7 @@ class EditStringMatchPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.Add( self._example_string_matches, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
|
|
@ -135,7 +135,6 @@ class Dialog( wx.Dialog ):
|
|||
|
||||
self.SetIcon( HG.client_controller.frame_icon )
|
||||
|
||||
self.Bind( wx.EVT_BUTTON, self.EventDialogButton )
|
||||
self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
|
||||
|
||||
if parent is not None and position == 'center':
|
||||
|
@ -160,14 +159,6 @@ class Dialog( wx.Dialog ):
|
|||
|
||||
|
||||
|
||||
def EventDialogButton( self, event ):
|
||||
|
||||
if self.IsModal():
|
||||
|
||||
self.EndModal( event.GetId() )
|
||||
|
||||
|
||||
|
||||
def SetInitialSize( self, size ):
|
||||
|
||||
( width, height ) = size
|
||||
|
@ -224,7 +215,10 @@ class DialogChooseNewServiceMethod( Dialog ):
|
|||
self.EndModal( wx.ID_OK )
|
||||
|
||||
|
||||
def GetRegister( self ): return self._should_register
|
||||
def GetRegister( self ):
|
||||
|
||||
return self._should_register
|
||||
|
||||
|
||||
class DialogCommitInterstitialFiltering( Dialog ):
|
||||
|
||||
|
@ -232,10 +226,10 @@ class DialogCommitInterstitialFiltering( Dialog ):
|
|||
|
||||
Dialog.__init__( self, parent, 'commit and continue?', position = 'center' )
|
||||
|
||||
self._commit = wx.Button( self, id = wx.ID_YES, label = 'commit and continue' )
|
||||
self._commit = ClientGUICommon.BetterButton( self, 'commit and continue', self.EndModal, wx.ID_YES )
|
||||
self._commit.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._back = wx.Button( self, id = wx.ID_CANCEL, label = 'go back' )
|
||||
self._back = ClientGUICommon.BetterButton( self, 'go back', self.EndModal, wx.ID_NO )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
|
@ -266,7 +260,6 @@ class DialogFinishFiltering( Dialog ):
|
|||
self._forget.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
|
||||
self._back = ClientGUICommon.BetterButton( self, 'back to filtering', self.EndModal, wx.ID_CANCEL )
|
||||
self._back.SetId( wx.ID_CANCEL )
|
||||
|
||||
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
|
@ -303,7 +296,7 @@ class DialogGenerateNewAccounts( Dialog ):
|
|||
|
||||
self._lifetime = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
self._ok = wx.Button( self, label = 'OK' )
|
||||
self._ok = wx.Button( self, id = wx.ID_OK, label = 'OK' )
|
||||
self._ok.Bind( wx.EVT_BUTTON, self.EventOK )
|
||||
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
|
@ -520,18 +513,25 @@ class DialogInputLocalBooruShare( Dialog ):
|
|||
|
||||
def EventCopyExternalShareURL( self, event ):
|
||||
|
||||
self._service = HG.client_controller.services_manager.GetService( CC.LOCAL_BOORU_SERVICE_KEY )
|
||||
internal_port = self._service.GetPort()
|
||||
|
||||
external_ip = HydrusNATPunch.GetExternalIP() # eventually check for optional host replacement here
|
||||
|
||||
external_port = self._service.GetUPnPPort()
|
||||
|
||||
if external_port is None:
|
||||
if internal_port is None:
|
||||
|
||||
external_port = self._service.GetPort()
|
||||
wx.MessageBox( 'The local booru is not currently running!' )
|
||||
|
||||
|
||||
url = 'http://' + external_ip + ':' + str( external_port ) + '/gallery?share_key=' + self._share_key.hex()
|
||||
try:
|
||||
|
||||
url = self._service.GetExternalShareURL( self._share_key )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.ShowException( e )
|
||||
|
||||
wx.MessageBox( 'Unfortunately, could not generate an external URL: {}'.format( e ) )
|
||||
|
||||
return
|
||||
|
||||
|
||||
HG.client_controller.pub( 'clipboard', 'text', url )
|
||||
|
||||
|
@ -697,6 +697,8 @@ class FrameInputLocalFiles( wx.Frame ):
|
|||
|
||||
self.Show()
|
||||
|
||||
self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
|
||||
|
||||
HG.client_controller.gui.RegisterUIUpdateWindow( self )
|
||||
|
||||
HG.client_controller.CallToThreadLongRunning( self.THREADParseImportablePaths, self._unparsed_paths_queue, self._currently_parsing, self._work_to_do, self._parsed_path_queue, self._progress_updater, self._pause_event, self._cancel_event )
|
||||
|
@ -768,6 +770,22 @@ class FrameInputLocalFiles( wx.Frame ):
|
|||
self.Close()
|
||||
|
||||
|
||||
def EventCharHook( self, event ):
|
||||
|
||||
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
|
||||
|
||||
if key == wx.WXK_ESCAPE:
|
||||
|
||||
self._TidyUp()
|
||||
|
||||
self.Close()
|
||||
|
||||
else:
|
||||
|
||||
event.Skip()
|
||||
|
||||
|
||||
|
||||
def EventDeleteAfterSuccessCheck( self, event ):
|
||||
|
||||
if self._delete_after_success.GetValue():
|
||||
|
@ -1247,84 +1265,6 @@ class DialogInputNamespaceRegex( Dialog ):
|
|||
return ( namespace, regex )
|
||||
|
||||
|
||||
class DialogInputNewFormField( Dialog ):
|
||||
|
||||
def __init__( self, parent, form_field = None ):
|
||||
|
||||
Dialog.__init__( self, parent, 'configure form field' )
|
||||
|
||||
if form_field is None: ( name, field_type, default, editable ) = ( '', CC.FIELD_TEXT, '', True )
|
||||
else: ( name, field_type, default, editable ) = form_field
|
||||
|
||||
self._name = wx.TextCtrl( self )
|
||||
|
||||
self._type = wx.Choice( self )
|
||||
|
||||
self._default = wx.TextCtrl( self )
|
||||
|
||||
self._editable = wx.CheckBox( self )
|
||||
|
||||
self._ok = wx.Button( self, id = wx.ID_OK, label = 'OK' )
|
||||
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'Cancel' )
|
||||
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
|
||||
#
|
||||
|
||||
self._name.SetValue( name )
|
||||
|
||||
for temp_type in CC.FIELDS: self._type.Append( CC.field_string_lookup[ temp_type ], temp_type )
|
||||
self._type.Select( field_type )
|
||||
|
||||
self._default.SetValue( default )
|
||||
|
||||
self._editable.SetValue( editable )
|
||||
|
||||
#
|
||||
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'name: ', self._name ) )
|
||||
rows.append( ( 'type: ', self._type ) )
|
||||
rows.append( ( 'default: ', self._default ) )
|
||||
rows.append( ( 'editable: ', self._editable ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
b_box = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
b_box.Add( self._ok, CC.FLAGS_VCENTER )
|
||||
b_box.Add( self._cancel, CC.FLAGS_VCENTER )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.Add( b_box, CC.FLAGS_BUTTON_SIZER )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
( x, y ) = self.GetEffectiveMinSize()
|
||||
|
||||
self.SetInitialSize( ( x, y ) )
|
||||
|
||||
wx.CallAfter( self._ok.SetFocus )
|
||||
|
||||
|
||||
def GetFormField( self ):
|
||||
|
||||
name = self._name.GetValue()
|
||||
|
||||
field_type = self._type.GetClientData( self._type.GetSelection() )
|
||||
|
||||
default = self._default.GetValue()
|
||||
|
||||
editable = self._editable.GetValue()
|
||||
|
||||
return ( name, field_type, default, editable )
|
||||
|
||||
|
||||
class DialogInputTags( Dialog ):
|
||||
|
||||
def __init__( self, parent, service_key, tags, message = '' ):
|
||||
|
@ -1339,7 +1279,7 @@ class DialogInputTags( Dialog ):
|
|||
|
||||
self._tag_box = ClientGUIACDropdown.AutoCompleteDropdownTagsWrite( self, self.EnterTags, expand_parents, CC.LOCAL_FILE_SERVICE_KEY, service_key, null_entry_callable = self.OK )
|
||||
|
||||
self._ok = wx.Button( self, id = wx.ID_OK, label = 'OK' )
|
||||
self._ok = ClientGUICommon.BetterButton( self, 'OK', self.EndModal, wx.ID_OK )
|
||||
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'Cancel' )
|
||||
|
@ -1424,7 +1364,7 @@ class DialogInputUPnPMapping( Dialog ):
|
|||
self._description = wx.TextCtrl( self )
|
||||
self._duration = wx.SpinCtrl( self, min = 0, max = 86400 )
|
||||
|
||||
self._ok = wx.Button( self, id = wx.ID_OK, label = 'OK' )
|
||||
self._ok = ClientGUICommon.BetterButton( self, 'OK', self.EndModal, wx.ID_OK )
|
||||
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'Cancel' )
|
||||
|
@ -1702,10 +1642,10 @@ class DialogSelectFromURLTree( Dialog ):
|
|||
|
||||
self._tree = wx.lib.agw.customtreectrl.CustomTreeCtrl( self, agwStyle = agwStyle )
|
||||
|
||||
self._ok = wx.Button( self, id = wx.ID_OK )
|
||||
self._ok = ClientGUICommon.BetterButton( self, 'OK', self.EndModal, wx.ID_OK )
|
||||
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL )
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'Cancel' )
|
||||
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
|
||||
#
|
||||
|
@ -1807,7 +1747,6 @@ class DialogSelectFromURLTree( Dialog ):
|
|||
return urls
|
||||
|
||||
|
||||
|
||||
class DialogSelectImageboard( Dialog ):
|
||||
|
||||
def __init__( self, parent ):
|
||||
|
@ -1896,7 +1835,7 @@ class DialogTextEntry( Dialog ):
|
|||
self._text.SetMaxLength( self._max_chars )
|
||||
|
||||
|
||||
self._ok = wx.Button( self, id = wx.ID_OK, label = 'ok' )
|
||||
self._ok = ClientGUICommon.BetterButton( self, 'ok', self.EndModal, wx.ID_OK )
|
||||
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel' )
|
||||
|
@ -1994,11 +1933,11 @@ class DialogYesNo( Dialog ):
|
|||
|
||||
Dialog.__init__( self, parent, title, position = 'center' )
|
||||
|
||||
self._yes = wx.Button( self, id = wx.ID_YES )
|
||||
self._yes = ClientGUICommon.BetterButton( self, yes_label, self.EndModal, wx.ID_YES )
|
||||
self._yes.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
self._yes.SetLabelText( yes_label )
|
||||
|
||||
self._no = wx.Button( self, id = wx.ID_NO )
|
||||
self._no = ClientGUICommon.BetterButton( self, no_label, self.EndModal, wx.ID_NO )
|
||||
self._no.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
self._no.SetLabelText( no_label )
|
||||
|
||||
|
@ -2052,9 +1991,8 @@ class DialogYesYesNo( Dialog ):
|
|||
yes_buttons.append( yes_button )
|
||||
|
||||
|
||||
self._no = wx.Button( self, id = wx.ID_NO )
|
||||
self._no = ClientGUICommon.BetterButton( self, no_label, self.EndModal, wx.ID_NO )
|
||||
self._no.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
self._no.SetLabelText( no_label )
|
||||
|
||||
#
|
||||
|
||||
|
|
|
@ -351,7 +351,7 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
|||
|
||||
listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
|
||||
|
||||
columns = [ ( 'description', -1 ), ( 'internal ip', 17 ), ( 'internal port', 7 ), ( 'external ip', 17 ), ( 'external port', 7 ), ( 'prototcol', 5 ), ( 'lease', 12 ) ]
|
||||
columns = [ ( 'description', -1 ), ( 'internal ip', 17 ), ( 'internal port', 7 ), ( 'external port', 7 ), ( 'prototcol', 5 ), ( 'lease', 12 ) ]
|
||||
|
||||
self._mappings_list_ctrl = ClientGUIListCtrl.BetterListCtrl( listctrl_panel, 'manage_upnp_mappings', 12, 36, columns, self._ConvertDataToListCtrlTuples, delete_key_callback = self._Remove, activation_callback = self._Edit )
|
||||
|
||||
|
@ -387,6 +387,8 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._mappings_list_ctrl.Sort( 0 )
|
||||
|
||||
self._started_external_ip_fetch = False
|
||||
|
||||
self._RefreshMappings()
|
||||
|
||||
|
||||
|
@ -406,7 +408,7 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
|||
|
||||
( external_port, protocol, internal_port, description, duration ) = dlg.GetInfo()
|
||||
|
||||
for ( existing_description, existing_internal_ip, existing_internal_port, existing_external_ip, existing_external_port, existing_protocol, existing_lease ) in self._mappings:
|
||||
for ( existing_description, existing_internal_ip, existing_internal_port, existing_external_port, existing_protocol, existing_lease ) in self._mappings:
|
||||
|
||||
if external_port == existing_external_port and protocol == existing_protocol:
|
||||
|
||||
|
@ -432,7 +434,7 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
|||
|
||||
def _ConvertDataToListCtrlTuples( self, mapping ):
|
||||
|
||||
( description, internal_ip, internal_port, external_ip, external_port, protocol, duration ) = mapping
|
||||
( description, internal_ip, internal_port, external_port, protocol, duration ) = mapping
|
||||
|
||||
if duration == 0:
|
||||
|
||||
|
@ -443,7 +445,7 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
|||
pretty_duration = HydrusData.TimeDeltaToPrettyTimeDelta( duration )
|
||||
|
||||
|
||||
display_tuple = ( description, internal_ip, str( internal_port ), external_ip, str( external_port ), protocol, pretty_duration )
|
||||
display_tuple = ( description, internal_ip, str( internal_port ), str( external_port ), protocol, pretty_duration )
|
||||
sort_tuple = mapping
|
||||
|
||||
return ( display_tuple, sort_tuple )
|
||||
|
@ -457,7 +459,7 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
|||
|
||||
for selected_mapping in selected_mappings:
|
||||
|
||||
( description, internal_ip, internal_port, external_ip, external_port, protocol, duration ) = selected_mapping
|
||||
( description, internal_ip, internal_port, external_port, protocol, duration ) = selected_mapping
|
||||
|
||||
with ClientGUIDialogs.DialogInputUPnPMapping( self, external_port, protocol, internal_port, description, duration ) as dlg:
|
||||
|
||||
|
@ -486,6 +488,41 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
|||
|
||||
|
||||
|
||||
def _RefreshExternalIP( self ):
|
||||
|
||||
def wx_code( external_ip_text ):
|
||||
|
||||
if not self:
|
||||
|
||||
return
|
||||
|
||||
|
||||
self._status_st.SetLabelText( external_ip_text )
|
||||
|
||||
|
||||
def THREADdo_it():
|
||||
|
||||
try:
|
||||
|
||||
external_ip = HydrusNATPunch.GetExternalIP()
|
||||
|
||||
external_ip_text = 'External IP: {}'.format( external_ip )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
external_ip_text = 'Error finding external IP: ' + str( e )
|
||||
|
||||
return
|
||||
|
||||
|
||||
wx.CallAfter( wx_code, external_ip_text )
|
||||
|
||||
|
||||
self._status_st.SetLabelText( 'Loading external IP\u2026' )
|
||||
|
||||
HG.client_controller.CallToThread( THREADdo_it )
|
||||
|
||||
|
||||
def _RefreshMappings( self ):
|
||||
|
||||
def wx_code( mappings ):
|
||||
|
@ -501,6 +538,13 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._status_st.SetLabelText( '' )
|
||||
|
||||
if not self._started_external_ip_fetch:
|
||||
|
||||
self._started_external_ip_fetch = True
|
||||
|
||||
self._RefreshExternalIP()
|
||||
|
||||
|
||||
|
||||
def THREADdo_it():
|
||||
|
||||
|
@ -520,7 +564,7 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
|||
wx.CallAfter( wx_code, mappings )
|
||||
|
||||
|
||||
self._status_st.SetLabelText( 'Refreshing mappings--please wait...' )
|
||||
self._status_st.SetLabelText( 'Refreshing mappings--please wait\u2026' )
|
||||
|
||||
self._mappings_list_ctrl.SetData( [] )
|
||||
|
||||
|
@ -535,7 +579,7 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
|||
|
||||
for selected_mapping in selected_mappings:
|
||||
|
||||
( description, internal_ip, internal_port, external_ip, external_port, protocol, duration ) = selected_mapping
|
||||
( description, internal_ip, internal_port, external_port, protocol, duration ) = selected_mapping
|
||||
|
||||
HydrusNATPunch.RemoveUPnPMapping( external_port, protocol )
|
||||
|
||||
|
|
|
@ -268,8 +268,6 @@ class FullscreenHoverFrameRightDuplicates( FullscreenHoverFrame ):
|
|||
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'this is better\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_BETTER ) ) )
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'same quality\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_SAME_QUALITY ) ) )
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'alternates\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_ALTERNATE ) ) )
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'not duplicates\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_NOT_DUPLICATE ) ) )
|
||||
menu_items.append( ( 'separator', None, None, None ) )
|
||||
menu_items.append( ( 'normal', 'edit background lighten/darken switch intensity', 'edit how much the background will brighten or darken as you switch between the pair', self._EditBackgroundSwitchIntensity ) )
|
||||
|
||||
|
@ -289,23 +287,40 @@ class FullscreenHoverFrameRightDuplicates( FullscreenHoverFrame ):
|
|||
|
||||
self._cog_button = ClientGUICommon.MenuBitmapButton( self, CC.GlobalBMPs.cog, menu_items )
|
||||
|
||||
dupe_commands = []
|
||||
|
||||
dupe_commands.append( ( 'this is better', 'Set that the current file you are looking at is better than the other in the pair.', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better' ) ) )
|
||||
dupe_commands.append( ( 'same quality', 'Set that the two files are duplicates of very similar quality.', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_exactly_the_same' ) ) )
|
||||
dupe_commands.append( ( 'alternates', 'Set that the files are not duplicates, but that one is derived from the other or that they are both descendants of a common ancestor.', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_alternates' ) ) )
|
||||
dupe_commands.append( ( 'not duplicates', 'Set that the files are not duplicates or otherwise related--that this pair is a false-positive match.', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_not_dupes' ) ) )
|
||||
dupe_commands.append( ( 'custom action', 'Choose one of the other actions but customise the merge and delete options for this specific decision.', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_custom_action' ) ) )
|
||||
|
||||
command_button_vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
for ( label, tooltip, command ) in dupe_commands:
|
||||
dupe_boxes = []
|
||||
|
||||
dupe_commands = []
|
||||
|
||||
dupe_commands.append( ( 'this is better, and delete the other', 'Set that the current file you are looking at is better than the other in the pair, and set the other file to be deleted.', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better_and_delete_other' ) ) )
|
||||
dupe_commands.append( ( 'this is better, but keep both', 'Set that the current file you are looking at is better than the other in the pair, but keep both files.', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_this_is_better_but_keep_both' ) ) )
|
||||
dupe_commands.append( ( 'they are the same quality', 'Set that the two files are duplicates of very similar quality.', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_exactly_the_same' ) ) )
|
||||
|
||||
dupe_boxes.append( ( 'they are duplicates', dupe_commands ) )
|
||||
|
||||
dupe_commands = []
|
||||
|
||||
dupe_commands.append( ( 'they are related alternates', 'Set that the files are not duplicates, but that one is derived from the other or that they are both descendants of a common ancestor.', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_alternates' ) ) )
|
||||
dupe_commands.append( ( 'they are not related', 'Set that the files are not duplicates or otherwise related--that this potential pair is a false positive match.', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_false_positive' ) ) )
|
||||
dupe_commands.append( ( 'custom action', 'Choose one of the other actions but customise the merge and delete options for this specific decision.', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_custom_action' ) ) )
|
||||
|
||||
dupe_boxes.append( ( 'other', dupe_commands ) )
|
||||
|
||||
for ( panel_name, dupe_commands ) in dupe_boxes:
|
||||
|
||||
command_button = ClientGUICommon.BetterButton( self, label, HG.client_controller.pub, 'canvas_application_command', command, self._canvas_key )
|
||||
button_panel = ClientGUICommon.StaticBox( self, panel_name )
|
||||
|
||||
command_button.SetToolTip( tooltip )
|
||||
for ( label, tooltip, command ) in dupe_commands:
|
||||
|
||||
command_button = ClientGUICommon.BetterButton( button_panel, label, HG.client_controller.pub, 'canvas_application_command', command, self._canvas_key )
|
||||
|
||||
command_button.SetToolTip( tooltip )
|
||||
|
||||
button_panel.Add( command_button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
||||
command_button_vbox.Add( command_button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
command_button_vbox.Add( button_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
||||
self._comparison_statements_vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
|
|
@ -2109,7 +2109,7 @@ class EditLoginStepPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
vbox = ClientGUICommon.BetterBoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.Add( required_credentials_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.Add( static_args_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.Add( temp_args_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
|
|
@ -899,6 +899,11 @@ class ManagementPanel( wx.lib.scrolledpanel.ScrolledPanel ):
|
|||
|
||||
|
||||
|
||||
def GetSortBy( self ):
|
||||
|
||||
return self._sort_by.GetSort()
|
||||
|
||||
|
||||
def _MakeCurrentSelectionTagsBox( self, sizer ):
|
||||
|
||||
tags_box = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'selection tags' )
|
||||
|
@ -1044,8 +1049,6 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'this is better\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_BETTER ) ) )
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'same quality\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_SAME_QUALITY ) ) )
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'alternates\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_ALTERNATE ) ) )
|
||||
menu_items.append( ( 'normal', 'edit duplicate action options for \'not duplicates\'', 'edit what content is merged when you filter files', HydrusData.Call( self._EditMergeOptions, HC.DUPLICATE_NOT_DUPLICATE ) ) )
|
||||
|
||||
self._edit_merge_options = ClientGUICommon.MenuButton( self._main_right_panel, 'edit default duplicate action options', menu_items )
|
||||
|
||||
|
@ -1073,9 +1076,10 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
random_filtering_panel = ClientGUICommon.StaticBox( self._main_right_panel, 'quick and dirty processing' )
|
||||
|
||||
self._show_some_dupes = ClientGUICommon.BetterButton( random_filtering_panel, 'show some random potential pairs', self._ShowSomeDupes )
|
||||
self._set_random_as_alternates_button = ClientGUICommon.BetterButton( random_filtering_panel, 'set current media as all alternates', self._SetCurrentMediaAs, HC.DUPLICATE_ALTERNATE )
|
||||
self._set_random_as_same_quality_button = ClientGUICommon.BetterButton( random_filtering_panel, 'set current media as all same quality', self._SetCurrentMediaAs, HC.DUPLICATE_SAME_QUALITY )
|
||||
self._set_random_as_not_duplicates_button = ClientGUICommon.BetterButton( random_filtering_panel, 'set current media as not duplicates', self._SetCurrentMediaAs, HC.DUPLICATE_NOT_DUPLICATE )
|
||||
|
||||
self._set_random_as_same_quality_button = ClientGUICommon.BetterButton( random_filtering_panel, 'set current media as duplicates of the same quality', self._SetCurrentMediaAs, HC.DUPLICATE_SAME_QUALITY )
|
||||
self._set_random_as_alternates_button = ClientGUICommon.BetterButton( random_filtering_panel, 'set current media as all related alternates', self._SetCurrentMediaAs, HC.DUPLICATE_ALTERNATE )
|
||||
self._set_random_as_false_positives_button = ClientGUICommon.BetterButton( random_filtering_panel, 'set current media as not related/false positive', self._SetCurrentMediaAs, HC.DUPLICATE_FALSE_POSITIVE )
|
||||
|
||||
#
|
||||
|
||||
|
@ -1167,9 +1171,9 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
self._filtering_panel.Add( self._launch_filter, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
random_filtering_panel.Add( self._show_some_dupes, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
random_filtering_panel.Add( self._set_random_as_alternates_button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
random_filtering_panel.Add( self._set_random_as_same_quality_button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
random_filtering_panel.Add( self._set_random_as_not_duplicates_button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
random_filtering_panel.Add( self._set_random_as_alternates_button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
random_filtering_panel.Add( self._set_random_as_false_positives_button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
vbox = ClientGUICommon.BetterBoxSizer( wx.VERTICAL )
|
||||
|
||||
|
@ -1381,7 +1385,7 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
search_distance = self._search_distance_spinctrl.GetValue()
|
||||
|
||||
self._controller.Write( 'maintain_similar_files_duplicate_pairs', search_distance, job_key = job_key )
|
||||
self._controller.Write( 'maintain_similar_files_search_for_potential_duplicates', search_distance, job_key = job_key )
|
||||
|
||||
self._controller.pub( 'modal_message', job_key )
|
||||
|
||||
|
|
|
@ -1627,9 +1627,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledCanvas ):
|
|||
|
||||
flat_media = self._GetSelectedFlatMedia()
|
||||
|
||||
hashes = { media.GetHash() for media in flat_media }
|
||||
|
||||
num_files = len( hashes )
|
||||
num_files = len( flat_media )
|
||||
|
||||
if num_files > 0:
|
||||
|
||||
|
@ -1648,21 +1646,69 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledCanvas ):
|
|||
text = 'This will regenerate the {} selected files\' thumbnails, but only if they are the wrong size.'.format( HydrusData.ToHumanInt( num_files ) )
|
||||
|
||||
|
||||
text += os.linesep * 2
|
||||
text += 'It may take some time to finish this job.'
|
||||
do_it_now = True
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
|
||||
if num_files > 50:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
text += os.linesep * 2
|
||||
text += 'You have selected {} files, so this job may take some time. If you would like, you can simply schedule it to happen in idle time.'.format( HydrusData.ToHumanInt( num_files ) )
|
||||
|
||||
yes_tuples = []
|
||||
|
||||
yes_tuples.append( ( 'do it now', 'now' ) )
|
||||
yes_tuples.append( ( 'do it later', 'later' ) )
|
||||
|
||||
with ClientGUIDialogs.DialogYesYesNo( self, text, yes_tuples = yes_tuples, no_label = 'forget it' ) as dlg:
|
||||
|
||||
self._SetFocussedMedia( None )
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
value = dlg.GetValue()
|
||||
|
||||
if value == 'now':
|
||||
|
||||
do_it_now = True
|
||||
|
||||
elif value == 'later':
|
||||
|
||||
do_it_now = False
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
time.sleep( 1 )
|
||||
|
||||
else:
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
|
||||
|
||||
HG.client_controller.Write( 'regenerate_file_data', job_type, hashes )
|
||||
if dlg.ShowModal() != wx.ID_YES:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
if do_it_now:
|
||||
|
||||
self._SetFocussedMedia( None )
|
||||
|
||||
time.sleep( 0.1 )
|
||||
|
||||
HG.client_controller.CallToThread( HG.client_controller.files_maintenance_manager.RunJobImmediately, flat_media, job_type )
|
||||
|
||||
else:
|
||||
|
||||
hashes = { media.GetHash() for media in flat_media }
|
||||
|
||||
HG.client_controller.CallToThread( HG.client_controller.files_maintenance_manager.ScheduleJob, hashes, job_type )
|
||||
|
||||
|
||||
|
||||
|
||||
def _RescindDownloadSelected( self ):
|
||||
|
@ -1819,11 +1865,21 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledCanvas ):
|
|||
|
||||
elif duplicate_action_options is None:
|
||||
|
||||
yes_no_text = 'set all pair relationships to ' + HC.duplicate_type_string_lookup[ duplicate_type ] + ' (with default duplicate action/merge options)'
|
||||
yes_no_text = 'set all pair relationships to ' + HC.duplicate_type_string_lookup[ duplicate_type ]
|
||||
|
||||
new_options = HG.client_controller.new_options
|
||||
|
||||
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
|
||||
if duplicate_type in [ HC.DUPLICATE_BETTER, HC.DUPLICATE_SAME_QUALITY ]:
|
||||
|
||||
yes_no_text += ' (with default duplicate action/merge options)'
|
||||
|
||||
new_options = HG.client_controller.new_options
|
||||
|
||||
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
|
||||
|
||||
else:
|
||||
|
||||
duplicate_action_options = None
|
||||
|
||||
|
||||
else:
|
||||
|
||||
|
@ -1917,7 +1973,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledCanvas ):
|
|||
|
||||
def _SetDuplicatesCustom( self ):
|
||||
|
||||
duplicate_types = [ HC.DUPLICATE_BETTER, HC.DUPLICATE_SAME_QUALITY, HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_NOT_DUPLICATE ]
|
||||
duplicate_types = [ HC.DUPLICATE_BETTER, HC.DUPLICATE_SAME_QUALITY ]
|
||||
|
||||
choice_tuples = [ ( HC.duplicate_type_string_lookup[ duplicate_type ], duplicate_type ) for duplicate_type in duplicate_types ]
|
||||
|
||||
|
@ -2242,9 +2298,9 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledCanvas ):
|
|||
|
||||
self._SetDuplicatesFocusedBetter()
|
||||
|
||||
elif action == 'duplicate_media_set_not_duplicate':
|
||||
elif action == 'duplicate_media_set_false_positive':
|
||||
|
||||
self._SetDuplicates( HC.DUPLICATE_NOT_DUPLICATE )
|
||||
self._SetDuplicates( HC.DUPLICATE_FALSE_POSITIVE )
|
||||
|
||||
elif action == 'duplicate_media_set_same_quality':
|
||||
|
||||
|
@ -4117,8 +4173,6 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
ClientGUIMenus.AppendMenuItem( self, duplicates_action_submenu, 'set all selected as alternates', 'Set all the selected files as alternates.', self.ProcessApplicationCommand, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_media_set_alternate' ) )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( self, duplicates_action_submenu, 'set all selected as not duplicates', 'Set all the selected files as not duplicates.', self.ProcessApplicationCommand, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_media_set_not_duplicate' ) )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( self, duplicates_action_submenu, 'make a custom duplicates action', 'Choose which duplicates status to set to this selection and customise non-default merge options.', self.ProcessApplicationCommand, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_media_set_custom' ) )
|
||||
|
||||
if collections_selected:
|
||||
|
@ -4138,7 +4192,7 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
duplicates_edit_action_submenu = wx.Menu()
|
||||
|
||||
for duplicate_type in ( HC.DUPLICATE_BETTER, HC.DUPLICATE_SAME_QUALITY, HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_NOT_DUPLICATE ):
|
||||
for duplicate_type in ( HC.DUPLICATE_BETTER, HC.DUPLICATE_SAME_QUALITY ):
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( self, duplicates_edit_action_submenu, 'for ' + HC.duplicate_type_string_lookup[ duplicate_type ], 'Edit what happens when you set this status.', self._EditDuplicateActionOptions, duplicate_type )
|
||||
|
||||
|
@ -4160,7 +4214,7 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
duplicates_view_menu = wx.Menu()
|
||||
|
||||
for duplicate_type in ( HC.DUPLICATE_BETTER_OR_WORSE, HC.DUPLICATE_BETTER, HC.DUPLICATE_WORSE, HC.DUPLICATE_SAME_QUALITY, HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_NOT_DUPLICATE, HC.DUPLICATE_UNKNOWN ):
|
||||
for duplicate_type in ( HC.DUPLICATE_BETTER_OR_WORSE, HC.DUPLICATE_BETTER, HC.DUPLICATE_WORSE, HC.DUPLICATE_SAME_QUALITY, HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_FALSE_POSITIVE, HC.DUPLICATE_UNKNOWN ):
|
||||
|
||||
if duplicate_type in file_duplicate_types_to_counts:
|
||||
|
||||
|
|
|
@ -218,7 +218,11 @@ class DialogPageChooser( ClientGUIDialogs.Dialog ):
|
|||
|
||||
entries.append( ( 'page_query', CC.LOCAL_FILE_SERVICE_KEY ) )
|
||||
entries.append( ( 'page_query', CC.TRASH_SERVICE_KEY ) )
|
||||
entries.append( ( 'page_query', CC.COMBINED_LOCAL_FILE_SERVICE_KEY ) )
|
||||
|
||||
if HG.client_controller.new_options.GetBoolean( 'advanced_mode' ):
|
||||
|
||||
entries.append( ( 'page_query', CC.COMBINED_LOCAL_FILE_SERVICE_KEY ) )
|
||||
|
||||
|
||||
for service in self._services:
|
||||
|
||||
|
@ -417,6 +421,10 @@ class Page( wx.SplitterWindow ):
|
|||
|
||||
new_panel.Collect( self._page_key, collect_by )
|
||||
|
||||
sort_by = self._management_panel.GetSortBy()
|
||||
|
||||
new_panel.Sort( self._page_key, sort_by )
|
||||
|
||||
|
||||
self.ReplaceWindow( self._media_panel, new_panel )
|
||||
|
||||
|
|
|
@ -1505,17 +1505,6 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
def _CopyExternalShareURL( self ):
|
||||
|
||||
try:
|
||||
|
||||
external_ip = HydrusNATPunch.GetExternalIP()
|
||||
|
||||
except Exception as e:
|
||||
|
||||
wx.MessageBox( str( e ) )
|
||||
|
||||
return
|
||||
|
||||
|
||||
internal_port = self._service.GetPort()
|
||||
|
||||
if internal_port is None:
|
||||
|
@ -1523,18 +1512,22 @@ class ReviewServicePanel( wx.Panel ):
|
|||
wx.MessageBox( 'The local booru is not currently running!' )
|
||||
|
||||
|
||||
external_port = self._service.GetUPnPPort()
|
||||
|
||||
if external_port is None:
|
||||
|
||||
external_port = internal_port
|
||||
|
||||
|
||||
urls = []
|
||||
|
||||
for share_key in self._booru_shares.GetData( only_selected = True ):
|
||||
|
||||
url = 'http://' + external_ip + ':' + str( external_port ) + '/gallery?share_key=' + share_key.hex()
|
||||
try:
|
||||
|
||||
url = self._service.GetExternalShareURL( share_key )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.ShowException( e )
|
||||
|
||||
wx.MessageBox( 'Unfortunately, could not generate an external URL: {}'.format( e ) )
|
||||
|
||||
return
|
||||
|
||||
|
||||
urls.append( url )
|
||||
|
||||
|
|
|
@ -1504,7 +1504,7 @@ class EditJSONParsingRulePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
vbox.Add( self._parse_rule_type, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( self._string_match, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
@ -3070,7 +3070,7 @@ class EditPageParserPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( test_url_fetch_panel, rows )
|
||||
|
||||
test_url_fetch_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
test_url_fetch_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
test_url_fetch_panel.Add( self._fetch_example_data, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
test_url_fetch_panel.Add( self._test_network_job_control, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
|
|
@ -1041,6 +1041,11 @@ class PopupMessageManager( wx.Frame ):
|
|||
|
||||
def EventMove( self, event ):
|
||||
|
||||
if not self: # funny runtime error caused this
|
||||
|
||||
return
|
||||
|
||||
|
||||
if self._OKToAlterUI():
|
||||
|
||||
self._SizeAndPositionAndShow()
|
||||
|
|
|
@ -319,7 +319,7 @@ class PanelPredicateSystemDuplicateRelationships( PanelPredicateSystem ):
|
|||
|
||||
self._num = wx.SpinCtrl( self, min = 0, max = 65535 )
|
||||
|
||||
choices = [ ( HC.duplicate_type_string_lookup[ status ], status ) for status in ( HC.DUPLICATE_BETTER_OR_WORSE, HC.DUPLICATE_BETTER, HC.DUPLICATE_WORSE, HC.DUPLICATE_SAME_QUALITY, HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_NOT_DUPLICATE, HC.DUPLICATE_UNKNOWN ) ]
|
||||
choices = [ ( HC.duplicate_type_string_lookup[ status ], status ) for status in ( HC.DUPLICATE_BETTER_OR_WORSE, HC.DUPLICATE_BETTER, HC.DUPLICATE_WORSE, HC.DUPLICATE_SAME_QUALITY, HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_FALSE_POSITIVE, HC.DUPLICATE_UNKNOWN ) ]
|
||||
|
||||
self._dupe_type = ClientGUICommon.BetterRadioBox( self, choices = choices, style = wx.RA_SPECIFY_ROWS )
|
||||
|
||||
|
|
|
@ -271,7 +271,7 @@ class EditCookiePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.Add( expires_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
@ -363,7 +363,7 @@ class EditDefaultTagImportOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.Add( self._list_ctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
@ -1135,11 +1135,7 @@ class EditDuplicateActionOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
self._delete_second_file = wx.CheckBox( self )
|
||||
self._sync_archive = wx.CheckBox( self )
|
||||
self._delete_both_files = wx.CheckBox( self )
|
||||
|
||||
self._delete_both_files.SetToolTip( 'This is only enabled on custom actions.' )
|
||||
|
||||
self._sync_urls_action = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
|
@ -1154,7 +1150,7 @@ class EditDuplicateActionOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
( tag_service_options, rating_service_options, delete_second_file, sync_archive, delete_both_files, sync_urls_action ) = duplicate_action_options.ToTuple()
|
||||
( tag_service_options, rating_service_options, sync_archive, sync_urls_action ) = duplicate_action_options.ToTuple()
|
||||
|
||||
services_manager = HG.client_controller.services_manager
|
||||
|
||||
|
@ -1182,18 +1178,11 @@ class EditDuplicateActionOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
|
||||
|
||||
self._delete_second_file.SetValue( delete_second_file )
|
||||
self._sync_archive.SetValue( sync_archive )
|
||||
self._delete_both_files.SetValue( delete_both_files )
|
||||
|
||||
#
|
||||
|
||||
if not for_custom_action:
|
||||
|
||||
self._delete_both_files.Disable()
|
||||
|
||||
|
||||
if self._duplicate_action in ( HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_NOT_DUPLICATE ) and not for_custom_action:
|
||||
if self._duplicate_action in ( HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_FALSE_POSITIVE ) and not for_custom_action:
|
||||
|
||||
self._sync_archive.Disable()
|
||||
self._sync_urls_action.Disable()
|
||||
|
@ -1205,11 +1194,6 @@ class EditDuplicateActionOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._sync_urls_action.SelectClientData( sync_urls_action )
|
||||
|
||||
|
||||
if self._duplicate_action != HC.DUPLICATE_BETTER:
|
||||
|
||||
self._delete_second_file.Disable()
|
||||
|
||||
|
||||
#
|
||||
|
||||
button_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
@ -1244,8 +1228,6 @@ class EditDuplicateActionOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'delete worse file: ', self._delete_second_file ) )
|
||||
rows.append( ( 'delete both files: ', self._delete_both_files ) )
|
||||
rows.append( ( 'if one file is archived, archive the other as well: ', self._sync_archive ) )
|
||||
rows.append( ( 'sync known urls?: ', self._sync_urls_action ) )
|
||||
|
||||
|
@ -1574,12 +1556,10 @@ class EditDuplicateActionOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
tag_service_actions = self._tag_service_actions.GetClientData()
|
||||
rating_service_actions = self._rating_service_actions.GetClientData()
|
||||
delete_second_file = self._delete_second_file.GetValue()
|
||||
sync_archive = self._sync_archive.GetValue()
|
||||
delete_both_files = self._delete_both_files.GetValue()
|
||||
sync_urls_action = self._sync_urls_action.GetChoice()
|
||||
|
||||
duplicate_action_options = ClientDuplicates.DuplicateActionOptions( tag_service_actions, rating_service_actions, delete_second_file, sync_archive, delete_both_files, sync_urls_action )
|
||||
duplicate_action_options = ClientDuplicates.DuplicateActionOptions( tag_service_actions, rating_service_actions, sync_archive, sync_urls_action )
|
||||
|
||||
return duplicate_action_options
|
||||
|
||||
|
@ -5205,7 +5185,7 @@ class EditTagImportOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( downloader_options_panel, rows )
|
||||
|
||||
downloader_options_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
downloader_options_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
if not self._show_downloader_options:
|
||||
|
||||
|
|
|
@ -1075,6 +1075,17 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
self._upnp = ClientGUICommon.NoneableSpinCtrl( self._client_server_options_panel, 'upnp port', none_phrase = 'do not forward port', max = 65535 )
|
||||
|
||||
self._external_scheme_override = ClientGUICommon.NoneableTextCtrl( self._client_server_options_panel, message = 'scheme (http/https) override when copying external links' )
|
||||
self._external_host_override = ClientGUICommon.NoneableTextCtrl( self._client_server_options_panel, message = 'host override when copying external links' )
|
||||
self._external_port_override = ClientGUICommon.NoneableTextCtrl( self._client_server_options_panel, message = 'port override when copying external links' )
|
||||
|
||||
if service_type != HC.LOCAL_BOORU:
|
||||
|
||||
self._external_scheme_override.Hide()
|
||||
self._external_host_override.Hide()
|
||||
self._external_port_override.Hide()
|
||||
|
||||
|
||||
self._bandwidth_rules = ClientGUIControls.BandwidthRulesCtrl( self._client_server_options_panel, dictionary[ 'bandwidth_rules' ] )
|
||||
|
||||
#
|
||||
|
@ -1089,6 +1100,10 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._support_cors.SetValue( dictionary[ 'support_cors' ] )
|
||||
self._log_requests.SetValue( dictionary[ 'log_requests' ] )
|
||||
|
||||
self._external_scheme_override.SetValue( dictionary[ 'external_scheme_override' ] )
|
||||
self._external_host_override.SetValue( dictionary[ 'external_host_override' ] )
|
||||
self._external_port_override.SetValue( dictionary[ 'external_port_override' ] )
|
||||
|
||||
#
|
||||
|
||||
self._client_server_options_panel.Add( self._port, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
@ -1096,6 +1111,9 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._client_server_options_panel.Add( self._support_cors, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._client_server_options_panel.Add( self._log_requests, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._client_server_options_panel.Add( self._upnp, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._client_server_options_panel.Add( self._external_scheme_override, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._client_server_options_panel.Add( self._external_host_override, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._client_server_options_panel.Add( self._external_port_override, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._client_server_options_panel.Add( self._bandwidth_rules, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.Add( self._client_server_options_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
@ -1131,6 +1149,9 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
dictionary_part[ 'allow_non_local_connections' ] = self._allow_non_local_connections.GetValue()
|
||||
dictionary_part[ 'support_cors' ] = self._support_cors.GetValue()
|
||||
dictionary_part[ 'log_requests' ] = self._log_requests.GetValue()
|
||||
dictionary_part[ 'external_scheme_override' ] = self._external_scheme_override.GetValue()
|
||||
dictionary_part[ 'external_host_override' ] = self._external_host_override.GetValue()
|
||||
dictionary_part[ 'external_port_override' ] = self._external_port_override.GetValue()
|
||||
dictionary_part[ 'bandwidth_rules' ] = self._bandwidth_rules.GetValue()
|
||||
|
||||
return dictionary_part
|
||||
|
@ -1268,7 +1289,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
self.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
@ -1319,7 +1340,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
self.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
@ -1548,14 +1569,13 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
self._verify_regular_https = wx.CheckBox( general )
|
||||
|
||||
self._external_host = wx.TextCtrl( self )
|
||||
self._external_host.SetToolTip( 'If you have trouble parsing your external ip using UPnP, you can force it to be this.' )
|
||||
|
||||
self._network_timeout = wx.SpinCtrl( self, min = 3, max = 300 )
|
||||
self._network_timeout = wx.SpinCtrl( general, min = 3, max = 300 )
|
||||
self._network_timeout.SetToolTip( 'If a network connection cannot be made in this duration or, if once started, it experiences uninterrupted inactivity for six times this duration, it will be abandoned.' )
|
||||
|
||||
self._max_network_jobs = wx.SpinCtrl( self, min = 1, max = 30 )
|
||||
self._max_network_jobs_per_domain = wx.SpinCtrl( self, min = 1, max = 5 )
|
||||
self._max_network_jobs = wx.SpinCtrl( general, min = 1, max = 30 )
|
||||
self._max_network_jobs_per_domain = wx.SpinCtrl( general, min = 1, max = 5 )
|
||||
|
||||
#
|
||||
|
||||
proxy_panel = ClientGUICommon.StaticBox( self, 'proxy settings' )
|
||||
|
||||
|
@ -1576,15 +1596,13 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._max_network_jobs.SetValue( self._new_options.GetInteger( 'max_network_jobs' ) )
|
||||
self._max_network_jobs_per_domain.SetValue( self._new_options.GetInteger( 'max_network_jobs_per_domain' ) )
|
||||
|
||||
if HC.options[ 'external_host' ] is not None:
|
||||
|
||||
self._external_host.SetValue( HC.options[ 'external_host' ] )
|
||||
|
||||
|
||||
#
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'network timeout (seconds): ', self._network_timeout ) )
|
||||
rows.append( ( 'max number of simultaneous active network jobs: ', self._max_network_jobs ) )
|
||||
rows.append( ( 'max number of simultaneous active network jobs per domain: ', self._max_network_jobs_per_domain ) )
|
||||
rows.append( ( 'BUGFIX: verify regular https traffic:', self._verify_regular_https ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( general, rows )
|
||||
|
@ -1616,19 +1634,9 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
#
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'network timeout (seconds): ', self._network_timeout ) )
|
||||
rows.append( ( 'max number of simultaneous active network jobs: ', self._max_network_jobs ) )
|
||||
rows.append( ( 'max number of simultaneous active network jobs per domain: ', self._max_network_jobs_per_domain ) )
|
||||
rows.append( ( 'external ip/host override: ', self._external_host ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( general, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.Add( proxy_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
@ -1641,15 +1649,6 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._new_options.SetNoneableString( 'http_proxy', self._http_proxy.GetValue() )
|
||||
self._new_options.SetNoneableString( 'https_proxy', self._https_proxy.GetValue() )
|
||||
|
||||
external_host = self._external_host.GetValue()
|
||||
|
||||
if external_host == '':
|
||||
|
||||
external_host = None
|
||||
|
||||
|
||||
HC.options[ 'external_host' ] = external_host
|
||||
|
||||
self._new_options.SetInteger( 'network_timeout', self._network_timeout.GetValue() )
|
||||
self._new_options.SetInteger( 'max_network_jobs', self._max_network_jobs.GetValue() )
|
||||
self._new_options.SetInteger( 'max_network_jobs_per_domain', self._max_network_jobs_per_domain.GetValue() )
|
||||
|
@ -2044,7 +2043,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( mime_panel, rows )
|
||||
|
||||
mime_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
mime_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
mime_panel.Add( self._mime_launch_listctrl, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
vbox.Add( mime_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
@ -2412,7 +2411,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.Add( frame_locations_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
@ -2610,7 +2609,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
@ -2702,7 +2701,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._new_options = HG.client_controller.new_options
|
||||
|
||||
self._jobs_panel = ClientGUICommon.StaticBox( self, 'when to run high cpu jobs' )
|
||||
self._maintenance_panel = ClientGUICommon.StaticBox( self, 'maintenance period' )
|
||||
self._file_maintenance_panel = ClientGUICommon.StaticBox( self, 'file maintenance' )
|
||||
self._vacuum_panel = ClientGUICommon.StaticBox( self, 'vacuum' )
|
||||
|
||||
self._idle_panel = ClientGUICommon.StaticBox( self._jobs_panel, 'idle' )
|
||||
self._shutdown_panel = ClientGUICommon.StaticBox( self._jobs_panel, 'shutdown' )
|
||||
|
@ -2728,11 +2728,28 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._idle_shutdown.Bind( wx.EVT_CHOICE, self.EventIdleShutdown )
|
||||
|
||||
self._idle_shutdown_max_minutes = wx.SpinCtrl( self._shutdown_panel, min = 1, max = 1440 )
|
||||
self._shutdown_work_period = ClientGUITime.TimeDeltaButton( self._shutdown_panel, min = 3600, days = True, hours = True )
|
||||
self._shutdown_work_period = ClientGUITime.TimeDeltaButton( self._shutdown_panel, min = 60, days = True, hours = True, minutes = True )
|
||||
|
||||
#
|
||||
|
||||
self._maintenance_vacuum_period_days = ClientGUICommon.NoneableSpinCtrl( self._maintenance_panel, '', min = 28, max = 365, none_phrase = 'do not automatically vacuum' )
|
||||
self._file_maintenance_during_idle = wx.CheckBox( self._file_maintenance_panel )
|
||||
self._file_maintenance_on_shutdown = wx.CheckBox( self._file_maintenance_panel )
|
||||
self._file_maintenance_throttle_enable = wx.CheckBox( self._file_maintenance_panel )
|
||||
|
||||
min_unit_value = 10
|
||||
max_unit_value = 100000
|
||||
min_time_delta = 3600
|
||||
|
||||
self._file_maintenance_throttle_velocity = ClientGUITime.VelocityCtrl( self._file_maintenance_panel, min_unit_value, max_unit_value, min_time_delta, days = True, hours = True, per_phrase = 'every', unit = 'files' )
|
||||
|
||||
tt = 'Please note that this throttle is not very rigorous, as file processing history is not currently saved on client restart. If you restart the client, the file manager thinks it has run on 0 files and will be happy to run until the throttle kicks in again.'
|
||||
|
||||
self._file_maintenance_throttle_enable.SetToolTip( tt )
|
||||
self._file_maintenance_throttle_velocity.SetToolTip( tt )
|
||||
|
||||
#
|
||||
|
||||
self._maintenance_vacuum_period_days = ClientGUICommon.NoneableSpinCtrl( self._vacuum_panel, '', min = 28, max = 365, none_phrase = 'do not automatically vacuum' )
|
||||
|
||||
tts = 'Vacuuming is a kind of full defrag of the database\'s internal page table. It can take a long time (1MB/s) on a slow drive and does not need to be done often, so feel free to set this at 90 days+.'
|
||||
|
||||
|
@ -2749,8 +2766,21 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._idle_shutdown_max_minutes.SetValue( HC.options[ 'idle_shutdown_max_minutes' ] )
|
||||
self._shutdown_work_period.SetValue( self._new_options.GetInteger( 'shutdown_work_period' ) )
|
||||
|
||||
self._file_maintenance_during_idle.SetValue( self._new_options.GetBoolean( 'file_maintenance_during_idle' ) )
|
||||
self._file_maintenance_on_shutdown.SetValue( self._new_options.GetBoolean( 'file_maintenance_on_shutdown' ) )
|
||||
self._file_maintenance_throttle_enable.SetValue( self._new_options.GetBoolean( 'file_maintenance_throttle_enable' ) )
|
||||
|
||||
file_maintenance_throttle_files = self._new_options.GetInteger( 'file_maintenance_throttle_files' )
|
||||
file_maintenance_throttle_time_delta = self._new_options.GetInteger( 'file_maintenance_throttle_time_delta' )
|
||||
|
||||
file_maintenance_throttle_velocity = ( file_maintenance_throttle_files, file_maintenance_throttle_time_delta )
|
||||
|
||||
self._file_maintenance_throttle_velocity.SetValue( file_maintenance_throttle_velocity )
|
||||
|
||||
self._maintenance_vacuum_period_days.SetValue( self._new_options.GetNoneableInteger( 'maintenance_vacuum_period_days' ) )
|
||||
|
||||
self._file_maintenance_throttle_enable.Bind( wx.EVT_CHECKBOX, self.EventFileMaintenanceThrottle )
|
||||
|
||||
#
|
||||
|
||||
rows = []
|
||||
|
@ -2762,7 +2792,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._idle_panel, rows )
|
||||
|
||||
self._idle_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._idle_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
|
@ -2774,7 +2804,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._shutdown_panel, rows )
|
||||
|
||||
self._shutdown_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._shutdown_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
|
@ -2802,23 +2832,42 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
#
|
||||
|
||||
message = 'File maintenance jobs include reparsing file metadata and regenerating thumbnails.'
|
||||
|
||||
self._file_maintenance_panel.Add( ClientGUICommon.BetterStaticText( self._file_maintenance_panel, label = message ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'Permit file maintenance to run during idle time: ', self._file_maintenance_during_idle ) )
|
||||
rows.append( ( 'Permit file maintenance to run during shutdown: ', self._file_maintenance_on_shutdown ) )
|
||||
rows.append( ( 'Throttle file maintenance: ', self._file_maintenance_throttle_enable ) )
|
||||
rows.append( ( 'Throttle to this value: ', self._file_maintenance_throttle_velocity ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._file_maintenance_panel, rows )
|
||||
|
||||
self._file_maintenance_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'Number of days to wait between vacuums: ', self._maintenance_vacuum_period_days ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._maintenance_panel, rows )
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._vacuum_panel, rows )
|
||||
|
||||
self._maintenance_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
self._vacuum_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( self._jobs_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( self._maintenance_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( self._file_maintenance_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( self._vacuum_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
self._EnableDisableFileMaintenanceThrottle()
|
||||
self._EnableDisableIdleNormal()
|
||||
self._EnableDisableIdleShutdown()
|
||||
|
||||
|
@ -2831,12 +2880,16 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._idle_mouse_period.Enable()
|
||||
self._idle_cpu_max.Enable()
|
||||
|
||||
self._file_maintenance_during_idle.Enable()
|
||||
|
||||
else:
|
||||
|
||||
self._idle_period.Disable()
|
||||
self._idle_mouse_period.Disable()
|
||||
self._idle_cpu_max.Disable()
|
||||
|
||||
self._file_maintenance_during_idle.Disable()
|
||||
|
||||
|
||||
|
||||
def _EnableDisableIdleShutdown( self ):
|
||||
|
@ -2846,11 +2899,27 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._shutdown_work_period.Disable()
|
||||
self._idle_shutdown_max_minutes.Disable()
|
||||
|
||||
self._file_maintenance_on_shutdown.Disable()
|
||||
|
||||
else:
|
||||
|
||||
self._shutdown_work_period.Enable()
|
||||
self._idle_shutdown_max_minutes.Enable()
|
||||
|
||||
self._file_maintenance_on_shutdown.Enable()
|
||||
|
||||
|
||||
|
||||
def _EnableDisableFileMaintenanceThrottle( self ):
|
||||
|
||||
if self._file_maintenance_throttle_enable.GetValue() == True:
|
||||
|
||||
self._file_maintenance_throttle_velocity.Enable()
|
||||
|
||||
else:
|
||||
|
||||
self._file_maintenance_throttle_velocity.Disable()
|
||||
|
||||
|
||||
|
||||
def EventIdleNormal( self, event ):
|
||||
|
@ -2863,6 +2932,11 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._EnableDisableIdleShutdown()
|
||||
|
||||
|
||||
def EventFileMaintenanceThrottle( self, event ):
|
||||
|
||||
self._EnableDisableFileMaintenanceThrottle()
|
||||
|
||||
|
||||
def UpdateOptions( self ):
|
||||
|
||||
HC.options[ 'idle_normal' ] = self._idle_normal.GetValue()
|
||||
|
@ -2876,6 +2950,17 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
self._new_options.SetInteger( 'shutdown_work_period', self._shutdown_work_period.GetValue() )
|
||||
|
||||
self._new_options.SetBoolean( 'file_maintenance_during_idle', self._file_maintenance_during_idle.GetValue() )
|
||||
self._new_options.SetBoolean( 'file_maintenance_on_shutdown', self._file_maintenance_on_shutdown.GetValue() )
|
||||
self._new_options.SetBoolean( 'file_maintenance_throttle_enable', self._file_maintenance_throttle_enable.GetValue() )
|
||||
|
||||
file_maintenance_throttle_velocity = self._file_maintenance_throttle_velocity.GetValue()
|
||||
|
||||
( file_maintenance_throttle_files, file_maintenance_throttle_time_delta ) = file_maintenance_throttle_velocity
|
||||
|
||||
self._new_options.SetInteger( 'file_maintenance_throttle_files', file_maintenance_throttle_files )
|
||||
self._new_options.SetInteger( 'file_maintenance_throttle_time_delta', file_maintenance_throttle_time_delta )
|
||||
|
||||
self._new_options.SetNoneableInteger( 'maintenance_vacuum_period_days', self._maintenance_vacuum_period_days.GetValue() )
|
||||
|
||||
|
||||
|
@ -2963,7 +3048,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
self._media_viewer_panel.Add( self._media_viewer_options, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._media_viewer_panel.Add( self._media_viewer_edit_button, CC.FLAGS_LONE_BUTTON )
|
||||
|
@ -3123,6 +3208,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
self._fallback_sort = ClientGUICommon.ChoiceSort( self )
|
||||
|
||||
self._save_page_sort_on_change = wx.CheckBox( self )
|
||||
|
||||
self._default_collect = ClientGUICommon.CheckboxCollect( self )
|
||||
|
||||
self._sort_by = wx.ListBox( self )
|
||||
|
@ -3162,12 +3249,15 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._sort_by.Append( '-'.join( sort_by ), sort_by )
|
||||
|
||||
|
||||
self._save_page_sort_on_change.SetValue( self._new_options.GetBoolean( 'save_page_sort_on_change' ) )
|
||||
|
||||
#
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'Default sort: ', self._default_sort ) )
|
||||
rows.append( ( 'Secondary sort (when primary gives two equal values): ', self._fallback_sort ) )
|
||||
rows.append( ( 'Update default sort every time a new sort is manually chosen: ', self._save_page_sort_on_change ) )
|
||||
rows.append( ( 'Default collect: ', self._default_collect ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
@ -3228,6 +3318,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
self._new_options.SetDefaultSort( self._default_sort.GetSort() )
|
||||
self._new_options.SetFallbackSort( self._fallback_sort.GetSort() )
|
||||
self._new_options.SetBoolean( 'save_page_sort_on_change', self._save_page_sort_on_change.GetValue() )
|
||||
HC.options[ 'default_collect' ] = self._default_collect.GetChoice()
|
||||
|
||||
sort_by_choices = []
|
||||
|
@ -3422,7 +3513,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( media_panel, rows )
|
||||
|
||||
media_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
media_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
vbox.Add( media_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
@ -3444,7 +3535,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( buffer_panel, rows )
|
||||
|
||||
buffer_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
buffer_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
vbox.Add( buffer_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
@ -3464,7 +3555,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( ac_panel, rows )
|
||||
|
||||
ac_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
ac_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
vbox.Add( ac_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
@ -3476,7 +3567,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( misc_panel, rows )
|
||||
|
||||
misc_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
misc_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
vbox.Add( misc_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
@ -3700,7 +3791,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( general_panel, rows )
|
||||
|
||||
general_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
general_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
vbox.Add( general_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
@ -3818,7 +3909,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
gridbox = ClientGUICommon.WrapInGrid( render_panel, rows )
|
||||
|
||||
render_panel.Add( render_st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
render_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
render_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
vbox.Add( render_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
@ -4015,7 +4106,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
desc = 'This will search the database for statistically related tags based on what your focused file already has.'
|
||||
|
||||
panel_vbox.Add( ClientGUICommon.BetterStaticText( suggested_tags_related_panel, desc ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
panel_vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
panel_vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
suggested_tags_related_panel.SetSizer( panel_vbox )
|
||||
|
||||
|
@ -4030,7 +4121,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( suggested_tags_file_lookup_script_panel, rows )
|
||||
|
||||
panel_vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
panel_vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
suggested_tags_file_lookup_script_panel.SetSizer( panel_vbox )
|
||||
|
||||
|
@ -4061,7 +4152,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
desc = 'The manage tags dialog can provide several kinds of tag suggestions. For simplicity, most are turned off by default.'
|
||||
|
||||
suggested_tags_panel.Add( ClientGUICommon.BetterStaticText( suggested_tags_panel, desc ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
suggested_tags_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
suggested_tags_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
suggested_tags_panel.Add( suggest_tags_panel_notebook, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
#
|
||||
|
@ -4202,7 +4293,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
|
|
@ -437,7 +437,7 @@ class Shortcuts( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
def __iter__( self ):
|
||||
|
||||
for ( shortcut, command ) in list(self._shortcuts_to_commands.items()):
|
||||
for ( shortcut, command ) in list( self._shortcuts_to_commands.items() ):
|
||||
|
||||
yield ( shortcut, command )
|
||||
|
||||
|
|
|
@ -2450,7 +2450,10 @@ class ManageTagParents( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
reason = dlg.GetValue()
|
||||
|
||||
else: do_it = False
|
||||
else:
|
||||
|
||||
do_it = False
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -2506,7 +2509,10 @@ class ManageTagParents( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
reason = dlg.GetValue()
|
||||
|
||||
else: do_it = False
|
||||
else:
|
||||
|
||||
do_it = False
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -3356,7 +3362,10 @@ class ManageTagSiblings( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
|
||||
|
||||
self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PETITIONED ].update( current_pairs )
|
||||
if do_it:
|
||||
|
||||
self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PETITIONED ].update( current_pairs )
|
||||
|
||||
|
||||
|
||||
if len( pending_pairs ) > 0:
|
||||
|
|
|
@ -38,7 +38,11 @@ class EditCheckerOptions( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
self._death_file_velocity = VelocityCtrl( self, min_time_delta = 60, days = True, hours = True, minutes = True, per_phrase = 'in', unit = 'files' )
|
||||
min_unit_value = 0
|
||||
max_unit_value = 1000
|
||||
min_time_delta = 60
|
||||
|
||||
self._death_file_velocity = VelocityCtrl( self, min_unit_value, max_unit_value, min_time_delta, days = True, hours = True, minutes = True, per_phrase = 'in', unit = 'files' )
|
||||
|
||||
self._flat_check_period_checkbox = wx.CheckBox( self )
|
||||
|
||||
|
@ -489,11 +493,11 @@ class TimeDeltaCtrl( wx.Panel ):
|
|||
|
||||
class VelocityCtrl( wx.Panel ):
|
||||
|
||||
def __init__( self, parent, min_time_delta = 60, days = False, hours = False, minutes = False, seconds = False, per_phrase = 'per', unit = None ):
|
||||
def __init__( self, parent, min_unit_value, max_unit_value, min_time_delta, days = False, hours = False, minutes = False, seconds = False, per_phrase = 'per', unit = None ):
|
||||
|
||||
wx.Panel.__init__( self, parent )
|
||||
|
||||
self._num = wx.SpinCtrl( self, min = 0, max = 1000, size = ( 60, -1 ) )
|
||||
self._num = wx.SpinCtrl( self, min = min_unit_value, max = max_unit_value, size = ( 60, -1 ) )
|
||||
|
||||
self._times = TimeDeltaCtrl( self, min = min_time_delta, days = days, hours = hours, minutes = minutes, seconds = seconds )
|
||||
|
||||
|
|
|
@ -1226,7 +1226,10 @@ class MediaList( object ):
|
|||
return False
|
||||
|
||||
|
||||
def HasNoMedia( self ): return len( self._sorted_media ) == 0
|
||||
def HasNoMedia( self ):
|
||||
|
||||
return len( self._sorted_media ) == 0
|
||||
|
||||
|
||||
def ProcessContentUpdates( self, service_keys_to_content_updates ):
|
||||
|
||||
|
@ -1255,11 +1258,11 @@ class MediaList( object ):
|
|||
|
||||
deleted_from_trash_and_local_view = service_key == CC.TRASH_SERVICE_KEY and self._file_service_key in local_file_services
|
||||
|
||||
trashed_and_non_trash_local_view = HC.options[ 'remove_trashed_files' ] and service_key in non_trash_local_file_services and self._file_service_key in non_trash_local_file_services
|
||||
trashed_from_our_local_file_domain = HC.options[ 'remove_trashed_files' ] and service_key in local_file_domains and self._file_service_key == service_key
|
||||
|
||||
deleted_from_repo_and_repo_view = service_key not in local_file_services and self._file_service_key == service_key
|
||||
|
||||
if deleted_from_trash_and_local_view or trashed_and_non_trash_local_view or deleted_from_repo_and_repo_view:
|
||||
if deleted_from_trash_and_local_view or trashed_from_our_local_file_domain or deleted_from_repo_and_repo_view:
|
||||
|
||||
self._RemoveMediaByHashes( hashes )
|
||||
|
||||
|
@ -1474,11 +1477,24 @@ class MediaCollection( MediaList, Media ):
|
|||
return len( self._hashes )
|
||||
|
||||
|
||||
def GetNumInbox( self ): return sum( ( media.GetNumInbox() for media in self._sorted_media ) )
|
||||
def GetNumInbox( self ):
|
||||
|
||||
return sum( ( media.GetNumInbox() for media in self._sorted_media ) )
|
||||
|
||||
|
||||
def GetNumFrames( self ): return sum( ( media.GetNumFrames() for media in self._sorted_media ) )
|
||||
def GetNumFrames( self ):
|
||||
|
||||
num_frames = ( media.GetNumFrames() for media in self._sorted_media )
|
||||
|
||||
return sum( ( nf for nf in num_frames if nf is not None ) )
|
||||
|
||||
|
||||
def GetNumWords( self ): return sum( ( media.GetNumWords() for media in self._sorted_media ) )
|
||||
def GetNumWords( self ):
|
||||
|
||||
num_words = ( media.GetNumWords() for media in self._sorted_media )
|
||||
|
||||
return sum( ( nw for nw in num_words if nw is not None ) )
|
||||
|
||||
|
||||
def GetPrettyInfoLines( self ):
|
||||
|
||||
|
@ -1498,7 +1514,17 @@ class MediaCollection( MediaList, Media ):
|
|||
return self._ratings_manager
|
||||
|
||||
|
||||
def GetResolution( self ): return ( self._width, self._height )
|
||||
def GetResolution( self ):
|
||||
|
||||
if self._width is None:
|
||||
|
||||
return ( 0, 0 )
|
||||
|
||||
else:
|
||||
|
||||
return ( self._width, self._height )
|
||||
|
||||
|
||||
|
||||
def GetSingletonsTagsManagers( self ):
|
||||
|
||||
|
@ -1709,8 +1735,14 @@ class MediaSingleton( Media ):
|
|||
|
||||
( width, height ) = self._media_result.GetResolution()
|
||||
|
||||
if width is None: return ( 0, 0 )
|
||||
else: return ( width, height )
|
||||
if width is None:
|
||||
|
||||
return ( 0, 0 )
|
||||
|
||||
else:
|
||||
|
||||
return ( width, height )
|
||||
|
||||
|
||||
|
||||
def GetSize( self ):
|
||||
|
|
|
@ -313,6 +313,18 @@ def GetSearchURLs( url ):
|
|||
search_urls.add( r.geturl() )
|
||||
|
||||
|
||||
for url in list( search_urls ):
|
||||
|
||||
if url.endswith( '/' ):
|
||||
|
||||
search_urls.add( url[:-1] )
|
||||
|
||||
else:
|
||||
|
||||
search_urls.add( url + '/' )
|
||||
|
||||
|
||||
|
||||
return search_urls
|
||||
|
||||
VALID_DENIED = 0
|
||||
|
|
|
@ -90,6 +90,13 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
self._dictionary[ 'booleans' ][ 'saving_sash_positions_on_exit' ] = True
|
||||
|
||||
self._dictionary[ 'booleans' ][ 'file_maintenance_on_shutdown' ] = True
|
||||
self._dictionary[ 'booleans' ][ 'file_maintenance_during_idle' ] = True
|
||||
|
||||
self._dictionary[ 'booleans' ][ 'file_maintenance_throttle_enable' ] = True
|
||||
|
||||
self._dictionary[ 'booleans' ][ 'save_page_sort_on_change' ] = False
|
||||
|
||||
self._dictionary[ 'booleans' ][ 'pause_all_new_network_traffic' ] = False
|
||||
self._dictionary[ 'booleans' ][ 'pause_all_file_queues' ] = False
|
||||
self._dictionary[ 'booleans' ][ 'pause_all_watcher_checkers' ] = False
|
||||
|
@ -163,10 +170,8 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
from . import ClientTags
|
||||
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_BETTER ] = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE, ClientTags.TagFilter() ) ], [], True, True, sync_urls_action = HC.CONTENT_MERGE_ACTION_COPY )
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_SAME_QUALITY ] = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE, ClientTags.TagFilter() ) ], [], False, True, sync_urls_action = HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE )
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_ALTERNATE ] = ClientDuplicates.DuplicateActionOptions( [], [], False )
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_NOT_DUPLICATE ] = ClientDuplicates.DuplicateActionOptions( [], [], False )
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_BETTER ] = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE, ClientTags.TagFilter() ) ], [], sync_archive = True, sync_urls_action = HC.CONTENT_MERGE_ACTION_COPY )
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_SAME_QUALITY ] = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE, ClientTags.TagFilter() ) ], [], sync_archive = True, sync_urls_action = HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE )
|
||||
|
||||
#
|
||||
|
||||
|
@ -223,6 +228,9 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
self._dictionary[ 'integers' ][ 'thumbnail_border' ] = 1
|
||||
self._dictionary[ 'integers' ][ 'thumbnail_margin' ] = 2
|
||||
|
||||
self._dictionary[ 'integers' ][ 'file_maintenance_throttle_files' ] = 200
|
||||
self._dictionary[ 'integers' ][ 'file_maintenance_throttle_time_delta' ] = 86400
|
||||
|
||||
self._dictionary[ 'integers' ][ 'subscription_network_error_delay' ] = 12 * 3600
|
||||
self._dictionary[ 'integers' ][ 'subscription_other_error_delay' ] = 36 * 3600
|
||||
self._dictionary[ 'integers' ][ 'downloader_network_error_delay' ] = 90 * 60
|
||||
|
@ -693,7 +701,14 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
return self._dictionary[ 'duplicate_action_options' ][ duplicate_type ]
|
||||
if duplicate_type in self._dictionary[ 'duplicate_action_options' ]:
|
||||
|
||||
return self._dictionary[ 'duplicate_action_options' ][ duplicate_type ]
|
||||
|
||||
else:
|
||||
|
||||
return ClientDuplicates.DuplicateActionOptions( [], [] )
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ from . import HydrusConstants as HC
|
|||
from . import HydrusData
|
||||
from . import HydrusExceptions
|
||||
from . import HydrusGlobals as HG
|
||||
from . import HydrusNATPunch
|
||||
from . import HydrusNetwork
|
||||
from . import HydrusNetworking
|
||||
from . import HydrusSerialisable
|
||||
|
@ -65,6 +66,10 @@ def GenerateDefaultServiceDictionary( service_type ):
|
|||
dictionary[ 'support_cors' ] = False
|
||||
dictionary[ 'log_requests' ] = False
|
||||
|
||||
dictionary[ 'external_scheme_override' ] = None
|
||||
dictionary[ 'external_host_override' ] = None
|
||||
dictionary[ 'external_port_override' ] = None
|
||||
|
||||
if service_type == HC.LOCAL_BOORU:
|
||||
|
||||
allow_non_local_connections = True
|
||||
|
@ -339,6 +344,9 @@ class ServiceLocalServerService( Service ):
|
|||
dictionary[ 'log_requests' ] = self._log_requests
|
||||
dictionary[ 'bandwidth_tracker' ] = self._bandwidth_tracker
|
||||
dictionary[ 'bandwidth_rules' ] = self._bandwidth_rules
|
||||
dictionary[ 'external_scheme_override' ] = self._external_scheme_override
|
||||
dictionary[ 'external_host_override' ] = self._external_host_override
|
||||
dictionary[ 'external_port_override' ] = self._external_port_override
|
||||
|
||||
return dictionary
|
||||
|
||||
|
@ -354,6 +362,9 @@ class ServiceLocalServerService( Service ):
|
|||
self._log_requests = dictionary[ 'log_requests' ]
|
||||
self._bandwidth_tracker = dictionary[ 'bandwidth_tracker' ]
|
||||
self._bandwidth_rules = dictionary[ 'bandwidth_rules' ]
|
||||
self._external_scheme_override = dictionary[ 'external_scheme_override' ]
|
||||
self._external_host_override = dictionary[ 'external_host_override' ]
|
||||
self._external_port_override = dictionary[ 'external_port_override' ]
|
||||
|
||||
# this should support the same serverservice interface so we can just toss it at the regular serverengine and all the bandwidth will work ok
|
||||
|
||||
|
@ -424,7 +435,46 @@ class ServiceLocalServerService( Service ):
|
|||
|
||||
class ServiceLocalBooru( ServiceLocalServerService ):
|
||||
|
||||
pass
|
||||
def GetExternalShareURL( self, share_key ):
|
||||
|
||||
if self._external_scheme_override is None:
|
||||
|
||||
scheme = 'http'
|
||||
|
||||
else:
|
||||
|
||||
scheme = self._external_scheme_override
|
||||
|
||||
|
||||
if self._external_host_override is None:
|
||||
|
||||
host = HydrusNATPunch.GetExternalIP()
|
||||
|
||||
else:
|
||||
|
||||
host = self._external_host_override
|
||||
|
||||
|
||||
if self._external_port_override is None:
|
||||
|
||||
if self._upnp_port is None:
|
||||
|
||||
port = self._port
|
||||
|
||||
else:
|
||||
|
||||
port = self._upnp_port
|
||||
|
||||
|
||||
else:
|
||||
|
||||
port = self._external_port_override
|
||||
|
||||
|
||||
url = '{}://{}:{}/gallery?share_key={}'.format( scheme, host, port, share_key.hex() )
|
||||
|
||||
return url
|
||||
|
||||
|
||||
class ServiceClientAPI( ServiceLocalServerService ):
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 18
|
||||
SOFTWARE_VERSION = 352
|
||||
SOFTWARE_VERSION = 353
|
||||
CLIENT_API_VERSION = 6
|
||||
|
||||
SERVER_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
@ -196,7 +196,7 @@ DEFINITIONS_TYPE_HASHES = 0
|
|||
DEFINITIONS_TYPE_TAGS = 1
|
||||
|
||||
DUPLICATE_UNKNOWN = 0
|
||||
DUPLICATE_NOT_DUPLICATE = 1
|
||||
DUPLICATE_FALSE_POSITIVE = 1
|
||||
DUPLICATE_SAME_QUALITY = 2
|
||||
DUPLICATE_ALTERNATE = 3
|
||||
DUPLICATE_BETTER = 4
|
||||
|
@ -208,7 +208,7 @@ DUPLICATE_BETTER_OR_WORSE = 8
|
|||
duplicate_type_string_lookup = {}
|
||||
|
||||
duplicate_type_string_lookup[ DUPLICATE_UNKNOWN ] = 'potential duplicates'
|
||||
duplicate_type_string_lookup[ DUPLICATE_NOT_DUPLICATE ] = 'not duplicates'
|
||||
duplicate_type_string_lookup[ DUPLICATE_FALSE_POSITIVE ] = 'not related/false positive'
|
||||
duplicate_type_string_lookup[ DUPLICATE_SAME_QUALITY ] = 'same quality'
|
||||
duplicate_type_string_lookup[ DUPLICATE_ALTERNATE ] = 'alternates'
|
||||
duplicate_type_string_lookup[ DUPLICATE_BETTER ] = 'this is better'
|
||||
|
|
|
@ -493,6 +493,7 @@ class HydrusController( object ):
|
|||
|
||||
job = self.CallRepeating( 60.0, 300.0, self.MaintainDB )
|
||||
|
||||
job.WakeOnPubSub( 'wake_idle_workers' )
|
||||
job.ShouldDelayOnWakeup( True )
|
||||
|
||||
self._daemon_jobs[ 'maintain_db' ] = job
|
||||
|
|
|
@ -29,11 +29,6 @@ EXTERNAL_IP[ 'time' ] = 0
|
|||
|
||||
def GetExternalIP():
|
||||
|
||||
if 'external_host' in HC.options and HC.options[ 'external_host' ] is not None:
|
||||
|
||||
return HC.options[ 'external_host' ]
|
||||
|
||||
|
||||
if HydrusData.TimeHasPassed( EXTERNAL_IP[ 'time' ] + ( 3600 * 24 ) ):
|
||||
|
||||
cmd = [ upnpc_path, '-l' ]
|
||||
|
@ -118,8 +113,6 @@ def AddUPnPMapping( internal_client, internal_port, external_port, protocol, des
|
|||
|
||||
def GetUPnPMappings():
|
||||
|
||||
external_ip_address = GetExternalIP()
|
||||
|
||||
cmd = [ upnpc_path, '-l' ]
|
||||
|
||||
sbp_kwargs = HydrusData.GetSubprocessKWArgs( text = True )
|
||||
|
@ -203,7 +196,7 @@ def GetUPnPMappings():
|
|||
|
||||
lease_time = int( rest_of_line[1:] )
|
||||
|
||||
processed_data.append( ( description, internal_client, internal_port, external_ip_address, external_port, protocol, lease_time ) )
|
||||
processed_data.append( ( description, internal_client, internal_port, external_port, protocol, lease_time ) )
|
||||
|
||||
|
||||
return processed_data
|
||||
|
@ -261,7 +254,7 @@ class ServicesUPnPManager( object ):
|
|||
|
||||
current_mappings = GetUPnPMappings()
|
||||
|
||||
our_mappings = { ( internal_client, internal_port ) : external_port for ( description, internal_client, internal_port, external_ip_address, external_port, protocol, enabled ) in current_mappings }
|
||||
our_mappings = { ( internal_client, internal_port ) : external_port for ( description, internal_client, internal_port, external_port, protocol, enabled ) in current_mappings }
|
||||
|
||||
except:
|
||||
|
||||
|
|
|
@ -392,8 +392,7 @@ class HydrusResource( Resource ):
|
|||
if request.channel is None:
|
||||
|
||||
# Connection was lost, it seems.
|
||||
|
||||
request.finish()
|
||||
# no need for request.finish
|
||||
|
||||
return
|
||||
|
||||
|
@ -741,10 +740,10 @@ class HydrusResource( Resource ):
|
|||
|
||||
d.addErrback( self._errbackHandleEmergencyError, request )
|
||||
|
||||
reactor.callLater( 0, d.callback, request )
|
||||
|
||||
request.notifyFinish().addErrback( self._errbackDisconnected, d )
|
||||
|
||||
reactor.callLater( 0, d.callback, request )
|
||||
|
||||
return NOT_DONE_YET
|
||||
|
||||
|
||||
|
@ -764,10 +763,10 @@ class HydrusResource( Resource ):
|
|||
|
||||
d.addErrback( self._errbackHandleEmergencyError, request )
|
||||
|
||||
reactor.callLater( 0, d.callback, request )
|
||||
|
||||
request.notifyFinish().addErrback( self._errbackDisconnected, d )
|
||||
|
||||
reactor.callLater( 0, d.callback, request )
|
||||
|
||||
return NOT_DONE_YET
|
||||
|
||||
|
||||
|
@ -789,10 +788,10 @@ class HydrusResource( Resource ):
|
|||
|
||||
d.addErrback( self._errbackHandleEmergencyError, request )
|
||||
|
||||
reactor.callLater( 0, d.callback, request )
|
||||
|
||||
request.notifyFinish().addErrback( self._errbackDisconnected, d )
|
||||
|
||||
reactor.callLater( 0, d.callback, request )
|
||||
|
||||
return NOT_DONE_YET
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ from . import ClientConstants as CC
|
|||
from . import HydrusGlobals as HG
|
||||
from . import ClientAPI
|
||||
from . import ClientDefaults
|
||||
from . import ClientFiles
|
||||
from . import ClientNetworking
|
||||
from . import ClientNetworkingBandwidth
|
||||
from . import ClientNetworkingDomain
|
||||
|
@ -253,7 +254,7 @@ class Controller( object ):
|
|||
self._managers = {}
|
||||
|
||||
self.services_manager = ClientCaches.ServicesManager( self )
|
||||
self.client_files_manager = ClientCaches.ClientFilesManager( self )
|
||||
self.client_files_manager = ClientFiles.ClientFilesManager( self )
|
||||
|
||||
self.parsing_cache = ClientCaches.ParsingCache()
|
||||
|
||||
|
|
|
@ -24,12 +24,10 @@ class TestNATPunch( unittest.TestCase ):
|
|||
|
||||
mappings = HydrusNATPunch.GetUPnPMappings()
|
||||
|
||||
external_ip_address = mappings[0][3]
|
||||
|
||||
mappings_without_lease_times = [ mapping[:-1] for mapping in mappings ]
|
||||
|
||||
self.assertIn( ( description_tcp, internal_client, internal_port, external_ip_address, external_port, 'TCP' ), mappings_without_lease_times )
|
||||
self.assertIn( ( description_udp, internal_client, internal_port, external_ip_address, external_port, 'UDP' ), mappings_without_lease_times )
|
||||
self.assertIn( ( description_tcp, internal_client, internal_port, external_port, 'TCP' ), mappings_without_lease_times )
|
||||
self.assertIn( ( description_udp, internal_client, internal_port, external_port, 'UDP' ), mappings_without_lease_times )
|
||||
|
||||
HydrusNATPunch.RemoveUPnPMapping( external_port, 'TCP' )
|
||||
HydrusNATPunch.RemoveUPnPMapping( external_port, 'UDP' )
|
||||
|
@ -38,7 +36,7 @@ class TestNATPunch( unittest.TestCase ):
|
|||
|
||||
mappings_without_lease_times = [ mapping[:-1] for mapping in mappings ]
|
||||
|
||||
self.assertNotIn( ( description_tcp, internal_client, internal_port, external_ip_address, external_port, 'TCP' ), mappings_without_lease_times )
|
||||
self.assertNotIn( ( description_udp, internal_client, internal_port, external_ip_address, external_port, 'UDP' ), mappings_without_lease_times )
|
||||
self.assertNotIn( ( description_tcp, internal_client, internal_port, external_port, 'TCP' ), mappings_without_lease_times )
|
||||
self.assertNotIn( ( description_udp, internal_client, internal_port, external_port, 'UDP' ), mappings_without_lease_times )
|
||||
|
||||
|
||||
|
|
|
@ -159,9 +159,9 @@ class TestSerialisables( unittest.TestCase ):
|
|||
self.assertEqual( obj.ToTuple(), dupe_obj.ToTuple() )
|
||||
|
||||
|
||||
duplicate_action_options_delete_and_move = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ) ], True )
|
||||
duplicate_action_options_copy = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY ) ], False )
|
||||
duplicate_action_options_merge = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ) ], False )
|
||||
duplicate_action_options_delete_and_move = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE ) ] )
|
||||
duplicate_action_options_copy = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY ) ] )
|
||||
duplicate_action_options_merge = ClientDuplicates.DuplicateActionOptions( [ ( CC.LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE, ClientTags.TagFilter() ) ], [ ( TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ), ( TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE ) ] )
|
||||
|
||||
inbox = True
|
||||
size = 40960
|
||||
|
@ -277,7 +277,7 @@ class TestSerialisables( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, local_media_empty, file_deletion_reason = file_deletion_reason )
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, local_media_empty, delete_second = True, file_deletion_reason = file_deletion_reason )
|
||||
|
||||
scu = {}
|
||||
|
||||
|
@ -287,7 +287,7 @@ class TestSerialisables( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, trashed_media_empty, file_deletion_reason = file_deletion_reason )
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, trashed_media_empty, delete_second = True, file_deletion_reason = file_deletion_reason )
|
||||
|
||||
scu = {}
|
||||
|
||||
|
@ -297,13 +297,13 @@ class TestSerialisables( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, deleted_media_empty, file_deletion_reason = file_deletion_reason )
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, deleted_media_empty, delete_second = True, file_deletion_reason = file_deletion_reason )
|
||||
|
||||
self.assertEqual( result, {} )
|
||||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, other_local_media_has_values, file_deletion_reason = file_deletion_reason )
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_has_values, other_local_media_has_values, delete_second = True, file_deletion_reason = file_deletion_reason )
|
||||
|
||||
scu = {}
|
||||
|
||||
|
@ -316,7 +316,7 @@ class TestSerialisables( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_empty, other_local_media_has_values, file_deletion_reason = file_deletion_reason )
|
||||
result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates( local_media_empty, other_local_media_has_values, delete_second = True, file_deletion_reason = file_deletion_reason )
|
||||
|
||||
scu = {}
|
||||
|
||||
|
|
Loading…
Reference in New Issue