Version 299
This commit is contained in:
parent
76fe7117c7
commit
2f106cc97d
|
@ -8,6 +8,34 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 299</h3></li>
|
||||
<ul>
|
||||
<li>wrote ui to review and even edit session cookies by network context. it is still a bit rough but will help with future development.</li>
|
||||
<li>added a 'open_selection_in_new_page' shortcut to the 'media' shortcut set that will work on the thumbnail view</li>
|
||||
<li>added a 'export_files' shortcut to the 'media' shortcut set that will work on the thumbnail view</li>
|
||||
<li>fudged manage siblings logic to not do the borked 'hey, that sibling already exists' as soon as you type the old sibling--it will now auto-petition any existing siblings when you click 'add' with a good automated petition reason that makes sense to the janitor</li>
|
||||
<li>manage siblings now also only shows rows appropriate to the current selection like parents does. it gets a new 'notes' column to specify conflicts that will be auto-petitioned as above</li>
|
||||
<li>manage siblings and parents now have a laggy 'show all pairs' checkbox to let you quickly review everything like you used to</li>
|
||||
<li>fixed a database-level bug that meant petitioned and pending the same left-hand sibling tag (like petition a->b, pend a->c) would sometimes not both save together</li>
|
||||
<li>when you middle-click or right-click->open a new page on a selection of tags/search predicates, the new page will now be named after the tags</li>
|
||||
<li>if subscriptions hit their periodic file limit, they will now give a little popup message describing what happened and possible causes and actions the user can take</li>
|
||||
<li>added a 'make a modal popup in five seconds' action to help->debug</li>
|
||||
<li>modal popups will now hide/show other child frames (like review services) rather than minimise/restore, as this latter action can raise the entire progam to the front</li>
|
||||
<li>added a 'make a parentless text control dialog' debug entry to test some key event catching</li>
|
||||
<li>added a 'layout all tlws' debug entry to help->debug that'll hopefully help figure out some child window sizing/position issues</li>
|
||||
<li>added a 'shortcut report mode', which will report caught shortcut keys and their matched commands, if any</li>
|
||||
<li>fixed a little shortcut catching bug in the main gui</li>
|
||||
<li>finished adding the new bytes control</li>
|
||||
<li>updated ffmpeg for windows</li>
|
||||
<li>improved sankaku default bandwidth rules to stop a subscription bandwidth rules mismatch that could sometimes make for subscription delays--existing users may like to add a 2GB/day rule for sankakucomplex.com</li>
|
||||
<li>improved some service and account error handling to better propagate the exact problem up the exception handling chain</li>
|
||||
<li>the clientside 'update A actually had hash B' repository sync error is now dealt with in a less severe way, and the bad update file is saved to disk with a request for it to be forwarded to hydrus dev for further investigation</li>
|
||||
<li>file parsing is now more resistant to invalid negative values for properties like width, height, and duration</li>
|
||||
<li>improved some focus code that may have been affecting linux stability</li>
|
||||
<li>improved a deviant art url-not-found error</li>
|
||||
<li>improved a wx version boot test</li>
|
||||
<li>misc fixes</li>
|
||||
</ul>
|
||||
<li><h3>version 298</h3></li>
|
||||
<ul>
|
||||
<li>wrote a new 'bytescontrol' and related noneablebytescontrol that allows for a uniform way to choose a bytes size value with a wider range--and deployed it all over the place</li>
|
||||
|
|
|
@ -336,7 +336,7 @@ SHORTCUTS_RESERVED_NAMES = [ 'archive_delete_filter', 'duplicate_filter', 'media
|
|||
|
||||
# shortcut commands
|
||||
|
||||
SHORTCUTS_MEDIA_ACTIONS = [ 'manage_file_tags', 'manage_file_ratings', 'manage_file_urls', 'manage_file_notes', 'archive_file', 'inbox_file', 'delete_file', 'remove_file_from_view', 'open_file_in_external_program', 'launch_the_archive_delete_filter', 'copy_bmp', 'copy_file', 'copy_path', 'copy_sha256_hash', 'get_similar_to_exact', 'get_similar_to_very_similar', 'get_similar_to_similar', 'get_similar_to_speculative' ]
|
||||
SHORTCUTS_MEDIA_ACTIONS = [ 'manage_file_tags', 'manage_file_ratings', 'manage_file_urls', 'manage_file_notes', 'archive_file', 'inbox_file', 'delete_file', 'export_files', 'remove_file_from_view', 'open_file_in_external_program', 'open_selection_in_new_page', 'launch_the_archive_delete_filter', 'copy_bmp', 'copy_file', 'copy_path', 'copy_sha256_hash', 'get_similar_to_exact', 'get_similar_to_very_similar', 'get_similar_to_similar', 'get_similar_to_speculative' ]
|
||||
SHORTCUTS_MEDIA_VIEWER_ACTIONS = [ 'move_animation_to_previous_frame', 'move_animation_to_next_frame', 'switch_between_fullscreen_borderless_and_regular_framed_window', 'pan_up', 'pan_down', 'pan_left', 'pan_right', 'zoom_in', 'zoom_out', 'switch_between_100_percent_and_canvas_zoom', 'flip_darkmode' ]
|
||||
SHORTCUTS_MEDIA_VIEWER_BROWSER_ACTIONS = [ 'view_next', 'view_first', 'view_last', 'view_previous' ]
|
||||
SHORTCUTS_MAIN_GUI_ACTIONS = [ 'refresh', 'new_page', 'synchronised_wait_switch', 'set_media_focus', 'show_hide_splitters', 'set_search_focus', 'unclose_page', 'close_page', 'redo', 'undo', 'flip_darkmode', 'check_all_import_folders' ]
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
import os
|
||||
import wx
|
||||
|
||||
wx_first_num = int( wx.__version__[0] )
|
||||
|
||||
if wx_first_num < 4:
|
||||
|
||||
wx_error = 'Unfortunately, hydrus now requires the new Phoenix (4.x) version of wx.'
|
||||
wx_error += os.linesep * 2
|
||||
wx_error += 'Please check the \'running from source\' page in the html help for more details.'
|
||||
|
||||
raise Exception( wx_error )
|
||||
|
||||
import ClientCaches
|
||||
import ClientData
|
||||
import ClientDaemons
|
||||
|
@ -26,28 +39,15 @@ import ClientGUIDialogs
|
|||
import ClientGUIScrolledPanelsManagement
|
||||
import ClientGUITopLevelWindows
|
||||
import gc
|
||||
import os
|
||||
import psutil
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
import wx
|
||||
|
||||
if not HG.twisted_is_broke:
|
||||
|
||||
from twisted.internet import reactor, defer
|
||||
|
||||
|
||||
wx_first_num = int( wx.__version__[0] )
|
||||
|
||||
if wx_first_num < 4:
|
||||
|
||||
wx_error = 'Unfortunately, hydrus now requires the new Phoenix (4.x) version of wx.'
|
||||
wx_error += os.linesep * 2
|
||||
wx_error += 'The good news is that you can get the new version via pip. If you still need the old version of wx, Phoenix works a lot better with virtual environments.'
|
||||
|
||||
raise Exception( wx_error )
|
||||
|
||||
class Controller( HydrusController.HydrusController ):
|
||||
|
||||
def __init__( self, db_dir, no_daemons, no_wal ):
|
||||
|
|
|
@ -7547,7 +7547,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
reason_id = self._GetTextId( reason )
|
||||
|
||||
self._c.execute( 'DELETE FROM tag_sibling_petitions WHERE service_id = ? AND bad_tag_id = ?;', ( service_id, bad_tag_id ) )
|
||||
self._c.execute( 'DELETE FROM tag_sibling_petitions WHERE service_id = ? AND bad_tag_id = ? AND good_tag_id = ?;', ( service_id, bad_tag_id, good_tag_id ) )
|
||||
|
||||
self._c.execute( 'INSERT OR IGNORE INTO tag_sibling_petitions ( service_id, bad_tag_id, good_tag_id, reason_id, status ) VALUES ( ?, ?, ?, ?, ? );', ( service_id, bad_tag_id, good_tag_id, reason_id, new_status ) )
|
||||
|
||||
|
|
|
@ -90,6 +90,8 @@ def SetDefaultBandwidthManagerRules( bandwidth_manager ):
|
|||
|
||||
rules.AddRule( HC.BANDWIDTH_TYPE_REQUESTS, 4, 1 )
|
||||
|
||||
rules.AddRule( HC.BANDWIDTH_TYPE_DATA, 86400, 2 * GB ) # keep this in there so subs can know better when to stop running (the files come from a subdomain, which causes a pain for bandwidth calcs)
|
||||
|
||||
bandwidth_manager.SetRules( ClientNetworking.NetworkContext( CC.NETWORK_CONTEXT_DOMAIN, 'sankakucomplex.com' ), rules )
|
||||
|
||||
def SetDefaultDomainManagerData( domain_manager ):
|
||||
|
|
|
@ -1067,6 +1067,8 @@ class GalleryDeviantArt( Gallery ):
|
|||
|
||||
def _ParseImagePage( self, html, referral_url ):
|
||||
|
||||
img_url = None
|
||||
|
||||
soup = GetSoup( html )
|
||||
|
||||
download_button = soup.find( 'a', class_ = 'dev-page-download' )
|
||||
|
@ -1127,6 +1129,11 @@ class GalleryDeviantArt( Gallery ):
|
|||
img_url = download_button[ 'href' ]
|
||||
|
||||
|
||||
if img_url is None:
|
||||
|
||||
raise HydrusExceptions.ParseException( 'Could not find a download link--maybe this work was text?' )
|
||||
|
||||
|
||||
return img_url
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import ClientGUIMenus
|
|||
import ClientGUIPages
|
||||
import ClientGUIParsing
|
||||
import ClientGUIPopupMessages
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUIScrolledPanelsEdit
|
||||
import ClientGUIScrolledPanelsManagement
|
||||
import ClientGUIScrolledPanelsReview
|
||||
|
@ -88,7 +89,9 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
self._statusbar_thread_updater = ClientGUICommon.ThreadToGUIUpdater( self._statusbar, self.RefreshStatusBar )
|
||||
|
||||
self._focus_holder = wx.Window( self, size = ( 0, 0 ) )
|
||||
self._focus_holder = wx.Window( self )
|
||||
|
||||
self._focus_holder.SetSize( ( 0, 0 ) )
|
||||
|
||||
self._closed_pages = []
|
||||
self._closed_page_keys = set()
|
||||
|
@ -757,6 +760,55 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
|
||||
|
||||
def _DebugMakeDelayedModalPopup( self ):
|
||||
|
||||
def do_it( controller ):
|
||||
|
||||
time.sleep( 5 )
|
||||
|
||||
job_key = ClientThreading.JobKey( cancellable = True )
|
||||
|
||||
job_key.SetVariable( 'popup_title', 'debug modal job' )
|
||||
|
||||
controller.pub( 'modal_message', job_key )
|
||||
|
||||
for i in range( 5 ):
|
||||
|
||||
if job_key.IsCancelled():
|
||||
|
||||
break
|
||||
|
||||
|
||||
job_key.SetVariable( 'popup_text_1', 'Will auto-dismiss in ' + HydrusData.ConvertTimeDeltaToPrettyString( 5 - i ) + '.' )
|
||||
job_key.SetVariable( 'popup_gauge_1', ( i, 5 ) )
|
||||
|
||||
time.sleep( 1 )
|
||||
|
||||
|
||||
job_key.Delete()
|
||||
|
||||
|
||||
self._controller.CallToThread( do_it, self._controller )
|
||||
|
||||
|
||||
def _DebugMakeParentlessTextCtrl( self ):
|
||||
|
||||
with wx.Dialog( None, title = 'parentless debug dialog' ) as dlg:
|
||||
|
||||
control = wx.TextCtrl( dlg )
|
||||
|
||||
control.SetValue( 'debug test input' )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( control, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
dlg.SetSizer( vbox )
|
||||
|
||||
dlg.ShowModal()
|
||||
|
||||
|
||||
|
||||
def _DebugMakeSomePopups( self ):
|
||||
|
||||
for i in range( 1, 7 ):
|
||||
|
@ -983,6 +1035,20 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
|
||||
|
||||
def _ForceLayoutAllTLWs( self ):
|
||||
|
||||
tlws = wx.GetTopLevelWindows()
|
||||
|
||||
for tlw in tlws:
|
||||
|
||||
HydrusData.ShowText( 'TLW ' + repr( tlw ) + ': pre size/pos - ' + repr( tuple( tlw.GetSize() ) ) + ' ' + repr( tuple( tlw.GetPosition() ) ) )
|
||||
|
||||
tlw.Layout()
|
||||
|
||||
HydrusData.ShowText( 'TLW ' + repr( tlw ) + ': post size/pos - ' + repr( tuple( tlw.GetSize() ) ) + ' ' + repr( tuple( tlw.GetPosition() ) ) )
|
||||
|
||||
|
||||
|
||||
def _GenerateMenuInfo( self, name ):
|
||||
|
||||
menu = wx.Menu()
|
||||
|
@ -1495,6 +1561,12 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
ClientGUIMenus.AppendMenuLabel( menu, '(This section is under construction)' )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( self, menu, 'review session cookies', 'Review and edit which cookies you have for which network contexts.', self._ReviewNetworkSessions )
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
ClientGUIMenus.AppendMenuLabel( menu, '(This section is under construction)' )
|
||||
|
||||
# this will be the easy-mode 'export ability to download from blahbooru' that'll bundle it all into a nice package with a neat png.
|
||||
# need a name for this that isn't 'downloader', or maybe it should be, and I should rename downloaders below to 'gallery query generator' or whatever.
|
||||
|
||||
|
@ -1716,12 +1788,16 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
ClientGUIMenus.AppendMenuCheckItem( self, report_modes, 'gui report mode', 'Have the gui report inside information, where supported.', HG.gui_report_mode, self._SwitchBoolean, 'gui_report_mode' )
|
||||
ClientGUIMenus.AppendMenuCheckItem( self, 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( self, report_modes, 'network report mode', 'Have the network engine report new jobs.', HG.network_report_mode, self._SwitchBoolean, 'network_report_mode' )
|
||||
ClientGUIMenus.AppendMenuCheckItem( self, report_modes, 'shortcut report mode', 'Have the new shortcut system report what shortcuts it catches and whether it matches an action.', HG.shortcut_report_mode, self._SwitchBoolean, 'shortcut_report_mode' )
|
||||
|
||||
ClientGUIMenus.AppendMenu( debug, report_modes, 'report modes' )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( self, debug, 'make some popups', 'Throw some varied popups at the message manager, just to check it is working.', self._DebugMakeSomePopups )
|
||||
ClientGUIMenus.AppendMenuItem( self, debug, 'make a popup in five seconds', 'Throw a delayed popup at the message manager, giving you time to minimise or otherwise alter the client before it arrives.', self._controller.CallLater, 5, HydrusData.ShowText, 'This is a delayed popup message.' )
|
||||
ClientGUIMenus.AppendMenuItem( self, debug, 'make a modal popup in five seconds', 'Throw up a delayed modal popup to test with. It will stay alive for five seconds.', self._DebugMakeDelayedModalPopup )
|
||||
ClientGUIMenus.AppendMenuItem( self, debug, 'make a parentless text ctrl dialog', 'Make a parentless text control in a dialog to test some character event catching.', self._DebugMakeParentlessTextCtrl )
|
||||
ClientGUIMenus.AppendMenuItem( self, debug, 'force a gui layout now', 'Tell the gui to relayout--useful to test some gui bootup layout issues.', self.Layout )
|
||||
ClientGUIMenus.AppendMenuItem( self, debug, 'force a layout for all tlws now', 'Tell all frames to relayout--useful to test some layout issues.', self._ForceLayoutAllTLWs )
|
||||
ClientGUIMenus.AppendMenuItem( self, debug, 'flush log', 'Command the log to write any buffered contents to hard drive.', HydrusData.DebugPrint, 'Flushing log' )
|
||||
ClientGUIMenus.AppendMenuItem( self, debug, 'print garbage', 'Print some information about the python garbage to the log.', self._DebugPrintGarbage )
|
||||
ClientGUIMenus.AppendMenuItem( self, debug, 'show scheduled jobs', 'Print some information about the currently scheduled jobs log.', self._DebugShowScheduledJobs )
|
||||
|
@ -2625,6 +2701,15 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
frame.SetPanel( panel )
|
||||
|
||||
|
||||
def _ReviewNetworkSessions( self ):
|
||||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, 'review network sessions' )
|
||||
|
||||
panel = ClientGUIScrolledPanelsReview.ReviewNetworkSessionsPanel( frame, self._controller.network_engine.session_manager )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
||||
def _ReviewServices( self ):
|
||||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, self._controller.PrepStringForDisplay( 'Review Services' ), 'review_services' )
|
||||
|
@ -2882,6 +2967,10 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
HG.network_report_mode = not HG.network_report_mode
|
||||
|
||||
elif name == 'shortcut_report_mode':
|
||||
|
||||
HG.shortcut_report_mode = not HG.shortcut_report_mode
|
||||
|
||||
elif name == 'pubsub_profile_mode':
|
||||
|
||||
HG.pubsub_profile_mode = not HG.pubsub_profile_mode
|
||||
|
@ -3915,7 +4004,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
self._notebook.ChooseNewPageForDeepestNotebook()
|
||||
|
||||
if action == 'close_page':
|
||||
elif action == 'close_page':
|
||||
|
||||
self._notebook.CloseCurrentPage()
|
||||
|
||||
|
|
|
@ -1797,11 +1797,13 @@ class MenuButton( BetterButton ):
|
|||
|
||||
class NetworkContextButton( BetterButton ):
|
||||
|
||||
def __init__( self, parent, network_context ):
|
||||
def __init__( self, parent, network_context, limited_types = None, allow_default = True ):
|
||||
|
||||
BetterButton.__init__( self, parent, network_context.ToUnicode(), self._Edit )
|
||||
|
||||
self._network_context = network_context
|
||||
self._limited_types = limited_types
|
||||
self._allow_default = allow_default
|
||||
|
||||
|
||||
def _Edit( self ):
|
||||
|
@ -1811,7 +1813,7 @@ class NetworkContextButton( BetterButton ):
|
|||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit network context' ) as dlg:
|
||||
|
||||
panel = ClientGUIScrolledPanelsEdit.EditNetworkContextPanel( dlg, self._network_context )
|
||||
panel = ClientGUIScrolledPanelsEdit.EditNetworkContextPanel( dlg, self._network_context, limited_types = self._limited_types, allow_default = self._allow_default )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
|
@ -1855,11 +1857,19 @@ class NoneableSpinCtrl( wx.Panel ):
|
|||
self._checkbox.Bind( wx.EVT_CHECKBOX, self.EventCheckBox )
|
||||
self._checkbox.SetLabelText( none_phrase )
|
||||
|
||||
self._one = wx.SpinCtrl( self, min = min, max = max, size = ( 60, -1 ) )
|
||||
self._one = wx.SpinCtrl( self, min = min, max = max )
|
||||
|
||||
width = ClientData.ConvertTextToPixelWidth( self._one, len( str( max ) ) + 2 )
|
||||
|
||||
self._one.SetInitialSize( ( width, -1 ) )
|
||||
|
||||
if num_dimensions == 2:
|
||||
|
||||
self._two = wx.SpinCtrl( self, initial = 0, min = min, max = max, size = ( 60, -1 ) )
|
||||
self._two = wx.SpinCtrl( self, initial = 0, min = min, max = max )
|
||||
|
||||
width = ClientData.ConvertTextToPixelWidth( self._two, len( str( max ) ) + 2 )
|
||||
|
||||
self._two.SetInitialSize( ( width, -1 ) )
|
||||
|
||||
|
||||
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
|
|
@ -237,7 +237,11 @@ class BytesControl( wx.Panel ):
|
|||
|
||||
wx.Panel.__init__( self, parent )
|
||||
|
||||
self._spin = wx.SpinCtrl( self, min = 0, max = 1048576, size = ( 60, -1 ) )
|
||||
self._spin = wx.SpinCtrl( self, min = 0, max = 1048576 )
|
||||
|
||||
width = ClientData.ConvertTextToPixelWidth( self._spin, 9 )
|
||||
|
||||
self._spin.SetSize( ( width, -1 ) )
|
||||
|
||||
self._unit = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
|
@ -349,6 +353,16 @@ class NoneableBytesControl( wx.Panel ):
|
|||
|
||||
|
||||
|
||||
def SetToolTip( self, text ):
|
||||
|
||||
wx.Panel.SetToolTip( self, text )
|
||||
|
||||
for c in self.GetChildren():
|
||||
|
||||
c.SetToolTip( text )
|
||||
|
||||
|
||||
|
||||
def SetValue( self, value ):
|
||||
|
||||
if value is None:
|
||||
|
|
|
@ -3648,6 +3648,8 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._pairs_to_reasons = {}
|
||||
|
||||
self._show_all = wx.CheckBox( self )
|
||||
|
||||
self._tag_parents = ClientGUIListCtrl.BetterListCtrl( self, 'tag_parents', 30, 25, [ ( '', 4 ), ( 'child', 25 ), ( 'parent', -1 ) ], self._ConvertPairToListCtrlTuples, delete_key_callback = self._ListCtrlActivated, activation_callback = self._ListCtrlActivated )
|
||||
self._tag_parents.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventItemSelected )
|
||||
self._tag_parents.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventItemSelected )
|
||||
|
@ -3690,6 +3692,7 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
vbox.Add( self._status_st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( self._count_st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( ClientGUICommon.WrapInText( self._show_all, self, 'show all pairs' ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( self._tag_parents, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.Add( self._add, CC.FLAGS_LONE_BUTTON )
|
||||
vbox.Add( tags_box, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
@ -3700,6 +3703,7 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
#
|
||||
|
||||
self.Bind( ClientGUIListBoxes.EVT_LIST_BOX, self.EventListBoxChanged )
|
||||
self._show_all.Bind( wx.EVT_CHECKBOX, self.EventShowAll )
|
||||
|
||||
HG.client_controller.CallToThread( self.THREADInitialise, tags, self._service_key )
|
||||
|
||||
|
@ -4022,6 +4026,8 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
all_pairs = set()
|
||||
|
||||
show_all = self._show_all.GetValue()
|
||||
|
||||
for ( status, pairs ) in self._current_statuses_to_pairs.items():
|
||||
|
||||
if status == HC.CONTENT_STATUS_DELETED:
|
||||
|
@ -4029,10 +4035,9 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
continue
|
||||
|
||||
|
||||
|
||||
if len( pertinent_tags ) == 0:
|
||||
|
||||
if status == HC.CONTENT_STATUS_CURRENT:
|
||||
if status == HC.CONTENT_STATUS_CURRENT and not show_all:
|
||||
|
||||
continue
|
||||
|
||||
|
@ -4049,7 +4054,7 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
( a, b ) = pair
|
||||
|
||||
if a in pertinent_tags or b in pertinent_tags:
|
||||
if a in pertinent_tags or b in pertinent_tags or show_all:
|
||||
|
||||
all_pairs.add( pair )
|
||||
|
||||
|
@ -4095,7 +4100,10 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
children = self._children.GetTags()
|
||||
parents = self._parents.GetTags()
|
||||
|
||||
for parent in parents: self._AddPairs( children, parent )
|
||||
for parent in parents:
|
||||
|
||||
self._AddPairs( children, parent )
|
||||
|
||||
|
||||
self._children.SetTags( [] )
|
||||
self._parents.SetTags( [] )
|
||||
|
@ -4115,6 +4123,11 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
self._UpdateListCtrlData()
|
||||
|
||||
|
||||
def EventShowAll( self, event ):
|
||||
|
||||
self._UpdateListCtrlData()
|
||||
|
||||
|
||||
def GetContentUpdates( self ):
|
||||
|
||||
# we make it manually here because of the mass pending tags done (but not undone on a rescind) on a pending pair!
|
||||
|
@ -4178,23 +4191,7 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._child_input.Enable()
|
||||
self._parent_input.Enable()
|
||||
'''
|
||||
all_pairs = set()
|
||||
|
||||
for ( status, pairs ) in self._original_statuses_to_pairs.items():
|
||||
|
||||
if status == HC.CONTENT_STATUS_DELETED:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
all_pairs.update( pairs )
|
||||
|
||||
|
||||
self._tag_parents.AddDatas( all_pairs )
|
||||
|
||||
self._tag_parents.Sort( 2 )
|
||||
'''
|
||||
if tags is None:
|
||||
|
||||
self._UpdateListCtrlData()
|
||||
|
@ -4270,7 +4267,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
self.SetInitialSize( ( 550, 780 ) )
|
||||
self.SetInitialSize( ( 850, 780 ) )
|
||||
|
||||
|
||||
def _SetSearchFocus( self ):
|
||||
|
@ -4354,10 +4351,14 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._current_new = None
|
||||
|
||||
self._tag_siblings = ClientGUIListCtrl.BetterListCtrl( self, 'tag_siblings', 30, 25, [ ( '', 4 ), ( 'old', 25 ), ( 'new', -1 ) ], self._ConvertPairToListCtrlTuples, delete_key_callback = self._ListCtrlActivated, activation_callback = self._ListCtrlActivated )
|
||||
self._show_all = wx.CheckBox( self )
|
||||
|
||||
self._tag_siblings = ClientGUIListCtrl.BetterListCtrl( self, 'tag_siblings', 30, 40, [ ( '', 4 ), ( 'old', 25 ), ( 'new', 25 ), ( 'note', -1 ) ], self._ConvertPairToListCtrlTuples, delete_key_callback = self._ListCtrlActivated, activation_callback = self._ListCtrlActivated )
|
||||
self._tag_siblings.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventItemSelected )
|
||||
self._tag_siblings.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventItemSelected )
|
||||
|
||||
self._tag_siblings.Sort( 2 )
|
||||
|
||||
self._old_siblings = ClientGUIListBoxes.ListBoxTagsStringsAddRemove( self, self._service_key, show_sibling_text = False )
|
||||
self._new_sibling = ClientGUICommon.BetterStaticText( self )
|
||||
|
||||
|
@ -4398,6 +4399,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
vbox.Add( self._status_st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( self._count_st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( ClientGUICommon.WrapInText( self._show_all, self, 'show all pairs' ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( self._tag_siblings, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.Add( self._add, CC.FLAGS_LONE_BUTTON )
|
||||
vbox.Add( text_box, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
@ -4407,10 +4409,13 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
#
|
||||
|
||||
self._show_all.Bind( wx.EVT_CHECKBOX, self.EventShowAll )
|
||||
self.Bind( ClientGUIListBoxes.EVT_LIST_BOX, self.EventListBoxChanged )
|
||||
|
||||
HG.client_controller.CallToThread( self.THREADInitialise, tags, self._service_key )
|
||||
|
||||
|
||||
def _AddPairs( self, olds, new ):
|
||||
def _AddPairs( self, olds, new, remove_only = False, default_reason = None ):
|
||||
|
||||
new_pairs = []
|
||||
current_pairs = []
|
||||
|
@ -4427,27 +4432,32 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
elif pair in self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PETITIONED ]:
|
||||
|
||||
petitioned_pairs.append( pair )
|
||||
if not remove_only:
|
||||
|
||||
petitioned_pairs.append( pair )
|
||||
|
||||
|
||||
elif pair in self._original_statuses_to_pairs[ HC.CONTENT_STATUS_CURRENT ]:
|
||||
|
||||
current_pairs.append( pair )
|
||||
|
||||
elif self._CanAdd( pair ):
|
||||
elif not remove_only and self._CanAdd( pair ):
|
||||
|
||||
new_pairs.append( pair )
|
||||
|
||||
|
||||
|
||||
affected_pairs = []
|
||||
|
||||
if len( new_pairs ) > 0:
|
||||
|
||||
do_it = True
|
||||
|
||||
if self._service_key != CC.LOCAL_TAG_SERVICE_KEY:
|
||||
|
||||
if self._service.HasPermission( HC.CONTENT_TYPE_TAG_SIBLINGS, HC.PERMISSION_ACTION_OVERRULE ):
|
||||
if default_reason is not None:
|
||||
|
||||
reason = default_reason
|
||||
|
||||
elif self._service.HasPermission( HC.CONTENT_TYPE_TAG_SIBLINGS, HC.PERMISSION_ACTION_OVERRULE ):
|
||||
|
||||
reason = 'admin'
|
||||
|
||||
|
@ -4470,7 +4480,10 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
reason = dlg.GetValue()
|
||||
|
||||
else: do_it = False
|
||||
else:
|
||||
|
||||
do_it = False
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -4484,8 +4497,6 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PENDING ].update( new_pairs )
|
||||
|
||||
affected_pairs.extend( new_pairs )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
|
@ -4495,60 +4506,58 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
if self._service_key != CC.LOCAL_TAG_SERVICE_KEY:
|
||||
|
||||
if len( current_pairs ) > 10:
|
||||
if default_reason is not None:
|
||||
|
||||
pair_strings = 'The many pairs you entered.'
|
||||
reason = default_reason
|
||||
|
||||
elif self._service.HasPermission( HC.CONTENT_TYPE_TAG_SIBLINGS, HC.PERMISSION_ACTION_OVERRULE ):
|
||||
|
||||
reason = 'admin'
|
||||
|
||||
else:
|
||||
|
||||
pair_strings = os.linesep.join( ( old + '->' + new for ( old, new ) in current_pairs ) )
|
||||
|
||||
|
||||
if len( current_pairs ) > 1: message = 'The pairs:' + os.linesep * 2 + pair_strings + os.linesep * 2 + 'Already exist.'
|
||||
else: message = 'The pair ' + pair_strings + ' already exists.'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, message, title = 'Choose what to do.', yes_label = 'petition it', no_label = 'do nothing' ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
if len( pending_pairs ) > 10:
|
||||
|
||||
if self._service.HasPermission( HC.CONTENT_TYPE_TAG_SIBLINGS, HC.PERMISSION_ACTION_OVERRULE ):
|
||||
|
||||
reason = 'admin'
|
||||
|
||||
else:
|
||||
|
||||
message = 'Enter a reason for this pair to be removed. A janitor will review your petition.'
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, message ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK: reason = dlg.GetValue()
|
||||
else: do_it = False
|
||||
|
||||
|
||||
|
||||
if do_it:
|
||||
|
||||
for pair in current_pairs: self._pairs_to_reasons[ pair ] = reason
|
||||
|
||||
pair_strings = 'The many pairs you entered.'
|
||||
|
||||
else:
|
||||
|
||||
do_it = False
|
||||
pair_strings = os.linesep.join( ( old + '->' + new for ( old, new ) in pending_pairs ) )
|
||||
|
||||
|
||||
message = 'Enter a reason for:'
|
||||
message += os.linesep * 2
|
||||
message += pair_strings
|
||||
message += os.linesep * 2
|
||||
message += 'to be removed. You will see the delete as soon as you upload, but a janitor will review your petition to decide if all users should receive it as well.'
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, message ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
reason = dlg.GetValue()
|
||||
|
||||
else:
|
||||
|
||||
do_it = False
|
||||
|
||||
|
||||
|
||||
|
||||
if do_it:
|
||||
|
||||
for pair in current_pairs:
|
||||
|
||||
self._pairs_to_reasons[ pair ] = reason
|
||||
|
||||
|
||||
|
||||
|
||||
if do_it:
|
||||
|
||||
self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PETITIONED ].update( current_pairs )
|
||||
|
||||
affected_pairs.extend( current_pairs )
|
||||
|
||||
|
||||
self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PETITIONED ].update( current_pairs )
|
||||
|
||||
|
||||
if len( pending_pairs ) > 0:
|
||||
|
||||
|
||||
if len( pending_pairs ) > 10:
|
||||
|
||||
pair_strings = 'The many pairs you entered.'
|
||||
|
@ -4558,8 +4567,14 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
pair_strings = os.linesep.join( ( old + '->' + new for ( old, new ) in pending_pairs ) )
|
||||
|
||||
|
||||
if len( pending_pairs ) > 1: message = 'The pairs:' + os.linesep * 2 + pair_strings + os.linesep * 2 + 'Are pending.'
|
||||
else: message = 'The pair ' + pair_strings + ' is pending.'
|
||||
if len( pending_pairs ) > 1:
|
||||
|
||||
message = 'The pairs:' + os.linesep * 2 + pair_strings + os.linesep * 2 + 'Are pending.'
|
||||
|
||||
else:
|
||||
|
||||
message = 'The pair ' + pair_strings + ' is pending.'
|
||||
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, message, title = 'Choose what to do.', yes_label = 'rescind the pend', no_label = 'do nothing' ) as dlg:
|
||||
|
||||
|
@ -4567,8 +4582,6 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PENDING ].difference_update( pending_pairs )
|
||||
|
||||
affected_pairs.extend( pending_pairs )
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -4592,40 +4605,10 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PETITIONED ].difference_update( petitioned_pairs )
|
||||
|
||||
affected_pairs.extend( petitioned_pairs )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if len( affected_pairs ) > 0:
|
||||
|
||||
def in_current( pair ):
|
||||
|
||||
for status in ( HC.CONTENT_STATUS_CURRENT, HC.CONTENT_STATUS_PENDING, HC.CONTENT_STATUS_PETITIONED ):
|
||||
|
||||
if pair in self._current_statuses_to_pairs[ status ]:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
affected_pairs = [ ( self._tag_siblings.HasData( pair ), in_current( pair ), pair ) for pair in affected_pairs ]
|
||||
|
||||
to_add = [ pair for ( exists, current, pair ) in affected_pairs if not exists ]
|
||||
to_update = [ pair for ( exists, current, pair ) in affected_pairs if exists and current ]
|
||||
to_delete = [ pair for ( exists, current, pair ) in affected_pairs if exists and not current ]
|
||||
|
||||
self._tag_siblings.AddDatas( to_add )
|
||||
self._tag_siblings.UpdateDatas( to_update )
|
||||
self._tag_siblings.DeleteDatas( to_delete )
|
||||
|
||||
self._tag_siblings.Sort()
|
||||
|
||||
|
||||
|
||||
def _CanAdd( self, potential_pair ):
|
||||
|
||||
|
@ -4687,6 +4670,8 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
|
||||
|
||||
self._UpdateListCtrlData()
|
||||
|
||||
|
||||
def _ConvertPairToListCtrlTuples( self, pair ):
|
||||
|
||||
|
@ -4709,67 +4694,98 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
pretty_status = sign
|
||||
|
||||
display_tuple = ( pretty_status, old, new )
|
||||
sort_tuple = ( status, old, new )
|
||||
existing_olds = self._old_siblings.GetTags()
|
||||
|
||||
note = ''
|
||||
|
||||
if old in existing_olds:
|
||||
|
||||
if status == HC.CONTENT_STATUS_PENDING:
|
||||
|
||||
note = 'CONFLICT: Will be rescinded on add.'
|
||||
|
||||
elif status == HC.CONTENT_STATUS_CURRENT:
|
||||
|
||||
note = 'CONFLICT: Will be petitioned/deleted on add.'
|
||||
|
||||
|
||||
|
||||
display_tuple = ( pretty_status, old, new, note )
|
||||
sort_tuple = ( status, old, new, note )
|
||||
|
||||
return ( display_tuple, sort_tuple )
|
||||
|
||||
|
||||
def _SetButtonStatus( self ):
|
||||
|
||||
if self._current_new is None or len( self._old_siblings.GetTags() ) == 0: self._add.Disable()
|
||||
else: self._add.Enable()
|
||||
if self._current_new is None or len( self._old_siblings.GetTags() ) == 0:
|
||||
|
||||
self._add.Disable()
|
||||
|
||||
else:
|
||||
|
||||
self._add.Enable()
|
||||
|
||||
|
||||
|
||||
def _UpdateListCtrlData( self ):
|
||||
|
||||
olds = self._old_siblings.GetTags()
|
||||
|
||||
pertinent_tags = set( olds )
|
||||
|
||||
if self._current_new is not None:
|
||||
|
||||
pertinent_tags.add( self._current_new )
|
||||
|
||||
|
||||
self._tag_siblings.DeleteDatas( self._tag_siblings.GetData() )
|
||||
|
||||
all_pairs = set()
|
||||
|
||||
show_all = self._show_all.GetValue()
|
||||
|
||||
for ( status, pairs ) in self._current_statuses_to_pairs.items():
|
||||
|
||||
if status == HC.CONTENT_STATUS_DELETED:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
if len( pertinent_tags ) == 0:
|
||||
|
||||
if status == HC.CONTENT_STATUS_CURRENT and not show_all:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
# show all pending/petitioned
|
||||
|
||||
all_pairs.update( pairs )
|
||||
|
||||
else:
|
||||
|
||||
# show all appropriate
|
||||
|
||||
for pair in pairs:
|
||||
|
||||
( a, b ) = pair
|
||||
|
||||
if a in pertinent_tags or b in pertinent_tags or show_all:
|
||||
|
||||
all_pairs.add( pair )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
self._tag_siblings.AddDatas( all_pairs )
|
||||
|
||||
self._tag_siblings.Sort()
|
||||
|
||||
|
||||
def EnterOlds( self, olds ):
|
||||
|
||||
potential_olds = olds
|
||||
|
||||
olds = set()
|
||||
|
||||
for potential_old in potential_olds:
|
||||
|
||||
do_it = True
|
||||
|
||||
current_pairs = self._current_statuses_to_pairs[ HC.CONTENT_STATUS_CURRENT ].union( self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PENDING ] ).difference( self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PETITIONED ] )
|
||||
|
||||
current_olds = { current_old for ( current_old, current_new ) in current_pairs }
|
||||
|
||||
while potential_old in current_olds:
|
||||
|
||||
olds_to_news = dict( current_pairs )
|
||||
|
||||
conflicting_new = olds_to_news[ potential_old ]
|
||||
|
||||
message = 'There already is a relationship set for ' + potential_old + '! It goes to ' + conflicting_new + '.'
|
||||
message += os.linesep * 2
|
||||
message += 'You cannot have two siblings for the same original term.'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, message, title = 'Choose what to do.', yes_label = 'I want to overwrite the existing record', no_label = 'do nothing' ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
self._AddPairs( [ potential_old ], conflicting_new )
|
||||
|
||||
else:
|
||||
|
||||
do_it = False
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
current_pairs = self._current_statuses_to_pairs[ HC.CONTENT_STATUS_CURRENT ].union( self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PENDING ] ).difference( self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PETITIONED ] )
|
||||
|
||||
current_olds = { current_old for ( current_old, current_new ) in current_pairs }
|
||||
|
||||
|
||||
if do_it:
|
||||
|
||||
olds.add( potential_old )
|
||||
|
||||
|
||||
|
||||
if self._current_new in olds:
|
||||
|
||||
self.SetNew( set() )
|
||||
|
@ -4777,6 +4793,8 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self._old_siblings.EnterTags( olds )
|
||||
|
||||
self._UpdateListCtrlData()
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
|
@ -4784,13 +4802,38 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
if self._current_new is not None and len( self._old_siblings.GetTags() ) > 0:
|
||||
|
||||
# let's eliminate conflicts first
|
||||
|
||||
olds = self._old_siblings.GetTags()
|
||||
|
||||
current_pairs = self._current_statuses_to_pairs[ HC.CONTENT_STATUS_CURRENT ].union( self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PENDING ] ).difference( self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PETITIONED ] )
|
||||
|
||||
olds_to_news = dict( current_pairs )
|
||||
|
||||
current_olds = { current_old for ( current_old, current_new ) in current_pairs }
|
||||
|
||||
for old in olds:
|
||||
|
||||
if old in current_olds:
|
||||
|
||||
conflicting_new = olds_to_news[ old ]
|
||||
|
||||
if conflicting_new != self._current_new:
|
||||
|
||||
self._AddPairs( [ old ], conflicting_new, remove_only = True, default_reason = 'AUTO-PETITION TO REASSIGN TO: ' + self._current_new )
|
||||
|
||||
|
||||
|
||||
|
||||
#
|
||||
|
||||
self._AddPairs( olds, self._current_new )
|
||||
|
||||
self._old_siblings.SetTags( set() )
|
||||
self.SetNew( set() )
|
||||
|
||||
self._UpdateListCtrlData()
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
|
@ -4800,6 +4843,16 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
self._SetButtonStatus()
|
||||
|
||||
|
||||
def EventListBoxChanged( self, event ):
|
||||
|
||||
self._UpdateListCtrlData()
|
||||
|
||||
|
||||
def EventShowAll( self, event ):
|
||||
|
||||
self._UpdateListCtrlData()
|
||||
|
||||
|
||||
def GetContentUpdates( self ):
|
||||
|
||||
# we make it manually here because of the mass pending tags done (but not undone on a rescind) on a pending pair!
|
||||
|
@ -4811,8 +4864,15 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
if self._service_key == CC.LOCAL_TAG_SERVICE_KEY:
|
||||
|
||||
for pair in self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PENDING ]: content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_TAG_SIBLINGS, HC.CONTENT_UPDATE_ADD, pair ) )
|
||||
for pair in self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PETITIONED ]: content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_TAG_SIBLINGS, HC.CONTENT_UPDATE_DELETE, pair ) )
|
||||
for pair in self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PENDING ]:
|
||||
|
||||
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_TAG_SIBLINGS, HC.CONTENT_UPDATE_ADD, pair ) )
|
||||
|
||||
|
||||
for pair in self._current_statuses_to_pairs[ HC.CONTENT_STATUS_PETITIONED ]:
|
||||
|
||||
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_TAG_SIBLINGS, HC.CONTENT_UPDATE_DELETE, pair ) )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
|
@ -4861,6 +4921,8 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
self._current_new = new
|
||||
|
||||
|
||||
self._UpdateListCtrlData()
|
||||
|
||||
self._SetButtonStatus()
|
||||
|
||||
|
||||
|
@ -4894,23 +4956,12 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
self._old_input.Enable()
|
||||
self._new_input.Enable()
|
||||
|
||||
all_pairs = set()
|
||||
|
||||
for ( status, pairs ) in self._original_statuses_to_pairs.items():
|
||||
if tags is None:
|
||||
|
||||
if status == HC.CONTENT_STATUS_DELETED:
|
||||
|
||||
continue
|
||||
|
||||
self._UpdateListCtrlData()
|
||||
|
||||
all_pairs.update( pairs )
|
||||
|
||||
|
||||
self._tag_siblings.AddDatas( all_pairs )
|
||||
|
||||
self._tag_siblings.Sort( 2 )
|
||||
|
||||
if tags is not None:
|
||||
else:
|
||||
|
||||
self.EnterOlds( tags )
|
||||
|
||||
|
|
|
@ -1187,7 +1187,13 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
if len( predicates ) > 0:
|
||||
|
||||
HG.client_controller.pub( 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY, initial_predicates = predicates )
|
||||
s = [ predicate.GetUnicode() for predicate in predicates ]
|
||||
|
||||
s.sort()
|
||||
|
||||
page_name = ', '.join( s )
|
||||
|
||||
HG.client_controller.pub( 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY, initial_predicates = predicates, page_name = page_name )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1586,7 +1586,7 @@ class ManagementPanelImporterGallery( ManagementPanelImporter ):
|
|||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self._query_input.SetFocus()
|
||||
wx.CallAfter( self._query_input.SetFocus )
|
||||
|
||||
|
||||
|
||||
|
@ -1978,7 +1978,10 @@ class ManagementPanelImporterPageOfImages( ManagementPanelImporter ):
|
|||
|
||||
def SetSearchFocus( self, page_key ):
|
||||
|
||||
if page_key == self._page_key: self._page_url_input.SetFocus()
|
||||
if page_key == self._page_key:
|
||||
|
||||
wx.CallAfter( self._page_url_input.SetFocus )
|
||||
|
||||
|
||||
|
||||
def Start( self ):
|
||||
|
@ -2298,7 +2301,7 @@ class ManagementPanelImporterThreadWatcher( ManagementPanelImporter ):
|
|||
|
||||
if page_key == self._page_key and self._thread_input.IsEditable():
|
||||
|
||||
self._thread_input.SetFocus()
|
||||
wx.CallAfter( self._thread_input.SetFocus )
|
||||
|
||||
|
||||
|
||||
|
@ -2463,7 +2466,7 @@ class ManagementPanelImporterURLs( ManagementPanelImporter ):
|
|||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self._url_input.SetFocus()
|
||||
wx.CallAfter( self._url_input.SetFocus )
|
||||
|
||||
|
||||
|
||||
|
@ -3262,8 +3265,10 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
if page_key == self._page_key:
|
||||
|
||||
try: self._searchbox.SetFocus() # there's a chance this doesn't exist!
|
||||
except: self._controller.pub( 'set_media_focus' )
|
||||
if self._search_enabled:
|
||||
|
||||
wx.CallAfter( self._searchbox.SetFocus )
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -497,6 +497,54 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
|
||||
|
||||
|
||||
def _ExportFiles( self ):
|
||||
|
||||
if len( self._selected_media ) > 0:
|
||||
|
||||
flat_media = []
|
||||
|
||||
for media in self._sorted_media:
|
||||
|
||||
if media in self._selected_media:
|
||||
|
||||
if media.IsCollection():
|
||||
|
||||
flat_media.extend( media.GetFlatMedia() )
|
||||
|
||||
else:
|
||||
|
||||
flat_media.append( media )
|
||||
|
||||
|
||||
|
||||
|
||||
with ClientGUIDialogs.DialogSetupExport( None, flat_media ) as dlg:
|
||||
|
||||
dlg.ShowModal()
|
||||
|
||||
|
||||
self.SetFocus()
|
||||
|
||||
|
||||
|
||||
def _ExportTags( self ):
|
||||
|
||||
if len( self._selected_media ) > 0:
|
||||
|
||||
services = HG.client_controller.services_manager.GetServices( ( HC.LOCAL_TAG, HC.TAG_REPOSITORY, HC.COMBINED_TAG ) )
|
||||
|
||||
service_keys = [ service.GetServiceKey() for service in services ]
|
||||
|
||||
service_key = ClientGUIDialogs.SelectServiceKey( service_keys = service_keys )
|
||||
|
||||
hashes = self._GetSelectedHashes()
|
||||
|
||||
if service_key is not None:
|
||||
|
||||
ClientTags.ExportToHTA( self, service_key, hashes )
|
||||
|
||||
|
||||
|
||||
|
||||
def _FullScreen( self, first_media = None ):
|
||||
|
@ -1664,6 +1712,10 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
self._CopyHashesToClipboard( 'sha256' )
|
||||
|
||||
elif action == 'export_files':
|
||||
|
||||
self._ExportFiles()
|
||||
|
||||
elif action == 'manage_file_ratings':
|
||||
|
||||
self._ManageRatings()
|
||||
|
@ -1716,6 +1768,10 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
self._OpenExternally()
|
||||
|
||||
elif action == 'open_selection_in_new_page':
|
||||
|
||||
self._ShowSelectionInNewPage()
|
||||
|
||||
elif action == 'launch_the_archive_delete_filter':
|
||||
|
||||
self._ArchiveDeleteFilter()
|
||||
|
@ -2019,46 +2075,6 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
HG.client_controller.GetCache( 'thumbnail' ).Waterfall( self._page_key, thumbnails_to_render_later )
|
||||
|
||||
|
||||
def _ExportFiles( self ):
|
||||
|
||||
if len( self._selected_media ) > 0:
|
||||
|
||||
flat_media = []
|
||||
|
||||
for media in self._sorted_media:
|
||||
|
||||
if media in self._selected_media:
|
||||
|
||||
if media.IsCollection(): flat_media.extend( media.GetFlatMedia() )
|
||||
else: flat_media.append( media )
|
||||
|
||||
|
||||
|
||||
with ClientGUIDialogs.DialogSetupExport( None, flat_media ) as dlg: dlg.ShowModal()
|
||||
|
||||
self.SetFocus()
|
||||
|
||||
|
||||
|
||||
def _ExportTags( self ):
|
||||
|
||||
if len( self._selected_media ) > 0:
|
||||
|
||||
services = HG.client_controller.services_manager.GetServices( ( HC.LOCAL_TAG, HC.TAG_REPOSITORY, HC.COMBINED_TAG ) )
|
||||
|
||||
service_keys = [ service.GetServiceKey() for service in services ]
|
||||
|
||||
service_key = ClientGUIDialogs.SelectServiceKey( service_keys = service_keys )
|
||||
|
||||
hashes = self._GetSelectedHashes()
|
||||
|
||||
if service_key is not None:
|
||||
|
||||
ClientTags.ExportToHTA( self, service_key, hashes )
|
||||
|
||||
|
||||
|
||||
|
||||
def _FadeThumbnails( self, thumbnails ):
|
||||
|
||||
if len( thumbnails ) == 0:
|
||||
|
|
|
@ -653,7 +653,7 @@ class Page( wx.SplitterWindow ):
|
|||
|
||||
def SetMediaFocus( self ):
|
||||
|
||||
self._media_panel.SetFocus()
|
||||
wx.CallAfter( self._media_panel.SetFocus )
|
||||
|
||||
|
||||
def SetMediaResults( self, media_results ):
|
||||
|
|
|
@ -1000,16 +1000,16 @@ class PopupMessageDialogPanel( ClientGUIScrolledPanels.ReviewPanelVetoable ):
|
|||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
self._windows_minimised = []
|
||||
self._windows_hidden = []
|
||||
|
||||
self._MinimiseOtherWindows()
|
||||
self._HideOtherWindows()
|
||||
|
||||
self._message_pubbed = False
|
||||
|
||||
self._update_job = HG.client_controller.CallRepeatingWXSafe( self, 0.5, 0.25, self.REPEATINGUpdate )
|
||||
|
||||
|
||||
def _MinimiseOtherWindows( self ):
|
||||
def _HideOtherWindows( self ):
|
||||
|
||||
for tlw in wx.GetTopLevelWindows():
|
||||
|
||||
|
@ -1035,9 +1035,9 @@ class PopupMessageDialogPanel( ClientGUIScrolledPanels.ReviewPanelVetoable ):
|
|||
continue
|
||||
|
||||
|
||||
tlw.Iconize()
|
||||
tlw.Hide()
|
||||
|
||||
self._windows_minimised.append( tlw )
|
||||
self._windows_hidden.append( tlw )
|
||||
|
||||
|
||||
|
||||
|
@ -1055,12 +1055,12 @@ class PopupMessageDialogPanel( ClientGUIScrolledPanels.ReviewPanelVetoable ):
|
|||
|
||||
def _RestoreOtherWindows( self ):
|
||||
|
||||
for tlw in self._windows_minimised:
|
||||
for tlw in self._windows_hidden:
|
||||
|
||||
tlw.Restore()
|
||||
tlw.Show()
|
||||
|
||||
|
||||
self._windows_minimised = []
|
||||
self._windows_hidden = []
|
||||
|
||||
|
||||
def _Update( self ):
|
||||
|
|
|
@ -211,6 +211,97 @@ class EditChooseMultiple( ClientGUIScrolledPanels.EditPanel ):
|
|||
return datas
|
||||
|
||||
|
||||
class EditCookiePanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, name, value, domain, path, expires ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
self._name = wx.TextCtrl( self )
|
||||
self._value = wx.TextCtrl( self )
|
||||
self._domain = wx.TextCtrl( self )
|
||||
self._path = wx.TextCtrl( self )
|
||||
|
||||
expires_panel = ClientGUICommon.StaticBox( self, 'expires' )
|
||||
|
||||
self._expires_st = ClientGUICommon.BetterStaticText( expires_panel )
|
||||
self._expires_st_utc = ClientGUICommon.BetterStaticText( expires_panel )
|
||||
self._expires_time_delta = ClientGUITime.TimeDeltaButton( expires_panel, min = 1200, days = True, hours = True, minutes = True )
|
||||
|
||||
#
|
||||
|
||||
self._name.SetValue( name )
|
||||
self._value.SetValue( value )
|
||||
self._domain.SetValue( domain )
|
||||
self._path.SetValue( path )
|
||||
|
||||
self._expires = expires
|
||||
|
||||
self._expires_time_delta.SetValue( 30 * 86400 )
|
||||
|
||||
#
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'Actual expires as UTC Timestamp: ', self._expires_st_utc ) )
|
||||
rows.append( ( 'Set expires as a delta from now: ', self._expires_time_delta ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( expires_panel, rows )
|
||||
|
||||
expires_panel.Add( self._expires_st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
expires_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'name: ', self._name ) )
|
||||
rows.append( ( 'value: ', self._value ) )
|
||||
rows.append( ( 'domain: ', self._domain ) )
|
||||
rows.append( ( 'path: ', self._path ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
vbox.Add( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( expires_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
#
|
||||
|
||||
self._UpdateExpiresText()
|
||||
|
||||
self._expires_time_delta.Bind( ClientGUITime.EVT_TIME_DELTA, self.EventTimeDelta )
|
||||
|
||||
|
||||
def _UpdateExpiresText( self ):
|
||||
|
||||
self._expires_st.SetLabelText( HydrusData.ConvertTimestampToPrettyExpires( self._expires ) )
|
||||
self._expires_st_utc.SetLabelText( str( self._expires ) )
|
||||
|
||||
|
||||
def EventTimeDelta( self, event ):
|
||||
|
||||
time_delta = self._expires_time_delta.GetValue()
|
||||
|
||||
expires = HydrusData.GetNow() + time_delta
|
||||
|
||||
self._expires = expires
|
||||
|
||||
self._UpdateExpiresText()
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
||||
name = self._name.GetValue()
|
||||
value = self._value.GetValue()
|
||||
domain = self._domain.GetValue()
|
||||
path = self._path.GetValue()
|
||||
expires = self._expires
|
||||
|
||||
return ( name, value, domain, path, expires )
|
||||
|
||||
|
||||
class EditDomainManagerInfoPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, url_matches, network_contexts_to_custom_header_dicts ):
|
||||
|
@ -1147,13 +1238,18 @@ class EditMediaViewOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
class EditNetworkContextPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, network_context ):
|
||||
def __init__( self, parent, network_context, limited_types = None, allow_default = True ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
if limited_types is None:
|
||||
|
||||
limited_types = ( CC.NETWORK_CONTEXT_GLOBAL, CC.NETWORK_CONTEXT_DOMAIN, CC.NETWORK_CONTEXT_HYDRUS, CC.NETWORK_CONTEXT_DOWNLOADER, CC.NETWORK_CONTEXT_DOWNLOADER_QUERY, CC.NETWORK_CONTEXT_SUBSCRIPTION, CC.NETWORK_CONTEXT_THREAD_WATCHER_THREAD )
|
||||
|
||||
|
||||
self._context_type = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
for ct in ( CC.NETWORK_CONTEXT_GLOBAL, CC.NETWORK_CONTEXT_DOMAIN, CC.NETWORK_CONTEXT_HYDRUS, CC.NETWORK_CONTEXT_DOWNLOADER, CC.NETWORK_CONTEXT_DOWNLOADER_QUERY, CC.NETWORK_CONTEXT_SUBSCRIPTION, CC.NETWORK_CONTEXT_THREAD_WATCHER_THREAD ):
|
||||
for ct in limited_types:
|
||||
|
||||
self._context_type.Append( CC.network_context_type_string_lookup[ ct ], ct )
|
||||
|
||||
|
@ -1177,6 +1273,11 @@ class EditNetworkContextPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._context_data_none = wx.CheckBox( self, label = 'No specific data--acts as default.' )
|
||||
|
||||
if not allow_default:
|
||||
|
||||
self._context_data_none.Hide()
|
||||
|
||||
|
||||
names = HG.client_controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_SUBSCRIPTION )
|
||||
|
||||
for name in names:
|
||||
|
@ -1453,7 +1554,7 @@ class EditNetworkContextCustomHeadersPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
self._network_context = ClientGUICommon.NetworkContextButton( self, network_context )
|
||||
self._network_context = ClientGUICommon.NetworkContextButton( self, network_context, allow_default = False )
|
||||
|
||||
self._key = wx.TextCtrl( self )
|
||||
self._value = wx.TextCtrl( self )
|
||||
|
@ -1795,7 +1896,7 @@ class EditServersideService( ClientGUIScrolledPanels.EditPanel ):
|
|||
ClientGUICommon.StaticBox.__init__( self, parent, 'file repository' )
|
||||
|
||||
self._log_uploader_ips = wx.CheckBox( self )
|
||||
self._max_storage = ClientGUICommon.NoneableSpinCtrl( self, unit = 'MB', multiplier = 1024 * 1024 )
|
||||
self._max_storage = ClientGUIControls.NoneableBytesControl( self, initial_value = 5 * 1024 * 1024 * 1024 )
|
||||
|
||||
#
|
||||
|
||||
|
|
|
@ -2780,8 +2780,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
rows.append( ( 'Prefer system FFMPEG: ', self._use_system_ffmpeg ) )
|
||||
rows.append( ( 'Media zooms: ', self._media_zooms ) )
|
||||
rows.append( ( 'WINDOWS ONLY: Hide and anchor mouse cursor on slow canvas drags: ', self._anchor_and_hide_canvas_drags ) )
|
||||
rows.append( ( 'BUGFIX: Load images with PIL: ', self._load_images_with_pil ) )
|
||||
rows.append( ( 'BUGFIX: Disable OpenCV for gifs: ', self._disable_cv_for_gifs ) )
|
||||
rows.append( ( 'BUGFIX: Load images with PIL (slower): ', self._load_images_with_pil ) )
|
||||
rows.append( ( 'BUGFIX: Load gifs with PIL instead of OpenCV (slower, bad transparency): ', self._disable_cv_for_gifs ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
|
@ -3100,11 +3100,11 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
help_hbox = ClientGUICommon.WrapInText( disk_cache_help_button, disk_panel, 'help for this panel -->', wx.Colour( 0, 0, 255 ) )
|
||||
|
||||
self._disk_cache_init_period = ClientGUICommon.NoneableSpinCtrl( disk_panel, 'run disk cache on boot for this long', unit = 's', none_phrase = 'do not run', min = 1, max = 120 )
|
||||
self._disk_cache_init_period.SetToolTip( 'When the client boots, it can speed up operation by reading the front of the database into memory. This sets the max number of seconds it can spend doing that.' )
|
||||
self._disk_cache_init_period = ClientGUICommon.NoneableSpinCtrl( disk_panel, unit = 's', none_phrase = 'do not run', min = 1, max = 120 )
|
||||
self._disk_cache_init_period.SetToolTip( 'When the client boots, it can speed up operation (particularly loading your session pages) by reading the front of its database into memory. This sets the max number of seconds it can spend doing that.' )
|
||||
|
||||
self._disk_cache_maintenance_mb = ClientGUICommon.NoneableSpinCtrl( disk_panel, 'disk cache maintenance', unit = 'MB', none_phrase = 'do not keep db cached', min = 32, max = 65536 )
|
||||
self._disk_cache_maintenance_mb.SetToolTip( 'The client can regularly check the front of its database is cached in memory. This represents how many megabytes it will ensure are cached.' )
|
||||
self._disk_cache_maintenance = ClientGUIControls.NoneableBytesControl( disk_panel, initial_value = 256 * 1024 * 1024, none_label = 'do not keep db cached' )
|
||||
self._disk_cache_maintenance.SetToolTip( 'The client can regularly ensure the front of its database is cached in your OS\'s disk cache. This represents how many megabytes it will ensure are cached in memory.' )
|
||||
|
||||
#
|
||||
|
||||
|
@ -3163,7 +3163,19 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
#
|
||||
|
||||
self._disk_cache_init_period.SetValue( self._new_options.GetNoneableInteger( 'disk_cache_init_period' ) )
|
||||
self._disk_cache_maintenance_mb.SetValue( self._new_options.GetNoneableInteger( 'disk_cache_maintenance_mb' ) )
|
||||
|
||||
disk_cache_maintenance_mb = self._new_options.GetNoneableInteger( 'disk_cache_maintenance_mb' )
|
||||
|
||||
if disk_cache_maintenance_mb is None:
|
||||
|
||||
disk_cache_maintenance = disk_cache_maintenance_mb
|
||||
|
||||
else:
|
||||
|
||||
disk_cache_maintenance = disk_cache_maintenance_mb * 1024 * 1024
|
||||
|
||||
|
||||
self._disk_cache_maintenance.SetValue( disk_cache_maintenance )
|
||||
|
||||
( thumbnail_width, thumbnail_height ) = HC.options[ 'thumbnail_dimensions' ]
|
||||
|
||||
|
@ -3193,11 +3205,17 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
#
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'run disk cache on boot for this long: ', self._disk_cache_init_period ) )
|
||||
rows.append( ( 'regularly ensure this much of the db is in OS\'s disk cache: ', self._disk_cache_maintenance ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( disk_panel, rows )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
disk_panel.Add( help_hbox, CC.FLAGS_BUTTON_SIZER )
|
||||
disk_panel.Add( self._disk_cache_init_period, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
disk_panel.Add( self._disk_cache_maintenance_mb, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
disk_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
vbox.Add( disk_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
@ -3237,7 +3255,9 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
text += os.linesep
|
||||
text += 'If you have a lot of memory, you can set a generous potential video buffer to compensate.'
|
||||
text += os.linesep
|
||||
text += 'If the video buffer can hold an entire video, it only needs to be rendered once and will loop smoothly.'
|
||||
text += 'If the video buffer can hold an entire video, it only needs to be rendered once and will play and loop very smoothly.'
|
||||
text += os.linesep
|
||||
text += 'PROTIP: Do not go crazy here.'
|
||||
|
||||
buffer_panel.Add( wx.StaticText( buffer_panel, label = text ), CC.FLAGS_VCENTER )
|
||||
|
||||
|
@ -3299,7 +3319,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
def _ShowDiskCacheHelp( self ):
|
||||
|
||||
message = 'The hydrus database runs best on a drive with fast random access latency. Important and heavy read and write operations can function up to 100 times faster when started raw from an SSD rather than an HDD.'
|
||||
message = 'The hydrus database runs best on a drive with fast random access latency. Certain important operations can function up to 100 times faster when started raw from an SSD rather than an HDD.'
|
||||
message += os.linesep * 2
|
||||
message += 'To get around this, the client populates a pre-boot and ongoing disk cache. By contiguously frontloading the database into memory, the most important functions do not need to wait on your disk for most of their work.'
|
||||
message += os.linesep * 2
|
||||
|
@ -3307,7 +3327,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
message += os.linesep * 2
|
||||
message += 'If you run the database from an SSD, you can reduce or entirely eliminate these values, as the benefit is not so stark. 2s and 256MB is fine.'
|
||||
message += os.linesep * 2
|
||||
message += 'Unless you are testing, do not go crazy with this stuff. You can set 8192MB if you like, but there are diminishing returns.'
|
||||
message += 'Unless you are testing, do not go crazy with this stuff. You can set 8192MB if you like, but there are diminishing (and potentially negative) returns.'
|
||||
|
||||
wx.MessageBox( message )
|
||||
|
||||
|
@ -3354,7 +3374,19 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
def UpdateOptions( self ):
|
||||
|
||||
self._new_options.SetNoneableInteger( 'disk_cache_init_period', self._disk_cache_init_period.GetValue() )
|
||||
self._new_options.SetNoneableInteger( 'disk_cache_maintenance_mb', self._disk_cache_maintenance_mb.GetValue() )
|
||||
|
||||
disk_cache_maintenance = self._disk_cache_maintenance.GetValue()
|
||||
|
||||
if disk_cache_maintenance is None:
|
||||
|
||||
disk_cache_maintenance_mb = disk_cache_maintenance
|
||||
|
||||
else:
|
||||
|
||||
disk_cache_maintenance_mb = disk_cache_maintenance // ( 1024 * 1024 )
|
||||
|
||||
|
||||
self._new_options.SetNoneableInteger( 'disk_cache_maintenance_mb', disk_cache_maintenance_mb )
|
||||
|
||||
new_thumbnail_dimensions = [ self._thumbnail_width.GetValue(), self._thumbnail_height.GetValue() ]
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import ClientConstants as CC
|
|||
import ClientData
|
||||
import ClientDefaults
|
||||
import ClientGUICommon
|
||||
import ClientGUIControls
|
||||
import ClientGUIDialogs
|
||||
import ClientGUIFrames
|
||||
import ClientGUIListCtrl
|
||||
|
@ -15,6 +16,7 @@ import ClientNetworking
|
|||
import ClientTags
|
||||
import ClientThreading
|
||||
import collections
|
||||
import cookielib
|
||||
import HydrusConstants as HC
|
||||
import HydrusData
|
||||
import HydrusGlobals as HG
|
||||
|
@ -565,7 +567,9 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
|
|||
|
||||
for network_context in self._bandwidths.GetData( only_selected = True ):
|
||||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self._controller.GetGUI(), 'review bandwidth for ' + network_context.ToUnicode() )
|
||||
parent = self.GetTopLevelParent().GetParent()
|
||||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( parent, 'review bandwidth for ' + network_context.ToUnicode() )
|
||||
|
||||
panel = ReviewNetworkContextBandwidthPanel( frame, self._controller, network_context )
|
||||
|
||||
|
@ -992,6 +996,325 @@ class ReviewServicesPanel( ClientGUIScrolledPanels.ReviewPanel ):
|
|||
self._InitialiseServices()
|
||||
|
||||
|
||||
class ReviewNetworkSessionsPanel( ClientGUIScrolledPanels.ReviewPanel ):
|
||||
|
||||
def __init__( self, parent, session_manager ):
|
||||
|
||||
ClientGUIScrolledPanels.ReviewPanel.__init__( self, parent )
|
||||
|
||||
self._session_manager = session_manager
|
||||
|
||||
listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
|
||||
|
||||
self._listctrl = ClientGUIListCtrl.BetterListCtrl( listctrl_panel, 'review_network_sessions', 32, 34, [ ( 'network context', -1 ), ( 'cookies', 9 ), ( 'expires', 28 ) ], self._ConvertNetworkContextToListCtrlTuple, delete_key_callback = self._Clear, activation_callback = self._Review )
|
||||
|
||||
self._listctrl.Sort()
|
||||
|
||||
listctrl_panel.SetListCtrl( self._listctrl )
|
||||
|
||||
listctrl_panel.AddButton( 'create new', self._Add )
|
||||
listctrl_panel.AddButton( 'review', self._Review, enabled_only_on_selection = True )
|
||||
listctrl_panel.AddButton( 'clear', self._Clear, enabled_only_on_selection = True )
|
||||
listctrl_panel.AddSeparator()
|
||||
listctrl_panel.AddButton( 'refresh', self._Update )
|
||||
|
||||
#
|
||||
|
||||
self._Update()
|
||||
|
||||
#
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( listctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def _Add( self ):
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'enter new network context' ) as dlg:
|
||||
|
||||
network_context = ClientNetworking.NetworkContext( CC.NETWORK_CONTEXT_DOMAIN, 'example.com' )
|
||||
|
||||
panel = ClientGUIScrolledPanelsEdit.EditNetworkContextPanel( dlg, network_context, limited_types = ( CC.NETWORK_CONTEXT_DOMAIN, CC.NETWORK_CONTEXT_HYDRUS ), allow_default = False )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
network_context = panel.GetValue()
|
||||
|
||||
self._AddNetworkContext( network_context )
|
||||
|
||||
|
||||
|
||||
self._Update()
|
||||
|
||||
|
||||
def _AddNetworkContext( self, network_context ):
|
||||
|
||||
# this establishes a bare session
|
||||
|
||||
self._session_manager.GetSession( network_context )
|
||||
|
||||
|
||||
def _Clear( self ):
|
||||
|
||||
for network_context in self._listctrl.GetData( only_selected = True ):
|
||||
|
||||
self._session_manager.ClearSession( network_context )
|
||||
|
||||
self._AddNetworkContext( network_context )
|
||||
|
||||
|
||||
self._Update()
|
||||
|
||||
|
||||
def _ConvertNetworkContextToListCtrlTuple( self, network_context ):
|
||||
|
||||
session = self._session_manager.GetSession( network_context )
|
||||
|
||||
pretty_network_context = network_context.ToUnicode()
|
||||
|
||||
number_of_cookies = len( session.cookies )
|
||||
pretty_number_of_cookies = HydrusData.ConvertIntToPrettyString( number_of_cookies )
|
||||
|
||||
expires_numbers = [ c.expires for c in session.cookies if c.expires is not None ]
|
||||
|
||||
if len( expires_numbers ) == 0:
|
||||
|
||||
if number_of_cookies > 0:
|
||||
|
||||
expiry = 0
|
||||
pretty_expiry = 'session'
|
||||
|
||||
else:
|
||||
|
||||
expiry = -1
|
||||
pretty_expiry = ''
|
||||
|
||||
|
||||
else:
|
||||
|
||||
expiry = max( expires_numbers )
|
||||
pretty_expiry = HydrusData.ConvertTimestampToPrettyExpires( expiry )
|
||||
|
||||
|
||||
display_tuple = ( pretty_network_context, pretty_number_of_cookies, pretty_expiry )
|
||||
sort_tuple = ( pretty_network_context, number_of_cookies, expiry )
|
||||
|
||||
return ( display_tuple, sort_tuple )
|
||||
|
||||
|
||||
def _Review( self ):
|
||||
|
||||
for network_context in self._listctrl.GetData( only_selected = True ):
|
||||
|
||||
parent = self.GetTopLevelParent().GetParent()
|
||||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( parent, 'review session for ' + network_context.ToUnicode() )
|
||||
|
||||
panel = ReviewNetworkSessionPanel( frame, self._session_manager, network_context )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
||||
|
||||
def _Update( self ):
|
||||
|
||||
network_contexts = [ network_context for network_context in self._session_manager.GetNetworkContexts() if network_context.context_type in ( CC.NETWORK_CONTEXT_DOMAIN, CC.NETWORK_CONTEXT_HYDRUS ) ]
|
||||
|
||||
self._listctrl.SetData( network_contexts )
|
||||
|
||||
|
||||
class ReviewNetworkSessionPanel( ClientGUIScrolledPanels.ReviewPanel ):
|
||||
|
||||
def __init__( self, parent, session_manager, network_context ):
|
||||
|
||||
ClientGUIScrolledPanels.ReviewPanel.__init__( self, parent )
|
||||
|
||||
self._session_manager = session_manager
|
||||
self._network_context = network_context
|
||||
|
||||
self._session = self._session_manager.GetSession( self._network_context )
|
||||
|
||||
self._description = ClientGUICommon.BetterStaticText( self, network_context.ToUnicode() )
|
||||
|
||||
listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
|
||||
|
||||
self._listctrl = ClientGUIListCtrl.BetterListCtrl( listctrl_panel, 'review_network_session', 8, 18, [ ( 'name', -1 ), ( 'value', 32 ), ( 'domain', 20 ), ( 'path', 8 ), ( 'expires', 28 ) ], self._ConvertCookieToListCtrlTuple, delete_key_callback = self._Delete, activation_callback = self._Edit )
|
||||
|
||||
self._listctrl.Sort()
|
||||
|
||||
listctrl_panel.SetListCtrl( self._listctrl )
|
||||
|
||||
listctrl_panel.AddButton( 'add', self._Add )
|
||||
listctrl_panel.AddButton( 'edit', self._Edit, enabled_only_on_selection = True )
|
||||
listctrl_panel.AddButton( 'delete', self._Delete, enabled_only_on_selection = True )
|
||||
listctrl_panel.AddSeparator()
|
||||
listctrl_panel.AddButton( 'refresh', self._Update )
|
||||
|
||||
#
|
||||
|
||||
self._Update()
|
||||
|
||||
#
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.Add( self._description, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.Add( listctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def _Add( self ):
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit cookie' ) as dlg:
|
||||
|
||||
name = 'name'
|
||||
value = '123'
|
||||
|
||||
if self._network_context.context_type == CC.NETWORK_CONTEXT_DOMAIN:
|
||||
|
||||
domain = '.' + self._network_context.context_data
|
||||
|
||||
else:
|
||||
|
||||
domain = 'service domain'
|
||||
|
||||
|
||||
path = '/'
|
||||
expires = HydrusData.GetNow() + 30 * 86400
|
||||
|
||||
panel = ClientGUIScrolledPanelsEdit.EditCookiePanel( dlg, name, value, domain, path, expires )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
( name, value, domain, path, expires ) = panel.GetValue()
|
||||
|
||||
self._SetCookie( name, value, domain, path, expires )
|
||||
|
||||
|
||||
|
||||
self._Update()
|
||||
|
||||
|
||||
def _ConvertCookieToListCtrlTuple( self, cookie ):
|
||||
|
||||
name = cookie.name
|
||||
pretty_name = name
|
||||
|
||||
value = cookie.value
|
||||
pretty_value = value
|
||||
|
||||
domain = cookie.domain
|
||||
pretty_domain = domain
|
||||
|
||||
path = cookie.path
|
||||
pretty_path = path
|
||||
|
||||
expiry = cookie.expires
|
||||
|
||||
if expiry is None:
|
||||
|
||||
expiry = -1
|
||||
pretty_expiry = 'session'
|
||||
|
||||
else:
|
||||
|
||||
pretty_expiry = HydrusData.ConvertTimestampToPrettyExpires( expiry )
|
||||
|
||||
|
||||
display_tuple = ( pretty_name, pretty_value, pretty_domain, pretty_path, pretty_expiry )
|
||||
sort_tuple = ( name, value, domain, path, expiry )
|
||||
|
||||
return ( display_tuple, sort_tuple )
|
||||
|
||||
|
||||
def _Delete( self ):
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, 'Delete all selected cookies?' ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
for cookie in self._listctrl.GetData( only_selected = True ):
|
||||
|
||||
domain = cookie.domain
|
||||
path = cookie.path
|
||||
name = cookie.name
|
||||
|
||||
self._session.cookies.clear( domain, path, name )
|
||||
|
||||
|
||||
self._Update()
|
||||
|
||||
|
||||
|
||||
|
||||
def _Edit( self ):
|
||||
|
||||
for cookie in self._listctrl.GetData( only_selected = True ):
|
||||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'edit cookie' ) as dlg:
|
||||
|
||||
name = cookie.name
|
||||
value = cookie.value
|
||||
domain = cookie.domain
|
||||
path = cookie.path
|
||||
expires = cookie.expires
|
||||
|
||||
panel = ClientGUIScrolledPanelsEdit.EditCookiePanel( dlg, name, value, domain, path, expires )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
( name, value, domain, path, expires ) = panel.GetValue()
|
||||
|
||||
self._SetCookie( name, value, domain, path, expires )
|
||||
|
||||
else:
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
self._Update()
|
||||
|
||||
|
||||
def _SetCookie( self, name, value, domain, path, expires ):
|
||||
|
||||
version = 0
|
||||
port = None
|
||||
port_specified = False
|
||||
domain_specified = True
|
||||
domain_initial_dot = domain.startswith( '.' )
|
||||
path_specified = True
|
||||
secure = False
|
||||
discard = False
|
||||
comment = None
|
||||
comment_url = None
|
||||
rest = {}
|
||||
|
||||
cookie = cookielib.Cookie( version, name, value, port, port_specified, domain, domain_specified, domain_initial_dot, path, path_specified, secure, expires, discard, comment, comment_url, rest )
|
||||
|
||||
self._session.cookies.set_cookie( cookie )
|
||||
|
||||
|
||||
def _Update( self ):
|
||||
|
||||
self._session = self._session_manager.GetSession( self._network_context )
|
||||
|
||||
cookies = list( self._session.cookies )
|
||||
|
||||
self._listctrl.SetData( cookies )
|
||||
|
||||
|
||||
class MigrateDatabasePanel( ClientGUIScrolledPanels.ReviewPanel ):
|
||||
|
||||
RESIZED_RATIO = 0.012
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import ClientData
|
||||
import ClientGUICommon
|
||||
import HydrusConstants as HC
|
||||
import HydrusData
|
||||
import HydrusGlobals as HG
|
||||
import wx
|
||||
|
||||
|
@ -74,17 +75,49 @@ class ShortcutsHandler( object ):
|
|||
shortcut_processed = True
|
||||
|
||||
|
||||
if HG.shortcut_report_mode:
|
||||
|
||||
message = 'Shortcut "' + shortcut.ToString() + '" matched to command "' + command.ToString() + '" on ' + repr( self._parent ) + '.'
|
||||
|
||||
if command_processed:
|
||||
|
||||
message += ' It was processed.'
|
||||
|
||||
else:
|
||||
|
||||
message += ' It was not processed.'
|
||||
|
||||
|
||||
HydrusData.ShowText( message )
|
||||
|
||||
|
||||
|
||||
return shortcut_processed
|
||||
|
||||
|
||||
def EventCharHook( self, event ):
|
||||
|
||||
if IShouldCatchCharHook( self._parent ):
|
||||
shortcut = ClientData.ConvertKeyEventToShortcut( event )
|
||||
|
||||
if shortcut is not None:
|
||||
|
||||
shortcut = ClientData.ConvertKeyEventToShortcut( event )
|
||||
if HG.shortcut_report_mode:
|
||||
|
||||
message = 'Key shortcut "' + shortcut.ToString() + '" passing through ' + repr( self._parent ) + '.'
|
||||
|
||||
if IShouldCatchCharHook( self._parent ):
|
||||
|
||||
message += ' I am in a state to catch it.'
|
||||
|
||||
else:
|
||||
|
||||
message += ' I am not in a state to catch it.'
|
||||
|
||||
|
||||
HydrusData.ShowText( message )
|
||||
|
||||
|
||||
if shortcut is not None:
|
||||
if IShouldCatchCharHook( self._parent ):
|
||||
|
||||
shortcut_processed = self._ProcessShortcut( shortcut )
|
||||
|
||||
|
|
|
@ -36,7 +36,10 @@ def EfficientlyResizeNumpyImage( numpy_image, ( target_x, target_y ) ):
|
|||
|
||||
( im_y, im_x, depth ) = numpy_image.shape
|
||||
|
||||
if target_x >= im_x and target_y >= im_y: return numpy_image
|
||||
if target_x >= im_x and target_y >= im_y:
|
||||
|
||||
return numpy_image
|
||||
|
||||
|
||||
# this seems to slow things down a lot, at least for cv!
|
||||
#if im_x > 2 * target_x and im_y > 2 * target_y: result = cv2.resize( numpy_image, ( 2 * target_x, 2 * target_y ), interpolation = cv2.INTER_NEAREST )
|
||||
|
@ -47,7 +50,10 @@ def EfficientlyThumbnailNumpyImage( numpy_image, ( target_x, target_y ) ):
|
|||
|
||||
( im_y, im_x, depth ) = numpy_image.shape
|
||||
|
||||
if target_x >= im_x and target_y >= im_y: return numpy_image
|
||||
if target_x >= im_x and target_y >= im_y:
|
||||
|
||||
return numpy_image
|
||||
|
||||
|
||||
( target_x, target_y ) = HydrusImageHandling.GetThumbnailResolution( ( im_x, im_y ), ( target_x, target_y ) )
|
||||
|
||||
|
|
|
@ -4259,6 +4259,17 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
return HydrusData.TimeHasPassed( self._no_work_until )
|
||||
|
||||
|
||||
def _ShowHitPeriodicFileLimitMessage( self, query_text ):
|
||||
|
||||
message = 'When syncing, the query "' + query_text + '" for subscription "' + self._name + '" hit its periodic file limit!'
|
||||
message += os.linesep * 2
|
||||
message += 'This may be because the query has not run in a while--so the backlog of files has built up--or that the site has changed how it presents file urls on its gallery pages (and so the subscription thinks it is seeing new files when it truly is not).'
|
||||
message += os.linesep * 2
|
||||
message += 'If the former is true, you might want to fill in the gap with a manual download page, but if the latter is true, the maintainer for the download parser (hydrus dev or whoever), would be interested in knowing this information so they can roll out a fix.'
|
||||
|
||||
HydrusData.ShowText( message )
|
||||
|
||||
|
||||
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
|
||||
|
||||
if version == 1:
|
||||
|
@ -4678,6 +4689,8 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
if self._periodic_file_limit is not None and total_new_urls + 1 > self._periodic_file_limit:
|
||||
|
||||
self._ShowHitPeriodicFileLimitMessage( query_text )
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
@ -4768,6 +4781,8 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
if self._periodic_file_limit is not None and total_new_urls + 1 > self._periodic_file_limit:
|
||||
|
||||
self._ShowHitPeriodicFileLimitMessage( query_text )
|
||||
|
||||
keep_checking = False
|
||||
|
||||
break
|
||||
|
|
|
@ -1974,6 +1974,23 @@ class NetworkSessionManager( HydrusSerialisable.SerialisableBase ):
|
|||
self._network_contexts_to_session_timeouts = {}
|
||||
|
||||
|
||||
def _CleanSessionCookies( self, network_context, session ):
|
||||
|
||||
if network_context not in self._network_contexts_to_session_timeouts:
|
||||
|
||||
self._network_contexts_to_session_timeouts[ network_context ] = 0
|
||||
|
||||
|
||||
if HydrusData.TimeHasPassed( self._network_contexts_to_session_timeouts[ network_context ] ):
|
||||
|
||||
session.cookies.clear_session_cookies()
|
||||
|
||||
|
||||
self._network_contexts_to_session_timeouts[ network_context ] = HydrusData.GetNow() + self.SESSION_TIMEOUT
|
||||
|
||||
session.cookies.clear_expired_cookies()
|
||||
|
||||
|
||||
def _GenerateSession( self, network_context ):
|
||||
|
||||
session = requests.Session()
|
||||
|
@ -2026,6 +2043,14 @@ class NetworkSessionManager( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
def GetNetworkContexts( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._network_contexts_to_sessions.keys()
|
||||
|
||||
|
||||
|
||||
def GetSession( self, network_context ):
|
||||
|
||||
with self._lock:
|
||||
|
@ -2047,17 +2072,7 @@ class NetworkSessionManager( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
#
|
||||
|
||||
if network_context not in self._network_contexts_to_session_timeouts:
|
||||
|
||||
self._network_contexts_to_session_timeouts[ network_context ] = 0
|
||||
|
||||
|
||||
if HydrusData.TimeHasPassed( self._network_contexts_to_session_timeouts[ network_context ] ):
|
||||
|
||||
session.cookies.clear_session_cookies()
|
||||
|
||||
|
||||
self._network_contexts_to_session_timeouts[ network_context ] = HydrusData.GetNow() + self.SESSION_TIMEOUT
|
||||
self._CleanSessionCookies( network_context, session )
|
||||
|
||||
#
|
||||
|
||||
|
|
|
@ -130,9 +130,18 @@ class NetworkLoginManager( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
service = services_manager.GetService( service_key )
|
||||
|
||||
if not service.IsFunctional( ignore_account = True ):
|
||||
try:
|
||||
|
||||
raise HydrusExceptions.LoginException( 'Service has had a recent error or is otherwise not functional! You might like to try refreshing its account in \'review services\'.' )
|
||||
service.CheckFunctional( ignore_account = True )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
message = 'Service has had a recent error or is otherwise not functional! Specific error was:'
|
||||
message += os.linesep * 2
|
||||
message += HydrusData.ToUnicode( e )
|
||||
message += 'You might like to try refreshing its account in \'review services\'.'
|
||||
|
||||
raise HydrusExceptions.LoginException( message )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -148,9 +148,9 @@ class Service( object ):
|
|||
|
||||
def __hash__( self ): return self._service_key.__hash__()
|
||||
|
||||
def _GetFunctionalStatus( self ):
|
||||
def _CheckFunctional( self ):
|
||||
|
||||
return ( True, 'service is functional' )
|
||||
pass
|
||||
|
||||
|
||||
def _GetSerialisableDictionary( self ):
|
||||
|
@ -170,6 +170,14 @@ class Service( object ):
|
|||
HG.client_controller.pub( 'service_updated', self )
|
||||
|
||||
|
||||
def CheckFunctional( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._CheckFunctional()
|
||||
|
||||
|
||||
|
||||
def Duplicate( self ):
|
||||
|
||||
with self._lock:
|
||||
|
@ -220,15 +228,15 @@ class Service( object ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
( functional, status ) = self._GetFunctionalStatus()
|
||||
|
||||
if not functional:
|
||||
try:
|
||||
|
||||
return 'service not functional: ' + status
|
||||
self._CheckFunctional()
|
||||
|
||||
else:
|
||||
return 'service is functional'
|
||||
|
||||
return status
|
||||
except Exception as e:
|
||||
|
||||
return HydrusData.ToUnicode( e )
|
||||
|
||||
|
||||
|
||||
|
@ -242,9 +250,16 @@ class Service( object ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
( functional, status ) = self._GetFunctionalStatus()
|
||||
|
||||
return functional
|
||||
try:
|
||||
|
||||
self._CheckFunctional()
|
||||
|
||||
return True
|
||||
|
||||
except:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -273,14 +288,14 @@ class Service( object ):
|
|||
|
||||
class ServiceLocalBooru( Service ):
|
||||
|
||||
def _GetFunctionalStatus( self ):
|
||||
def _CheckFunctional( self ):
|
||||
|
||||
if not self._bandwidth_rules.CanStartRequest( self._bandwidth_tracker ):
|
||||
|
||||
return ( False, 'bandwidth exceeded' )
|
||||
raise HydrusExceptions.BandwidthException( 'bandwidth exceeded' )
|
||||
|
||||
|
||||
return Service._GetFunctionalStatus( self )
|
||||
Service._CheckFunctional( self )
|
||||
|
||||
|
||||
def _GetSerialisableDictionary( self ):
|
||||
|
@ -490,11 +505,11 @@ class ServiceRemote( Service ):
|
|||
return 3600 * 4
|
||||
|
||||
|
||||
def _GetFunctionalStatus( self ):
|
||||
def _CheckFunctional( self ):
|
||||
|
||||
if not HydrusData.TimeHasPassed( self._no_requests_until ):
|
||||
|
||||
return ( False, self._no_requests_reason + ' - next request ' + HydrusData.ConvertTimestampToPrettyPending( self._no_requests_until ) )
|
||||
raise HydrusExceptions.PermissionException( self._no_requests_reason + ' - next request ' + HydrusData.ConvertTimestampToPrettyPending( self._no_requests_until ) )
|
||||
|
||||
|
||||
example_nj = ClientNetworking.NetworkJobHydrus( self._service_key, 'GET', self._GetBaseURL() )
|
||||
|
@ -503,10 +518,10 @@ class ServiceRemote( Service ):
|
|||
|
||||
if not can_start:
|
||||
|
||||
return ( False, 'bandwidth exceeded' )
|
||||
raise HydrusExceptions.BandwidthException( 'bandwidth exceeded' )
|
||||
|
||||
|
||||
return Service._GetFunctionalStatus( self )
|
||||
Service._CheckFunctional( self )
|
||||
|
||||
|
||||
def _GetSerialisableDictionary( self ):
|
||||
|
@ -619,19 +634,19 @@ class ServiceRestricted( ServiceRemote ):
|
|||
|
||||
|
||||
|
||||
def _GetFunctionalStatus( self, ignore_account = False ):
|
||||
def _CheckFunctional( self, ignore_account = False ):
|
||||
|
||||
if not self._credentials.HasAccessKey():
|
||||
|
||||
return ( False, 'this service has no access key set' )
|
||||
raise HydrusExceptions.PermissionException( 'this service has no access key set' )
|
||||
|
||||
|
||||
if not ignore_account and not self._account.IsFunctional():
|
||||
if not ignore_account:
|
||||
|
||||
return ( False, 'account problem: ' + self._account.GetStatusString() )
|
||||
self._account.CheckFunctional()
|
||||
|
||||
|
||||
return ServiceRemote._GetFunctionalStatus( self )
|
||||
ServiceRemote._CheckFunctional( self )
|
||||
|
||||
|
||||
def _GetSerialisableDictionary( self ):
|
||||
|
@ -652,6 +667,14 @@ class ServiceRestricted( ServiceRemote ):
|
|||
self._next_account_sync = dictionary[ 'next_account_sync' ]
|
||||
|
||||
|
||||
def CheckFunctional( self, ignore_account = False ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._CheckFunctional( ignore_account = ignore_account )
|
||||
|
||||
|
||||
|
||||
def GetAccount( self ):
|
||||
|
||||
with self._lock:
|
||||
|
@ -687,9 +710,16 @@ class ServiceRestricted( ServiceRemote ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
( functional, status ) = self._GetFunctionalStatus( ignore_account = ignore_account )
|
||||
|
||||
return functional
|
||||
try:
|
||||
|
||||
self._CheckFunctional( ignore_account = ignore_account )
|
||||
|
||||
return True
|
||||
|
||||
except:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -937,9 +967,16 @@ class ServiceRepository( ServiceRestricted ):
|
|||
return False
|
||||
|
||||
|
||||
( result, reason ) = self._GetFunctionalStatus()
|
||||
try:
|
||||
|
||||
self._CheckFunctional()
|
||||
|
||||
except:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
return result
|
||||
return True
|
||||
|
||||
|
||||
def _GetSerialisableDictionary( self ):
|
||||
|
@ -1147,16 +1184,28 @@ class ServiceRepository( ServiceRestricted ):
|
|||
|
||||
if update_network_string_hash != update_hash:
|
||||
|
||||
# this is the weird update problem, seems to be network related
|
||||
# throwing a whole hullabaloo about it only caused problems, as the real fix was 'unpause it, try again'
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._paused = True
|
||||
|
||||
self._DealWithFundamentalNetworkError()
|
||||
self._DelayFutureRequests( 'had an unusual update response' )
|
||||
|
||||
|
||||
message = 'Update ' + update_hash.encode( 'hex' ) + ' downloaded from the ' + self._name + ' repository had hash ' + update_network_string_hash.encode( 'hex' ) + '! This is a serious error!'
|
||||
filename = 'should be ' + update_network_string_hash.encode( 'hex' ) + '.wew'
|
||||
|
||||
path = os.path.join( HG.client_controller.db_dir, filename )
|
||||
|
||||
with open( path, 'wb' ) as f:
|
||||
|
||||
f.write( update_network_string )
|
||||
|
||||
|
||||
message = 'Update ' + update_hash.encode( 'hex' ) + ' downloaded from the ' + self._name + ' repository had hash ' + update_network_string_hash.encode( 'hex' ) + '!'
|
||||
message += os.linesep * 2
|
||||
message += 'The repository has been paused for now. Please look into what could be wrong and report this to the hydrus dev.'
|
||||
message += 'This is an unusual network error that hydrus dev is trying to pin down. The bad file has been written to ' + path + '--please inform hydrus dev of what has happened and send him that file!'
|
||||
message += os.linesep * 2
|
||||
message += 'Your repository will try again later, which usually fixes this problem.'
|
||||
|
||||
HydrusData.ShowText( message )
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 18
|
||||
SOFTWARE_VERSION = 298
|
||||
SOFTWARE_VERSION = 299
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
|
|
@ -223,6 +223,31 @@ def GetFileInfo( path, mime = None ):
|
|||
duration = int( duration_in_s * 1000 )
|
||||
|
||||
|
||||
if width is not None and width < 0:
|
||||
|
||||
width *= -1
|
||||
|
||||
|
||||
if height is not None and height < 0:
|
||||
|
||||
width *= -1
|
||||
|
||||
|
||||
if duration is not None and duration < 0:
|
||||
|
||||
duration *= -1
|
||||
|
||||
|
||||
if num_frames is not None and num_frames < 0:
|
||||
|
||||
num_frames *= -1
|
||||
|
||||
|
||||
if num_words is not None and num_words < 0:
|
||||
|
||||
num_words *= -1
|
||||
|
||||
|
||||
return ( size, mime, width, height, duration, num_frames, num_words )
|
||||
|
||||
def GetHashFromPath( path ):
|
||||
|
|
|
@ -15,6 +15,7 @@ callto_report_mode = False
|
|||
db_report_mode = False
|
||||
db_profile_mode = False
|
||||
gui_report_mode = False
|
||||
shortcut_report_mode = False
|
||||
hover_window_report_mode = False
|
||||
menu_profile_mode = False
|
||||
network_report_mode = False
|
||||
|
|
|
@ -356,6 +356,34 @@ class Account( object ):
|
|||
return self.__repr__()
|
||||
|
||||
|
||||
def _CheckFunctional( self ):
|
||||
|
||||
if self._created == 0:
|
||||
|
||||
raise HydrusExceptions.PermissionException( 'account is unsynced' )
|
||||
|
||||
|
||||
if self._account_type.HasPermission( HC.CONTENT_TYPE_SERVICES, HC.PERMISSION_ACTION_OVERRULE ):
|
||||
|
||||
return # admins can do anything
|
||||
|
||||
|
||||
if self._IsBanned():
|
||||
|
||||
raise HydrusExceptions.PermissionException( 'This account is banned: ' + self._GetBannedString() )
|
||||
|
||||
|
||||
if self._IsExpired():
|
||||
|
||||
raise HydrusExceptions.PermissionException( 'This account is expired: ' + self._GetExpiresString() )
|
||||
|
||||
|
||||
if not self._account_type.BandwidthOK( self._bandwidth_tracker ):
|
||||
|
||||
raise HydrusExceptions.PermissionException( 'account has exceeded bandwidth' )
|
||||
|
||||
|
||||
|
||||
def _GetBannedString( self ):
|
||||
|
||||
if self._banned_info is None:
|
||||
|
@ -375,36 +403,6 @@ class Account( object ):
|
|||
return HydrusData.ConvertTimestampToPrettyExpires( self._expires )
|
||||
|
||||
|
||||
def _GetFunctionalStatus( self ):
|
||||
|
||||
if self._created == 0:
|
||||
|
||||
return ( False, 'account is unsynced' )
|
||||
|
||||
|
||||
if self._account_type.HasPermission( HC.CONTENT_TYPE_SERVICES, HC.PERMISSION_ACTION_OVERRULE ):
|
||||
|
||||
return ( True, 'admin: can do anything' )
|
||||
|
||||
|
||||
if self._IsBanned():
|
||||
|
||||
return ( False, self._GetBannedString() )
|
||||
|
||||
|
||||
if self._IsExpired():
|
||||
|
||||
return ( False, self._GetExpiresString() )
|
||||
|
||||
|
||||
if not self._account_type.BandwidthOK( self._bandwidth_tracker ):
|
||||
|
||||
return ( False, 'account has exceeded bandwidth' )
|
||||
|
||||
|
||||
return ( True, 'account is functional' )
|
||||
|
||||
|
||||
def _IsBanned( self ):
|
||||
|
||||
if self._banned_info is None:
|
||||
|
@ -464,22 +462,7 @@ class Account( object ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
if self._IsBanned():
|
||||
|
||||
banned_string = self._GetBannedString()
|
||||
|
||||
raise HydrusExceptions.PermissionException( 'This account is banned: ' + banned_string )
|
||||
|
||||
|
||||
if self._IsExpired():
|
||||
|
||||
raise HydrusExceptions.PermissionException( 'This account has expired.' )
|
||||
|
||||
|
||||
if not self._account_type.BandwidthOK( self._bandwidth_tracker ):
|
||||
|
||||
raise HydrusExceptions.PermissionException( 'This account has no remaining bandwidth.' )
|
||||
|
||||
self._CheckFunctional()
|
||||
|
||||
|
||||
|
||||
|
@ -545,15 +528,15 @@ class Account( object ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
( functional, status ) = self._GetFunctionalStatus()
|
||||
|
||||
if functional:
|
||||
try:
|
||||
|
||||
return status
|
||||
self._CheckFunctional()
|
||||
|
||||
else:
|
||||
return 'account is functional'
|
||||
|
||||
return 'account not functional: ' + status
|
||||
except Exception as e:
|
||||
|
||||
return HydrusData.ToUnicode( e )
|
||||
|
||||
|
||||
|
||||
|
@ -578,9 +561,16 @@ class Account( object ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
( functional, status ) = self._GetFunctionalStatus()
|
||||
|
||||
return functional
|
||||
try:
|
||||
|
||||
self._CheckFunctional()
|
||||
|
||||
return True
|
||||
|
||||
except:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue