Version 285

This commit is contained in:
Hydrus Network Developer 2017-12-06 16:06:56 -06:00
parent a7f254485a
commit de9b2e9f8b
59 changed files with 1665 additions and 1179 deletions

View File

@ -8,6 +8,38 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 285</h3></li>
<ul>
<li>added 'network' main gui menu and moved network stuff from services to it</li>
<li>split the new domain manager stuff up into separate dialogs and menu entries on the new network menu</li>
<li>manage url classes dialog now lists url type in the listctrl</li>
<li>added url class links info (which will permit client-specific settings and downloader mappings for url classes) to the domain manager</li>
<li>wrote a 'url class links' dialog and added it to the new network menu (only the 'display on media viewer' part works atm)</li>
<li>the domain manager now filters urls on the media viewer depending on whether they have a url match and are set to display in the new links panel</li>
<li>updated the local booru service code to the new service system</li>
<li>the local booru's shares can be reviewed again under review services</li>
<li>the local booru's port and bandwidth settings can be set again under manage services</li>
<li>the different gui parts of the local booru are updated to new controls</li>
<li>fixed a local booru 404 reporting error</li>
<li>the edit subscription panel now has a 'paste queries' button that lets you add queries en masse</li>
<li>added 'manage_file_urls' to shortcuts system</li>
<li>added several 'get_similar_to_x' actions to the shortcuts system</li>
<li>the manage upnp dialog now initialises its mappings on another thread and fails better when UpnP mappings cannot be fetched</li>
<li>connection and readtimeout network exceptions are now recognised more specifically by the client. subscriptions will only delay for an hour on one of these exceptions</li>
<li>improved the resilience of the HF login code after wake from sleep (when networking is often not available for a couple of seconds)</li>
<li>like the recent subscription query randomisation update, subscriptions themselves are now synced in random order (this stops a subscription named 'aadvark' always getting first bite into available bandwidth)</li>
<li>fixed import for jpegs that have unparsable exif data</li>
<li>fixed a bug in 'next month' bandwidth estimate calculation when the month is December, wew</li>
<li>fixed some logic that was setting max page at 165 rather than the intended 200 and added a dialog that even lets you engage the debug override at 200 if you are insane</li>
<li>all audio mime detection and duration parsing is now done through ffmpeg. hsaudiotag is no longer needed to run the program</li>
<li>since the old listctrl sort crash bug is still hitting some people, I've disabled sort on the old listctrl class. feel free to try to sort any listctrls now, no matter your situation. I will continue replacing the old class with the new working class over time</li>
<li>updated another listctrl</li>
<li>a ton of misc controller options/manager access refactoring</li>
<li>cleared out some old code</li>
<li>moved Time controls to their own file and added velocity and checker options stuff as well</li>
<li>wrote a new edit panel for single controls and updated the time delta button to use it</li>
<li>misc refactoring</li>
</ul>
<li><h3>version 284</h3></li>
<ul>
<li>fixed subscription queries turning dead on the initial sync</li>

View File

@ -17,7 +17,7 @@
<h3>what you will need</h3>
<p>You will need basic python experience, python 2.7 and a number of python modules. Most of it you can get through pip. I think this will do for most systems:</p>
<ul>
<li>pip install beautifulsoup4 hsaudiotag lxml lz4 nose numpy opencv-python pafy Pillow psutil pycrypto pylzma PyOpenSSL PyPDF2 PyYAML requests Send2Trash service_identity twisted youtube-dl</li>
<li>pip install beautifulsoup4 lxml lz4 nose numpy opencv-python pafy Pillow psutil pycrypto pylzma PyOpenSSL PyPDF2 PyYAML requests Send2Trash service_identity twisted youtube-dl</li>
</ul>
<p>Although you may want to do all that in smaller batches. Ultimately, the best way to figure out if you have enough is to just keep running client.pyw and see what it complains about missing.</p>
<p>I use Ubuntu 17.04, which also requires something like:</p>

View File

