parent
6151866d29
commit
6cd2995275
|
@ -8,6 +8,27 @@
|
|||
<div class="content">
|
||||
<h3 id="changelog"><a href="#changelog">changelog</a></h3>
|
||||
<ul>
|
||||
<li><h3 id="version_456"><a href="#version_456">version 456</a></h3></li>
|
||||
<ul>
|
||||
<li>misc:</li>
|
||||
<li>the client no longer regularly commits a full garbage collection during memory maintenance. this debug-tier operation can take up to 15s on very large clients, resulting in awful lag. various instances of forcing it after big operations complete (e.g. to encourage post-subscription memory cleanup), are now replaced with regular pauses to allow python to clean itself more granularly. this may result in temporary memory bloat for some very subscription-heavy clients, so feedback would be appreciated</li>
|
||||
<li>right-clicking on a single url import item in a 'file log' now shows you all the known hashes, parsed urls, and parsed tags for that item. I hope this will help debug some weird problems!</li>
|
||||
<li>all multi-column lists across the program now convert an enter/return key press into an 'activate' command, as if you had double-clicked. this should make it easier to, for instance, highlight a downloader or shift/ctrl select a bunch of sibling rows and mass-delete (issue #933)</li>
|
||||
<li>the subscription gap filler button now propagates file import options and tag import options from the subscription to the downloader it creates (issue #910)</li>
|
||||
<li>a new 'mpv report mode' now prints a huge amount of mpv debug information to the hydrus log when activated</li>
|
||||
<li>improved how mpv prints log messages to the hydrus log, including immediate log flushing</li>
|
||||
<li>fixed a bug that meant the hydrus server was not saving custom update period or anonymisation period for next boot. thank you for the reports, and sorry for the trouble! (issue #976)</li>
|
||||
<li>cleaned up some database savepoint handling after a serious transaction error occurs</li>
|
||||
<li>the client api now ignores any parameter with a value of null, as if it were not there, rather than moaning about invalid datatypes (issue #922)</li>
|
||||
<li>.</li>
|
||||
<li>url classes:</li>
|
||||
<li>the edit url class dialog is now broken into two notebook pages--'match rules', which strictly covers how to recognise a url, and 'options', which handles url storage, conversion, and normalisation</li>
|
||||
<li>url classes can now support single-value parameters (a parameter with just a value, not a key/value pair). if turned on, then at least one single-value parameter is required to match the url, and multiple are permitted. a checkbox in the dialog turns this on and a string match lets you determine if the url class matches the received single value params</li>
|
||||
<li>added unit tests to test the new single-value parameter matching</li>
|
||||
<li>fixed an issue where StringMatch buttons were not emitting their valueChanged signal, guess how I discovered that bug this week</li>
|
||||
<li>fixed the insertion of default parameter values when the URL Class has non-alphabetised params</li>
|
||||
<li>refactored and cleaned up some related parsing and string convertion code into new ClientString module</li>
|
||||
</ul>
|
||||
<li><h3 id="version_455"><a href="#version_455">version 455</a></h3></li>
|
||||
<ul>
|
||||
<li>misc:</li>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import gc
|
||||
import hashlib
|
||||
import os
|
||||
import psutil
|
||||
|
@ -2030,8 +2029,6 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
try:
|
||||
|
||||
gc.collect()
|
||||
|
||||
self.frame_splash_status.SetTitleText( 'shutting down gui\u2026' )
|
||||
|
||||
self.ShutdownView()
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import collections
|
||||
import gc
|
||||
import os
|
||||
import random
|
||||
import threading
|
||||
|
@ -1321,8 +1320,6 @@ class FilesMaintenanceManager( object ):
|
|||
self._idle_work_rules = HydrusNetworking.BandwidthRules()
|
||||
self._active_work_rules = HydrusNetworking.BandwidthRules()
|
||||
|
||||
self._jobs_since_last_gc_collect = 0
|
||||
|
||||
self._ReInitialiseWorkRules()
|
||||
|
||||
self._maintenance_lock = threading.Lock()
|
||||
|
@ -1748,6 +1745,8 @@ class FilesMaintenanceManager( object ):
|
|||
|
||||
try:
|
||||
|
||||
big_pauser = HydrusData.BigJobPauser( wait_time = 0.8 )
|
||||
|
||||
cleared_jobs = []
|
||||
|
||||
num_to_do = len( media_results )
|
||||
|
@ -1759,6 +1758,8 @@ class FilesMaintenanceManager( object ):
|
|||
|
||||
for ( i, media_result ) in enumerate( media_results ):
|
||||
|
||||
big_pauser.Pause()
|
||||
|
||||
hash = media_result.GetHash()
|
||||
|
||||
if job_key.IsCancelled():
|
||||
|
@ -1865,15 +1866,6 @@ class FilesMaintenanceManager( object ):
|
|||
cleared_jobs.append( ( hash, job_type, additional_data ) )
|
||||
|
||||
|
||||
self._jobs_since_last_gc_collect += 1
|
||||
|
||||
if self._jobs_since_last_gc_collect > 100:
|
||||
|
||||
gc.collect()
|
||||
|
||||
self._jobs_since_last_gc_collect = 0
|
||||
|
||||
|
||||
if len( cleared_jobs ) > 100:
|
||||
|
||||
self._controller.WriteSynchronous( 'file_maintenance_clear_jobs', cleared_jobs )
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1361,7 +1361,13 @@ class FrameGUI( ClientGUITopLevelWindows.MainFrameThatResizes ):
|
|||
|
||||
job_key.SetStatusTitle( 'sub gap downloader test' )
|
||||
|
||||
call = HydrusData.Call( HG.client_controller.pub, 'make_new_subscription_gap_downloader', ( b'', 'safebooru tag search' ), 'skirt', 2 )
|
||||
file_import_options = HG.client_controller.new_options.GetDefaultFileImportOptions( 'quiet' )
|
||||
|
||||
from hydrus.client.importing.options import TagImportOptions
|
||||
|
||||
tag_import_options = TagImportOptions.TagImportOptions( is_default = True )
|
||||
|
||||
call = HydrusData.Call( HG.client_controller.pub, 'make_new_subscription_gap_downloader', ( b'', 'safebooru tag search' ), 'skirt', file_import_options, tag_import_options, 2 )
|
||||
|
||||
call.SetLabel( 'start a new downloader for this to fill in the gap!' )
|
||||
|
||||
|
@ -4842,22 +4848,22 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
def _SwitchBoolean( self, name ):
|
||||
|
||||
if name == 'callto_report_mode':
|
||||
|
||||
HG.callto_report_mode = not HG.callto_report_mode
|
||||
|
||||
elif name == 'daemon_report_mode':
|
||||
|
||||
HG.daemon_report_mode = not HG.daemon_report_mode
|
||||
|
||||
elif name == 'cache_report_mode':
|
||||
if name == 'cache_report_mode':
|
||||
|
||||
HG.cache_report_mode = not HG.cache_report_mode
|
||||
|
||||
elif name == 'callto_report_mode':
|
||||
|
||||
HG.callto_report_mode = not HG.callto_report_mode
|
||||
|
||||
elif name == 'canvas_tile_outline_mode':
|
||||
|
||||
HG.canvas_tile_outline_mode = not HG.canvas_tile_outline_mode
|
||||
|
||||
elif name == 'daemon_report_mode':
|
||||
|
||||
HG.daemon_report_mode = not HG.daemon_report_mode
|
||||
|
||||
elif name == 'db_report_mode':
|
||||
|
||||
HG.db_report_mode = not HG.db_report_mode
|
||||
|
@ -4886,6 +4892,14 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
HG.media_load_report_mode = not HG.media_load_report_mode
|
||||
|
||||
elif name == 'mpv_report_mode':
|
||||
|
||||
HG.mpv_report_mode = not HG.mpv_report_mode
|
||||
|
||||
level = 'debug' if HG.mpv_report_mode else 'fatal'
|
||||
|
||||
self._controller.pub( 'set_mpv_log_level', level )
|
||||
|
||||
elif name == 'network_report_mode':
|
||||
|
||||
HG.network_report_mode = not HG.network_report_mode
|
||||
|
@ -5292,7 +5306,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
event.ignore() # we always ignore, as we'll close through the window through other means
|
||||
|
||||
|
||||
def CreateNewSubscriptionGapDownloader( self, gug_key_and_name, query_text, file_limit ):
|
||||
def CreateNewSubscriptionGapDownloader( self, gug_key_and_name, query_text, file_import_options, tag_import_options, file_limit ):
|
||||
|
||||
page = self._notebook.GetOrMakeGalleryDownloaderPage( desired_page_name = 'subscription gap downloaders', select_page = True )
|
||||
|
||||
|
@ -5305,7 +5319,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
multiple_gallery_import = management_controller.GetVariable( 'multiple_gallery_import' )
|
||||
|
||||
multiple_gallery_import.PendSubscriptionGapDownloader( gug_key_and_name, query_text, file_limit )
|
||||
multiple_gallery_import.PendSubscriptionGapDownloader( gug_key_and_name, query_text, file_import_options, tag_import_options, file_limit )
|
||||
|
||||
self._notebook.ShowPage( page )
|
||||
|
||||
|
@ -6094,6 +6108,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
ClientGUIMenus.AppendMenuCheckItem( report_modes, 'gui report mode', 'Have the gui report inside information, where supported.', HG.gui_report_mode, self._SwitchBoolean, 'gui_report_mode' )
|
||||
ClientGUIMenus.AppendMenuCheckItem( report_modes, 'hover window report mode', 'Have the hover windows report their show/hide logic.', HG.hover_window_report_mode, self._SwitchBoolean, 'hover_window_report_mode' )
|
||||
ClientGUIMenus.AppendMenuCheckItem( report_modes, 'media load report mode', 'Have the client report media load information, where supported.', HG.media_load_report_mode, self._SwitchBoolean, 'media_load_report_mode' )
|
||||
ClientGUIMenus.AppendMenuCheckItem( report_modes, 'mpv report mode', 'Have the client report significant mpv debug information.', HG.mpv_report_mode, self._SwitchBoolean, 'mpv_report_mode' )
|
||||
ClientGUIMenus.AppendMenuCheckItem( report_modes, 'network report mode', 'Have the network engine report new jobs.', HG.network_report_mode, self._SwitchBoolean, 'network_report_mode' )
|
||||
ClientGUIMenus.AppendMenuCheckItem( report_modes, 'pubsub report mode', 'Report info about every pubsub processed.', HG.pubsub_report_mode, self._SwitchBoolean, 'pubsub_report_mode' )
|
||||
ClientGUIMenus.AppendMenuCheckItem( report_modes, 'similar files metadata generation report mode', 'Have the phash generation routine report its progress.', HG.phash_generation_report_mode, self._SwitchBoolean, 'phash_generation_report_mode' )
|
||||
|
|
|
@ -12,8 +12,8 @@ from hydrus.core import HydrusSerialisable
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientDefaults
|
||||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientPaths
|
||||
from hydrus.client import ClientStrings
|
||||
from hydrus.client.gui import ClientGUIDialogs
|
||||
from hydrus.client.gui import ClientGUIDialogsQuick
|
||||
from hydrus.client.gui import ClientGUIFunctions
|
||||
|
@ -891,11 +891,13 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
def __init__( self, parent: QW.QWidget, url_class: ClientNetworkingDomain.URLClass ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
|
||||
self._update_already_in_progress = False # Used to avoid infinite recursion on control updates.
|
||||
|
||||
self._original_url_class = url_class
|
||||
|
||||
( url_type, preferred_scheme, netloc, path_components, parameters, api_lookup_converter, send_referral_url, referral_url_converter, example_url ) = url_class.ToTuple()
|
||||
|
||||
self._name = QW.QLineEdit( self )
|
||||
|
||||
self._url_type = ClientGUICommon.BetterChoice( self )
|
||||
|
@ -905,22 +907,22 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._url_type.addItem( HC.url_type_string_lookup[ url_type ], url_type )
|
||||
|
||||
|
||||
self._preferred_scheme = ClientGUICommon.BetterChoice( self )
|
||||
self._notebook = ClientGUICommon.BetterNotebook( self )
|
||||
|
||||
#
|
||||
|
||||
self._matching_panel = ClientGUICommon.StaticBox( self._notebook, 'matching' )
|
||||
|
||||
#
|
||||
|
||||
self._preferred_scheme = ClientGUICommon.BetterChoice( self._matching_panel )
|
||||
|
||||
self._preferred_scheme.addItem( 'http', 'http' )
|
||||
self._preferred_scheme.addItem( 'https', 'https' )
|
||||
|
||||
self._netloc = QW.QLineEdit( self )
|
||||
self._netloc = QW.QLineEdit( self._matching_panel )
|
||||
|
||||
self._alphabetise_get_parameters = QW.QCheckBox( self )
|
||||
|
||||
tt = 'Normally, to ensure the same URLs are merged, hydrus will alphabetise GET parameters as part of the normalisation process.'
|
||||
tt += os.linesep * 2
|
||||
tt += 'Almost all servers support GET params in any order. One or two do not. Uncheck this if you know there is a problem.'
|
||||
|
||||
self._alphabetise_get_parameters.setToolTip( tt )
|
||||
|
||||
self._match_subdomains = QW.QCheckBox( self )
|
||||
self._match_subdomains = QW.QCheckBox( self._matching_panel )
|
||||
|
||||
tt = 'Should this class apply to subdomains as well?'
|
||||
tt += os.linesep * 2
|
||||
|
@ -930,51 +932,15 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._match_subdomains.setToolTip( tt )
|
||||
|
||||
self._keep_matched_subdomains = QW.QCheckBox( self )
|
||||
|
||||
tt = 'Should this url keep its matched subdomains when it is normalised?'
|
||||
tt += os.linesep * 2
|
||||
tt += 'This is typically useful for direct file links that are often served on a numbered CDN subdomain like \'img3.example.com\' but are also valid on the neater main domain.'
|
||||
|
||||
self._keep_matched_subdomains.setToolTip( tt )
|
||||
|
||||
self._can_produce_multiple_files = QW.QCheckBox( self )
|
||||
|
||||
tt = 'If checked, the client will not rely on instances of this URL class to predetermine \'already in db\' or \'previously deleted\' outcomes. This is important for post types like pixiv pages (which can ultimately be manga, and represent many pages) and tweets (which can have multiple images).'
|
||||
tt += os.linesep * 2
|
||||
tt += 'Most booru-type Post URLs only produce one file per URL and should not have this checked. Checking this avoids some bad logic where the client would falsely think it if it had seen one file at the URL, it had seen them all, but it then means the client has to download those pages\' content again whenever it sees them (so it can check against the direct File URLs, which are always considered one-file each).'
|
||||
|
||||
self._can_produce_multiple_files.setToolTip( tt )
|
||||
|
||||
self._should_be_associated_with_files = QW.QCheckBox( self )
|
||||
|
||||
tt = 'If checked, the client will try to remember this url with any files it ends up importing. It will present this url in \'known urls\' ui across the program.'
|
||||
tt += os.linesep * 2
|
||||
tt += 'If this URL is a File or Post URL and the client comes across it after having already downloaded it once, it can skip the redundant download since it knows it already has (or has already deleted) the file once before.'
|
||||
tt += os.linesep * 2
|
||||
tt += 'Turning this on is only useful if the URL is non-ephemeral (i.e. the URL will produce the exact same file(s) in six months\' time). It is usually not appropriate for booru gallery or thread urls, which alter regularly, but is for static Post URLs or some fixed doujin galleries.'
|
||||
|
||||
self._should_be_associated_with_files.setToolTip( tt )
|
||||
|
||||
self._keep_fragment = QW.QCheckBox( self )
|
||||
|
||||
tt = 'If checked, fragment text will be kept. This is the component sometimes after an URL that starts with a "#", such as "#kwGFb3xhA3k8B".'
|
||||
tt += os.linesep * 2
|
||||
tt += 'This data is never sent to a server, so in normal cases should never be kept, but for some clever services such as Mega, with complicated javascript navigation, it may contain unique clientside navigation data if you open the URL in your browser.'
|
||||
tt += os.linesep * 2
|
||||
tt += 'Only turn this on if you know it is needed. For almost all sites, it only hurts the normalisation process.'
|
||||
|
||||
self._keep_fragment.setToolTip( tt )
|
||||
|
||||
#
|
||||
|
||||
path_components_panel = ClientGUICommon.StaticBox( self, 'path components' )
|
||||
path_components_panel = ClientGUICommon.StaticBox( self._matching_panel, 'path components' )
|
||||
|
||||
self._path_components = ClientGUIListBoxes.QueueListBox( path_components_panel, 6, self._ConvertPathComponentRowToString, self._AddPathComponent, self._EditPathComponent )
|
||||
|
||||
#
|
||||
|
||||
parameters_panel = ClientGUICommon.StaticBox( self, 'parameters' )
|
||||
parameters_panel = ClientGUICommon.StaticBox( self._matching_panel, 'parameters' )
|
||||
|
||||
parameters_listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( parameters_panel )
|
||||
|
||||
|
@ -988,7 +954,125 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
headers_panel = ClientGUICommon.StaticBox( self, 'header overrides' )
|
||||
( has_single_value_parameters, single_value_parameters_string_match ) = url_class.GetSingleValueParameterData()
|
||||
|
||||
self._has_single_value_parameters = QW.QCheckBox( self._matching_panel )
|
||||
|
||||
tt = 'Some URLs have parameters with just a key or a value, not a "key=value" pair. Normally these are removed on normalisation, but if you turn this on, then this URL will keep them and require at least one.'
|
||||
|
||||
self._has_single_value_parameters.setToolTip( tt )
|
||||
|
||||
self._has_single_value_parameters.setChecked( has_single_value_parameters )
|
||||
|
||||
self._single_value_parameters_string_match = ClientGUIStringControls.StringMatchButton( self._matching_panel, single_value_parameters_string_match )
|
||||
|
||||
tt = 'All single-value parameters must match this!'
|
||||
|
||||
#
|
||||
|
||||
self._notebook.addTab( self._matching_panel, 'match rules' )
|
||||
|
||||
#
|
||||
|
||||
self._options_panel = ClientGUICommon.StaticBox( self._notebook, 'options' )
|
||||
|
||||
#
|
||||
|
||||
self._keep_matched_subdomains = QW.QCheckBox( self._options_panel )
|
||||
|
||||
tt = 'Should this url keep its matched subdomains when it is normalised?'
|
||||
tt += os.linesep * 2
|
||||
tt += 'This is typically useful for direct file links that are often served on a numbered CDN subdomain like \'img3.example.com\' but are also valid on the neater main domain.'
|
||||
|
||||
self._keep_matched_subdomains.setToolTip( tt )
|
||||
|
||||
self._alphabetise_get_parameters = QW.QCheckBox( self._options_panel )
|
||||
|
||||
tt = 'Normally, to ensure the same URLs are merged, hydrus will alphabetise GET parameters as part of the normalisation process.'
|
||||
tt += os.linesep * 2
|
||||
tt += 'Almost all servers support GET params in any order. One or two do not. Uncheck this if you know there is a problem.'
|
||||
|
||||
self._alphabetise_get_parameters.setToolTip( tt )
|
||||
|
||||
self._can_produce_multiple_files = QW.QCheckBox( self._options_panel )
|
||||
|
||||
tt = 'If checked, the client will not rely on instances of this URL class to predetermine \'already in db\' or \'previously deleted\' outcomes. This is important for post types like pixiv pages (which can ultimately be manga, and represent many pages) and tweets (which can have multiple images).'
|
||||
tt += os.linesep * 2
|
||||
tt += 'Most booru-type Post URLs only produce one file per URL and should not have this checked. Checking this avoids some bad logic where the client would falsely think it if it had seen one file at the URL, it had seen them all, but it then means the client has to download those pages\' content again whenever it sees them (so it can check against the direct File URLs, which are always considered one-file each).'
|
||||
|
||||
self._can_produce_multiple_files.setToolTip( tt )
|
||||
|
||||
self._should_be_associated_with_files = QW.QCheckBox( self._options_panel )
|
||||
|
||||
tt = 'If checked, the client will try to remember this url with any files it ends up importing. It will present this url in \'known urls\' ui across the program.'
|
||||
tt += os.linesep * 2
|
||||
tt += 'If this URL is a File or Post URL and the client comes across it after having already downloaded it once, it can skip the redundant download since it knows it already has (or has already deleted) the file once before.'
|
||||
tt += os.linesep * 2
|
||||
tt += 'Turning this on is only useful if the URL is non-ephemeral (i.e. the URL will produce the exact same file(s) in six months\' time). It is usually not appropriate for booru gallery or thread urls, which alter regularly, but is for static Post URLs or some fixed doujin galleries.'
|
||||
|
||||
self._should_be_associated_with_files.setToolTip( tt )
|
||||
|
||||
self._keep_fragment = QW.QCheckBox( self._options_panel )
|
||||
|
||||
tt = 'If checked, fragment text will be kept. This is the component sometimes after an URL that starts with a "#", such as "#kwGFb3xhA3k8B".'
|
||||
tt += os.linesep * 2
|
||||
tt += 'This data is never sent to a server, so in normal cases should never be kept, but for some clever services such as Mega, with complicated javascript navigation, it may contain unique clientside navigation data if you open the URL in your browser.'
|
||||
tt += os.linesep * 2
|
||||
tt += 'Only turn this on if you know it is needed. For almost all sites, it only hurts the normalisation process.'
|
||||
|
||||
self._keep_fragment.setToolTip( tt )
|
||||
|
||||
#
|
||||
|
||||
self._referral_url_panel = ClientGUICommon.StaticBox( self._options_panel, 'referral url' )
|
||||
|
||||
self._send_referral_url = ClientGUICommon.BetterChoice( self._referral_url_panel )
|
||||
|
||||
for send_referral_url_type in ClientNetworkingDomain.SEND_REFERRAL_URL_TYPES:
|
||||
|
||||
self._send_referral_url.addItem( ClientNetworkingDomain.send_referral_url_string_lookup[ send_referral_url_type ], send_referral_url_type )
|
||||
|
||||
|
||||
tt = 'Do not change this unless you know you need to. It fixes complicated problems.'
|
||||
|
||||
self._send_referral_url.setToolTip( tt )
|
||||
|
||||
self._referral_url_converter = ClientGUIStringControls.StringConverterButton( self._referral_url_panel, referral_url_converter )
|
||||
|
||||
tt = 'This will generate a referral URL from the original URL. If the URL needs a referral URL, and you can infer what that would be from just this URL, this will let hydrus download this URL without having to previously visit the referral URL (e.g. letting the user drag-and-drop import). It also lets you set up alternate referral URLs for perculiar situations.'
|
||||
|
||||
self._referral_url_converter.setToolTip( tt )
|
||||
|
||||
self._referral_url = QW.QLineEdit( self._referral_url_panel )
|
||||
self._referral_url.setReadOnly( True )
|
||||
|
||||
#
|
||||
|
||||
self._api_url_panel = ClientGUICommon.StaticBox( self._options_panel, 'api url' )
|
||||
|
||||
self._api_lookup_converter = ClientGUIStringControls.StringConverterButton( self._api_url_panel, api_lookup_converter )
|
||||
|
||||
tt = 'This will let you generate an alternate URL for the client to use for the actual download whenever it encounters a URL in this class. You must have a separate URL class to match the API type (which will link to parsers).'
|
||||
|
||||
self._api_lookup_converter.setToolTip( tt )
|
||||
|
||||
self._api_url = QW.QLineEdit( self._api_url_panel )
|
||||
self._api_url.setReadOnly( True )
|
||||
|
||||
#
|
||||
|
||||
self._next_gallery_page_panel = ClientGUICommon.StaticBox( self._options_panel, 'next gallery page' )
|
||||
|
||||
self._next_gallery_page_choice = ClientGUICommon.BetterChoice( self._next_gallery_page_panel )
|
||||
|
||||
self._next_gallery_page_delta = QP.MakeQSpinBox( self._next_gallery_page_panel, min=1, max=65536 )
|
||||
|
||||
self._next_gallery_page_url = QW.QLineEdit( self._next_gallery_page_panel )
|
||||
self._next_gallery_page_url.setReadOnly( True )
|
||||
|
||||
#
|
||||
|
||||
headers_panel = ClientGUICommon.StaticBox( self._options_panel, 'header overrides' )
|
||||
|
||||
header_overrides = url_class.GetHeaderOverrides()
|
||||
|
||||
|
@ -996,11 +1080,7 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
self._next_gallery_page_panel = ClientGUICommon.StaticBox( self, 'next gallery page' )
|
||||
|
||||
self._next_gallery_page_choice = ClientGUICommon.BetterChoice( self._next_gallery_page_panel )
|
||||
|
||||
self._next_gallery_page_delta = QP.MakeQSpinBox( self._next_gallery_page_panel, min=1, max=65536 )
|
||||
self._notebook.addTab( self._options_panel, 'options' )
|
||||
|
||||
#
|
||||
|
||||
|
@ -1017,40 +1097,6 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._normalised_url.setToolTip( tt )
|
||||
|
||||
( url_type, preferred_scheme, netloc, path_components, parameters, api_lookup_converter, send_referral_url, referral_url_converter, example_url ) = url_class.ToTuple()
|
||||
|
||||
self._send_referral_url = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
for send_referral_url_type in ClientNetworkingDomain.SEND_REFERRAL_URL_TYPES:
|
||||
|
||||
self._send_referral_url.addItem( ClientNetworkingDomain.send_referral_url_string_lookup[ send_referral_url_type ], send_referral_url_type )
|
||||
|
||||
|
||||
tt = 'Do not change this unless you know you need to. It fixes complicated problems.'
|
||||
|
||||
self._send_referral_url.setToolTip( tt )
|
||||
|
||||
self._referral_url_converter = ClientGUIStringControls.StringConverterButton( self, referral_url_converter )
|
||||
|
||||
tt = 'This will generate a referral URL from the original URL. If the URL needs a referral URL, and you can infer what that would be from just this URL, this will let hydrus download this URL without having to previously visit the referral URL (e.g. letting the user drag-and-drop import). It also lets you set up alternate referral URLs for perculiar situations.'
|
||||
|
||||
self._referral_url_converter.setToolTip( tt )
|
||||
|
||||
self._referral_url = QW.QLineEdit()
|
||||
self._referral_url.setReadOnly( True )
|
||||
|
||||
self._api_lookup_converter = ClientGUIStringControls.StringConverterButton( self, api_lookup_converter )
|
||||
|
||||
tt = 'This will let you generate an alternate URL for the client to use for the actual download whenever it encounters a URL in this class. You must have a separate URL class to match the API type (which will link to parsers).'
|
||||
|
||||
self._api_lookup_converter.setToolTip( tt )
|
||||
|
||||
self._api_url = QW.QLineEdit( self )
|
||||
self._api_url.setReadOnly( True )
|
||||
|
||||
self._next_gallery_page_url = QW.QLineEdit( self )
|
||||
self._next_gallery_page_url.setReadOnly( True )
|
||||
|
||||
#
|
||||
|
||||
name = url_class.GetName()
|
||||
|
@ -1074,7 +1120,7 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._path_components.AddDatas( path_components )
|
||||
|
||||
self._parameters.AddDatas( list(parameters.items()) )
|
||||
self._parameters.AddDatas( list( parameters.items() ) )
|
||||
|
||||
self._parameters.Sort()
|
||||
|
||||
|
@ -1106,7 +1152,29 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
headers_panel.Add( self._header_overrides, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
headers_panel.Add( self._header_overrides, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
#
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'preferred scheme: ', self._preferred_scheme ) )
|
||||
rows.append( ( 'network location: ', self._netloc ) )
|
||||
rows.append( ( 'should subdomains also match this class?: ', self._match_subdomains ) )
|
||||
|
||||
gridbox_1 = ClientGUICommon.WrapInGrid( self._matching_panel, rows )
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'has single-value parameter(s): ', self._has_single_value_parameters ) )
|
||||
rows.append( ( 'string match for single-value parameters: ', self._single_value_parameters_string_match ) )
|
||||
|
||||
gridbox_2 = ClientGUICommon.WrapInGrid( self._matching_panel, rows )
|
||||
|
||||
self._matching_panel.Add( gridbox_1, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._matching_panel.Add( path_components_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._matching_panel.Add( parameters_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._matching_panel.Add( gridbox_2, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
|
@ -1115,7 +1183,49 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
QP.AddToLayout( hbox, self._next_gallery_page_choice, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
QP.AddToLayout( hbox, self._next_gallery_page_delta, CC.FLAGS_CENTER_PERPENDICULAR )
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'next gallery page url: ', self._next_gallery_page_url ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._next_gallery_page_panel, rows )
|
||||
|
||||
self._next_gallery_page_panel.Add( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
self._next_gallery_page_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'send referral url?: ', self._send_referral_url ) )
|
||||
rows.append( ( 'optional referral url converter: ', self._referral_url_converter ) )
|
||||
rows.append( ( 'referral url: ', self._referral_url ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._referral_url_panel, rows )
|
||||
|
||||
self._referral_url_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'optional api url converter: ', self._api_lookup_converter ) )
|
||||
rows.append( ( 'api url: ', self._api_url ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._api_url_panel, rows )
|
||||
|
||||
self._api_url_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'if matching by subdomain, keep it when normalising?: ', self._keep_matched_subdomains ) )
|
||||
rows.append( ( 'alphabetise GET parameters when normalising?: ', self._alphabetise_get_parameters ) )
|
||||
rows.append( ( 'keep fragment when normalising?: ', self._keep_fragment ) )
|
||||
rows.append( ( 'post page can produce multiple files?: ', self._can_produce_multiple_files ) )
|
||||
rows.append( ( 'associate a \'known url\' with resulting files?: ', self._should_be_associated_with_files ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._options_panel, rows )
|
||||
|
||||
self._options_panel.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._options_panel.Add( self._api_url_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._options_panel.Add( self._referral_url_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._options_panel.Add( self._next_gallery_page_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._options_panel.Add( headers_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
#
|
||||
|
||||
|
@ -1123,37 +1233,20 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
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( ( 'should alphabetise GET parameters when normalising?: ', self._alphabetise_get_parameters ) )
|
||||
rows.append( ( 'should subdomains also match this class?: ', self._match_subdomains ) )
|
||||
rows.append( ( 'keep those subdomains when normalising?: ', self._keep_matched_subdomains ) )
|
||||
rows.append( ( 'post page can produce multiple files?: ', self._can_produce_multiple_files ) )
|
||||
rows.append( ( 'associate a \'known url\' with resulting files?: ', self._should_be_associated_with_files ) )
|
||||
rows.append( ( 'keep fragment?: ', self._keep_fragment ) )
|
||||
|
||||
gridbox_1 = ClientGUICommon.WrapInGrid( self, rows )
|
||||
gridbox_1 = ClientGUICommon.WrapInGrid( self._matching_panel, rows )
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'example url: ', self._example_url ) )
|
||||
rows.append( ( 'normalised url: ', self._normalised_url ) )
|
||||
rows.append( ( 'send referral url?: ', self._send_referral_url ) )
|
||||
rows.append( ( 'optional referral url converter: ', self._referral_url_converter ) )
|
||||
rows.append( ( 'referral url: ', self._referral_url ) )
|
||||
rows.append( ( 'optional api url converter: ', self._api_lookup_converter ) )
|
||||
rows.append( ( 'api url: ', self._api_url ) )
|
||||
rows.append( ( 'next gallery page url: ', self._next_gallery_page_url ) )
|
||||
|
||||
gridbox_2 = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
vbox = QP.VBoxLayout()
|
||||
|
||||
QP.AddToLayout( vbox, gridbox_1, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
QP.AddToLayout( vbox, path_components_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
QP.AddToLayout( vbox, parameters_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
QP.AddToLayout( vbox, headers_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
QP.AddToLayout( vbox, self._next_gallery_page_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
QP.AddToLayout( vbox, self._notebook, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
QP.AddToLayout( vbox, self._example_url_classes, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
QP.AddToLayout( vbox, gridbox_2, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
@ -1176,6 +1269,8 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._send_referral_url.currentIndexChanged.connect( self._UpdateControls )
|
||||
self._referral_url_converter.valueChanged.connect( self._UpdateControls )
|
||||
self._api_lookup_converter.valueChanged.connect( self._UpdateControls )
|
||||
self._has_single_value_parameters.clicked.connect( self._UpdateControls )
|
||||
self._single_value_parameters_string_match.valueChanged.connect( self._UpdateControls )
|
||||
|
||||
self._should_be_associated_with_files.clicked.connect( self.EventAssociationUpdate )
|
||||
|
||||
|
@ -1203,7 +1298,7 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return
|
||||
|
||||
|
||||
string_match = ClientParsing.StringMatch()
|
||||
string_match = ClientStrings.StringMatch()
|
||||
|
||||
with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit value' ) as dlg:
|
||||
|
||||
|
@ -1257,7 +1352,7 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def _AddPathComponent( self ):
|
||||
|
||||
string_match = ClientParsing.StringMatch()
|
||||
string_match = ClientStrings.StringMatch()
|
||||
default = None
|
||||
|
||||
return self._EditPathComponent( ( string_match, default ) )
|
||||
|
@ -1464,6 +1559,8 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
netloc = self._netloc.text()
|
||||
path_components = self._path_components.GetData()
|
||||
parameters = dict( self._parameters.GetData() )
|
||||
has_single_value_parameters = self._has_single_value_parameters.isChecked()
|
||||
single_value_parameters_string_match = self._single_value_parameters_string_match.GetValue()
|
||||
header_overrides = self._header_overrides.GetValue()
|
||||
api_lookup_converter = self._api_lookup_converter.GetValue()
|
||||
send_referral_url = self._send_referral_url.GetValue()
|
||||
|
@ -1482,6 +1579,8 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
netloc = netloc,
|
||||
path_components = path_components,
|
||||
parameters = parameters,
|
||||
has_single_value_parameters = has_single_value_parameters,
|
||||
single_value_parameters_string_match = single_value_parameters_string_match,
|
||||
header_overrides = header_overrides,
|
||||
api_lookup_converter = api_lookup_converter,
|
||||
send_referral_url = send_referral_url,
|
||||
|
@ -1568,6 +1667,8 @@ class EditURLClassPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._next_gallery_page_panel.setEnabled( False )
|
||||
|
||||
|
||||
self._single_value_parameters_string_match.setEnabled( self._has_single_value_parameters.isChecked() )
|
||||
|
||||
#
|
||||
|
||||
url_class = self._GetValue()
|
||||
|
|
|
@ -27,6 +27,7 @@ from hydrus.client.gui.lists import ClientGUIListCtrl
|
|||
from hydrus.client.gui.widgets import ClientGUICommon
|
||||
from hydrus.client.importing import ClientImportFileSeeds
|
||||
from hydrus.client.importing.options import FileImportOptions
|
||||
from hydrus.client.metadata import ClientTagSorting
|
||||
|
||||
def GetRetryIgnoredParam( window ):
|
||||
|
||||
|
@ -199,6 +200,83 @@ class EditFileSeedCachePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
ClientGUIMenus.AppendMenuItem( menu, 'copy sources', 'Copy all the selected sources to clipboard.', self._CopySelectedFileSeedData )
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'copy notes', 'Copy all the selected notes to clipboard.', self._CopySelectedNotes )
|
||||
|
||||
if len( selected_file_seeds ) == 1:
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
( selected_file_seed, ) = selected_file_seeds
|
||||
|
||||
hash_types_to_hashes = selected_file_seed.GetHashTypesToHashes()
|
||||
|
||||
if len( hash_types_to_hashes ) == 0:
|
||||
|
||||
ClientGUIMenus.AppendMenuLabel( menu, 'no hashes yet' )
|
||||
|
||||
else:
|
||||
|
||||
hash_submenu = QW.QMenu( menu )
|
||||
|
||||
for hash_type in ( 'sha256', 'md5', 'sha1', 'sha512' ):
|
||||
|
||||
if hash_type in hash_types_to_hashes:
|
||||
|
||||
h = hash_types_to_hashes[ hash_type ]
|
||||
|
||||
ClientGUIMenus.AppendMenuLabel( hash_submenu, '{}:{}'.format( hash_type, h.hex() ) )
|
||||
|
||||
|
||||
|
||||
ClientGUIMenus.AppendMenu( menu, hash_submenu, 'hashes' )
|
||||
|
||||
|
||||
#
|
||||
|
||||
if selected_file_seed.IsURLFileImport():
|
||||
|
||||
urls = sorted( selected_file_seed.GetURLs() )
|
||||
|
||||
if len( urls ) == 0:
|
||||
|
||||
ClientGUIMenus.AppendMenuLabel( menu, 'no parsed urls' )
|
||||
|
||||
else:
|
||||
|
||||
url_submenu = QW.QMenu( menu )
|
||||
|
||||
for url in urls:
|
||||
|
||||
ClientGUIMenus.AppendMenuLabel( url_submenu, url )
|
||||
|
||||
|
||||
ClientGUIMenus.AppendMenu( menu, url_submenu, 'parsed urls' )
|
||||
|
||||
|
||||
#
|
||||
|
||||
tags = list( selected_file_seed.GetExternalTags() )
|
||||
|
||||
tag_sort = ClientTagSorting.TagSort( sort_type = ClientTagSorting.SORT_BY_HUMAN_TAG, sort_order = CC.SORT_ASC )
|
||||
|
||||
ClientTagSorting.SortTags( tag_sort, tags )
|
||||
|
||||
if len( tags ) == 0:
|
||||
|
||||
ClientGUIMenus.AppendMenuLabel( menu, 'no parsed tags' )
|
||||
|
||||
else:
|
||||
|
||||
tag_submenu = QW.QMenu( menu )
|
||||
|
||||
for tag in tags:
|
||||
|
||||
ClientGUIMenus.AppendMenuLabel( tag_submenu, tag )
|
||||
|
||||
|
||||
ClientGUIMenus.AppendMenu( menu, tag_submenu, 'parsed tags' )
|
||||
|
||||
|
||||
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'open sources', 'Open all the selected sources in your file explorer or web browser.', self._OpenSelectedFileSeedData )
|
||||
|
|
|
@ -67,6 +67,10 @@ def GetClientAPIVersionString():
|
|||
|
||||
'''
|
||||
|
||||
def log_handler( loglevel, component, message ):
|
||||
|
||||
HydrusData.DebugPrint( '[{}] {}: {}'.format( loglevel, component, message ) )
|
||||
|
||||
#Not sure how well this works with hardware acceleration. This just renders to a QWidget. In my tests it seems fine, even with vdpau video out, but I'm not 100% sure it actually uses hardware acceleration.
|
||||
#Here is an example on how to render into a QOpenGLWidget instead: https://gist.github.com/cosven/b313de2acce1b7e15afda263779c0afc
|
||||
class mpvWidget( QW.QWidget ):
|
||||
|
@ -88,8 +92,10 @@ class mpvWidget( QW.QWidget ):
|
|||
self.setAttribute( QC.Qt.WA_DontCreateNativeAncestors )
|
||||
self.setAttribute( QC.Qt.WA_NativeWindow )
|
||||
|
||||
loglevel = 'debug' if HG.mpv_report_mode else 'fatal'
|
||||
|
||||
# loglevels: fatal, error, debug
|
||||
self._player = mpv.MPV( wid = str( int( self.winId() ) ), log_handler = print, loglevel = 'fatal' )
|
||||
self._player = mpv.MPV( wid = str( int( self.winId() ) ), log_handler = log_handler, loglevel = loglevel )
|
||||
|
||||
# hydev notes on OSC:
|
||||
# OSC is by default off, default input bindings are by default off
|
||||
|
@ -130,6 +136,7 @@ class mpvWidget( QW.QWidget ):
|
|||
HG.client_controller.sub( self, 'UpdateAudioMute', 'new_audio_mute' )
|
||||
HG.client_controller.sub( self, 'UpdateAudioVolume', 'new_audio_volume' )
|
||||
HG.client_controller.sub( self, 'UpdateConf', 'notify_new_options' )
|
||||
HG.client_controller.sub( self, 'SetLogLevel', 'set_mpv_log_level' )
|
||||
|
||||
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [], catch_mouse = True )
|
||||
|
||||
|
@ -456,6 +463,11 @@ class mpvWidget( QW.QWidget ):
|
|||
self._my_shortcut_handler.SetShortcuts( [ shortcut_set ] )
|
||||
|
||||
|
||||
def SetLogLevel( self, level: str ):
|
||||
|
||||
self._player.set_loglevel( level )
|
||||
|
||||
|
||||
def SetMedia( self, media, start_paused = False ):
|
||||
|
||||
if media == self._media:
|
||||
|
|
|
@ -24,6 +24,7 @@ from hydrus.client import ClientDefaults
|
|||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientPaths
|
||||
from hydrus.client import ClientSerialisable
|
||||
from hydrus.client import ClientStrings
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.gui import ClientGUIDialogs
|
||||
from hydrus.client.gui import ClientGUIDialogsQuick
|
||||
|
@ -1444,7 +1445,7 @@ class EditJSONParsingRulePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._parse_rule_type.addItem( 'all dictionary/list items', ClientParsing.JSON_PARSE_RULE_TYPE_ALL_ITEMS )
|
||||
self._parse_rule_type.addItem( 'indexed item', ClientParsing.JSON_PARSE_RULE_TYPE_INDEXED_ITEM )
|
||||
|
||||
string_match = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'posts', example_string = 'posts' )
|
||||
string_match = ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'posts', example_string = 'posts' )
|
||||
|
||||
self._string_match = ClientGUIStringPanels.EditStringMatchPanel( self, string_match )
|
||||
|
||||
|
@ -1651,7 +1652,7 @@ class EditJSONFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
with ClientGUITopLevelWindowsPanels.DialogEdit( self, dlg_title, frame_key = 'deeply_nested_dialog' ) as dlg:
|
||||
|
||||
new_rule = ( ClientParsing.JSON_PARSE_RULE_TYPE_DICT_KEY, ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'posts', example_string = 'posts' ) )
|
||||
new_rule = ( ClientParsing.JSON_PARSE_RULE_TYPE_DICT_KEY, ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'posts', example_string = 'posts' ) )
|
||||
|
||||
panel = EditJSONParsingRulePanel( dlg, new_rule )
|
||||
|
||||
|
@ -1858,7 +1859,7 @@ class EditContentParserPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._veto_panel = QW.QWidget( self._content_panel )
|
||||
|
||||
self._veto_if_matches_found = QW.QCheckBox( self._veto_panel )
|
||||
self._string_match = ClientGUIStringPanels.EditStringMatchPanel( self._veto_panel, ClientParsing.StringMatch() )
|
||||
self._string_match = ClientGUIStringPanels.EditStringMatchPanel( self._veto_panel, ClientStrings.StringMatch() )
|
||||
|
||||
self._temp_variable_panel = QW.QWidget( self._content_panel )
|
||||
|
||||
|
@ -3853,7 +3854,7 @@ class ManageParsingScriptsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
url = ''
|
||||
query_type = HC.GET
|
||||
file_identifier_type = ClientParsing.FILE_IDENTIFIER_TYPE_MD5
|
||||
file_identifier_string_converter = ClientParsing.StringConverter( ( ( ClientParsing.STRING_CONVERSION_ENCODE, 'hex' ), ), 'some hash bytes' )
|
||||
file_identifier_string_converter = ClientStrings.StringConverter( ( ( ClientStrings.STRING_CONVERSION_ENCODE, 'hex' ), ), 'some hash bytes' )
|
||||
file_identifier_arg_name = 'md5'
|
||||
static_args = {}
|
||||
children = []
|
||||
|
@ -4611,7 +4612,7 @@ class TestPanelFormula( TestPanel ):
|
|||
|
||||
try:
|
||||
|
||||
formula.SetStringProcessor( ClientParsing.StringProcessor() )
|
||||
formula.SetStringProcessor( ClientStrings.StringProcessor() )
|
||||
|
||||
texts = formula.Parse( example_parsing_context, self._example_data_raw )
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ from hydrus.core import HydrusText
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientStrings
|
||||
from hydrus.client.gui import ClientGUIDialogs
|
||||
from hydrus.client.gui import ClientGUIScrolledPanels
|
||||
from hydrus.client.gui import ClientGUIStringPanels
|
||||
|
@ -21,7 +22,7 @@ class StringConverterButton( ClientGUICommon.BetterButton ):
|
|||
|
||||
valueChanged = QC.Signal()
|
||||
|
||||
def __init__( self, parent, string_converter: ClientParsing.StringConverter ):
|
||||
def __init__( self, parent, string_converter: ClientStrings.StringConverter ):
|
||||
|
||||
ClientGUICommon.BetterButton.__init__( self, parent, 'edit string converter', self._Edit )
|
||||
|
||||
|
@ -62,7 +63,7 @@ class StringConverterButton( ClientGUICommon.BetterButton ):
|
|||
self.setText( elided_label )
|
||||
|
||||
|
||||
def GetValue( self ) -> ClientParsing.StringConverter:
|
||||
def GetValue( self ) -> ClientStrings.StringConverter:
|
||||
|
||||
return self._string_converter
|
||||
|
||||
|
@ -72,7 +73,7 @@ class StringConverterButton( ClientGUICommon.BetterButton ):
|
|||
self._example_string_override = example_string
|
||||
|
||||
|
||||
def SetValue( self, string_converter: ClientParsing.StringConverter ):
|
||||
def SetValue( self, string_converter: ClientStrings.StringConverter ):
|
||||
|
||||
self._string_converter = string_converter
|
||||
|
||||
|
@ -85,7 +86,7 @@ class StringMatchButton( ClientGUICommon.BetterButton ):
|
|||
|
||||
valueChanged = QC.Signal()
|
||||
|
||||
def __init__( self, parent, string_match: ClientParsing.StringMatch ):
|
||||
def __init__( self, parent, string_match: ClientStrings.StringMatch ):
|
||||
|
||||
ClientGUICommon.BetterButton.__init__( self, parent, 'edit string match', self._Edit )
|
||||
|
||||
|
@ -108,6 +109,8 @@ class StringMatchButton( ClientGUICommon.BetterButton ):
|
|||
|
||||
self._UpdateLabel()
|
||||
|
||||
self.valueChanged.emit()
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -118,12 +121,12 @@ class StringMatchButton( ClientGUICommon.BetterButton ):
|
|||
self.setText( label )
|
||||
|
||||
|
||||
def GetValue( self ) -> ClientParsing.StringMatch:
|
||||
def GetValue( self ) -> ClientStrings.StringMatch:
|
||||
|
||||
return self._string_match
|
||||
|
||||
|
||||
def SetValue( self, string_match: ClientParsing.StringMatch ):
|
||||
def SetValue( self, string_match: ClientStrings.StringMatch ):
|
||||
|
||||
self._string_match = string_match
|
||||
|
||||
|
@ -132,7 +135,7 @@ class StringMatchButton( ClientGUICommon.BetterButton ):
|
|||
|
||||
class StringProcessorButton( ClientGUICommon.BetterButton ):
|
||||
|
||||
def __init__( self, parent, string_processor: ClientParsing.StringProcessor, test_data_callable: typing.Callable[ [], ClientParsing.ParsingTestData ] ):
|
||||
def __init__( self, parent, string_processor: ClientStrings.StringProcessor, test_data_callable: typing.Callable[ [], ClientParsing.ParsingTestData ] ):
|
||||
|
||||
ClientGUICommon.BetterButton.__init__( self, parent, 'edit string processor', self._Edit )
|
||||
|
||||
|
@ -179,12 +182,12 @@ class StringProcessorButton( ClientGUICommon.BetterButton ):
|
|||
self.setText( label )
|
||||
|
||||
|
||||
def GetValue( self ) -> ClientParsing.StringProcessor:
|
||||
def GetValue( self ) -> ClientStrings.StringProcessor:
|
||||
|
||||
return self._string_processor
|
||||
|
||||
|
||||
def SetValue( self, string_processor: ClientParsing.StringProcessor ):
|
||||
def SetValue( self, string_processor: ClientStrings.StringProcessor ):
|
||||
|
||||
self._string_processor = string_processor
|
||||
|
||||
|
@ -193,7 +196,7 @@ class StringProcessorButton( ClientGUICommon.BetterButton ):
|
|||
|
||||
class StringMatchToStringMatchDictControl( QW.QWidget ):
|
||||
|
||||
def __init__( self, parent, initial_dict: typing.Dict[ ClientParsing.StringMatch, ClientParsing.StringMatch ], min_height = 10, key_name = 'key' ):
|
||||
def __init__( self, parent, initial_dict: typing.Dict[ ClientStrings.StringMatch, ClientStrings.StringMatch ], min_height = 10, key_name = 'key' ):
|
||||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
|
@ -243,7 +246,7 @@ class StringMatchToStringMatchDictControl( QW.QWidget ):
|
|||
|
||||
with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit ' + self._key_name ) as dlg:
|
||||
|
||||
string_match = ClientParsing.StringMatch()
|
||||
string_match = ClientStrings.StringMatch()
|
||||
|
||||
panel = ClientGUIStringPanels.EditStringMatchPanel( dlg, string_match )
|
||||
|
||||
|
@ -261,7 +264,7 @@ class StringMatchToStringMatchDictControl( QW.QWidget ):
|
|||
|
||||
with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit match' ) as dlg:
|
||||
|
||||
string_match = ClientParsing.StringMatch()
|
||||
string_match = ClientStrings.StringMatch()
|
||||
|
||||
panel = ClientGUIStringPanels.EditStringMatchPanel( dlg, string_match )
|
||||
|
||||
|
@ -326,7 +329,7 @@ class StringMatchToStringMatchDictControl( QW.QWidget ):
|
|||
self._listctrl.Sort()
|
||||
|
||||
|
||||
def GetValue( self ) -> typing.Dict[ str, ClientParsing.StringMatch ]:
|
||||
def GetValue( self ) -> typing.Dict[ str, ClientStrings.StringMatch ]:
|
||||
|
||||
value_dict = dict( self._listctrl.GetData() )
|
||||
|
||||
|
@ -530,7 +533,7 @@ class StringToStringDictControl( QW.QWidget ):
|
|||
|
||||
class StringToStringMatchDictControl( QW.QWidget ):
|
||||
|
||||
def __init__( self, parent, initial_dict: typing.Dict[ str, ClientParsing.StringMatch ], min_height = 10, key_name = 'key' ):
|
||||
def __init__( self, parent, initial_dict: typing.Dict[ str, ClientStrings.StringMatch ], min_height = 10, key_name = 'key' ):
|
||||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
|
@ -592,7 +595,7 @@ class StringToStringMatchDictControl( QW.QWidget ):
|
|||
|
||||
with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit match' ) as dlg:
|
||||
|
||||
string_match = ClientParsing.StringMatch()
|
||||
string_match = ClientStrings.StringMatch()
|
||||
|
||||
panel = ClientGUIStringPanels.EditStringMatchPanel( dlg, string_match )
|
||||
|
||||
|
@ -638,7 +641,7 @@ class StringToStringMatchDictControl( QW.QWidget ):
|
|||
|
||||
with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit match' ) as dlg:
|
||||
|
||||
string_match = ClientParsing.StringMatch()
|
||||
string_match = ClientStrings.StringMatch()
|
||||
|
||||
panel = ClientGUIStringPanels.EditStringMatchPanel( dlg, string_match )
|
||||
|
||||
|
@ -669,7 +672,7 @@ class StringToStringMatchDictControl( QW.QWidget ):
|
|||
return { key for ( key, value ) in self._listctrl.GetData() }
|
||||
|
||||
|
||||
def GetValue( self ) -> typing.Dict[ str, ClientParsing.StringMatch ]:
|
||||
def GetValue( self ) -> typing.Dict[ str, ClientStrings.StringMatch ]:
|
||||
|
||||
value_dict = dict( self._listctrl.GetData() )
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ from hydrus.core import HydrusExceptions
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientStrings
|
||||
from hydrus.client.gui import ClientGUIDialogsQuick
|
||||
from hydrus.client.gui import ClientGUIFunctions
|
||||
from hydrus.client.gui import ClientGUIScrolledPanels
|
||||
|
@ -26,7 +27,7 @@ class MultilineStringConversionTestPanel( QW.QWidget ):
|
|||
|
||||
textSelected = QC.Signal( str )
|
||||
|
||||
def __init__( self, parent: QW.QWidget, string_processor: ClientParsing.StringProcessor ):
|
||||
def __init__( self, parent: QW.QWidget, string_processor: ClientStrings.StringProcessor ):
|
||||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
|
@ -121,7 +122,7 @@ class MultilineStringConversionTestPanel( QW.QWidget ):
|
|||
return results
|
||||
|
||||
|
||||
def SetStringProcessor( self, string_processor: ClientParsing.StringProcessor ):
|
||||
def SetStringProcessor( self, string_processor: ClientStrings.StringProcessor ):
|
||||
|
||||
self._string_processor = string_processor
|
||||
|
||||
|
@ -155,7 +156,7 @@ class MultilineStringConversionTestPanel( QW.QWidget ):
|
|||
|
||||
class SingleStringConversionTestPanel( QW.QWidget ):
|
||||
|
||||
def __init__( self, parent: QW.QWidget, string_processor: ClientParsing.StringProcessor ):
|
||||
def __init__( self, parent: QW.QWidget, string_processor: ClientStrings.StringProcessor ):
|
||||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
|
@ -193,7 +194,7 @@ class SingleStringConversionTestPanel( QW.QWidget ):
|
|||
|
||||
for i in range( len( processing_steps ) ):
|
||||
|
||||
if isinstance( processing_steps[i], ClientParsing.StringSlicer ):
|
||||
if isinstance( processing_steps[i], ClientStrings.StringSlicer ):
|
||||
|
||||
continue
|
||||
|
||||
|
@ -276,11 +277,11 @@ class SingleStringConversionTestPanel( QW.QWidget ):
|
|||
return self._example_string.text()
|
||||
|
||||
|
||||
def SetStringProcessor( self, string_processor: ClientParsing.StringProcessor ):
|
||||
def SetStringProcessor( self, string_processor: ClientStrings.StringProcessor ):
|
||||
|
||||
self._string_processor = string_processor
|
||||
|
||||
if True in ( isinstance( processing_step, ClientParsing.StringSlicer ) for processing_step in self._string_processor.GetProcessingSteps() ):
|
||||
if True in ( isinstance( processing_step, ClientStrings.StringSlicer ) for processing_step in self._string_processor.GetProcessingSteps() ):
|
||||
|
||||
self.setToolTip( 'String Slicing is ignored here.' )
|
||||
|
||||
|
@ -301,7 +302,7 @@ class SingleStringConversionTestPanel( QW.QWidget ):
|
|||
|
||||
class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent: QW.QWidget, string_converter: ClientParsing.StringConverter, example_string_override = None ):
|
||||
def __init__( self, parent: QW.QWidget, string_converter: ClientStrings.StringConverter, example_string_override = None ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
|
@ -361,7 +362,7 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def _AddConversion( self ):
|
||||
|
||||
conversion_type = ClientParsing.STRING_CONVERSION_APPEND_TEXT
|
||||
conversion_type = ClientStrings.STRING_CONVERSION_APPEND_TEXT
|
||||
data = 'extra text'
|
||||
|
||||
try:
|
||||
|
@ -437,7 +438,7 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
( number, conversion_type, data ) = conversion
|
||||
|
||||
pretty_number = HydrusData.ToHumanInt( number )
|
||||
pretty_conversion = ClientParsing.StringConverter.ConversionToString( ( conversion_type, data ) )
|
||||
pretty_conversion = ClientStrings.StringConverter.ConversionToString( ( conversion_type, data ) )
|
||||
|
||||
string_converter = self._GetValue()
|
||||
|
||||
|
@ -578,7 +579,7 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
example_string = self._example_string.text()
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions, example_string )
|
||||
string_converter = ClientStrings.StringConverter( conversions, example_string )
|
||||
|
||||
return string_converter
|
||||
|
||||
|
@ -672,9 +673,9 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._conversion_type = ClientGUICommon.BetterChoice( self._control_panel )
|
||||
|
||||
for t_type in ( ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING, ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_END, ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING, ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_END, ClientParsing.STRING_CONVERSION_PREPEND_TEXT, ClientParsing.STRING_CONVERSION_APPEND_TEXT, ClientParsing.STRING_CONVERSION_ENCODE, ClientParsing.STRING_CONVERSION_DECODE, ClientParsing.STRING_CONVERSION_REVERSE, ClientParsing.STRING_CONVERSION_REGEX_SUB, ClientParsing.STRING_CONVERSION_DATE_DECODE, ClientParsing.STRING_CONVERSION_DATE_ENCODE, ClientParsing.STRING_CONVERSION_INTEGER_ADDITION ):
|
||||
for t_type in ( ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING, ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_END, ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING, ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_END, ClientStrings.STRING_CONVERSION_PREPEND_TEXT, ClientStrings.STRING_CONVERSION_APPEND_TEXT, ClientStrings.STRING_CONVERSION_ENCODE, ClientStrings.STRING_CONVERSION_DECODE, ClientStrings.STRING_CONVERSION_REVERSE, ClientStrings.STRING_CONVERSION_REGEX_SUB, ClientStrings.STRING_CONVERSION_DATE_DECODE, ClientStrings.STRING_CONVERSION_DATE_ENCODE, ClientStrings.STRING_CONVERSION_INTEGER_ADDITION ):
|
||||
|
||||
self._conversion_type.addItem( ClientParsing.conversion_type_str_lookup[ t_type ], t_type )
|
||||
self._conversion_type.addItem( ClientStrings.conversion_type_str_lookup[ t_type ], t_type )
|
||||
|
||||
|
||||
self._data_text = QW.QLineEdit( self._control_panel )
|
||||
|
@ -738,22 +739,22 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
if conversion_type == ClientParsing.STRING_CONVERSION_ENCODE:
|
||||
if conversion_type == ClientStrings.STRING_CONVERSION_ENCODE:
|
||||
|
||||
self._data_encoding.SetValue( data )
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_DECODE:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_DECODE:
|
||||
|
||||
self._data_decoding.SetValue( data )
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_REGEX_SUB:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_REGEX_SUB:
|
||||
|
||||
( pattern, repl ) = data
|
||||
|
||||
self._data_text.setText( pattern )
|
||||
self._data_regex_repl.setText( repl )
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_DATE_DECODE:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_DATE_DECODE:
|
||||
|
||||
( phrase, timezone_type, timezone_offset ) = data
|
||||
|
||||
|
@ -761,7 +762,7 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._data_timezone_decode.SetValue( timezone_type )
|
||||
self._data_timezone_offset.setValue( timezone_offset )
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_DATE_ENCODE:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_DATE_ENCODE:
|
||||
|
||||
( phrase, timezone_type ) = data
|
||||
|
||||
|
@ -878,37 +879,37 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
conversion_type = self._conversion_type.GetValue()
|
||||
|
||||
if conversion_type == ClientParsing.STRING_CONVERSION_ENCODE:
|
||||
if conversion_type == ClientStrings.STRING_CONVERSION_ENCODE:
|
||||
|
||||
self._data_encoding_label.setVisible( True )
|
||||
self._data_encoding.setVisible( True )
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_DECODE:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_DECODE:
|
||||
|
||||
self._data_decoding_label.setVisible( True )
|
||||
self._data_decoding.setVisible( True )
|
||||
|
||||
elif conversion_type in ( ClientParsing.STRING_CONVERSION_PREPEND_TEXT, ClientParsing.STRING_CONVERSION_APPEND_TEXT, ClientParsing.STRING_CONVERSION_DATE_DECODE, ClientParsing.STRING_CONVERSION_DATE_ENCODE, ClientParsing.STRING_CONVERSION_REGEX_SUB ):
|
||||
elif conversion_type in ( ClientStrings.STRING_CONVERSION_PREPEND_TEXT, ClientStrings.STRING_CONVERSION_APPEND_TEXT, ClientStrings.STRING_CONVERSION_DATE_DECODE, ClientStrings.STRING_CONVERSION_DATE_ENCODE, ClientStrings.STRING_CONVERSION_REGEX_SUB ):
|
||||
|
||||
self._data_text_label.setVisible( True )
|
||||
self._data_text.setVisible( True )
|
||||
|
||||
data_text_label = 'string data: '
|
||||
|
||||
if conversion_type == ClientParsing.STRING_CONVERSION_PREPEND_TEXT:
|
||||
if conversion_type == ClientStrings.STRING_CONVERSION_PREPEND_TEXT:
|
||||
|
||||
data_text_label = 'text to prepend: '
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_APPEND_TEXT:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_APPEND_TEXT:
|
||||
|
||||
data_text_label = 'text to append: '
|
||||
|
||||
elif conversion_type in ( ClientParsing.STRING_CONVERSION_DATE_DECODE, ClientParsing.STRING_CONVERSION_DATE_ENCODE ):
|
||||
elif conversion_type in ( ClientStrings.STRING_CONVERSION_DATE_DECODE, ClientStrings.STRING_CONVERSION_DATE_ENCODE ):
|
||||
|
||||
self._data_date_link_label.setVisible( True )
|
||||
self._data_date_link.setVisible( True )
|
||||
|
||||
if conversion_type == ClientParsing.STRING_CONVERSION_DATE_DECODE:
|
||||
if conversion_type == ClientStrings.STRING_CONVERSION_DATE_DECODE:
|
||||
|
||||
data_text_label = 'date decode phrase: '
|
||||
|
||||
|
@ -921,7 +922,7 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._data_timezone_offset.setVisible( True )
|
||||
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_DATE_ENCODE:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_DATE_ENCODE:
|
||||
|
||||
data_text_label = 'date encode phrase: '
|
||||
|
||||
|
@ -929,7 +930,7 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._data_timezone_encode.setVisible( True )
|
||||
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_REGEX_SUB:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_REGEX_SUB:
|
||||
|
||||
data_text_label = 'regex pattern: '
|
||||
|
||||
|
@ -939,12 +940,12 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._data_text_label.setText( data_text_label )
|
||||
|
||||
elif conversion_type in ( ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING, ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_END, ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING, ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_END, ClientParsing.STRING_CONVERSION_INTEGER_ADDITION ):
|
||||
elif conversion_type in ( ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING, ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_END, ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING, ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_END, ClientStrings.STRING_CONVERSION_INTEGER_ADDITION ):
|
||||
|
||||
self._data_number_label.setVisible( True )
|
||||
self._data_number.setVisible( True )
|
||||
|
||||
if conversion_type == ClientParsing.STRING_CONVERSION_INTEGER_ADDITION:
|
||||
if conversion_type == ClientStrings.STRING_CONVERSION_INTEGER_ADDITION:
|
||||
|
||||
self._data_number.setMinimum( -65535 )
|
||||
|
||||
|
@ -955,23 +956,23 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
data_number_label = 'number data: '
|
||||
|
||||
if conversion_type == ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING:
|
||||
if conversion_type == ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING:
|
||||
|
||||
data_number_label = 'characters to remove: '
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_END:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_END:
|
||||
|
||||
data_number_label = 'characters to remove: '
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING:
|
||||
|
||||
data_number_label = 'characters to take: '
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_END:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_END:
|
||||
|
||||
data_number_label = 'characters to take: '
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_INTEGER_ADDITION:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_INTEGER_ADDITION:
|
||||
|
||||
data_number_label = 'number to add: '
|
||||
|
||||
|
@ -986,7 +987,7 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
conversions = [ self.GetValue() ]
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions, self._example_text )
|
||||
string_converter = ClientStrings.StringConverter( conversions, self._example_text )
|
||||
|
||||
example_conversion = string_converter.Convert( self._example_text )
|
||||
|
||||
|
@ -1009,30 +1010,30 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
conversion_type = self._conversion_type.GetValue()
|
||||
|
||||
if conversion_type == ClientParsing.STRING_CONVERSION_ENCODE:
|
||||
if conversion_type == ClientStrings.STRING_CONVERSION_ENCODE:
|
||||
|
||||
data = self._data_encoding.GetValue()
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_DECODE:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_DECODE:
|
||||
|
||||
data = self._data_decoding.GetValue()
|
||||
|
||||
elif conversion_type in ( ClientParsing.STRING_CONVERSION_PREPEND_TEXT, ClientParsing.STRING_CONVERSION_APPEND_TEXT ):
|
||||
elif conversion_type in ( ClientStrings.STRING_CONVERSION_PREPEND_TEXT, ClientStrings.STRING_CONVERSION_APPEND_TEXT ):
|
||||
|
||||
data = self._data_text.text()
|
||||
|
||||
elif conversion_type in ( ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING, ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_END, ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING, ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_END, ClientParsing.STRING_CONVERSION_INTEGER_ADDITION ):
|
||||
elif conversion_type in ( ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING, ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_END, ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING, ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_END, ClientStrings.STRING_CONVERSION_INTEGER_ADDITION ):
|
||||
|
||||
data = self._data_number.value()
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_REGEX_SUB:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_REGEX_SUB:
|
||||
|
||||
pattern = self._data_text.text()
|
||||
repl = self._data_regex_repl.text()
|
||||
|
||||
data = ( pattern, repl )
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_DATE_DECODE:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_DATE_DECODE:
|
||||
|
||||
phrase = self._data_text.text()
|
||||
timezone_time = self._data_timezone_decode.GetValue()
|
||||
|
@ -1040,7 +1041,7 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
data = ( phrase, timezone_time, timezone_offset )
|
||||
|
||||
elif conversion_type == ClientParsing.STRING_CONVERSION_DATE_ENCODE:
|
||||
elif conversion_type == ClientStrings.STRING_CONVERSION_DATE_ENCODE:
|
||||
|
||||
phrase = self._data_text.text()
|
||||
timezone_time = self._data_timezone_encode.GetValue()
|
||||
|
@ -1058,25 +1059,25 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
class EditStringMatchPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent: QW.QWidget, string_match: ClientParsing.StringMatch, test_data = typing.Optional[ ClientParsing.ParsingTestData ] ):
|
||||
def __init__( self, parent: QW.QWidget, string_match: ClientStrings.StringMatch, test_data = typing.Optional[ ClientParsing.ParsingTestData ] ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
self._match_type = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
self._match_type.addItem( 'any characters', ClientParsing.STRING_MATCH_ANY )
|
||||
self._match_type.addItem( 'fixed characters', ClientParsing.STRING_MATCH_FIXED )
|
||||
self._match_type.addItem( 'character set', ClientParsing.STRING_MATCH_FLEXIBLE )
|
||||
self._match_type.addItem( 'regex', ClientParsing.STRING_MATCH_REGEX )
|
||||
self._match_type.addItem( 'any characters', ClientStrings.STRING_MATCH_ANY )
|
||||
self._match_type.addItem( 'fixed characters', ClientStrings.STRING_MATCH_FIXED )
|
||||
self._match_type.addItem( 'character set', ClientStrings.STRING_MATCH_FLEXIBLE )
|
||||
self._match_type.addItem( 'regex', ClientStrings.STRING_MATCH_REGEX )
|
||||
|
||||
self._match_value_fixed_input = QW.QLineEdit( self )
|
||||
self._match_value_regex_input = QW.QLineEdit( self )
|
||||
|
||||
self._match_value_flexible_input = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
self._match_value_flexible_input.addItem( 'alphabetic characters (a-zA-Z)', ClientParsing.ALPHA )
|
||||
self._match_value_flexible_input.addItem( 'alphanumeric characters (a-zA-Z0-9)', ClientParsing.ALPHANUMERIC )
|
||||
self._match_value_flexible_input.addItem( 'numeric characters (0-9)', ClientParsing.NUMERIC )
|
||||
self._match_value_flexible_input.addItem( 'alphabetic characters (a-zA-Z)', ClientStrings.ALPHA )
|
||||
self._match_value_flexible_input.addItem( 'alphanumeric characters (a-zA-Z0-9)', ClientStrings.ALPHANUMERIC )
|
||||
self._match_value_flexible_input.addItem( 'numeric characters (0-9)', ClientStrings.NUMERIC )
|
||||
|
||||
self._min_chars = ClientGUICommon.NoneableSpinCtrl( self, min = 1, max = 65535, unit = 'characters', none_phrase = 'no limit' )
|
||||
self._max_chars = ClientGUICommon.NoneableSpinCtrl( self, min = 1, max = 65535, unit = 'characters', none_phrase = 'no limit' )
|
||||
|
@ -1132,24 +1133,24 @@ class EditStringMatchPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
match_type = self._match_type.GetValue()
|
||||
|
||||
if match_type == ClientParsing.STRING_MATCH_ANY:
|
||||
if match_type == ClientStrings.STRING_MATCH_ANY:
|
||||
|
||||
match_value = ''
|
||||
|
||||
elif match_type == ClientParsing.STRING_MATCH_FLEXIBLE:
|
||||
elif match_type == ClientStrings.STRING_MATCH_FLEXIBLE:
|
||||
|
||||
match_value = self._match_value_flexible_input.GetValue()
|
||||
|
||||
elif match_type == ClientParsing.STRING_MATCH_FIXED:
|
||||
elif match_type == ClientStrings.STRING_MATCH_FIXED:
|
||||
|
||||
match_value = self._match_value_fixed_input.text()
|
||||
|
||||
elif match_type == ClientParsing.STRING_MATCH_REGEX:
|
||||
elif match_type == ClientStrings.STRING_MATCH_REGEX:
|
||||
|
||||
match_value = self._match_value_regex_input.text()
|
||||
|
||||
|
||||
if match_type == ClientParsing.STRING_MATCH_FIXED:
|
||||
if match_type == ClientStrings.STRING_MATCH_FIXED:
|
||||
|
||||
min_chars = None
|
||||
max_chars = None
|
||||
|
@ -1162,7 +1163,7 @@ class EditStringMatchPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
example_string = self._example_string.text()
|
||||
|
||||
|
||||
string_match = ClientParsing.StringMatch( match_type = match_type, match_value = match_value, min_chars = min_chars, max_chars = max_chars, example_string = example_string )
|
||||
string_match = ClientStrings.StringMatch( match_type = match_type, match_value = match_value, min_chars = min_chars, max_chars = max_chars, example_string = example_string )
|
||||
|
||||
return string_match
|
||||
|
||||
|
@ -1185,7 +1186,7 @@ class EditStringMatchPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._max_chars.setVisible( False )
|
||||
self._example_string.setVisible( False )
|
||||
|
||||
if match_type == ClientParsing.STRING_MATCH_FIXED:
|
||||
if match_type == ClientStrings.STRING_MATCH_FIXED:
|
||||
|
||||
self._match_value_fixed_input_label.setVisible( True )
|
||||
self._match_value_fixed_input.setVisible( True )
|
||||
|
@ -1200,12 +1201,12 @@ class EditStringMatchPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._max_chars.setVisible( True )
|
||||
self._example_string.setVisible( True )
|
||||
|
||||
if match_type == ClientParsing.STRING_MATCH_FLEXIBLE:
|
||||
if match_type == ClientStrings.STRING_MATCH_FLEXIBLE:
|
||||
|
||||
self._match_value_flexible_input_label.setVisible( True )
|
||||
self._match_value_flexible_input.setVisible( True )
|
||||
|
||||
elif match_type == ClientParsing.STRING_MATCH_REGEX:
|
||||
elif match_type == ClientStrings.STRING_MATCH_REGEX:
|
||||
|
||||
self._match_value_regex_input_label.setVisible( True )
|
||||
self._match_value_regex_input.setVisible( True )
|
||||
|
@ -1219,7 +1220,7 @@ class EditStringMatchPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
match_type = self._match_type.GetValue()
|
||||
|
||||
if match_type == ClientParsing.STRING_MATCH_FIXED:
|
||||
if match_type == ClientStrings.STRING_MATCH_FIXED:
|
||||
|
||||
self._example_string_matches.clear()
|
||||
|
||||
|
@ -1262,23 +1263,23 @@ class EditStringMatchPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return string_match
|
||||
|
||||
|
||||
def SetValue( self, string_match: ClientParsing.StringMatch ):
|
||||
def SetValue( self, string_match: ClientStrings.StringMatch ):
|
||||
|
||||
( match_type, match_value, min_chars, max_chars, example_string ) = string_match.ToTuple()
|
||||
|
||||
self._match_type.SetValue( match_type )
|
||||
|
||||
self._match_value_flexible_input.SetValue( ClientParsing.ALPHA )
|
||||
self._match_value_flexible_input.SetValue( ClientStrings.ALPHA )
|
||||
|
||||
if match_type == ClientParsing.STRING_MATCH_FIXED:
|
||||
if match_type == ClientStrings.STRING_MATCH_FIXED:
|
||||
|
||||
self._match_value_fixed_input.setText( match_value )
|
||||
|
||||
elif match_type == ClientParsing.STRING_MATCH_FLEXIBLE:
|
||||
elif match_type == ClientStrings.STRING_MATCH_FLEXIBLE:
|
||||
|
||||
self._match_value_flexible_input.SetValue( match_value )
|
||||
|
||||
elif match_type == ClientParsing.STRING_MATCH_REGEX:
|
||||
elif match_type == ClientStrings.STRING_MATCH_REGEX:
|
||||
|
||||
self._match_value_regex_input.setText( match_value )
|
||||
|
||||
|
@ -1296,7 +1297,7 @@ SELECT_RANGE = 1
|
|||
|
||||
class EditStringSlicerPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, string_slicer: ClientParsing.StringSlicer, test_data: typing.Sequence[ str ] = [] ):
|
||||
def __init__( self, parent, string_slicer: ClientStrings.StringSlicer, test_data: typing.Sequence[ str ] = [] ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
|
@ -1416,7 +1417,7 @@ class EditStringSlicerPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
index_end = self._index_end.GetValue()
|
||||
|
||||
|
||||
string_slicer = ClientParsing.StringSlicer( index_start = index_start, index_end = index_end )
|
||||
string_slicer = ClientStrings.StringSlicer( index_start = index_start, index_end = index_end )
|
||||
|
||||
return string_slicer
|
||||
|
||||
|
@ -1463,7 +1464,7 @@ class EditStringSlicerPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return string_slicer
|
||||
|
||||
|
||||
def SetValue( self, string_slicer: ClientParsing.StringSlicer ):
|
||||
def SetValue( self, string_slicer: ClientStrings.StringSlicer ):
|
||||
|
||||
( index_start, index_end ) = string_slicer.GetIndexStartEnd()
|
||||
|
||||
|
@ -1486,7 +1487,7 @@ class EditStringSlicerPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
class EditStringSorterPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, string_sorter: ClientParsing.StringSorter, test_data: typing.Sequence[ str ] = [] ):
|
||||
def __init__( self, parent, string_sorter: ClientStrings.StringSorter, test_data: typing.Sequence[ str ] = [] ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
|
@ -1496,9 +1497,9 @@ class EditStringSorterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._sort_type = ClientGUICommon.BetterChoice( self._controls_panel )
|
||||
|
||||
self._sort_type.addItem( ClientParsing.sort_str_enum[ ClientParsing.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT ], ClientParsing.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT )
|
||||
self._sort_type.addItem( ClientParsing.sort_str_enum[ ClientParsing.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC ], ClientParsing.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC )
|
||||
self._sort_type.addItem( ClientParsing.sort_str_enum[ ClientParsing.CONTENT_PARSER_SORT_TYPE_REVERSE ], ClientParsing.CONTENT_PARSER_SORT_TYPE_REVERSE )
|
||||
self._sort_type.addItem( ClientStrings.sort_str_enum[ ClientStrings.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT ], ClientStrings.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT )
|
||||
self._sort_type.addItem( ClientStrings.sort_str_enum[ ClientStrings.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC ], ClientStrings.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC )
|
||||
self._sort_type.addItem( ClientStrings.sort_str_enum[ ClientStrings.CONTENT_PARSER_SORT_TYPE_REVERSE ], ClientStrings.CONTENT_PARSER_SORT_TYPE_REVERSE )
|
||||
|
||||
tt = 'Human sort sorts numbers as you understand them. "image 2" comes before "image 10". Lexicographic compares each character in turn. "image 02" comes before "image 10", which comes before "image 2".'
|
||||
|
||||
|
@ -1568,7 +1569,7 @@ class EditStringSorterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
asc = self._asc.isChecked()
|
||||
regex = self._regex.GetValue()
|
||||
|
||||
string_sorter = ClientParsing.StringSorter( sort_type = sort_type, asc = asc, regex = regex )
|
||||
string_sorter = ClientStrings.StringSorter( sort_type = sort_type, asc = asc, regex = regex )
|
||||
|
||||
return string_sorter
|
||||
|
||||
|
@ -1626,7 +1627,7 @@ class EditStringSorterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return string_sorter
|
||||
|
||||
|
||||
def SetValue( self, string_sorter: ClientParsing.StringSorter ):
|
||||
def SetValue( self, string_sorter: ClientStrings.StringSorter ):
|
||||
|
||||
sort_type = string_sorter.GetSortType()
|
||||
asc = string_sorter.GetAscending()
|
||||
|
@ -1641,7 +1642,7 @@ class EditStringSorterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
class EditStringSplitterPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, string_splitter: ClientParsing.StringSplitter, example_string: str = '' ):
|
||||
def __init__( self, parent, string_splitter: ClientStrings.StringSplitter, example_string: str = '' ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
|
@ -1707,7 +1708,7 @@ class EditStringSplitterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
separator = self._separator.text()
|
||||
max_splits = self._max_splits.GetValue()
|
||||
|
||||
string_splitter = ClientParsing.StringSplitter( separator = separator, max_splits = max_splits )
|
||||
string_splitter = ClientStrings.StringSplitter( separator = separator, max_splits = max_splits )
|
||||
|
||||
return string_splitter
|
||||
|
||||
|
@ -1733,7 +1734,7 @@ class EditStringSplitterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return string_splitter
|
||||
|
||||
|
||||
def SetValue( self, string_splitter: ClientParsing.StringSplitter ):
|
||||
def SetValue( self, string_splitter: ClientStrings.StringSplitter ):
|
||||
|
||||
separator = string_splitter.GetSeparator()
|
||||
max_splits = string_splitter.GetMaxSplits()
|
||||
|
@ -1746,7 +1747,7 @@ class EditStringSplitterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
class EditStringProcessorPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, string_processor: ClientParsing.StringProcessor, test_data: ClientParsing.ParsingTestData ):
|
||||
def __init__( self, parent, string_processor: ClientStrings.StringProcessor, test_data: ClientParsing.ParsingTestData ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
|
@ -1804,11 +1805,11 @@ class EditStringProcessorPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
def _Add( self ):
|
||||
|
||||
choice_tuples = [
|
||||
( 'String Match', ClientParsing.StringMatch, 'An object that filters strings.' ),
|
||||
( 'String Converter', ClientParsing.StringConverter, 'An object that converts strings from one thing to another.' ),
|
||||
( 'String Splitter', ClientParsing.StringSplitter, 'An object that breaks strings into smaller strings.' ),
|
||||
( 'String Sorter', ClientParsing.StringSorter, 'An object that sorts strings.' ),
|
||||
( 'String Selector/Slicer', ClientParsing.StringSlicer, 'An object that filter-selects from the list of strings. Either absolute index position or a range.' )
|
||||
( 'String Match', ClientStrings.StringMatch, 'An object that filters strings.' ),
|
||||
( 'String Converter', ClientStrings.StringConverter, 'An object that converts strings from one thing to another.' ),
|
||||
( 'String Splitter', ClientStrings.StringSplitter, 'An object that breaks strings into smaller strings.' ),
|
||||
( 'String Sorter', ClientStrings.StringSorter, 'An object that sorts strings.' ),
|
||||
( 'String Selector/Slicer', ClientStrings.StringSlicer, 'An object that filter-selects from the list of strings. Either absolute index position or a range.' )
|
||||
]
|
||||
|
||||
try:
|
||||
|
@ -1820,15 +1821,15 @@ class EditStringProcessorPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
raise HydrusExceptions.VetoException()
|
||||
|
||||
|
||||
if string_processing_step_type == ClientParsing.StringMatch:
|
||||
if string_processing_step_type == ClientStrings.StringMatch:
|
||||
|
||||
example_text = self._single_test_panel.GetStartingText()
|
||||
|
||||
string_processing_step = ClientParsing.StringMatch( example_string = example_text )
|
||||
string_processing_step = ClientStrings.StringMatch( example_string = example_text )
|
||||
|
||||
example_text = self._GetExampleTextForStringProcessingStep( string_processing_step )
|
||||
|
||||
string_processing_step = ClientParsing.StringMatch( example_string = example_text )
|
||||
string_processing_step = ClientStrings.StringMatch( example_string = example_text )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -1838,33 +1839,33 @@ class EditStringProcessorPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return self._Edit( string_processing_step )
|
||||
|
||||
|
||||
def _Edit( self, string_processing_step: ClientParsing.StringProcessingStep ):
|
||||
def _Edit( self, string_processing_step: ClientStrings.StringProcessingStep ):
|
||||
|
||||
example_text = self._GetExampleTextForStringProcessingStep( string_processing_step )
|
||||
|
||||
with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit processing step' ) as dlg:
|
||||
|
||||
if isinstance( string_processing_step, ClientParsing.StringMatch ):
|
||||
if isinstance( string_processing_step, ClientStrings.StringMatch ):
|
||||
|
||||
test_data = ClientParsing.ParsingTestData( {}, ( example_text, ) )
|
||||
|
||||
panel = EditStringMatchPanel( dlg, string_processing_step, test_data = test_data )
|
||||
|
||||
elif isinstance( string_processing_step, ClientParsing.StringConverter ):
|
||||
elif isinstance( string_processing_step, ClientStrings.StringConverter ):
|
||||
|
||||
panel = EditStringConverterPanel( dlg, string_processing_step, example_string_override = example_text )
|
||||
|
||||
elif isinstance( string_processing_step, ClientParsing.StringSplitter ):
|
||||
elif isinstance( string_processing_step, ClientStrings.StringSplitter ):
|
||||
|
||||
panel = EditStringSplitterPanel( dlg, string_processing_step, example_string = example_text )
|
||||
|
||||
elif isinstance( string_processing_step, ClientParsing.StringSorter ):
|
||||
elif isinstance( string_processing_step, ClientStrings.StringSorter ):
|
||||
|
||||
test_data = self._GetExampleTextsForStringSorter( string_processing_step )
|
||||
|
||||
panel = EditStringSorterPanel( dlg, string_processing_step, test_data = test_data )
|
||||
|
||||
elif isinstance( string_processing_step, ClientParsing.StringSlicer ):
|
||||
elif isinstance( string_processing_step, ClientStrings.StringSlicer ):
|
||||
|
||||
test_data = self._GetExampleTextsForStringSorter( string_processing_step )
|
||||
|
||||
|
@ -1886,12 +1887,12 @@ class EditStringProcessorPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
|
||||
|
||||
def _ConvertDataToListBoxString( self, string_processing_step: ClientParsing.StringProcessingStep ):
|
||||
def _ConvertDataToListBoxString( self, string_processing_step: ClientStrings.StringProcessingStep ):
|
||||
|
||||
return string_processing_step.ToString( with_type = True )
|
||||
|
||||
|
||||
def _GetExampleTextForStringProcessingStep( self, string_processing_step: ClientParsing.StringProcessingStep ):
|
||||
def _GetExampleTextForStringProcessingStep( self, string_processing_step: ClientStrings.StringProcessingStep ):
|
||||
|
||||
# ultimately rework this to multiline test_data m8, but the panels need it first
|
||||
|
||||
|
@ -1913,7 +1914,7 @@ class EditStringProcessorPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return example_text
|
||||
|
||||
|
||||
def _GetExampleTextsForStringSorter( self, string_processing_step: ClientParsing.StringProcessingStep ):
|
||||
def _GetExampleTextsForStringSorter( self, string_processing_step: ClientStrings.StringProcessingStep ):
|
||||
|
||||
# ultimately rework this to multiline test_data m8
|
||||
|
||||
|
@ -1939,7 +1940,7 @@ class EditStringProcessorPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
processing_steps = self._processing_steps.GetData()
|
||||
|
||||
string_processor = ClientParsing.StringProcessor()
|
||||
string_processor = ClientStrings.StringProcessor()
|
||||
|
||||
string_processor.SetProcessingSteps( processing_steps )
|
||||
|
||||
|
@ -1961,7 +1962,7 @@ class EditStringProcessorPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return string_processor
|
||||
|
||||
|
||||
def SetValue( self, string_processor: ClientParsing.StringProcessor ):
|
||||
def SetValue( self, string_processor: ClientStrings.StringProcessor ):
|
||||
|
||||
processing_steps = string_processor.GetProcessingSteps()
|
||||
|
||||
|
|
|
@ -559,6 +559,10 @@ class BetterListCtrl( QW.QTreeWidget ):
|
|||
|
||||
self.ProcessDeleteAction()
|
||||
|
||||
elif key in ( QC.Qt.Key_Enter, QC.Qt.Key_Return ):
|
||||
|
||||
self.ProcessActivateAction()
|
||||
|
||||
elif key in ( ord( 'A' ), ord( 'a' ) ) and modifier == QC.Qt.ControlModifier:
|
||||
|
||||
self.selectAll()
|
||||
|
@ -633,6 +637,14 @@ class BetterListCtrl( QW.QTreeWidget ):
|
|||
return len( self.selectedItems() ) > 0
|
||||
|
||||
|
||||
def ProcessActivateAction( self ):
|
||||
|
||||
if self._activation_callback is not None:
|
||||
|
||||
self._activation_callback()
|
||||
|
||||
|
||||
|
||||
def ProcessDeleteAction( self ):
|
||||
|
||||
if self._use_simple_delete:
|
||||
|
|
|
@ -19,9 +19,9 @@ from hydrus.client import ClientApplicationCommand as CAC
|
|||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientFiles
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client import ClientPaths
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientStrings
|
||||
from hydrus.client.gui import ClientGUIDragDrop
|
||||
from hydrus.client.gui import ClientGUICore as CGC
|
||||
from hydrus.client.gui import ClientGUIDialogs
|
||||
|
@ -42,6 +42,7 @@ from hydrus.client.gui import QtPorting as QP
|
|||
from hydrus.client.gui.canvas import ClientGUICanvas
|
||||
from hydrus.client.gui.canvas import ClientGUICanvasFrame
|
||||
from hydrus.client.gui.networking import ClientGUIHydrusNetwork
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class MediaPanel( ClientMedia.ListeningMediaList, QW.QScrollArea ):
|
||||
|
|
|
@ -385,6 +385,11 @@ class FileSeed( HydrusSerialisable.SerialisableBase ):
|
|||
return None
|
||||
|
||||
|
||||
def GetHashTypesToHashes( self ):
|
||||
|
||||
return dict( self._hashes )
|
||||
|
||||
|
||||
def GetPreImportStatusPredictionHash( self, file_import_options: FileImportOptions.FileImportOptions ) -> ClientImportFiles.FileImportStatus:
|
||||
|
||||
hash_match_found = False
|
||||
|
@ -587,6 +592,19 @@ class FileSeed( HydrusSerialisable.SerialisableBase ):
|
|||
return search_file_seeds
|
||||
|
||||
|
||||
def GetExternalTags( self ):
|
||||
|
||||
t = set( self._tags )
|
||||
t.update( self._external_filterable_tags )
|
||||
|
||||
return t
|
||||
|
||||
|
||||
def GetURLs( self ):
|
||||
|
||||
return set( self._urls )
|
||||
|
||||
|
||||
def HasHash( self ):
|
||||
|
||||
return self.GetHash() is not None
|
||||
|
@ -696,6 +714,11 @@ class FileSeed( HydrusSerialisable.SerialisableBase ):
|
|||
return self.status == CC.STATUS_DELETED
|
||||
|
||||
|
||||
def IsLocalFileImport( self ):
|
||||
|
||||
return self.file_seed_type == FILE_SEED_TYPE_HDD
|
||||
|
||||
|
||||
def IsProbablyMasterPostURL( self ):
|
||||
|
||||
if self.file_seed_type == FILE_SEED_TYPE_URL:
|
||||
|
@ -724,6 +747,11 @@ class FileSeed( HydrusSerialisable.SerialisableBase ):
|
|||
return True
|
||||
|
||||
|
||||
def IsURLFileImport( self ):
|
||||
|
||||
return self.file_seed_type == FILE_SEED_TYPE_URL
|
||||
|
||||
|
||||
def Normalise( self ):
|
||||
|
||||
if self.file_seed_type == FILE_SEED_TYPE_URL:
|
||||
|
|
|
@ -1467,7 +1467,7 @@ class MultipleGalleryImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
def PendSubscriptionGapDownloader( self, gug_key_and_name, query_text, file_limit ):
|
||||
def PendSubscriptionGapDownloader( self, gug_key_and_name, query_text, file_import_options, tag_import_options, file_limit ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
|
@ -1493,8 +1493,8 @@ class MultipleGalleryImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
gallery_import.SetFileLimit( file_limit )
|
||||
|
||||
gallery_import.SetFileImportOptions( self._file_import_options )
|
||||
gallery_import.SetTagImportOptions( self._tag_import_options )
|
||||
gallery_import.SetFileImportOptions( file_import_options )
|
||||
gallery_import.SetTagImportOptions( tag_import_options )
|
||||
|
||||
publish_to_page = False
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import gc
|
||||
import os
|
||||
import random
|
||||
import threading
|
||||
|
@ -226,7 +225,7 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
message += os.linesep
|
||||
message += 'Either a user uploaded a lot of files to that query in a short period, in which case there is a gap in your subscription you may wish to fill, or the site has just changed its URL format, in which case you may see several of these messages for this site over the coming weeks, and you should ignore them.'
|
||||
|
||||
call = HydrusData.Call( HG.client_controller.pub, 'make_new_subscription_gap_downloader', self._gug_key_and_name, query_text, file_limit * 5 )
|
||||
call = HydrusData.Call( HG.client_controller.pub, 'make_new_subscription_gap_downloader', self._gug_key_and_name, query_text, self._file_import_options.Duplicate(), self._tag_import_options.Duplicate(), file_limit * 5 )
|
||||
|
||||
call.SetLabel( 'start a new downloader for this to fill in the gap!' )
|
||||
|
||||
|
@ -1707,6 +1706,8 @@ class SubscriptionsManager( object ):
|
|||
|
||||
self._wake_event = threading.Event()
|
||||
|
||||
self._big_pauser = HydrusData.BigJobPauser( wait_time = 0.8 )
|
||||
|
||||
self._controller.sub( self, 'Shutdown', 'shutdown' )
|
||||
|
||||
|
||||
|
@ -1726,11 +1727,6 @@ class SubscriptionsManager( object ):
|
|||
|
||||
|
||||
|
||||
if done_some:
|
||||
|
||||
gc.collect()
|
||||
|
||||
|
||||
|
||||
def _GetMainLoopWaitTime( self ):
|
||||
|
||||
|
@ -1901,6 +1897,8 @@ class SubscriptionsManager( object ):
|
|||
wait_time = self._GetMainLoopWaitTime()
|
||||
|
||||
|
||||
self._big_pauser.Pause()
|
||||
|
||||
self._wake_event.wait( wait_time )
|
||||
|
||||
self._wake_event.clear()
|
||||
|
|
|
@ -96,7 +96,17 @@ def ParseClientAPIPOSTByteArgs( args ):
|
|||
|
||||
try:
|
||||
|
||||
v = bytes.fromhex( parsed_request_args[ var_name ] )
|
||||
raw_value = parsed_request_args[ var_name ]
|
||||
|
||||
# In JSON, if someone puts 'null' for an optional value, treat that as 'did not enter anything'
|
||||
if raw_value is None:
|
||||
|
||||
del parsed_request_args[ var_name ]
|
||||
|
||||
continue
|
||||
|
||||
|
||||
v = bytes.fromhex( raw_value )
|
||||
|
||||
if len( v ) == 0:
|
||||
|
||||
|
@ -120,7 +130,17 @@ def ParseClientAPIPOSTByteArgs( args ):
|
|||
|
||||
try:
|
||||
|
||||
v_list = [ bytes.fromhex( hash_hex ) for hash_hex in parsed_request_args[ var_name ] ]
|
||||
raw_value = parsed_request_args[ var_name ]
|
||||
|
||||
# In JSON, if someone puts 'null' for an optional value, treat that as 'did not enter anything'
|
||||
if raw_value is None:
|
||||
|
||||
del parsed_request_args[ var_name ]
|
||||
|
||||
continue
|
||||
|
||||
|
||||
v_list = [ bytes.fromhex( hash_hex ) for hash_hex in raw_value ]
|
||||
|
||||
v_list = [ v for v in v_list if len( v ) > 0 ]
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ from hydrus.core import HydrusSerialisable
|
|||
from hydrus.core.networking import HydrusNetworking
|
||||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientStrings
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.networking import ClientNetworkingContexts
|
||||
|
||||
|
@ -40,12 +40,6 @@ def AddCookieToSession( session, name, value, domain, path, expires, secure = Fa
|
|||
|
||||
session.cookies.set_cookie( cookie )
|
||||
|
||||
def AlphabetiseQueryText( query_text ):
|
||||
|
||||
( query_dict, param_order ) = ConvertQueryTextToDict( query_text )
|
||||
|
||||
return ConvertQueryDictToText( query_dict )
|
||||
|
||||
def ConvertDomainIntoAllApplicableDomains( domain, discard_www = True ):
|
||||
|
||||
# is an ip address or localhost, possibly with a port
|
||||
|
@ -119,21 +113,53 @@ def ConvertHTTPToHTTPS( url ):
|
|||
raise Exception( 'Given a url that did not have a scheme!' )
|
||||
|
||||
|
||||
def ConvertQueryDictToText( query_dict, param_order = None ):
|
||||
def ConvertQueryDictToText( query_dict, single_value_parameters, param_order = None ):
|
||||
|
||||
# we now do everything with requests, which does all the unicode -> %20 business naturally, phew
|
||||
# we still want to call str explicitly to coerce integers and so on that'll slip in here and there
|
||||
|
||||
if param_order is None:
|
||||
|
||||
param_pairs = sorted( query_dict.items() )
|
||||
param_order = sorted( query_dict.keys() )
|
||||
|
||||
else:
|
||||
single_value_parameters = list( single_value_parameters )
|
||||
single_value_parameters.sort()
|
||||
|
||||
param_pairs = [ ( key, query_dict[ key ] ) for key in param_order if key in query_dict ]
|
||||
for i in range( len( single_value_parameters ) ):
|
||||
|
||||
param_order.append( None )
|
||||
|
||||
|
||||
|
||||
query_text = '&'.join( ( str( key ) + '=' + str( value ) for ( key, value ) in param_pairs ) )
|
||||
params = []
|
||||
|
||||
single_value_parameter_index = 0
|
||||
|
||||
for key in param_order:
|
||||
|
||||
if key is None:
|
||||
|
||||
try:
|
||||
|
||||
params.append( single_value_parameters[ single_value_parameter_index ] )
|
||||
|
||||
except IndexError:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
single_value_parameter_index += 1
|
||||
|
||||
else:
|
||||
|
||||
if key in query_dict:
|
||||
|
||||
params.append( '{}={}'.format( key, query_dict[ key ] ) )
|
||||
|
||||
|
||||
|
||||
|
||||
query_text = '&'.join( params )
|
||||
|
||||
return query_text
|
||||
|
||||
|
@ -153,6 +179,7 @@ def ConvertQueryTextToDict( query_text ):
|
|||
param_order = []
|
||||
|
||||
query_dict = {}
|
||||
single_value_parameters = []
|
||||
|
||||
pairs = query_text.split( '&' )
|
||||
|
||||
|
@ -162,7 +189,38 @@ def ConvertQueryTextToDict( query_text ):
|
|||
|
||||
# for the moment, ignore tracker bugs and so on that have only key and no value
|
||||
|
||||
if len( result ) == 2:
|
||||
if len( result ) == 1:
|
||||
|
||||
( value, ) = result
|
||||
|
||||
if value == '':
|
||||
|
||||
continue
|
||||
|
||||
|
||||
try:
|
||||
|
||||
unquoted_value = urllib.parse.unquote( value )
|
||||
|
||||
if True not in ( bad_char in unquoted_value for bad_char in bad_chars ):
|
||||
|
||||
requoted_value = urllib.parse.quote( unquoted_value )
|
||||
|
||||
if requoted_value == value:
|
||||
|
||||
value = unquoted_value
|
||||
|
||||
|
||||
|
||||
except:
|
||||
|
||||
pass
|
||||
|
||||
|
||||
single_value_parameters.append( value )
|
||||
param_order.append( None )
|
||||
|
||||
elif len( result ) == 2:
|
||||
|
||||
( key, value ) = result
|
||||
|
||||
|
@ -210,7 +268,7 @@ def ConvertQueryTextToDict( query_text ):
|
|||
|
||||
|
||||
|
||||
return ( query_dict, param_order )
|
||||
return ( query_dict, single_value_parameters, param_order )
|
||||
|
||||
def ConvertURLClassesIntoAPIPairs( url_classes ):
|
||||
|
||||
|
@ -1772,7 +1830,11 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
|
|||
netloc = p.netloc
|
||||
path = p.path
|
||||
params = p.params
|
||||
query = AlphabetiseQueryText( p.query )
|
||||
|
||||
( query_dict, single_value_parameters, param_order ) = ConvertQueryTextToDict( p.query )
|
||||
|
||||
query = ConvertQueryDictToText( query_dict, single_value_parameters )
|
||||
|
||||
fragment = ''
|
||||
|
||||
r = urllib.parse.ParseResult( scheme, netloc, path, params, query, fragment )
|
||||
|
@ -2979,7 +3041,7 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_URL_CLASS
|
||||
SERIALISABLE_NAME = 'URL Class'
|
||||
SERIALISABLE_VERSION = 10
|
||||
SERIALISABLE_VERSION = 11
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -2990,6 +3052,8 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
netloc = 'hostname.com',
|
||||
path_components = None,
|
||||
parameters = None,
|
||||
has_single_value_parameters = False,
|
||||
single_value_parameters_string_match = None,
|
||||
header_overrides = None,
|
||||
api_lookup_converter = None,
|
||||
send_referral_url = SEND_REFERRAL_URL_ONLY_IF_PROVIDED,
|
||||
|
@ -3014,16 +3078,21 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
path_components = []
|
||||
|
||||
path_components.append( ( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'post', example_string = 'post' ), None ) )
|
||||
path_components.append( ( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'page.php', example_string = 'page.php' ), None ) )
|
||||
path_components.append( ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'post', example_string = 'post' ), None ) )
|
||||
path_components.append( ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'page.php', example_string = 'page.php' ), None ) )
|
||||
|
||||
|
||||
if parameters is None:
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters[ 's' ] = ( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'view', example_string = 'view' ), None )
|
||||
parameters[ 'id' ] = ( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FLEXIBLE, match_value = ClientParsing.NUMERIC, example_string = '123456' ), None )
|
||||
parameters[ 's' ] = ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'view', example_string = 'view' ), None )
|
||||
parameters[ 'id' ] = ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FLEXIBLE, match_value = ClientStrings.NUMERIC, example_string = '123456' ), None )
|
||||
|
||||
|
||||
if single_value_parameters_string_match is None:
|
||||
|
||||
single_value_parameters_string_match = ClientStrings.StringMatch()
|
||||
|
||||
|
||||
if header_overrides is None:
|
||||
|
@ -3033,12 +3102,12 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
if api_lookup_converter is None:
|
||||
|
||||
api_lookup_converter = ClientParsing.StringConverter( example_string = 'https://hostname.com/post/page.php?id=123456&s=view' )
|
||||
api_lookup_converter = ClientStrings.StringConverter( example_string = 'https://hostname.com/post/page.php?id=123456&s=view' )
|
||||
|
||||
|
||||
if referral_url_converter is None:
|
||||
|
||||
referral_url_converter = ClientParsing.StringConverter( example_string = 'https://hostname.com/post/page.php?id=123456&s=view' )
|
||||
referral_url_converter = ClientStrings.StringConverter( example_string = 'https://hostname.com/post/page.php?id=123456&s=view' )
|
||||
|
||||
|
||||
# if the args are not serialisable stuff, lets overwrite here
|
||||
|
@ -3062,6 +3131,8 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
self._path_components = path_components
|
||||
self._parameters = parameters
|
||||
self._has_single_value_parameters = has_single_value_parameters
|
||||
self._single_value_parameters_string_match = single_value_parameters_string_match
|
||||
self._header_overrides = header_overrides
|
||||
self._api_lookup_converter = api_lookup_converter
|
||||
|
||||
|
@ -3145,7 +3216,7 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
def _ClipAndFleshOutQuery( self, query, allow_clip = True ):
|
||||
|
||||
( query_dict, param_order ) = ConvertQueryTextToDict( query )
|
||||
( query_dict, single_value_parameters, param_order ) = ConvertQueryTextToDict( query )
|
||||
|
||||
if allow_clip:
|
||||
|
||||
|
@ -3164,6 +3235,8 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
query_dict[ key ] = default
|
||||
|
||||
param_order.append( key )
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -3172,7 +3245,12 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
param_order = None
|
||||
|
||||
|
||||
query = ConvertQueryDictToText( query_dict, param_order = param_order )
|
||||
if not self._has_single_value_parameters:
|
||||
|
||||
single_value_parameters = []
|
||||
|
||||
|
||||
query = ConvertQueryDictToText( query_dict, single_value_parameters, param_order = param_order )
|
||||
|
||||
return query
|
||||
|
||||
|
@ -3181,25 +3259,63 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
serialisable_url_class_key = self._url_class_key.hex()
|
||||
serialisable_path_components = [ ( string_match.GetSerialisableTuple(), default ) for ( string_match, default ) in self._path_components ]
|
||||
serialisable_parameters = [ ( key, ( string_match.GetSerialisableTuple(), default ) ) for ( key, ( string_match, default ) ) in list(self._parameters.items()) ]
|
||||
serialisable_parameters = [ ( key, ( string_match.GetSerialisableTuple(), default ) ) for ( key, ( string_match, default ) ) in self._parameters.items() ]
|
||||
serialisable_single_value_parameters_string_match = self._single_value_parameters_string_match.GetSerialisableTuple()
|
||||
serialisable_header_overrides = list( self._header_overrides.items() )
|
||||
serialisable_api_lookup_converter = self._api_lookup_converter.GetSerialisableTuple()
|
||||
serialisable_referral_url_converter = self._referral_url_converter.GetSerialisableTuple()
|
||||
|
||||
booleans = ( self._match_subdomains, self._keep_matched_subdomains, self._alphabetise_get_parameters, self._can_produce_multiple_files, self._should_be_associated_with_files, self._keep_fragment )
|
||||
|
||||
return ( serialisable_url_class_key, self._url_type, self._preferred_scheme, self._netloc, booleans, serialisable_path_components, serialisable_parameters, serialisable_header_overrides, serialisable_api_lookup_converter, self._send_referral_url, serialisable_referral_url_converter, self._gallery_index_type, self._gallery_index_identifier, self._gallery_index_delta, self._example_url )
|
||||
return (
|
||||
serialisable_url_class_key,
|
||||
self._url_type,
|
||||
self._preferred_scheme,
|
||||
self._netloc,
|
||||
booleans,
|
||||
serialisable_path_components,
|
||||
serialisable_parameters,
|
||||
self._has_single_value_parameters,
|
||||
serialisable_single_value_parameters_string_match,
|
||||
serialisable_header_overrides,
|
||||
serialisable_api_lookup_converter,
|
||||
self._send_referral_url,
|
||||
serialisable_referral_url_converter,
|
||||
self._gallery_index_type,
|
||||
self._gallery_index_identifier,
|
||||
self._gallery_index_delta,
|
||||
self._example_url
|
||||
)
|
||||
|
||||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
( serialisable_url_class_key, self._url_type, self._preferred_scheme, self._netloc, booleans, serialisable_path_components, serialisable_parameters, serialisable_header_overrides, serialisable_api_lookup_converter, self._send_referral_url, serialisable_referral_url_converter, self._gallery_index_type, self._gallery_index_identifier, self._gallery_index_delta, self._example_url ) = serialisable_info
|
||||
(
|
||||
serialisable_url_class_key,
|
||||
self._url_type,
|
||||
self._preferred_scheme,
|
||||
self._netloc,
|
||||
booleans,
|
||||
serialisable_path_components,
|
||||
serialisable_parameters,
|
||||
self._has_single_value_parameters,
|
||||
serialisable_single_value_parameters_string_match,
|
||||
serialisable_header_overrides,
|
||||
serialisable_api_lookup_converter,
|
||||
self._send_referral_url,
|
||||
serialisable_referral_url_converter,
|
||||
self._gallery_index_type,
|
||||
self._gallery_index_identifier,
|
||||
self._gallery_index_delta,
|
||||
self._example_url
|
||||
) = serialisable_info
|
||||
|
||||
( self._match_subdomains, self._keep_matched_subdomains, self._alphabetise_get_parameters, self._can_produce_multiple_files, self._should_be_associated_with_files, self._keep_fragment ) = booleans
|
||||
|
||||
self._url_class_key = bytes.fromhex( serialisable_url_class_key )
|
||||
self._path_components = [ ( HydrusSerialisable.CreateFromSerialisableTuple( serialisable_string_match ), default ) for ( serialisable_string_match, default ) in serialisable_path_components ]
|
||||
self._parameters = { key : ( HydrusSerialisable.CreateFromSerialisableTuple( serialisable_string_match ), default ) for ( key, ( serialisable_string_match, default ) ) in serialisable_parameters }
|
||||
self._single_value_parameters_string_match = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_single_value_parameters_string_match )
|
||||
self._header_overrides = dict( serialisable_header_overrides )
|
||||
self._api_lookup_converter = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_api_lookup_converter )
|
||||
self._referral_url_converter = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_referral_url_converter )
|
||||
|
@ -3215,7 +3331,7 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
serialisable_url_class_key = url_class_key.hex()
|
||||
|
||||
api_lookup_converter = ClientParsing.StringConverter( example_string = example_url )
|
||||
api_lookup_converter = ClientStrings.StringConverter( example_string = example_url )
|
||||
|
||||
serialisable_api_lookup_converter = api_lookup_converter.GetSerialisableTuple()
|
||||
|
||||
|
@ -3289,7 +3405,7 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
( serialisable_url_class_key, url_type, preferred_scheme, netloc, match_subdomains, keep_matched_subdomains, serialisable_path_components, serialisable_parameters, serialisable_api_lookup_converter, can_produce_multiple_files, should_be_associated_with_files, gallery_index_type, gallery_index_identifier, gallery_index_delta, example_url ) = old_serialisable_info
|
||||
|
||||
send_referral_url = SEND_REFERRAL_URL_ONLY_IF_PROVIDED
|
||||
referral_url_converter = ClientParsing.StringConverter( example_string = 'https://hostname.com/post/page.php?id=123456&s=view' )
|
||||
referral_url_converter = ClientStrings.StringConverter( example_string = 'https://hostname.com/post/page.php?id=123456&s=view' )
|
||||
|
||||
serialisable_referrel_url_converter = referral_url_converter.GetSerialisableTuple()
|
||||
|
||||
|
@ -3335,6 +3451,38 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
return ( 10, new_serialisable_info )
|
||||
|
||||
|
||||
if version == 10:
|
||||
|
||||
( serialisable_url_class_key, url_type, preferred_scheme, netloc, booleans, serialisable_path_components, serialisable_parameters, serialisable_header_overrides, serialisable_api_lookup_converter, send_referral_url, serialisable_referrel_url_converter, gallery_index_type, gallery_index_identifier, gallery_index_delta, example_url ) = old_serialisable_info
|
||||
|
||||
has_single_value_parameters = False
|
||||
single_value_parameters_string_match = ClientStrings.StringMatch()
|
||||
|
||||
serialisable_single_value_parameters_match = single_value_parameters_string_match.GetSerialisableTuple()
|
||||
|
||||
new_serialisable_info = (
|
||||
serialisable_url_class_key,
|
||||
url_type,
|
||||
preferred_scheme,
|
||||
netloc,
|
||||
booleans,
|
||||
serialisable_path_components,
|
||||
serialisable_parameters,
|
||||
has_single_value_parameters,
|
||||
serialisable_single_value_parameters_match,
|
||||
serialisable_header_overrides,
|
||||
serialisable_api_lookup_converter,
|
||||
send_referral_url,
|
||||
serialisable_referrel_url_converter,
|
||||
gallery_index_type,
|
||||
gallery_index_identifier,
|
||||
gallery_index_delta,
|
||||
example_url
|
||||
)
|
||||
|
||||
return ( 11, new_serialisable_info )
|
||||
|
||||
|
||||
|
||||
def AlphabetiseGetParameters( self ):
|
||||
|
||||
|
@ -3455,7 +3603,7 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
page_index_name = self._gallery_index_identifier
|
||||
|
||||
( query_dict, param_order ) = ConvertQueryTextToDict( query )
|
||||
( query_dict, single_value_parameters, param_order ) = ConvertQueryTextToDict( query )
|
||||
|
||||
if page_index_name not in query_dict:
|
||||
|
||||
|
@ -3480,7 +3628,12 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
param_order = None
|
||||
|
||||
|
||||
query = ConvertQueryDictToText( query_dict, param_order = param_order )
|
||||
if not self._has_single_value_parameters:
|
||||
|
||||
single_value_parameters = []
|
||||
|
||||
|
||||
query = ConvertQueryDictToText( query_dict, single_value_parameters, param_order = param_order )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -3534,6 +3687,11 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
return 'URL Class "' + self._name + '" - ' + ConvertURLIntoDomain( self.GetExampleURL() )
|
||||
|
||||
|
||||
def GetSingleValueParameterData( self ):
|
||||
|
||||
return ( self._has_single_value_parameters, self._single_value_parameters_string_match )
|
||||
|
||||
|
||||
def GetSortingComplexityKey( self ):
|
||||
|
||||
# we sort url classes so that
|
||||
|
@ -3649,9 +3807,9 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
self._url_class_key = HydrusData.GenerateKey()
|
||||
|
||||
|
||||
def SetExampleURL( self, example_url ):
|
||||
def SetAlphabetiseGetParameters( self, alphabetise_get_parameters: bool ):
|
||||
|
||||
self._example_url = example_url
|
||||
self._alphabetise_get_parameters = alphabetise_get_parameters
|
||||
|
||||
|
||||
def SetClassKey( self, match_key ):
|
||||
|
@ -3659,6 +3817,17 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
self._url_class_key = match_key
|
||||
|
||||
|
||||
def SetExampleURL( self, example_url ):
|
||||
|
||||
self._example_url = example_url
|
||||
|
||||
|
||||
def SetSingleValueParameterData( self, has_single_value_parameters: bool, single_value_parameters_string_match: ClientStrings.StringMatch ):
|
||||
|
||||
self._has_single_value_parameters = has_single_value_parameters
|
||||
self._single_value_parameters_string_match = single_value_parameters_string_match
|
||||
|
||||
|
||||
def SetURLBooleans(
|
||||
self,
|
||||
match_subdomains: bool,
|
||||
|
@ -3731,9 +3900,9 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
|
||||
|
||||
( url_parameters, param_order ) = ConvertQueryTextToDict( p.query )
|
||||
( url_parameters, single_value_parameters, param_order ) = ConvertQueryTextToDict( p.query )
|
||||
|
||||
for ( key, ( string_match, default ) ) in list(self._parameters.items()):
|
||||
for ( key, ( string_match, default ) ) in self._parameters.items():
|
||||
|
||||
if key not in url_parameters:
|
||||
|
||||
|
@ -3759,6 +3928,26 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
|
||||
|
||||
if self._has_single_value_parameters:
|
||||
|
||||
if len( single_value_parameters ) == 0:
|
||||
|
||||
raise HydrusExceptions.URLClassException( 'Was expecting single-value parameter(s), but this URL did not seem to have any.' )
|
||||
|
||||
|
||||
for single_value_parameter in single_value_parameters:
|
||||
|
||||
try:
|
||||
|
||||
self._single_value_parameters_string_match.Test( single_value_parameter )
|
||||
|
||||
except HydrusExceptions.StringMatchException as e:
|
||||
|
||||
raise HydrusExceptions.URLClassException( str( e ) )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def ToTuple( self ):
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ from hydrus.core import HydrusSerialisable
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientStrings
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.networking import ClientNetworkingContexts
|
||||
from hydrus.client.networking import ClientNetworkingDomain
|
||||
|
@ -818,7 +819,7 @@ class LoginCredentialDefinition( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
if string_match is None:
|
||||
|
||||
string_match = ClientParsing.StringMatch()
|
||||
string_match = ClientStrings.StringMatch()
|
||||
|
||||
|
||||
HydrusSerialisable.SerialisableBaseNamed.__init__( self, name )
|
||||
|
@ -1100,7 +1101,7 @@ class LoginScriptDomain( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
for ( name, value_string_match ) in list(old_required_cookies_info.items()):
|
||||
|
||||
key_string_match = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = name, example_string = name )
|
||||
key_string_match = ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = name, example_string = name )
|
||||
|
||||
new_required_cookies_info[ key_string_match ] = value_string_match
|
||||
|
||||
|
@ -1533,7 +1534,7 @@ class LoginStep( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
for ( name, value_string_match ) in list(old_required_cookies_info.items()):
|
||||
|
||||
key_string_match = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = name, example_string = name )
|
||||
key_string_match = ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = name, example_string = name )
|
||||
|
||||
new_required_cookies_info[ key_string_match ] = value_string_match
|
||||
|
||||
|
@ -1649,9 +1650,11 @@ class LoginStep( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
params = ''
|
||||
fragment = ''
|
||||
|
||||
single_value_parameters = []
|
||||
|
||||
if self._method == 'GET':
|
||||
|
||||
query = ClientNetworkingDomain.ConvertQueryDictToText( query_dict )
|
||||
query = ClientNetworkingDomain.ConvertQueryDictToText( query_dict, single_value_parameters )
|
||||
body = None
|
||||
test_result_body = ''
|
||||
|
||||
|
@ -1659,7 +1662,7 @@ class LoginStep( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
query = ''
|
||||
body = query_dict
|
||||
test_result_body = ClientNetworkingDomain.ConvertQueryDictToText( query_dict )
|
||||
test_result_body = ClientNetworkingDomain.ConvertQueryDictToText( query_dict, single_value_parameters )
|
||||
|
||||
|
||||
r = urllib.parse.ParseResult( scheme, netloc, path, params, query, fragment )
|
||||
|
|
|
@ -81,7 +81,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 20
|
||||
SOFTWARE_VERSION = 455
|
||||
SOFTWARE_VERSION = 456
|
||||
CLIENT_API_VERSION = 20
|
||||
|
||||
SERVER_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import collections
|
||||
import gc
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
|
@ -619,8 +618,6 @@ class HydrusController( object ):
|
|||
|
||||
def MaintainMemorySlow( self ):
|
||||
|
||||
gc.collect()
|
||||
|
||||
HydrusPaths.CleanUpOldTempPaths()
|
||||
|
||||
self._MaintainCallToThreads()
|
||||
|
|
|
@ -416,9 +416,23 @@ class DBCursorTransactionWrapper( DBBase ):
|
|||
|
||||
def Save( self ):
|
||||
|
||||
self._Execute( 'RELEASE hydrus_savepoint;' )
|
||||
|
||||
self._Execute( 'SAVEPOINT hydrus_savepoint;' )
|
||||
if self._in_transaction:
|
||||
|
||||
try:
|
||||
|
||||
self._Execute( 'RELEASE hydrus_savepoint;' )
|
||||
|
||||
except sqlite3.OperationalError:
|
||||
|
||||
HydrusData.Print( 'Tried to release a database savepoint, but failed!' )
|
||||
|
||||
|
||||
self._Execute( 'SAVEPOINT hydrus_savepoint;' )
|
||||
|
||||
else:
|
||||
|
||||
HydrusData.Print( 'Received a call to save, but was not in a transaction!' )
|
||||
|
||||
|
||||
|
||||
def TimeToCommit( self ):
|
||||
|
|
|
@ -64,6 +64,7 @@ phash_generation_report_mode = False
|
|||
network_report_mode = False
|
||||
pubsub_report_mode = False
|
||||
daemon_report_mode = False
|
||||
mpv_report_mode = False
|
||||
force_idle_mode = False
|
||||
no_page_limit_mode = False
|
||||
thumbnail_debug_mode = False
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import gc
|
||||
import os
|
||||
import psutil
|
||||
import re
|
||||
|
@ -99,8 +98,6 @@ def CleanUpTempPath( os_file_handle, temp_path ):
|
|||
|
||||
except OSError:
|
||||
|
||||
gc.collect()
|
||||
|
||||
try:
|
||||
|
||||
os.close( os_file_handle )
|
||||
|
|
|
@ -2568,6 +2568,8 @@ class ServerServiceRestricted( ServerService ):
|
|||
|
||||
dictionary[ 'bandwidth_rules' ] = self._bandwidth_rules
|
||||
|
||||
dictionary[ 'service_options' ] = self._service_options
|
||||
|
||||
dictionary[ 'server_message' ] = self._server_message
|
||||
|
||||
return dictionary
|
||||
|
|
|
@ -398,7 +398,8 @@ class ParsedRequestArguments( dict ):
|
|||
|
||||
def GetValue( self, key, expected_type, expected_list_type = None, default_value = None ):
|
||||
|
||||
if key in self:
|
||||
# not None because in JSON sometimes people put 'null' to mean 'did not enter this optional parameter'
|
||||
if key in self and self[ key ] is not None:
|
||||
|
||||
value = self[ key ]
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ from hydrus.core import HydrusGlobals as HG
|
|||
from hydrus.core.networking import HydrusNetworking
|
||||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientStrings
|
||||
from hydrus.client import ClientServices
|
||||
from hydrus.client.networking import ClientNetworking
|
||||
from hydrus.client.networking import ClientNetworkingBandwidth
|
||||
|
@ -240,13 +240,13 @@ class TestNetworkingDomain( unittest.TestCase ):
|
|||
|
||||
path_components = []
|
||||
|
||||
path_components.append( ( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'post', example_string = 'post' ), None ) )
|
||||
path_components.append( ( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'page.php', example_string = 'page.php' ), None ) )
|
||||
path_components.append( ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'post', example_string = 'post' ), None ) )
|
||||
path_components.append( ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'page.php', example_string = 'page.php' ), None ) )
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters[ 's' ] = ( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'view', example_string = 'view' ), None )
|
||||
parameters[ 'id' ] = ( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FLEXIBLE, match_value = ClientParsing.NUMERIC, example_string = '123456' ), None )
|
||||
parameters[ 's' ] = ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'view', example_string = 'view' ), None )
|
||||
parameters[ 'id' ] = ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FLEXIBLE, match_value = ClientStrings.NUMERIC, example_string = '123456' ), None )
|
||||
|
||||
send_referral_url = ClientNetworkingDomain.SEND_REFERRAL_URL_ONLY_IF_PROVIDED
|
||||
referral_url_converter = None
|
||||
|
@ -305,9 +305,9 @@ class TestNetworkingDomain( unittest.TestCase ):
|
|||
|
||||
conversions = []
|
||||
|
||||
conversions.append( ( ClientParsing.STRING_CONVERSION_REGEX_SUB, ( 'testbooru.cx', 'replace.com' ) ) )
|
||||
conversions.append( ( ClientStrings.STRING_CONVERSION_REGEX_SUB, ( 'testbooru.cx', 'replace.com' ) ) )
|
||||
|
||||
referral_url_converter = ClientParsing.StringConverter( conversions, good_url )
|
||||
referral_url_converter = ClientStrings.StringConverter( conversions, good_url )
|
||||
|
||||
send_referral_url = ClientNetworkingDomain.SEND_REFERRAL_URL_CONVERTER_IF_NONE_PROVIDED
|
||||
|
||||
|
@ -344,8 +344,8 @@ class TestNetworkingDomain( unittest.TestCase ):
|
|||
|
||||
path_components = []
|
||||
|
||||
path_components.append( ( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = 'file', example_string = 'file' ), None ) )
|
||||
path_components.append( ( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_ANY ), None ) )
|
||||
path_components.append( ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'file', example_string = 'file' ), None ) )
|
||||
path_components.append( ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_ANY ), None ) )
|
||||
|
||||
parameters = {}
|
||||
|
||||
|
@ -372,6 +372,84 @@ class TestNetworkingDomain( unittest.TestCase ):
|
|||
|
||||
self.assertEqual( url_class.Normalise( example_url ), example_url )
|
||||
|
||||
# single-value params test
|
||||
|
||||
single_value_good_url = 'https://testbooru.cx/post/page.php?id=123456&token&s=view'
|
||||
single_value_bad_url = 'https://testbooru.cx/post/page.php?id=123456&bad_token&s=view'
|
||||
single_value_missing_url = 'https://testbooru.cx/post/page.php?id=123456&s=view'
|
||||
single_value_good_url_multiple = 'https://testbooru.cx/post/page.php?id=123456&token1&token2&s=view&token0'
|
||||
|
||||
single_value_good_url_alphabetical_normalised = 'https://testbooru.cx/post/page.php?id=123456&s=view&token'
|
||||
single_value_good_url_multiple_alphabetical_normalised = 'https://testbooru.cx/post/page.php?id=123456&s=view&token0&token1&token2'
|
||||
|
||||
name = 'single value lad'
|
||||
url_type = HC.URL_TYPE_POST
|
||||
preferred_scheme = 'https'
|
||||
netloc = 'testbooru.cx'
|
||||
|
||||
alphabetise_get_parameters = True
|
||||
match_subdomains = False
|
||||
keep_matched_subdomains = False
|
||||
can_produce_multiple_files = False
|
||||
should_be_associated_with_files = True
|
||||
keep_fragment = False
|
||||
|
||||
path_components = []
|
||||
|
||||
path_components.append( ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'post', example_string = 'post' ), None ) )
|
||||
path_components.append( ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'page.php', example_string = 'page.php' ), None ) )
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters[ 's' ] = ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = 'view', example_string = 'view' ), None )
|
||||
parameters[ 'id' ] = ( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FLEXIBLE, match_value = ClientStrings.NUMERIC, example_string = '123456' ), None )
|
||||
|
||||
has_single_value_parameters = True
|
||||
single_value_parameters_string_match = ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_REGEX, match_value = '^token.*', example_string = 'token1' )
|
||||
|
||||
example_url = single_value_good_url
|
||||
|
||||
url_class = ClientNetworkingDomain.URLClass(
|
||||
name,
|
||||
url_type = url_type,
|
||||
preferred_scheme = preferred_scheme,
|
||||
netloc = netloc,
|
||||
path_components = path_components,
|
||||
parameters = parameters,
|
||||
has_single_value_parameters = has_single_value_parameters,
|
||||
single_value_parameters_string_match = single_value_parameters_string_match,
|
||||
send_referral_url = send_referral_url,
|
||||
referral_url_converter = referral_url_converter,
|
||||
gallery_index_type = gallery_index_type,
|
||||
gallery_index_identifier = gallery_index_identifier,
|
||||
gallery_index_delta = gallery_index_delta,
|
||||
example_url = example_url
|
||||
)
|
||||
|
||||
url_class.SetURLBooleans( match_subdomains, keep_matched_subdomains, alphabetise_get_parameters, can_produce_multiple_files, should_be_associated_with_files, keep_fragment )
|
||||
|
||||
self.assertEqual( url_class.Normalise( single_value_good_url ), single_value_good_url_alphabetical_normalised )
|
||||
self.assertEqual( url_class.Normalise( single_value_good_url_multiple ), single_value_good_url_multiple_alphabetical_normalised )
|
||||
|
||||
self.assertEqual( url_class.Matches( single_value_good_url ), True )
|
||||
self.assertEqual( url_class.Matches( single_value_good_url_alphabetical_normalised ), True )
|
||||
self.assertEqual( url_class.Matches( single_value_good_url_multiple ), True )
|
||||
self.assertEqual( url_class.Matches( single_value_good_url_multiple_alphabetical_normalised ), True )
|
||||
self.assertEqual( url_class.Matches( single_value_bad_url ), False )
|
||||
self.assertEqual( url_class.Matches( single_value_missing_url ), False )
|
||||
|
||||
url_class.SetAlphabetiseGetParameters( False )
|
||||
|
||||
self.assertEqual( url_class.Normalise( single_value_good_url ), single_value_good_url )
|
||||
self.assertEqual( url_class.Normalise( single_value_good_url_multiple ), single_value_good_url_multiple )
|
||||
|
||||
self.assertEqual( url_class.Matches( single_value_good_url ), True )
|
||||
self.assertEqual( url_class.Matches( single_value_good_url_alphabetical_normalised ), True )
|
||||
self.assertEqual( url_class.Matches( single_value_good_url_multiple ), True )
|
||||
self.assertEqual( url_class.Matches( single_value_good_url_multiple_alphabetical_normalised ), True )
|
||||
self.assertEqual( url_class.Matches( single_value_bad_url ), False )
|
||||
self.assertEqual( url_class.Matches( single_value_missing_url ), False )
|
||||
|
||||
|
||||
class TestNetworkingEngine( unittest.TestCase ):
|
||||
|
||||
|
|
|
@ -5,121 +5,121 @@ import unittest
|
|||
from hydrus.core import HydrusConstants as HC
|
||||
from hydrus.core import HydrusExceptions
|
||||
|
||||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientStrings
|
||||
|
||||
class TestStringConverter( unittest.TestCase ):
|
||||
|
||||
def test_basics( self ):
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING, 1 ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING, 1 ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), '123456789' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_END, 1 ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_END, 1 ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), '012345678' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING, 7 ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING, 7 ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), '0123456' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_END, 7 ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_END, 7 ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), '3456789' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_PREPEND_TEXT, 'abc' ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_PREPEND_TEXT, 'abc' ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), 'abc0123456789' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_APPEND_TEXT, 'xyz' ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_APPEND_TEXT, 'xyz' ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), '0123456789xyz' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_ENCODE, 'url percent encoding' ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_ENCODE, 'url percent encoding' ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '01234 56789' ), '01234%2056789' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_DECODE, 'url percent encoding' ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_DECODE, 'url percent encoding' ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '01234%2056789' ), '01234 56789' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_ENCODE, 'unicode escape characters' ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_ENCODE, 'unicode escape characters' ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '01234\u039456789' ), '01234\\u039456789' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_DECODE, 'unicode escape characters' ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_DECODE, 'unicode escape characters' ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '01234\\u039456789' ), '01234\u039456789' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_ENCODE, 'html entities' ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_ENCODE, 'html entities' ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '01234&56789' ), '01234&56789' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_DECODE, 'html entities' ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_DECODE, 'html entities' ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '01234&56789' ), '01234&56789' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_ENCODE, 'hex' ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_ENCODE, 'hex' ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( b'\xe5\xafW\xa6\x87\xf0\x89\x89O^\xce\xdeP\x04\x94X' ), 'e5af57a687f089894f5ecede50049458' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_ENCODE, 'base64' ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_ENCODE, 'base64' ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( b'\xe5\xafW\xa6\x87\xf0\x89\x89O^\xce\xdeP\x04\x94X' ), '5a9XpofwiYlPXs7eUASUWA==' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_REVERSE, None ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_REVERSE, None ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), '9876543210' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_REGEX_SUB, ( '\\d', 'd' ) ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_REGEX_SUB, ( '\\d', 'd' ) ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( 'abc123' ), 'abcddd' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_DATE_DECODE, ( '%Y-%m-%d %H:%M:%S', HC.TIMEZONE_GMT, 0 ) ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_DATE_DECODE, ( '%Y-%m-%d %H:%M:%S', HC.TIMEZONE_GMT, 0 ) ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '1970-01-02 00:00:00' ), '86400' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_DATE_ENCODE, ( '%Y-%m-%d %H:%M:%S', 0 ) ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_DATE_ENCODE, ( '%Y-%m-%d %H:%M:%S', 0 ) ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '86400' ), '1970-01-02 00:00:00' )
|
||||
|
||||
#
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = [ ( ClientParsing.STRING_CONVERSION_INTEGER_ADDITION, 5 ) ] )
|
||||
string_converter = ClientStrings.StringConverter( conversions = [ ( ClientStrings.STRING_CONVERSION_INTEGER_ADDITION, 5 ) ] )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '4' ), '9' )
|
||||
|
||||
|
@ -128,81 +128,81 @@ class TestStringConverter( unittest.TestCase ):
|
|||
|
||||
conversions = []
|
||||
|
||||
conversions.append( ( ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING, 1 ) )
|
||||
conversions.append( ( ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_BEGINNING, 1 ) )
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = conversions )
|
||||
string_converter = ClientStrings.StringConverter( conversions = conversions )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), '123456789' )
|
||||
|
||||
#
|
||||
|
||||
conversions.append( ( ClientParsing.STRING_CONVERSION_REMOVE_TEXT_FROM_END, 1 ) )
|
||||
conversions.append( ( ClientStrings.STRING_CONVERSION_REMOVE_TEXT_FROM_END, 1 ) )
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = conversions )
|
||||
string_converter = ClientStrings.StringConverter( conversions = conversions )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), '12345678' )
|
||||
|
||||
#
|
||||
|
||||
conversions.append( ( ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING, 7 ) )
|
||||
conversions.append( ( ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_BEGINNING, 7 ) )
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = conversions )
|
||||
string_converter = ClientStrings.StringConverter( conversions = conversions )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), '1234567' )
|
||||
|
||||
#
|
||||
|
||||
conversions.append( ( ClientParsing.STRING_CONVERSION_CLIP_TEXT_FROM_END, 6 ) )
|
||||
conversions.append( ( ClientStrings.STRING_CONVERSION_CLIP_TEXT_FROM_END, 6 ) )
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = conversions )
|
||||
string_converter = ClientStrings.StringConverter( conversions = conversions )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), '234567' )
|
||||
|
||||
#
|
||||
|
||||
conversions.append( ( ClientParsing.STRING_CONVERSION_PREPEND_TEXT, 'abc' ) )
|
||||
conversions.append( ( ClientStrings.STRING_CONVERSION_PREPEND_TEXT, 'abc' ) )
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = conversions )
|
||||
string_converter = ClientStrings.StringConverter( conversions = conversions )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), 'abc234567' )
|
||||
|
||||
#
|
||||
|
||||
conversions.append( ( ClientParsing.STRING_CONVERSION_APPEND_TEXT, 'x z' ) )
|
||||
conversions.append( ( ClientStrings.STRING_CONVERSION_APPEND_TEXT, 'x z' ) )
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = conversions )
|
||||
string_converter = ClientStrings.StringConverter( conversions = conversions )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), 'abc234567x z' )
|
||||
|
||||
#
|
||||
|
||||
conversions.append( ( ClientParsing.STRING_CONVERSION_ENCODE, 'url percent encoding' ) )
|
||||
conversions.append( ( ClientStrings.STRING_CONVERSION_ENCODE, 'url percent encoding' ) )
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = conversions )
|
||||
string_converter = ClientStrings.StringConverter( conversions = conversions )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), 'abc234567x%20z' )
|
||||
|
||||
#
|
||||
|
||||
conversions.append( ( ClientParsing.STRING_CONVERSION_DECODE, 'url percent encoding' ) )
|
||||
conversions.append( ( ClientStrings.STRING_CONVERSION_DECODE, 'url percent encoding' ) )
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = conversions )
|
||||
string_converter = ClientStrings.StringConverter( conversions = conversions )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), 'abc234567x z' )
|
||||
|
||||
#
|
||||
|
||||
conversions.append( ( ClientParsing.STRING_CONVERSION_REVERSE, None ) )
|
||||
conversions.append( ( ClientStrings.STRING_CONVERSION_REVERSE, None ) )
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = conversions )
|
||||
string_converter = ClientStrings.StringConverter( conversions = conversions )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), 'z x765432cba' )
|
||||
|
||||
#
|
||||
|
||||
conversions.append( ( ClientParsing.STRING_CONVERSION_REGEX_SUB, ( '\\d', 'd' ) ) )
|
||||
conversions.append( ( ClientStrings.STRING_CONVERSION_REGEX_SUB, ( '\\d', 'd' ) ) )
|
||||
|
||||
string_converter = ClientParsing.StringConverter( conversions = conversions )
|
||||
string_converter = ClientStrings.StringConverter( conversions = conversions )
|
||||
|
||||
self.assertEqual( string_converter.Convert( '0123456789' ), 'z xddddddcba' )
|
||||
|
||||
|
@ -211,7 +211,7 @@ class TestStringMatch( unittest.TestCase ):
|
|||
|
||||
def test_basics( self ):
|
||||
|
||||
all_string_match = ClientParsing.StringMatch()
|
||||
all_string_match = ClientStrings.StringMatch()
|
||||
|
||||
self.assertTrue( all_string_match.Matches( '123' ) )
|
||||
self.assertTrue( all_string_match.Matches( 'abc' ) )
|
||||
|
@ -219,7 +219,7 @@ class TestStringMatch( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
min_string_match = ClientParsing.StringMatch( min_chars = 4 )
|
||||
min_string_match = ClientStrings.StringMatch( min_chars = 4 )
|
||||
|
||||
self.assertFalse( min_string_match.Matches( '123' ) )
|
||||
self.assertFalse( min_string_match.Matches( 'abc' ) )
|
||||
|
@ -227,7 +227,7 @@ class TestStringMatch( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
max_string_match = ClientParsing.StringMatch( max_chars = 4 )
|
||||
max_string_match = ClientStrings.StringMatch( max_chars = 4 )
|
||||
|
||||
self.assertTrue( max_string_match.Matches( '123' ) )
|
||||
self.assertTrue( max_string_match.Matches( 'abc' ) )
|
||||
|
@ -235,7 +235,7 @@ class TestStringMatch( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
min_max_string_match = ClientParsing.StringMatch( min_chars = 4, max_chars = 10 )
|
||||
min_max_string_match = ClientStrings.StringMatch( min_chars = 4, max_chars = 10 )
|
||||
|
||||
self.assertFalse( min_max_string_match.Matches( '123' ) )
|
||||
self.assertFalse( min_max_string_match.Matches( 'abc' ) )
|
||||
|
@ -243,7 +243,7 @@ class TestStringMatch( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
alpha_string_match = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FLEXIBLE, match_value = ClientParsing.ALPHA )
|
||||
alpha_string_match = ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FLEXIBLE, match_value = ClientStrings.ALPHA )
|
||||
|
||||
self.assertFalse( alpha_string_match.Matches( '123' ) )
|
||||
self.assertTrue( alpha_string_match.Matches( 'abc' ) )
|
||||
|
@ -251,7 +251,7 @@ class TestStringMatch( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
alphanum_string_match = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FLEXIBLE, match_value = ClientParsing.ALPHANUMERIC )
|
||||
alphanum_string_match = ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FLEXIBLE, match_value = ClientStrings.ALPHANUMERIC )
|
||||
|
||||
self.assertTrue( alphanum_string_match.Matches( '123' ) )
|
||||
self.assertTrue( alphanum_string_match.Matches( 'abc' ) )
|
||||
|
@ -259,7 +259,7 @@ class TestStringMatch( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
num_string_match = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FLEXIBLE, match_value = ClientParsing.NUMERIC )
|
||||
num_string_match = ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FLEXIBLE, match_value = ClientStrings.NUMERIC )
|
||||
|
||||
self.assertTrue( num_string_match.Matches( '123' ) )
|
||||
self.assertFalse( num_string_match.Matches( 'abc' ) )
|
||||
|
@ -267,7 +267,7 @@ class TestStringMatch( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
fixed_string_match = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FIXED, match_value = '123' )
|
||||
fixed_string_match = ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FIXED, match_value = '123' )
|
||||
|
||||
self.assertTrue( fixed_string_match.Matches( '123' ) )
|
||||
self.assertFalse( fixed_string_match.Matches( 'abc' ) )
|
||||
|
@ -275,7 +275,7 @@ class TestStringMatch( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
re_string_match = ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_REGEX, match_value = '\\d' )
|
||||
re_string_match = ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_REGEX, match_value = '\\d' )
|
||||
|
||||
self.assertTrue( re_string_match.Matches( '123' ) )
|
||||
self.assertFalse( re_string_match.Matches( 'abc' ) )
|
||||
|
@ -301,117 +301,117 @@ class TestStringSlicer( unittest.TestCase ):
|
|||
|
||||
#
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 0, index_end = 1 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 0, index_end = 1 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ a ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 1st string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 3, index_end = 4 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 3, index_end = 4 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ d ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 4th string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = -3, index_end = -2 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = -3, index_end = -2 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ h ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 3rd from last string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = -1 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = -1 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ j ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the last string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 15, index_end = 16 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 15, index_end = 16 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 16th string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = -15, index_end = -14 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = -15, index_end = -14 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 15th from last string' )
|
||||
|
||||
#
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 0 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 0 )
|
||||
self.assertEqual( slicer.Slice( test_list ), test_list )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 1st string and onwards' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 3 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 3 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ d, e, f, g, h, i, j ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 4th string and onwards' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = -3 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = -3 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ h, i, j ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 3rd from last string and onwards' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 15 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 15 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 16th string and onwards' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = -15 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = -15 )
|
||||
self.assertEqual( slicer.Slice( test_list ), test_list )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 15th from last string and onwards' )
|
||||
|
||||
#
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_end = 0 )
|
||||
slicer = ClientStrings.StringSlicer( index_end = 0 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting nothing' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_end = 3 )
|
||||
slicer = ClientStrings.StringSlicer( index_end = 3 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ a, b, c ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting up to and including the 3rd string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_end = -3 )
|
||||
slicer = ClientStrings.StringSlicer( index_end = -3 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ a, b, c, d, e, f, g ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting up to and including the 4th from last string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_end = 15 )
|
||||
slicer = ClientStrings.StringSlicer( index_end = 15 )
|
||||
self.assertEqual( slicer.Slice( test_list ), test_list )
|
||||
self.assertEqual( slicer.ToString(), 'selecting up to and including the 15th string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_end = -15 )
|
||||
slicer = ClientStrings.StringSlicer( index_end = -15 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting up to and including the 16th from last string' )
|
||||
|
||||
#
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 0, index_end = 5 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 0, index_end = 5 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ a, b, c, d, e ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 1st string up to and including the 5th string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 3, index_end = 5 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 3, index_end = 5 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ d, e ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 4th string up to and including the 5th string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = -5, index_end = -3 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = -5, index_end = -3 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ f, g ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 5th from last string up to and including the 4th from last string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 3, index_end = -3 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 3, index_end = -3 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [ d, e, f, g ] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 4th string up to and including the 4th from last string' )
|
||||
|
||||
#
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 3, index_end = 3 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 3, index_end = 3 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting nothing' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 5, index_end = 3 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 5, index_end = 3 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting nothing' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = -3, index_end = -3 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = -3, index_end = -3 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting nothing' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = -3, index_end = -5 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = -3, index_end = -5 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting nothing' )
|
||||
|
||||
#
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = 15, index_end = 20 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = 15, index_end = 20 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 16th string up to and including the 20th string' )
|
||||
|
||||
slicer = ClientParsing.StringSlicer( index_start = -15, index_end = -12 )
|
||||
slicer = ClientStrings.StringSlicer( index_start = -15, index_end = -12 )
|
||||
self.assertEqual( slicer.Slice( test_list ), [] )
|
||||
self.assertEqual( slicer.ToString(), 'selecting the 15th from last string up to and including the 13th from last string' )
|
||||
|
||||
|
@ -438,48 +438,48 @@ class TestStringSorter( unittest.TestCase ):
|
|||
|
||||
|
||||
|
||||
sorter = ClientParsing.StringSorter( sort_type = ClientParsing.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC, asc = True, regex = None )
|
||||
sorter = ClientStrings.StringSorter( sort_type = ClientStrings.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC, asc = True, regex = None )
|
||||
correct = [ a, b, c, d, e ]
|
||||
|
||||
do_sort_test( sorter, correct )
|
||||
|
||||
sorter = ClientParsing.StringSorter( sort_type = ClientParsing.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC, asc = False, regex = None )
|
||||
sorter = ClientStrings.StringSorter( sort_type = ClientStrings.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC, asc = False, regex = None )
|
||||
correct = [ e, d, c, b, a ]
|
||||
|
||||
do_sort_test( sorter, correct )
|
||||
|
||||
#
|
||||
|
||||
sorter = ClientParsing.StringSorter( sort_type = ClientParsing.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT, asc = True, regex = None )
|
||||
sorter = ClientStrings.StringSorter( sort_type = ClientStrings.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT, asc = True, regex = None )
|
||||
correct = [ a, b, c, d, e ]
|
||||
|
||||
do_sort_test( sorter, correct )
|
||||
|
||||
sorter = ClientParsing.StringSorter( sort_type = ClientParsing.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT, asc = False, regex = None )
|
||||
sorter = ClientStrings.StringSorter( sort_type = ClientStrings.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT, asc = False, regex = None )
|
||||
correct = [ e, d, c, b, a ]
|
||||
|
||||
do_sort_test( sorter, correct )
|
||||
|
||||
#
|
||||
|
||||
sorter = ClientParsing.StringSorter( sort_type = ClientParsing.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC, asc = True, regex = '\\d+' )
|
||||
sorter = ClientStrings.StringSorter( sort_type = ClientStrings.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC, asc = True, regex = '\\d+' )
|
||||
correct = [ c, b, a, d, e ]
|
||||
|
||||
do_sort_test( sorter, correct )
|
||||
|
||||
sorter = ClientParsing.StringSorter( sort_type = ClientParsing.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC, asc = False, regex = '\\d+' )
|
||||
sorter = ClientStrings.StringSorter( sort_type = ClientStrings.CONTENT_PARSER_SORT_TYPE_LEXICOGRAPHIC, asc = False, regex = '\\d+' )
|
||||
correct = [ d, a, b, c, e ]
|
||||
|
||||
do_sort_test( sorter, correct )
|
||||
|
||||
#
|
||||
|
||||
sorter = ClientParsing.StringSorter( sort_type = ClientParsing.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT, asc = True, regex = '\\d+' )
|
||||
sorter = ClientStrings.StringSorter( sort_type = ClientStrings.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT, asc = True, regex = '\\d+' )
|
||||
correct = [ b, a, d, c, e ]
|
||||
|
||||
do_sort_test( sorter, correct )
|
||||
|
||||
sorter = ClientParsing.StringSorter( sort_type = ClientParsing.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT, asc = False, regex = '\\d+' )
|
||||
sorter = ClientStrings.StringSorter( sort_type = ClientStrings.CONTENT_PARSER_SORT_TYPE_HUMAN_SORT, asc = False, regex = '\\d+' )
|
||||
correct = [ c, d, a, b, e ]
|
||||
|
||||
do_sort_test( sorter, correct )
|
||||
|
@ -489,13 +489,13 @@ class TestStringSplitter( unittest.TestCase ):
|
|||
|
||||
def test_basics( self ):
|
||||
|
||||
splitter = ClientParsing.StringSplitter( separator = ', ' )
|
||||
splitter = ClientStrings.StringSplitter( separator = ', ' )
|
||||
|
||||
self.assertEqual( splitter.Split( '123' ), [ '123' ] )
|
||||
self.assertEqual( splitter.Split( '1,2,3' ), [ '1,2,3' ] )
|
||||
self.assertEqual( splitter.Split( '1, 2, 3' ), [ '1', '2', '3' ] )
|
||||
|
||||
splitter = ClientParsing.StringSplitter( separator = ', ', max_splits = 2 )
|
||||
splitter = ClientStrings.StringSplitter( separator = ', ', max_splits = 2 )
|
||||
|
||||
self.assertEqual( splitter.Split( '123' ), [ '123' ] )
|
||||
self.assertEqual( splitter.Split( '1,2,3' ), [ '1,2,3' ] )
|
||||
|
@ -506,7 +506,7 @@ class TestStringProcessor( unittest.TestCase ):
|
|||
|
||||
def test_basics( self ):
|
||||
|
||||
processor = ClientParsing.StringProcessor()
|
||||
processor = ClientStrings.StringProcessor()
|
||||
|
||||
self.assertEqual( processor.ProcessStrings( [] ), [] )
|
||||
self.assertEqual( processor.ProcessStrings( [ 'test' ] ), [ 'test' ] )
|
||||
|
@ -514,13 +514,13 @@ class TestStringProcessor( unittest.TestCase ):
|
|||
|
||||
processing_steps = []
|
||||
|
||||
processing_steps.append( ClientParsing.StringSplitter( separator = ',', max_splits = 2 ) )
|
||||
processing_steps.append( ClientStrings.StringSplitter( separator = ',', max_splits = 2 ) )
|
||||
|
||||
processing_steps.append( ClientParsing.StringMatch( match_type = ClientParsing.STRING_MATCH_FLEXIBLE, match_value = ClientParsing.NUMERIC ) )
|
||||
processing_steps.append( ClientStrings.StringMatch( match_type = ClientStrings.STRING_MATCH_FLEXIBLE, match_value = ClientStrings.NUMERIC ) )
|
||||
|
||||
conversions = [ ( ClientParsing.STRING_CONVERSION_APPEND_TEXT, 'abc' ) ]
|
||||
conversions = [ ( ClientStrings.STRING_CONVERSION_APPEND_TEXT, 'abc' ) ]
|
||||
|
||||
processing_steps.append( ClientParsing.StringConverter( conversions = conversions ) )
|
||||
processing_steps.append( ClientStrings.StringConverter( conversions = conversions ) )
|
||||
|
||||
processor.SetProcessingSteps( processing_steps )
|
||||
|
||||
|
|
Loading…
Reference in New Issue