1974 lines
59 KiB
Python
1974 lines
59 KiB
Python
from . import ClientConstants as CC
|
|
from . import ClientDownloading
|
|
from . import ClientImporting
|
|
from . import ClientNetworkingContexts
|
|
from . import ClientNetworkingJobs
|
|
from . import ClientRatings
|
|
from . import ClientThreading
|
|
import hashlib
|
|
from . import HydrusConstants as HC
|
|
from . import HydrusData
|
|
from . import HydrusExceptions
|
|
from . import HydrusGlobals as HG
|
|
from . import HydrusNetwork
|
|
from . import HydrusNetworking
|
|
from . import HydrusSerialisable
|
|
import json
|
|
import os
|
|
import threading
|
|
import time
|
|
import traceback
|
|
import wx
|
|
|
|
def GenerateDefaultServiceDictionary( service_type ):
|
|
|
|
dictionary = HydrusSerialisable.SerialisableDictionary()
|
|
|
|
if service_type in HC.REMOTE_SERVICES:
|
|
|
|
dictionary[ 'credentials' ] = HydrusNetwork.Credentials( 'hostname', 45871 )
|
|
dictionary[ 'no_requests_reason' ] = ''
|
|
dictionary[ 'no_requests_until' ] = 0
|
|
|
|
if service_type in HC.RESTRICTED_SERVICES:
|
|
|
|
dictionary[ 'account' ] = HydrusNetwork.Account.GenerateSerialisableTupleFromAccount( HydrusNetwork.Account.GenerateUnknownAccount() )
|
|
dictionary[ 'next_account_sync' ] = 0
|
|
|
|
if service_type in HC.REPOSITORIES:
|
|
|
|
dictionary[ 'metadata' ] = HydrusNetwork.Metadata()
|
|
dictionary[ 'paused' ] = False
|
|
dictionary[ 'tag_archive_sync' ] = []
|
|
|
|
|
|
|
|
if service_type == HC.IPFS:
|
|
|
|
dictionary[ 'credentials' ] = HydrusNetwork.Credentials( '127.0.0.1', 5001 )
|
|
dictionary[ 'multihash_prefix' ] = ''
|
|
|
|
|
|
|
|
if service_type == HC.LOCAL_TAG:
|
|
|
|
dictionary[ 'tag_archive_sync' ] = []
|
|
|
|
|
|
if service_type == HC.LOCAL_BOORU:
|
|
|
|
dictionary[ 'port' ] = None
|
|
dictionary[ 'upnp_port' ] = None
|
|
dictionary[ 'bandwidth_tracker' ] = HydrusNetworking.BandwidthTracker()
|
|
dictionary[ 'bandwidth_rules' ] = HydrusNetworking.BandwidthRules()
|
|
|
|
|
|
if service_type in HC.RATINGS_SERVICES:
|
|
|
|
dictionary[ 'shape' ] = ClientRatings.CIRCLE
|
|
dictionary[ 'colours' ] = []
|
|
|
|
if service_type == HC.LOCAL_RATING_LIKE:
|
|
|
|
dictionary[ 'colours' ] = list(ClientRatings.default_like_colours.items())
|
|
|
|
elif service_type == HC.LOCAL_RATING_NUMERICAL:
|
|
|
|
dictionary[ 'colours' ] = list(ClientRatings.default_numerical_colours.items())
|
|
dictionary[ 'num_stars' ] = 5
|
|
dictionary[ 'allow_zero' ]= True
|
|
|
|
|
|
|
|
return dictionary
|
|
|
|
def GenerateService( service_key, service_type, name, dictionary = None ):
|
|
|
|
if dictionary is None:
|
|
|
|
dictionary = GenerateDefaultServiceDictionary( service_type )
|
|
|
|
|
|
if service_type == HC.LOCAL_TAG:
|
|
|
|
cl = ServiceLocalTag
|
|
|
|
elif service_type == HC.LOCAL_RATING_LIKE:
|
|
|
|
cl = ServiceLocalRatingLike
|
|
|
|
elif service_type == HC.LOCAL_RATING_NUMERICAL:
|
|
|
|
cl = ServiceLocalRatingNumerical
|
|
|
|
elif service_type in HC.REPOSITORIES:
|
|
|
|
cl = ServiceRepository
|
|
|
|
elif service_type in HC.RESTRICTED_SERVICES:
|
|
|
|
cl = ServiceRestricted
|
|
|
|
elif service_type == HC.IPFS:
|
|
|
|
cl = ServiceIPFS
|
|
|
|
elif service_type in HC.REMOTE_SERVICES:
|
|
|
|
cl = ServiceRemote
|
|
|
|
elif service_type == HC.LOCAL_BOORU:
|
|
|
|
cl = ServiceLocalBooru
|
|
|
|
else:
|
|
|
|
cl = Service
|
|
|
|
|
|
return cl( service_key, service_type, name, dictionary )
|
|
|
|
class Service( object ):
|
|
|
|
def __init__( self, service_key, service_type, name, dictionary = None ):
|
|
|
|
if dictionary is None:
|
|
|
|
dictionary = GenerateDefaultServiceDictionary( service_type )
|
|
|
|
|
|
self._service_key = service_key
|
|
self._service_type = service_type
|
|
self._name = name
|
|
|
|
self._dirty = False
|
|
self._lock = threading.Lock()
|
|
|
|
self._LoadFromDictionary( dictionary )
|
|
|
|
|
|
def __hash__( self ): return self._service_key.__hash__()
|
|
|
|
def _CheckFunctional( self ):
|
|
|
|
pass
|
|
|
|
|
|
def _GetSerialisableDictionary( self ):
|
|
|
|
return HydrusSerialisable.SerialisableDictionary()
|
|
|
|
|
|
def _LoadFromDictionary( self, dictionary ):
|
|
|
|
pass
|
|
|
|
|
|
def _SetDirty( self ):
|
|
|
|
self._dirty = True
|
|
|
|
HG.client_controller.pub( 'service_updated', self )
|
|
|
|
|
|
def CheckFunctional( self ):
|
|
|
|
with self._lock:
|
|
|
|
self._CheckFunctional()
|
|
|
|
|
|
|
|
def Duplicate( self ):
|
|
|
|
with self._lock:
|
|
|
|
dictionary = self._GetSerialisableDictionary()
|
|
|
|
duplicate = GenerateService( self._service_key, self._service_type, self._name, dictionary )
|
|
|
|
return duplicate
|
|
|
|
|
|
|
|
def GetSerialisableDictionary( self ):
|
|
|
|
with self._lock:
|
|
|
|
self._dirty = False
|
|
|
|
return self._GetSerialisableDictionary()
|
|
|
|
|
|
|
|
def GetName( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._name
|
|
|
|
|
|
|
|
def GetServiceKey( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._service_key
|
|
|
|
|
|
|
|
def GetServiceType( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._service_type
|
|
|
|
|
|
|
|
def GetStatusString( self ):
|
|
|
|
with self._lock:
|
|
|
|
try:
|
|
|
|
self._CheckFunctional()
|
|
|
|
return 'service is functional'
|
|
|
|
except Exception as e:
|
|
|
|
return str( e )
|
|
|
|
|
|
|
|
|
|
def IsDirty( self ):
|
|
|
|
return self._dirty
|
|
|
|
|
|
def IsFunctional( self ):
|
|
|
|
with self._lock:
|
|
|
|
try:
|
|
|
|
self._CheckFunctional()
|
|
|
|
return True
|
|
|
|
except:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def SetClean( self ):
|
|
|
|
with self._lock:
|
|
|
|
self._dirty = False
|
|
|
|
|
|
|
|
def SetName( self, name ):
|
|
|
|
with self._lock:
|
|
|
|
self._name = name
|
|
|
|
|
|
|
|
def ToTuple( self ):
|
|
|
|
dictionary = self._GetSerialisableDictionary()
|
|
|
|
return ( self._service_key, self._service_type, self._name, dictionary )
|
|
|
|
|
|
class ServiceLocalBooru( Service ):
|
|
|
|
def _CheckFunctional( self ):
|
|
|
|
if not self._bandwidth_rules.CanStartRequest( self._bandwidth_tracker ):
|
|
|
|
raise HydrusExceptions.BandwidthException( 'bandwidth exceeded' )
|
|
|
|
|
|
Service._CheckFunctional( self )
|
|
|
|
|
|
def _GetSerialisableDictionary( self ):
|
|
|
|
dictionary = Service._GetSerialisableDictionary( self )
|
|
|
|
dictionary[ 'port' ] = self._port
|
|
dictionary[ 'upnp_port' ] = self._upnp_port
|
|
dictionary[ 'bandwidth_tracker' ] = self._bandwidth_tracker
|
|
dictionary[ 'bandwidth_rules' ] = self._bandwidth_rules
|
|
|
|
return dictionary
|
|
|
|
|
|
def _LoadFromDictionary( self, dictionary ):
|
|
|
|
Service._LoadFromDictionary( self, dictionary )
|
|
|
|
self._port = dictionary[ 'port' ]
|
|
self._upnp_port = dictionary[ 'upnp_port' ]
|
|
self._bandwidth_tracker = dictionary[ 'bandwidth_tracker' ]
|
|
self._bandwidth_rules = dictionary[ 'bandwidth_rules' ]
|
|
|
|
# this should support the same serverservice interface so we can just toss it at the regular serverengine and all the bandwidth will work ok
|
|
|
|
|
|
def BandwidthOK( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._bandwidth_rules.CanStartRequest( self._bandwidth_tracker )
|
|
|
|
|
|
|
|
def GetUPnPPort( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._upnp_port
|
|
|
|
|
|
|
|
def GetPort( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._port
|
|
|
|
|
|
|
|
def ReportDataUsed( self, num_bytes ):
|
|
|
|
with self._lock:
|
|
|
|
self._bandwidth_tracker.ReportDataUsed( num_bytes )
|
|
|
|
|
|
|
|
def ReportRequestUsed( self ):
|
|
|
|
with self._lock:
|
|
|
|
self._bandwidth_tracker.ReportRequestUsed()
|
|
|
|
|
|
|
|
class ServiceLocalTag( Service ):
|
|
|
|
def _GetSerialisableDictionary( self ):
|
|
|
|
dictionary = Service._GetSerialisableDictionary( self )
|
|
|
|
dictionary[ 'tag_archive_sync' ] = list(self._tag_archive_sync.items())
|
|
|
|
return dictionary
|
|
|
|
|
|
def _LoadFromDictionary( self, dictionary ):
|
|
|
|
Service._LoadFromDictionary( self, dictionary )
|
|
|
|
self._tag_archive_sync = dict( dictionary[ 'tag_archive_sync' ] )
|
|
|
|
|
|
def GetTagArchiveSync( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._tag_archive_sync
|
|
|
|
|
|
|
|
class ServiceLocalRating( Service ):
|
|
|
|
def _GetSerialisableDictionary( self ):
|
|
|
|
dictionary = Service._GetSerialisableDictionary( self )
|
|
|
|
dictionary[ 'shape' ] = self._shape
|
|
dictionary[ 'colours' ] = list(self._colours.items())
|
|
|
|
return dictionary
|
|
|
|
|
|
def _LoadFromDictionary( self, dictionary ):
|
|
|
|
Service._LoadFromDictionary( self, dictionary )
|
|
|
|
self._shape = dictionary[ 'shape' ]
|
|
self._colours = dict( dictionary[ 'colours' ] )
|
|
|
|
|
|
def GetColour( self, rating_state ):
|
|
|
|
with self._lock:
|
|
|
|
return self._colours[ rating_state ]
|
|
|
|
|
|
|
|
def GetShape( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._shape
|
|
|
|
|
|
|
|
class ServiceLocalRatingLike( ServiceLocalRating ):
|
|
|
|
pass
|
|
|
|
class ServiceLocalRatingNumerical( ServiceLocalRating ):
|
|
|
|
def _GetSerialisableDictionary( self ):
|
|
|
|
dictionary = ServiceLocalRating._GetSerialisableDictionary( self )
|
|
|
|
dictionary[ 'num_stars' ] = self._num_stars
|
|
dictionary[ 'allow_zero' ] = self._allow_zero
|
|
|
|
return dictionary
|
|
|
|
|
|
def _LoadFromDictionary( self, dictionary ):
|
|
|
|
ServiceLocalRating._LoadFromDictionary( self, dictionary )
|
|
|
|
self._num_stars = dictionary[ 'num_stars' ]
|
|
self._allow_zero = dictionary[ 'allow_zero' ]
|
|
|
|
|
|
def AllowZero( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._allow_zero
|
|
|
|
|
|
|
|
def GetNumStars( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._num_stars
|
|
|
|
|
|
|
|
class ServiceRemote( Service ):
|
|
|
|
def __init__( self, service_key, service_type, name, dictionary = None ):
|
|
|
|
Service.__init__( self, service_key, service_type, name, dictionary = dictionary )
|
|
|
|
self.network_context = ClientNetworkingContexts.NetworkContext( CC.NETWORK_CONTEXT_HYDRUS, self._service_key )
|
|
|
|
|
|
def _DelayFutureRequests( self, reason, duration = None ):
|
|
|
|
if duration is None:
|
|
|
|
duration = self._GetErrorWaitPeriod()
|
|
|
|
|
|
next_no_requests_until = HydrusData.GetNow() + duration
|
|
|
|
if next_no_requests_until > self._no_requests_until:
|
|
|
|
self._no_requests_reason = reason
|
|
self._no_requests_until = HydrusData.GetNow() + duration
|
|
|
|
|
|
self._SetDirty()
|
|
|
|
|
|
def _GetBaseURL( self ):
|
|
|
|
( host, port ) = self._credentials.GetAddress()
|
|
|
|
base_url = 'https://' + host + ':' + str( port ) + '/'
|
|
|
|
return base_url
|
|
|
|
|
|
def _GetErrorWaitPeriod( self ):
|
|
|
|
return 3600 * 4
|
|
|
|
|
|
def _CheckFunctional( self, including_external_communication = True ):
|
|
|
|
if including_external_communication:
|
|
|
|
self._CheckCanCommunicateExternally()
|
|
|
|
|
|
Service._CheckFunctional( self )
|
|
|
|
|
|
def _CheckCanCommunicateExternally( self ):
|
|
|
|
if not HydrusData.TimeHasPassed( self._no_requests_until ):
|
|
|
|
raise HydrusExceptions.PermissionException( self._no_requests_reason + ' - next request ' + HydrusData.TimestampToPrettyTimeDelta( self._no_requests_until ) )
|
|
|
|
|
|
example_nj = ClientNetworkingJobs.NetworkJobHydrus( self._service_key, 'GET', self._GetBaseURL() )
|
|
|
|
can_start = HG.client_controller.network_engine.bandwidth_manager.CanDoWork( example_nj.GetNetworkContexts(), threshold = 60 )
|
|
|
|
if not can_start:
|
|
|
|
raise HydrusExceptions.BandwidthException( 'bandwidth exceeded' )
|
|
|
|
|
|
|
|
def _GetSerialisableDictionary( self ):
|
|
|
|
dictionary = Service._GetSerialisableDictionary( self )
|
|
|
|
dictionary[ 'credentials' ] = self._credentials
|
|
dictionary[ 'no_requests_reason' ] = self._no_requests_reason
|
|
dictionary[ 'no_requests_until' ] = self._no_requests_until
|
|
|
|
return dictionary
|
|
|
|
|
|
def _LoadFromDictionary( self, dictionary ):
|
|
|
|
Service._LoadFromDictionary( self, dictionary )
|
|
|
|
self._credentials = dictionary[ 'credentials' ]
|
|
self._no_requests_reason = dictionary[ 'no_requests_reason' ]
|
|
self._no_requests_until = dictionary[ 'no_requests_until' ]
|
|
|
|
|
|
def DelayFutureRequests( self, reason, duration = None ):
|
|
|
|
with self._lock:
|
|
|
|
self._DelayFutureRequests( reason, duration = None )
|
|
|
|
|
|
|
|
def GetBandwidthCurrentMonthSummary( self ):
|
|
|
|
with self._lock:
|
|
|
|
return HG.client_controller.network_engine.bandwidth_manager.GetCurrentMonthSummary( self.network_context )
|
|
|
|
|
|
|
|
def GetBandwidthStringsAndGaugeTuples( self ):
|
|
|
|
with self._lock:
|
|
|
|
return HG.client_controller.network_engine.bandwidth_manager.GetBandwidthStringsAndGaugeTuples( self.network_context )
|
|
|
|
|
|
|
|
def GetBaseURL( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._GetBaseURL()
|
|
|
|
|
|
|
|
def GetCredentials( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._credentials
|
|
|
|
|
|
|
|
def SetCredentials( self, credentials ):
|
|
|
|
with self._lock:
|
|
|
|
self._credentials = credentials
|
|
|
|
self._SetDirty()
|
|
|
|
|
|
|
|
class ServiceRestricted( ServiceRemote ):
|
|
|
|
def _DealWithAccountError( self ):
|
|
|
|
account_key = self._account.GetAccountKey()
|
|
|
|
self._account = HydrusNetwork.Account.GenerateUnknownAccount( account_key )
|
|
|
|
self._next_account_sync = HydrusData.GetNow()
|
|
|
|
self._SetDirty()
|
|
|
|
HG.client_controller.pub( 'important_dirt_to_clean' )
|
|
|
|
|
|
def _DealWithFundamentalNetworkError( self ):
|
|
|
|
account_key = self._account.GetAccountKey()
|
|
|
|
self._account = HydrusNetwork.Account.GenerateUnknownAccount( account_key )
|
|
|
|
self._next_account_sync = HydrusData.GetNow() + HC.UPDATE_DURATION * 10
|
|
|
|
self._SetDirty()
|
|
|
|
HG.client_controller.pub( 'important_dirt_to_clean' )
|
|
|
|
|
|
def _GetErrorWaitPeriod( self ):
|
|
|
|
if self._account.HasPermission( HC.CONTENT_TYPE_ACCOUNTS, HC.PERMISSION_ACTION_OVERRULE ):
|
|
|
|
return 900
|
|
|
|
else:
|
|
|
|
return HC.UPDATE_DURATION
|
|
|
|
|
|
|
|
def _CheckFunctional( self, including_external_communication = True, including_account = True ):
|
|
|
|
if including_account:
|
|
|
|
self._account.CheckFunctional()
|
|
|
|
|
|
ServiceRemote._CheckFunctional( self, including_external_communication = including_external_communication )
|
|
|
|
|
|
def _CheckCanCommunicateExternally( self ):
|
|
|
|
if not self._credentials.HasAccessKey():
|
|
|
|
raise HydrusExceptions.PermissionException( 'this service has no access key set' )
|
|
|
|
|
|
ServiceRemote._CheckCanCommunicateExternally( self )
|
|
|
|
|
|
|
|
def _GetSerialisableDictionary( self ):
|
|
|
|
dictionary = ServiceRemote._GetSerialisableDictionary( self )
|
|
|
|
dictionary[ 'account' ] = HydrusNetwork.Account.GenerateSerialisableTupleFromAccount( self._account )
|
|
dictionary[ 'next_account_sync' ] = self._next_account_sync
|
|
|
|
return dictionary
|
|
|
|
|
|
def _LoadFromDictionary( self, dictionary ):
|
|
|
|
ServiceRemote._LoadFromDictionary( self, dictionary )
|
|
|
|
self._account = HydrusNetwork.Account.GenerateAccountFromSerialisableTuple( dictionary[ 'account' ] )
|
|
self._next_account_sync = dictionary[ 'next_account_sync' ]
|
|
|
|
|
|
def CheckFunctional( self, including_external_communication = True, including_account = True ):
|
|
|
|
with self._lock:
|
|
|
|
self._CheckFunctional( including_external_communication = including_external_communication, including_account = including_account )
|
|
|
|
|
|
|
|
def GetAccount( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._account
|
|
|
|
|
|
|
|
def GetNextAccountSyncStatus( self ):
|
|
|
|
if HydrusData.TimeHasPassed( self._next_account_sync ):
|
|
|
|
s = 'imminently'
|
|
|
|
else:
|
|
|
|
s = HydrusData.TimestampToPrettyTimeDelta( self._next_account_sync )
|
|
|
|
|
|
return 'next account sync ' + s
|
|
|
|
|
|
def HasPermission( self, content_type, action ):
|
|
|
|
with self._lock:
|
|
|
|
return self._account.HasPermission( content_type, action )
|
|
|
|
|
|
|
|
def IsDirty( self ):
|
|
|
|
if ServiceRemote.IsDirty( self ):
|
|
|
|
return True
|
|
|
|
|
|
return self._account.IsDirty()
|
|
|
|
|
|
def IsFunctional( self, including_external_communication = True, including_account = True ):
|
|
|
|
with self._lock:
|
|
|
|
try:
|
|
|
|
self._CheckFunctional( including_external_communication = including_external_communication, including_account = including_account )
|
|
|
|
return True
|
|
|
|
except:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def Request( self, method, command, request_args = None, request_headers = None, report_hooks = None, temp_path = None ):
|
|
|
|
if request_args is None: request_args = {}
|
|
if request_headers is None: request_headers = {}
|
|
if report_hooks is None: report_hooks = []
|
|
|
|
try:
|
|
|
|
if method == HC.GET:
|
|
|
|
query = HydrusNetwork.DumpToGETQuery( request_args )
|
|
|
|
body = ''
|
|
|
|
content_type = None
|
|
|
|
elif method == HC.POST:
|
|
|
|
query = ''
|
|
|
|
if command == 'file':
|
|
|
|
content_type = HC.APPLICATION_OCTET_STREAM
|
|
|
|
body = request_args[ 'file' ]
|
|
|
|
del request_args[ 'file' ]
|
|
|
|
else:
|
|
|
|
content_type = HC.APPLICATION_JSON
|
|
|
|
body = HydrusNetwork.DumpHydrusArgsToNetworkBytes( request_args )
|
|
|
|
|
|
|
|
if query != '':
|
|
|
|
command_and_query = command + '?' + query
|
|
|
|
else:
|
|
|
|
command_and_query = command
|
|
|
|
|
|
url = self.GetBaseURL() + command_and_query
|
|
|
|
if method == HC.GET:
|
|
|
|
method = 'GET'
|
|
|
|
elif method == HC.POST:
|
|
|
|
method = 'POST'
|
|
|
|
|
|
network_job = ClientNetworkingJobs.NetworkJobHydrus( self._service_key, method, url, body = body, temp_path = temp_path )
|
|
|
|
if command in ( '', 'access_key', 'access_key_verification' ):
|
|
|
|
# don't try to establish a session key for these requests
|
|
network_job.SetForLogin( True )
|
|
|
|
if command == 'access_key_verification':
|
|
|
|
network_job.AddAdditionalHeader( 'Hydrus-Key', self._credentials.GetAccessKey().hex() )
|
|
|
|
|
|
|
|
if content_type is not None:
|
|
|
|
network_job.AddAdditionalHeader( 'Content-Type', HC.mime_string_lookup[ content_type ] )
|
|
|
|
|
|
network_job.SetDeathTime( HydrusData.GetNow() + 30 ) # we don't want to wait on logins during shutdown and so on
|
|
|
|
HG.client_controller.network_engine.AddJob( network_job )
|
|
|
|
network_job.WaitUntilDone()
|
|
|
|
network_bytes = network_job.GetContentBytes()
|
|
|
|
content_type = network_job.GetContentType()
|
|
|
|
if content_type == 'application/json':
|
|
|
|
hydrus_args = HydrusNetwork.ParseNetworkBytesToHydrusArgs( network_bytes )
|
|
|
|
if command == 'account' and 'account' in hydrus_args:
|
|
|
|
data_used = network_job.GetTotalDataUsed()
|
|
|
|
account = hydrus_args[ 'account' ]
|
|
|
|
# because the account was one behind when it was serialised! mostly do this just to sync up nicely with the service bandwidth tracker
|
|
account.ReportDataUsed( data_used )
|
|
account.ReportRequestUsed()
|
|
|
|
|
|
response = hydrus_args
|
|
|
|
else:
|
|
|
|
response = network_bytes
|
|
|
|
|
|
return response
|
|
|
|
except Exception as e:
|
|
|
|
with self._lock:
|
|
|
|
if isinstance( e, HydrusExceptions.ServerBusyException ):
|
|
|
|
self._DelayFutureRequests( 'server was busy', 5 * 60 )
|
|
|
|
elif isinstance( e, HydrusExceptions.SessionException ):
|
|
|
|
HG.client_controller.network_engine.session_manager.ClearSession( self.network_context )
|
|
|
|
elif isinstance( e, HydrusExceptions.PermissionException ):
|
|
|
|
self._DealWithAccountError()
|
|
|
|
elif isinstance( e, HydrusExceptions.ForbiddenException ):
|
|
|
|
self._DealWithAccountError()
|
|
|
|
elif isinstance( e, HydrusExceptions.NetworkVersionException ):
|
|
|
|
self._DealWithFundamentalNetworkError()
|
|
|
|
elif isinstance( e, HydrusExceptions.NotFoundException ):
|
|
|
|
self._DelayFutureRequests( 'got an unexpected 404', HC.UPDATE_DURATION )
|
|
|
|
elif isinstance( e, HydrusExceptions.BandwidthException ):
|
|
|
|
self._DelayFutureRequests( 'service has exceeded bandwidth', HC.UPDATE_DURATION * 5 )
|
|
|
|
else:
|
|
|
|
self._DelayFutureRequests( str( e ) )
|
|
|
|
|
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
def SetClean( self ):
|
|
|
|
ServiceRemote.SetClean( self )
|
|
|
|
self._account.SetClean()
|
|
|
|
|
|
def SyncAccount( self, force = False ):
|
|
|
|
with self._lock:
|
|
|
|
name = self._name
|
|
|
|
if force:
|
|
|
|
do_it = True
|
|
|
|
if force:
|
|
|
|
self._no_requests_until = 0
|
|
|
|
|
|
else:
|
|
|
|
do_it = HydrusData.TimeHasPassed( self._next_account_sync )
|
|
|
|
if do_it:
|
|
|
|
try:
|
|
|
|
self._CheckFunctional( including_account = False )
|
|
|
|
except:
|
|
|
|
do_it = False
|
|
|
|
self._next_account_sync = HydrusData.GetNow() + HC.UPDATE_DURATION
|
|
|
|
self._SetDirty()
|
|
|
|
|
|
|
|
|
|
|
|
if do_it:
|
|
|
|
try:
|
|
|
|
response = self.Request( HC.GET, 'account' )
|
|
|
|
with self._lock:
|
|
|
|
self._account = response[ 'account' ]
|
|
|
|
if force:
|
|
|
|
self._no_requests_until = 0
|
|
|
|
|
|
|
|
HG.client_controller.pub( 'notify_new_permissions' )
|
|
|
|
except ( HydrusExceptions.CancelledException, HydrusExceptions.NetworkException ) as e:
|
|
|
|
HydrusData.Print( 'Failed to refresh account for ' + name + ':' )
|
|
|
|
HydrusData.Print( e )
|
|
|
|
if force:
|
|
|
|
raise
|
|
|
|
|
|
except Exception:
|
|
|
|
HydrusData.Print( 'Failed to refresh account for ' + name + ':' )
|
|
|
|
HydrusData.Print( traceback.format_exc() )
|
|
|
|
if force:
|
|
|
|
raise
|
|
|
|
|
|
finally:
|
|
|
|
with self._lock:
|
|
|
|
self._next_account_sync = HydrusData.GetNow() + HC.UPDATE_DURATION * 5
|
|
|
|
self._SetDirty()
|
|
|
|
HG.client_controller.pub( 'important_dirt_to_clean' )
|
|
|
|
|
|
|
|
|
|
|
|
class ServiceRepository( ServiceRestricted ):
|
|
|
|
def __init__( self, service_key, service_type, name, dictionary = None ):
|
|
|
|
ServiceRestricted.__init__( self, service_key, service_type, name, dictionary = dictionary )
|
|
|
|
self._sync_lock = threading.Lock()
|
|
|
|
|
|
def _CanSyncDownload( self ):
|
|
|
|
try:
|
|
|
|
self._CheckFunctional()
|
|
|
|
return True
|
|
|
|
except:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def _CanSyncProcess( self ):
|
|
|
|
try:
|
|
|
|
self._CheckFunctional( including_external_communication = False, including_account = False )
|
|
|
|
return True
|
|
|
|
except:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def _CheckFunctional( self, including_external_communication = True, including_account = True ):
|
|
|
|
if self._paused:
|
|
|
|
raise HydrusExceptions.PermissionException( 'Repository is paused!' )
|
|
|
|
|
|
if HG.client_controller.options[ 'pause_repo_sync' ]:
|
|
|
|
raise HydrusExceptions.PermissionException( 'All repositories are paused!' )
|
|
|
|
|
|
ServiceRestricted._CheckFunctional( self, including_external_communication = including_external_communication, including_account = including_account )
|
|
|
|
|
|
def _GetSerialisableDictionary( self ):
|
|
|
|
dictionary = ServiceRestricted._GetSerialisableDictionary( self )
|
|
|
|
dictionary[ 'metadata' ] = self._metadata
|
|
dictionary[ 'paused' ] = self._paused
|
|
dictionary[ 'tag_archive_sync' ] = list(self._tag_archive_sync.items())
|
|
|
|
return dictionary
|
|
|
|
|
|
def _LoadFromDictionary( self, dictionary ):
|
|
|
|
ServiceRestricted._LoadFromDictionary( self, dictionary )
|
|
|
|
self._metadata = dictionary[ 'metadata' ]
|
|
self._paused = dictionary[ 'paused' ]
|
|
self._tag_archive_sync = dict( dictionary[ 'tag_archive_sync' ] )
|
|
|
|
|
|
def CanDoIdleShutdownWork( self ):
|
|
|
|
with self._lock:
|
|
|
|
if not self._CanSyncProcess():
|
|
|
|
return False
|
|
|
|
|
|
service_key = self._service_key
|
|
|
|
|
|
( download_value, processing_value, range ) = HG.client_controller.Read( 'repository_progress', service_key )
|
|
|
|
return processing_value < range
|
|
|
|
|
|
def GetNextUpdateDueString( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._metadata.GetNextUpdateDueString( from_client = True )
|
|
|
|
|
|
|
|
def GetTagArchiveSync( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._tag_archive_sync
|
|
|
|
|
|
|
|
def GetUpdateHashes( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._metadata.GetUpdateHashes()
|
|
|
|
|
|
|
|
def GetUpdateInfo( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._metadata.GetUpdateInfo()
|
|
|
|
|
|
|
|
def IsPaused( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._paused
|
|
|
|
|
|
|
|
def PausePlay( self ):
|
|
|
|
with self._lock:
|
|
|
|
self._paused = not self._paused
|
|
|
|
self._SetDirty()
|
|
|
|
HG.client_controller.pub( 'important_dirt_to_clean' )
|
|
|
|
if not self._paused:
|
|
|
|
HG.client_controller.pub( 'notify_new_permissions' )
|
|
|
|
|
|
|
|
|
|
def Reset( self ):
|
|
|
|
with self._lock:
|
|
|
|
self._no_requests_reason = ''
|
|
self._no_requests_until = 0
|
|
|
|
self._account = HydrusNetwork.Account.GenerateUnknownAccount()
|
|
self._next_account_sync = 0
|
|
|
|
self._metadata = HydrusNetwork.Metadata()
|
|
|
|
self._SetDirty()
|
|
|
|
HG.client_controller.pub( 'important_dirt_to_clean' )
|
|
|
|
HG.client_controller.Write( 'reset_repository', self )
|
|
|
|
|
|
|
|
def Sync( self, only_process_when_idle = False, stop_time = None ):
|
|
|
|
with self._sync_lock: # to stop sync_now button clicks from stomping over the regular daemon and vice versa
|
|
|
|
try:
|
|
|
|
self.SyncDownloadMetadata()
|
|
|
|
self.SyncDownloadUpdates( stop_time )
|
|
|
|
self.SyncProcessUpdates( only_process_when_idle, stop_time )
|
|
|
|
self.SyncThumbnails( stop_time )
|
|
|
|
except Exception as e:
|
|
|
|
self._DelayFutureRequests( str( e ) )
|
|
|
|
HydrusData.ShowText( 'The service "' + self._name + '" encountered an error while trying to sync! It will not do any work for a little while. Please elevate this to hydrus dev if a fix is not obvious.' )
|
|
|
|
HydrusData.ShowException( e )
|
|
|
|
finally:
|
|
|
|
if self.IsDirty():
|
|
|
|
HG.client_controller.pub( 'important_dirt_to_clean' )
|
|
|
|
|
|
|
|
|
|
|
|
def SyncDownloadUpdates( self, stop_time ):
|
|
|
|
with self._lock:
|
|
|
|
if not self._CanSyncDownload():
|
|
|
|
return
|
|
|
|
|
|
name = self._name
|
|
service_key = self._service_key
|
|
|
|
|
|
update_hashes = HG.client_controller.Read( 'missing_repository_update_hashes', service_key )
|
|
|
|
if len( update_hashes ) > 0:
|
|
|
|
job_key = ClientThreading.JobKey( cancellable = True, stop_time = stop_time )
|
|
|
|
try:
|
|
|
|
job_key.SetVariable( 'popup_title', name + ' sync: downloading updates' )
|
|
|
|
HG.client_controller.pub( 'message', job_key )
|
|
|
|
for ( i, update_hash ) in enumerate( update_hashes ):
|
|
|
|
status = 'update ' + HydrusData.ConvertValueRangeToPrettyString( i + 1, len( update_hashes ) )
|
|
|
|
HG.client_controller.pub( 'splash_set_status_text', status, print_to_log = False )
|
|
job_key.SetVariable( 'popup_text_1', status )
|
|
job_key.SetVariable( 'popup_gauge_1', ( i + 1, len( update_hashes ) ) )
|
|
|
|
with self._lock:
|
|
|
|
if not self._CanSyncDownload():
|
|
|
|
return
|
|
|
|
|
|
|
|
( i_paused, should_quit ) = job_key.WaitIfNeeded()
|
|
|
|
if should_quit:
|
|
|
|
with self._lock:
|
|
|
|
self._DelayFutureRequests( 'download was recently cancelled', 3 * 60 )
|
|
|
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
|
update_network_string = self.Request( HC.GET, 'update', { 'update_hash' : update_hash } )
|
|
|
|
except HydrusExceptions.CancelledException as e:
|
|
|
|
self._DelayFutureRequests( str( e ) )
|
|
|
|
return
|
|
|
|
except HydrusExceptions.NetworkException as e:
|
|
|
|
HydrusData.Print( 'Attempting to download an update for ' + name + ' resulted in a network error:' )
|
|
|
|
HydrusData.Print( e )
|
|
|
|
return
|
|
|
|
|
|
update_network_string_hash = hashlib.sha256( update_network_string ).digest()
|
|
|
|
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._DelayFutureRequests( 'had an unusual update response' )
|
|
|
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
|
update = HydrusSerialisable.CreateFromNetworkBytes( update_network_string )
|
|
|
|
except Exception as e:
|
|
|
|
with self._lock:
|
|
|
|
self._paused = True
|
|
|
|
self._DealWithFundamentalNetworkError()
|
|
|
|
|
|
message = 'Update ' + update_hash.hex() + ' downloaded from the ' + self._name + ' repository failed to load! This is a serious error!'
|
|
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.'
|
|
|
|
HydrusData.ShowText( message )
|
|
|
|
HydrusData.ShowException( e )
|
|
|
|
return
|
|
|
|
|
|
if isinstance( update, HydrusNetwork.DefinitionsUpdate ):
|
|
|
|
mime = HC.APPLICATION_HYDRUS_UPDATE_DEFINITIONS
|
|
|
|
elif isinstance( update, HydrusNetwork.ContentUpdate ):
|
|
|
|
mime = HC.APPLICATION_HYDRUS_UPDATE_CONTENT
|
|
|
|
else:
|
|
|
|
with self._lock:
|
|
|
|
self._paused = True
|
|
|
|
self._DealWithFundamentalNetworkError()
|
|
|
|
|
|
message = 'Update ' + update_hash.hex() + ' downloaded from the ' + self._name + ' was not a valid update--it was a ' + repr( update ) + '! This is a serious error!'
|
|
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.'
|
|
|
|
HydrusData.ShowText( message )
|
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
|
HG.client_controller.WriteSynchronous( 'import_update', update_network_string, update_hash, mime )
|
|
|
|
except Exception as e:
|
|
|
|
with self._lock:
|
|
|
|
self._paused = True
|
|
|
|
self._DealWithFundamentalNetworkError()
|
|
|
|
|
|
message = 'While downloading updates for the ' + self._name + ' repository, one failed to import! The error follows:'
|
|
|
|
HydrusData.ShowText( message )
|
|
|
|
HydrusData.ShowException( e )
|
|
|
|
return
|
|
|
|
|
|
|
|
job_key.SetVariable( 'popup_text_1', 'finished' )
|
|
job_key.DeleteVariable( 'popup_gauge_1' )
|
|
|
|
finally:
|
|
|
|
job_key.Finish()
|
|
job_key.Delete( 5 )
|
|
|
|
|
|
|
|
|
|
def SyncDownloadMetadata( self ):
|
|
|
|
with self._lock:
|
|
|
|
if not self._CanSyncDownload():
|
|
|
|
return
|
|
|
|
|
|
do_it = self._metadata.UpdateDue( from_client = True )
|
|
|
|
next_update_index = self._metadata.GetNextUpdateIndex()
|
|
|
|
service_key = self._service_key
|
|
|
|
name = self._name
|
|
|
|
|
|
if do_it:
|
|
|
|
try:
|
|
|
|
response = self.Request( HC.GET, 'metadata', { 'since' : next_update_index } )
|
|
|
|
metadata_slice = response[ 'metadata_slice' ]
|
|
|
|
except HydrusExceptions.CancelledException as e:
|
|
|
|
self._DelayFutureRequests( str( e ) )
|
|
|
|
return
|
|
|
|
except HydrusExceptions.NetworkException as e:
|
|
|
|
HydrusData.Print( 'Attempting to download metadata for ' + name + ' resulted in a network error:' )
|
|
|
|
HydrusData.Print( e )
|
|
|
|
return
|
|
|
|
|
|
HG.client_controller.WriteSynchronous( 'associate_repository_update_hashes', service_key, metadata_slice )
|
|
|
|
with self._lock:
|
|
|
|
self._metadata.UpdateFromSlice( metadata_slice )
|
|
|
|
self._SetDirty()
|
|
|
|
|
|
|
|
|
|
def SyncProcessUpdates( self, only_when_idle = False, stop_time = None ):
|
|
|
|
with self._lock:
|
|
|
|
if not self._CanSyncProcess():
|
|
|
|
return
|
|
|
|
|
|
|
|
if only_when_idle and not HG.client_controller.CurrentlyIdle():
|
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
|
( did_some_work, did_everything ) = HG.client_controller.WriteSynchronous( 'process_repository', self._service_key, only_when_idle, stop_time )
|
|
|
|
if did_some_work:
|
|
|
|
with self._lock:
|
|
|
|
self._SetDirty()
|
|
|
|
if self._service_type == HC.TAG_REPOSITORY:
|
|
|
|
HG.client_controller.pub( 'notify_new_force_refresh_tags_data' )
|
|
|
|
|
|
|
|
|
|
if not did_everything:
|
|
|
|
time.sleep( 3 ) # stop spamming of repo sync daemon from bringing this up again too quick
|
|
|
|
|
|
except Exception as e:
|
|
|
|
with self._lock:
|
|
|
|
message = 'Failed to process updates for the ' + self._name + ' repository! The error follows:'
|
|
|
|
HydrusData.ShowText( message )
|
|
|
|
HydrusData.ShowException( e )
|
|
|
|
self._paused = True
|
|
|
|
self._SetDirty()
|
|
|
|
|
|
HG.client_controller.pub( 'important_dirt_to_clean' )
|
|
|
|
|
|
|
|
def SyncThumbnails( self, stop_time ):
|
|
|
|
with self._lock:
|
|
|
|
if not self._CanSyncDownload():
|
|
|
|
return
|
|
|
|
|
|
name = self._name
|
|
service_key = self._service_key
|
|
|
|
|
|
thumbnail_hashes = HG.client_controller.Read( 'missing_thumbnail_hashes', service_key )
|
|
|
|
num_to_do = len( thumbnail_hashes )
|
|
|
|
if num_to_do > 0:
|
|
|
|
client_files_manager = HG.client_controller.client_files_manager
|
|
|
|
job_key = ClientThreading.JobKey( cancellable = True, stop_time = stop_time )
|
|
|
|
try:
|
|
|
|
job_key.SetVariable( 'popup_title', name + ' sync: downloading thumbnails' )
|
|
|
|
HG.client_controller.pub( 'message', job_key )
|
|
|
|
for ( i, thumbnail_hash ) in enumerate( thumbnail_hashes ):
|
|
|
|
status = 'thumbnail ' + HydrusData.ConvertValueRangeToPrettyString( i + 1, num_to_do )
|
|
|
|
HG.client_controller.pub( 'splash_set_status_text', status, print_to_log = False )
|
|
job_key.SetVariable( 'popup_text_1', status )
|
|
job_key.SetVariable( 'popup_gauge_1', ( i + 1, num_to_do ) )
|
|
|
|
with self._lock:
|
|
|
|
if not self._CanSyncDownload():
|
|
|
|
break
|
|
|
|
|
|
|
|
( i_paused, should_quit ) = job_key.WaitIfNeeded()
|
|
|
|
if should_quit:
|
|
|
|
with self._lock:
|
|
|
|
self._DelayFutureRequests( 'download was recently cancelled', 3 * 60 )
|
|
|
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
|
thumbnail = self.Request( HC.GET, 'thumbnail', { 'hash' : thumbnail_hash } )
|
|
|
|
except HydrusExceptions.CancelledException as e:
|
|
|
|
self._DelayFutureRequests( str( e ) )
|
|
|
|
return
|
|
|
|
except HydrusExceptions.NetworkException as e:
|
|
|
|
HydrusData.Print( 'Attempting to download a thumbnail for ' + name + ' resulted in a network error:' )
|
|
|
|
HydrusData.Print( e )
|
|
|
|
return
|
|
|
|
|
|
client_files_manager.AddFullSizeThumbnailFromBytes( thumbnail_hash, thumbnail )
|
|
|
|
|
|
job_key.SetVariable( 'popup_text_1', 'finished' )
|
|
job_key.DeleteVariable( 'popup_gauge_1' )
|
|
|
|
finally:
|
|
|
|
job_key.Finish()
|
|
job_key.Delete( 5 )
|
|
|
|
|
|
|
|
|
|
class ServiceIPFS( ServiceRemote ):
|
|
|
|
def _GetSerialisableDictionary( self ):
|
|
|
|
dictionary = ServiceRemote._GetSerialisableDictionary( self )
|
|
|
|
dictionary[ 'multihash_prefix' ] = self._multihash_prefix
|
|
|
|
return dictionary
|
|
|
|
|
|
def _LoadFromDictionary( self, dictionary ):
|
|
|
|
ServiceRemote._LoadFromDictionary( self, dictionary )
|
|
|
|
self._multihash_prefix = dictionary[ 'multihash_prefix' ]
|
|
|
|
|
|
def _ConvertMultihashToURLTree( self, name, size, multihash ):
|
|
|
|
api_base_url = self._GetAPIBaseURL()
|
|
|
|
links_url = api_base_url + 'object/links/' + multihash
|
|
|
|
network_job = ClientNetworkingJobs.NetworkJob( 'GET', links_url )
|
|
|
|
network_job.OverrideBandwidth()
|
|
|
|
HG.client_controller.network_engine.AddJob( network_job )
|
|
|
|
network_job.WaitUntilDone()
|
|
|
|
parsing_text = network_job.GetContentText()
|
|
|
|
links_json = json.loads( parsing_text )
|
|
|
|
is_directory = False
|
|
|
|
if 'Links' in links_json:
|
|
|
|
for link in links_json[ 'Links' ]:
|
|
|
|
if link[ 'Name' ] != '':
|
|
|
|
is_directory = True
|
|
|
|
|
|
|
|
|
|
if is_directory:
|
|
|
|
children = []
|
|
|
|
for link in links_json[ 'Links' ]:
|
|
|
|
subname = link[ 'Name' ]
|
|
subsize = link[ 'Size' ]
|
|
submultihash = link[ 'Hash' ]
|
|
|
|
children.append( self._ConvertMultihashToURLTree( subname, subsize, submultihash ) )
|
|
|
|
|
|
if size is None:
|
|
|
|
size = sum( ( subsize for ( type_gumpf, subname, subsize, submultihash ) in children ) )
|
|
|
|
|
|
return ( 'directory', name, size, children )
|
|
|
|
else:
|
|
|
|
url = api_base_url + 'cat/' + multihash
|
|
|
|
return ( 'file', name, size, url )
|
|
|
|
|
|
|
|
def _GetAPIBaseURL( self ):
|
|
|
|
( host, port ) = self._credentials.GetAddress()
|
|
|
|
api_base_url = 'http://' + host + ':' + str( port ) + '/api/v0/'
|
|
|
|
return api_base_url
|
|
|
|
|
|
def GetDaemonVersion( self ):
|
|
|
|
with self._lock:
|
|
|
|
api_base_url = self._GetAPIBaseURL()
|
|
|
|
|
|
url = api_base_url + 'version'
|
|
|
|
network_job = ClientNetworkingJobs.NetworkJob( 'GET', url )
|
|
|
|
network_job.OverrideBandwidth()
|
|
|
|
HG.client_controller.network_engine.AddJob( network_job )
|
|
|
|
network_job.WaitUntilDone()
|
|
|
|
parsing_text = network_job.GetContentText()
|
|
|
|
j = json.loads( parsing_text )
|
|
|
|
return j[ 'Version' ]
|
|
|
|
|
|
def GetMultihashPrefix( self ):
|
|
|
|
with self._lock:
|
|
|
|
return self._multihash_prefix
|
|
|
|
|
|
|
|
def ImportFile( self, multihash ):
|
|
|
|
def on_wx_select_tree( job_key, url_tree ):
|
|
|
|
from . import ClientGUIDialogs
|
|
|
|
with ClientGUIDialogs.DialogSelectFromURLTree( HG.client_controller.gui, url_tree ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
urls = dlg.GetURLs()
|
|
|
|
if len( urls ) > 0:
|
|
|
|
HG.client_controller.CallToThread( ClientImporting.THREADDownloadURLs, job_key, urls, multihash )
|
|
|
|
|
|
|
|
|
|
|
|
def off_wx():
|
|
|
|
job_key = ClientThreading.JobKey( pausable = True, cancellable = True )
|
|
|
|
job_key.SetVariable( 'popup_text_1', 'Looking up multihash information' )
|
|
|
|
HG.client_controller.pub( 'message', job_key )
|
|
|
|
with self._lock:
|
|
|
|
url_tree = self._ConvertMultihashToURLTree( multihash, None, multihash )
|
|
|
|
|
|
if url_tree[0] == 'file':
|
|
|
|
url = url_tree[3]
|
|
|
|
HG.client_controller.CallToThread( ClientImporting.THREADDownloadURL, job_key, url, multihash )
|
|
|
|
else:
|
|
|
|
job_key.SetVariable( 'popup_text_1', 'Waiting for user selection' )
|
|
|
|
wx.CallAfter( on_wx_select_tree, job_key, url_tree )
|
|
|
|
|
|
|
|
HG.client_controller.CallToThread( off_wx )
|
|
|
|
|
|
def PinDirectory( self, hashes, note ):
|
|
|
|
job_key = ClientThreading.JobKey( pausable = True, cancellable = True )
|
|
|
|
job_key.SetVariable( 'popup_title', 'creating ipfs directory on ' + self._name )
|
|
|
|
HG.client_controller.pub( 'message', job_key )
|
|
|
|
try:
|
|
|
|
file_info = []
|
|
|
|
hashes = list( hashes )
|
|
|
|
hashes.sort()
|
|
|
|
for ( i, hash ) in enumerate( hashes ):
|
|
|
|
( i_paused, should_quit ) = job_key.WaitIfNeeded()
|
|
|
|
if should_quit:
|
|
|
|
return
|
|
|
|
|
|
job_key.SetVariable( 'popup_text_1', 'pinning files: ' + HydrusData.ConvertValueRangeToPrettyString( i + 1, len( hashes ) ) )
|
|
job_key.SetVariable( 'popup_gauge_1', ( i + 1, len( hashes ) ) )
|
|
|
|
( media_result, ) = HG.client_controller.Read( 'media_results', ( hash, ) )
|
|
|
|
mime = media_result.GetMime()
|
|
|
|
result = HG.client_controller.Read( 'service_filenames', self._service_key, { hash } )
|
|
|
|
if len( result ) == 0:
|
|
|
|
multihash = self.PinFile( hash, mime )
|
|
|
|
else:
|
|
|
|
( multihash, ) = result
|
|
|
|
|
|
file_info.append( ( hash, mime, multihash ) )
|
|
|
|
|
|
with self._lock:
|
|
|
|
api_base_url = self._GetAPIBaseURL()
|
|
|
|
|
|
url = api_base_url + 'object/new?arg=unixfs-dir'
|
|
|
|
network_job = ClientNetworkingJobs.NetworkJob( 'GET', url )
|
|
|
|
network_job.OverrideBandwidth()
|
|
|
|
HG.client_controller.network_engine.AddJob( network_job )
|
|
|
|
network_job.WaitUntilDone()
|
|
|
|
parsing_text = network_job.GetContentText()
|
|
|
|
response_json = json.loads( parsing_text )
|
|
|
|
for ( i, ( hash, mime, multihash ) ) in enumerate( file_info ):
|
|
|
|
( i_paused, should_quit ) = job_key.WaitIfNeeded()
|
|
|
|
if should_quit:
|
|
|
|
return
|
|
|
|
|
|
job_key.SetVariable( 'popup_text_1', 'creating directory: ' + HydrusData.ConvertValueRangeToPrettyString( i + 1, len( file_info ) ) )
|
|
job_key.SetVariable( 'popup_gauge_1', ( i + 1, len( file_info ) ) )
|
|
|
|
object_multihash = response_json[ 'Hash' ]
|
|
|
|
filename = hash.hex() + HC.mime_ext_lookup[ mime ]
|
|
|
|
url = api_base_url + 'object/patch/add-link?arg=' + object_multihash + '&arg=' + filename + '&arg=' + multihash
|
|
|
|
network_job = ClientNetworkingJobs.NetworkJob( 'GET', url )
|
|
|
|
network_job.OverrideBandwidth()
|
|
|
|
HG.client_controller.network_engine.AddJob( network_job )
|
|
|
|
network_job.WaitUntilDone()
|
|
|
|
parsing_text = network_job.GetContentText()
|
|
|
|
response_json = json.loads( parsing_text )
|
|
|
|
|
|
directory_multihash = response_json[ 'Hash' ]
|
|
|
|
url = api_base_url + 'pin/add?arg=' + directory_multihash
|
|
|
|
network_job = ClientNetworkingJobs.NetworkJob( 'GET', url )
|
|
|
|
network_job.OverrideBandwidth()
|
|
|
|
HG.client_controller.network_engine.AddJob( network_job )
|
|
|
|
network_job.WaitUntilDone()
|
|
|
|
content_update_row = ( hashes, directory_multihash, note )
|
|
|
|
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_DIRECTORIES, HC.CONTENT_UPDATE_ADD, content_update_row ) ]
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', { self._service_key : content_updates } )
|
|
|
|
job_key.SetVariable( 'popup_text_1', 'done!' )
|
|
job_key.DeleteVariable( 'popup_gauge_1' )
|
|
|
|
with self._lock:
|
|
|
|
text = self._multihash_prefix + directory_multihash
|
|
|
|
|
|
job_key.SetVariable( 'popup_clipboard', ( 'copy multihash to clipboard', text ) )
|
|
|
|
job_key.Finish()
|
|
|
|
return directory_multihash
|
|
|
|
except Exception as e:
|
|
|
|
HydrusData.ShowException( e )
|
|
|
|
job_key.SetVariable( 'popup_text_1', 'error' )
|
|
job_key.DeleteVariable( 'popup_gauge_1' )
|
|
|
|
job_key.Cancel()
|
|
|
|
|
|
|
|
def PinFile( self, hash, mime ):
|
|
|
|
mime_string = HC.mime_string_lookup[ mime ]
|
|
|
|
with self._lock:
|
|
|
|
api_base_url = self._GetAPIBaseURL()
|
|
|
|
|
|
url = api_base_url + 'add'
|
|
|
|
client_files_manager = HG.client_controller.client_files_manager
|
|
|
|
path = client_files_manager.GetFilePath( hash, mime )
|
|
|
|
files = { 'path' : ( hash.hex(), open( path, 'rb' ), mime_string ) }
|
|
|
|
network_job = ClientNetworkingJobs.NetworkJob( 'GET', url )
|
|
|
|
network_job.SetFiles( files )
|
|
|
|
network_job.OverrideBandwidth()
|
|
|
|
HG.client_controller.network_engine.AddJob( network_job )
|
|
|
|
network_job.WaitUntilDone()
|
|
|
|
parsing_text = network_job.GetContentText()
|
|
|
|
j = json.loads( parsing_text )
|
|
|
|
multihash = j[ 'Hash' ]
|
|
|
|
( media_result, ) = HG.client_controller.Read( 'media_results', ( hash, ) )
|
|
|
|
file_info_manager = media_result.GetFileInfoManager()
|
|
|
|
content_update_row = ( file_info_manager, multihash )
|
|
|
|
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_ADD, content_update_row ) ]
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', { self._service_key : content_updates } )
|
|
|
|
return multihash
|
|
|
|
|
|
def UnpinDirectory( self, multihash ):
|
|
|
|
with self._lock:
|
|
|
|
api_base_url = self._GetAPIBaseURL()
|
|
|
|
|
|
url = api_base_url + 'pin/rm/' + multihash
|
|
|
|
network_job = ClientNetworkingJobs.NetworkJob( 'GET', url )
|
|
|
|
network_job.OverrideBandwidth()
|
|
|
|
HG.client_controller.network_engine.AddJob( network_job )
|
|
|
|
network_job.WaitUntilDone()
|
|
|
|
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_DIRECTORIES, HC.CONTENT_UPDATE_DELETE, multihash ) ]
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', { self._service_key : content_updates } )
|
|
|
|
|
|
def UnpinFile( self, hash, multihash ):
|
|
|
|
with self._lock:
|
|
|
|
api_base_url = self._GetAPIBaseURL()
|
|
|
|
|
|
url = api_base_url + 'pin/rm/' + multihash
|
|
|
|
try:
|
|
|
|
network_job = ClientNetworkingJobs.NetworkJob( 'GET', url )
|
|
|
|
network_job.OverrideBandwidth()
|
|
|
|
HG.client_controller.network_engine.AddJob( network_job )
|
|
|
|
network_job.WaitUntilDone()
|
|
|
|
except HydrusExceptions.NetworkException as e:
|
|
|
|
if 'not pinned' not in str( e ):
|
|
|
|
raise
|
|
|
|
|
|
|
|
content_updates = [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, { hash } ) ]
|
|
|
|
HG.client_controller.WriteSynchronous( 'content_updates', { self._service_key : content_updates } )
|
|
|
|
|