@ -309,9 +309,7 @@ class ClientFilesManager( object ):
full_size_path = self._GenerateExpectedFullSizeThumbnailPath( hash )
options = self._controller.GetOptions()
thumbnail_dimensions = options[ 'thumbnail_dimensions' ]
thumbnail_dimensions = self._controller.options[ 'thumbnail_dimensions' ]
if mime in ( HC.IMAGE_GIF, HC.IMAGE_PNG ):
@ -385,7 +383,7 @@ class ClientFilesManager( object ):
def _GetRebalanceTuple( self ):
( locations_to_ideal_weights, resized_thumbnail_override, full_size_thumbnail_override ) = self._controller.GetNewOptions().GetClientFilesLocationsToIdealWeights()
( locations_to_ideal_weights, resized_thumbnail_override, full_size_thumbnail_override ) = self._controller.new_options.GetClientFilesLocationsToIdealWeights()
total_weight = sum( locations_to_ideal_weights.values() )
@ -1341,8 +1339,6 @@ class DataCache( object ):
if key not in self._keys_to_data:
options = self._controller.GetOptions()
while self._total_estimated_memory_footprint > self._cache_size:
self._DeleteItem()
@ -1676,9 +1672,7 @@ class RenderedImageCache( object ):
self._controller = controller
options = self._controller.GetOptions()
cache_size = options[ 'fullscreen_cache_size' ]
cache_size = self._controller.options[ 'fullscreen_cache_size' ]
self._data_cache = DataCache( self._controller, cache_size, timeout = 600 )
@ -1723,9 +1717,7 @@ class ThumbnailCache( object ):
self._controller = controller
options = self._controller.GetOptions()
cache_size = options[ 'thumbnail_cache_size' ]
cache_size = self._controller.options[ 'thumbnail_cache_size' ]
self._data_cache = DataCache( self._controller, cache_size, timeout = 86400 )
@ -1747,9 +1739,7 @@ class ThumbnailCache( object ):
def _GetResizedHydrusBitmapFromHardDrive( self, display_media ):
options = self._controller.GetOptions()
thumbnail_dimensions = options[ 'thumbnail_dimensions' ]
thumbnail_dimensions = self._controller.options[ 'thumbnail_dimensions' ]
if tuple( thumbnail_dimensions ) == HC.UNSCALED_THUMBNAIL_DIMENSIONS:
@ -1819,11 +1809,9 @@ class ThumbnailCache( object ):
options = HG.client_controller.GetOptions()
( media_x, media_y ) = display_media.GetResolution()
( actual_x, actual_y ) = hydrus_bitmap.GetSize()
( desired_x, desired_y ) = options[ 'thumbnail_dimensions' ]
( desired_x, desired_y ) = self._controller.options[ 'thumbnail_dimensions' ]
too_large = actual_x > desired_x or actual_y > desired_y
@ -1883,9 +1871,9 @@ class ThumbnailCache( object ):
path = os.path.join( HC.STATIC_DIR, name + '.png' )
options = self._controller.GetOptions()
thumbnail_dimensions = self._controller.options[ 'thumbnail_dimensions' ]
thumbnail = HydrusFileHandling.GenerateThumbnailFromStaticImage( path, options[ 'thumbnail_dimensions' ], HC.IMAGE_PNG )
thumbnail = HydrusFileHandling.GenerateThumbnailFromStaticImage( path, thumbnail_dimensions, HC.IMAGE_PNG )
with open( temp_path, 'wb' ) as f:
@ -2441,9 +2429,7 @@ class TagParentsManager( object ):
def ExpandPredicates( self, service_key, predicates ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_parents_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_parents_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY
@ -2477,9 +2463,7 @@ class TagParentsManager( object ):
def ExpandTags( self, service_key, tags ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_parents_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_parents_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY
@ -2499,9 +2483,7 @@ class TagParentsManager( object ):
def GetParents( self, service_key, tag ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_parents_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_parents_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY
@ -2599,9 +2581,7 @@ class TagSiblingsManager( object ):
def GetAutocompleteSiblings( self, service_key, search_text, exact_match = False ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY
@ -2652,9 +2632,7 @@ class TagSiblingsManager( object ):
def GetSibling( self, service_key, tag ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY
@ -2676,9 +2654,7 @@ class TagSiblingsManager( object ):
def GetAllSiblings( self, service_key, tag ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY
@ -2719,9 +2695,7 @@ class TagSiblingsManager( object ):
def CollapsePredicates( self, service_key, predicates ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY
@ -2778,9 +2752,7 @@ class TagSiblingsManager( object ):
def CollapsePairs( self, service_key, pairs ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY
@ -2812,9 +2784,7 @@ class TagSiblingsManager( object ):
def CollapseStatusesToTags( self, service_key, statuses_to_tags ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY
@ -2836,9 +2806,7 @@ class TagSiblingsManager( object ):
def CollapseTag( self, service_key, tag ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY
@ -2860,9 +2828,7 @@ class TagSiblingsManager( object ):
def CollapseTags( self, service_key, tags ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY
@ -2875,9 +2841,7 @@ class TagSiblingsManager( object ):
def CollapseTagsToCount( self, service_key, tags_to_count ):
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
if self._controller.new_options.GetBoolean( 'apply_all_siblings_to_all_services' ):
service_key = CC.COMBINED_TAG_SERVICE_KEY

View File

@ -336,7 +336,7 @@ SHORTCUTS_RESERVED_NAMES = [ 'archive_delete_filter', 'duplicate_filter', 'media
# shortcut commands
SHORTCUTS_MEDIA_ACTIONS = [ 'manage_file_tags', 'manage_file_ratings', 'archive_file', 'inbox_file', 'delete_file', 'remove_file_from_view', 'open_file_in_external_program', 'launch_the_archive_delete_filter', 'copy_bmp', 'copy_file', 'copy_path', 'copy_sha256_hash' ]
SHORTCUTS_MEDIA_ACTIONS = [ 'manage_file_tags', 'manage_file_ratings', 'manage_file_urls', 'archive_file', 'inbox_file', 'delete_file', 'remove_file_from_view', 'open_file_in_external_program', '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' ]
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', 'synchronised_wait_switch', 'set_media_focus', 'show_hide_splitters', 'set_search_focus', 'unclose_page', 'close_page', 'redo', 'undo', 'flip_darkmode' ]

View File

@ -499,11 +499,6 @@ class Controller( HydrusController.HydrusController ):
return self.gui
def GetOptions( self ):
return self.options
def GetNewOptions( self ):
return self.new_options
@ -619,6 +614,8 @@ class Controller( HydrusController.HydrusController ):
wx.MessageBox( 'Your domain manager was missing on boot! I have recreated a new empty one. Please check that your hard drive and client are ok and let the hydrus dev know the details if there is a mystery.' )
domain_manager.Initialise()
login_manager = ClientNetworkingLogin.NetworkLoginManager()
self.network_engine = ClientNetworking.NetworkEngine( self, bandwidth_manager, session_manager, domain_manager, login_manager )
@ -629,7 +626,7 @@ class Controller( HydrusController.HydrusController ):
self._shortcuts_manager = ClientCaches.ShortcutsManager( self )
self._managers[ 'local_booru' ] = ClientCaches.LocalBooruCache( self )
self.local_booru_manager = ClientCaches.LocalBooruCache( self )
self.pub( 'splash_set_status_subtext', u'tag censorship' )
@ -1359,7 +1356,10 @@ class Controller( HydrusController.HydrusController ):
def Write( self, action, *args, **kwargs ):
if action == 'content_updates': self._managers[ 'undo' ].AddCommand( 'content_updates', *args, **kwargs )
if action == 'content_updates':
self._managers[ 'undo' ].AddCommand( 'content_updates', *args, **kwargs )
return HydrusController.HydrusController.Write( self, action, *args, **kwargs )

View File

@ -1633,7 +1633,7 @@ class DB( HydrusDB.HydrusDB ):
def _CacheSimilarFilesMaintenanceDue( self ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
if new_options.GetBoolean( 'maintain_similar_files_duplicate_pairs_during_idle' ):
@ -1644,7 +1644,7 @@ class DB( HydrusDB.HydrusDB ):
return True
search_distance = HG.client_controller.GetNewOptions().GetInteger( 'similar_files_duplicate_pairs_search_distance' )
search_distance = new_options.GetInteger( 'similar_files_duplicate_pairs_search_distance' )
( count, ) = self._c.execute( 'SELECT COUNT( * ) FROM shape_search_cache WHERE searched_distance IS NULL or searched_distance < ?;', ( search_distance, ) ).fetchone()
@ -3731,9 +3731,7 @@ class DB( HydrusDB.HydrusDB ):
show_inbox_and_archive = True
new_options = self._controller.GetNewOptions()
if new_options.GetBoolean( 'filter_inbox_and_archive_predicates' ) and ( num_inbox == 0 or num_archive == 0 ):
if self._controller.new_options.GetBoolean( 'filter_inbox_and_archive_predicates' ) and ( num_inbox == 0 or num_archive == 0 ):
show_inbox_and_archive = False
@ -5511,7 +5509,7 @@ class DB( HydrusDB.HydrusDB ):
newest_first.sort( key = sort_key, reverse = True )
num_we_want = HG.client_controller.GetNewOptions().GetNoneableInteger( 'num_recent_tags' )
num_we_want = HG.client_controller.new_options.GetNoneableInteger( 'num_recent_tags' )
if num_we_want == None:
@ -6662,9 +6660,7 @@ class DB( HydrusDB.HydrusDB ):
# vacuum
new_options = self._controller.GetNewOptions()
maintenance_vacuum_period_days = new_options.GetNoneableInteger( 'maintenance_vacuum_period_days' )
maintenance_vacuum_period_days = self._controller.new_options.GetNoneableInteger( 'maintenance_vacuum_period_days' )
if maintenance_vacuum_period_days is not None:
@ -6839,9 +6835,7 @@ class DB( HydrusDB.HydrusDB ):
( file_info_manager, multihash ) = row
hash = file_info_manager.GetHash()
hash_id = self._GetHashId( hash )
hash_id = self._GetHashId( file_info_manager.hash )
self._SetServiceFilename( service_id, hash_id, multihash )
@ -8315,11 +8309,9 @@ class DB( HydrusDB.HydrusDB ):
password = hashlib.sha256( password_bytes ).digest()
options = self._controller.GetOptions()
self._controller.options[ 'password' ] = password
options[ 'password' ] = password
self._SaveOptions( options )
self._SaveOptions( self._controller.options )
def _SetServiceFilename( self, service_id, hash_id, filename ):
@ -10503,7 +10495,7 @@ class DB( HydrusDB.HydrusDB ):
def _Vacuum( self, stop_time = None, force_vacuum = False ):
new_options = self._controller.GetNewOptions()
new_options = self._controller.new_options
maintenance_vacuum_period_days = new_options.GetNoneableInteger( 'maintenance_vacuum_period_days' )

View File

@ -16,15 +16,13 @@ import wx
def DAEMONCheckExportFolders( controller ):
options = controller.GetOptions()
if not options[ 'pause_export_folders_sync' ]:
if not controller.options[ 'pause_export_folders_sync' ]:
export_folders = controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_EXPORT_FOLDER )
for export_folder in export_folders:
if options[ 'pause_export_folders_sync' ]:
if controller.options[ 'pause_export_folders_sync' ]:
break
@ -35,9 +33,7 @@ def DAEMONCheckExportFolders( controller ):
def DAEMONCheckImportFolders( controller ):
options = controller.GetOptions()
if not options[ 'pause_import_folders_sync' ]:
if not controller.options[ 'pause_import_folders_sync' ]:
import_folder_names = controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FOLDER )
@ -45,7 +41,7 @@ def DAEMONCheckImportFolders( controller ):
import_folder = controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FOLDER, name )
if options[ 'pause_import_folders_sync' ]:
if controller.options[ 'pause_import_folders_sync' ]:
break
@ -268,9 +264,7 @@ def DAEMONSynchroniseAccounts( controller ):
def DAEMONSynchroniseRepositories( controller ):
options = controller.GetOptions()
if not options[ 'pause_repo_sync' ]:
if not controller.options[ 'pause_repo_sync' ]:
services = controller.services_manager.GetServices( HC.REPOSITORIES )
@ -281,7 +275,7 @@ def DAEMONSynchroniseRepositories( controller ):
return
if options[ 'pause_repo_sync' ]:
if controller.options[ 'pause_repo_sync' ]:
return
@ -300,13 +294,13 @@ def DAEMONSynchroniseRepositories( controller ):
def DAEMONSynchroniseSubscriptions( controller ):
options = controller.GetOptions()
subscription_names = list( controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_SUBSCRIPTION ) )
subscription_names = controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_SUBSCRIPTION )
random.shuffle( subscription_names )
for name in subscription_names:
p1 = options[ 'pause_subs_sync' ]
p1 = controller.options[ 'pause_subs_sync' ]
p2 = controller.ViewIsShutdown()
if p1 or p2:

View File

@ -253,10 +253,8 @@ def GetDefaultHentaiFoundryInfo():
def GetDefaultFileImportOptions():
options = HG.client_controller.GetOptions()
automatic_archive = False
exclude_deleted = options[ 'exclude_deleted_files' ]
exclude_deleted = HG.client_controller.options[ 'exclude_deleted_files' ]
min_size = None
min_resolution = None

View File

@ -118,9 +118,7 @@ def GenerateExportFilename( destination_directory, media, terms ):
def GetExportPath():
options = HG.client_controller.GetOptions()
portable_path = options[ 'export_path' ]
portable_path = HG.client_controller.options[ 'export_path' ]
if portable_path is None:
@ -229,9 +227,7 @@ class ExportFolder( HydrusSerialisable.SerialisableBaseNamed ):
if phrase is None:
new_options = HG.client_controller.GetNewOptions()
phrase = new_options.GetString( 'export_phrase' )
phrase = HG.client_controller.new_options.GetString( 'export_phrase' )
self._path = path

View File

@ -55,7 +55,7 @@ ID_TIMER_GUI_BANDWIDTH = wx.NewId()
# Sizer Flags
MENU_ORDER = [ 'file', 'undo', 'pages', 'database', 'pending', 'services', 'help' ]
MENU_ORDER = [ 'file', 'undo', 'pages', 'database', 'pending', 'network', 'services', 'help' ]
class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
@ -63,7 +63,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
self._controller = controller
title = self._controller.GetNewOptions().GetString( 'main_gui_title' )
title = self._controller.new_options.GetString( 'main_gui_title' )
if title is None or title == '':
@ -637,9 +637,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
def _CheckImportFolder( self, name = None ):
options = self._controller.GetOptions()
if options[ 'pause_import_folders_sync' ]:
if self._controller.options[ 'pause_import_folders_sync' ]:
HydrusData.ShowText( 'Import folders are currently paused under the \'services\' menu. Please unpause them and try this again.' )
@ -873,8 +871,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
menu = wx.Menu()
p = self._controller.PrepStringForDisplay
def file():
ClientGUIMenus.AppendMenuItem( self, menu, 'import files', 'Add new files to the database.', self._ImportFiles )
@ -931,7 +927,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuItem( self, menu, 'exit', 'Shut the client down.', self.Exit )
return ( menu, p( '&File' ), True )
return ( menu, '&file', True )
def undo():
@ -998,7 +994,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
show = False
return ( menu, p( '&Undo' ), show )
return ( menu, '&undo', show )
def pages():
@ -1168,7 +1164,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
#
return ( menu, p( '&Pages' ), True )
return ( menu, '&pages', True )
def database():
@ -1223,7 +1219,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenu( menu, submenu, 'regenerate' )
return ( menu, p( '&Database' ), True )
return ( menu, '&database', True )
def pending():
@ -1295,7 +1291,40 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
show = total_num_pending > 0
return ( menu, p( '&Pending (' + HydrusData.ConvertIntToPrettyString( total_num_pending ) + ')' ), show )
return ( menu, '&pending (' + HydrusData.ConvertIntToPrettyString( total_num_pending ) + ')', show )
def network():
ClientGUIMenus.AppendMenuItem( self, menu, 'review bandwidth usage', 'See where you are consuming data.', self._ReviewBandwidth )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage subscriptions', 'Change the queries you want the client to regularly import from.', self._ManageSubscriptions )
ClientGUIMenus.AppendSeparator( menu )
# and transition this to 'manage logins'
ClientGUIMenus.AppendMenuItem( self, menu, 'manage pixiv account', 'Set up your pixiv username and password.', self._ManagePixivAccount )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage boorus', 'Change the html parsing information for boorus to download from.', self._ManageBoorus )
# manage downloaders, or maybe just rename the parsing scripts stuff
ClientGUIMenus.AppendMenuItem( self, menu, 'manage parsing scripts', 'Manage how the client parses different types of web content.', self._ManageParsingScripts )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuLabel( menu, '(This section is under construction)' )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage url classes', 'Configure which URLs the client can recognise.', self._ManageURLMatches )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage url class links', 'Configure how URLs present across the client.', self._ManageURLMatchLinks )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage http headers', 'Configure how the client talks to the network.', self._ManageNetworkHeaders )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage upnp', 'If your router supports it, see and edit your current UPnP NAT traversal mappings.', self._ManageUPnP )
return ( menu, '&network', True )
def services():
@ -1409,11 +1438,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenu( menu, admin_menu, 'administrate services' )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'review bandwidth usage', 'See where you are consuming data.', self._ReviewBandwidth )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage network rules (under construction)', 'Configure how the client talks to the network.', self._ManageNetworkRules )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'import repository update files', 'Add repository update files to the database.', self._ImportUpdateFiles )
@ -1424,21 +1448,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuItem( self, menu, 'manage tag siblings', 'Set certain tags to be automatically replaced with other tags.', self._ManageTagSiblings )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage tag parents', 'Set certain tags to be automatically added with other tags.', self._ManageTagParents )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage parsing scripts', 'Manage how the client parses different types of web content.', self._ManageParsingScripts )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage boorus', 'Change the html parsing information for boorus to download from.', self._ManageBoorus )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage pixiv account', 'Set up your pixiv username and password.', self._ManagePixivAccount )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage subscriptions', 'Change the queries you want the client to regularly import from.', self._ManageSubscriptions )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage upnp', 'If your router supports it, see and edit your current UPnP NAT traversal mappings.', self._ManageUPnP )
return ( menu, p( '&Services' ), True )
return ( menu, '&services', True )
def help():
@ -1513,13 +1523,14 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuItem( self, menu, 'hardcoded shortcuts', 'Review some currently hardcoded shortcuts.', wx.MessageBox, CC.SHORTCUT_HELP )
ClientGUIMenus.AppendMenuItem( self, menu, 'about', 'See this client\'s version and other information.', self._AboutWindow )
return ( menu, p( '&Help' ), True )
return ( menu, '&help', True )
if name == 'file': return file()
elif name == 'undo': return undo()
elif name == 'pages': return pages()
elif name == 'database': return database()
elif name == 'network': return network()
elif name == 'pending': return pending()
elif name == 'services': return services()
elif name == 'help': return help()
@ -1782,28 +1793,24 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
def _ManageNetworkRules( self ):
def _ManageNetworkHeaders( self ):
title = 'manage network rules'
title = 'manage network headers'
with ClientGUITopLevelWindows.DialogEdit( self, title ) as dlg:
# eventually make this a proper management panel with several notebook pages or something
domain_manager = self._controller.network_engine.domain_manager
url_matches = domain_manager.GetURLMatches()
network_contexts_to_custom_header_dicts = domain_manager.GetNetworkContextsToCustomHeaderDicts()
panel = ClientGUIScrolledPanelsEdit.EditDomainManagerInfoPanel( dlg, url_matches, network_contexts_to_custom_header_dicts )
panel = ClientGUIScrolledPanelsEdit.EditNetworkContextCustomHeadersPanel( dlg, network_contexts_to_custom_header_dicts )
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
( url_matches, network_contexts_to_custom_header_dicts ) = panel.GetValue()
network_contexts_to_custom_header_dicts = panel.GetValue()
domain_manager.SetURLMatches( url_matches )
domain_manager.SetNetworkContextsToCustomHeaderDicts( network_contexts_to_custom_header_dicts )
@ -1941,6 +1948,54 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
with ClientGUIDialogsManage.DialogManageTagSiblings( self ) as dlg: dlg.ShowModal()
def _ManageURLMatches( self ):
title = 'manage url classes'
with ClientGUITopLevelWindows.DialogEdit( self, title ) as dlg:
domain_manager = self._controller.network_engine.domain_manager
url_matches = domain_manager.GetURLMatches()
panel = ClientGUIScrolledPanelsEdit.EditURLMatchesPanel( dlg, url_matches )
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
url_matches = panel.GetValue()
domain_manager.SetURLMatches( url_matches )
def _ManageURLMatchLinks( self ):
title = 'manage url class links'
with ClientGUITopLevelWindows.DialogEdit( self, title ) as dlg:
# eventually make this a proper management panel with several notebook pages or something
domain_manager = self._controller.network_engine.domain_manager
( url_match_names_to_display, url_match_names_to_page_parsing_keys, url_match_names_to_gallery_parsing_keys ) = domain_manager.GetURLMatchLinks()
panel = ClientGUIScrolledPanelsEdit.EditURLMatchLinksPanel( dlg, self._controller.network_engine, url_match_names_to_display, url_match_names_to_page_parsing_keys, url_match_names_to_gallery_parsing_keys )
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
( url_match_names_to_display, url_match_names_to_page_parsing_keys, url_match_names_to_gallery_parsing_keys ) = panel.GetValue()
domain_manager.SetURLMatchLinks( url_match_names_to_display, url_match_names_to_page_parsing_keys, url_match_names_to_gallery_parsing_keys )
def _ManageUPnP( self ):
with ClientGUIDialogsManage.DialogManageUPnP( self ) as dlg: dlg.ShowModal()
@ -3190,6 +3245,13 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def ImportFiles( self, paths ):
# can more easily do this when seeds are doing their own tags
# get current media page
# if it is an import page, ask user if they want to add it to the page or make a new one
# if using existing, then load the panel without file import options
# think about how to merge 'delete_after_success' or not--maybe this can be handled by seeds as well
paths = [ HydrusData.ToUnicode( path ) for path in paths ]
self._ImportFiles( paths )
@ -3306,6 +3368,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
self._dirty_menus.add( 'pages' )
self._dirty_menus.add( 'services' )
self._dirty_menus.add( 'network' )
self._menu_updater.Update()
@ -3314,6 +3377,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
self._dirty_menus.add( 'pages' )
self._dirty_menus.add( 'services' )
self._dirty_menus.add( 'network' )
self._menu_updater.Update()

View File

@ -276,9 +276,7 @@ class AutoCompleteDropdown( wx.Panel ):
def _UpdateBackgroundColour( self ):
new_options = HG.client_controller.GetNewOptions()
colour = new_options.GetColour( CC.COLOUR_AUTOCOMPLETE_BACKGROUND )
colour = HG.client_controller.new_options.GetColour( CC.COLOUR_AUTOCOMPLETE_BACKGROUND )
if not self._intercept_key_events:

View File

@ -91,7 +91,7 @@ def CalculateCanvasZooms( canvas, media, show_action ):
return ( 1.0, 1.0 )
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
( canvas_width, canvas_height ) = CalculateCanvasMediaSize( media, canvas.GetClientSize() )
@ -378,7 +378,7 @@ class Animation( wx.Window ):
def _DrawWhite( self, dc ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
dc.SetBackground( wx.Brush( new_options.GetColour( CC.COLOUR_MEDIA_BACKGROUND ) ) )
@ -439,7 +439,7 @@ class Animation( wx.Window ):
path = client_files_manager.GetFilePath( hash, mime )
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
launch_path = new_options.GetMimeLaunch( mime )
@ -1183,7 +1183,7 @@ class Canvas( wx.Window ):
self._reserved_shortcut_names.append( 'media' )
self._reserved_shortcut_names.append( 'media_viewer' )
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
self._custom_shortcut_names = self._new_options.GetStringList( 'default_media_viewer_custom_shortcuts' )
@ -1712,6 +1712,10 @@ class Canvas( wx.Window ):
self._ManageTags()
elif action == 'manage_file_urls':
self._ManageURLs()
elif action == 'archive_file':
self._Archive()
@ -2824,21 +2828,13 @@ class CanvasWithDetails( Canvas ):
urls = self._current_media.GetLocationsManager().GetURLs()
urls = list( urls )
url_tuples = HG.client_controller.network_engine.domain_manager.ConvertURLsToMediaViewerTuples( urls )
urls.sort()
urls = urls[ : 10 ]
for url in urls:
for ( display_string, url ) in url_tuples:
parse = urlparse.urlparse( url )
( text_width, text_height ) = dc.GetTextExtent( display_string )
url_string = parse.hostname
( text_width, text_height ) = dc.GetTextExtent( url_string )
dc.DrawText( url_string, client_width - text_width - 3, current_y )
dc.DrawText( display_string, client_width - text_width - 3, current_y )
current_y += text_height + 4
@ -3243,7 +3239,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
duplicate_type = dlg_1.GetChoice()
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
@ -3333,7 +3329,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
else:
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
duplicate_intensity = new_options.GetNoneableInteger( 'duplicate_background_switch_intensity' )
@ -3484,7 +3480,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
if duplicate_action_options is None:
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
@ -5294,7 +5290,7 @@ class EmbedButton( wx.Window ):
center_y = y / 2
radius = min( 50, center_x, center_y ) - 5
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
dc.SetBackground( wx.Brush( new_options.GetColour( CC.COLOUR_MEDIA_BACKGROUND ) ) )
@ -5448,7 +5444,7 @@ class OpenExternallyPanel( wx.Panel ):
wx.Panel.__init__( self, parent )
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
self.SetBackgroundColour( self._new_options.GetColour( CC.COLOUR_MEDIA_BACKGROUND ) )
@ -5529,7 +5525,7 @@ class StaticImage( wx.Window ):
def _DrawBackground( self, dc ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
dc.SetBackground( wx.Brush( new_options.GetColour( CC.COLOUR_MEDIA_BACKGROUND ) ) )

View File

@ -719,14 +719,14 @@ class CheckboxManagerOptions( CheckboxManager ):
def GetCurrentValue( self ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
return new_options.GetBoolean( self._boolean_name )
def Invert( self ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
new_options.InvertBoolean( self._boolean_name )
@ -2923,345 +2923,3 @@ class ThreadToGUIUpdater( object ):
( TimeDeltaEvent, EVT_TIME_DELTA ) = wx.lib.newevent.NewCommandEvent()
class TimeDeltaButton( wx.Button ):
def __init__( self, parent, min = 1, days = False, hours = False, minutes = False, seconds = False, monthly_allowed = False ):
wx.Button.__init__( self, parent )
self._min = min
self._show_days = days
self._show_hours = hours
self._show_minutes = minutes
self._show_seconds = seconds
self._monthly_allowed = monthly_allowed
self._value = self._min
self.SetLabelText( 'initialising' )
self.Bind( wx.EVT_BUTTON, self.EventButton )
def _RefreshLabel( self ):
text_components = []
value = self._value
if value is None:
text = 'monthly'
else:
text = HydrusData.ConvertTimeDeltaToPrettyString( value )
self.SetLabelText( text )
def EventButton( self, event ):
import ClientGUIDialogs
with ClientGUIDialogs.DialogInputTimeDelta( self, self._value, min = self._min, days = self._show_days, hours = self._show_hours, minutes = self._show_minutes, seconds = self._show_seconds, monthly_allowed = self._monthly_allowed ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
value = dlg.GetValue()
self.SetValue( value )
new_event = TimeDeltaEvent( 0 )
wx.PostEvent( self, new_event )
def GetValue( self ):
return self._value
def SetValue( self, value ):
self._value = value
self._RefreshLabel()
self.GetParent().Layout()
class TimeDeltaCtrl( wx.Panel ):
def __init__( self, parent, min = 1, days = False, hours = False, minutes = False, seconds = False, monthly_allowed = False, monthly_label = 'monthly' ):
wx.Panel.__init__( self, parent )
self._min = min
self._show_days = days
self._show_hours = hours
self._show_minutes = minutes
self._show_seconds = seconds
self._monthly_allowed = monthly_allowed
hbox = wx.BoxSizer( wx.HORIZONTAL )
if self._show_days:
self._days = wx.SpinCtrl( self, min = 0, max = 3653, size = ( 50, -1 ) )
self._days.Bind( wx.EVT_SPINCTRL, self.EventChange )
hbox.AddF( self._days, CC.FLAGS_VCENTER )
hbox.AddF( BetterStaticText( self, 'days' ), CC.FLAGS_VCENTER )
if self._show_hours:
self._hours = wx.SpinCtrl( self, min = 0, max = 23, size = ( 45, -1 ) )
self._hours.Bind( wx.EVT_SPINCTRL, self.EventChange )
hbox.AddF( self._hours, CC.FLAGS_VCENTER )
hbox.AddF( BetterStaticText( self, 'hours' ), CC.FLAGS_VCENTER )
if self._show_minutes:
self._minutes = wx.SpinCtrl( self, min = 0, max = 59, size = ( 45, -1 ) )
self._minutes.Bind( wx.EVT_SPINCTRL, self.EventChange )
hbox.AddF( self._minutes, CC.FLAGS_VCENTER )
hbox.AddF( BetterStaticText( self, 'minutes' ), CC.FLAGS_VCENTER )
if self._show_seconds:
self._seconds = wx.SpinCtrl( self, min = 0, max = 59, size = ( 45, -1 ) )
self._seconds.Bind( wx.EVT_SPINCTRL, self.EventChange )
hbox.AddF( self._seconds, CC.FLAGS_VCENTER )
hbox.AddF( BetterStaticText( self, 'seconds' ), CC.FLAGS_VCENTER )
if self._monthly_allowed:
self._monthly = wx.CheckBox( self )
self._monthly.Bind( wx.EVT_CHECKBOX, self.EventChange )
hbox.AddF( self._monthly, CC.FLAGS_VCENTER )
hbox.AddF( BetterStaticText( self, monthly_label ), CC.FLAGS_VCENTER )
self.SetSizer( hbox )
def _UpdateEnables( self ):
value = self.GetValue()
if value is None:
if self._show_days:
self._days.Disable()
if self._show_hours:
self._hours.Disable()
if self._show_minutes:
self._minutes.Disable()
if self._show_seconds:
self._seconds.Disable()
else:
if self._show_days:
self._days.Enable()
if self._show_hours:
self._hours.Enable()
if self._show_minutes:
self._minutes.Enable()
if self._show_seconds:
self._seconds.Enable()
def EventChange( self, event ):
value = self.GetValue()
if value is not None and value < self._min:
self.SetValue( self._min )
self._UpdateEnables()
new_event = TimeDeltaEvent( 0 )
wx.PostEvent( self, new_event )
def GetValue( self ):
if self._monthly_allowed and self._monthly.GetValue():
return None
value = 0
if self._show_days:
value += self._days.GetValue() * 86400
if self._show_hours:
value += self._hours.GetValue() * 3600
if self._show_minutes:
value += self._minutes.GetValue() * 60
if self._show_seconds:
value += self._seconds.GetValue()
return value
def SetValue( self, value ):
if self._monthly_allowed:
if value is None:
self._monthly.SetValue( True )
else:
self._monthly.SetValue( False )
if value is not None:
if value < self._min:
value = self._min
if self._show_days:
self._days.SetValue( value / 86400 )
value %= 86400
if self._show_hours:
self._hours.SetValue( value / 3600 )
value %= 3600
if self._show_minutes:
self._minutes.SetValue( value / 60 )
value %= 60
if self._show_seconds:
self._seconds.SetValue( value )
self._UpdateEnables()
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 ):
wx.Panel.__init__( self, parent )
self._num = wx.SpinCtrl( self, min = 0, max = 1000, size = ( 60, -1 ) )
self._times = TimeDeltaCtrl( self, min = min_time_delta, days = days, hours = hours, minutes = minutes, seconds = seconds )
#
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._num, CC.FLAGS_VCENTER )
mid_text = per_phrase
if unit is not None:
mid_text = unit + ' ' + mid_text
hbox.AddF( BetterStaticText( self, mid_text ), CC.FLAGS_VCENTER )
hbox.AddF( self._times, CC.FLAGS_VCENTER )
self.SetSizer( hbox )
def GetValue( self ):
num = self._num.GetValue()
time_delta = self._times.GetValue()
return ( num, time_delta )
def SetToolTipString( self, text ):
wx.Panel.SetToolTipString( self, text )
for c in self.GetChildren():
c.SetToolTipString( text )
def SetValue( self, velocity ):
( num, time_delta ) = velocity
self._num.SetValue( num )
self._times.SetValue( time_delta )

View File

@ -6,6 +6,7 @@ import ClientGUIDialogs
import ClientGUIListCtrl
import ClientGUIMenus
import ClientGUIScrolledPanels
import ClientGUITime
import ClientGUITopLevelWindows
import HydrusConstants as HC
import HydrusData
@ -153,7 +154,7 @@ class BandwidthRulesCtrl( ClientGUICommon.StaticBox ):
self._bandwidth_type.Bind( wx.EVT_CHOICE, self.EventBandwidth )
self._time_delta = ClientGUICommon.TimeDeltaButton( self, min = 1, days = True, hours = True, minutes = True, seconds = True, monthly_allowed = True )
self._time_delta = ClientGUITime.TimeDeltaButton( self, min = 1, days = True, hours = True, minutes = True, seconds = True, monthly_allowed = True )
self._max_allowed = wx.SpinCtrl( self, min = 1, max = 1024 * 1024 * 1024 )
@ -230,22 +231,21 @@ class EditStringToStringDictControl( wx.Panel ):
wx.Panel.__init__( self, parent )
listctrl_panel = ClientGUIListCtrl.SaneListCtrlPanel( self )
listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
self._listctrl = ClientGUIListCtrl.SaneListCtrl( listctrl_panel, 120, [ ( 'key', 200 ), ( 'value', -1 ) ], delete_key_callback = self.Delete, activation_callback = self.Edit )
self._listctrl = ClientGUIListCtrl.BetterListCtrl( listctrl_panel, 'key_to_value', 10, 36, [ ( 'key', 20 ), ( 'value', -1 ) ], self._ConvertDataToListCtrlTuples, delete_key_callback = self.Delete, activation_callback = self.Edit )
listctrl_panel.SetListCtrl( self._listctrl )
listctrl_panel.AddButton( 'add', self.Add )
listctrl_panel.AddButton( 'edit', self.Edit, enabled_only_on_selection = True )
listctrl_panel.AddButton( 'delete', self.Delete, enabled_only_on_selection = True )
listctrl_panel.AddButton( 'add', self._Add )
listctrl_panel.AddButton( 'edit', self._Edit, enabled_only_on_selection = True )
listctrl_panel.AddButton( 'delete', self._Delete, enabled_only_on_selection = True )
#
for display_tuple in initial_dict.items():
self._listctrl.Append( display_tuple, display_tuple )
self._listctrl.AddDatas( initial_dict.items() )
self._listctrl.Sort()
#
@ -256,7 +256,17 @@ class EditStringToStringDictControl( wx.Panel ):
self.SetSizer( vbox )
def Add( self ):
def _ConvertDataToListCtrlTuples( self, data ):
( key, value ) = data
display_tuple = ( key, value )
sort_tuple = ( key, value )
return ( display_tuple, sort_tuple )
def _Add( self ):
with ClientGUIDialogs.DialogTextEntry( self, 'enter the key', allow_blank = False ) as dlg:
@ -270,33 +280,31 @@ class EditStringToStringDictControl( wx.Panel ):
value = dlg.GetValue()
display_tuple = ( key, value )
data = ( key, value )
self._listctrl.Append( display_tuple, display_tuple )
self._listctrl.AddDatas( ( data, ) )
def Delete( self ):
def _Delete( self ):
with ClientGUIDialogs.DialogYesNo( self, 'Remove all selected?' ) as dlg:
if dlg.ShowModal() == wx.ID_YES:
self._listctrl.RemoveAllSelected()
self._listctrl.DeleteSelected()
def Edit( self ):
def _Edit( self ):
for i in self._listctrl.GetAllSelected():
for data in self._listctrl.GetData( only_selected = True ):
( key, value ) = self._listctrl.GetClientData( i )
import ClientGUIDialogs
( key, value ) = data
with ClientGUIDialogs.DialogTextEntry( self, 'edit the key', default = key, allow_blank = False ) as dlg:
@ -306,7 +314,7 @@ class EditStringToStringDictControl( wx.Panel ):
else:
return
break
@ -318,19 +326,23 @@ class EditStringToStringDictControl( wx.Panel ):
else:
return
break
display_tuple = ( key, value )
self._listctrl.DeleteDatas( ( data, ) )
self._listctrl.UpdateRow( i, display_tuple, display_tuple )
new_data = ( key, value )
self._listctrl.AddDatas( ( data, ) )
self._listctrl.Sort()
def GetValue( self ):
value_dict = { key : value for ( key, value ) in self._listctrl.GetClientData() }
value_dict = dict( self._listctrl.GetData() )
return value_dict

View File

@ -14,6 +14,7 @@ import ClientGUIImport
import ClientGUIListBoxes
import ClientGUIListCtrl
import ClientGUIPredicates
import ClientGUITime
import ClientGUITopLevelWindows
import ClientImporting
import ClientThreading
@ -129,7 +130,7 @@ class Dialog( wx.Dialog ):
wx.Dialog.__init__( self, parent, title = title, style = style, pos = pos )
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
@ -668,6 +669,16 @@ class DialogInputLocalBooruShare( Dialog ):
self._hashes = hashes
self._service = HG.client_controller.services_manager.GetService( CC.LOCAL_BOORU_SERVICE_KEY )
internal_port = self._service.GetPort()
if internal_port is None:
self._copy_internal_share_link.Disable()
self._copy_external_share_link.Disable()
#
rows = []
@ -1459,49 +1470,6 @@ class DialogInputTags( Dialog ):
self.EndModal( wx.ID_OK )
class DialogInputTimeDelta( Dialog ):
def __init__( self, parent, initial_value, min = 1, days = False, hours = False, minutes = False, seconds = False, monthly_allowed = False ):
Dialog.__init__( self, parent, 'input time delta' )
self._time_delta = ClientGUICommon.TimeDeltaCtrl( self, min = min, days = days, hours = hours, minutes = minutes, seconds = seconds, monthly_allowed = monthly_allowed )
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._time_delta.SetValue( initial_value )
#
b_box = wx.BoxSizer( wx.HORIZONTAL )
b_box.AddF( self._ok, CC.FLAGS_VCENTER )
b_box.AddF( self._cancel, CC.FLAGS_VCENTER )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._time_delta, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( b_box, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x, y ) )
wx.CallAfter( self._ok.SetFocus )
def GetValue( self ):
return self._time_delta.GetValue()
class DialogInputUPnPMapping( Dialog ):
def __init__( self, parent, external_port, protocol_type, internal_port, description, duration ):
@ -2231,7 +2199,7 @@ class DialogSetupExport( Dialog ):
Dialog.__init__( self, parent, 'setup export' )
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
self._tags_box = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'files\' tags' )
@ -2414,7 +2382,7 @@ class DialogSetupExport( Dialog ):
pattern = self._pattern.GetValue()
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
new_options.SetKeyList( 'default_neighbouring_txt_tag_service_keys', self._neighbouring_txt_tag_service_keys )
@ -2553,7 +2521,7 @@ class DialogSetupExport( Dialog ):
pattern = self._pattern.GetValue()
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
new_options.SetString( 'export_phrase', pattern )

View File

@ -16,6 +16,7 @@ import ClientGUIOptionsPanels
import ClientGUIPredicates
import ClientGUIScrolledPanelsEdit
import ClientGUISeedCache
import ClientGUITime
import ClientGUITopLevelWindows
import ClientImporting
import ClientMedia
@ -1173,7 +1174,7 @@ class DialogManageExportFolders( ClientGUIDialogs.Dialog ):
def _AddFolder( self ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
phrase = new_options.GetString( 'export_phrase' )
@ -1350,7 +1351,7 @@ class DialogManageExportFoldersEdit( ClientGUIDialogs.Dialog ):
self._period_box = ClientGUICommon.StaticBox( self, 'export period' )
self._period = ClientGUICommon.TimeDeltaButton( self._period_box, min = 3 * 60, days = True, hours = True, minutes = True )
self._period = ClientGUITime.TimeDeltaButton( self._period_box, min = 3 * 60, days = True, hours = True, minutes = True )
#
@ -2413,7 +2414,7 @@ class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
self._open_popup = wx.CheckBox( self._folder_box )
self._period = ClientGUICommon.TimeDeltaButton( self._folder_box, min = 3 * 60, days = True, hours = True, minutes = True )
self._period = ClientGUITime.TimeDeltaButton( self._folder_box, min = 3 * 60, days = True, hours = True, minutes = True )
self._paused = wx.CheckBox( self._folder_box )
@ -4836,6 +4837,8 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
self._hidden_cancel = wx.Button( self, id = wx.ID_CANCEL, size = ( 0, 0 ) )
self._status_st = ClientGUICommon.BetterStaticText( self )
self._mappings_list_ctrl = ClientGUIListCtrl.SaneListCtrl( self, 480, [ ( 'description', -1 ), ( 'internal ip', 100 ), ( 'internal port', 80 ), ( 'external ip', 100 ), ( 'external port', 80 ), ( 'protocol', 80 ), ( 'lease', 80 ) ], delete_key_callback = self.RemoveMappings, activation_callback = self.EditMappings )
self._add_custom = wx.Button( self, label = 'add custom mapping' )
@ -4853,10 +4856,6 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
#
self._RefreshMappings()
#
edit_buttons = wx.BoxSizer( wx.HORIZONTAL )
edit_buttons.AddF( self._add_custom, CC.FLAGS_VCENTER )
@ -4865,6 +4864,7 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._status_st, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._mappings_list_ctrl, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( edit_buttons, CC.FLAGS_BUTTON_SIZER )
vbox.AddF( self._ok, CC.FLAGS_LONE_BUTTON )
@ -4877,21 +4877,53 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
self.SetInitialSize( ( x, y ) )
wx.CallAfter( self._ok.SetFocus )
#
self._RefreshMappings()
def _RefreshMappings( self ):
def THREADdo_it():
def wx_code( mappings ):
if not self:
return
self._mappings = mappings
for mapping in self._mappings:
self._mappings_list_ctrl.Append( mapping, mapping )
self._status_st.SetLabelText( '' )
try:
mappings = HydrusNATPunch.GetUPnPMappings()
except Exception as e:
HydrusData.ShowException( e )
wx.CallAfter( wx.MessageBox, 'Could not load mappings:' + os.linesep * 2 + str( e ) )
return
wx.CallAfter( wx_code, mappings )
self._status_st.SetLabelText( 'Refreshing mappings--please wait...' )
self._mappings_list_ctrl.DeleteAllItems()
self._mappings = HydrusNATPunch.GetUPnPMappings()
for mapping in self._mappings:
self._mappings_list_ctrl.Append( mapping, mapping )
#self._mappings_list_ctrl.SortListItems( 1 )
HG.client_controller.CallToThread( THREADdo_it )
def EditMappings( self ):
@ -4919,7 +4951,10 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
if do_refresh: self._RefreshMappings()
if do_refresh:
self._RefreshMappings()
def EventAddCustomMapping( self, event ):
@ -4956,7 +4991,10 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
if do_refresh: self._RefreshMappings()
if do_refresh:
self._RefreshMappings()
def EventEditMapping( self, event ):
@ -4987,5 +5025,8 @@ class DialogManageUPnP( ClientGUIDialogs.Dialog ):
do_refresh = True
if do_refresh: self._RefreshMappings()
if do_refresh:
self._RefreshMappings()

View File

@ -84,7 +84,7 @@ class FullscreenHoverFrame( wx.Frame ):
try:
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
if new_options.GetBoolean( 'always_show_hover_windows' ):
@ -425,7 +425,7 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
def _SetDefaultShortcuts( self ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
default_media_viewer_custom_shortcuts = new_options.GetStringList( 'default_media_viewer_custom_shortcuts' )
@ -617,7 +617,7 @@ class FullscreenHoverFrameTopDuplicatesFilter( FullscreenHoverFrameTopNavigable
def _EditBackgroundSwitchIntensity( self ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
value = new_options.GetNoneableInteger( 'duplicate_background_switch_intensity' )
@ -638,7 +638,7 @@ class FullscreenHoverFrameTopDuplicatesFilter( FullscreenHoverFrameTopNavigable
def _EditMergeOptions( self, duplicate_type ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
@ -867,25 +867,17 @@ class FullscreenHoverFrameTopRight( FullscreenHoverFrame ):
urls = self._current_media.GetLocationsManager().GetURLs()
urls = list( urls )
urls.sort()
urls = urls[ : 10 ]
if urls != self._last_seen_urls:
self._last_seen_urls = list( urls )
self._urls_vbox.Clear( deleteWindows = True )
for url in urls:
url_tuples = HG.client_controller.network_engine.domain_manager.ConvertURLsToMediaViewerTuples( urls )
for ( display_string, url ) in url_tuples:
parse = urlparse.urlparse( url )
url_string = parse.hostname
link = wx.HyperlinkCtrl( self, id = -1, label = url_string, url = url )
link = wx.HyperlinkCtrl( self, id = -1, label = display_string, url = url )
link.SetToolTipString( url )

View File

@ -606,7 +606,7 @@ class FilenameTaggingOptionsPanel( wx.Panel ):
try:
tags = HydrusData.DeserialisePrettyTags( text )
tags = HydrusData.DeserialiseNewlinedTexts( text )
tags = HydrusTags.CleanTags( tags )

View File

@ -1019,7 +1019,7 @@ class ListBoxTags( ListBox ):
def _UpdateBackgroundColour( self ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
self._background_colour = new_options.GetColour( CC.COLOUR_TAGS_BOX )

View File

@ -9,7 +9,6 @@ import HydrusSerialisable
import os
import wx
from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin
from wx.lib.mixins.listctrl import ColumnSorterMixin
def SetNonDupeName( obj, disallowed_names ):
@ -26,8 +25,10 @@ def SetNonDupeName( obj, disallowed_names ):
obj.SetName( new_name )
class SaneListCtrl( wx.ListCtrl, ListCtrlAutoWidthMixin, ColumnSorterMixin ):
# This used to be ColumnSorterMixin, but it was crashing on sort-click on clients with many pages open
# I've disabled it for now because it was still catching people. The transition to BetterListCtrl will nuke the whole thing eventually.
class SaneListCtrl( wx.ListCtrl, ListCtrlAutoWidthMixin ):
def __init__( self, parent, height, columns, delete_key_callback = None, activation_callback = None ):
@ -35,7 +36,6 @@ class SaneListCtrl( wx.ListCtrl, ListCtrlAutoWidthMixin, ColumnSorterMixin ):
wx.ListCtrl.__init__( self, parent, style = wx.LC_REPORT )
ListCtrlAutoWidthMixin.__init__( self )
ColumnSorterMixin.__init__( self, num_columns )
self.itemDataMap = {}
self._data_indices_to_sort_indices = {}

View File

@ -1,5 +1,4 @@
import HydrusConstants as HC
import HydrusAudioHandling
import ClientDownloading
import HydrusExceptions
import HydrusPaths
@ -21,6 +20,7 @@ import ClientGUIMedia
import ClientGUIMenus
import ClientGUIScrolledPanelsEdit
import ClientGUISeedCache
import ClientGUITime
import ClientGUITopLevelWindows
import ClientImporting
import ClientMedia
@ -67,7 +67,7 @@ def CreateManagementController( page_name, management_type, file_service_key = N
file_service_key = CC.COMBINED_LOCAL_FILE_SERVICE_KEY
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
management_controller = ManagementController( page_name )
@ -867,7 +867,7 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
#
new_options = self._controller.GetNewOptions()
new_options = self._controller.new_options
self._search_distance_spinctrl.SetValue( new_options.GetInteger( 'similar_files_duplicate_pairs_search_distance' ) )
@ -1148,7 +1148,7 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
search_distance = self._search_distance_spinctrl.GetValue()
new_options = self._controller.GetNewOptions()
new_options = self._controller.new_options
new_options.SetInteger( 'similar_files_duplicate_pairs_search_distance', search_distance )
@ -2267,7 +2267,7 @@ class ManagementPanelImporterThreadWatcher( ManagementPanelImporter ):
with ClientGUITopLevelWindows.DialogEdit( self._checker_options_button, 'edit check timings' ) as dlg:
panel = ClientGUIScrolledPanelsEdit.EditCheckerOptions( dlg, checker_options )
panel = ClientGUITime.EditCheckerOptions( dlg, checker_options )
dlg.SetPanel( panel )

View File

@ -464,7 +464,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
def _EditDuplicateActionOptions( self, duplicate_type ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
@ -490,7 +490,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
display_media = self._focussed_media.GetDisplayMedia()
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
media_show_action = new_options.GetMediaShowAction( display_media.GetMime() )
@ -503,7 +503,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
path = client_files_manager.GetFilePath( hash, mime )
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
launch_path = new_options.GetMimeLaunch( mime )
@ -922,7 +922,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
path = client_files_manager.GetFilePath( hash, mime )
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
launch_path = new_options.GetMimeLaunch( mime )
@ -1034,6 +1034,10 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
self._ManageTags()
elif action == 'manage_file_urls':
self._ManageURLs()
elif action == 'archive_file':
self._Archive()
@ -1050,6 +1054,22 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
self._Remove()
elif action == 'get_similar_to_exact':
self._GetSimilarTo( HC.HAMMING_EXACT_MATCH )
elif action == 'get_similar_to_very_similar':
self._GetSimilarTo( HC.HAMMING_VERY_SIMILAR )
elif action == 'get_similar_to_similar':
self._GetSimilarTo( HC.HAMMING_SIMILAR )
elif action == 'get_similar_to_speculative':
self._GetSimilarTo( HC.HAMMING_SPECULATIVE )
elif action == 'open_file_in_external_program':
self._OpenExternally()
@ -1282,7 +1302,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
yes_no_text = 'set all pair relationships to ' + HC.duplicate_type_string_lookup[ duplicate_type ] + ' (with default duplicate action/merge options)'
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
@ -1358,7 +1378,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
duplicate_type = dlg_1.GetChoice()
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
duplicate_action_options = new_options.GetDuplicateActionOptions( duplicate_type )
@ -1517,7 +1537,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
def _UpdateBackgroundColour( self ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
self.SetBackgroundColour( new_options.GetColour( CC.COLOUR_THUMBGRID_BACKGROUND ) )
@ -1865,7 +1885,7 @@ class MediaPanelThumbnails( MediaPanel ):
dc = wx.MemoryDC( bmp )
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
dc.SetBackground( wx.Brush( new_options.GetColour( CC.COLOUR_THUMBGRID_BACKGROUND ) ) )
@ -2326,7 +2346,7 @@ class MediaPanelThumbnails( MediaPanel ):
( thumbnail_span_width, thumbnail_span_height ) = self._GetThumbnailSpanDimensions()
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
percent_visible = new_options.GetInteger( 'thumbnail_visibility_scroll_percent' ) / float( 100 )
@ -2460,7 +2480,7 @@ class MediaPanelThumbnails( MediaPanel ):
do_temp_dnd = False
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
if new_options.GetBoolean( 'discord_dnd_fix' ):
@ -2718,7 +2738,7 @@ class MediaPanelThumbnails( MediaPanel ):
def EventShowMenu( self, event ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
advanced_mode = new_options.GetBoolean( 'advanced_mode' )
@ -3871,7 +3891,7 @@ class Thumbnail( Selectable ):
dc = wx.MemoryDC( bmp )
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
if not local:
@ -3947,7 +3967,7 @@ class Thumbnail( Selectable ):
chapters = namespaces[ 'chapter' ]
pages = namespaces[ 'page' ]
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
if new_options.GetBoolean( 'show_thumbnail_page' ):

View File

@ -152,7 +152,7 @@ class DialogPageChooser( ClientGUIDialogs.Dialog ):
search_enabled = True
new_options = self._controller.GetNewOptions()
new_options = self._controller.new_options
tag_service_key = new_options.GetKey( 'default_tag_service_search_page' )
@ -901,7 +901,7 @@ class PagesNotebook( wx.Notebook ):
def _GetDefaultPageInsertionIndex( self ):
new_options = self._controller.GetNewOptions()
new_options = self._controller.new_options
new_page_goes = new_options.GetInteger( 'default_new_page_goes' )
@ -1105,7 +1105,7 @@ class PagesNotebook( wx.Notebook ):
return
new_options = self._controller.GetNewOptions()
new_options = self._controller.new_options
max_page_name_chars = new_options.GetInteger( 'max_page_name_chars' )
@ -1756,26 +1756,38 @@ class PagesNotebook( wx.Notebook ):
return current_page.NewPage( management_controller, initial_hashes = initial_hashes, forced_insertion_index = forced_insertion_index, on_deepest_notebook = on_deepest_notebook )
WARNING_TOTAL_PAGES = 165
MAX_TOTAL_PAGES = 200
( total_active_page_count, total_closed_page_count ) = self._controller.gui.GetTotalPageCounts()
if total_active_page_count + total_closed_page_count >= WARNING_TOTAL_PAGES:
self._controller.gui.DeleteAllClosedPages()
if not HG.no_page_limit_mode:
WARNING_TOTAL_PAGES = 200
MAX_TOTAL_PAGES = 165
( total_active_page_count, total_closed_page_count ) = self._controller.gui.GetTotalPageCounts()
if total_active_page_count + total_closed_page_count >= MAX_TOTAL_PAGES:
self._controller.gui.DeleteAllClosedPages()
if total_active_page_count >= MAX_TOTAL_PAGES:
HydrusData.ShowText( 'The client cannot have more than ' + str( MAX_TOTAL_PAGES ) + ' pages open! For system stability reasons, please close some now!' )
message = 'The client should not have more than ' + str( MAX_TOTAL_PAGES ) + ' pages open, as it leads to program instability! Are you sure you want to open more pages?'
return
with ClientGUIDialogs.DialogYesNo( self, message, title = 'Too many pages!', yes_label = 'yes, and do not tell me again', no_label = 'no' ) as dlg:
if dlg.ShowModal() == wx.ID_YES:
HG.no_page_limit_mode = True
self._controller.pub( 'notify_new_options' )
else:
return
if total_active_page_count == WARNING_TOTAL_PAGES - 5:
if total_active_page_count == WARNING_TOTAL_PAGES:
HydrusData.ShowText( 'You have ' + str( total_active_page_count ) + ' pages open! You can only open a few more before system stability is affected! Please close some now!' )
@ -1899,7 +1911,7 @@ class PagesNotebook( wx.Notebook ):
search_enabled = len( initial_hashes ) == 0
new_options = self._controller.GetNewOptions()
new_options = self._controller.new_options
tag_service_key = new_options.GetKey( 'default_tag_service_search_page' )
@ -2114,7 +2126,7 @@ class PagesNotebook( wx.Notebook ):
follow_dropped_page = not shift_down
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
if new_options.GetBoolean( 'reverse_page_shift_drag_behaviour' ):

View File

@ -111,129 +111,6 @@ class ReviewServicePanel( wx.Panel ):
if service_type == HC.LOCAL_BOORU:
booru_shares = self._controller.Read( 'local_booru_shares' )
self._booru_shares.DeleteAllItems()
for ( share_key, info ) in booru_shares.items():
name = info[ 'name' ]
text = info[ 'text' ]
timeout = info[ 'timeout' ]
hashes = info[ 'hashes' ]
self._booru_shares.Append( ( name, text, HydrusData.ConvertTimestampToPrettyExpires( timeout ), len( hashes ) ), ( name, text, timeout, ( len( hashes ), hashes, share_key ) ) )
def DeleteBoorus( self ):
with ClientGUIDialogs.DialogYesNo( self, 'Remove all selected?' ) as dlg:
if dlg.ShowModal() == wx.ID_YES:
for ( name, text, timeout, ( num_hashes, hashes, share_key ) ) in self._booru_shares.GetSelectedClientData():
self._controller.Write( 'delete_local_booru_share', share_key )
self._booru_shares.RemoveAllSelected()
def EditBoorus( self ):
writes = []
for ( name, text, timeout, ( num_hashes, hashes, share_key ) ) in self._booru_shares.GetSelectedClientData():
with ClientGUIDialogs.DialogInputLocalBooruShare( self, share_key, name, text, timeout, hashes, new_share = False) as dlg:
if dlg.ShowModal() == wx.ID_OK:
( share_key, name, text, timeout, hashes ) = dlg.GetInfo()
info = {}
info[ 'name' ] = name
info[ 'text' ] = text
info[ 'timeout' ] = timeout
info[ 'hashes' ] = hashes
writes.append( ( share_key, info ) )
for ( share_key, info ) in writes:
self._controller.Write( 'local_booru_share', share_key, info )
def EventBooruDelete( self, event ):
self.DeleteBoorus()
def EventBooruEdit( self, event ):
self.EditBoorus()
def EventBooruOpenSearch( self, event ):
for ( name, text, timeout, ( num_hashes, hashes, share_key ) ) in self._booru_shares.GetSelectedClientData():
self._controller.pub( 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY, initial_hashes = hashes, page_name = 'booru share' )
def EventCopyExternalShareURL( self, event ):
shares = self._booru_shares.GetSelectedClientData()
if len( shares ) > 0:
( name, text, timeout, ( num_hashes, hashes, share_key ) ) = shares[0]
info = self._service.GetInfo()
external_ip = HydrusNATPunch.GetExternalIP() # eventually check for optional host replacement here
external_port = info[ 'upnp' ]
if external_port is None: external_port = info[ 'port' ]
url = 'http://' + external_ip + ':' + HydrusData.ToUnicode( external_port ) + '/gallery?share_key=' + share_key.encode( 'hex' )
self._controller.pub( 'clipboard', 'text', url )
def EventCopyInternalShareURL( self, event ):
shares = self._booru_shares.GetSelectedClientData()
if len( shares ) > 0:
( name, text, timeout, ( num_hashes, hashes, share_key ) ) = shares[0]
info = self._service.GetInfo()
internal_ip = '127.0.0.1'
internal_port = info[ 'port' ]
url = 'http://' + internal_ip + ':' + str( internal_port ) + '/gallery?share_key=' + share_key.encode( 'hex' )
self._controller.pub( 'clipboard', 'text', url )
def EventDeleteLocalDeleted( self, event ):
@ -331,11 +208,6 @@ class ReviewServicePanel( wx.Panel ):
return self._service.GetServiceKey()
def RefreshLocalBooruShares( self ):
self._DisplayService()
class _ServicePanel( ClientGUICommon.StaticBox ):
def __init__( self, parent, service ):
@ -748,7 +620,7 @@ class ReviewServicePanel( wx.Panel ):
#
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
if not new_options.GetBoolean( 'advanced_mode' ):
@ -986,9 +858,7 @@ class ReviewServicePanel( wx.Panel ):
service_paused = self._service.IsPaused()
options = HG.client_controller.GetOptions()
all_repo_sync_paused = options[ 'pause_repo_sync' ]
all_repo_sync_paused = HG.client_controller.options[ 'pause_repo_sync' ]
if service_paused or all_repo_sync_paused or not processing_work_to_do:
@ -1258,9 +1128,26 @@ class ReviewServicePanel( wx.Panel ):
self._service = service
self._share_key_info = {}
self._my_updater = ClientGUICommon.ThreadToGUIUpdater( self, self._Refresh )
self._name_and_type = ClientGUICommon.BetterStaticText( self )
self._service_status = ClientGUICommon.BetterStaticText( self )
booru_search_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
self._booru_shares = ClientGUIListCtrl.BetterListCtrl( booru_search_panel, 'local_booru_shares', 10, 36, [ ( 'name', -1 ), ( 'info', 36 ), ( 'expires', 12 ), ( 'files', 12 ) ], self._ConvertDataToListCtrlTuple, delete_key_callback = self._Delete, activation_callback = self._Edit )
booru_search_panel.SetListCtrl( self._booru_shares )
booru_search_panel.AddButton( 'edit', self._Edit, enabled_only_on_selection = True )
booru_search_panel.AddButton( 'delete', self._Delete, enabled_only_on_selection = True )
booru_search_panel.AddSeparator()
booru_search_panel.AddButton( 'open in new page', self._OpenSearch, enabled_only_on_selection = True )
booru_search_panel.AddButton( 'copy internal share url', self._CopyInternalShareURL, enabled_check_func = self._CanCopyURL )
booru_search_panel.AddButton( 'copy external share url', self._CopyExternalShareURL, enabled_check_func = self._CanCopyURL )
self._booru_shares.Sort()
#
@ -1268,11 +1155,173 @@ class ReviewServicePanel( wx.Panel ):
#
self.AddF( self._name_and_type, CC.FLAGS_EXPAND_PERPENDICULAR )
self.AddF( self._service_status, CC.FLAGS_EXPAND_PERPENDICULAR )
self.AddF( booru_search_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
HG.client_controller.sub( self, 'ServiceUpdated', 'service_updated' )
def _CanCopyURL( self ):
has_selected = self._booru_shares.HasSelected()
service_is_running = self._service.GetPort() is not None
return has_selected and service_is_running
def _ConvertDataToListCtrlTuple( self, share_key ):
info = self._share_key_info[ share_key ]
name = info[ 'name' ]
text = info[ 'text' ]
timeout = info[ 'timeout' ]
hashes = info[ 'hashes' ]
num_hashes = len( hashes )
pretty_name = name
pretty_text = text
pretty_timeout = HydrusData.ConvertTimestampToPrettyExpires( timeout )
pretty_hashes = HydrusData.ConvertIntToPrettyString( num_hashes )
display_tuple = ( pretty_name, pretty_text, pretty_timeout, pretty_hashes )
sort_tuple = ( name, text, timeout, num_hashes )
return ( display_tuple, sort_tuple )
def _CopyExternalShareURL( self ):
try:
external_ip = HydrusNATPunch.GetExternalIP()
except Exception as e:
wx.MessageBox( unicode( e ) )
return
internal_port = self._service.GetPort()
if internal_port is None:
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 + ':' + HydrusData.ToUnicode( external_port ) + '/gallery?share_key=' + share_key.encode( 'hex' )
urls.append( url )
text = os.linesep.join( urls )
HG.client_controller.pub( 'clipboard', 'text', text )
def _CopyInternalShareURL( self ):
internal_ip = '127.0.0.1'
internal_port = self._service.GetPort()
if internal_port is None:
wx.MessageBox( 'The local booru is not currently running!' )
urls = []
for share_key in self._booru_shares.GetData( only_selected = True ):
url = 'http://' + internal_ip + ':' + str( internal_port ) + '/gallery?share_key=' + share_key.encode( 'hex' )
urls.append( url )
text = os.linesep.join( urls )
HG.client_controller.pub( 'clipboard', 'text', text )
def _Delete( self ):
with ClientGUIDialogs.DialogYesNo( self, 'Remove all selected?' ) as dlg:
if dlg.ShowModal() == wx.ID_YES:
for share_key in self._booru_shares.GetData( only_selected = True ):
HG.client_controller.Write( 'delete_local_booru_share', share_key )
self._booru_shares.DeleteSelected()
def _Edit( self ):
for share_key in self._booru_shares.GetData( only_selected = True ):
info = self._share_key_info[ share_key ]
name = info[ 'name' ]
text = info[ 'text' ]
timeout = info[ 'timeout' ]
hashes = info[ 'hashes' ]
with ClientGUIDialogs.DialogInputLocalBooruShare( self, share_key, name, text, timeout, hashes, new_share = False) as dlg:
if dlg.ShowModal() == wx.ID_OK:
( share_key, name, text, timeout, hashes ) = dlg.GetInfo()
info = {}
info[ 'name' ] = name
info[ 'text' ] = text
info[ 'timeout' ] = timeout
info[ 'hashes' ] = hashes
HG.client_controller.Write( 'local_booru_share', share_key, info )
else:
break
self._Refresh()
def _OpenSearch( self ):
for share_key in self._booru_shares.GetData( only_selected = True ):
info = self._share_key_info[ share_key ]
name = info[ 'name' ]
hashes = info[ 'hashes' ]
HG.client_controller.pub( 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY, initial_hashes = hashes, page_name = 'booru share: ' + name )
def _Refresh( self ):
if not self:
@ -1280,7 +1329,20 @@ class ReviewServicePanel( wx.Panel ):
return
self._name_and_type.SetLabelText( 'This is a Local Booru service. This box will regain its old information and controls in a later version.' )
port = self._service.GetPort()
if port is None:
status = 'The local booru is not running.'
else:
status = 'The local booru should be running on port ' + str( port ) + '.'
self._service_status.SetLabelText( status )
HG.client_controller.CallToThread( self.THREADFetchInfo )
def ServiceUpdated( self, service ):
@ -1293,6 +1355,27 @@ class ReviewServicePanel( wx.Panel ):
def THREADFetchInfo( self ):
def wx_code( booru_shares ):
if not self:
return
self._share_key_info.update( booru_shares )
self._booru_shares.SetData( booru_shares.keys() )
self._booru_shares.Sort()
booru_shares = HG.client_controller.Read( 'local_booru_shares' )
wx.CallAfter( wx_code, booru_shares )
class _ServiceRatingPanel( ClientGUICommon.StaticBox ):
@ -1368,7 +1451,7 @@ class ReviewServicePanel( wx.Panel ):
#
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
advanced_mode = new_options.GetBoolean( 'advanced_mode' )

View File

@ -656,7 +656,7 @@ class PopupMessageManager( wx.Frame ):
going_to_bug_out_at_hide_or_show = windows_and_iconised or possibly_on_hidden_virtual_desktop
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
if new_options.GetBoolean( 'hide_message_manager_on_gui_iconise' ):

View File

@ -30,6 +30,33 @@ class EditPanel( ResizingScrolledPanel ):
raise NotImplementedError()
class EditSingleCtrlPanel( EditPanel ):
def __init__( self, parent ):
EditPanel.__init__( self, parent )
self._control = None
#
self._vbox = wx.BoxSizer( wx.VERTICAL )
self.SetSizer( self._vbox )
def GetValue( self ):
return self._control.GetValue()
def SetControl( self, control ):
self._control = control
self._vbox.AddF( control, CC.FLAGS_EXPAND_BOTH_WAYS )
class ManagePanel( ResizingScrolledPanel ):
def CanCancel( self ):

View File

@ -13,6 +13,7 @@ import ClientGUIListCtrl
import ClientGUIMenus
import ClientGUIScrolledPanels
import ClientGUISeedCache
import ClientGUITime
import ClientGUITopLevelWindows
import ClientNetworking
import ClientNetworkingDomain
@ -1784,6 +1785,7 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
queries_panel.SetListCtrl( self._queries )
queries_panel.AddButton( 'add', self._AddQuery )
queries_panel.AddButton( 'paste queries', self._PasteQueries )
queries_panel.AddButton( 'edit', self._EditQuery, enabled_only_on_selection = True )
queries_panel.AddButton( 'delete', self._DeleteQuery, enabled_only_on_selection = True )
queries_panel.AddSeparator()
@ -1967,7 +1969,7 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
( namespaces, search_value ) = ClientDefaults.GetDefaultNamespacesAndSearchValue( gallery_identifier )
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
tag_import_options = new_options.GetDefaultTagImportOptions( gallery_identifier )
@ -2050,7 +2052,7 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
with ClientGUITopLevelWindows.DialogEdit( self._checker_options_button, 'edit check timings' ) as dlg:
panel = EditCheckerOptions( dlg, self._checker_options )
panel = ClientGUITime.EditCheckerOptions( dlg, self._checker_options )
dlg.SetPanel( panel )
@ -2166,6 +2168,47 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
return False
def _PasteQueries( self ):
message = 'This will add new queries by pulling them from your clipboard. It assumes they are currently in your clipboard and newline separated. Is that ok?'
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
if dlg.ShowModal() != wx.ID_YES:
return
if wx.TheClipboard.Open():
data = wx.TextDataObject()
wx.TheClipboard.GetData( data )
wx.TheClipboard.Close()
text = data.GetText()
try:
query_texts = HydrusData.DeserialiseNewlinedTexts( text )
queries = [ ClientImporting.SubscriptionQuery( query_text ) for query_text in query_texts ]
self._queries.AddDatas( queries )
except:
wx.MessageBox( 'I could not understand what was in the clipboard' )
else:
wx.MessageBox( 'I could not get permission to access the clipboard.' )
def _PausePlay( self ):
selected_queries = self._queries.GetData( only_selected = True )
@ -2851,7 +2894,7 @@ class EditURLMatchPanel( ClientGUIScrolledPanels.EditPanel ):
self._url_type = ClientGUICommon.BetterChoice( self )
for url_type in ( HC.URL_TYPE_POST, HC.URL_TYPE_GALLERY, HC.URL_TYPE_API, HC.URL_TYPE_FILE ):
for url_type in ( HC.URL_TYPE_POST, HC.URL_TYPE_GALLERY, HC.URL_TYPE_FILE ):
self._url_type.Append( HC.url_type_string_lookup[ url_type ], url_type )
@ -3240,7 +3283,7 @@ class EditURLMatchesPanel( ClientGUIScrolledPanels.EditPanel ):
self._list_ctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
self._list_ctrl = ClientGUIListCtrl.BetterListCtrl( self._list_ctrl_panel, 'url_matches', 15, 40, [ ( 'name', 36 ), ( 'example url', -1 ) ], self._ConvertDataToListCtrlTuples, delete_key_callback = self._Delete, activation_callback = self._Edit )
self._list_ctrl = ClientGUIListCtrl.BetterListCtrl( self._list_ctrl_panel, 'url_matches', 15, 40, [ ( 'name', 36 ), ( 'type', 20 ), ( 'example url', -1 ) ], self._ConvertDataToListCtrlTuples, delete_key_callback = self._Delete, activation_callback = self._Edit )
self._list_ctrl_panel.SetListCtrl( self._list_ctrl )
@ -3303,13 +3346,15 @@ class EditURLMatchesPanel( ClientGUIScrolledPanels.EditPanel ):
def _ConvertDataToListCtrlTuples( self, url_match ):
name = url_match.GetName()
url_type = url_match.GetURLType()
example_url = url_match.GetExampleURL()
pretty_name = name
pretty_url_type = HC.url_type_string_lookup[ url_type ]
pretty_example_url = example_url
display_tuple = ( pretty_name, pretty_example_url )
sort_tuple = ( name, example_url )
display_tuple = ( pretty_name, pretty_url_type, pretty_example_url )
sort_tuple = ( name, url_type, example_url )
return ( display_tuple, sort_tuple )
@ -3369,84 +3414,193 @@ class EditURLMatchesPanel( ClientGUIScrolledPanels.EditPanel ):
return url_matches
class EditCheckerOptions( ClientGUIScrolledPanels.EditPanel ):
class EditURLMatchLinksPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, checker_options ):
def __init__( self, parent, network_engine, url_match_names_to_display, url_match_names_to_page_parsing_keys, url_match_names_to_gallery_parsing_keys ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
help_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.help, self._ShowHelp )
help_button.SetToolTipString( 'Show help regarding these checker options.' )
self._network_engine = network_engine
# add statictext or whatever that will update on any updates above to say 'given velocity of blah and last check at blah, next check in 5 mins'
# or indeed this could just take the seed cache and last check of the caller, if there is one
# this would be more useful to the user, to know 'right, on ok, it'll refresh in 30 mins'
self._display_list_ctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
self._intended_files_per_check = wx.SpinCtrl( self, min = 1, max = 1000 )
self._display_list_ctrl = ClientGUIListCtrl.BetterListCtrl( self._display_list_ctrl_panel, 'url_match_names_to_display', 15, 36, [ ( 'url class', -1 ), ( 'display on media viewer?', 36 ) ], self._ConvertDisplayDataToListCtrlTuples, activation_callback = self._EditDisplay )
self._never_faster_than = ClientGUICommon.TimeDeltaCtrl( self, min = 30, days = True, hours = True, minutes = True, seconds = True )
self._display_list_ctrl_panel.SetListCtrl( self._display_list_ctrl )
self._never_slower_than = ClientGUICommon.TimeDeltaCtrl( self, min = 600, days = True, hours = True, minutes = True )
self._display_list_ctrl_panel.AddButton( 'edit', self._EditDisplay, enabled_only_on_selection = True )
self._death_file_velocity = ClientGUICommon.VelocityCtrl( self, min_time_delta = 60, days = True, hours = True, minutes = True, per_phrase = 'in', unit = 'files' )
self._page_list_ctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
self._page_list_ctrl = ClientGUIListCtrl.BetterListCtrl( self._page_list_ctrl_panel, 'url_match_names_to_page_parsing_keys', 15, 36, [ ( 'url class', -1 ), ( 'page parser', 36 ) ], self._ConvertPageDataToListCtrlTuples, activation_callback = self._EditPage )
self._page_list_ctrl_panel.SetListCtrl( self._page_list_ctrl )
self._page_list_ctrl_panel.AddButton( 'edit', self._EditPage, enabled_only_on_selection = True )
self._gallery_list_ctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
self._gallery_list_ctrl = ClientGUIListCtrl.BetterListCtrl( self._gallery_list_ctrl_panel, 'url_match_names_to_gallery_parsing_keys', 15, 36, [ ( 'url class', -1 ), ( 'gallery parser', 36 ) ], self._ConvertGalleryDataToListCtrlTuples, activation_callback = self._EditGallery )
self._gallery_list_ctrl_panel.SetListCtrl( self._gallery_list_ctrl )
self._gallery_list_ctrl_panel.AddButton( 'edit', self._EditGallery, enabled_only_on_selection = True )
#
( intended_files_per_check, never_faster_than, never_slower_than, death_file_velocity ) = checker_options.ToTuple()
self._display_list_ctrl.AddDatas( url_match_names_to_display.items() )
self._intended_files_per_check.SetValue( intended_files_per_check )
self._never_faster_than.SetValue( never_faster_than )
self._never_slower_than.SetValue( never_slower_than )
self._death_file_velocity.SetValue( death_file_velocity )
self._display_list_ctrl.Sort( 0 )
self._page_list_ctrl.AddDatas( url_match_names_to_page_parsing_keys.items() )
self._page_list_ctrl.Sort( 0 )
self._gallery_list_ctrl.AddDatas( url_match_names_to_gallery_parsing_keys.items() )
self._gallery_list_ctrl.Sort( 0 )
#
rows = []
rows.append( ( 'intended new files per check: ', self._intended_files_per_check ) )
rows.append( ( 'stop checking if new files found falls below: ', self._death_file_velocity ) )
rows.append( ( 'never check faster than once per: ', self._never_faster_than ) )
rows.append( ( 'never check slower than once per: ', self._never_slower_than ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
vbox = wx.BoxSizer( wx.VERTICAL )
help_hbox = wx.BoxSizer( wx.HORIZONTAL )
st = ClientGUICommon.BetterStaticText( self, 'help for this panel -->' )
st.SetForegroundColour( wx.Colour( 0, 0, 255 ) )
help_hbox.AddF( st, CC.FLAGS_VCENTER )
help_hbox.AddF( help_button, CC.FLAGS_VCENTER )
vbox.AddF( help_hbox, CC.FLAGS_LONE_BUTTON )
vbox.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._display_list_ctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._page_list_ctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._gallery_list_ctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( vbox )
def _ShowHelp( self ):
def _ConvertDisplayDataToListCtrlTuples( self, data ):
help = 'PROTIP: Do not change anything here unless you understand what it means!'
help += os.linesep * 2
help += 'After its initialisation check, the checker times future checks so that it will probably find the same specified number of new files each time. When files are being posted frequently, it will check more often. When things are slow, it will slow down as well.'
help += os.linesep * 2
help += 'For instance, if it were set to try for 5 new files with every check, and at the last check it knew that the last 24 hours had produced 10 new files, it would check again 12 hours later. When that check was done and any new files found, it would then recalculate and repeat the process.'
help += os.linesep * 2
help += 'If the \'file velocity\' drops below a certain amount, the checker considers the source of files dead and will stop checking. If it falls into this state but you think there might have been a rush of new files, hit the \'check now\' button in an attempt to revive the checker. If there are new files, it will start checking again until they drop off once more.'
( url_match_name, display ) = data
wx.MessageBox( help )
pretty_name = url_match_name
if display:
pretty_display = 'yes'
else:
pretty_display = 'no'
display_tuple = ( pretty_name, pretty_display )
sort_tuple = ( url_match_name, display )
return ( display_tuple, sort_tuple )
def _ConvertGalleryDataToListCtrlTuples( self, data ):
( url_match_name, gallery_script_key ) = data
pretty_name = url_match_name
if gallery_script_key is None:
pretty_script_key = ''
else:
# fetch this from network engine
pretty_script_key = 'fetch this from network engine'
display_tuple = ( pretty_name, pretty_script_key )
sort_tuple = ( url_match_name, pretty_script_key )
return ( display_tuple, sort_tuple )
def _ConvertPageDataToListCtrlTuples( self, data ):
( url_match_name, page_script_key ) = data
pretty_name = url_match_name
if page_script_key is None:
pretty_script_key = ''
else:
pretty_script_key = 'fetch this from network engine'
display_tuple = ( pretty_name, pretty_script_key )
sort_tuple = ( url_match_name, pretty_script_key )
return ( display_tuple, sort_tuple )
def _EditDisplay( self ):
for data in self._display_list_ctrl.GetData( only_selected = True ):
( url_match_name, display ) = data
message = 'Show ' + url_match_name + ' in the media viewer?'
with ClientGUIDialogs.DialogYesNo( self, message, title = 'Show in the media viewer?' ) as dlg:
result = dlg.ShowModal()
if result in ( wx.ID_YES, wx.ID_NO ):
display = result == wx.ID_YES
self._display_list_ctrl.DeleteDatas( ( data, ) )
new_data = ( url_match_name, display )
self._display_list_ctrl.AddDatas( ( new_data, ) )
else:
break
self._display_list_ctrl.Sort()
def _EditGallery( self ):
for data in self._gallery_list_ctrl.GetData( only_selected = True ):
( url_match_name, gallery_script_key ) = data
# present the user with a dialog to choose gallery script key, or none
wx.MessageBox( 'This does not work yet!' )
break
def _EditPage( self ):
for data in self._page_list_ctrl.GetData( only_selected = True ):
( url_match_name, page_script_key ) = data
# present the user with a dialog to choose page script key, or none
wx.MessageBox( 'This does not work yet!' )
break
def GetValue( self ):
intended_files_per_check = self._intended_files_per_check.GetValue()
never_faster_than = self._never_faster_than.GetValue()
never_slower_than = self._never_slower_than.GetValue()
death_file_velocity = self._death_file_velocity.GetValue()
url_match_names_to_display = dict( self._display_list_ctrl.GetData() )
url_match_names_to_page_parsing_keys = dict( self._page_list_ctrl.GetData() )
url_match_names_to_gallery_parsing_keys = dict( self._gallery_list_ctrl.GetData() )
return ClientData.CheckerOptions( intended_files_per_check, never_faster_than, never_slower_than, death_file_velocity )
return ( url_match_names_to_display, url_match_names_to_page_parsing_keys, url_match_names_to_gallery_parsing_keys )

View File

@ -22,6 +22,7 @@ import ClientMedia
import ClientRatings
import ClientSerialisable
import ClientServices
import ClientGUITime
import collections
import HydrusConstants as HC
import HydrusData
@ -1046,36 +1047,36 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
ClientGUICommon.StaticBox.__init__( self, parent, 'local booru' )
#dictionary[ 'port' ] = None
#dictionary[ 'upnp_port' ] = None
#dictionary[ 'bandwidth_rules' ] = HydrusNetworking.BandwidthRules()
self._st = ClientGUICommon.BetterStaticText( self )
'''
if service_type == HC.LOCAL_BOORU:
self._booru_options_panel = ClientGUICommon.StaticBox( self, 'options' )
self._port = ClientGUICommon.NoneableSpinCtrl( self._booru_options_panel, 'booru local port', none_phrase = 'do not run local booru service', min = 1, max = 65535 )
self._upnp = ClientGUICommon.NoneableSpinCtrl( self._booru_options_panel, 'upnp port', none_phrase = 'do not forward port', max = 65535 )
self._max_monthly_data = ClientGUICommon.NoneableSpinCtrl( self._booru_options_panel, 'max monthly MB', multiplier = 1024 * 1024 )
'''
#
self._st.SetLabelText( 'This is a Local Booru service. This box will get regain its port options in a future update.' )
self._bandwidth_rules = ClientGUIControls.BandwidthRulesCtrl( self._booru_options_panel, dictionary[ 'bandwidth_rules' ] )
#
self.AddF( self._st, CC.FLAGS_EXPAND_PERPENDICULAR )
self._port.SetValue( dictionary[ 'port' ] )
self._upnp.SetValue( dictionary[ 'upnp_port' ] )
#
self._booru_options_panel.AddF( self._port, CC.FLAGS_EXPAND_PERPENDICULAR )
self._booru_options_panel.AddF( self._upnp, CC.FLAGS_EXPAND_PERPENDICULAR )
self._booru_options_panel.AddF( self._bandwidth_rules, CC.FLAGS_EXPAND_BOTH_WAYS )
self.AddF( self._booru_options_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
def GetValue( self ):
dictionary_part = {}
dictionary_part[ 'port' ] = self._port.GetValue()
dictionary_part[ 'upnp_port' ] = self._upnp.GetValue()
dictionary_part[ 'bandwidth_rules' ] = self._bandwidth_rules.GetValue()
return dictionary_part
@ -1259,7 +1260,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
ClientGUIScrolledPanels.ManagePanel.__init__( self, parent )
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
self._listbook = ClientGUICommon.ListBook( self )
@ -1311,7 +1312,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
#
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
( locations_to_ideal_weights, resized_thumbnail_override, full_size_thumbnail_override ) = self._new_options.GetClientFilesLocationsToIdealWeights()
@ -1574,7 +1575,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
wx.Panel.__init__( self, parent )
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
self._current_colourset = ClientGUICommon.BetterChoice( self )
@ -1773,7 +1774,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._external_host.SetValue( HC.options[ 'external_host' ] )
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
self._network_timeout.SetValue( self._new_options.GetInteger( 'network_timeout' ) )
@ -1908,7 +1909,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
checker_options = self._new_options.GetDefaultThreadCheckerOptions()
self._thread_checker_options = ClientGUIScrolledPanelsEdit.EditCheckerOptions( thread_checker, checker_options )
self._thread_checker_options = ClientGUITime.EditCheckerOptions( thread_checker, checker_options )
#
@ -1980,7 +1981,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
wx.Panel.__init__( self, parent )
self._new_options = HG.client_controller.GetNewOptions()
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' )
@ -2304,7 +2305,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
tag_import_options = new_options.GetDefaultTagImportOptions( gallery_identifier )
@ -2424,7 +2425,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._trash_max_age.SetValue( HC.options[ 'trash_max_age' ] )
self._trash_max_size.SetValue( HC.options[ 'trash_max_size' ] )
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
for mime in HC.SEARCHABLE_MIMES:
@ -2616,7 +2617,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
#
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
self._main_gui_title.SetValue( self._new_options.GetString( 'main_gui_title' ) )
@ -2826,7 +2827,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
wx.Panel.__init__( self, parent )
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
self._animation_start_position = wx.SpinCtrl( self, min = 0, max = 100 )
@ -3065,7 +3066,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
#
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
try:
@ -5885,7 +5886,7 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._tags_box_sorter.SetTagsBox( self._tags_box )
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
self._add_parents_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'auto-add entered tags\' parents' )
self._add_parents_checkbox.SetValue( self._new_options.GetBoolean( 'add_parents_on_manage_tags' ) )
@ -6238,7 +6239,7 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
if len( recent_tags ) > 0 and HG.client_controller.GetNewOptions().GetNoneableInteger( 'num_recent_tags' ) is not None:
if len( recent_tags ) > 0 and HG.client_controller.new_options.GetNoneableInteger( 'num_recent_tags' ) is not None:
HG.client_controller.Write( 'push_recent_tags', self._tag_service_key, recent_tags )
@ -6377,7 +6378,7 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
try:
tags = HydrusData.DeserialisePrettyTags( text )
tags = HydrusData.DeserialiseNewlinedTexts( text )
tags = HydrusTags.CleanTags( tags )

View File

@ -9,6 +9,7 @@ import ClientGUIScrolledPanels
import ClientGUIScrolledPanelsEdit
import ClientGUIPanels
import ClientGUIPopupMessages
import ClientGUITime
import ClientGUITopLevelWindows
import ClientNetworking
import ClientTags
@ -308,8 +309,8 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
ClientGUIScrolledPanels.ReviewPanel.__init__( self, parent )
self._history_time_delta_threshold = ClientGUICommon.TimeDeltaButton( self, days = True, hours = True, minutes = True, seconds = True )
self._history_time_delta_threshold.Bind( ClientGUICommon.EVT_TIME_DELTA, self.EventTimeDeltaChanged )
self._history_time_delta_threshold = ClientGUITime.TimeDeltaButton( self, days = True, hours = True, minutes = True, seconds = True )
self._history_time_delta_threshold.Bind( ClientGUITime.EVT_TIME_DELTA, self.EventTimeDeltaChanged )
self._history_time_delta_none = wx.CheckBox( self, label = 'show all' )
self._history_time_delta_none.Bind( wx.EVT_CHECKBOX, self.EventTimeDeltaChanged )
@ -567,7 +568,7 @@ class ReviewNetworkContextBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
self._current_usage_st = ClientGUICommon.BetterStaticText( usage_panel )
self._time_delta_usage_bandwidth_type = ClientGUICommon.BetterChoice( usage_panel )
self._time_delta_usage_time_delta = ClientGUICommon.TimeDeltaButton( usage_panel, days = True, hours = True, minutes = True, seconds = True )
self._time_delta_usage_time_delta = ClientGUITime.TimeDeltaButton( usage_panel, days = True, hours = True, minutes = True, seconds = True )
self._time_delta_usage_st = ClientGUICommon.BetterStaticText( usage_panel )
#
@ -985,7 +986,7 @@ class MigrateDatabasePanel( ClientGUIScrolledPanels.ReviewPanel ):
self._controller = controller
self._new_options = self._controller.GetNewOptions()
self._new_options = self._controller.new_options
ClientGUIScrolledPanels.ReviewPanel.__init__( self, parent )

View File

@ -22,7 +22,7 @@ class ListBoxTagsSuggestionsFavourites( ClientGUIListBoxes.ListBoxTagsStrings ):
self._activate_callable = activate_callable
width = HG.client_controller.GetNewOptions().GetInteger( 'suggested_tags_width' )
width = HG.client_controller.new_options.GetInteger( 'suggested_tags_width' )
if width is not None:
@ -53,7 +53,7 @@ class ListBoxTagsSuggestionsRelated( ClientGUIListBoxes.ListBoxTagsPredicates ):
self._activate_callable = activate_callable
width = HG.client_controller.GetNewOptions().GetInteger( 'suggested_tags_width' )
width = HG.client_controller.new_options.GetInteger( 'suggested_tags_width' )
self.SetMinSize( ( width, -1 ) )
@ -96,7 +96,7 @@ class RecentTagsPanel( wx.Panel ):
self._service_key = service_key
self._canvas_key = canvas_key
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
vbox = wx.BoxSizer( wx.VERTICAL )
@ -150,7 +150,7 @@ class RelatedTagsPanel( wx.Panel ):
self._media = media
self._canvas_key = canvas_key
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
vbox = wx.BoxSizer( wx.VERTICAL )
@ -258,7 +258,7 @@ class FileLookupScriptTagsPanel( wx.Panel ):
self._script_choice.Append( script.GetName(), script )
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
favourite_file_lookup_script = new_options.GetNoneableString( 'favourite_file_lookup_script' )
@ -376,7 +376,7 @@ class SuggestedTagsPanel( wx.Panel ):
self._media = media
self._canvas_key = canvas_key
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
layout_mode = self._new_options.GetNoneableString( 'suggested_tags_layout' )

438
include/ClientGUITime.py Normal file
View File

@ -0,0 +1,438 @@
import ClientConstants as CC
import ClientData
import ClientGUICommon
import ClientGUIScrolledPanels
import ClientGUITopLevelWindows
import HydrusData
import os
import wx
class EditCheckerOptions( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, checker_options ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
help_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.help, self._ShowHelp )
help_button.SetToolTipString( 'Show help regarding these checker options.' )
# add statictext or whatever that will update on any updates above to say 'given velocity of blah and last check at blah, next check in 5 mins'
# or indeed this could just take the seed cache and last check of the caller, if there is one
# this would be more useful to the user, to know 'right, on ok, it'll refresh in 30 mins'
self._intended_files_per_check = wx.SpinCtrl( self, min = 1, max = 1000 )
self._never_faster_than = TimeDeltaCtrl( self, min = 30, days = True, hours = True, minutes = True, seconds = True )
self._never_slower_than = TimeDeltaCtrl( self, min = 600, days = True, hours = True, minutes = True )
self._death_file_velocity = VelocityCtrl( self, min_time_delta = 60, days = True, hours = True, minutes = True, per_phrase = 'in', unit = 'files' )
#
( intended_files_per_check, never_faster_than, never_slower_than, death_file_velocity ) = checker_options.ToTuple()
self._intended_files_per_check.SetValue( intended_files_per_check )
self._never_faster_than.SetValue( never_faster_than )
self._never_slower_than.SetValue( never_slower_than )
self._death_file_velocity.SetValue( death_file_velocity )
#
rows = []
rows.append( ( 'intended new files per check: ', self._intended_files_per_check ) )
rows.append( ( 'stop checking if new files found falls below: ', self._death_file_velocity ) )
rows.append( ( 'never check faster than once per: ', self._never_faster_than ) )
rows.append( ( 'never check slower than once per: ', self._never_slower_than ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
vbox = wx.BoxSizer( wx.VERTICAL )
help_hbox = wx.BoxSizer( wx.HORIZONTAL )
st = ClientGUICommon.BetterStaticText( self, 'help for this panel -->' )
st.SetForegroundColour( wx.Colour( 0, 0, 255 ) )
help_hbox.AddF( st, CC.FLAGS_VCENTER )
help_hbox.AddF( help_button, CC.FLAGS_VCENTER )
vbox.AddF( help_hbox, CC.FLAGS_LONE_BUTTON )
vbox.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self.SetSizer( vbox )
def _ShowHelp( self ):
help = 'PROTIP: Do not change anything here unless you understand what it means!'
help += os.linesep * 2
help += 'After its initialisation check, the checker times future checks so that it will probably find the same specified number of new files each time. When files are being posted frequently, it will check more often. When things are slow, it will slow down as well.'
help += os.linesep * 2
help += 'For instance, if it were set to try for 5 new files with every check, and at the last check it knew that the last 24 hours had produced 10 new files, it would check again 12 hours later. When that check was done and any new files found, it would then recalculate and repeat the process.'
help += os.linesep * 2
help += 'If the \'file velocity\' drops below a certain amount, the checker considers the source of files dead and will stop checking. If it falls into this state but you think there might have been a rush of new files, hit the \'check now\' button in an attempt to revive the checker. If there are new files, it will start checking again until they drop off once more.'
wx.MessageBox( help )
def GetValue( self ):
intended_files_per_check = self._intended_files_per_check.GetValue()
never_faster_than = self._never_faster_than.GetValue()
never_slower_than = self._never_slower_than.GetValue()
death_file_velocity = self._death_file_velocity.GetValue()
return ClientData.CheckerOptions( intended_files_per_check, never_faster_than, never_slower_than, death_file_velocity )
( TimeDeltaEvent, EVT_TIME_DELTA ) = wx.lib.newevent.NewCommandEvent()
class TimeDeltaButton( wx.Button ):
def __init__( self, parent, min = 1, days = False, hours = False, minutes = False, seconds = False, monthly_allowed = False ):
wx.Button.__init__( self, parent )
self._min = min
self._show_days = days
self._show_hours = hours
self._show_minutes = minutes
self._show_seconds = seconds
self._monthly_allowed = monthly_allowed
self._value = self._min
self.SetLabelText( 'initialising' )
self.Bind( wx.EVT_BUTTON, self.EventButton )
def _RefreshLabel( self ):
value = self._value
if value is None:
text = 'monthly'
else:
text = HydrusData.ConvertTimeDeltaToPrettyString( value )
self.SetLabelText( text )
def EventButton( self, event ):
with ClientGUITopLevelWindows.DialogEdit( self, 'edit time delta' ) as dlg:
panel = ClientGUIScrolledPanels.EditSingleCtrlPanel( dlg )
control = TimeDeltaCtrl( panel, min = self._min, days = self._show_days, hours = self._show_hours, minutes = self._show_minutes, seconds = self._show_seconds, monthly_allowed = self._monthly_allowed )
control.SetValue( self._value )
panel.SetControl( control )
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
value = panel.GetValue()
self.SetValue( value )
new_event = TimeDeltaEvent( 0 )
wx.PostEvent( self, new_event )
def GetValue( self ):
return self._value
def SetValue( self, value ):
self._value = value
self._RefreshLabel()
self.GetParent().Layout()
class TimeDeltaCtrl( wx.Panel ):
def __init__( self, parent, min = 1, days = False, hours = False, minutes = False, seconds = False, monthly_allowed = False, monthly_label = 'monthly' ):
wx.Panel.__init__( self, parent )
self._min = min
self._show_days = days
self._show_hours = hours
self._show_minutes = minutes
self._show_seconds = seconds
self._monthly_allowed = monthly_allowed
hbox = wx.BoxSizer( wx.HORIZONTAL )
if self._show_days:
self._days = wx.SpinCtrl( self, min = 0, max = 3653, size = ( 50, -1 ) )
self._days.Bind( wx.EVT_SPINCTRL, self.EventChange )
hbox.AddF( self._days, CC.FLAGS_VCENTER )
hbox.AddF( ClientGUICommon.BetterStaticText( self, 'days' ), CC.FLAGS_VCENTER )
if self._show_hours:
self._hours = wx.SpinCtrl( self, min = 0, max = 23, size = ( 45, -1 ) )
self._hours.Bind( wx.EVT_SPINCTRL, self.EventChange )
hbox.AddF( self._hours, CC.FLAGS_VCENTER )
hbox.AddF( ClientGUICommon.BetterStaticText( self, 'hours' ), CC.FLAGS_VCENTER )
if self._show_minutes:
self._minutes = wx.SpinCtrl( self, min = 0, max = 59, size = ( 45, -1 ) )
self._minutes.Bind( wx.EVT_SPINCTRL, self.EventChange )
hbox.AddF( self._minutes, CC.FLAGS_VCENTER )
hbox.AddF( ClientGUICommon.BetterStaticText( self, 'minutes' ), CC.FLAGS_VCENTER )
if self._show_seconds:
self._seconds = wx.SpinCtrl( self, min = 0, max = 59, size = ( 45, -1 ) )
self._seconds.Bind( wx.EVT_SPINCTRL, self.EventChange )
hbox.AddF( self._seconds, CC.FLAGS_VCENTER )
hbox.AddF( ClientGUICommon.BetterStaticText( self, 'seconds' ), CC.FLAGS_VCENTER )
if self._monthly_allowed:
self._monthly = wx.CheckBox( self )
self._monthly.Bind( wx.EVT_CHECKBOX, self.EventChange )
hbox.AddF( self._monthly, CC.FLAGS_VCENTER )
hbox.AddF( ClientGUICommon.BetterStaticText( self, monthly_label ), CC.FLAGS_VCENTER )
self.SetSizer( hbox )
def _UpdateEnables( self ):
value = self.GetValue()
if value is None:
if self._show_days:
self._days.Disable()
if self._show_hours:
self._hours.Disable()
if self._show_minutes:
self._minutes.Disable()
if self._show_seconds:
self._seconds.Disable()
else:
if self._show_days:
self._days.Enable()
if self._show_hours:
self._hours.Enable()
if self._show_minutes:
self._minutes.Enable()
if self._show_seconds:
self._seconds.Enable()
def EventChange( self, event ):
value = self.GetValue()
if value is not None and value < self._min:
self.SetValue( self._min )
self._UpdateEnables()
new_event = TimeDeltaEvent( 0 )
wx.PostEvent( self, new_event )
def GetValue( self ):
if self._monthly_allowed and self._monthly.GetValue():
return None
value = 0
if self._show_days:
value += self._days.GetValue() * 86400
if self._show_hours:
value += self._hours.GetValue() * 3600
if self._show_minutes:
value += self._minutes.GetValue() * 60
if self._show_seconds:
value += self._seconds.GetValue()
return value
def SetValue( self, value ):
if self._monthly_allowed:
if value is None:
self._monthly.SetValue( True )
else:
self._monthly.SetValue( False )
if value is not None:
if value < self._min:
value = self._min
if self._show_days:
self._days.SetValue( value / 86400 )
value %= 86400
if self._show_hours:
self._hours.SetValue( value / 3600 )
value %= 3600
if self._show_minutes:
self._minutes.SetValue( value / 60 )
value %= 60
if self._show_seconds:
self._seconds.SetValue( value )
self._UpdateEnables()
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 ):
wx.Panel.__init__( self, parent )
self._num = wx.SpinCtrl( self, min = 0, max = 1000, size = ( 60, -1 ) )
self._times = TimeDeltaCtrl( self, min = min_time_delta, days = days, hours = hours, minutes = minutes, seconds = seconds )
#
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._num, CC.FLAGS_VCENTER )
mid_text = per_phrase
if unit is not None:
mid_text = unit + ' ' + mid_text
hbox.AddF( ClientGUICommon.BetterStaticText( self, mid_text ), CC.FLAGS_VCENTER )
hbox.AddF( self._times, CC.FLAGS_VCENTER )
self.SetSizer( hbox )
def GetValue( self ):
num = self._num.GetValue()
time_delta = self._times.GetValue()
return ( num, time_delta )
def SetToolTipString( self, text ):
wx.Panel.SetToolTipString( self, text )
for c in self.GetChildren():
c.SetToolTipString( text )
def SetValue( self, velocity ):
( num, time_delta ) = velocity
self._num.SetValue( num )
self._times.SetValue( time_delta )

View File

@ -109,7 +109,7 @@ def GetSafeSize( tlw, min_size, gravity ):
def ExpandTLWIfPossible( tlw, frame_key, desired_size_delta ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = new_options.GetFrameLocation( frame_key )
@ -148,7 +148,7 @@ def PostSizeChangedEvent( window ):
def SaveTLWSizeAndPosition( tlw, frame_key ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = new_options.GetFrameLocation( frame_key )
@ -170,7 +170,7 @@ def SaveTLWSizeAndPosition( tlw, frame_key ):
def SetTLWSizeAndPosition( tlw, frame_key ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = new_options.GetFrameLocation( frame_key )
@ -293,7 +293,7 @@ class NewDialog( wx.Dialog ):
wx.Dialog.__init__( self, parent, title = title, style = style )
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
@ -638,7 +638,7 @@ class Frame( wx.Frame ):
wx.Frame.__init__( self, parent, title = title, style = style )
self._new_options = HG.client_controller.GetNewOptions()
self._new_options = HG.client_controller.new_options
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )

View File

@ -55,7 +55,7 @@ def EfficientlyThumbnailNumpyImage( numpy_image, ( target_x, target_y ) ):
def GenerateNumpyImage( path, mime ):
if mime == HC.IMAGE_GIF or HG.client_controller.GetNewOptions().GetBoolean( 'load_images_with_pil' ):
if mime == HC.IMAGE_GIF or HG.client_controller.new_options.GetBoolean( 'load_images_with_pil' ):
# a regular cv.imread call, can crash the whole process on random thumbs, hooray, so have this as backup
# it was just the read that was the problem, so this seems to work fine, even if pil is only about half as fast
@ -302,7 +302,7 @@ HydrusFileHandling.GenerateThumbnailFromStaticImage = GenerateThumbnailFromStati
def ResizeNumpyImage( mime, numpy_image, ( target_x, target_y ) ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
( scale_up_quality, scale_down_quality ) = new_options.GetMediaZoomQuality( mime )

View File

@ -373,7 +373,7 @@ class FileImportJob( object ):
mime = HydrusFileHandling.GetMime( self._temp_path )
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
if mime in HC.DECOMPRESSION_BOMB_IMAGES and new_options.GetBoolean( 'do_not_import_decompression_bombs' ):
@ -430,7 +430,7 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
self._pending_queries = []
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
self._get_tags_if_url_known_and_file_redundant = new_options.GetBoolean( 'get_tags_if_url_known_and_file_redundant' )
@ -1766,7 +1766,7 @@ class ImportFolder( HydrusSerialisable.SerialisableBaseNamed ):
if tag_import_options is None:
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
tag_import_options = new_options.GetDefaultTagImportOptions( ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_DEFAULT ) )
@ -3717,7 +3717,7 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
self._file_import_options = ClientDefaults.GetDefaultFileImportOptions()
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
self._tag_import_options = new_options.GetDefaultTagImportOptions( self._gallery_identifier )
@ -4478,12 +4478,21 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
except HydrusExceptions.NetworkException as e:
if isinstance( e, HydrusExceptions.NetworkInfrastructureException ):
delay = 3600
else:
delay = HC.UPDATE_DURATION
HydrusData.Print( 'The subscription ' + self._name + ' encountered an exception when trying to sync:' )
HydrusData.PrintException( e )
job_key.SetVariable( 'popup_text_1', 'Encountered a network error, will retry again later' )
self._DelayWork( HC.UPDATE_DURATION, 'network error: ' + HydrusData.ToUnicode( e ) )
self._DelayWork( delay, 'network error: ' + HydrusData.ToUnicode( e ) )
time.sleep( 5 )
@ -4936,7 +4945,7 @@ class ThreadWatcherImport( HydrusSerialisable.SerialisableBase ):
file_import_options = ClientDefaults.GetDefaultFileImportOptions()
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
tag_import_options = new_options.GetDefaultTagImportOptions( ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_THREAD_WATCHER ) )
@ -5148,7 +5157,7 @@ class ThreadWatcherImport( HydrusSerialisable.SerialisableBase ):
def _PublishPageName( self, page_key ):
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
cannot_rename = not new_options.GetBoolean( 'permit_watchers_to_name_their_pages' )

View File

@ -39,9 +39,7 @@ class HydrusResourceBooruFile( HydrusResourceBooru ):
share_key = request.hydrus_args[ 'share_key' ]
hash = request.hydrus_args[ 'hash' ]
local_booru_manager = HG.client_controller.GetManager( 'local_booru' )
local_booru_manager.CheckFileAuthorised( share_key, hash )
HG.client_controller.local_booru_manager.CheckFileAuthorised( share_key, hash )
client_files_manager = HG.client_controller.client_files_manager
@ -61,7 +59,7 @@ class HydrusResourceBooruGallery( HydrusResourceBooru ):
share_key = request.hydrus_args[ 'share_key' ]
local_booru_manager = HG.client_controller.GetManager( 'local_booru' )
local_booru_manager = HG.client_controller.local_booru_manager
local_booru_manager.CheckShareAuthorised( share_key )
@ -143,7 +141,7 @@ class HydrusResourceBooruPage( HydrusResourceBooru ):
share_key = request.hydrus_args[ 'share_key' ]
hash = request.hydrus_args[ 'hash' ]
local_booru_manager = HG.client_controller.GetManager( 'local_booru' )
local_booru_manager = HG.client_controller.local_booru_manager
local_booru_manager.CheckFileAuthorised( share_key, hash )
@ -232,7 +230,7 @@ class HydrusResourceBooruThumbnail( HydrusResourceBooru ):
share_key = request.hydrus_args[ 'share_key' ]
hash = request.hydrus_args[ 'hash' ]
local_booru_manager = HG.client_controller.GetManager( 'local_booru' )
local_booru_manager = HG.client_controller.local_booru_manager
local_booru_manager.CheckFileAuthorised( share_key, hash )

View File

@ -251,59 +251,24 @@ class FileInfoManager( object ):
mime = HC.APPLICATION_UNKNOWN
self._hash = hash
self._size = size
self._mime = mime
self._width = width
self._height = height
self._duration = duration
self._num_frames = num_frames
self._num_words = num_words
self.hash = hash
self.size = size
self.mime = mime
self.width = width
self.height = height
self.duration = duration
self.num_frames = num_frames
self.num_words = num_words
def Duplicate( self ):
return FileInfoManager( self._hash, self._size, self._mime, self._width, self._height, self._duration, self._num_frames, self._num_words )
def GetDuration( self ):
return self._duration
def GetHash( self ):
return self._hash
def GetMime( self ):
return self._mime
def GetNumFrames( self ):
return self._num_frames
def GetNumWords( self ):
return self._num_words
def GetResolution( self ):
return ( self._width, self._height )
def GetSize( self ):
return self._size
return FileInfoManager( self.hash, self.size, self.mime, self.width, self.height, self.duration, self.num_frames, self.num_words )
def ToTuple( self ):
return ( self._hash, self._size, self._mime, self._width, self._height, self._duration, self._num_frames, self._num_words )
return ( self.hash, self.size, self.mime, self.width, self.height, self.duration, self.num_frames, self.num_words )
class LocationsManager( object ):
@ -1040,7 +1005,7 @@ class MediaList( object ):
if for_media_viewer:
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
media_show_action = new_options.GetMediaShowAction( media.GetMime() )
@ -1239,7 +1204,7 @@ class MediaList( object ):
self._media_sort = media_sort
media_sort_fallback = HG.client_controller.GetNewOptions().GetFallbackSort()
media_sort_fallback = HG.client_controller.new_options.GetFallbackSort()
( sort_key, reverse ) = media_sort_fallback.GetSortKeyAndReverse( self._file_service_key )
@ -1860,7 +1825,7 @@ class MediaResult( object ):
def GetDuration( self ):
return self._file_info_manager.GetDuration()
return self._file_info_manager.duration
def GetFileInfoManager( self ):
@ -1870,7 +1835,7 @@ class MediaResult( object ):
def GetHash( self ):
return self._file_info_manager.GetHash()
return self._file_info_manager.hash
def GetInbox( self ):
@ -1885,17 +1850,17 @@ class MediaResult( object ):
def GetMime( self ):
return self._file_info_manager.GetMime()
return self._file_info_manager.mime
def GetNumFrames( self ):
return self._file_info_manager.GetNumFrames()
return self._file_info_manager.num_frames
def GetNumWords( self ):
return self._file_info_manager.GetNumWords()
return self._file_info_manager.num_words
def GetRatingsManager( self ):
@ -1905,12 +1870,12 @@ class MediaResult( object ):
def GetResolution( self ):
return self._file_info_manager.GetResolution()
return ( self._file_info_manager.width, self._file_info_manager.height )
def GetSize( self ):
return self._file_info_manager.GetSize()
return self._file_info_manager.size
def GetTagsManager( self ):

View File

@ -1080,7 +1080,7 @@ class NetworkJob( object ):
self._status_text = u'sending request\u2026'
connect_timeout = HG.client_controller.GetNewOptions().GetInteger( 'network_timeout' )
connect_timeout = HG.client_controller.new_options.GetInteger( 'network_timeout' )
read_timeout = connect_timeout * 6
@ -1621,7 +1621,7 @@ class NetworkJob( object ):
if not self._CanReattemptRequest():
raise HydrusExceptions.NetworkException( 'Could not connect!' )
raise HydrusExceptions.ConnectionException( 'Could not connect!' )
with self._lock:
@ -1637,7 +1637,7 @@ class NetworkJob( object ):
if not self._CanReattemptRequest():
raise HydrusExceptions.NetworkException( 'Connection successful, but reading response timed out!' )
raise HydrusExceptions.ConnectionException( 'Connection successful, but reading response timed out!' )
with self._lock:

View File

@ -92,7 +92,7 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_DOMAIN_MANAGER
SERIALISABLE_NAME = 'Domain Manager'
SERIALISABLE_VERSION = 1
SERIALISABLE_VERSION = 2
def __init__( self ):
@ -103,6 +103,10 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
self._url_matches = HydrusSerialisable.SerialisableList()
self._network_contexts_to_custom_header_dicts = collections.defaultdict( dict )
self._url_match_names_to_display = {}
self._url_match_names_to_page_parsing_keys = HydrusSerialisable.SerialisableBytesDictionary()
self._url_match_names_to_gallery_parsing_keys = HydrusSerialisable.SerialisableBytesDictionary()
self._domains_to_url_matches = collections.defaultdict( list )
self._dirty = False
@ -115,31 +119,29 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
def _GetSerialisableInfo( self ):
serialisable_url_matches = self._url_matches.GetSerialisableTuple()
serialisable_url_match_names_to_display = self._url_match_names_to_display.items()
serialisable_url_match_names_to_page_parsing_keys = self._url_match_names_to_page_parsing_keys.GetSerialisableTuple()
serialisable_url_match_names_to_gallery_parsing_keys = self._url_match_names_to_gallery_parsing_keys.GetSerialisableTuple()
serialisable_network_contexts_to_custom_header_dicts = [ ( network_context.GetSerialisableTuple(), custom_header_dict.items() ) for ( network_context, custom_header_dict ) in self._network_contexts_to_custom_header_dicts.items() ]
return ( serialisable_url_matches, serialisable_network_contexts_to_custom_header_dicts )
return ( serialisable_url_matches, serialisable_url_match_names_to_display, serialisable_url_match_names_to_page_parsing_keys, serialisable_url_match_names_to_gallery_parsing_keys, serialisable_network_contexts_to_custom_header_dicts )
def _GetURLMatch( self, url ):
domain = ConvertURLIntoDomain( url )
domain = ConvertDomainIntoSecondLevelDomain( ConvertURLIntoDomain( url ) )
if domain in self._domains_to_url_matches:
url_matches = self._domains_to_url_matches[ domain ]
# it would be nice to somehow sort these based on descending complexity
# maybe by length of example url
# in this way, url matches can have overlapping desmaign
# e.g. 'post url' vs 'post url, manga subpage'
for url_match in url_matches:
try:
url_match.Test( url )
return url_match.Normalise( url )
return url_match
except HydrusExceptions.URLMatchException:
@ -153,10 +155,14 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( serialisable_url_matches, serialisable_network_contexts_to_custom_header_dicts ) = serialisable_info
( serialisable_url_matches, serialisable_url_match_names_to_display, serialisable_url_match_names_to_page_parsing_keys, serialisable_url_match_names_to_gallery_parsing_keys, serialisable_network_contexts_to_custom_header_dicts ) = serialisable_info
self._url_matches = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_url_matches )
self._url_match_names_to_display = dict( serialisable_url_match_names_to_display )
self._url_match_names_to_page_parsing_keys = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_url_match_names_to_page_parsing_keys )
self._url_match_names_to_gallery_parsing_keys = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_url_match_names_to_gallery_parsing_keys )
self._network_contexts_to_custom_header_dicts = collections.defaultdict( dict )
for ( serialisable_network_context, custom_header_dict_items ) in serialisable_network_contexts_to_custom_header_dicts:
@ -179,12 +185,95 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
self._domains_to_url_matches[ domain ].append( url_match )
# we now sort them in descending complexity so that
# post url/manga subpage
# is before
# post url
def key( u_m ):
return len( u_m.GetExampleURL() )
for url_matches in self._domains_to_url_matches.values():
url_matches.sort( key = key, reverse = True )
def _SetDirty( self ):
self._dirty = True
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
if version == 1:
( serialisable_url_matches, serialisable_network_contexts_to_custom_header_dicts ) = old_serialisable_info
url_matches = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_url_matches )
url_match_names_to_display = {}
url_match_names_to_page_parsing_keys = HydrusSerialisable.SerialisableBytesDictionary()
url_match_names_to_gallery_parsing_keys = HydrusSerialisable.SerialisableBytesDictionary()
for url_match in url_matches:
name = url_match.GetName()
if url_match.IsPostURL():
url_match_names_to_display[ name ] = True
url_match_names_to_page_parsing_keys[ name ] = None
if url_match.IsGalleryURL():
url_match_names_to_gallery_parsing_keys[ name ] = None
serialisable_url_match_names_to_display = url_match_names_to_display.items()
serialisable_url_match_names_to_page_parsing_keys = url_match_names_to_page_parsing_keys.GetSerialisableTuple()
serialisable_url_match_names_to_gallery_parsing_keys = url_match_names_to_gallery_parsing_keys.GetSerialisableTuple()
new_serialisable_info = ( serialisable_url_matches, serialisable_url_match_names_to_display, serialisable_url_match_names_to_page_parsing_keys, serialisable_url_match_names_to_gallery_parsing_keys, serialisable_network_contexts_to_custom_header_dicts )
return ( 2, new_serialisable_info )
def _UpdateURLMatchLinks( self ):
for url_match in self._url_matches:
name = url_match.GetName()
if url_match.IsPostURL():
if name not in self._url_match_names_to_display:
self._url_match_names_to_display[ name ] = True
if name not in self._url_match_names_to_page_parsing_keys:
self._url_match_names_to_page_parsing_keys[ name ] = None
if url_match.IsGalleryURL():
if name not in self._url_match_names_to_gallery_parsing_keys:
self._url_match_names_to_gallery_parsing_keys[ name ] = None
def CanValidateInPopup( self, network_contexts ):
# we can always do this for headers
@ -192,6 +281,47 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
return True
def ConvertURLsToMediaViewerTuples( self, urls ):
url_tuples = []
with self._lock:
for url in urls:
url_match = self._GetURLMatch( url )
if url_match is None:
domain = ConvertURLIntoDomain( url )
url_tuples.append( ( domain, url ) )
else:
name = url_match.GetName()
if url_match.IsPostURL() and name in self._url_match_names_to_display:
if self._url_match_names_to_display[ name ]:
url_tuples.append( ( name, url ) )
if len( url_tuples ) == 10:
break
url_tuples.sort()
return url_tuples
def GenerateValidationPopupProcess( self, network_contexts ):
with self._lock:
@ -275,6 +405,19 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
def GetURLMatchLinks( self ):
with self._lock:
return ( dict( self._url_match_names_to_display ), dict( self._url_match_names_to_page_parsing_keys ), dict( self._url_match_names_to_gallery_parsing_keys ) )
def Initialise( self ):
self._RecalcCache()
def IsDirty( self ):
with self._lock:
@ -308,9 +451,6 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
def NormaliseURL( self, url ):
# call this before an entry into a seed cache or the db
# use it in the dialog to review mass db-level changes
with self._lock:
url_match = self._GetURLMatch( url )
@ -372,6 +512,26 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
self._url_matches.extend( url_matches )
self._UpdateURLMatchLinks()
self._RecalcCache()
self._SetDirty()
def SetURLMatchLinks( self, url_match_names_to_display, url_match_names_to_page_parsing_keys, url_match_names_to_gallery_parsing_keys ):
with self._lock:
self._url_match_names_to_display = {}
self._url_match_names_to_page_parsing_keys = HydrusSerialisable.SerialisableBytesDictionary()
self._url_match_names_to_gallery_parsing_keys = HydrusSerialisable.SerialisableBytesDictionary()
self._url_match_names_to_display.update( url_match_names_to_display )
self._url_match_names_to_page_parsing_keys.update( url_match_names_to_page_parsing_keys )
self._url_match_names_to_gallery_parsing_keys.update( url_match_names_to_gallery_parsing_keys )
self._SetDirty()
@ -577,7 +737,7 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
def GetDomain( self ):
return ConvertURLIntoDomain( self._example_url )
return ConvertDomainIntoSecondLevelDomain( HydrusData.ToByteString( self._netloc ) )
def GetExampleURL( self ):
@ -590,6 +750,16 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
return self._url_type
def IsGalleryURL( self ):
return self._url_type == HC.URL_TYPE_GALLERY
def IsPostURL( self ):
return self._url_type == HC.URL_TYPE_POST
def Normalise( self, url ):
p = urlparse.urlparse( url )

View File

@ -294,7 +294,30 @@ class NetworkLoginManager( HydrusSerialisable.SerialisableBase ):
session = self.engine.session_manager.GetSession( network_context )
response = session.get( 'https://www.hentai-foundry.com/' )
num_attempts = 0
while True:
try:
response = session.get( 'https://www.hentai-foundry.com/', timeout = 10 )
break
except requests.exceptions.ConnectionError, requests.exceptions.ConnectTimeout:
if num_attempts < 3:
num_attempts += 1
time.sleep( 3 )
else:
raise HydrusExceptions.ConnectionException( 'Could not connect to HF to log in!' )
time.sleep( 1 )

View File

@ -190,7 +190,7 @@ class RasterContainerVideo( RasterContainer ):
( x, y ) = self._target_resolution
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
video_buffer_size_mb = new_options.GetInteger( 'video_buffer_size_mb' )

View File

@ -427,7 +427,7 @@ class FileSystemPredicates( object ):
self._duplicate_predicates = []
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
forced_search_limit = new_options.GetNoneableInteger( 'forced_search_limit' )

View File

@ -932,9 +932,7 @@ class ServiceRepository( ServiceRestricted ):
return False
options = HG.client_controller.GetOptions()
if options[ 'pause_repo_sync' ]:
if HG.client_controller.options[ 'pause_repo_sync' ]:
return False
@ -968,8 +966,6 @@ class ServiceRepository( ServiceRestricted ):
with self._lock:
options = HG.client_controller.GetOptions()
if not self._CanSync():
return False

View File

@ -220,7 +220,7 @@ def RenderTag( tag, render_for_user ):
if render_for_user:
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
if new_options.GetBoolean( 'show_namespaces' ):

View File

@ -62,7 +62,7 @@ class GIFRenderer( object ):
self._num_frames = num_frames
self._target_resolution = target_resolution
new_options = HG.client_controller.GetNewOptions()
new_options = HG.client_controller.new_options
if new_options.GetBoolean( 'disable_cv_for_gifs' ) or cv2.__version__.startswith( '2' ):

View File

@ -1,8 +1,3 @@
import hsaudiotag
import hsaudiotag.auto
import hsaudiotag.flac
import hsaudiotag.mpeg
import hsaudiotag.ogg
import HydrusConstants as HC
import os
import threading
@ -11,55 +6,7 @@ import traceback
#if HC.PLATFORM_WINDOWS: import mp3play
parsed_noises = {}
def GetFLACDuration( path ):
hsaudio_object = hsaudiotag.flac.FLAC( path )
if not hsaudio_object.valid: raise Exception( 'FLAC file was not valid!' )
length_in_seconds = hsaudio_object.duration
length_in_ms = int( length_in_seconds * 1000 )
return length_in_ms
def GetMP3Duration( path ):
hsaudio_object = hsaudiotag.mpeg.Mpeg( path )
if not hsaudio_object.valid: raise Exception( 'MP3 file was not valid!' )
length_in_seconds = hsaudio_object.duration
length_in_ms = int( length_in_seconds * 1000 )
return length_in_ms
def GetOGGVorbisDuration( path ):
hsaudio_object = hsaudiotag.ogg.Vorbis( path )
if not hsaudio_object.valid: raise Exception( 'Ogg Vorbis file was not valid!' )
length_in_seconds = hsaudio_object.duration
length_in_ms = int( length_in_seconds * 1000 )
return length_in_ms
def GetWMADuration( path ):
hsaudio_object = hsaudiotag.wma.WMADecoder( path )
if not hsaudio_object.valid: raise Exception( 'WMA file was not valid!' )
length_in_seconds = hsaudio_object.duration
length_in_ms = int( length_in_seconds * 1000 )
return length_in_ms
# There used to be hsaudiotag duration stuff here, but I moved it all to FFMPEG
'''
def PlayNoise( name ):
@ -81,4 +28,4 @@ def PlayNoise( name ):
noise = parsed_noises[ name ]
noise.play()
'''
'''

View File

@ -49,7 +49,7 @@ options = {}
# Misc
NETWORK_VERSION = 18
SOFTWARE_VERSION = 284
SOFTWARE_VERSION = 285
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -629,13 +629,15 @@ def DebugPrint( debug_info ):
sys.stdout.flush()
sys.stderr.flush()
def DeserialisePrettyTags( text ):
def DeserialiseNewlinedTexts( text ):
text = text.replace( '\r', '' )
tags = text.split( '\n' )
texts = text.split( '\n' )
return tags
texts = [ line for line in texts if line != '' ]
return texts
def EncodeBytes( encoding, data ):
@ -1612,9 +1614,7 @@ class ContentUpdate( object ):
( file_info_manager, timestamp ) = self._row
hash = file_info_manager.GetHash()
hashes = { hash }
hashes = { file_info_manager.hash }
elif self._action in ( HC.CONTENT_UPDATE_ARCHIVE, HC.CONTENT_UPDATE_DELETE, HC.CONTENT_UPDATE_UNDELETE, HC.CONTENT_UPDATE_INBOX, HC.CONTENT_UPDATE_PEND, HC.CONTENT_UPDATE_RESCIND_PEND, HC.CONTENT_UPDATE_RESCIND_PETITION ):
@ -1685,56 +1685,6 @@ class ContentUpdate( object ):
return ( self._data_type, self._action, self._row )
class EditLogAction( object ):
yaml_tag = u'!EditLogAction'
def __init__( self, action ): self._action = action
def GetAction( self ): return self._action
class EditLogActionAdd( EditLogAction ):
yaml_tag = u'!EditLogActionAdd'
def __init__( self, data ):
EditLogAction.__init__( self, HC.ADD )
self._data = data
def GetData( self ): return self._data
class EditLogActionDelete( EditLogAction ):
yaml_tag = u'!EditLogActionDelete'
def __init__( self, identifier ):
EditLogAction.__init__( self, HC.DELETE )
self._identifier = identifier
def GetIdentifier( self ): return self._identifier
class EditLogActionEdit( EditLogAction ):
yaml_tag = u'!EditLogActionEdit'
def __init__( self, identifier, data ):
EditLogAction.__init__( self, HC.EDIT )
self._identifier = identifier
self._data = data
def GetData( self ): return self._data
def GetIdentifier( self ): return self._identifier
class JobDatabase( object ):
def __init__( self, job_type, synchronous, action, *args, **kwargs ):

View File

@ -25,8 +25,13 @@ class StringMatchException( ParseException ): pass
class URLMatchException( ParseException ): pass
class NetworkException( Exception ): pass
class NetworkInfrastructureException( NetworkException ): pass
class ConnectionException( NetworkInfrastructureException ): pass
class FirewallException( NetworkInfrastructureException ): pass
class ServerBusyException( NetworkInfrastructureException ): pass
class BandwidthException( NetworkException ): pass
class FirewallException( NetworkException ): pass
class ForbiddenException( NetworkException ): pass
class LoginException( NetworkException ): pass
class NetworkVersionException( NetworkException ): pass
@ -35,7 +40,6 @@ class NotFoundException( NetworkException ): pass
class NotModifiedException( NetworkException ): pass
class PermissionException( NetworkException ): pass
class RedirectionException( NetworkException ): pass
class ServerBusyException( NetworkException ): pass
class ServerException( NetworkException ): pass
class SessionException( NetworkException ): pass
class WrongServiceTypeException( NetworkException ): pass

View File

@ -1,10 +1,5 @@
import gc
import hashlib
import hsaudiotag
import hsaudiotag.auto
import hsaudiotag.flac
import hsaudiotag.mpeg
import hsaudiotag.ogg
import HydrusAudioHandling
import HydrusConstants as HC
import HydrusDocumentHandling
@ -219,21 +214,13 @@ def GetFileInfo( path, mime = None ):
num_words = HydrusDocumentHandling.GetPDFNumWords( path )
elif mime == HC.AUDIO_MP3:
elif mime in HC.AUDIO:
duration = HydrusAudioHandling.GetMP3Duration( path )
ffmpeg_lines = HydrusVideoHandling.GetFFMPEGInfoLines( path )
elif mime == HC.AUDIO_OGG:
duration_in_s = HydrusVideoHandling.ParseFFMPEGDuration( ffmpeg_lines )
duration = HydrusAudioHandling.GetOGGVorbisDuration( path )
elif mime == HC.AUDIO_FLAC:
duration = HydrusAudioHandling.GetFLACDuration( path )
elif mime == HC.AUDIO_WMA:
duration = HydrusAudioHandling.GetWMADuration( path )
duration = int( duration_in_s * 1000 )
return ( size, mime, width, height, duration, num_frames, num_words )
@ -320,15 +307,5 @@ def GetMime( path ):
HydrusData.PrintException( e, do_wait = False )
hsaudio_object = hsaudiotag.auto.File( path )
if hsaudio_object.valid:
if isinstance( hsaudio_object.original, hsaudiotag.mpeg.Mpeg ): return HC.AUDIO_MP3
elif isinstance( hsaudio_object.original, hsaudiotag.flac.FLAC ): return HC.AUDIO_FLAC
elif isinstance( hsaudio_object.original, hsaudiotag.ogg.Vorbis ): return HC.AUDIO_OGG
elif isinstance( hsaudio_object.original, hsaudiotag.wma.WMADecoder ): return HC.AUDIO_WMA
return HC.APPLICATION_UNKNOWN

View File

@ -127,7 +127,14 @@ def GeneratePILImage( path ):
if pil_image.format == 'JPEG' and hasattr( pil_image, '_getexif' ):
exif_dict = pil_image._getexif()
try:
exif_dict = pil_image._getexif()
except:
exif_dict = None
if exif_dict is not None:

View File

@ -44,7 +44,10 @@ def GetExternalIP():
( output, error ) = p.communicate()
if error is not None and len( error ) > 0: raise Exception( 'Problem while trying to fetch External IP:' + os.linesep * 2 + HydrusData.ToUnicode( error ) )
if error is not None and len( error ) > 0:
raise Exception( 'Problem while trying to fetch External IP:' + os.linesep * 2 + HydrusData.ToUnicode( error ) )
else:
try:

View File

@ -622,7 +622,16 @@ class BandwidthTracker( HydrusSerialisable.SerialisableBase ):
( year, month ) = ( dt.year, dt.month )
next_month_dt = datetime.datetime( year, month + 1, 1 )
next_month_year = year
if month == 12:
next_month_year += 1
next_month = ( month + 1 ) % 12
next_month_dt = datetime.datetime( next_month_year, next_month, 1 )
next_month_time = calendar.timegm( next_month_dt.timetuple() )

View File

@ -320,6 +320,10 @@ class SerialisableBytesDictionary( SerialisableBase, dict ):
encoded_value = [ item.encode( 'hex' ) for item in value ]
elif value is None:
encoded_value = value
else:
encoded_value = value.encode( 'hex' )
@ -348,6 +352,10 @@ class SerialisableBytesDictionary( SerialisableBase, dict ):
value = [ encoded_item.decode( 'hex' ) for encoded_item in encoded_value ]
elif encoded_value is None:
value = encoded_value
else:
value = encoded_value.decode( 'hex' )

View File

@ -1,5 +1,4 @@
import collections
import HydrusAudioHandling
import HydrusConstants as HC
import HydrusDocumentHandling
import HydrusExceptions

View File

@ -586,7 +586,7 @@ class HydrusResource( Resource ):
response_context = ResponseContext( 403, mime = default_mime, body = default_encoding( failure.value ) )
elif failure.type in ( HydrusExceptions.NotFoundException, HydrusExceptions.DataMissing ):
elif failure.type in ( HydrusExceptions.NotFoundException, HydrusExceptions.DataMissing, HydrusExceptions.FileMissingException ):
response_context = ResponseContext( 404, mime = default_mime, body = default_encoding( failure.value ) )

View File

@ -279,11 +279,6 @@ class Controller( HydrusController.HydrusController ):
return self.db.GetFilesDir()
def GetServerSessionManager( self ):
return self._server_session_manager
def GetServices( self ):
return list( self._services )
@ -293,7 +288,7 @@ class Controller( HydrusController.HydrusController ):
HydrusController.HydrusController.InitModel( self )
self._server_session_manager = HydrusSessions.HydrusSessionManagerServer()
self.server_session_manager = HydrusSessions.HydrusSessionManagerServer()
self._service_keys_to_connected_ports = {}
@ -421,7 +416,7 @@ class Controller( HydrusController.HydrusController ):
self.WriteSynchronous( 'dirty_services', dirty_services )
dirty_accounts = self._server_session_manager.GetDirtyAccounts()
dirty_accounts = self.server_session_manager.GetDirtyAccounts()
if len( dirty_accounts ) > 0:

View File

@ -74,9 +74,7 @@ class HydrusResourceSessionKey( HydrusServerResources.HydrusResource ):
access_key = self._parseAccessKey( request )
session_manager = HG.server_controller.GetServerSessionManager()
( session_key, expires ) = session_manager.AddSession( self._service_key, access_key )
( session_key, expires ) = HG.server_controller.server_session_manager.AddSession( self._service_key, access_key )
now = HydrusData.GetNow()
@ -151,9 +149,7 @@ class HydrusResourceRestricted( HydrusServerResources.HydrusResource ):
raise Exception( 'Problem parsing cookies!' )
session_manager = HG.server_controller.GetServerSessionManager()
account = session_manager.GetAccount( self._service_key, session_key )
account = HG.server_controller.server_session_manager.GetAccount( self._service_key, session_key )
request.hydrus_account = account
@ -229,13 +225,11 @@ class HydrusResourceRestrictedAccountModification( HydrusResourceRestricted ):
kwargs = request.hydrus_args # for things like expires, title, and so on
server_session_manager = HG.server_controller.GetServerSessionManager()
with HG.dirty_object_lock:
HG.server_controller.WriteSynchronous( 'account_modification', self._service_key, request.hydrus_account, action, subject_accounts, **kwargs )
server_session_manager.UpdateAccounts( self._service_key, subject_accounts )
HG.server_controller.server_session_manager.UpdateAccounts( self._service_key, subject_accounts )
response_context = HydrusServerResources.ResponseContext( 200 )
@ -263,9 +257,7 @@ class HydrusResourceRestrictedAccountTypes( HydrusResourceRestricted ):
HG.server_controller.WriteSynchronous( 'account_types', self._service_key, request.hydrus_account, account_types, deletee_account_type_keys_to_new_account_type_keys )
session_manager = HG.server_controller.GetServerSessionManager()
session_manager.RefreshAccounts( self._service_key )
HG.server_controller.server_session_manager.RefreshAccounts( self._service_key )
response_context = HydrusServerResources.ResponseContext( 200 )

View File

@ -250,7 +250,7 @@ class TestServer( unittest.TestCase ):
with open( file_path, 'wb' ) as f: f.write( EXAMPLE_FILE )
with open( thumbnail_path, 'wb' ) as f: f.write( EXAMPLE_THUMBNAIL )
local_booru_manager = HG.test_controller.GetManager( 'local_booru' )
local_booru_manager = HG.client_controller.local_booru_manager
#

21
test.py
View File

@ -79,6 +79,10 @@ class Controller( object ):
self.new_options = ClientData.ClientOptions( self.db_dir )
HC.options = ClientDefaults.GetClientDefaultOptions()
self.options = HC.options
def show_text( text ): pass
HydrusData.ShowText = show_text
@ -125,8 +129,6 @@ class Controller( object ):
self._reads[ 'tag_parents' ] = {}
self._reads[ 'tag_siblings' ] = {}
HC.options = ClientDefaults.GetClientDefaultOptions()
self._writes = collections.defaultdict( list )
self._managers = {}
@ -138,8 +140,9 @@ class Controller( object ):
self._managers[ 'tag_siblings' ] = ClientCaches.TagSiblingsManager( self )
self._managers[ 'tag_parents' ] = ClientCaches.TagParentsManager( self )
self._managers[ 'undo' ] = ClientCaches.UndoManager( self )
self._server_session_manager = HydrusSessions.HydrusSessionManagerServer()
self._managers[ 'local_booru' ] = ClientCaches.LocalBooruCache( self )
self.server_session_manager = HydrusSessions.HydrusSessionManagerServer()
self.local_booru_manager = ClientCaches.LocalBooruCache( self )
self._cookies = {}
@ -216,21 +219,11 @@ class Controller( object ):
return self.new_options
def GetOptions( self ):
return HC.options
def GetManager( self, manager_type ):
return self._managers[ manager_type ]
def GetServerSessionManager( self ):
return self._server_session_manager
def GetWrite( self, name ):
write = self._writes[ name ]