Version 284
This commit is contained in:
parent
cbbeec7197
commit
a7f254485a
|
@ -8,6 +8,44 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 284</h3></li>
|
||||
<ul>
|
||||
<li>fixed subscription queries turning dead on the initial sync</li>
|
||||
<li>all dead subscription queries have been set to check again in case they can revive</li>
|
||||
<li>added query file velocity to edit subscription panel</li>
|
||||
<li>subscription network contexts now reflect the new multiple subscription query system, and are named "sub_name: query_text". as every query now counts as its own separate subscription network context, this will stop query-heavy subscriptions from throttling so much on bandwidth limits</li>
|
||||
<li>finished URLMatch object, which matches and normalises URLs into certain 'classes' like 'gelbooru post url'</li>
|
||||
<li>expanded some URLMatch subdomain options</li>
|
||||
<li>fixed some test logic in URLMatch</li>
|
||||
<li>finished the last of the EditURLMatchPanel</li>
|
||||
<li>split the 'manage network rules' dialog into two panels--it now has a 'url classes' tab</li>
|
||||
<li>wrote a panel for managing url matches</li>
|
||||
<li>added export/import/duplicate buttons to EditURLMatchesPanel</li>
|
||||
<li>wrote some URLMatches for hentai-foundry as an initial test of the system and added a temp button to add them--please check them out to see how it all works</li>
|
||||
<li>all, invert, inbox, and archive (and none, lol) thumbnail 'select' menu items now have counts</li>
|
||||
<li>invert is now at the bottom</li>
|
||||
<li>the thumbnail select menu now has local/remote entries if applicable (this typically is only true in 'all known files' file domain)</li>
|
||||
<li>added png/clipboard export/import/duplicate code to the generic new listctrl button wrapper panel, which will save a bunch of time as the png/clipboard sharing system expands</li>
|
||||
<li>added a human-facing serialisable name to all objects on the new serialisation system and tied the new import/export code into it for png presentation</li>
|
||||
<li>the edit import folder dialog will now complain (but not veto) on an ok event if any of the entered paths do not exist</li>
|
||||
<li>if you attempt to manually run an import folder while import folders are globally paused, you'll get a little popup telling you so</li>
|
||||
<li>added an experimental 'thumbnail fill' setting to options->gui. it zooms the existing thumbnails so they fill the whole thumb space. feedback on this from those who would be interested in a prettier system would be appreciated</li>
|
||||
<li>added 'paste tags' buttons to filename tagging options panel</li>
|
||||
<li>the paths/urls in the file import cache are now their own object that holds the creation/modified/source times and current status and note. this object can also hold prospective urls, tags, and hashes for future use</li>
|
||||
<li>a bunch of file import actions are faster</li>
|
||||
<li>all the different importers now use this new file import object</li>
|
||||
<li>fixed a screen position calculation in the new drag and drop filtering code that was accidentally including too many possible drop candidates on drops in the top-left corner of the main gui (if you had trouble moving tabs to the left, this should be it fixed!)</li>
|
||||
<li>fixed a problem display volume/chapter/page tags that included unicode characters in thumbnail banners and media viewers</li>
|
||||
<li>fixed a rare media display bug in the dupe filter</li>
|
||||
<li>fixed some 'C++ part of panel has been deleted' bugs in review services if the frame is shut down before delayed db info is fetched</li>
|
||||
<li>cleaned up some more 'C++ deleted' errors in import files selection dialog</li>
|
||||
<li>fixed the network context custom header panel 'add' action, which wasn't saving the value of the panel</li>
|
||||
<li>fixed a bunch of bugs in the newish QueueListBox class</li>
|
||||
<li>SynchroniseRepositories daemon will be better about quitting early on application shutdown</li>
|
||||
<li>cleaned up some pending pretty timestamp grammar</li>
|
||||
<li>when the client cannot clean up a temporary file, it will print more error information</li>
|
||||
<li>added pylzma to the 'running from source' library recommendations. this is not required, but if available it adds ZWS flash support</li>
|
||||
</ul>
|
||||
<li><h3>version 283</h3></li>
|
||||
<ul>
|
||||
<li>subscription popups show a bit more info about their individual queries</li>
|
||||
|
|
|
@ -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 PyOpenSSL PyPDF2 PyYAML requests Send2Trash service_identity twisted youtube-dl</li>
|
||||
<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>
|
||||
</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>
|
||||
|
|
|
@ -6627,7 +6627,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if stop_time is not None:
|
||||
|
||||
HG.client_controller.pub( 'splash_set_status_subtext', HydrusData.ConvertTimestampToPrettyPending( stop_time ) )
|
||||
HG.client_controller.pub( 'splash_set_status_subtext', HydrusData.ConvertTimestampToPrettyPending( stop_time, prefix = '' ) )
|
||||
|
||||
if HydrusData.TimeHasPassed( stop_time ):
|
||||
|
||||
|
@ -9998,6 +9998,64 @@ class DB( HydrusDB.HydrusDB ):
|
|||
self.pub_initial_message( message )
|
||||
|
||||
|
||||
if version == 283:
|
||||
|
||||
have_heavy_subs = False
|
||||
|
||||
some_revived = False
|
||||
|
||||
try:
|
||||
|
||||
subscriptions = self._GetJSONDumpNamed( HydrusSerialisable.SERIALISABLE_TYPE_SUBSCRIPTION )
|
||||
|
||||
for subscription in subscriptions:
|
||||
|
||||
save_it = False
|
||||
|
||||
if len( subscription._queries ) > 1:
|
||||
|
||||
have_heavy_subs = True
|
||||
|
||||
|
||||
for query in subscription._queries:
|
||||
|
||||
if query.IsDead():
|
||||
|
||||
query.CheckNow()
|
||||
|
||||
save_it = True
|
||||
|
||||
|
||||
|
||||
if save_it:
|
||||
|
||||
self._SetJSONDump( subscription )
|
||||
|
||||
some_revived = True
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.Print( 'While attempting to revive dead subscription queries, I had this problem:' )
|
||||
HydrusData.PrintException( e )
|
||||
|
||||
|
||||
if some_revived:
|
||||
|
||||
message = 'The old subscription syncing code was setting many new queries \'dead\' after their first sync. All your dead subscription queries have been set to check again in case they can revive.'
|
||||
|
||||
self.pub_initial_message( message )
|
||||
|
||||
|
||||
if have_heavy_subs:
|
||||
|
||||
message = 'The way subscriptions consume bandwidth has changed to stop heavy subs with many queries from being throttled so often. If you have big subscriptions with a bunch of work to do, they may catch up right now!'
|
||||
|
||||
self.pub_initial_message( message )
|
||||
|
||||
|
||||
|
||||
self._controller.pub( 'splash_set_title_text', 'updated db to v' + str( version + 1 ) )
|
||||
|
||||
self._c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )
|
||||
|
|
|
@ -272,15 +272,15 @@ def DAEMONSynchroniseRepositories( controller ):
|
|||
|
||||
if not options[ 'pause_repo_sync' ]:
|
||||
|
||||
if HydrusThreading.IsThreadShuttingDown():
|
||||
|
||||
return
|
||||
|
||||
|
||||
services = controller.services_manager.GetServices( HC.REPOSITORIES )
|
||||
|
||||
for service in services:
|
||||
|
||||
if HydrusThreading.IsThreadShuttingDown():
|
||||
|
||||
return
|
||||
|
||||
|
||||
if options[ 'pause_repo_sync' ]:
|
||||
|
||||
return
|
||||
|
@ -288,8 +288,13 @@ def DAEMONSynchroniseRepositories( controller ):
|
|||
|
||||
service.Sync( only_process_when_idle = True )
|
||||
|
||||
|
||||
time.sleep( 5 )
|
||||
if HydrusThreading.IsThreadShuttingDown():
|
||||
|
||||
return
|
||||
|
||||
|
||||
time.sleep( 3 )
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -643,6 +643,7 @@ def SortTagsList( tags, sort_type ):
|
|||
class ApplicationCommand( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_APPLICATION_COMMAND
|
||||
SERIALISABLE_NAME = 'Application Command'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, command_type = None, data = None ):
|
||||
|
@ -780,6 +781,7 @@ sqlite3.register_adapter( Booru, yaml.safe_dump )
|
|||
class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_CLIENT_OPTIONS
|
||||
SERIALISABLE_NAME = 'Client Options'
|
||||
SERIALISABLE_VERSION = 3
|
||||
|
||||
def __init__( self, db_dir = None ):
|
||||
|
@ -855,6 +857,8 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
self._dictionary[ 'booleans' ][ 'anchor_and_hide_canvas_drags' ] = HC.PLATFORM_WINDOWS
|
||||
|
||||
self._dictionary[ 'booleans' ][ 'thumbnail_fill' ] = False
|
||||
|
||||
#
|
||||
|
||||
self._dictionary[ 'colours' ] = HydrusSerialisable.SerialisableDictionary()
|
||||
|
@ -1825,6 +1829,7 @@ class Credentials( HydrusData.HydrusYAMLBase ):
|
|||
class DuplicateActionOptions( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_DUPLICATE_ACTION_OPTIONS
|
||||
SERIALISABLE_NAME = 'Duplicate Action Options'
|
||||
SERIALISABLE_VERSION = 2
|
||||
|
||||
def __init__( self, tag_service_actions = None, rating_service_actions = None, delete_second_file = False, sync_archive = False, delete_both_files = False ):
|
||||
|
@ -2190,6 +2195,7 @@ sqlite3.register_adapter( Imageboard, yaml.safe_dump )
|
|||
class Shortcut( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUT
|
||||
SERIALISABLE_NAME = 'Shortcut'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, shortcut_type = None, shortcut_key = None, modifiers = None ):
|
||||
|
@ -2300,6 +2306,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class Shortcuts( HydrusSerialisable.SerialisableBaseNamed ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUTS
|
||||
SERIALISABLE_NAME = 'Shortcuts'
|
||||
SERIALISABLE_VERSION = 2
|
||||
|
||||
def __init__( self, name ):
|
||||
|
@ -2534,6 +2541,7 @@ def ConvertMouseEventToShortcut( event ):
|
|||
class TagCensor( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_TAG_CENSOR
|
||||
SERIALISABLE_NAME = 'Tag Censorship Rules'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self ):
|
||||
|
@ -2734,6 +2742,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class CheckerOptions( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_CHECKER_OPTIONS
|
||||
SERIALISABLE_NAME = 'Checker Timing Options'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, intended_files_per_check = 8, never_faster_than = 300, never_slower_than = 86400, death_file_velocity = ( 1, 86400 ) ):
|
||||
|
@ -2754,18 +2763,18 @@ class CheckerOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
current_files_found = seed_cache.GetNumNewFilesSince( since )
|
||||
|
||||
if len( seed_cache ) == 0:
|
||||
# when a thread is only 30mins old (i.e. first file was posted 30 mins ago), we don't want to calculate based on a longer delete time delta
|
||||
# we want next check to be like 30mins from now, not 12 hours
|
||||
# so we'll say "5 files in 30 mins" rather than "5 files in 24 hours"
|
||||
|
||||
earliest_source_time = seed_cache.GetEarliestSourceTime()
|
||||
|
||||
if earliest_source_time is None:
|
||||
|
||||
current_time_delta = death_time_delta
|
||||
|
||||
else:
|
||||
|
||||
# when a thread is only 30mins old (i.e. first file was posted 30 mins ago), we don't want to calculate based on a longer delete time delta
|
||||
# we want next check to be like 30mins from now, not 12 hours
|
||||
# so we'll say "5 files in 30 mins" rather than "5 files in 24 hours"
|
||||
|
||||
earliest_source_time = seed_cache.GetEarliestSourceTime()
|
||||
|
||||
early_time_delta = max( last_check_time - earliest_source_time, 30 )
|
||||
|
||||
current_time_delta = min( early_time_delta, death_time_delta )
|
||||
|
@ -2829,7 +2838,12 @@ class CheckerOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
def GetPrettyCurrentVelocity( self, seed_cache, last_check_time ):
|
||||
def GetRawCurrentVelocity( self, seed_cache, last_check_time ):
|
||||
|
||||
return self._GetCurrentFilesVelocity( seed_cache, last_check_time )
|
||||
|
||||
|
||||
def GetPrettyCurrentVelocity( self, seed_cache, last_check_time, no_prefix = False ):
|
||||
|
||||
if len( seed_cache ) == 0:
|
||||
|
||||
|
@ -2844,9 +2858,18 @@ class CheckerOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
else:
|
||||
|
||||
if no_prefix:
|
||||
|
||||
pretty_current_velocity = ''
|
||||
|
||||
else:
|
||||
|
||||
pretty_current_velocity = 'at last check, found '
|
||||
|
||||
|
||||
( current_files_found, current_time_delta ) = self._GetCurrentFilesVelocity( seed_cache, last_check_time )
|
||||
|
||||
pretty_current_velocity = 'at last check, found ' + HydrusData.ConvertIntToPrettyString( current_files_found ) + ' files in previous ' + HydrusData.ConvertTimeDeltaToPrettyString( current_time_delta )
|
||||
pretty_current_velocity += HydrusData.ConvertIntToPrettyString( current_files_found ) + ' files in previous ' + HydrusData.ConvertTimeDeltaToPrettyString( current_time_delta )
|
||||
|
||||
|
||||
return pretty_current_velocity
|
||||
|
|
|
@ -754,3 +754,181 @@ def GetDefaultShortcuts():
|
|||
shortcuts.append( media_viewer )
|
||||
|
||||
return shortcuts
|
||||
|
||||
def GetDefaultURLMatches():
|
||||
|
||||
import ClientNetworkingDomain
|
||||
import ClientParsing
|
||||
|
||||
url_matches = []
|
||||
|
||||
#
|
||||
|
||||
name = 'hentai foundry artist pictures gallery page base'
|
||||
url_type = HC.URL_TYPE_GALLERY
|
||||
preferred_scheme = 'https'
|
||||
netloc = 'www.hentai-foundry.com'
|
||||
allow_subdomains = False
|
||||
keep_subdomains = False
|
||||
|
||||
path_components = []
|
||||
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'pictures', example_string = 'pictures' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'user', example_string = 'user' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_ANY, example_string = 'daruak' ) )
|
||||
|
||||
parameters = {}
|
||||
example_url = 'https://www.hentai-foundry.com/pictures/user/daruak'
|
||||
|
||||
url_match = ClientNetworkingDomain.URLMatch( name, url_type = url_type, preferred_scheme = preferred_scheme, netloc = netloc, allow_subdomains = allow_subdomains, keep_subdomains = keep_subdomains, path_components = path_components, parameters = parameters, example_url = example_url )
|
||||
|
||||
url_matches.append( url_match )
|
||||
|
||||
#
|
||||
|
||||
name = 'hentai foundry artist pictures gallery page'
|
||||
url_type = HC.URL_TYPE_GALLERY
|
||||
preferred_scheme = 'https'
|
||||
netloc = 'www.hentai-foundry.com'
|
||||
allow_subdomains = False
|
||||
keep_subdomains = False
|
||||
|
||||
path_components = []
|
||||
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'pictures', example_string = 'pictures' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'user', example_string = 'user' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_ANY, example_string = 'daruak' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'page', example_string = 'page' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FLEXIBLE, match_value = ClientParsing.NUMERIC, example_string = '2' ) )
|
||||
|
||||
parameters = {}
|
||||
example_url = 'https://www.hentai-foundry.com/pictures/user/daruak/page/2'
|
||||
|
||||
url_match = ClientNetworkingDomain.URLMatch( name, url_type = url_type, preferred_scheme = preferred_scheme, netloc = netloc, allow_subdomains = allow_subdomains, keep_subdomains = keep_subdomains, path_components = path_components, parameters = parameters, example_url = example_url )
|
||||
|
||||
url_matches.append( url_match )
|
||||
|
||||
#
|
||||
|
||||
name = 'hentai foundry artist scraps gallery page base'
|
||||
url_type = HC.URL_TYPE_GALLERY
|
||||
preferred_scheme = 'https'
|
||||
netloc = 'www.hentai-foundry.com'
|
||||
allow_subdomains = False
|
||||
keep_subdomains = False
|
||||
|
||||
path_components = []
|
||||
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'pictures', example_string = 'pictures' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'user', example_string = 'user' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_ANY, example_string = 'Sparrow' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'scraps', example_string = 'scraps' ) )
|
||||
|
||||
parameters = {}
|
||||
example_url = 'https://www.hentai-foundry.com/pictures/user/Sparrow/scraps'
|
||||
|
||||
url_match = ClientNetworkingDomain.URLMatch( name, url_type = url_type, preferred_scheme = preferred_scheme, netloc = netloc, allow_subdomains = allow_subdomains, keep_subdomains = keep_subdomains, path_components = path_components, parameters = parameters, example_url = example_url )
|
||||
|
||||
url_matches.append( url_match )
|
||||
|
||||
#
|
||||
|
||||
name = 'hentai foundry artist scraps gallery page'
|
||||
url_type = HC.URL_TYPE_GALLERY
|
||||
preferred_scheme = 'https'
|
||||
netloc = 'www.hentai-foundry.com'
|
||||
allow_subdomains = False
|
||||
keep_subdomains = False
|
||||
|
||||
path_components = []
|
||||
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'pictures', example_string = 'pictures' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'user', example_string = 'user' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_ANY, example_string = 'Sparrow' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'scraps', example_string = 'scraps' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'page', example_string = 'page' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FLEXIBLE, match_value = ClientParsing.NUMERIC, example_string = '3' ) )
|
||||
|
||||
parameters = {}
|
||||
example_url = 'https://www.hentai-foundry.com/pictures/user/Sparrow/scraps/page/3'
|
||||
|
||||
url_match = ClientNetworkingDomain.URLMatch( name, url_type = url_type, preferred_scheme = preferred_scheme, netloc = netloc, allow_subdomains = allow_subdomains, keep_subdomains = keep_subdomains, path_components = path_components, parameters = parameters, example_url = example_url )
|
||||
|
||||
url_matches.append( url_match )
|
||||
|
||||
#
|
||||
|
||||
name = 'hentai foundry tag search gallery page base'
|
||||
url_type = HC.URL_TYPE_GALLERY
|
||||
preferred_scheme = 'https'
|
||||
netloc = 'www.hentai-foundry.com'
|
||||
allow_subdomains = False
|
||||
keep_subdomains = False
|
||||
|
||||
path_components = []
|
||||
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'search', example_string = 'search' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'index', example_string = 'index' ) )
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters[ 'query' ] = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_ANY, example_string = 'thick_thighs' )
|
||||
|
||||
example_url = 'https://www.hentai-foundry.com/search/index?query=thick_thighs'
|
||||
|
||||
url_match = ClientNetworkingDomain.URLMatch( name, url_type = url_type, preferred_scheme = preferred_scheme, netloc = netloc, allow_subdomains = allow_subdomains, keep_subdomains = keep_subdomains, path_components = path_components, parameters = parameters, example_url = example_url )
|
||||
|
||||
url_matches.append( url_match )
|
||||
|
||||
#
|
||||
|
||||
name = 'hentai foundry tag search gallery page'
|
||||
url_type = HC.URL_TYPE_GALLERY
|
||||
preferred_scheme = 'https'
|
||||
netloc = 'www.hentai-foundry.com'
|
||||
allow_subdomains = False
|
||||
keep_subdomains = False
|
||||
|
||||
path_components = []
|
||||
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'search', example_string = 'search' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'index', example_string = 'index' ) )
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters[ 'query' ] = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_ANY, example_string = 'thick_thighs' )
|
||||
parameters[ 'page' ] = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FLEXIBLE, match_value = ClientParsing.NUMERIC, example_string = '5' )
|
||||
|
||||
example_url = 'https://www.hentai-foundry.com/search/index?query=thick_thighs&page=5'
|
||||
|
||||
url_match = ClientNetworkingDomain.URLMatch( name, url_type = url_type, preferred_scheme = preferred_scheme, netloc = netloc, allow_subdomains = allow_subdomains, keep_subdomains = keep_subdomains, path_components = path_components, parameters = parameters, example_url = example_url )
|
||||
|
||||
url_matches.append( url_match )
|
||||
|
||||
#
|
||||
|
||||
name = 'hentai foundry file page'
|
||||
url_type = HC.URL_TYPE_POST
|
||||
preferred_scheme = 'https'
|
||||
netloc = 'www.hentai-foundry.com'
|
||||
allow_subdomains = False
|
||||
keep_subdomains = False
|
||||
|
||||
path_components = []
|
||||
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'pictures', example_string = 'pictures' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'user', example_string = 'user' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_ANY, example_string = 'LittlePaw' ) )
|
||||
path_components.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FLEXIBLE, match_value = ClientParsing.NUMERIC, example_string = '554706' ) )
|
||||
|
||||
parameters = {}
|
||||
example_url = 'https://www.hentai-foundry.com/pictures/user/LittlePaw/554706/Serpent-Girl'
|
||||
|
||||
url_match = ClientNetworkingDomain.URLMatch( name, url_type = url_type, preferred_scheme = preferred_scheme, netloc = netloc, allow_subdomains = allow_subdomains, keep_subdomains = keep_subdomains, path_components = path_components, parameters = parameters, example_url = example_url )
|
||||
|
||||
url_matches.append( url_match )
|
||||
|
||||
#
|
||||
|
||||
return url_matches
|
||||
|
||||
|
|
|
@ -460,6 +460,7 @@ def ParsePageForURLs( html, starting_url ):
|
|||
class GalleryIdentifier( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_GALLERY_IDENTIFIER
|
||||
SERIALISABLE_NAME = 'Gallery Identifier'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, site_type = None, additional_info = None ):
|
||||
|
|
|
@ -85,7 +85,9 @@ class FileDropTarget( wx.PyDropTarget ):
|
|||
|
||||
def OnDrop( self, x, y ):
|
||||
|
||||
drop_tlp = ClientGUICommon.GetXYTopTLP( x, y )
|
||||
screen_position = self._parent.ClientToScreen( ( x, y ) )
|
||||
|
||||
drop_tlp = ClientGUICommon.GetXYTopTLP( screen_position )
|
||||
my_tlp = ClientGUICommon.GetTLP( self._parent )
|
||||
|
||||
if drop_tlp == my_tlp:
|
||||
|
|
|
@ -215,6 +215,7 @@ def ParseExportPhrase( phrase ):
|
|||
class ExportFolder( HydrusSerialisable.SerialisableBaseNamed ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_EXPORT_FOLDER
|
||||
SERIALISABLE_NAME = 'Export Folder'
|
||||
SERIALISABLE_VERSION = 2
|
||||
|
||||
def __init__( self, name, path = '', export_type = HC.EXPORT_FOLDER_TYPE_REGULAR, file_search_context = None, period = 3600, phrase = None ):
|
||||
|
|
|
@ -637,6 +637,13 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
def _CheckImportFolder( self, name = None ):
|
||||
|
||||
options = self._controller.GetOptions()
|
||||
|
||||
if options[ 'pause_import_folders_sync' ]:
|
||||
|
||||
HydrusData.ShowText( 'Import folders are currently paused under the \'services\' menu. Please unpause them and try this again.' )
|
||||
|
||||
|
||||
if name is None:
|
||||
|
||||
import_folders = self._controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FOLDER )
|
||||
|
@ -1785,16 +1792,18 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
domain_manager = self._controller.network_engine.domain_manager
|
||||
|
||||
url_matches = domain_manager.GetURLMatches()
|
||||
network_contexts_to_custom_header_dicts = domain_manager.GetNetworkContextsToCustomHeaderDicts()
|
||||
|
||||
panel = ClientGUIScrolledPanelsEdit.EditNetworkContextCustomHeadersPanel( dlg, network_contexts_to_custom_header_dicts )
|
||||
panel = ClientGUIScrolledPanelsEdit.EditDomainManagerInfoPanel( dlg, url_matches, network_contexts_to_custom_header_dicts )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
network_contexts_to_custom_header_dicts = panel.GetValue()
|
||||
( url_matches, network_contexts_to_custom_header_dicts ) = panel.GetValue()
|
||||
|
||||
domain_manager.SetURLMatches( url_matches )
|
||||
domain_manager.SetNetworkContextsToCustomHeaderDicts( network_contexts_to_custom_header_dicts )
|
||||
|
||||
|
||||
|
|
|
@ -3625,7 +3625,16 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
if self._current_media is not None:
|
||||
|
||||
self.SetMedia( self._media_list.GetNext( self._current_media ) )
|
||||
try:
|
||||
|
||||
other_media = self._media_list.GetNext( self._current_media )
|
||||
|
||||
self.SetMedia( other_media )
|
||||
|
||||
except HydrusExceptions.DataMissing:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -3802,25 +3811,29 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
def wx_close():
|
||||
|
||||
if self:
|
||||
if not self:
|
||||
|
||||
wx.CallAfter( wx.MessageBox, 'All pairs have been filtered!' )
|
||||
|
||||
self._Close()
|
||||
return
|
||||
|
||||
|
||||
wx.CallAfter( wx.MessageBox, 'All pairs have been filtered!' )
|
||||
|
||||
self._Close()
|
||||
|
||||
|
||||
def wx_continue( unprocessed_pairs ):
|
||||
|
||||
if self:
|
||||
if not self:
|
||||
|
||||
self._unprocessed_pairs = unprocessed_pairs
|
||||
|
||||
self._currently_fetching_pairs = False
|
||||
|
||||
self._ShowNewPair()
|
||||
return
|
||||
|
||||
|
||||
self._unprocessed_pairs = unprocessed_pairs
|
||||
|
||||
self._currently_fetching_pairs = False
|
||||
|
||||
self._ShowNewPair()
|
||||
|
||||
|
||||
result = HG.client_controller.Read( 'unique_duplicate_pairs', self._file_service_key, HC.DUPLICATE_UNKNOWN )
|
||||
|
||||
|
@ -3961,7 +3974,10 @@ class CanvasMediaList( ClientMedia.ListeningMediaList, CanvasWithHovers ):
|
|||
|
||||
next_media = self._GetNext( self._current_media )
|
||||
|
||||
if next_media == self._current_media: next_media = None
|
||||
if next_media == self._current_media:
|
||||
|
||||
next_media = None
|
||||
|
||||
|
||||
hashes = { self._current_media.GetHash() }
|
||||
|
||||
|
@ -4050,7 +4066,10 @@ class CanvasMediaList( ClientMedia.ListeningMediaList, CanvasWithHovers ):
|
|||
|
||||
next_media = self._GetNext( self._current_media )
|
||||
|
||||
if next_media == self._current_media: next_media = None
|
||||
if next_media == self._current_media:
|
||||
|
||||
next_media = None
|
||||
|
||||
|
||||
else:
|
||||
|
||||
|
|
|
@ -65,11 +65,11 @@ def GetTLPParents( window ):
|
|||
|
||||
return parents
|
||||
|
||||
def GetXYTopTLP( x, y ):
|
||||
def GetXYTopTLP( screen_position ):
|
||||
|
||||
tlps = wx.GetTopLevelWindows()
|
||||
|
||||
hittest_tlps = [ tlp for tlp in tlps if tlp.HitTest( ( x, y ) ) == wx.HT_WINDOW_INSIDE ]
|
||||
hittest_tlps = [ tlp for tlp in tlps if tlp.HitTest( tlp.ScreenToClient( screen_position ) ) == wx.HT_WINDOW_INSIDE and tlp.IsShown() ]
|
||||
|
||||
if len( hittest_tlps ) == 0:
|
||||
|
||||
|
@ -2795,9 +2795,15 @@ class RadioBox( StaticBox ):
|
|||
|
||||
|
||||
|
||||
def SetSelection( self, index ): self._indices_to_radio_buttons[ index ].SetValue( True )
|
||||
def SetSelection( self, index ):
|
||||
|
||||
self._indices_to_radio_buttons[ index ].SetValue( True )
|
||||
|
||||
|
||||
def SetString( self, index, text ): self._indices_to_radio_buttons[ index ].SetLabelText( text )
|
||||
def SetString( self, index, text ):
|
||||
|
||||
self._indices_to_radio_buttons[ index ].SetLabelText( text )
|
||||
|
||||
|
||||
class TextAndGauge( wx.Panel ):
|
||||
|
||||
|
@ -2818,6 +2824,11 @@ class TextAndGauge( wx.Panel ):
|
|||
|
||||
def SetValue( self, text, value, range ):
|
||||
|
||||
if not self:
|
||||
|
||||
return
|
||||
|
||||
|
||||
if text != self._st.GetLabelText():
|
||||
|
||||
self._st.SetLabelText( text )
|
||||
|
|
|
@ -913,7 +913,7 @@ class FrameInputLocalFiles( wx.Frame ):
|
|||
|
||||
self._job_key = ClientThreading.JobKey()
|
||||
|
||||
HG.client_controller.CallToThread( self.THREADParseImportablePaths, paths, self._job_key )
|
||||
HG.client_controller.CallToThread( self.THREADParseImportablePaths, paths, self._job_key, self._progress_updater )
|
||||
|
||||
|
||||
|
||||
|
@ -970,6 +970,11 @@ class FrameInputLocalFiles( wx.Frame ):
|
|||
|
||||
def DoneParsing( self ):
|
||||
|
||||
if not self:
|
||||
|
||||
return
|
||||
|
||||
|
||||
self._currently_parsing = False
|
||||
|
||||
self._ProcessQueue()
|
||||
|
@ -1083,9 +1088,9 @@ class FrameInputLocalFiles( wx.Frame ):
|
|||
self._tag_button.Enable()
|
||||
|
||||
|
||||
def THREADParseImportablePaths( self, raw_paths, job_key ):
|
||||
def THREADParseImportablePaths( self, raw_paths, job_key, progress_updater ):
|
||||
|
||||
self._progress_updater.Update( 'Finding all files', None, None )
|
||||
progress_updater.Update( 'Finding all files', None, None )
|
||||
|
||||
file_paths = ClientFiles.GetAllPaths( raw_paths )
|
||||
|
||||
|
@ -1108,7 +1113,7 @@ class FrameInputLocalFiles( wx.Frame ):
|
|||
|
||||
message = 'Parsed ' + HydrusData.ConvertValueRangeToPrettyString( i, num_file_paths )
|
||||
|
||||
self._progress_updater.Update( message, i, num_file_paths )
|
||||
progress_updater.Update( message, i, num_file_paths )
|
||||
|
||||
( i_paused, should_quit ) = job_key.WaitIfNeeded()
|
||||
|
||||
|
@ -1196,7 +1201,7 @@ class FrameInputLocalFiles( wx.Frame ):
|
|||
|
||||
HydrusData.Print( message )
|
||||
|
||||
self._progress_updater.Update( message, num_file_paths, num_file_paths )
|
||||
progress_updater.Update( message, num_file_paths, num_file_paths )
|
||||
|
||||
self._done_parsing_updater.Update()
|
||||
|
||||
|
|
|
@ -2759,6 +2759,11 @@ class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
|
|||
return
|
||||
|
||||
|
||||
if not os.path.exists( path ):
|
||||
|
||||
wx.MessageBox( 'The path you have entered--"' + path + '"--does not exist! The dialog will not force you to correct it, but you should not let this import folder run until you have corrected or created it!' )
|
||||
|
||||
|
||||
if HC.BASE_DIR.startswith( path ) or HG.client_controller.GetDBDir().startswith( path ):
|
||||
|
||||
wx.MessageBox( 'You cannot set an import path that includes your install or database directory!' )
|
||||
|
@ -2766,32 +2771,72 @@ class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
|
|||
return
|
||||
|
||||
|
||||
if self._action_successful.GetChoice() == CC.IMPORT_FOLDER_MOVE and self._location_successful.GetPath() in ( '', None ):
|
||||
if self._action_successful.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
||||
|
||||
wx.MessageBox( 'You must enter a path for your successful file move location!' )
|
||||
path = self._location_successful.GetPath()
|
||||
|
||||
return
|
||||
if path in ( '', None ):
|
||||
|
||||
wx.MessageBox( 'You must enter a path for your successful file move location!' )
|
||||
|
||||
return
|
||||
|
||||
|
||||
if not os.path.exists( path ):
|
||||
|
||||
wx.MessageBox( 'The path you have entered for your successful file move location--"' + path + '"--does not exist! The dialog will not force you to correct it, but you should not let this import folder run until you have corrected or created it!' )
|
||||
|
||||
|
||||
|
||||
if self._action_redundant.GetChoice() == CC.IMPORT_FOLDER_MOVE and self._location_redundant.GetPath() in ( '', None ):
|
||||
if self._action_redundant.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
||||
|
||||
wx.MessageBox( 'You must enter a path for your redundant file move location!' )
|
||||
path = self._location_redundant.GetPath()
|
||||
|
||||
return
|
||||
if path in ( '', None ):
|
||||
|
||||
wx.MessageBox( 'You must enter a path for your redundant file move location!' )
|
||||
|
||||
return
|
||||
|
||||
|
||||
if not os.path.exists( path ):
|
||||
|
||||
wx.MessageBox( 'The path you have entered for your redundant file move location--"' + path + '"--does not exist! The dialog will not force you to correct it, but you should not let this import folder run until you have corrected or created it!' )
|
||||
|
||||
|
||||
|
||||
if self._action_deleted.GetChoice() == CC.IMPORT_FOLDER_MOVE and self._location_deleted.GetPath() in ( '', None ):
|
||||
if self._action_deleted.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
||||
|
||||
wx.MessageBox( 'You must enter a path for your deleted file move location!' )
|
||||
path = self._location_deleted.GetPath()
|
||||
|
||||
return
|
||||
if path in ( '', None ):
|
||||
|
||||
wx.MessageBox( 'You must enter a path for your deleted file move location!' )
|
||||
|
||||
return
|
||||
|
||||
|
||||
if not os.path.exists( path ):
|
||||
|
||||
wx.MessageBox( 'The path you have entered for your deleted file move location--"' + path + '"--does not exist! The dialog will not force you to correct it, but you should not let this import folder run until you have corrected or created it!' )
|
||||
|
||||
|
||||
|
||||
if self._action_failed.GetChoice() == CC.IMPORT_FOLDER_MOVE and self._location_failed.GetPath() in ( '', None ):
|
||||
if self._action_failed.GetChoice() == CC.IMPORT_FOLDER_MOVE:
|
||||
|
||||
wx.MessageBox( 'You must enter a path for your failed file move location!' )
|
||||
path = self._location_failed.GetPath()
|
||||
|
||||
return
|
||||
if path in ( '', None ):
|
||||
|
||||
wx.MessageBox( 'You must enter a path for your failed file move location!' )
|
||||
|
||||
return
|
||||
|
||||
|
||||
if not os.path.exists( path ):
|
||||
|
||||
wx.MessageBox( 'The path you have entered for your failed file move location--"' + path + '"--does not exist! The dialog will not force you to correct it, but you should not let this import folder run until you have corrected or created it!' )
|
||||
|
||||
|
||||
|
||||
self.EndModal( wx.ID_OK )
|
||||
|
|
|
@ -451,6 +451,8 @@ class FilenameTaggingOptionsPanel( wx.Panel ):
|
|||
|
||||
self._tag_box = ClientGUIACDropdown.AutoCompleteDropdownTagsWrite( self._tags_panel, self.EnterTags, expand_parents, CC.LOCAL_FILE_SERVICE_KEY, service_key )
|
||||
|
||||
self._tags_paste_button = ClientGUICommon.BetterButton( self._tags_panel, 'paste tags', self._PasteTags )
|
||||
|
||||
#
|
||||
|
||||
self._single_tags_panel = ClientGUICommon.StaticBox( self, 'tags just for selected files' )
|
||||
|
@ -459,6 +461,8 @@ class FilenameTaggingOptionsPanel( wx.Panel ):
|
|||
|
||||
self._single_tags = ClientGUIListBoxes.ListBoxTagsStringsAddRemove( self._single_tags_panel, self._service_key, self.SingleTagsRemoved )
|
||||
|
||||
self._single_tags_paste_button = ClientGUICommon.BetterButton( self._single_tags_panel, 'paste tags', self._PasteSingleTags )
|
||||
|
||||
expand_parents = True
|
||||
|
||||
self._single_tag_box = ClientGUIACDropdown.AutoCompleteDropdownTagsWrite( self._single_tags_panel, self.EnterTagsSingle, expand_parents, CC.LOCAL_FILE_SERVICE_KEY, service_key )
|
||||
|
@ -530,9 +534,11 @@ class FilenameTaggingOptionsPanel( wx.Panel ):
|
|||
|
||||
self._tags_panel.AddF( self._tags, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._tags_panel.AddF( self._tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._tags_panel.AddF( self._tags_paste_button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self._single_tags_panel.AddF( self._single_tags, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._single_tags_panel.AddF( self._single_tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._single_tags_panel.AddF( self._single_tags_paste_button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
txt_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
|
@ -586,6 +592,69 @@ class FilenameTaggingOptionsPanel( wx.Panel ):
|
|||
self._dir_checkbox_3.Bind( wx.EVT_CHECKBOX, self.EventRefresh )
|
||||
|
||||
|
||||
def _GetTagsFromClipboard( self ):
|
||||
|
||||
if wx.TheClipboard.Open():
|
||||
|
||||
data = wx.TextDataObject()
|
||||
|
||||
wx.TheClipboard.GetData( data )
|
||||
|
||||
wx.TheClipboard.Close()
|
||||
|
||||
text = data.GetText()
|
||||
|
||||
try:
|
||||
|
||||
tags = HydrusData.DeserialisePrettyTags( text )
|
||||
|
||||
tags = HydrusTags.CleanTags( tags )
|
||||
|
||||
return tags
|
||||
|
||||
except:
|
||||
|
||||
raise Exception( 'I could not understand what was in the clipboard' )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
raise Exception( 'I could not get permission to access the clipboard.' )
|
||||
|
||||
|
||||
|
||||
def _PasteTags( self ):
|
||||
|
||||
try:
|
||||
|
||||
tags = self._GetTagsFromClipboard()
|
||||
|
||||
except Exception as e:
|
||||
|
||||
wx.MessageBox( HydrusData.ToUnicode( e ) )
|
||||
|
||||
return
|
||||
|
||||
|
||||
self.EnterTags( tags )
|
||||
|
||||
|
||||
def _PasteSingleTags( self ):
|
||||
|
||||
try:
|
||||
|
||||
tags = self._GetTagsFromClipboard()
|
||||
|
||||
except Exception as e:
|
||||
|
||||
wx.MessageBox( HydrusData.ToUnicode( e ) )
|
||||
|
||||
return
|
||||
|
||||
|
||||
self.EnterTagsSingle( tags )
|
||||
|
||||
|
||||
def _ShowTXTHelp( self ):
|
||||
|
||||
message = 'If you would like to add custom tags with your files, add a .txt file beside the file like so:'
|
||||
|
@ -699,10 +768,12 @@ class FilenameTaggingOptionsPanel( wx.Panel ):
|
|||
|
||||
|
||||
self._single_tag_box.Enable()
|
||||
self._single_tags_paste_button.Enable()
|
||||
|
||||
else:
|
||||
|
||||
self._single_tag_box.Disable()
|
||||
self._single_tags_paste_button.Disable()
|
||||
|
||||
|
||||
self._single_tags.SetTags( single_tags )
|
||||
|
|
|
@ -14,6 +14,8 @@ import HydrusTags
|
|||
import os
|
||||
import wx
|
||||
|
||||
( ListBoxEvent, EVT_LIST_BOX ) = wx.lib.newevent.NewCommandEvent()
|
||||
|
||||
class QueueListBox( wx.Panel ):
|
||||
|
||||
def __init__( self, parent, data_to_pretty_callable, add_callable, edit_callable ):
|
||||
|
@ -24,7 +26,7 @@ class QueueListBox( wx.Panel ):
|
|||
|
||||
wx.Panel.__init__( self, parent )
|
||||
|
||||
self._listbox = wx.ListBox( self, style = wx.LB_MULTIPLE )
|
||||
self._listbox = wx.ListBox( self, style = wx.LB_EXTENDED )
|
||||
|
||||
self._up_button = ClientGUICommon.BetterButton( self, u'\u2191', self._Up )
|
||||
|
||||
|
@ -63,6 +65,7 @@ class QueueListBox( wx.Panel ):
|
|||
#
|
||||
|
||||
self._listbox.Bind( wx.EVT_LISTBOX, self.EventSelection )
|
||||
self._listbox.Bind( wx.EVT_LISTBOX_DCLICK, self.EventEdit )
|
||||
|
||||
|
||||
def _Add( self ):
|
||||
|
@ -84,15 +87,15 @@ class QueueListBox( wx.Panel ):
|
|||
|
||||
def _Delete( self ):
|
||||
|
||||
indices = self._listbox.GetSelections()
|
||||
|
||||
indices.sort( reverse = True )
|
||||
indices = list( self._listbox.GetSelections() )
|
||||
|
||||
if len( indices ) == 0:
|
||||
|
||||
return
|
||||
|
||||
|
||||
indices.sort( reverse = True )
|
||||
|
||||
import ClientGUIDialogs
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'Remove all selected?' ) as dlg_yn:
|
||||
|
@ -106,16 +109,18 @@ class QueueListBox( wx.Panel ):
|
|||
|
||||
|
||||
|
||||
wx.PostEvent( self.GetEventHandler(), ListBoxEvent( -1 ) )
|
||||
|
||||
|
||||
def _Down( self ):
|
||||
|
||||
indices = self._listbox.GetSelections()
|
||||
indices = list( self._listbox.GetSelections() )
|
||||
|
||||
indices.sort( reverse = True )
|
||||
|
||||
for i in indices:
|
||||
|
||||
if i > 0:
|
||||
if i < self._listbox.GetCount() - 1:
|
||||
|
||||
if not self._listbox.IsSelected( i + 1 ): # is the one below not selected?
|
||||
|
||||
|
@ -124,6 +129,8 @@ class QueueListBox( wx.Panel ):
|
|||
|
||||
|
||||
|
||||
wx.PostEvent( self.GetEventHandler(), ListBoxEvent( -1 ) )
|
||||
|
||||
|
||||
def _Edit( self ):
|
||||
|
||||
|
@ -144,7 +151,7 @@ class QueueListBox( wx.Panel ):
|
|||
|
||||
pretty_new_data = self._data_to_pretty_callable( new_data )
|
||||
|
||||
self._listbox.Set( pretty_new_data, i, new_data )
|
||||
self._listbox.Insert( pretty_new_data, i, new_data )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -152,9 +159,14 @@ class QueueListBox( wx.Panel ):
|
|||
|
||||
|
||||
|
||||
wx.PostEvent( self.GetEventHandler(), ListBoxEvent( -1 ) )
|
||||
|
||||
|
||||
def _SwapRows( self, index_a, index_b ):
|
||||
|
||||
a_was_selected = self._listbox.IsSelected( index_a )
|
||||
b_was_selected = self._listbox.IsSelected( index_b )
|
||||
|
||||
data_a = self._listbox.GetClientData( index_a )
|
||||
data_b = self._listbox.GetClientData( index_b )
|
||||
|
||||
|
@ -167,6 +179,16 @@ class QueueListBox( wx.Panel ):
|
|||
self._listbox.Delete( index_b )
|
||||
self._listbox.Insert( pretty_data_a, index_b, data_a )
|
||||
|
||||
if b_was_selected:
|
||||
|
||||
self._listbox.Select( index_a )
|
||||
|
||||
|
||||
if a_was_selected:
|
||||
|
||||
self._listbox.Select( index_b )
|
||||
|
||||
|
||||
|
||||
def _Up( self ):
|
||||
|
||||
|
@ -183,6 +205,8 @@ class QueueListBox( wx.Panel ):
|
|||
|
||||
|
||||
|
||||
wx.PostEvent( self.GetEventHandler(), ListBoxEvent( -1 ) )
|
||||
|
||||
|
||||
def AddDatas( self, datas ):
|
||||
|
||||
|
@ -191,15 +215,22 @@ class QueueListBox( wx.Panel ):
|
|||
self._AddData( data )
|
||||
|
||||
|
||||
wx.PostEvent( self.GetEventHandler(), ListBoxEvent( -1 ) )
|
||||
|
||||
|
||||
def Bind( self, event, handler ):
|
||||
|
||||
self._listbox.Bind( event, handler )
|
||||
|
||||
|
||||
def EventEdit( self, event ):
|
||||
|
||||
self._Edit()
|
||||
|
||||
|
||||
def EventSelection( self, event ):
|
||||
|
||||
if self._listbox.GetSelection() == wx.NOT_FOUND:
|
||||
if len( self._listbox.GetSelections() ) == 0:
|
||||
|
||||
self._up_button.Disable()
|
||||
self._delete_button.Disable()
|
||||
|
@ -233,8 +264,6 @@ class QueueListBox( wx.Panel ):
|
|||
return datas
|
||||
|
||||
|
||||
( ListBoxEvent, EVT_LIST_BOX ) = wx.lib.newevent.NewCommandEvent()
|
||||
|
||||
class ListBox( wx.ScrolledWindow ):
|
||||
|
||||
TEXT_X_PADDING = 3
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import ClientConstants as CC
|
||||
import ClientData
|
||||
import ClientGUICommon
|
||||
import ClientSerialisable
|
||||
import HydrusData
|
||||
import HydrusExceptions
|
||||
import HydrusGlobals as HG
|
||||
import HydrusSerialisable
|
||||
import os
|
||||
import wx
|
||||
from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin
|
||||
from wx.lib.mixins.listctrl import ColumnSorterMixin
|
||||
|
@ -923,6 +928,9 @@ class BetterListCtrlPanel( wx.Panel ):
|
|||
|
||||
self._listctrl = None
|
||||
|
||||
self._permitted_object_types = []
|
||||
self._import_add_callable = lambda x: None
|
||||
|
||||
self._button_infos = []
|
||||
|
||||
|
||||
|
@ -941,11 +949,177 @@ class BetterListCtrlPanel( wx.Panel ):
|
|||
|
||||
|
||||
|
||||
def _Duplicate( self ):
|
||||
|
||||
dupe_data = self._GetExportObject()
|
||||
|
||||
if dupe_data is not None:
|
||||
|
||||
dupe_data = dupe_data.Duplicate()
|
||||
|
||||
self._ImportObject( dupe_data )
|
||||
|
||||
|
||||
|
||||
def _ExportToClipboard( self ):
|
||||
|
||||
export_object = self._GetExportObject()
|
||||
|
||||
if export_object is not None:
|
||||
|
||||
json = export_object.DumpToString()
|
||||
|
||||
HG.client_controller.pub( 'clipboard', 'text', json )
|
||||
|
||||
|
||||
|
||||
def _ExportToPng( self ):
|
||||
|
||||
export_object = self._GetExportObject()
|
||||
|
||||
if export_object is not None:
|
||||
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientGUISerialisable
|
||||
|
||||
with ClientGUITopLevelWindows.DialogNullipotent( self, 'export to png' ) as dlg:
|
||||
|
||||
panel = ClientGUISerialisable.PngExportPanel( dlg, export_object )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
dlg.ShowModal()
|
||||
|
||||
|
||||
|
||||
|
||||
def _GetExportObject( self ):
|
||||
|
||||
to_export = HydrusSerialisable.SerialisableList()
|
||||
|
||||
for obj in self._listctrl.GetData( only_selected = True ):
|
||||
|
||||
to_export.append( obj )
|
||||
|
||||
|
||||
if len( to_export ) == 0:
|
||||
|
||||
return None
|
||||
|
||||
elif len( to_export ) == 1:
|
||||
|
||||
return to_export[0]
|
||||
|
||||
else:
|
||||
|
||||
return to_export
|
||||
|
||||
|
||||
|
||||
def _HasSelected( self ):
|
||||
|
||||
return self._listctrl.HasSelected()
|
||||
|
||||
|
||||
def _ImportFromClipboard( self ):
|
||||
|
||||
if wx.TheClipboard.Open():
|
||||
|
||||
data = wx.TextDataObject()
|
||||
|
||||
wx.TheClipboard.GetData( data )
|
||||
|
||||
wx.TheClipboard.Close()
|
||||
|
||||
raw_text = data.GetText()
|
||||
|
||||
try:
|
||||
|
||||
obj = HydrusSerialisable.CreateFromString( raw_text )
|
||||
|
||||
self._ImportObject( obj )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
wx.MessageBox( 'I could not understand what was in the clipboard' )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
wx.MessageBox( 'I could not get permission to access the clipboard.' )
|
||||
|
||||
|
||||
|
||||
def _ImportFromPng( self ):
|
||||
|
||||
with wx.FileDialog( self, 'select the png with the encoded script', wildcard = 'PNG (*.png)|*.png' ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
path = HydrusData.ToUnicode( dlg.GetPath() )
|
||||
|
||||
try:
|
||||
|
||||
payload = ClientSerialisable.LoadFromPng( path )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
wx.MessageBox( HydrusData.ToUnicode( e ) )
|
||||
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
|
||||
obj = HydrusSerialisable.CreateFromNetworkString( payload )
|
||||
|
||||
self._ImportObject( obj )
|
||||
|
||||
except:
|
||||
|
||||
wx.MessageBox( 'I could not understand what was encoded in the png!' )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _ImportObject( self, obj ):
|
||||
|
||||
bad_object_types = set()
|
||||
|
||||
if isinstance( obj, HydrusSerialisable.SerialisableList ):
|
||||
|
||||
for sub_obj in obj:
|
||||
|
||||
self._ImportObject( sub_obj )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if isinstance( obj, self._permitted_object_types ):
|
||||
|
||||
self._import_add_callable( obj )
|
||||
|
||||
else:
|
||||
|
||||
bad_object_types.add( type( obj ).__name__ )
|
||||
|
||||
|
||||
|
||||
if len( bad_object_types ) > 0:
|
||||
|
||||
message = 'The imported objects included these types:'
|
||||
message += os.linesep * 2
|
||||
message += os.linesep.join( bad_object_types )
|
||||
message += os.linesep * 2
|
||||
message += 'Whereas this control only allows:'
|
||||
message += os.linesep * 2
|
||||
message += os.linesep.join( ( o.__name__ for o in self._permitted_object_types ) )
|
||||
|
||||
wx.MessageBox( message )
|
||||
|
||||
|
||||
|
||||
def _UpdateButtons( self ):
|
||||
|
||||
for ( button, enabled_check_func ) in self._button_infos:
|
||||
|
@ -970,6 +1144,26 @@ class BetterListCtrlPanel( wx.Panel ):
|
|||
self._UpdateButtons()
|
||||
|
||||
|
||||
def AddImportExportButtons( self, permitted_object_types, import_add_callable ):
|
||||
|
||||
self._permitted_object_types = permitted_object_types
|
||||
self._import_add_callable = import_add_callable
|
||||
|
||||
export_menu_items = []
|
||||
|
||||
export_menu_items.append( ( 'normal', 'to clipboard', 'Serialise the selected data and put it on your clipboard.', self._ExportToClipboard ) )
|
||||
export_menu_items.append( ( 'normal', 'to png', 'Serialise the selected data and encode it to an image file you can easily share with other hydrus users.', self._ExportToPng ) )
|
||||
|
||||
import_menu_items = []
|
||||
|
||||
import_menu_items.append( ( 'normal', 'from clipboard', 'Load a data from text in your clipboard.', self._ImportFromClipboard ) )
|
||||
import_menu_items.append( ( 'normal', 'from png', 'Load a data from an encoded png.', self._ImportFromPng ) )
|
||||
|
||||
self.AddMenuButton( 'export', export_menu_items, enabled_only_on_selection = True )
|
||||
self.AddMenuButton( 'import', import_menu_items )
|
||||
self.AddButton( 'duplicate', self._Duplicate, enabled_only_on_selection = True )
|
||||
|
||||
|
||||
def AddMenuButton( self, label, menu_items, enabled_only_on_selection = False, enabled_check_func = None ):
|
||||
|
||||
button = ClientGUICommon.MenuButton( self, label, menu_items )
|
||||
|
|
|
@ -550,6 +550,7 @@ def GenerateDumpMultipartFormDataCTAndBody( fields ):
|
|||
class ManagementController( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_MANAGEMENT_CONTROLLER
|
||||
SERIALISABLE_NAME = 'Client Page Management Controller'
|
||||
SERIALISABLE_VERSION = 3
|
||||
|
||||
def __init__( self, page_name = 'page' ):
|
||||
|
@ -2354,7 +2355,7 @@ class ManagementPanelImporterThreadWatcher( ManagementPanelImporter ):
|
|||
|
||||
if watcher_status == '':
|
||||
|
||||
watcher_status = 'next check in ' + HydrusData.ConvertTimestampToPrettyPending( next_check_time )
|
||||
watcher_status = 'next check ' + HydrusData.ConvertTimestampToPrettyPending( next_check_time )
|
||||
|
||||
|
||||
if self._thread_pause_button.GetBitmap() != CC.GlobalBMPs.pause:
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2325,6 +2325,7 @@ class PagesNotebook( wx.Notebook ):
|
|||
class GUISession( HydrusSerialisable.SerialisableBaseNamed ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION
|
||||
SERIALISABLE_NAME = 'GUI Session'
|
||||
SERIALISABLE_VERSION = 3
|
||||
|
||||
def __init__( self, name ):
|
||||
|
|
|
@ -361,6 +361,11 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
def _Refresh( self ):
|
||||
|
||||
if not self:
|
||||
|
||||
return
|
||||
|
||||
|
||||
name = self._service.GetName()
|
||||
service_type = self._service.GetServiceType()
|
||||
|
||||
|
@ -405,6 +410,11 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
def _Refresh( self ):
|
||||
|
||||
if not self:
|
||||
|
||||
return
|
||||
|
||||
|
||||
HG.client_controller.CallToThread( self.THREADFetchInfo )
|
||||
|
||||
|
||||
|
@ -422,11 +432,13 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
def wx_code( text ):
|
||||
|
||||
if self:
|
||||
if not self:
|
||||
|
||||
self._file_info_st.SetLabelText( text )
|
||||
return
|
||||
|
||||
|
||||
self._file_info_st.SetLabelText( text )
|
||||
|
||||
|
||||
service_info = HG.client_controller.Read( 'service_info', self._service.GetServiceKey() )
|
||||
|
||||
|
@ -477,6 +489,11 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
def _Refresh( self ):
|
||||
|
||||
if not self:
|
||||
|
||||
return
|
||||
|
||||
|
||||
credentials = self._service.GetCredentials()
|
||||
|
||||
( host, port ) = credentials.GetAddress()
|
||||
|
@ -581,6 +598,11 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
def _Refresh( self ):
|
||||
|
||||
if not self:
|
||||
|
||||
return
|
||||
|
||||
|
||||
account = self._service.GetAccount()
|
||||
|
||||
account_type = account.GetAccountType()
|
||||
|
@ -843,6 +865,11 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
def _Refresh( self ):
|
||||
|
||||
if not self:
|
||||
|
||||
return
|
||||
|
||||
|
||||
service_paused = self._service.IsPaused()
|
||||
|
||||
self._sync_now_button.Disable()
|
||||
|
@ -924,51 +951,52 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
def wx_code( download_text, download_value, processing_text, processing_value, range ):
|
||||
|
||||
if self:
|
||||
if not self:
|
||||
|
||||
self._download_progress.SetValue( download_text, download_value, range )
|
||||
self._processing_progress.SetValue( processing_text, processing_value, range )
|
||||
return
|
||||
|
||||
if processing_value == download_value:
|
||||
|
||||
self._sync_now_button.Disable()
|
||||
|
||||
|
||||
self._download_progress.SetValue( download_text, download_value, range )
|
||||
self._processing_progress.SetValue( processing_text, processing_value, range )
|
||||
|
||||
if processing_value == download_value:
|
||||
|
||||
if download_value == 0:
|
||||
|
||||
self._export_updates_button.Disable()
|
||||
|
||||
else:
|
||||
|
||||
self._export_updates_button.Enable()
|
||||
|
||||
self._sync_now_button.Disable()
|
||||
|
||||
if processing_value == 0:
|
||||
|
||||
self._reset_button.Disable()
|
||||
|
||||
else:
|
||||
|
||||
self._reset_button.Enable()
|
||||
|
||||
|
||||
if download_value == 0:
|
||||
|
||||
processing_work_to_do = processing_value < download_value
|
||||
self._export_updates_button.Disable()
|
||||
|
||||
service_paused = self._service.IsPaused()
|
||||
else:
|
||||
|
||||
options = HG.client_controller.GetOptions()
|
||||
self._export_updates_button.Enable()
|
||||
|
||||
all_repo_sync_paused = options[ 'pause_repo_sync' ]
|
||||
|
||||
if processing_value == 0:
|
||||
|
||||
if service_paused or all_repo_sync_paused or not processing_work_to_do:
|
||||
|
||||
self._sync_now_button.Disable()
|
||||
|
||||
else:
|
||||
|
||||
self._sync_now_button.Enable()
|
||||
|
||||
|
||||
self._reset_button.Disable()
|
||||
|
||||
else:
|
||||
|
||||
self._reset_button.Enable()
|
||||
|
||||
|
||||
processing_work_to_do = processing_value < download_value
|
||||
|
||||
service_paused = self._service.IsPaused()
|
||||
|
||||
options = HG.client_controller.GetOptions()
|
||||
|
||||
all_repo_sync_paused = options[ 'pause_repo_sync' ]
|
||||
|
||||
if service_paused or all_repo_sync_paused or not processing_work_to_do:
|
||||
|
||||
self._sync_now_button.Disable()
|
||||
|
||||
else:
|
||||
|
||||
self._sync_now_button.Enable()
|
||||
|
||||
|
||||
|
||||
|
@ -1091,6 +1119,11 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
def _Refresh( self ):
|
||||
|
||||
if not self:
|
||||
|
||||
return
|
||||
|
||||
|
||||
HG.client_controller.CallToThread( self.THREADFetchInfo )
|
||||
|
||||
|
||||
|
@ -1194,18 +1227,20 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
def wx_code( ipfs_shares ):
|
||||
|
||||
if self:
|
||||
if not self:
|
||||
|
||||
self._ipfs_shares.DeleteAllItems()
|
||||
return
|
||||
|
||||
for ( multihash, num_files, total_size, note ) in ipfs_shares:
|
||||
|
||||
sort_tuple = ( multihash, num_files, total_size, note )
|
||||
|
||||
display_tuple = self._GetDisplayTuple( sort_tuple )
|
||||
|
||||
self._ipfs_shares.Append( display_tuple, sort_tuple )
|
||||
|
||||
|
||||
self._ipfs_shares.DeleteAllItems()
|
||||
|
||||
for ( multihash, num_files, total_size, note ) in ipfs_shares:
|
||||
|
||||
sort_tuple = ( multihash, num_files, total_size, note )
|
||||
|
||||
display_tuple = self._GetDisplayTuple( sort_tuple )
|
||||
|
||||
self._ipfs_shares.Append( display_tuple, sort_tuple )
|
||||
|
||||
|
||||
|
||||
|
@ -1240,6 +1275,11 @@ class ReviewServicePanel( wx.Panel ):
|
|||
|
||||
def _Refresh( self ):
|
||||
|
||||
if not self:
|
||||
|
||||
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.' )
|
||||
|
||||
|
||||
|
|
|
@ -206,6 +206,35 @@ class EditChooseMultiple( ClientGUIScrolledPanels.EditPanel ):
|
|||
return datas
|
||||
|
||||
|
||||
class EditDomainManagerInfoPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, url_matches, network_contexts_to_custom_header_dicts ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
self._notebook = wx.Notebook( self )
|
||||
|
||||
self._url_matches_panel = EditURLMatchesPanel( self._notebook, url_matches )
|
||||
self._network_contexts_to_custom_header_dicts_panel = EditNetworkContextCustomHeadersPanel( self._notebook, network_contexts_to_custom_header_dicts )
|
||||
|
||||
self._notebook.AddPage( self._url_matches_panel, 'url classes', select = True )
|
||||
self._notebook.AddPage( self._network_contexts_to_custom_header_dicts_panel, 'custom headers', select = False )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._notebook, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
||||
url_matches = self._url_matches_panel.GetValue()
|
||||
network_contexts_to_custom_header_dicts = self._network_contexts_to_custom_header_dicts_panel.GetValue()
|
||||
|
||||
return ( url_matches, network_contexts_to_custom_header_dicts )
|
||||
|
||||
|
||||
class EditDuplicateActionOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, duplicate_action, duplicate_action_options ):
|
||||
|
@ -1207,6 +1236,8 @@ class EditNetworkContextCustomHeadersPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
( network_context, key, value, approved, reason ) = panel.GetValue()
|
||||
|
||||
data = ( network_context, ( key, value ), approved, reason )
|
||||
|
||||
self._list_ctrl.AddDatas( ( data, ) )
|
||||
|
@ -1748,7 +1779,7 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
queries_panel = ClientGUIListCtrl.BetterListCtrlPanel( self._query_panel )
|
||||
|
||||
self._queries = ClientGUIListCtrl.BetterListCtrl( queries_panel, 'subscription_queries', 8, 20, [ ( 'query', 20 ), ( 'paused', 8 ), ( 'status', 8 ), ( 'last new file time', 20 ), ( 'last check time', 20 ), ( 'next check time', 20 ), ( 'file progress', 14 ), ( 'file summary', -1 ) ], self._ConvertQueryToListCtrlTuples, delete_key_callback = self._DeleteQuery, activation_callback = self._EditQuery )
|
||||
self._queries = ClientGUIListCtrl.BetterListCtrl( queries_panel, 'subscription_queries', 8, 20, [ ( 'query', 20 ), ( 'paused', 8 ), ( 'status', 8 ), ( 'last new file time', 20 ), ( 'last check time', 20 ), ( 'next check time', 20 ), ( 'file velocity', 20 ), ( 'file progress', 14 ), ( 'file summary', -1 ) ], self._ConvertQueryToListCtrlTuples, delete_key_callback = self._DeleteQuery, activation_callback = self._EditQuery )
|
||||
|
||||
queries_panel.SetListCtrl( self._queries )
|
||||
|
||||
|
@ -1988,6 +2019,9 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
pretty_next_check_time = query.GetNextCheckStatusString()
|
||||
|
||||
file_velocity = self._checker_options.GetRawCurrentVelocity( query.GetSeedCache(), last_check_time )
|
||||
pretty_file_velocity = self._checker_options.GetPrettyCurrentVelocity( query.GetSeedCache(), last_check_time, no_prefix = True )
|
||||
|
||||
( file_status, ( num_done, num_total ) ) = seed_cache.GetStatus()
|
||||
|
||||
file_value_range = ( num_total, num_done )
|
||||
|
@ -1995,8 +2029,8 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
pretty_file_status = file_status
|
||||
|
||||
display_tuple = ( pretty_query_text, pretty_paused, pretty_status, pretty_last_new_file_time, pretty_last_check_time, pretty_next_check_time, pretty_file_value_range, pretty_file_status )
|
||||
sort_tuple = ( query_text, paused, status, last_new_file_time, last_check_time, next_check_time, file_value_range, file_status )
|
||||
display_tuple = ( pretty_query_text, pretty_paused, pretty_status, pretty_last_new_file_time, pretty_last_check_time, pretty_next_check_time, pretty_file_velocity, pretty_file_value_range, pretty_file_status )
|
||||
sort_tuple = ( query_text, paused, status, last_new_file_time, last_check_time, next_check_time, file_velocity, file_value_range, file_status )
|
||||
|
||||
return ( display_tuple, sort_tuple )
|
||||
|
||||
|
@ -2215,7 +2249,7 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
else:
|
||||
|
||||
status = 'delaying for ' + HydrusData.ConvertTimestampToPrettyPending( self._no_work_until ) + ' because: ' + self._no_work_until_reason
|
||||
status = 'delaying ' + HydrusData.ConvertTimestampToPrettyPending( self._no_work_until, prefix = 'for' ) + ' because: ' + self._no_work_until_reason
|
||||
|
||||
|
||||
self._delay_st.SetLabelText( status )
|
||||
|
@ -2807,7 +2841,7 @@ class EditTagImportOptions( ClientGUIScrolledPanels.EditPanel ):
|
|||
return tag_import_options
|
||||
|
||||
|
||||
class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
||||
class EditURLMatchPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, url_match ):
|
||||
|
||||
|
@ -2815,6 +2849,13 @@ class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._name = wx.TextCtrl( self )
|
||||
|
||||
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 ):
|
||||
|
||||
self._url_type.Append( HC.url_type_string_lookup[ url_type ], url_type )
|
||||
|
||||
|
||||
self._preferred_scheme = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
self._preferred_scheme.Append( 'http', 'http' )
|
||||
|
@ -2822,7 +2863,8 @@ class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._netloc = wx.TextCtrl( self )
|
||||
|
||||
self._subdomain_is_important = wx.CheckBox( self )
|
||||
self._keep_subdomains= wx.CheckBox( self )
|
||||
self._allow_subdomains = wx.CheckBox( self )
|
||||
|
||||
#
|
||||
|
||||
|
@ -2834,9 +2876,15 @@ class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
parameters_panel = ClientGUICommon.StaticBox( self, 'parameters' )
|
||||
|
||||
self._parameters = ClientGUIListCtrl.BetterListCtrl( parameters_panel, 'url_match_path_components', 5, 20, [ ( 'key', 14 ), ( 'value', -1 ) ], self._ConvertParameterToListCtrlTuples, delete_key_callback = self._DeleteParameters, activation_callback = self._EditParameters )
|
||||
parameters_listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( parameters_panel )
|
||||
|
||||
# parameter buttons, do it with a new wrapper panel class
|
||||
self._parameters = ClientGUIListCtrl.BetterListCtrl( parameters_listctrl_panel, 'url_match_path_components', 5, 45, [ ( 'key', 14 ), ( 'value', -1 ) ], self._ConvertParameterToListCtrlTuples, delete_key_callback = self._DeleteParameters, activation_callback = self._EditParameters )
|
||||
|
||||
parameters_listctrl_panel.SetListCtrl( self._parameters )
|
||||
|
||||
parameters_listctrl_panel.AddButton( 'add', self._AddParameters )
|
||||
parameters_listctrl_panel.AddButton( 'edit', self._EditParameters, enabled_only_on_selection = True )
|
||||
parameters_listctrl_panel.AddButton( 'delete', self._DeleteParameters, enabled_only_on_selection = True )
|
||||
|
||||
#
|
||||
|
||||
|
@ -2844,42 +2892,65 @@ class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._example_url_matches = ClientGUICommon.BetterStaticText( self )
|
||||
|
||||
self._normalised_url = wx.TextCtrl( self )
|
||||
self._normalised_url.Disable()
|
||||
|
||||
#
|
||||
|
||||
name = url_match.GetName()
|
||||
|
||||
self._name.SetValue( name )
|
||||
|
||||
( preferred_scheme, netloc, subdomain_is_important, path_components, parameters, example_url ) = url_match.ToTuple()
|
||||
( url_type, preferred_scheme, netloc, allow_subdomains, keep_subdomains, path_components, parameters, example_url ) = url_match.ToTuple()
|
||||
|
||||
self._url_type.SelectClientData( url_type )
|
||||
|
||||
self._preferred_scheme.SelectClientData( preferred_scheme )
|
||||
|
||||
self._netloc.SetValue( netloc )
|
||||
|
||||
self._subdomain_is_important.SetValue( subdomain_is_important )
|
||||
self._allow_subdomains.SetValue( allow_subdomains )
|
||||
self._keep_subdomains.SetValue( keep_subdomains )
|
||||
|
||||
self._path_components.AddDatas( path_components )
|
||||
00
|
||||
|
||||
self._parameters.AddDatas( parameters.items() )
|
||||
|
||||
self._parameters.Sort()
|
||||
|
||||
self._example_url.SetValue( example_url )
|
||||
|
||||
example_url_width = ClientData.ConvertTextToPixelWidth( self._example_url, 75 )
|
||||
|
||||
self._example_url.SetMinSize( ( example_url_width, -1 ) )
|
||||
|
||||
self._UpdateControls()
|
||||
|
||||
#
|
||||
|
||||
path_components_panel.AddF( self._path_components, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
#
|
||||
|
||||
parameters_panel.AddF( parameters_listctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
#
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'name: ', self._name ) )
|
||||
rows.append( ( 'url type: ', self._url_type ) )
|
||||
rows.append( ( 'preferred scheme: ', self._preferred_scheme ) )
|
||||
rows.append( ( 'network location: ', self._netloc ) )
|
||||
rows.append( ( 'keep subdomains?: ', self._subdomain_is_important ) )
|
||||
rows.append( ( 'allow subdomains?: ', self._allow_subdomains ) )
|
||||
rows.append( ( 'keep subdomains?: ', self._keep_subdomains ) )
|
||||
|
||||
gridbox_1 = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'example url: ', self._example_url ) )
|
||||
rows.append( ( 'normalised url: ', self._normalised_url ) )
|
||||
|
||||
gridbox_2 = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
|
@ -2897,20 +2968,59 @@ class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._preferred_scheme.Bind( wx.EVT_CHOICE, self.EventUpdate )
|
||||
self._netloc.Bind( wx.EVT_TEXT, self.EventUpdate )
|
||||
self._subdomain_is_important.Bind( wx.EVT_CHECKBOX, self.EventUpdate )
|
||||
self.Bind( wx.EVT_CHECKBOX, self.EventUpdate )
|
||||
self._example_url.Bind( wx.EVT_TEXT, self.EventUpdate )
|
||||
self.Bind( ClientGUIListBoxes.EVT_LIST_BOX, self.EventUpdate )
|
||||
|
||||
|
||||
def _AddParameters( self ):
|
||||
|
||||
# throw up a dialog to take key text
|
||||
# warn on key conflict I guess
|
||||
with ClientGUIDialogs.DialogTextEntry( self, 'edit the key', default = 'key', allow_blank = False ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
key = dlg.GetValue()
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
# throw up a string match dialog to take value
|
||||
existing_keys = self._GetExistingKeys()
|
||||
|
||||
# add it
|
||||
if key in existing_keys:
|
||||
|
||||
wx.MessageBox( 'That key already exists!' )
|
||||
|
||||
return
|
||||
|
||||
|
||||
pass
|
||||
import ClientGUIParsing
|
||||
|
||||
string_match = ClientParsing.StringMatch()
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit value' ) as dlg:
|
||||
|
||||
panel = ClientGUIParsing.EditStringMatchPanel( dlg, string_match )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
string_match = panel.GetValue()
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
self._parameters.AddDatas( ( key, string_match ) )
|
||||
|
||||
self._parameters.Sort()
|
||||
|
||||
self._UpdateControls()
|
||||
|
||||
|
||||
def _AddPathComponent( self ):
|
||||
|
@ -2943,19 +3053,77 @@ class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def _DeleteParameters( self ):
|
||||
|
||||
# ask for certain, then do it
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'Remove all selected?' ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
self._parameters.DeleteSelected()
|
||||
|
||||
|
||||
|
||||
pass
|
||||
self._UpdateControls()
|
||||
|
||||
|
||||
def _EditParameters( self ):
|
||||
|
||||
# for each in list, throw up dialog for key, value
|
||||
# delete and readd
|
||||
# break on cancel, etc...
|
||||
# sort at the end
|
||||
selected_params = self._parameters.GetData( only_selected = True )
|
||||
|
||||
pass
|
||||
for parameter in selected_params:
|
||||
|
||||
( original_key, original_string_match ) = parameter
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, 'edit the key', default = original_key, allow_blank = False ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
key = dlg.GetValue()
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
if key != original_key:
|
||||
|
||||
existing_keys = self._GetExistingKeys()
|
||||
|
||||
if key in existing_keys:
|
||||
|
||||
wx.MessageBox( 'That key already exists!' )
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
import ClientGUIParsing
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit value' ) as dlg:
|
||||
|
||||
panel = ClientGUIParsing.EditStringMatchPanel( dlg, original_string_match )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
string_match = panel.GetValue()
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
self._parameters.DeleteDatas( ( parameter, ) )
|
||||
|
||||
new_parameter = ( key, string_match )
|
||||
|
||||
self._parameters.AddDatas( ( new_parameter, ) )
|
||||
|
||||
|
||||
self._parameters.Sort()
|
||||
|
||||
self._UpdateControls()
|
||||
|
||||
|
||||
def _EditPathComponent( self, string_match ):
|
||||
|
@ -2968,7 +3136,7 @@ class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
if dlg.Showmodal() == wx.ID_OK:
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
new_string_match = panel.GetValue()
|
||||
|
||||
|
@ -2981,23 +3149,44 @@ class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
|
||||
|
||||
def _GetExistingKeys( self ):
|
||||
|
||||
params = self._parameters.GetData()
|
||||
|
||||
keys = { key for ( key, string_match ) in params }
|
||||
|
||||
return keys
|
||||
|
||||
|
||||
def _GetValue( self ):
|
||||
|
||||
name = self._name.GetValue()
|
||||
url_type = self._url_type.GetChoice()
|
||||
preferred_scheme = self._preferred_scheme.GetChoice()
|
||||
netloc = self._netloc.GetValue()
|
||||
subdomain_is_important = self._subdomain_is_important.GetValue()
|
||||
allow_subdomains = self._allow_subdomains.GetValue()
|
||||
keep_subdomains = self._keep_subdomains.GetValue()
|
||||
path_components = self._path_components.GetData()
|
||||
parameters = self._parameters.GetData()
|
||||
parameters = dict( self._parameters.GetData() )
|
||||
example_url = self._example_url.GetValue()
|
||||
|
||||
url_match = ClientNetworkingDomain.URLMatch( name, preferred_scheme = preferred_scheme, netloc = netloc, subdomain_is_important = subdomain_is_important, path_components = path_components, parameters = parameters, example_url = example_url )
|
||||
url_match = ClientNetworkingDomain.URLMatch( name, url_type = url_type, preferred_scheme = preferred_scheme, netloc = netloc, allow_subdomains = allow_subdomains, keep_subdomains = keep_subdomains, path_components = path_components, parameters = parameters, example_url = example_url )
|
||||
|
||||
return url_match
|
||||
|
||||
|
||||
def _UpdateControls( self ):
|
||||
|
||||
if self._allow_subdomains.GetValue():
|
||||
|
||||
self._keep_subdomains.Enable()
|
||||
|
||||
else:
|
||||
|
||||
self._keep_subdomains.SetValue( False )
|
||||
self._keep_subdomains.Disable()
|
||||
|
||||
|
||||
url_match = self._GetValue()
|
||||
|
||||
try:
|
||||
|
@ -3007,6 +3196,8 @@ class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._example_url_matches.SetLabelText( 'Example matches ok!' )
|
||||
self._example_url_matches.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._normalised_url.SetValue( url_match.Normalise( self._example_url.GetValue() ) )
|
||||
|
||||
except HydrusExceptions.URLMatchException as e:
|
||||
|
||||
reason = unicode( e )
|
||||
|
@ -3014,6 +3205,8 @@ class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._example_url_matches.SetLabelText( 'Example does not match - ' + reason )
|
||||
self._example_url_matches.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
|
||||
self._normalised_url.SetValue( '' )
|
||||
|
||||
|
||||
|
||||
def EventUpdate( self, event ):
|
||||
|
@ -3039,6 +3232,143 @@ class EditURLMatch( ClientGUIScrolledPanels.EditPanel ):
|
|||
return url_match
|
||||
|
||||
|
||||
class EditURLMatchesPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, url_matches ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
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_panel.SetListCtrl( self._list_ctrl )
|
||||
|
||||
self._list_ctrl_panel.AddButton( 'add', self._Add )
|
||||
self._list_ctrl_panel.AddButton( 'edit', self._Edit, enabled_only_on_selection = True )
|
||||
self._list_ctrl_panel.AddButton( 'delete', self._Delete, enabled_only_on_selection = True )
|
||||
self._list_ctrl_panel.AddSeparator()
|
||||
self._list_ctrl_panel.AddImportExportButtons( ClientNetworkingDomain.URLMatch, self._AddURLMatch )
|
||||
self._list_ctrl_panel.AddSeparator()
|
||||
self._list_ctrl_panel.AddButton( 'add the hf examples', self._AddHFExamples )
|
||||
|
||||
self._list_ctrl.Sort( 0 )
|
||||
|
||||
#
|
||||
|
||||
self._list_ctrl.AddDatas( url_matches )
|
||||
#
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._list_ctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def _Add( self ):
|
||||
|
||||
url_match = ClientNetworkingDomain.URLMatch( 'new url class' )
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit url class' ) as dlg:
|
||||
|
||||
panel = EditURLMatchPanel( dlg, url_match )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
url_match = panel.GetValue()
|
||||
|
||||
self._AddURLMatch( url_match )
|
||||
|
||||
|
||||
|
||||
|
||||
def _AddHFExamples( self ):
|
||||
|
||||
for url_match in ClientDefaults.GetDefaultURLMatches():
|
||||
|
||||
self._AddURLMatch( url_match )
|
||||
|
||||
|
||||
|
||||
def _AddURLMatch( self, url_match ):
|
||||
|
||||
ClientGUIListCtrl.SetNonDupeName( url_match, self._GetExistingNames() )
|
||||
|
||||
self._list_ctrl.AddDatas( ( url_match, ) )
|
||||
|
||||
|
||||
def _ConvertDataToListCtrlTuples( self, url_match ):
|
||||
|
||||
name = url_match.GetName()
|
||||
example_url = url_match.GetExampleURL()
|
||||
|
||||
pretty_name = name
|
||||
pretty_example_url = example_url
|
||||
|
||||
display_tuple = ( pretty_name, pretty_example_url )
|
||||
sort_tuple = ( name, example_url )
|
||||
|
||||
return ( display_tuple, sort_tuple )
|
||||
|
||||
|
||||
def _Delete( self ):
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'Remove all selected?' ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
self._list_ctrl.DeleteSelected()
|
||||
|
||||
|
||||
|
||||
|
||||
def _Edit( self ):
|
||||
|
||||
for url_match in self._list_ctrl.GetData( only_selected = True ):
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit url class' ) as dlg:
|
||||
|
||||
panel = EditURLMatchPanel( dlg, url_match )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
self._list_ctrl.DeleteDatas( ( url_match, ) )
|
||||
|
||||
url_match = panel.GetValue()
|
||||
|
||||
ClientGUIListCtrl.SetNonDupeName( url_match, self._GetExistingNames() )
|
||||
|
||||
self._list_ctrl.AddDatas( ( url_match, ) )
|
||||
|
||||
else:
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _GetExistingNames( self ):
|
||||
|
||||
url_matches = self._list_ctrl.GetData()
|
||||
|
||||
names = { url_match.GetName() for url_match in url_matches }
|
||||
|
||||
return names
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
||||
url_matches = self._list_ctrl.GetData()
|
||||
|
||||
return url_matches
|
||||
|
||||
|
||||
class EditCheckerOptions( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, checker_options ):
|
||||
|
|
|
@ -2590,6 +2590,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._show_thumbnail_title_banner = wx.CheckBox( self )
|
||||
self._show_thumbnail_page = wx.CheckBox( self )
|
||||
|
||||
self._thumbnail_fill = wx.CheckBox( self )
|
||||
|
||||
self._thumbnail_visibility_scroll_percent = wx.SpinCtrl( self, min = 1, max = 99 )
|
||||
self._thumbnail_visibility_scroll_percent.SetToolTipString( 'Lower numbers will cause fewer scrolls, higher numbers more.' )
|
||||
|
||||
|
@ -2665,6 +2667,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
self._show_thumbnail_page.SetValue( self._new_options.GetBoolean( 'show_thumbnail_page' ) )
|
||||
|
||||
self._thumbnail_fill.SetValue( self._new_options.GetBoolean( 'thumbnail_fill' ) )
|
||||
|
||||
self._thumbnail_visibility_scroll_percent.SetValue( self._new_options.GetInteger( 'thumbnail_visibility_scroll_percent' ) )
|
||||
|
||||
self._discord_dnd_fix.SetValue( self._new_options.GetBoolean( 'discord_dnd_fix' ) )
|
||||
|
@ -2703,6 +2707,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
rows.append( ( 'Hide the preview window: ', self._hide_preview ) )
|
||||
rows.append( ( 'Show \'title\' banner on thumbnails: ', self._show_thumbnail_title_banner ) )
|
||||
rows.append( ( 'Show volume/chapter/page number on thumbnails: ', self._show_thumbnail_page ) )
|
||||
rows.append( ( 'Zoom thumbnails so they \'fill\' their space (experimental): ', self._thumbnail_fill ) )
|
||||
rows.append( ( 'Do not scroll down on key navigation if thumbnail at least this % visible: ', self._thumbnail_visibility_scroll_percent ) )
|
||||
rows.append( ( 'BUGFIX: Discord file drag-and-drop fix (works for <=10, <50MB file DnDs): ', self._discord_dnd_fix ) )
|
||||
rows.append( ( 'BUGFIX: Always show media viewer hover windows: ', self._always_show_hover_windows ) )
|
||||
|
@ -2797,6 +2802,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
self._new_options.SetBoolean( 'show_thumbnail_title_banner', self._show_thumbnail_title_banner.GetValue() )
|
||||
self._new_options.SetBoolean( 'show_thumbnail_page', self._show_thumbnail_page.GetValue() )
|
||||
self._new_options.SetBoolean( 'thumbnail_fill', self._thumbnail_fill.GetValue() )
|
||||
|
||||
self._new_options.SetInteger( 'thumbnail_visibility_scroll_percent', self._thumbnail_visibility_scroll_percent.GetValue() )
|
||||
|
||||
|
@ -5184,7 +5190,7 @@ class ManageSubscriptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
else:
|
||||
|
||||
pretty_delay = 'delaying for ' + HydrusData.ConvertTimestampToPrettyPending( no_work_until ) + ' - ' + no_work_until_reason
|
||||
pretty_delay = 'delaying ' + HydrusData.ConvertTimestampToPrettyPending( no_work_until, prefix = 'for' ) + ' - ' + no_work_until_reason
|
||||
delay = no_work_until - HydrusData.GetNow()
|
||||
|
||||
|
||||
|
|
|
@ -57,28 +57,34 @@ class EditSeedCachePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def _ConvertSeedToListCtrlTuples( self, seed ):
|
||||
|
||||
sort_tuple = self._seed_cache.GetSeedInfo( seed )
|
||||
seed_index = self._seed_cache.GetSeedIndex( seed )
|
||||
|
||||
( seed_index, seed, status, added_timestamp, last_modified_timestamp, source_timestamp, note ) = sort_tuple
|
||||
seed_data = seed.seed_data
|
||||
status = seed.status
|
||||
added = seed.created
|
||||
modified = seed.modified
|
||||
source_time = seed.source_time
|
||||
note = seed.note
|
||||
|
||||
pretty_seed_index = HydrusData.ConvertIntToPrettyString( seed_index )
|
||||
pretty_seed = HydrusData.ToUnicode( seed )
|
||||
pretty_seed_data = HydrusData.ToUnicode( seed_data )
|
||||
pretty_status = CC.status_string_lookup[ status ]
|
||||
pretty_added = HydrusData.ConvertTimestampToPrettyAgo( added_timestamp ) + ' ago'
|
||||
pretty_modified = HydrusData.ConvertTimestampToPrettyAgo( last_modified_timestamp ) + ' ago'
|
||||
pretty_added = HydrusData.ConvertTimestampToPrettyAgo( added ) + ' ago'
|
||||
pretty_modified = HydrusData.ConvertTimestampToPrettyAgo( modified ) + ' ago'
|
||||
|
||||
if source_timestamp is None:
|
||||
if source_time is None:
|
||||
|
||||
pretty_source_time = 'unknown'
|
||||
|
||||
else:
|
||||
|
||||
pretty_source_time = HydrusData.ConvertTimestampToHumanPrettyTime( source_timestamp )
|
||||
pretty_source_time = HydrusData.ConvertTimestampToHumanPrettyTime( source_time )
|
||||
|
||||
|
||||
pretty_note = note.split( os.linesep )[0]
|
||||
|
||||
display_tuple = ( pretty_seed_index, pretty_seed, pretty_status, pretty_added, pretty_modified, pretty_source_time, pretty_note )
|
||||
display_tuple = ( pretty_seed_index, pretty_seed_data, pretty_status, pretty_added, pretty_modified, pretty_source_time, pretty_note )
|
||||
sort_tuple = ( seed_index, seed_data, status, added, modified, source_time, note )
|
||||
|
||||
return ( display_tuple, sort_tuple )
|
||||
|
||||
|
@ -89,7 +95,7 @@ class EditSeedCachePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
for seed in self._list_ctrl.GetData( only_selected = True ):
|
||||
|
||||
( seed_index, seed, status, added_timestamp, last_modified_timestamp, source_timestamp, note ) = self._seed_cache.GetSeedInfo( seed )
|
||||
note = seed.note
|
||||
|
||||
if note != '':
|
||||
|
||||
|
@ -107,7 +113,7 @@ class EditSeedCachePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
|
||||
|
||||
def _CopySelectedSeeds( self ):
|
||||
def _CopySelectedSeedData( self ):
|
||||
|
||||
seeds = self._list_ctrl.GetData( only_selected = True )
|
||||
|
||||
|
@ -115,7 +121,7 @@ class EditSeedCachePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
separator = os.linesep * 2
|
||||
|
||||
text = separator.join( seeds )
|
||||
text = separator.join( ( seed.seed_data for seed in seeds ) )
|
||||
|
||||
HG.client_controller.pub( 'clipboard', 'text', text )
|
||||
|
||||
|
@ -141,9 +147,14 @@ class EditSeedCachePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def _SetSelected( self, status_to_set ):
|
||||
|
||||
seeds_to_set = self._list_ctrl.GetData( only_selected = True )
|
||||
seeds = self._list_ctrl.GetData( only_selected = True )
|
||||
|
||||
self._seed_cache.UpdateSeedsStatus( seeds_to_set, status_to_set )
|
||||
for seed in seeds:
|
||||
|
||||
seed.SetStatus( status_to_set )
|
||||
|
||||
|
||||
self._seed_cache.NotifySeedsUpdated( seeds )
|
||||
|
||||
|
||||
def _ShowMenuIfNeeded( self ):
|
||||
|
@ -152,7 +163,7 @@ class EditSeedCachePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
menu = wx.Menu()
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( self, menu, 'copy sources', 'Copy all the selected sources to clipboard.', self._CopySelectedSeeds )
|
||||
ClientGUIMenus.AppendMenuItem( self, menu, 'copy sources', 'Copy all the selected sources to clipboard.', self._CopySelectedSeedData )
|
||||
ClientGUIMenus.AppendMenuItem( self, menu, 'copy notes', 'Copy all the selected notes to clipboard.', self._CopySelectedNotes )
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -462,6 +462,11 @@ class LocationsManager( object ):
|
|||
return CC.COMBINED_LOCAL_FILE_SERVICE_KEY in self._current
|
||||
|
||||
|
||||
def IsRemote( self ):
|
||||
|
||||
return CC.COMBINED_LOCAL_FILE_SERVICE_KEY not in self._current
|
||||
|
||||
|
||||
def IsTrashed( self ):
|
||||
|
||||
return CC.TRASH_SERVICE_KEY in self._current
|
||||
|
@ -713,12 +718,21 @@ class MediaList( object ):
|
|||
|
||||
def _GetNext( self, media ):
|
||||
|
||||
if media is None: return None
|
||||
if media is None:
|
||||
|
||||
return None
|
||||
|
||||
|
||||
next_index = self._sorted_media.index( media ) + 1
|
||||
|
||||
if next_index == len( self._sorted_media ): return self._GetFirst()
|
||||
else: return self._sorted_media[ next_index ]
|
||||
if next_index == len( self._sorted_media ):
|
||||
|
||||
return self._GetFirst()
|
||||
|
||||
else:
|
||||
|
||||
return self._sorted_media[ next_index ]
|
||||
|
||||
|
||||
|
||||
def _GetPrevious( self, media ):
|
||||
|
@ -1733,13 +1747,13 @@ class MediaSingleton( Media ):
|
|||
|
||||
( volume, ) = volumes
|
||||
|
||||
title_string_append = 'volume ' + str( volume )
|
||||
title_string_append = 'volume ' + HydrusData.ToUnicode( volume )
|
||||
|
||||
else:
|
||||
|
||||
volumes_sorted = HydrusTags.SortNumericTags( volumes )
|
||||
|
||||
title_string_append = 'volumes ' + str( volumes_sorted[0] ) + '-' + str( volumes_sorted[-1] )
|
||||
title_string_append = 'volumes ' + HydrusData.ToUnicode( volumes_sorted[0] ) + '-' + HydrusData.ToUnicode( volumes_sorted[-1] )
|
||||
|
||||
|
||||
if len( title_string ) > 0: title_string += ' - ' + title_string_append
|
||||
|
@ -1752,13 +1766,13 @@ class MediaSingleton( Media ):
|
|||
|
||||
( chapter, ) = chapters
|
||||
|
||||
title_string_append = 'chapter ' + str( chapter )
|
||||
title_string_append = 'chapter ' + HydrusData.ToUnicode( chapter )
|
||||
|
||||
else:
|
||||
|
||||
chapters_sorted = HydrusTags.SortNumericTags( chapters )
|
||||
|
||||
title_string_append = 'chapters ' + str( chapters_sorted[0] ) + '-' + str( chapters_sorted[-1] )
|
||||
title_string_append = 'chapters ' + HydrusData.ToUnicode( chapters_sorted[0] ) + '-' + HydrusData.ToUnicode( chapters_sorted[-1] )
|
||||
|
||||
|
||||
if len( title_string ) > 0: title_string += ' - ' + title_string_append
|
||||
|
@ -1771,13 +1785,13 @@ class MediaSingleton( Media ):
|
|||
|
||||
( page, ) = pages
|
||||
|
||||
title_string_append = 'page ' + str( page )
|
||||
title_string_append = 'page ' + HydrusData.ToUnicode( page )
|
||||
|
||||
else:
|
||||
|
||||
pages_sorted = HydrusTags.SortNumericTags( pages )
|
||||
|
||||
title_string_append = 'pages ' + str( pages_sorted[0] ) + '-' + str( pages_sorted[-1] )
|
||||
title_string_append = 'pages ' + HydrusData.ToUnicode( pages_sorted[0] ) + '-' + HydrusData.ToUnicode( pages_sorted[-1] )
|
||||
|
||||
|
||||
if len( title_string ) > 0: title_string += ' - ' + title_string_append
|
||||
|
@ -1940,6 +1954,7 @@ class MediaResult( object ):
|
|||
class MediaSort( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_MEDIA_SORT
|
||||
SERIALISABLE_NAME = 'Media Sort'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, sort_type = None, sort_asc = None ):
|
||||
|
|
|
@ -108,6 +108,7 @@ def ConvertStatusCodeAndDataIntoExceptionInfo( status_code, data, is_hydrus_serv
|
|||
class NetworkBandwidthManager( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_BANDWIDTH_MANAGER
|
||||
SERIALISABLE_NAME = 'Bandwidth Manager'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self ):
|
||||
|
@ -511,6 +512,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class NetworkContext( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_CONTEXT
|
||||
SERIALISABLE_NAME = 'Network Context'
|
||||
SERIALISABLE_VERSION = 2
|
||||
|
||||
def __init__( self, context_type = None, context_data = None ):
|
||||
|
@ -1788,6 +1790,8 @@ class NetworkJobSubscription( NetworkJobDownloader ):
|
|||
|
||||
class NetworkJobSubscriptionTemporary( NetworkJob ):
|
||||
|
||||
# temporary because we will move to the downloader_key stuff when that is available
|
||||
|
||||
def __init__( self, subscription_key, method, url, body = None, referral_url = None, temp_path = None ):
|
||||
|
||||
self._subscription_key = subscription_key
|
||||
|
@ -1926,6 +1930,7 @@ class NetworkJobThreadWatcher( NetworkJob ):
|
|||
class NetworkSessionManager( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_SESSION_MANAGER
|
||||
SERIALISABLE_NAME = 'Session Manager'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
SESSION_TIMEOUT = 60 * 60
|
||||
|
|
|
@ -91,6 +91,7 @@ valid_str_lookup[ VALID_UNKNOWN ] = 'unknown'
|
|||
class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_DOMAIN_MANAGER
|
||||
SERIALISABLE_NAME = 'Domain Manager'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self ):
|
||||
|
@ -266,6 +267,14 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
def GetURLMatches( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return list( self._url_matches )
|
||||
|
||||
|
||||
|
||||
def IsDirty( self ):
|
||||
|
||||
with self._lock:
|
||||
|
@ -355,6 +364,18 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
def SetURLMatches( self, url_matches ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._url_matches = HydrusSerialisable.SerialisableList()
|
||||
|
||||
self._url_matches.extend( url_matches )
|
||||
|
||||
self._SetDirty()
|
||||
|
||||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_DOMAIN_MANAGER ] = NetworkDomainManager
|
||||
|
||||
class DomainValidationPopupProcess( object ):
|
||||
|
@ -430,9 +451,15 @@ class DomainValidationPopupProcess( object ):
|
|||
class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_URL_MATCH
|
||||
SERIALISABLE_NAME = 'URL Match'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, name, preferred_scheme = 'https', netloc = 'hostname.com', subdomain_is_important = False, path_components = None, parameters = None, example_url = 'https://hostname.com/post/page.php?id=123456&s=view' ):
|
||||
def __init__( self, name, url_type = None, preferred_scheme = 'https', netloc = 'hostname.com', allow_subdomains = False, keep_subdomains = False, path_components = None, parameters = None, example_url = 'https://hostname.com/post/page.php?id=123456&s=view' ):
|
||||
|
||||
if url_type is None:
|
||||
|
||||
url_type = HC.URL_TYPE_POST
|
||||
|
||||
|
||||
if path_components is None:
|
||||
|
||||
|
@ -450,14 +477,18 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
parameters[ 'id' ] = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FLEXIBLE, match_value = ClientParsing.NUMERIC, example_string = '123456' )
|
||||
|
||||
|
||||
# an edit dialog panel for this that has example url and testing of current values
|
||||
# a parent panel or something that lists all current urls in the db that match and how they will be clipped, is this ok? kind of thing.
|
||||
# if the args are not serialisable stuff, lets overwrite here
|
||||
|
||||
path_components = HydrusSerialisable.SerialisableList( path_components )
|
||||
parameters = HydrusSerialisable.SerialisableDictionary( parameters )
|
||||
|
||||
HydrusSerialisable.SerialisableBaseNamed.__init__( self, name )
|
||||
|
||||
self._url_type = url_type
|
||||
self._preferred_scheme = preferred_scheme
|
||||
self._netloc = netloc
|
||||
self._subdomain_is_important = subdomain_is_important
|
||||
self._allow_subdomains = allow_subdomains
|
||||
self._keep_subdomains = keep_subdomains
|
||||
self._path_components = path_components
|
||||
self._parameters = parameters
|
||||
|
||||
|
@ -466,7 +497,7 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
def _ClipNetLoc( self, netloc ):
|
||||
|
||||
if self._subdomain_is_important:
|
||||
if self._keep_subdomains:
|
||||
|
||||
# for domains like artistname.website.com, where removing the subdomain may break the url, we leave it alone
|
||||
|
||||
|
@ -475,7 +506,6 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
else:
|
||||
|
||||
# for domains like mediaserver4.website.com, where multiple subdomains serve the same content as the larger site
|
||||
# if the main site doesn't deliver the same content as the subdomain, then subdomain_is_important
|
||||
|
||||
netloc = self._netloc
|
||||
|
||||
|
@ -488,12 +518,12 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
serialisable_path_components = self._path_components.GetSerialisableTuple()
|
||||
serialisable_parameters = self._parameters.GetSerialisableTuple()
|
||||
|
||||
return ( self._preferred_scheme, self._netloc, self._subdomain_is_important, serialisable_path_components, serialisable_parameters, self._example_url )
|
||||
return ( self._url_type, self._preferred_scheme, self._netloc, self._allow_subdomains, self._keep_subdomains, serialisable_path_components, serialisable_parameters, self._example_url )
|
||||
|
||||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
( self._preferred_scheme, self._netloc, self._subdomain_is_important, serialisable_path_components, serialisable_parameters, self._example_url ) = serialisable_info
|
||||
( self._url_type, self._preferred_scheme, self._netloc, self._allow_subdomains, self._keep_subdomains, serialisable_path_components, serialisable_parameters, self._example_url ) = serialisable_info
|
||||
|
||||
self._path_components = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_path_components )
|
||||
self._parameters = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_parameters )
|
||||
|
@ -550,6 +580,16 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
return ConvertURLIntoDomain( self._example_url )
|
||||
|
||||
|
||||
def GetExampleURL( self ):
|
||||
|
||||
return self._example_url
|
||||
|
||||
|
||||
def GetURLType( self ):
|
||||
|
||||
return self._url_type
|
||||
|
||||
|
||||
def Normalise( self, url ):
|
||||
|
||||
p = urlparse.urlparse( url )
|
||||
|
@ -568,10 +608,22 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
def Test( self, url ):
|
||||
|
||||
# split the url into parts according to urlparse
|
||||
p = urlparse.urlparse( url )
|
||||
|
||||
# test p.netloc with netloc, taking subdomain_is_important into account
|
||||
if self._allow_subdomains:
|
||||
|
||||
if p.netloc != self._netloc and not p.netloc.endswith( '.' + self._netloc ):
|
||||
|
||||
raise HydrusExceptions.URLMatchException( p.netloc + ' (potentially excluding subdomains) did not match ' + self._netloc )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if p.netloc != self._netloc:
|
||||
|
||||
raise HydrusExceptions.URLMatchException( p.netloc + ' did not match ' + self._netloc )
|
||||
|
||||
|
||||
|
||||
url_path = p.path
|
||||
|
||||
|
@ -580,11 +632,11 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
url_path = url_path[ 1 : ]
|
||||
|
||||
|
||||
url_path_components = p.path.split( '/' )
|
||||
url_path_components = url_path.split( '/' )
|
||||
|
||||
if len( url_path_components ) < len( self._path_components ):
|
||||
|
||||
raise HydrusExceptions.URLMatchException( p.path + ' did not have ' + str( len( self._path_components ) ) + ' components' )
|
||||
raise HydrusExceptions.URLMatchException( url_path + ' did not have ' + str( len( self._path_components ) ) + ' components' )
|
||||
|
||||
|
||||
for ( url_path_component, expected_path_component ) in zip( url_path_components, self._path_components ):
|
||||
|
@ -601,23 +653,25 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
url_parameters_list = urlparse.parse_qsl( p.query )
|
||||
|
||||
if len( url_parameters_list ) < len( self._parameters ):
|
||||
url_parameters = dict( url_parameters_list )
|
||||
|
||||
if len( url_parameters ) < len( self._parameters ):
|
||||
|
||||
raise HydrusExceptions.URLMatchException( p.query + ' did not have ' + str( len( self._parameters ) ) + ' value pairs' )
|
||||
|
||||
|
||||
for ( key, url_value ) in url_parameters_list:
|
||||
for ( key, string_match ) in self._parameters.items():
|
||||
|
||||
if key not in self._parameters:
|
||||
if key not in url_parameters:
|
||||
|
||||
raise HydrusExceptions.URLMatchException( key + ' not found in ' + p.query )
|
||||
|
||||
|
||||
expected_value = self._parameters[ key ]
|
||||
value = url_parameters[ key ]
|
||||
|
||||
try:
|
||||
|
||||
expected_value.Test( url_value )
|
||||
string_match.Test( value )
|
||||
|
||||
except HydrusExceptions.StringMatchException as e:
|
||||
|
||||
|
@ -626,5 +680,10 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_URLS_IMPORT ] = URLMatch
|
||||
def ToTuple( self ):
|
||||
|
||||
return ( self._url_type, self._preferred_scheme, self._netloc, self._allow_subdomains, self._keep_subdomains, self._path_components, self._parameters, self._example_url )
|
||||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_URL_MATCH ] = URLMatch
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ class LoginCredentials( object ):
|
|||
class NetworkLoginManager( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_LOGIN_MANAGER
|
||||
SERIALISABLE_NAME = 'Login Manager'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
SESSION_TIMEOUT = 60 * 45
|
||||
|
|
|
@ -157,6 +157,7 @@ def RenderTagRule( ( name, attrs, index ) ):
|
|||
class ParseFormulaHTML( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_PARSE_FORMULA_HTML
|
||||
SERIALISABLE_NAME = 'HTML Parsing Formula'
|
||||
SERIALISABLE_VERSION = 4
|
||||
|
||||
def __init__( self, tag_rules = None, content_rule = None, string_match = None, string_converter = None ):
|
||||
|
@ -432,6 +433,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class ParseNodeContent( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_PARSE_NODE_CONTENT
|
||||
SERIALISABLE_NAME = 'Content Parsing Node'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, name = None, content_type = None, formula = None, additional_info = None ):
|
||||
|
@ -538,6 +540,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class ParseNodeContentLink( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_PARSE_NODE_CONTENT_LINK
|
||||
SERIALISABLE_NAME = 'Content Parsing Link'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, name = None, formula = None, children = None ):
|
||||
|
@ -706,6 +709,7 @@ file_identifier_string_lookup[ FILE_IDENTIFIER_TYPE_USER_INPUT ] = 'custom user
|
|||
class ParseRootFileLookup( HydrusSerialisable.SerialisableBaseNamed ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_PARSE_ROOT_FILE_LOOKUP
|
||||
SERIALISABLE_NAME = 'File Lookup Script'
|
||||
SERIALISABLE_VERSION = 2
|
||||
|
||||
def __init__( self, name, url = None, query_type = None, file_identifier_type = None, file_identifier_string_converter = None, file_identifier_arg_name = None, static_args = None, children = None ):
|
||||
|
@ -1012,6 +1016,7 @@ transformation_type_str_lookup[ STRING_TRANSFORMATION_REVERSE ] = 'reverse text'
|
|||
class StringConverter( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_STRING_CONVERTER
|
||||
SERIALISABLE_NAME = 'String Converter'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, transformations = None, example_string = None ):
|
||||
|
@ -1180,6 +1185,7 @@ NUMERIC = 2
|
|||
class StringMatch( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_STRING_MATCH
|
||||
SERIALISABLE_NAME = 'String Match'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, match_type = STRING_MATCH_ANY, match_value = '', min_chars = None, max_chars = None, example_string = 'example string' ):
|
||||
|
|
|
@ -634,8 +634,14 @@ class HydrusBitmap( object ):
|
|||
|
||||
( width, height ) = self._size
|
||||
|
||||
if self._format == wx.BitmapBufferFormat_RGB: return wx.BitmapFromBuffer( width, height, self._GetData() )
|
||||
else: return wx.BitmapFromBufferRGBA( width, height, self._GetData() )
|
||||
if self._format == wx.BitmapBufferFormat_RGB:
|
||||
|
||||
return wx.BitmapFromBuffer( width, height, self._GetData() )
|
||||
|
||||
else:
|
||||
|
||||
return wx.BitmapFromBufferRGBA( width, height, self._GetData() )
|
||||
|
||||
|
||||
|
||||
def GetWxImage( self ):
|
||||
|
@ -663,5 +669,8 @@ class HydrusBitmap( object ):
|
|||
return len( self._data )
|
||||
|
||||
|
||||
def GetSize( self ): return self._size
|
||||
def GetSize( self ):
|
||||
|
||||
return self._size
|
||||
|
||||
|
||||
|
|
|
@ -263,6 +263,7 @@ class FileQueryResult( object ):
|
|||
class FileSearchContext( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_FILE_SEARCH_CONTEXT
|
||||
SERIALISABLE_NAME = 'File Search Context'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, file_service_key = CC.COMBINED_FILE_SERVICE_KEY, tag_service_key = CC.COMBINED_TAG_SERVICE_KEY, include_current_tags = True, include_pending_tags = True, predicates = None ):
|
||||
|
@ -707,6 +708,7 @@ class FileSystemPredicates( object ):
|
|||
class Predicate( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_PREDICATE
|
||||
SERIALISABLE_NAME = 'File Search Predicate'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, predicate_type = None, value = None, inclusive = True, min_current_count = 0, min_pending_count = 0, max_current_count = None, max_pending_count = None ):
|
||||
|
|
|
@ -182,22 +182,22 @@ def DumpToPng( width, payload, title, payload_description, text, path ):
|
|||
|
||||
HydrusPaths.CleanUpTempPath( os_file_handle, temp_path )
|
||||
|
||||
|
||||
|
||||
def GetPayloadTypeString( payload_obj ):
|
||||
|
||||
if isinstance( payload_obj, HydrusSerialisable.SerialisableList ):
|
||||
|
||||
return 'A list of ' + HydrusData.ConvertIntToPrettyString( len( payload_obj ) ) + ' ' + GetPayloadTypeString( payload_obj[0] ) + 's'
|
||||
return 'A list of ' + HydrusData.ConvertIntToPrettyString( len( payload_obj ) ) + ' ' + GetPayloadTypeString( payload_obj[0] )
|
||||
|
||||
else:
|
||||
|
||||
if isinstance( payload_obj, ClientParsing.ParseRootFileLookup ):
|
||||
if isinstance( payload_obj, HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
return 'File Lookup Script'
|
||||
return payload_obj.SERIALISABLE_NAME
|
||||
|
||||
elif isinstance( payload_obj, ClientImporting.Subscription ):
|
||||
else:
|
||||
|
||||
return 'Subscription'
|
||||
return repr( type( payload_obj ) )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -494,7 +494,7 @@ class ServiceRemote( Service ):
|
|||
|
||||
if not HydrusData.TimeHasPassed( self._no_requests_until ):
|
||||
|
||||
return ( False, self._no_requests_reason + ' - next request in ' + HydrusData.ConvertTimestampToPrettyPending( self._no_requests_until ) )
|
||||
return ( False, self._no_requests_reason + ' - next request ' + HydrusData.ConvertTimestampToPrettyPending( self._no_requests_until ) )
|
||||
|
||||
|
||||
example_nj = ClientNetworking.NetworkJobHydrus( self._service_key, 'GET', self._GetBaseURL() )
|
||||
|
@ -662,7 +662,7 @@ class ServiceRestricted( ServiceRemote ):
|
|||
|
||||
def GetNextAccountSyncStatus( self ):
|
||||
|
||||
return 'next account sync in ' + HydrusData.ConvertTimestampToPrettyPending( self._next_account_sync )
|
||||
return 'next account sync ' + HydrusData.ConvertTimestampToPrettyPending( self._next_account_sync )
|
||||
|
||||
|
||||
def HasPermission( self, content_type, action ):
|
||||
|
|
|
@ -49,7 +49,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 18
|
||||
SOFTWARE_VERSION = 283
|
||||
SOFTWARE_VERSION = 284
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
@ -651,6 +651,18 @@ site_type_string_lookup[ SITE_TYPE_PIXIV_TAG ] = 'pixiv tag'
|
|||
site_type_string_lookup[ SITE_TYPE_TUMBLR ] = 'tumblr'
|
||||
site_type_string_lookup[ SITE_TYPE_THREAD_WATCHER ] = 'thread watcher'
|
||||
|
||||
URL_TYPE_POST = 0
|
||||
URL_TYPE_API = 1
|
||||
URL_TYPE_FILE = 2
|
||||
URL_TYPE_GALLERY = 3
|
||||
|
||||
url_type_string_lookup = {}
|
||||
|
||||
url_type_string_lookup[ URL_TYPE_POST ] = 'post url'
|
||||
url_type_string_lookup[ URL_TYPE_API ] = 'api url'
|
||||
url_type_string_lookup[ URL_TYPE_FILE ] = 'file url'
|
||||
url_type_string_lookup[ URL_TYPE_GALLERY ] = 'gallery url'
|
||||
|
||||
# default options
|
||||
|
||||
DEFAULT_LOCAL_FILE_PORT = 45865
|
||||
|
|
|
@ -469,7 +469,7 @@ def ConvertTimestampToPrettyExpires( timestamp ):
|
|||
else: return 'expires in ' + ' '.join( ( m, s ) )
|
||||
|
||||
|
||||
def ConvertTimestampToPrettyPending( timestamp ):
|
||||
def ConvertTimestampToPrettyPending( timestamp, prefix = 'in' ):
|
||||
|
||||
if timestamp is None: return ''
|
||||
if timestamp == 0: return 'imminent'
|
||||
|
@ -507,12 +507,17 @@ def ConvertTimestampToPrettyPending( timestamp ):
|
|||
if years == 1: y = '1 year'
|
||||
else: y = str( years ) + ' years'
|
||||
|
||||
if years > 0: return ' '.join( ( y, mo ) )
|
||||
elif months > 0: return ' '.join( ( mo, d ) )
|
||||
elif days > 0: return ' '.join( ( d, h ) )
|
||||
elif hours > 0: return ' '.join( ( h, m ) )
|
||||
elif minutes > 0: return ' '.join( ( m, s ) )
|
||||
else: return s
|
||||
if prefix != '':
|
||||
|
||||
prefix += ' '
|
||||
|
||||
|
||||
if years > 0: return prefix + ' '.join( ( y, mo ) )
|
||||
elif months > 0: return prefix + ' '.join( ( mo, d ) )
|
||||
elif days > 0: return prefix + ' '.join( ( d, h ) )
|
||||
elif hours > 0: return prefix + ' '.join( ( h, m ) )
|
||||
elif minutes > 0: return prefix + ' '.join( ( m, s ) )
|
||||
else: return prefix + s
|
||||
|
||||
def ConvertTimestampToPrettySync( timestamp ):
|
||||
|
||||
|
@ -1390,6 +1395,7 @@ sqlite3.register_adapter( Account, yaml.safe_dump )
|
|||
class AccountIdentifier( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_ACCOUNT_IDENTIFIER
|
||||
SERIALISABLE_NAME = 'Account Identifier'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
TYPE_ACCOUNT_KEY = 1
|
||||
|
|
|
@ -911,6 +911,7 @@ class AccountType( object ):
|
|||
class ClientToServerUpdate( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_CLIENT_TO_SERVER_UPDATE
|
||||
SERIALISABLE_NAME = 'Client To Server Update'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self ):
|
||||
|
@ -1012,6 +1013,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class Content( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_CONTENT
|
||||
SERIALISABLE_NAME = 'Content'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, content_type = None, content_data = None ):
|
||||
|
@ -1200,6 +1202,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class ContentUpdate( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_CONTENT_UPDATE
|
||||
SERIALISABLE_NAME = 'Content Update'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self ):
|
||||
|
@ -1334,6 +1337,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class Credentials( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_CREDENTIALS
|
||||
SERIALISABLE_NAME = 'Credentials'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, host = None, port = None, access_key = None ):
|
||||
|
@ -1509,6 +1513,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class DefinitionsUpdate( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_DEFINITIONS_UPDATE
|
||||
SERIALISABLE_NAME = 'Definitions Update'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self ):
|
||||
|
@ -1585,6 +1590,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class Metadata( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_METADATA
|
||||
SERIALISABLE_NAME = 'Metadata'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
CLIENT_DELAY = 20 * 60
|
||||
|
@ -1711,7 +1717,7 @@ class Metadata( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
update_due = self._GetNextUpdateDueTime( from_client )
|
||||
|
||||
return 'next update due in ' + HydrusData.ConvertTimestampToPrettyPending( update_due )
|
||||
return 'next update due ' + HydrusData.ConvertTimestampToPrettyPending( update_due )
|
||||
|
||||
|
||||
|
||||
|
@ -1798,7 +1804,7 @@ class Metadata( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
next_update_time = self._next_update_due + delay
|
||||
|
||||
status = 'metadata synchronised up to ' + HydrusData.ConvertTimestampToPrettyAgo( biggest_end ) + ' ago, next update due in ' + HydrusData.ConvertTimestampToPrettyPending( next_update_time )
|
||||
status = 'metadata synchronised up to ' + HydrusData.ConvertTimestampToPrettyAgo( biggest_end ) + ' ago, next update due ' + HydrusData.ConvertTimestampToPrettyPending( next_update_time )
|
||||
|
||||
|
||||
return ( num_update_hashes, status )
|
||||
|
@ -1848,6 +1854,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class Petition( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_PETITION
|
||||
SERIALISABLE_NAME = 'Petition'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, action = None, petitioner_account = None, reason = None, contents = None ):
|
||||
|
|
|
@ -72,6 +72,7 @@ def GetLocalConnection( port, https = False ):
|
|||
class BandwidthRules( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_BANDWIDTH_RULES
|
||||
SERIALISABLE_NAME = 'Bandwidth Rules'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self ):
|
||||
|
@ -280,6 +281,7 @@ HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIAL
|
|||
class BandwidthTracker( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_BANDWIDTH_TRACKER
|
||||
SERIALISABLE_NAME = 'Bandwidth Tracker'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
# I want to track and query using smaller periods even when the total time delta is larger than the next step up to increase granularity
|
||||
|
|
|
@ -67,10 +67,12 @@ def CleanUpTempPath( os_file_handle, temp_path ):
|
|||
|
||||
os.remove( temp_path )
|
||||
|
||||
except OSError:
|
||||
except OSError as e:
|
||||
|
||||
HydrusData.Print( 'Could not delete the temporary file ' + temp_path )
|
||||
|
||||
HydrusData.PrintException( e )
|
||||
|
||||
|
||||
|
||||
def ConvertAbsPathToPortablePath( abs_path, base_dir_override = None ):
|
||||
|
|
|
@ -59,6 +59,7 @@ SERIALISABLE_TYPE_NETWORK_DOMAIN_MANAGER = 53
|
|||
SERIALISABLE_TYPE_SUBSCRIPTION_QUERY = 54
|
||||
SERIALISABLE_TYPE_STRING_CONVERTER = 55
|
||||
SERIALISABLE_TYPE_FILENAME_TAGGING_OPTIONS = 56
|
||||
SERIALISABLE_TYPE_SEED = 57
|
||||
|
||||
SERIALISABLE_TYPES_TO_OBJECT_TYPES = {}
|
||||
|
||||
|
@ -103,6 +104,7 @@ def CreateFromSerialisableTuple( obj_tuple ):
|
|||
class SerialisableBase( object ):
|
||||
|
||||
SERIALISABLE_TYPE = SERIALISABLE_TYPE_BASE
|
||||
SERIALISABLE_NAME = 'Base Serialisable Object'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def _GetSerialisableInfo( self ):
|
||||
|
@ -157,6 +159,7 @@ class SerialisableBase( object ):
|
|||
class SerialisableBaseNamed( SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = SERIALISABLE_TYPE_BASE_NAMED
|
||||
SERIALISABLE_NAME = 'Named Base Serialisable Object'
|
||||
|
||||
def __init__( self, name ):
|
||||
|
||||
|
@ -194,6 +197,7 @@ class SerialisableBaseNamed( SerialisableBase ):
|
|||
class SerialisableDictionary( SerialisableBase, dict ):
|
||||
|
||||
SERIALISABLE_TYPE = SERIALISABLE_TYPE_DICTIONARY
|
||||
SERIALISABLE_NAME = 'Serialisable Dictionary'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
|
@ -288,6 +292,7 @@ SERIALISABLE_TYPES_TO_OBJECT_TYPES[ SERIALISABLE_TYPE_DICTIONARY ] = Serialisabl
|
|||
class SerialisableBytesDictionary( SerialisableBase, dict ):
|
||||
|
||||
SERIALISABLE_TYPE = SERIALISABLE_TYPE_BYTES_DICT
|
||||
SERIALISABLE_NAME = 'Serialisable Dictionary With Bytestring Key/Value Support'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
|
@ -357,6 +362,7 @@ SERIALISABLE_TYPES_TO_OBJECT_TYPES[ SERIALISABLE_TYPE_BYTES_DICT ] = Serialisabl
|
|||
class SerialisableList( SerialisableBase, list ):
|
||||
|
||||
SERIALISABLE_TYPE = SERIALISABLE_TYPE_LIST
|
||||
SERIALISABLE_NAME = 'Serialisable List'
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
|
|
|
@ -88,8 +88,14 @@ def ConvertTagToSortable( t ):
|
|||
|
||||
for character in t:
|
||||
|
||||
if character.isdecimal(): int_component += character
|
||||
else: break
|
||||
if character.isdecimal():
|
||||
|
||||
int_component += character
|
||||
|
||||
else:
|
||||
|
||||
break
|
||||
|
||||
|
||||
i += 1
|
||||
|
||||
|
@ -98,7 +104,6 @@ def ConvertTagToSortable( t ):
|
|||
|
||||
number = int( int_component )
|
||||
|
||||
|
||||
return ( number, str_component )
|
||||
|
||||
else:
|
||||
|
|
|
@ -22,55 +22,77 @@ class TestData( unittest.TestCase ):
|
|||
|
||||
for i in range( 50 ):
|
||||
|
||||
seed = os.urandom( 16 ).encode( 'hex' )
|
||||
url = 'https://wew.lad/' + os.urandom( 16 ).encode( 'hex' )
|
||||
|
||||
seed = ClientImporting.Seed( ClientImporting.SEED_TYPE_URL, url )
|
||||
|
||||
seed.source_time = one_day_before - 10
|
||||
|
||||
seed_cache.AddSeeds( ( seed, ) )
|
||||
|
||||
seed_cache.UpdateSeedSourceTime( seed, one_day_before - 10 )
|
||||
|
||||
|
||||
for i in range( 50 ):
|
||||
|
||||
seed = os.urandom( 16 ).encode( 'hex' )
|
||||
url = 'https://wew.lad/' + os.urandom( 16 ).encode( 'hex' )
|
||||
|
||||
seed = ClientImporting.Seed( ClientImporting.SEED_TYPE_URL, url )
|
||||
|
||||
seed.source_time = last_check_time - 600
|
||||
|
||||
seed_cache.AddSeeds( ( seed, ) )
|
||||
|
||||
seed_cache.UpdateSeedSourceTime( seed, last_check_time - 600 )
|
||||
|
||||
|
||||
bare_seed_cache = ClientImporting.SeedCache()
|
||||
|
||||
bare_seed_cache.AddSeeds( ( 'early', ) )
|
||||
bare_seed_cache.AddSeeds( ( 'in_time_delta', ) )
|
||||
url = 'https://wew.lad/' + 'early'
|
||||
|
||||
bare_seed_cache.UpdateSeedSourceTime( 'early', one_day_before - 10 )
|
||||
bare_seed_cache.UpdateSeedSourceTime( 'in_time_delta', one_day_before + 10 )
|
||||
seed = ClientImporting.Seed( ClientImporting.SEED_TYPE_URL, url )
|
||||
|
||||
seed.source_time = one_day_before - 10
|
||||
|
||||
bare_seed_cache.AddSeeds( ( seed, ) )
|
||||
|
||||
url = 'https://wew.lad/' + 'in_time_delta'
|
||||
|
||||
seed = ClientImporting.Seed( ClientImporting.SEED_TYPE_URL, url )
|
||||
|
||||
seed.source_time = one_day_before + 10
|
||||
|
||||
bare_seed_cache.AddSeeds( ( seed, ) )
|
||||
|
||||
busy_seed_cache = ClientImporting.SeedCache()
|
||||
|
||||
busy_seed_cache.AddSeeds( ( 'early', ) )
|
||||
url = 'https://wew.lad/' + 'early'
|
||||
|
||||
busy_seed_cache.UpdateSeedSourceTime( 'early', one_day_before - 10 )
|
||||
seed = ClientImporting.Seed( ClientImporting.SEED_TYPE_URL, url )
|
||||
|
||||
seed.source_time = one_day_before - 10
|
||||
|
||||
busy_seed_cache.AddSeeds( ( seed, ) )
|
||||
|
||||
for i in range( 8640 ):
|
||||
|
||||
seed = os.urandom( 16 ).encode( 'hex' )
|
||||
url = 'https://wew.lad/' + os.urandom( 16 ).encode( 'hex' )
|
||||
|
||||
seed = ClientImporting.Seed( ClientImporting.SEED_TYPE_URL, url )
|
||||
|
||||
seed.source_time = one_day_before + ( ( i + 1 ) * 10 ) - 1
|
||||
|
||||
busy_seed_cache.AddSeeds( ( seed, ) )
|
||||
|
||||
busy_seed_cache.UpdateSeedSourceTime( seed, one_day_before + ( ( i + 1 ) * 10 ) - 1 )
|
||||
|
||||
|
||||
new_thread_seed_cache = ClientImporting.SeedCache()
|
||||
|
||||
for i in range( 10 ):
|
||||
|
||||
seed = os.urandom( 16 ).encode( 'hex' )
|
||||
url = 'https://wew.lad/' + os.urandom( 16 ).encode( 'hex' )
|
||||
|
||||
seed = ClientImporting.Seed( ClientImporting.SEED_TYPE_URL, url )
|
||||
|
||||
seed.source_time = last_check_time - 600
|
||||
|
||||
new_thread_seed_cache.AddSeeds( ( seed, ) )
|
||||
|
||||
new_thread_seed_cache.UpdateSeedSourceTime( seed, last_check_time - 600 )
|
||||
|
||||
|
||||
# empty
|
||||
# should say ok if last_check_time is 0, so it can initialise
|
||||
|
|
Loading…
Reference in New Issue