2015-03-25 22:04:19 +00:00
import ClientCaches
2015-04-01 20:44:54 +00:00
import ClientData
2015-05-20 21:31:40 +00:00
import ClientDaemons
2016-02-03 22:12:53 +00:00
import ClientDefaults
2018-01-03 22:37:30 +00:00
import ClientGUICommon
2016-11-02 21:09:14 +00:00
import ClientGUIMenus
2015-10-21 21:53:10 +00:00
import ClientNetworking
2017-10-04 17:51:58 +00:00
import ClientNetworkingDomain
2017-10-18 19:41:25 +00:00
import ClientNetworkingLogin
2016-02-17 22:06:47 +00:00
import ClientThreading
2013-11-06 18:22:07 +00:00
import hashlib
2015-04-01 20:44:54 +00:00
import HydrusConstants as HC
import HydrusController
import HydrusData
2013-07-24 20:26:00 +00:00
import HydrusExceptions
2017-05-10 21:33:58 +00:00
import HydrusGlobals as HG
2014-01-29 21:59:42 +00:00
import HydrusNetworking
2017-07-12 20:03:45 +00:00
import HydrusPaths
2015-10-21 21:53:10 +00:00
import HydrusSerialisable
2014-05-21 21:37:35 +00:00
import HydrusThreading
2016-12-07 22:12:52 +00:00
import HydrusVideoHandling
2013-02-19 00:11:43 +00:00
import ClientConstants as CC
import ClientDB
import ClientGUI
2013-05-29 20:19:54 +00:00
import ClientGUIDialogs
2017-04-05 21:16:40 +00:00
import ClientGUIScrolledPanelsManagement
import ClientGUITopLevelWindows
2018-01-03 22:37:30 +00:00
import gc
2013-02-19 00:11:43 +00:00
import os
2015-08-19 21:48:21 +00:00
import psutil
2015-04-08 18:10:50 +00:00
import threading
2013-02-19 00:11:43 +00:00
import time
import traceback
import wx
2017-10-25 21:45:15 +00:00
if not HG . twisted_is_broke :
from twisted . internet import reactor , defer
2018-01-03 22:37:30 +00:00
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 )
2015-04-01 20:44:54 +00:00
class Controller ( HydrusController . HydrusController ) :
2014-12-03 22:56:40 +00:00
2016-10-19 20:02:56 +00:00
def __init__ ( self , db_dir , no_daemons , no_wal ) :
2015-09-16 18:11:00 +00:00
2016-08-31 19:55:14 +00:00
self . _last_shutdown_was_bad = False
2017-05-10 21:33:58 +00:00
self . _is_booted = False
2017-07-27 00:47:13 +00:00
self . _splash = None
2016-10-19 20:02:56 +00:00
HydrusController . HydrusController . __init__ ( self , db_dir , no_daemons , no_wal )
2015-09-16 18:11:00 +00:00
2017-03-08 23:23:12 +00:00
self . _name = ' client '
2017-05-10 21:33:58 +00:00
HG . client_controller = self
2015-09-16 18:11:00 +00:00
2016-02-03 22:12:53 +00:00
# just to set up some defaults, in case some db update expects something for an odd yaml-loading reason
2017-10-18 19:41:25 +00:00
self . options = ClientDefaults . GetClientDefaultOptions ( )
self . new_options = ClientData . ClientOptions ( self . db_dir )
2016-02-03 22:12:53 +00:00
2017-10-18 19:41:25 +00:00
HC . options = self . options
2016-02-03 22:12:53 +00:00
2015-10-28 21:29:05 +00:00
self . _last_mouse_position = None
2015-11-25 22:00:57 +00:00
self . _menu_open = False
2016-01-13 22:08:19 +00:00
self . _previously_idle = False
2016-05-11 18:16:39 +00:00
self . _idle_started = None
2015-10-28 21:29:05 +00:00
2017-06-28 20:23:21 +00:00
self . client_files_manager = None
self . services_manager = None
2015-09-16 18:11:00 +00:00
2015-09-02 23:16:09 +00:00
def _InitDB ( self ) :
2017-07-12 20:03:45 +00:00
return ClientDB . DB ( self , self . db_dir , ' client ' , no_wal = self . _no_wal )
2015-09-02 23:16:09 +00:00
2017-07-27 00:47:13 +00:00
def _DestroySplash ( self ) :
if self . _splash is not None :
2017-08-09 21:33:51 +00:00
wx . CallAfter ( self . _splash . Destroy )
2017-07-27 00:47:13 +00:00
self . _splash = None
2017-11-08 22:07:12 +00:00
def _ReportShutdownDaemonsStatus ( self ) :
names = { daemon . name for daemon in self . _daemons if daemon . is_alive ( ) }
names = list ( names )
names . sort ( )
self . pub ( ' splash_set_status_subtext ' , ' , ' . join ( names ) )
2017-01-25 22:56:55 +00:00
def CallBlockingToWx ( self , func , * args , * * kwargs ) :
2015-08-26 21:18:39 +00:00
def wx_code ( job_key ) :
try :
2017-01-25 22:56:55 +00:00
result = func ( * args , * * kwargs )
2015-08-26 21:18:39 +00:00
job_key . SetVariable ( ' result ' , result )
2015-12-23 22:51:04 +00:00
except HydrusExceptions . PermissionException as e :
job_key . SetVariable ( ' error ' , e )
2015-08-26 21:18:39 +00:00
except Exception as e :
job_key . SetVariable ( ' error ' , e )
2015-11-18 22:44:07 +00:00
HydrusData . Print ( ' CallBlockingToWx just caught this error: ' )
2015-09-02 23:16:09 +00:00
HydrusData . DebugPrint ( traceback . format_exc ( ) )
2016-07-06 21:13:15 +00:00
finally :
job_key . Finish ( )
2015-08-26 21:18:39 +00:00
2016-02-17 22:06:47 +00:00
job_key = ClientThreading . JobKey ( )
2015-08-26 21:18:39 +00:00
job_key . Begin ( )
wx . CallAfter ( wx_code , job_key )
while not job_key . IsDone ( ) :
2015-11-04 22:30:28 +00:00
if self . _model_shutdown :
2018-01-03 22:37:30 +00:00
raise HydrusExceptions . ShutdownException ( ' Application is shutting down! ' )
2015-11-04 22:30:28 +00:00
2015-08-26 21:18:39 +00:00
time . sleep ( 0.05 )
2016-01-06 21:17:20 +00:00
if job_key . HasVariable ( ' result ' ) :
2016-09-28 18:48:01 +00:00
# result can be None, for wx_code that has no return variable
2016-01-06 21:17:20 +00:00
2016-09-28 18:48:01 +00:00
result = job_key . GetIfHasVariable ( ' result ' )
2016-01-06 21:17:20 +00:00
2016-09-28 18:48:01 +00:00
return result
2016-01-06 21:17:20 +00:00
2016-09-28 18:48:01 +00:00
error = job_key . GetIfHasVariable ( ' error ' )
if error is not None :
2016-01-06 21:17:20 +00:00
2016-09-28 18:48:01 +00:00
raise error
2016-01-06 21:17:20 +00:00
2015-08-26 21:18:39 +00:00
2016-09-28 18:48:01 +00:00
raise HydrusExceptions . ShutdownException ( )
2015-08-26 21:18:39 +00:00
2015-09-02 23:16:09 +00:00
def CheckAlreadyRunning ( self ) :
2017-07-12 20:03:45 +00:00
while HydrusData . IsAlreadyRunning ( self . db_dir , ' client ' ) :
2015-09-02 23:16:09 +00:00
2015-09-23 21:21:02 +00:00
self . pub ( ' splash_set_status_text ' , ' client already running ' )
2015-09-02 23:16:09 +00:00
def wx_code ( ) :
message = ' It looks like another instance of this client is already running, so this instance cannot start. '
message + = os . linesep * 2
message + = ' If the old instance is closing and does not quit for a _very_ long time, it is usually safe to force-close it from task manager. '
with ClientGUIDialogs . DialogYesNo ( self . _splash , message , ' The client is already running. ' , yes_label = ' wait a bit, then try again ' , no_label = ' forget it ' ) as dlg :
if dlg . ShowModal ( ) != wx . ID_YES :
raise HydrusExceptions . PermissionException ( )
self . CallBlockingToWx ( wx_code )
for i in range ( 10 , 0 , - 1 ) :
2017-07-12 20:03:45 +00:00
if not HydrusData . IsAlreadyRunning ( self . db_dir , ' client ' ) :
2015-09-02 23:16:09 +00:00
break
2015-09-23 21:21:02 +00:00
self . pub ( ' splash_set_status_text ' , ' waiting ' + str ( i ) + ' seconds ' )
2015-09-02 23:16:09 +00:00
time . sleep ( 1 )
2015-10-28 21:29:05 +00:00
def CheckMouseIdle ( self ) :
mouse_position = wx . GetMousePosition ( )
if self . _last_mouse_position is None :
self . _last_mouse_position = mouse_position
elif mouse_position != self . _last_mouse_position :
2016-12-14 21:19:07 +00:00
idle_before_position_update = self . CurrentlyIdle ( )
2015-10-28 21:29:05 +00:00
self . _timestamps [ ' last_mouse_action ' ] = HydrusData . GetNow ( )
self . _last_mouse_position = mouse_position
2016-12-14 21:19:07 +00:00
idle_after_position_update = self . CurrentlyIdle ( )
2015-10-28 21:29:05 +00:00
2016-12-14 21:19:07 +00:00
move_knocked_us_out_of_idle = ( not idle_before_position_update ) and idle_after_position_update
if move_knocked_us_out_of_idle :
2015-10-28 21:29:05 +00:00
2018-01-03 22:37:30 +00:00
self . gui . SetStatusBarDirty ( )
2015-10-28 21:29:05 +00:00
2018-01-10 22:41:51 +00:00
def CreateSplash ( self ) :
try :
self . _splash = ClientGUI . FrameSplash ( self )
except :
HydrusData . Print ( ' There was an error trying to start the splash screen! ' )
HydrusData . Print ( traceback . format_exc ( ) )
raise
2014-12-03 22:56:40 +00:00
def CurrentlyIdle ( self ) :
2017-05-10 21:33:58 +00:00
if HG . force_idle_mode :
2016-01-13 22:08:19 +00:00
2016-05-11 18:16:39 +00:00
self . _idle_started = 0
2016-01-13 22:08:19 +00:00
return True
2016-02-17 22:06:47 +00:00
if not HydrusData . TimeHasPassed ( self . _timestamps [ ' boot ' ] + 120 ) :
return False
2017-10-18 19:41:25 +00:00
idle_normal = self . options [ ' idle_normal ' ]
idle_period = self . options [ ' idle_period ' ]
idle_mouse_period = self . options [ ' idle_mouse_period ' ]
2015-10-28 21:29:05 +00:00
2015-12-02 22:32:18 +00:00
if idle_normal :
2015-08-19 21:48:21 +00:00
2016-01-13 22:08:19 +00:00
currently_idle = True
2015-12-02 22:32:18 +00:00
2016-01-13 22:08:19 +00:00
if idle_period is not None :
2015-10-28 21:29:05 +00:00
2016-01-13 22:08:19 +00:00
if not HydrusData . TimeHasPassed ( self . _timestamps [ ' last_user_action ' ] + idle_period ) :
currently_idle = False
2015-10-28 21:29:05 +00:00
2016-01-13 22:08:19 +00:00
if idle_mouse_period is not None :
2015-10-28 21:29:05 +00:00
2016-01-13 22:08:19 +00:00
if not HydrusData . TimeHasPassed ( self . _timestamps [ ' last_mouse_action ' ] + idle_mouse_period ) :
currently_idle = False
2015-10-28 21:29:05 +00:00
2015-08-19 21:48:21 +00:00
2016-01-13 22:08:19 +00:00
else :
2015-10-28 21:29:05 +00:00
2016-01-13 22:08:19 +00:00
currently_idle = False
2015-10-28 21:29:05 +00:00
2016-01-13 22:08:19 +00:00
2016-05-11 18:16:39 +00:00
turning_idle = currently_idle and not self . _previously_idle
2016-01-13 22:08:19 +00:00
self . _previously_idle = currently_idle
if turning_idle :
2015-10-28 21:29:05 +00:00
2016-05-11 18:16:39 +00:00
self . _idle_started = HydrusData . GetNow ( )
2016-01-13 22:08:19 +00:00
self . pub ( ' wake_daemons ' )
2015-10-28 21:29:05 +00:00
2015-08-19 21:48:21 +00:00
2016-05-11 18:16:39 +00:00
if not currently_idle :
self . _idle_started = None
2016-01-13 22:08:19 +00:00
return currently_idle
2015-08-26 21:18:39 +00:00
2016-05-11 18:16:39 +00:00
def CurrentlyVeryIdle ( self ) :
if self . _idle_started is not None and HydrusData . TimeHasPassed ( self . _idle_started + 3600 ) :
return True
return False
2015-08-26 21:18:39 +00:00
def DoIdleShutdownWork ( self ) :
2017-10-18 19:41:25 +00:00
stop_time = HydrusData . GetNow ( ) + ( self . options [ ' idle_shutdown_max_minutes ' ] * 60 )
2015-08-26 21:18:39 +00:00
2016-05-11 18:16:39 +00:00
self . MaintainDB ( stop_time = stop_time )
2015-08-26 21:18:39 +00:00
2017-10-18 19:41:25 +00:00
if not self . options [ ' pause_repo_sync ' ] :
2015-08-19 21:48:21 +00:00
2017-06-28 20:23:21 +00:00
services = self . services_manager . GetServices ( HC . REPOSITORIES )
2015-08-26 21:18:39 +00:00
for service in services :
if HydrusData . TimeHasPassed ( stop_time ) :
return
2017-03-02 02:14:56 +00:00
service . SyncProcessUpdates ( only_when_idle = False , stop_time = stop_time )
2015-08-26 21:18:39 +00:00
2015-08-19 21:48:21 +00:00
2014-12-03 22:56:40 +00:00
2014-02-19 22:37:23 +00:00
2015-08-26 21:18:39 +00:00
def Exit ( self ) :
2017-05-10 21:33:58 +00:00
if HG . emergency_exit :
2015-09-02 23:16:09 +00:00
2016-01-06 21:17:20 +00:00
self . ShutdownView ( )
self . ShutdownModel ( )
2015-09-02 23:16:09 +00:00
2018-01-10 22:41:51 +00:00
HydrusData . CleanRunningFile ( self . db_dir , ' client ' )
2016-01-06 21:17:20 +00:00
else :
2015-09-02 23:16:09 +00:00
2016-01-06 21:17:20 +00:00
try :
2017-10-18 19:41:25 +00:00
idle_shutdown_action = self . options [ ' idle_shutdown ' ]
2016-01-06 21:17:20 +00:00
2016-01-13 22:08:19 +00:00
if idle_shutdown_action in ( CC . IDLE_ON_SHUTDOWN , CC . IDLE_ON_SHUTDOWN_ASK_FIRST ) :
2017-10-18 19:41:25 +00:00
idle_shutdown_max_minutes = self . options [ ' idle_shutdown_max_minutes ' ]
2016-08-10 19:04:08 +00:00
time_to_stop = HydrusData . GetNow ( ) + ( idle_shutdown_max_minutes * 60 )
if self . ThereIsIdleShutdownWorkDue ( time_to_stop ) :
2016-01-13 22:08:19 +00:00
if idle_shutdown_action == CC . IDLE_ON_SHUTDOWN_ASK_FIRST :
2017-07-19 21:21:41 +00:00
text = ' Is now a good time for the client to do up to ' + HydrusData . ConvertIntToPrettyString ( idle_shutdown_max_minutes ) + ' minutes \' maintenance work? (Will auto-no in 15 seconds) '
2016-01-13 22:08:19 +00:00
with ClientGUIDialogs . DialogYesNo ( self . _splash , text , title = ' Maintenance is due ' ) as dlg_yn :
2018-01-03 22:37:30 +00:00
call_later = ClientThreading . CallLater ( dlg_yn , 15 , dlg_yn . EndModal , wx . ID_NO )
2017-07-19 21:21:41 +00:00
2016-01-13 22:08:19 +00:00
if dlg_yn . ShowModal ( ) == wx . ID_YES :
2017-05-10 21:33:58 +00:00
HG . do_idle_shutdown_work = True
2016-01-13 22:08:19 +00:00
2017-07-19 21:21:41 +00:00
call_later . Stop ( )
2016-01-13 22:08:19 +00:00
else :
2017-05-10 21:33:58 +00:00
HG . do_idle_shutdown_work = True
2016-01-13 22:08:19 +00:00
2016-01-06 21:17:20 +00:00
2017-08-09 21:33:51 +00:00
self . CallToThreadLongRunning ( self . THREADExitEverything )
2016-01-06 21:17:20 +00:00
2016-01-13 22:08:19 +00:00
except :
2016-01-06 21:17:20 +00:00
2017-07-27 00:47:13 +00:00
self . _DestroySplash ( )
2016-01-06 21:17:20 +00:00
2016-01-13 22:08:19 +00:00
HydrusData . DebugPrint ( traceback . format_exc ( ) )
2017-05-10 21:33:58 +00:00
HG . emergency_exit = True
2016-01-13 22:08:19 +00:00
self . Exit ( )
2016-01-06 21:17:20 +00:00
2015-08-26 21:18:39 +00:00
2013-02-19 00:11:43 +00:00
2017-01-25 22:56:55 +00:00
def GetApp ( self ) :
return self . _app
2017-06-07 22:05:15 +00:00
def GetBandwidthManager ( self ) :
raise NotImplementedError ( )
2017-12-13 22:33:07 +00:00
def GetClipboardText ( self ) :
if wx . TheClipboard . Open ( ) :
data = wx . TextDataObject ( )
wx . TheClipboard . GetData ( data )
wx . TheClipboard . Close ( )
text = data . GetText ( )
2018-01-17 22:52:10 +00:00
text = HydrusData . ToUnicode ( text )
2017-12-13 22:33:07 +00:00
return text
else :
raise Exception ( ' I could not get permission to access the clipboard. ' )
2017-04-19 20:58:30 +00:00
def GetCommandFromShortcut ( self , shortcut_names , shortcut ) :
return self . _shortcuts_manager . GetCommand ( shortcut_names , shortcut )
2017-06-28 20:23:21 +00:00
def GetGUI ( self ) :
2017-07-19 21:21:41 +00:00
return self . gui
2017-06-28 20:23:21 +00:00
2013-10-09 18:13:42 +00:00
2015-10-14 21:02:25 +00:00
def GetNewOptions ( self ) :
2017-10-18 19:41:25 +00:00
return self . new_options
2015-10-14 21:02:25 +00:00
2016-12-14 21:19:07 +00:00
def GoodTimeToDoForegroundWork ( self ) :
2017-07-19 21:21:41 +00:00
if self . gui :
2017-06-21 21:15:59 +00:00
2017-07-19 21:21:41 +00:00
return not self . gui . CurrentlyBusy ( )
2017-06-21 21:15:59 +00:00
else :
return True
2016-12-14 21:19:07 +00:00
2017-04-05 21:16:40 +00:00
def InitClientFilesManager ( self ) :
2017-06-28 20:23:21 +00:00
self . client_files_manager = ClientCaches . ClientFilesManager ( self )
2017-04-05 21:16:40 +00:00
2017-06-28 20:23:21 +00:00
missing_locations = self . client_files_manager . GetMissing ( )
2017-04-05 21:16:40 +00:00
while len ( missing_locations ) > 0 :
2017-04-19 20:58:30 +00:00
with ClientGUITopLevelWindows . DialogManage ( None , ' repair file system ' ) as dlg :
2017-04-05 21:16:40 +00:00
panel = ClientGUIScrolledPanelsManagement . RepairFileSystemPanel ( dlg , missing_locations )
dlg . SetPanel ( panel )
if dlg . ShowModal ( ) == wx . ID_OK :
2017-06-28 20:23:21 +00:00
self . client_files_manager = ClientCaches . ClientFilesManager ( self )
2017-04-05 21:16:40 +00:00
2017-06-28 20:23:21 +00:00
missing_locations = self . client_files_manager . GetMissing ( )
2017-04-05 21:16:40 +00:00
else :
raise HydrusExceptions . PermissionException ( ' File system failed, user chose to quit. ' )
2015-09-02 23:16:09 +00:00
def InitModel ( self ) :
2014-08-06 20:29:17 +00:00
2017-03-08 23:23:12 +00:00
self . pub ( ' splash_set_title_text ' , u ' booting db \u2026 ' )
2014-08-06 20:29:17 +00:00
2015-09-02 23:16:09 +00:00
HydrusController . HydrusController . InitModel ( self )
2015-08-26 21:18:39 +00:00
2017-11-08 22:07:12 +00:00
self . pub ( ' splash_set_status_text ' , u ' initialising managers ' )
self . pub ( ' splash_set_status_subtext ' , u ' services ' )
2017-06-28 20:23:21 +00:00
self . services_manager = ClientCaches . ServicesManager ( self )
2017-05-10 21:33:58 +00:00
2017-11-08 22:07:12 +00:00
self . pub ( ' splash_set_status_subtext ' , u ' options ' )
2017-10-18 19:41:25 +00:00
self . options = self . Read ( ' options ' )
self . new_options = self . Read ( ' serialisable ' , HydrusSerialisable . SERIALISABLE_TYPE_CLIENT_OPTIONS )
2015-08-26 21:18:39 +00:00
2017-10-18 19:41:25 +00:00
HC . options = self . options
2015-10-21 21:53:10 +00:00
2017-10-18 19:41:25 +00:00
if self . new_options . GetBoolean ( ' use_system_ffmpeg ' ) :
2016-12-07 22:12:52 +00:00
if HydrusVideoHandling . FFMPEG_PATH . startswith ( HC . BIN_DIR ) :
HydrusVideoHandling . FFMPEG_PATH = os . path . basename ( HydrusVideoHandling . FFMPEG_PATH )
2017-11-08 22:07:12 +00:00
self . pub ( ' splash_set_status_subtext ' , u ' client files ' )
2017-04-05 21:16:40 +00:00
self . InitClientFilesManager ( )
2015-12-02 22:32:18 +00:00
2017-07-05 21:09:28 +00:00
#
2017-11-08 22:07:12 +00:00
self . pub ( ' splash_set_status_subtext ' , u ' network ' )
2017-07-05 21:09:28 +00:00
bandwidth_manager = self . Read ( ' serialisable ' , HydrusSerialisable . SERIALISABLE_TYPE_NETWORK_BANDWIDTH_MANAGER )
2017-08-16 21:58:06 +00:00
if bandwidth_manager is None :
bandwidth_manager = ClientNetworking . NetworkBandwidthManager ( )
ClientDefaults . SetDefaultBandwidthManagerRules ( bandwidth_manager )
2017-10-11 17:38:14 +00:00
bandwidth_manager . _dirty = True
2017-08-16 21:58:06 +00:00
wx . MessageBox ( ' Your bandwidth manager was missing on boot! I have recreated a new empty one with default rules. Please check that your hard drive and client are ok and let the hydrus dev know the details if there is a mystery. ' )
2017-07-05 21:09:28 +00:00
session_manager = self . Read ( ' serialisable ' , HydrusSerialisable . SERIALISABLE_TYPE_NETWORK_SESSION_MANAGER )
2017-08-16 21:58:06 +00:00
if session_manager is None :
session_manager = ClientNetworking . NetworkSessionManager ( )
2017-10-11 17:38:14 +00:00
session_manager . _dirty = True
2017-08-16 21:58:06 +00:00
wx . MessageBox ( ' Your session manager was missing on boot! I have recreated a new empty one. Please check that your hard drive and client are ok and let the hydrus dev know the details if there is a mystery. ' )
2017-10-11 17:38:14 +00:00
domain_manager = self . Read ( ' serialisable ' , HydrusSerialisable . SERIALISABLE_TYPE_NETWORK_DOMAIN_MANAGER )
if domain_manager is None :
2017-10-18 19:41:25 +00:00
domain_manager = ClientNetworkingDomain . NetworkDomainManager ( )
2017-10-11 17:38:14 +00:00
2018-01-10 22:41:51 +00:00
ClientDefaults . SetDefaultDomainManagerData ( domain_manager )
2017-10-11 17:38:14 +00:00
domain_manager . _dirty = True
wx . MessageBox ( ' Your domain manager was missing on boot! I have recreated a new empty one. Please check that your hard drive and client are ok and let the hydrus dev know the details if there is a mystery. ' )
2017-10-04 17:51:58 +00:00
2017-12-06 22:06:56 +00:00
domain_manager . Initialise ( )
2017-10-18 19:41:25 +00:00
login_manager = ClientNetworkingLogin . NetworkLoginManager ( )
2017-07-05 21:09:28 +00:00
2017-10-04 17:51:58 +00:00
self . network_engine = ClientNetworking . NetworkEngine ( self , bandwidth_manager , session_manager , domain_manager , login_manager )
2017-07-05 21:09:28 +00:00
2017-08-09 21:33:51 +00:00
self . CallToThreadLongRunning ( self . network_engine . MainLoop )
2017-07-05 21:09:28 +00:00
#
2017-04-19 20:58:30 +00:00
self . _shortcuts_manager = ClientCaches . ShortcutsManager ( self )
2017-12-06 22:06:56 +00:00
self . local_booru_manager = ClientCaches . LocalBooruCache ( self )
2017-11-08 22:07:12 +00:00
self . pub ( ' splash_set_status_subtext ' , u ' tag censorship ' )
2015-11-25 22:00:57 +00:00
self . _managers [ ' tag_censorship ' ] = ClientCaches . TagCensorshipManager ( self )
2017-11-08 22:07:12 +00:00
self . pub ( ' splash_set_status_subtext ' , u ' tag siblings ' )
2015-11-25 22:00:57 +00:00
self . _managers [ ' tag_siblings ' ] = ClientCaches . TagSiblingsManager ( self )
2017-11-08 22:07:12 +00:00
self . pub ( ' splash_set_status_subtext ' , u ' tag parents ' )
2015-11-25 22:00:57 +00:00
self . _managers [ ' tag_parents ' ] = ClientCaches . TagParentsManager ( self )
self . _managers [ ' undo ' ] = ClientCaches . UndoManager ( self )
2014-08-06 20:29:17 +00:00
2015-09-02 23:16:09 +00:00
def wx_code ( ) :
2016-08-17 20:07:22 +00:00
self . _caches [ ' images ' ] = ClientCaches . RenderedImageCache ( self )
2015-11-25 22:00:57 +00:00
self . _caches [ ' thumbnail ' ] = ClientCaches . ThumbnailCache ( self )
2015-09-02 23:16:09 +00:00
CC . GlobalBMPs . STATICInitialise ( )
2014-08-06 20:29:17 +00:00
2017-11-08 22:07:12 +00:00
self . pub ( ' splash_set_status_subtext ' , u ' image caches ' )
2015-09-02 23:16:09 +00:00
self . CallBlockingToWx ( wx_code )
2014-08-06 20:29:17 +00:00
2017-12-13 22:33:07 +00:00
self . sub ( self , ' ToClipboard ' , ' clipboard ' )
2015-08-26 21:18:39 +00:00
self . sub ( self , ' RestartBooru ' , ' restart_booru ' )
2014-08-06 20:29:17 +00:00
2015-09-02 23:16:09 +00:00
def InitView ( self ) :
2014-08-06 20:29:17 +00:00
2017-10-18 19:41:25 +00:00
if self . options [ ' password ' ] is not None :
2015-09-02 23:16:09 +00:00
2015-09-23 21:21:02 +00:00
self . pub ( ' splash_set_status_text ' , ' waiting for password ' )
2015-09-02 23:16:09 +00:00
def wx_code_password ( ) :
while True :
with wx . PasswordEntryDialog ( self . _splash , ' Enter your password ' , ' Enter password ' ) as dlg :
if dlg . ShowModal ( ) == wx . ID_OK :
2016-11-23 20:37:53 +00:00
# this can produce unicode with cyrillic or w/e keyboards, which hashlib can't handle
password = HydrusData . ToByteString ( dlg . GetValue ( ) )
2017-10-18 19:41:25 +00:00
if hashlib . sha256 ( password ) . digest ( ) == self . options [ ' password ' ] : break
2016-11-23 20:37:53 +00:00
else :
raise HydrusExceptions . PermissionException ( ' Bad password check ' )
2015-09-02 23:16:09 +00:00
self . CallBlockingToWx ( wx_code_password )
2017-03-08 23:23:12 +00:00
self . pub ( ' splash_set_title_text ' , u ' booting gui \u2026 ' )
2015-09-02 23:16:09 +00:00
def wx_code_gui ( ) :
2017-07-19 21:21:41 +00:00
self . gui = ClientGUI . FrameGUI ( self )
2015-09-02 23:16:09 +00:00
self . ResetIdleTimer ( )
self . CallBlockingToWx ( wx_code_gui )
2016-08-24 18:36:56 +00:00
# ShowText will now popup as a message, as popup message manager has overwritten the hooks
2015-09-02 23:16:09 +00:00
HydrusController . HydrusController . InitView ( self )
2017-03-02 02:14:56 +00:00
self . _booru_port_connection = None
2014-08-06 20:29:17 +00:00
self . RestartBooru ( )
2016-01-20 23:57:33 +00:00
if not self . _no_daemons :
self . _daemons . append ( HydrusThreading . DAEMONWorker ( self , ' CheckMouseIdle ' , ClientDaemons . DAEMONCheckMouseIdle , period = 10 ) )
2017-03-08 23:23:12 +00:00
self . _daemons . append ( HydrusThreading . DAEMONWorker ( self , ' SynchroniseAccounts ' , ClientDaemons . DAEMONSynchroniseAccounts , ( ' notify_unknown_accounts ' , ) ) )
2017-03-02 02:14:56 +00:00
self . _daemons . append ( HydrusThreading . DAEMONWorker ( self , ' SaveDirtyObjects ' , ClientDaemons . DAEMONSaveDirtyObjects , ( ' important_dirt_to_clean ' , ) , period = 30 ) )
2016-12-14 21:19:07 +00:00
self . _daemons . append ( HydrusThreading . DAEMONForegroundWorker ( self , ' DownloadFiles ' , ClientDaemons . DAEMONDownloadFiles , ( ' notify_new_downloads ' , ' notify_new_permissions ' ) ) )
self . _daemons . append ( HydrusThreading . DAEMONForegroundWorker ( self , ' SynchroniseSubscriptions ' , ClientDaemons . DAEMONSynchroniseSubscriptions , ( ' notify_restart_subs_sync_daemon ' , ' notify_new_subscriptions ' ) , init_wait = 60 , pre_call_wait = 3 ) )
self . _daemons . append ( HydrusThreading . DAEMONForegroundWorker ( self , ' CheckImportFolders ' , ClientDaemons . DAEMONCheckImportFolders , ( ' notify_restart_import_folders_daemon ' , ' notify_new_import_folders ' ) , period = 180 ) )
self . _daemons . append ( HydrusThreading . DAEMONForegroundWorker ( self , ' CheckExportFolders ' , ClientDaemons . DAEMONCheckExportFolders , ( ' notify_restart_export_folders_daemon ' , ' notify_new_export_folders ' ) , period = 180 ) )
2017-01-11 22:31:30 +00:00
self . _daemons . append ( HydrusThreading . DAEMONForegroundWorker ( self , ' MaintainTrash ' , ClientDaemons . DAEMONMaintainTrash , init_wait = 120 ) )
2017-03-02 02:14:56 +00:00
self . _daemons . append ( HydrusThreading . DAEMONForegroundWorker ( self , ' SynchroniseRepositories ' , ClientDaemons . DAEMONSynchroniseRepositories , ( ' notify_restart_repo_sync_daemon ' , ' notify_new_permissions ' ) , period = 4 * 3600 , pre_call_wait = 1 ) )
2016-12-14 21:19:07 +00:00
self . _daemons . append ( HydrusThreading . DAEMONBackgroundWorker ( self , ' UPnP ' , ClientDaemons . DAEMONUPnP , ( ' notify_new_upnp_mappings ' , ) , init_wait = 120 , pre_call_wait = 6 ) )
2016-01-20 23:57:33 +00:00
2015-09-02 23:16:09 +00:00
2017-07-12 20:03:45 +00:00
if self . db . IsFirstStart ( ) :
2016-08-24 18:36:56 +00:00
message = ' Hi, this looks like the first time you have started the hydrus client. '
message + = os . linesep * 2
message + = ' Don \' t forget to check out the help if you haven \' t already. '
message + = os . linesep * 2
2016-10-19 20:02:56 +00:00
message + = ' To dismiss popup messages like this, right-click them. '
2016-08-24 18:36:56 +00:00
HydrusData . ShowText ( message )
2017-07-12 20:03:45 +00:00
if self . db . IsDBUpdated ( ) :
2016-08-24 18:36:56 +00:00
HydrusData . ShowText ( ' The client has updated to version ' + str ( HC . SOFTWARE_VERSION ) + ' ! ' )
2015-06-03 21:05:13 +00:00
2017-07-12 20:03:45 +00:00
for message in self . db . GetInitialMessages ( ) :
2016-12-21 22:30:54 +00:00
HydrusData . ShowText ( message )
2014-08-06 20:29:17 +00:00
2017-05-10 21:33:58 +00:00
def IsBooted ( self ) :
return self . _is_booted
2016-08-31 19:55:14 +00:00
def LastShutdownWasBad ( self ) :
return self . _last_shutdown_was_bad
2016-05-11 18:16:39 +00:00
def MaintainDB ( self , stop_time = None ) :
2013-02-19 00:11:43 +00:00
2017-10-18 19:41:25 +00:00
if self . new_options . GetBoolean ( ' maintain_similar_files_duplicate_pairs_during_idle ' ) :
2017-01-25 22:56:55 +00:00
phashes_stop_time = stop_time
if phashes_stop_time is None :
phashes_stop_time = HydrusData . GetNow ( ) + 15
self . WriteInterruptable ( ' maintain_similar_files_phashes ' , stop_time = phashes_stop_time )
tree_stop_time = stop_time
if tree_stop_time is None :
tree_stop_time = HydrusData . GetNow ( ) + 30
self . WriteInterruptable ( ' maintain_similar_files_tree ' , stop_time = tree_stop_time , abandon_if_other_work_to_do = True )
2017-10-18 19:41:25 +00:00
search_distance = self . new_options . GetInteger ( ' similar_files_duplicate_pairs_search_distance ' )
2017-01-25 22:56:55 +00:00
search_stop_time = stop_time
if search_stop_time is None :
search_stop_time = HydrusData . GetNow ( ) + 60
self . WriteInterruptable ( ' maintain_similar_files_duplicate_pairs ' , search_distance , stop_time = search_stop_time , abandon_if_other_work_to_do = True )
2016-01-06 21:17:20 +00:00
2017-03-29 19:39:34 +00:00
if stop_time is None or not HydrusData . TimeHasPassed ( stop_time ) :
self . WriteInterruptable ( ' vacuum ' , stop_time = stop_time )
2016-01-06 21:17:20 +00:00
2017-03-29 19:39:34 +00:00
if stop_time is None or not HydrusData . TimeHasPassed ( stop_time ) :
self . WriteInterruptable ( ' analyze ' , stop_time = stop_time )
2015-08-26 21:18:39 +00:00
2016-05-11 18:16:39 +00:00
if stop_time is None or not HydrusData . TimeHasPassed ( stop_time ) :
2015-08-26 21:18:39 +00:00
2016-05-11 18:16:39 +00:00
if HydrusData . TimeHasPassed ( self . _timestamps [ ' last_service_info_cache_fatten ' ] + ( 60 * 20 ) ) :
2014-12-17 22:35:12 +00:00
2016-05-11 18:16:39 +00:00
self . pub ( ' splash_set_status_text ' , ' fattening service info ' )
2017-06-28 20:23:21 +00:00
services = self . services_manager . GetServices ( )
2016-05-11 18:16:39 +00:00
for service in services :
2017-11-08 22:07:12 +00:00
self . pub ( ' splash_set_status_subtext ' , service . GetName ( ) )
2016-05-11 18:16:39 +00:00
try : self . Read ( ' service_info ' , service . GetServiceKey ( ) )
except : pass # sometimes this breaks when a service has just been removed and the client is closing, so ignore the error
self . _timestamps [ ' last_service_info_cache_fatten ' ] = HydrusData . GetNow ( )
2014-12-17 22:35:12 +00:00
2014-08-06 20:29:17 +00:00
2015-08-26 21:18:39 +00:00
2017-07-05 21:09:28 +00:00
def MaintainMemorySlow ( self ) :
2015-08-26 21:18:39 +00:00
2017-07-05 21:09:28 +00:00
HydrusController . HydrusController . MaintainMemorySlow ( self )
2015-08-26 21:18:39 +00:00
2017-05-10 21:33:58 +00:00
if HydrusData . TimeHasPassed ( self . _timestamps [ ' last_page_change ' ] + 30 * 60 ) :
2018-01-10 22:41:51 +00:00
self . pub ( ' delete_old_closed_pages ' )
2015-08-26 21:18:39 +00:00
self . _timestamps [ ' last_page_change ' ] = HydrusData . GetNow ( )
2017-10-18 19:41:25 +00:00
disk_cache_maintenance_mb = self . new_options . GetNoneableInteger ( ' disk_cache_maintenance_mb ' )
2017-05-10 21:33:58 +00:00
if disk_cache_maintenance_mb is not None :
2015-08-26 21:18:39 +00:00
2017-05-10 21:33:58 +00:00
if self . CurrentlyVeryIdle ( ) :
2017-09-06 20:18:20 +00:00
cache_period = 3600
disk_cache_stop_time = HydrusData . GetNow ( ) + 30
2017-05-10 21:33:58 +00:00
elif self . CurrentlyIdle ( ) :
2017-09-06 20:18:20 +00:00
cache_period = 1800
disk_cache_stop_time = HydrusData . GetNow ( ) + 10
2017-05-10 21:33:58 +00:00
else :
2017-09-06 20:18:20 +00:00
cache_period = 240
disk_cache_stop_time = HydrusData . GetNow ( ) + 2
2017-05-10 21:33:58 +00:00
2015-08-26 21:18:39 +00:00
2017-09-06 20:18:20 +00:00
if HydrusData . TimeHasPassed ( self . _timestamps [ ' last_disk_cache_population ' ] + cache_period ) :
self . Read ( ' load_into_disk_cache ' , stop_time = disk_cache_stop_time , caller_limit = disk_cache_maintenance_mb * 1024 * 1024 )
self . _timestamps [ ' last_disk_cache_population ' ] = HydrusData . GetNow ( )
2015-08-26 21:18:39 +00:00
2015-11-25 22:00:57 +00:00
def MenuIsOpen ( self ) :
return self . _menu_open
2017-05-03 21:33:48 +00:00
def PageCompletelyDestroyed ( self , page_key ) :
2015-09-16 18:11:00 +00:00
2017-07-19 21:21:41 +00:00
if self . gui :
2016-02-17 22:06:47 +00:00
2017-07-19 21:21:41 +00:00
return self . gui . PageCompletelyDestroyed ( page_key )
2016-02-17 22:06:47 +00:00
2017-06-21 21:15:59 +00:00
else :
2016-02-17 22:06:47 +00:00
return True
2015-09-16 18:11:00 +00:00
2017-05-03 21:33:48 +00:00
def PageClosedButNotDestroyed ( self , page_key ) :
2015-10-07 21:56:22 +00:00
2017-07-19 21:21:41 +00:00
if self . gui :
2017-06-21 21:15:59 +00:00
2017-07-19 21:21:41 +00:00
return self . gui . PageClosedButNotDestroyed ( page_key )
2017-06-21 21:15:59 +00:00
else :
return False
2015-10-07 21:56:22 +00:00
2015-11-25 22:00:57 +00:00
def PopupMenu ( self , window , menu ) :
if menu . GetMenuItemCount ( ) > 0 :
self . _menu_open = True
window . PopupMenu ( menu )
self . _menu_open = False
2016-11-02 21:09:14 +00:00
ClientGUIMenus . DestroyMenu ( menu )
2015-11-25 22:00:57 +00:00
2013-02-19 00:11:43 +00:00
def PrepStringForDisplay ( self , text ) :
2017-08-02 21:32:54 +00:00
return text . lower ( )
2017-07-27 00:47:13 +00:00
def ProcessPubSub ( self ) :
self . CallBlockingToWx ( self . _pubsub . Process )
2013-02-19 00:11:43 +00:00
2017-03-02 02:14:56 +00:00
def RefreshServices ( self ) :
2017-06-28 20:23:21 +00:00
self . services_manager . RefreshServices ( )
2017-03-02 02:14:56 +00:00
2015-10-28 21:29:05 +00:00
def ResetIdleTimer ( self ) :
self . _timestamps [ ' last_user_action ' ] = HydrusData . GetNow ( )
2014-08-13 22:18:12 +00:00
2015-08-26 21:18:39 +00:00
def ResetPageChangeTimer ( self ) :
self . _timestamps [ ' last_page_change ' ] = HydrusData . GetNow ( )
2014-07-09 22:15:14 +00:00
def RestartBooru ( self ) :
2017-06-28 20:23:21 +00:00
service = self . services_manager . GetService ( CC . LOCAL_BOORU_SERVICE_KEY )
2014-07-09 22:15:14 +00:00
2017-03-02 02:14:56 +00:00
port = service . GetPort ( )
2014-07-09 22:15:14 +00:00
def TWISTEDRestartServer ( ) :
def StartServer ( * args , * * kwargs ) :
try :
try :
2015-06-10 19:40:25 +00:00
connection = HydrusNetworking . GetLocalConnection ( port )
2014-07-09 22:15:14 +00:00
connection . close ( )
2015-11-04 22:30:28 +00:00
text = ' The client \' s booru server could not start because something was already bound to port ' + str ( port ) + ' . '
2014-07-09 22:15:14 +00:00
text + = os . linesep * 2
text + = ' This usually means another hydrus client is already running and occupying that port. It could be a previous instantiation of this client that has yet to shut itself down. '
text + = os . linesep * 2
text + = ' You can change the port this client tries to host its local server on in services->manage services. '
2016-08-24 18:36:56 +00:00
HydrusData . ShowText ( text )
2014-07-09 22:15:14 +00:00
except :
2016-04-06 19:52:45 +00:00
import ClientLocalServer
2017-03-02 02:14:56 +00:00
self . _booru_port_connection = reactor . listenTCP ( port , ClientLocalServer . HydrusServiceBooru ( service ) )
2014-07-09 22:15:14 +00:00
try :
2015-06-10 19:40:25 +00:00
connection = HydrusNetworking . GetLocalConnection ( port )
2014-07-09 22:15:14 +00:00
connection . close ( )
2015-06-10 19:40:25 +00:00
except Exception as e :
2014-07-09 22:15:14 +00:00
2015-11-04 22:30:28 +00:00
text = ' Tried to bind port ' + str ( port ) + ' for the local booru, but it failed: '
2015-06-10 19:40:25 +00:00
text + = os . linesep * 2
2015-11-04 22:30:28 +00:00
text + = HydrusData . ToUnicode ( e )
2014-07-09 22:15:14 +00:00
2016-08-24 18:36:56 +00:00
HydrusData . ShowText ( text )
2014-07-09 22:15:14 +00:00
2015-06-03 21:05:13 +00:00
except Exception as e :
wx . CallAfter ( HydrusData . ShowException , e )
2014-07-09 22:15:14 +00:00
2017-03-02 02:14:56 +00:00
if self . _booru_port_connection is None :
2016-02-17 22:06:47 +00:00
2016-03-16 22:19:14 +00:00
if port is not None :
StartServer ( )
2016-02-17 22:06:47 +00:00
2013-09-25 20:20:10 +00:00
else :
2017-03-02 02:14:56 +00:00
deferred = defer . maybeDeferred ( self . _booru_port_connection . stopListening )
2013-09-25 20:20:10 +00:00
2016-02-17 22:06:47 +00:00
if port is not None :
deferred . addCallback ( StartServer )
2013-09-25 20:20:10 +00:00
2017-10-25 21:45:15 +00:00
if HG . twisted_is_broke :
HydrusData . ShowText ( ' Twisted failed to import, so could not restart the booru! Please contact hydrus dev! ' )
else :
reactor . callFromThread ( TWISTEDRestartServer )
2013-09-25 20:20:10 +00:00
2014-01-08 18:40:02 +00:00
def RestoreDatabase ( self ) :
2017-07-12 20:03:45 +00:00
restore_intro = ' '
2017-07-19 21:21:41 +00:00
with wx . DirDialog ( self . gui , ' Select backup location. ' ) as dlg :
2014-01-08 18:40:02 +00:00
if dlg . ShowModal ( ) == wx . ID_OK :
2015-11-04 22:30:28 +00:00
path = HydrusData . ToUnicode ( dlg . GetPath ( ) )
2014-01-08 18:40:02 +00:00
2014-12-10 22:02:39 +00:00
text = ' Are you sure you want to restore a backup from " ' + path + ' " ? '
text + = os . linesep * 2
text + = ' Everything in your current database will be deleted! '
text + = os . linesep * 2
text + = ' The gui will shut down, and then it will take a while to complete the restore. Once it is done, the client will restart. '
2014-01-08 18:40:02 +00:00
2017-07-19 21:21:41 +00:00
with ClientGUIDialogs . DialogYesNo ( self . gui , text ) as dlg_yn :
2014-01-08 18:40:02 +00:00
if dlg_yn . ShowModal ( ) == wx . ID_YES :
2015-06-17 20:01:41 +00:00
def THREADRestart ( ) :
2017-07-19 21:21:41 +00:00
wx . CallAfter ( self . gui . Exit )
2015-06-17 20:01:41 +00:00
2017-07-12 20:03:45 +00:00
while not self . db . LoopIsFinished ( ) :
2015-12-16 22:41:06 +00:00
time . sleep ( 0.1 )
2015-06-17 20:01:41 +00:00
2017-07-12 20:03:45 +00:00
self . db . RestoreBackup ( path )
2015-06-17 20:01:41 +00:00
2017-05-10 21:33:58 +00:00
while not HG . shutdown_complete :
2015-12-16 22:41:06 +00:00
time . sleep ( 0.1 )
2015-06-17 20:01:41 +00:00
2015-12-16 22:41:06 +00:00
HydrusData . RestartProcess ( )
2015-06-17 20:01:41 +00:00
2014-01-08 18:40:02 +00:00
2017-08-09 21:33:51 +00:00
self . CallToThreadLongRunning ( THREADRestart )
2015-09-02 23:16:09 +00:00
def Run ( self ) :
self . _app = wx . App ( )
2016-09-07 20:01:05 +00:00
self . _app . locale = wx . Locale ( wx . LANGUAGE_DEFAULT ) # Very important to init this here and keep it non garbage collected
2016-08-24 18:36:56 +00:00
2018-01-24 23:09:42 +00:00
# do not import locale here and try anything clever--assume that bad locale formatting is due to OS-level mess-up, not mine
# wx locale is supposed to set it all up nice, so if someone's doesn't, explore that and find the external solution
2018-01-10 22:41:51 +00:00
2017-03-08 23:23:12 +00:00
HydrusData . Print ( u ' booting controller \u2026 ' )
2015-11-18 22:44:07 +00:00
2017-11-22 21:03:07 +00:00
self . frame_icon = wx . Icon ( os . path . join ( HC . STATIC_DIR , ' hydrus_32_non-transparent.png ' ) , wx . BITMAP_TYPE_PNG )
2018-01-10 22:41:51 +00:00
self . CreateSplash ( )
2015-09-02 23:16:09 +00:00
2017-08-09 21:33:51 +00:00
self . CallToThreadLongRunning ( self . THREADBootEverything )
2015-09-02 23:16:09 +00:00
self . _app . MainLoop ( )
2017-03-08 23:23:12 +00:00
HydrusData . Print ( u ' shutting down controller \u2026 ' )
2015-09-02 23:16:09 +00:00
2017-03-02 02:14:56 +00:00
def SaveDirtyObjects ( self ) :
2017-05-10 21:33:58 +00:00
with HG . dirty_object_lock :
2017-03-02 02:14:56 +00:00
2017-06-28 20:23:21 +00:00
dirty_services = [ service for service in self . services_manager . GetServices ( ) if service . IsDirty ( ) ]
2017-03-02 02:14:56 +00:00
if len ( dirty_services ) > 0 :
self . WriteSynchronous ( ' dirty_services ' , dirty_services )
2017-07-05 21:09:28 +00:00
if self . network_engine . bandwidth_manager . IsDirty ( ) :
self . WriteSynchronous ( ' serialisable ' , self . network_engine . bandwidth_manager )
self . network_engine . bandwidth_manager . SetClean ( )
2017-10-11 17:38:14 +00:00
if self . network_engine . domain_manager . IsDirty ( ) :
self . WriteSynchronous ( ' serialisable ' , self . network_engine . domain_manager )
self . network_engine . domain_manager . SetClean ( )
2017-07-05 21:09:28 +00:00
if self . network_engine . session_manager . IsDirty ( ) :
self . WriteSynchronous ( ' serialisable ' , self . network_engine . session_manager )
self . network_engine . session_manager . SetClean ( )
2017-03-02 02:14:56 +00:00
def SetServices ( self , services ) :
2017-05-10 21:33:58 +00:00
with HG . dirty_object_lock :
2017-03-02 02:14:56 +00:00
self . WriteSynchronous ( ' update_services ' , services )
2017-06-28 20:23:21 +00:00
self . services_manager . RefreshServices ( )
2017-03-02 02:14:56 +00:00
2017-07-05 21:09:28 +00:00
def ShutdownModel ( self ) :
if not HG . emergency_exit :
self . SaveDirtyObjects ( )
HydrusController . HydrusController . ShutdownModel ( self )
2015-09-02 23:16:09 +00:00
def ShutdownView ( self ) :
2017-05-10 21:33:58 +00:00
if not HG . emergency_exit :
2015-09-02 23:16:09 +00:00
2016-01-06 21:17:20 +00:00
self . pub ( ' splash_set_status_text ' , ' waiting for daemons to exit ' )
2015-09-02 23:16:09 +00:00
2016-01-06 21:17:20 +00:00
self . _ShutdownDaemons ( )
2017-05-10 21:33:58 +00:00
if HG . do_idle_shutdown_work :
2015-09-02 23:16:09 +00:00
2017-03-29 19:39:34 +00:00
try :
self . DoIdleShutdownWork ( )
except :
ClientData . ReportShutdownException ( )
2015-09-02 23:16:09 +00:00
2016-01-13 22:08:19 +00:00
HydrusController . HydrusController . ShutdownView ( self )
2015-09-16 18:11:00 +00:00
2014-01-08 18:40:02 +00:00
2017-06-14 21:19:11 +00:00
def StartFileQuery ( self , page_key , job_key , search_context ) :
2015-08-26 21:18:39 +00:00
2017-06-14 21:19:11 +00:00
self . CallToThread ( self . THREADDoFileQuery , page_key , job_key , search_context )
2015-08-26 21:18:39 +00:00
def SystemBusy ( self ) :
2016-01-13 22:08:19 +00:00
2017-05-10 21:33:58 +00:00
if HG . force_idle_mode :
2016-01-13 22:08:19 +00:00
return False
2017-10-18 19:41:25 +00:00
max_cpu = self . options [ ' idle_cpu_max ' ]
2015-08-26 21:18:39 +00:00
2015-12-02 22:32:18 +00:00
if max_cpu is None :
2015-08-26 21:18:39 +00:00
2015-12-02 22:32:18 +00:00
self . _system_busy = False
2015-08-26 21:18:39 +00:00
2015-12-02 22:32:18 +00:00
else :
2015-08-26 21:18:39 +00:00
2015-12-02 22:32:18 +00:00
if HydrusData . TimeHasPassed ( self . _timestamps [ ' last_cpu_check ' ] + 60 ) :
2015-08-26 21:18:39 +00:00
2015-12-02 22:32:18 +00:00
cpu_times = psutil . cpu_percent ( percpu = True )
2015-08-26 21:18:39 +00:00
2015-12-02 22:32:18 +00:00
if True in ( cpu_time > max_cpu for cpu_time in cpu_times ) :
self . _system_busy = True
else :
self . _system_busy = False
2015-08-26 21:18:39 +00:00
2015-12-02 22:32:18 +00:00
self . _timestamps [ ' last_cpu_check ' ] = HydrusData . GetNow ( )
2015-08-26 21:18:39 +00:00
return self . _system_busy
2013-08-28 21:31:52 +00:00
2016-08-10 19:04:08 +00:00
def ThereIsIdleShutdownWorkDue ( self , time_to_stop ) :
2015-08-26 21:18:39 +00:00
2016-08-10 19:04:08 +00:00
maintenance_due = self . Read ( ' maintenance_due ' , time_to_stop )
2015-05-20 21:31:40 +00:00
2016-05-11 18:16:39 +00:00
if maintenance_due :
2015-08-26 21:18:39 +00:00
2016-05-11 18:16:39 +00:00
return True
2015-08-26 21:18:39 +00:00
2017-06-28 20:23:21 +00:00
services = self . services_manager . GetServices ( HC . REPOSITORIES )
2017-03-02 02:14:56 +00:00
for service in services :
2015-08-26 21:18:39 +00:00
2017-03-02 02:14:56 +00:00
if service . CanDoIdleShutdownWork ( ) :
2015-08-26 21:18:39 +00:00
2017-03-02 02:14:56 +00:00
return True
2015-08-26 21:18:39 +00:00
2015-05-20 21:31:40 +00:00
2015-08-26 21:18:39 +00:00
return False
2015-05-20 21:31:40 +00:00
2017-06-14 21:19:11 +00:00
def THREADDoFileQuery ( self , page_key , job_key , search_context ) :
2013-08-28 21:31:52 +00:00
2017-02-08 22:27:00 +00:00
QUERY_CHUNK_SIZE = 256
2015-03-04 22:44:32 +00:00
query_hash_ids = self . Read ( ' file_query_ids ' , search_context )
media_results = [ ]
2017-02-08 22:27:00 +00:00
for sub_query_hash_ids in HydrusData . SplitListIntoChunks ( query_hash_ids , QUERY_CHUNK_SIZE ) :
2013-08-28 21:31:52 +00:00
2017-06-14 21:19:11 +00:00
if job_key . IsCancelled ( ) :
return
2013-08-28 21:31:52 +00:00
2016-05-04 21:50:55 +00:00
more_media_results = self . Read ( ' media_results_from_ids ' , sub_query_hash_ids )
2013-08-28 21:31:52 +00:00
2015-03-04 22:44:32 +00:00
media_results . extend ( more_media_results )
2013-08-28 21:31:52 +00:00
2017-06-14 21:19:11 +00:00
self . pub ( ' set_num_query_results ' , page_key , len ( media_results ) , len ( query_hash_ids ) )
2013-08-28 21:31:52 +00:00
2017-10-04 17:51:58 +00:00
self . WaitUntilViewFree ( )
2013-08-28 21:31:52 +00:00
2015-03-04 22:44:32 +00:00
2015-06-24 22:10:14 +00:00
search_context . SetComplete ( )
2017-06-14 21:19:11 +00:00
self . pub ( ' file_query_done ' , page_key , job_key , media_results )
2013-08-28 21:31:52 +00:00
2015-04-08 18:10:50 +00:00
def THREADBootEverything ( self ) :
try :
2015-09-02 23:16:09 +00:00
self . CheckAlreadyRunning ( )
2015-04-08 18:10:50 +00:00
2017-07-12 20:03:45 +00:00
self . _last_shutdown_was_bad = HydrusData . LastShutdownWasBad ( self . db_dir , ' client ' )
2016-08-31 19:55:14 +00:00
2017-07-12 20:03:45 +00:00
HydrusData . RecordRunningStart ( self . db_dir , ' client ' )
2015-04-08 18:10:50 +00:00
2015-09-02 23:16:09 +00:00
self . InitModel ( )
2015-05-13 20:22:39 +00:00
2015-09-02 23:16:09 +00:00
self . InitView ( )
2015-04-29 19:20:35 +00:00
2017-05-10 21:33:58 +00:00
self . _is_booted = True
2015-09-02 23:16:09 +00:00
except HydrusExceptions . PermissionException as e :
2015-04-08 18:10:50 +00:00
2015-11-18 22:44:07 +00:00
HydrusData . Print ( e )
2015-04-08 18:10:50 +00:00
2017-05-10 21:33:58 +00:00
HG . emergency_exit = True
2015-04-08 18:10:50 +00:00
2016-01-13 22:08:19 +00:00
self . Exit ( )
2015-04-08 18:10:50 +00:00
2016-01-13 22:08:19 +00:00
except Exception as e :
text = ' A serious error occured while trying to start the program. Its traceback will be shown next. It should have also been written to client.log. '
2015-09-02 23:16:09 +00:00
HydrusData . DebugPrint ( text )
2015-04-08 18:10:50 +00:00
2016-10-19 20:02:56 +00:00
HydrusData . DebugPrint ( traceback . format_exc ( ) )
2016-01-13 22:08:19 +00:00
2015-06-24 22:10:14 +00:00
wx . CallAfter ( wx . MessageBox , traceback . format_exc ( ) )
2016-01-13 22:08:19 +00:00
wx . CallAfter ( wx . MessageBox , text )
2017-05-10 21:33:58 +00:00
HG . emergency_exit = True
2016-01-13 22:08:19 +00:00
self . Exit ( )
2015-04-08 18:10:50 +00:00
finally :
2017-07-27 00:47:13 +00:00
self . _DestroySplash ( )
2015-04-08 18:10:50 +00:00
def THREADExitEverything ( self ) :
2015-06-03 21:05:13 +00:00
2015-04-08 18:10:50 +00:00
try :
2017-03-08 23:23:12 +00:00
self . pub ( ' splash_set_title_text ' , u ' shutting down gui \u2026 ' )
2015-09-23 21:21:02 +00:00
2015-09-02 23:16:09 +00:00
self . ShutdownView ( )
2015-04-08 18:10:50 +00:00
2017-03-08 23:23:12 +00:00
self . pub ( ' splash_set_title_text ' , u ' shutting down db \u2026 ' )
2015-09-23 21:21:02 +00:00
2015-09-02 23:16:09 +00:00
self . ShutdownModel ( )
2015-04-08 18:10:50 +00:00
2017-08-09 21:33:51 +00:00
self . pub ( ' splash_set_title_text ' , u ' cleaning up \u2026 ' )
self . pub ( ' splash_set_status_text ' , u ' ' )
2017-07-12 20:03:45 +00:00
HydrusData . CleanRunningFile ( self . db_dir , ' client ' )
2016-06-15 18:59:44 +00:00
2017-07-27 00:47:13 +00:00
except HydrusExceptions . PermissionException :
pass
except HydrusExceptions . ShutdownException :
pass
2015-04-08 18:10:50 +00:00
except :
2017-03-29 19:39:34 +00:00
ClientData . ReportShutdownException ( )
2015-04-08 18:10:50 +00:00
finally :
2017-07-27 00:47:13 +00:00
self . _DestroySplash ( )
2015-04-08 18:10:50 +00:00
2017-12-13 22:33:07 +00:00
def ToClipboard ( self , data_type , data ) :
# need this cause can't do it in a non-gui thread
if data_type == ' paths ' :
paths = data
if wx . TheClipboard . Open ( ) :
data = wx . DataObjectComposite ( )
file_data = wx . FileDataObject ( )
for path in paths : file_data . AddFile ( path )
text_data = wx . TextDataObject ( os . linesep . join ( paths ) )
data . Add ( file_data , True )
data . Add ( text_data , False )
wx . TheClipboard . SetData ( data )
wx . TheClipboard . Close ( )
else : wx . MessageBox ( ' Could not get permission to access the clipboard! ' )
elif data_type == ' text ' :
text = data
if wx . TheClipboard . Open ( ) :
data = wx . TextDataObject ( text )
wx . TheClipboard . SetData ( data )
wx . TheClipboard . Close ( )
else : wx . MessageBox ( ' I could not get permission to access the clipboard. ' )
elif data_type == ' bmp ' :
media = data
image_renderer = self . GetCache ( ' images ' ) . GetImageRenderer ( media )
def CopyToClipboard ( ) :
if wx . TheClipboard . Open ( ) :
wx_bmp = image_renderer . GetWXBitmap ( )
data = wx . BitmapDataObject ( wx_bmp )
wx . TheClipboard . SetData ( data )
wx . TheClipboard . Close ( )
else :
wx . MessageBox ( ' I could not get permission to access the clipboard. ' )
def THREADWait ( ) :
# have to do this in thread, because the image needs the wx event queue to render
start_time = time . time ( )
while not image_renderer . IsReady ( ) :
if HydrusData . TimeHasPassed ( start_time + 15 ) :
raise Exception ( ' The image did not render in fifteen seconds, so the attempt to copy it to the clipboard was abandoned. ' )
time . sleep ( 0.1 )
wx . CallAfter ( CopyToClipboard )
self . CallToThread ( THREADWait )
2017-10-04 17:51:58 +00:00
def WaitUntilViewFree ( self ) :
self . WaitUntilModelFree ( )
self . WaitUntilThumbnailsFree ( )
def WaitUntilThumbnailsFree ( self ) :
while True :
if self . _view_shutdown :
raise HydrusExceptions . ShutdownException ( ' Application shutting down! ' )
elif not self . _caches [ ' thumbnail ' ] . DoingWork ( ) :
return
else :
time . sleep ( 0.00001 )
2013-02-19 00:11:43 +00:00
def Write ( self , action , * args , * * kwargs ) :
2017-12-06 22:06:56 +00:00
if action == ' content_updates ' :
self . _managers [ ' undo ' ] . AddCommand ( ' content_updates ' , * args , * * kwargs )
2013-07-10 20:25:57 +00:00
2015-04-01 20:44:54 +00:00
return HydrusController . HydrusController . Write ( self , action , * args , * * kwargs )
2013-07-31 21:26:38 +00:00
2016-12-07 22:12:52 +00:00